# --
# Kernel/Output/HTML/ArticleCheckPGP.pm
# Copyright (C) 2001-2007 OTRS GmbH, http://otrs.org/
# --
# $Id: ArticleCheckPGP.pm,v 1.13 2007/08/22 11:00:08 martin Exp $
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see http://www.gnu.org/licenses/gpl.txt.
# --
package Kernel::Output::HTML::ArticleCheckPGP;
use strict;
use Kernel::System::Crypt;
use vars qw($VERSION);
$VERSION = '$Revision: 1.13 $';
$VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;
sub new {
my $Type = shift;
my %Param = @_;
# allocate new hash for object
my $Self = {};
bless ($Self, $Type);
# get needed objects
foreach (qw(ConfigObject LogObject MainObject DBObject LayoutObject UserID TicketObject ArticleID)) {
if ($Param{$_}) {
$Self->{$_} = $Param{$_};
}
else {
$Self->{LogObject}->Log(Priority => 'error', Message => "Need $_!");
}
}
$Self->{CryptObject} = Kernel::System::Crypt->new(%Param, CryptType => 'PGP');
return $Self;
}
sub Check {
my $Self = shift;
my %Param = @_;
my %SignCheck = ();
my @Return = ();
# check if pgp is enabled
if (!$Self->{ConfigObject}->Get('PGP')) {
return;
}
# check if article is an email
if ($Param{Article}->{ArticleType} !~ /email/i) {
return;
}
# check inline pgp crypt
if ($Param{Article}->{Body} =~ /^-----BEGIN PGP MESSAGE-----/) {
# check sender (don't decrypt sent emails)
if ($Param{Article}->{SenderType} =~ /(agent|system)/i) {
# return info
return ({
Key => 'Crypted',
Value => 'Sent message crypted to recipient!',
});
}
my %Decrypt = $Self->{CryptObject}->Decrypt(Message => $Param{Article}->{Body});
if ($Decrypt{Successful}) {
# remember to result
$Self->{Result} = \%Decrypt;
$Param{Article}->{Body} = $Decrypt{Data},
# updated article body
$Self->{TicketObject}->ArticleUpdate(
TicketID => $Param{Article}->{TicketID},
ArticleID => $Self->{ArticleID},
Key => 'Body',
Value => $Decrypt{Data},
UserID => $Self->{UserID},
);
}
else {
# return with error
return ({
Key => 'Crypted',
Value => $Decrypt{Message},
%Decrypt,
});
}
}
# check inline pgp signature
if ($Param{Article}->{Body} =~ /^-----BEGIN PGP SIGNED MESSAGE-----/) {
%SignCheck = $Self->{CryptObject}->Verify(
Message => $Param{Article}->{Body},
);
if (%SignCheck) {
# remember to result
$Self->{Result} = \%SignCheck;
}
else {
# return with error
return ({
Key => 'Signed',
Value => '"PGP SIGNED MESSAGE" header found, but invalid!',
});
}
}
# check mime pgp
else {
# check body
# if body =~ application/pgp-encrypted
# if crypted, decrypt it
# remember that it was crypted!
# write email to fs
my $Message = $Self->{TicketObject}->ArticlePlain(
ArticleID => $Self->{ArticleID},
UserID => $Self->{UserID},
);
use MIME::Parser;
my $parser = MIME::Parser->new();
$parser->decode_headers(0);
$parser->extract_nested_messages(0);
$parser->output_to_core("ALL");
my $Entity = $parser->parse_data($Message);
my $Head = $Entity->head();
$Head->unfold();
$Head->combine('Content-Type');
my $ContentType = $Head->get('Content-Type');
# check if we need to decrypt it
if ($ContentType && $ContentType =~ /multipart\/encrypted/i && $ContentType =~ /application\/pgp/i) {
# check sender (don't decrypt sent emails)
if ($Param{Article}->{SenderType} =~ /(agent|system)/i) {
# return info
return ({
Key => 'Crypted',
Value => 'Sent message crypted to recipient!',
Successful => 1,
});
}
# decrypt
my $Cryped = $Entity->parts(1)->as_string;
# Encrypt it
my %Decrypt = $Self->{CryptObject}->Decrypt(
Message => $Cryped,
);
if ($Decrypt{Successful}) {
$Entity = $parser->parse_data($Decrypt{Data});
my $Head = $Entity->head();
$Head->unfold();
$Head->combine('Content-Type');
$ContentType = $Head->get('Content-Type');
use Kernel::System::EmailParser;
my $ParserObject = Kernel::System::EmailParser->new(
%{$Self},
Entity => $Entity,
);
my $Body = $ParserObject->GetMessageBody();
# updated article body
$Self->{TicketObject}->ArticleUpdate(
TicketID => $Param{Article}->{TicketID},
ArticleID => $Self->{ArticleID},
Key => 'Body',
Value => $Body,
UserID => $Self->{UserID},
);
# delete crypted attachments
$Self->{TicketObject}->ArticleDeleteAttachment(
ArticleID => $Self->{ArticleID},
UserID => $Self->{UserID},
);
# write attachments to the storage
foreach my $Attachment ($ParserObject->GetAttachments()) {
$Self->{TicketObject}->ArticleWriteAttachment(
Content => $Attachment->{Content},
Filename => $Attachment->{Filename},
ContentType => $Attachment->{ContentType},
ArticleID => $Self->{ArticleID},
UserID => $Self->{UserID},
);
}
push (@Return, {
Key => 'Crypted',
Value => $Decrypt{Message},
%Decrypt,
},
);
}
else {
push (@Return, {
Key => 'Crypted',
Value => $Decrypt{Message},
%Decrypt,
},
);
}
}
if ($ContentType && $ContentType =~ /multipart\/signed/i && $ContentType =~ /application\/pgp/i) {
my $SignedText = $Entity->parts(0)->as_string();
my $SignatureText = $Entity->parts(1)->body_as_string();
# according to RFC3156 all line endings MUST be CR/LF
$SignedText =~ s/\x0A/\x0D\x0A/g;
$SignedText =~ s/\x0D+/\x0D/g;
%SignCheck = $Self->{CryptObject}->Verify(
Message => $SignedText,
Sign => $SignatureText,
);
}
}
if (%SignCheck) {
# return result
push (@Return, {
Key => 'Signed',
Value => $SignCheck{Message},
%SignCheck,
},
);
}
return @Return;
}
sub Filter {
my $Self = shift;
my %Param = @_;
# remove signature if one is found
if ($Self->{Result}->{SignatureFound}) {
# remove pgp begin signed message
$Param{Article}->{Body} =~ s/^-----BEGIN\sPGP\sSIGNED\sMESSAGE-----.+?Hash:\s.+?$//sm;
# remove pgp inline sign
$Param{Article}->{Body} =~ s/^-----BEGIN\sPGP\sSIGNATURE-----.+?-----END\sPGP\sSIGNATURE-----//sm;
}
}
1;