# --
# 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;
