This commit is contained in:
2024-10-14 00:08:40 +02:00
parent dbfba56f66
commit 1462d52e13
4572 changed files with 2658864 additions and 0 deletions

View File

@@ -0,0 +1,157 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::DestQueue;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
'Kernel::System::Queue',
'Kernel::System::SystemAddress',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get parser object
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject!";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub GetQueueID {
my ( $Self, %Param ) = @_;
# get email headers
my %GetParam = %{ $Param{Params} };
# check possible to, cc and resent-to emailaddresses
my $Recipient = '';
RECIPIENT:
for my $Key (qw(Resent-To Envelope-To To Cc Delivered-To X-Original-To)) {
next RECIPIENT if !$GetParam{$Key};
if ($Recipient) {
$Recipient .= ', ';
}
$Recipient .= $GetParam{$Key};
}
# get system address object
my $SystemAddressObject = $Kernel::OM->Get('Kernel::System::SystemAddress');
# get addresses
my @EmailAddresses = $Self->{ParserObject}->SplitAddressLine( Line => $Recipient );
# check addresses
EMAIL:
for my $Email (@EmailAddresses) {
next EMAIL if !$Email;
my $Address = $Self->{ParserObject}->GetEmailAddress( Email => $Email );
next EMAIL if !$Address;
# lookup queue id if recipiend address
my $QueueID = $SystemAddressObject->SystemAddressQueueID(
Address => $Address,
);
if ($QueueID) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => ref($Self),
Value => "Match email: $Email to QueueID $QueueID (MessageID:$GetParam{'Message-ID'})!",
);
return $QueueID;
}
# Address/Email not matched with any that is configured in the system
# or any error occured while checking it.
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => ref($Self),
Value => "No match for email: $Email (MessageID:$GetParam{'Message-ID'})!",
);
}
# If we get here means that none of the addresses in the message is defined as a system address
# or an error occured while checking it.
my $Queue = $Kernel::OM->Get('Kernel::Config')->Get('PostmasterDefaultQueue');
my $QueueID = $Kernel::OM->Get('Kernel::System::Queue')->QueueLookup(
Queue => $Queue,
);
if ($QueueID) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => ref($Self),
Value => "MessageID:$GetParam{'Message-ID'} to 'PostmasterDefaultQueue' ( QueueID:${QueueID} ).",
);
return $QueueID;
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => ref($Self),
Value => "Couldn't get QueueID for 'PostmasterDefaultQueue' (${Queue}) !",
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => ref($Self),
Value => "MessageID:$GetParam{'Message-ID'} to QueueID:1!",
);
return 1;
}
sub GetTrustedQueueID {
my ( $Self, %Param ) = @_;
# get email headers
my %GetParam = %{ $Param{Params} };
return if !$GetParam{'X-OTRS-Queue'};
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::DestQueue',
Value => "Existing X-OTRS-Queue header: $GetParam{'X-OTRS-Queue'} (MessageID:$GetParam{'Message-ID'})!",
);
# get dest queue
return $Kernel::OM->Get('Kernel::System::Queue')->QueueLookup(
Queue => $GetParam{'X-OTRS-Queue'},
);
}
1;

View File

@@ -0,0 +1,246 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::Filter;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::System::DB',
'Kernel::System::Log',
);
=head1 NAME
Kernel::System::PostMaster::Filter
=head1 DESCRIPTION
All postmaster database filters
=head1 PUBLIC INTERFACE
=head2 new()
Don't use the constructor directly, use the ObjectManager instead:
my $PMFilterObject = $Kernel::OM->Get('Kernel::System::PostMaster::Filter');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
=head2 FilterList()
get all filter
my %FilterList = $PMFilterObject->FilterList();
=cut
sub FilterList {
my ( $Self, %Param ) = @_;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
return if !$DBObject->Prepare(
SQL => 'SELECT f_name FROM postmaster_filter',
);
my %Data;
while ( my @Row = $DBObject->FetchrowArray() ) {
$Data{ $Row[0] } = $Row[0];
}
return %Data;
}
=head2 FilterAdd()
add a filter
$PMFilterObject->FilterAdd(
Name => 'some name',
StopAfterMatch => 0,
Match = [
{
Key => 'Subject',
Value => '^ADV: 123',
},
...
],
Not = [
{
Key => 'Subject',
Value => '1',
},
...
],
Set = [
{
Key => 'X-OTRS-Queue',
Value => 'Some::Queue',
},
...
],
);
=cut
sub FilterAdd {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(Name StopAfterMatch Match Set)) {
if ( !defined $Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
my @Not = @{ $Param{Not} || [] };
for my $Type (qw(Match Set)) {
my @Data = @{ $Param{$Type} };
for my $Index ( 0 .. ( scalar @Data ) - 1 ) {
return if !$DBObject->Do(
SQL =>
'INSERT INTO postmaster_filter (f_name, f_stop, f_type, f_key, f_value, f_not)'
. ' VALUES (?, ?, ?, ?, ?, ?)',
Bind => [
\$Param{Name}, \$Param{StopAfterMatch}, \$Type,
\$Data[$Index]->{Key}, \$Data[$Index]->{Value}, \$Not[$Index]->{Value},
],
);
}
}
return 1;
}
=head2 FilterDelete()
delete a filter
$PMFilterObject->FilterDelete(
Name => '123',
);
=cut
sub FilterDelete {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(Name)) {
if ( !defined $Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
return if !$DBObject->Do(
SQL => 'DELETE FROM postmaster_filter WHERE f_name = ?',
Bind => [ \$Param{Name} ],
);
return 1;
}
=head2 FilterGet()
get filter properties, returns HASH ref Match and Set
my %Data = $PMFilterObject->FilterGet(
Name => '132',
);
=cut
sub FilterGet {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(Name)) {
if ( !defined $Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
return if !$DBObject->Prepare(
SQL =>
'SELECT f_type, f_key, f_value, f_name, f_stop, f_not'
. ' FROM postmaster_filter'
. ' WHERE f_name = ?'
. ' ORDER BY f_key, f_value',
Bind => [ \$Param{Name} ],
);
my %Data;
while ( my @Row = $DBObject->FetchrowArray() ) {
push @{ $Data{ $Row[0] } }, {
Key => $Row[1],
Value => $Row[2],
};
$Data{Name} = $Row[3];
$Data{StopAfterMatch} = $Row[4];
if ( $Row[0] eq 'Match' ) {
push @{ $Data{Not} }, {
Key => $Row[1],
Value => $Row[5],
};
}
}
return %Data;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
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 L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut

View File

@@ -0,0 +1,108 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::Filter::CMD;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get parser object
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject!";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# get config options
my %Config;
my @Set;
if ( $Param{JobConfig} && ref( $Param{JobConfig} ) eq 'HASH' ) {
%Config = %{ $Param{JobConfig} };
if ( IsArrayRefWithData( $Config{Set} ) ) {
@Set = @{ $Config{Set} };
}
elsif ( IsHashRefWithData( $Config{Set} ) ) {
for my $Key ( sort keys %{ $Config{Set} } ) {
push @Set, {
Key => $Key,
Value => $Config{Set}->{$Key},
};
}
}
}
# check CMD config param
if ( !$Config{CMD} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::CMD',
Value => "Need CMD config option in PostMaster::PreFilterModule job!",
);
return;
}
# execute prog
my $TmpFile = $Kernel::OM->Get('Kernel::Config')->Get('TempDir') . "/PostMaster.Filter.CMD.$$";
## no critic
if ( open my $Prog, '|-', "$Config{CMD} > $TmpFile" ) {
## use critic
print $Prog $Self->{ParserObject}->GetPlainEmail();
close $Prog;
}
if ( -s $TmpFile ) {
open my $In, '<', $TmpFile; ## no critic
my $Ret = <$In>;
close $In;
# set new params
for my $SetItem (@Set) {
my $Key = $SetItem->{Key};
my $Value = $SetItem->{Value};
$Param{GetParam}->{$Key} = $Value;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::Filter::CMD',
Value =>
"Set param '$Key' to '$Value' because of '$Ret' (Message-ID: $Param{GetParam}->{'Message-ID'})",
);
}
}
unlink $TmpFile;
return 1;
}
1;

View File

@@ -0,0 +1,266 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::Filter::Decrypt;
use strict;
use warnings;
use Kernel::System::EmailParser;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
'Kernel::System::Crypt::PGP',
'Kernel::System::Crypt::SMIME',
);
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
# Get parser object.
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject!";
# get communication log object and MessageID
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# Check needed stuff.
for my $Needed (qw(JobConfig GetParam)) {
if ( !$Param{$Needed} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::Decrypt',
Value => "Need $Needed!",
);
return;
}
}
# Try to get message & encryption method.
my $Message;
my $ContentType;
my $EncryptionMethod = '';
if ( $Param{GetParam}->{Body} =~ /\A[\s\n]*^-----BEGIN PGP MESSAGE-----/m ) {
$Message = $Param{GetParam}->{Body};
$ContentType = $Param{GetParam}->{'Content-Type'} || '';
$EncryptionMethod = 'PGP';
}
elsif ( $Param{GetParam}->{'Content-Type'} =~ /application\/(x-pkcs7|pkcs7)-mime/i ) {
$EncryptionMethod = 'SMIME';
$ContentType = $Param{GetParam}->{'Content-Type'} || '';
}
else {
CONTENT:
for my $Content ( @{ $Param{GetParam}->{Attachment} } ) {
if ( $Content->{Content} =~ /\A[\s\n]*^-----BEGIN PGP MESSAGE-----/m ) {
$Message = $Content->{Content};
$ContentType = $Content->{ContentType} || '';
$EncryptionMethod = 'PGP';
last CONTENT;
}
elsif ( $Content->{Content} =~ /^-----BEGIN PKCS7-----/ ) {
$Message = $Content->{Content};
$ContentType = $Param{GetParam}->{'Content-Type'} || '';
$EncryptionMethod = 'SMIME';
last CONTENT;
}
}
}
if ( $EncryptionMethod eq 'PGP' ) {
# Try to decrypt body with PGP.
$Param{GetParam}->{'X-OTRS-BodyDecrypted'} = $Self->_DecryptPGP(
Body => $Message,
ContentType => $ContentType,
%Param
) || '';
# Return PGP decrypted content if encryption is PGP.
return $Param{GetParam}->{'X-OTRS-BodyDecrypted'} if $Param{GetParam}->{'X-OTRS-BodyDecrypted'};
}
elsif ( $EncryptionMethod eq 'SMIME' ) {
# Try to decrypt body with SMIME.
$Param{GetParam}->{'X-OTRS-BodyDecrypted'} = $Self->_DecryptSMIME(
Body => $Self->{ParserObject}->{Email}->as_string(),
ContentType => $ContentType,
%Param
) || '';
# Return SMIME decrypted content if encryption is SMIME
return $Param{GetParam}->{'X-OTRS-BodyDecrypted'} if $Param{GetParam}->{'X-OTRS-BodyDecrypted'};
}
else {
$Param{GetParam}->{'X-OTRS-BodyDecrypted'} = '';
}
return 1;
}
sub _DecryptPGP {
my ( $Self, %Param ) = @_;
my $DecryptBody = $Param{Body} || '';
my $ContentType = $Param{ContentType} || '';
# Check if PGP is active
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
if ( !$ConfigObject->Get('PGP') ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::Decrypt',
Value => "PGP is not activated",
);
return;
}
# Check for PGP encryption
if (
$DecryptBody !~ m{\A[\s\n]*^-----BEGIN PGP MESSAGE-----}i
&& $ContentType !~ m{application/pgp}i
)
{
return;
}
# PGP crypt object
my $CryptObject = $Kernel::OM->Get('Kernel::System::Crypt::PGP');
if ( !$CryptObject ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::Decrypt',
Value => "Not possible to create crypt object",
);
return;
}
# Try to decrypt.
my %Decrypt = $CryptObject->Decrypt( Message => $DecryptBody );
return if !$Decrypt{Successful};
my $ParserObject = Kernel::System::EmailParser->new( %{$Self}, Email => $Decrypt{Data} );
$DecryptBody = $ParserObject->GetMessageBody();
if ( $Param{JobConfig}->{StoreDecryptedBody} ) {
$Param{GetParam}->{Body} = $DecryptBody;
}
# Return content if successful
return $DecryptBody;
}
sub _DecryptSMIME {
my ( $Self, %Param ) = @_;
my $DecryptBody = $Param{Body} || '';
my $ContentType = $Param{ContentType} || '';
# Check if SMIME is active
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
if ( !$ConfigObject->Get('SMIME') ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::Decrypt',
Value => "SMIME is not activated",
);
return;
}
# Check for SMIME encryption
if (
$DecryptBody !~ m{^-----BEGIN PKCS7-----}i
&& $ContentType !~ m{application/(x-pkcs7|pkcs7)}i
)
{
return;
}
# SMIME crypt object
my $CryptObject = $Kernel::OM->Get('Kernel::System::Crypt::SMIME');
if ( !$CryptObject ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::Decrypt',
Value => "Not possible to create crypt object",
);
return;
}
my $IncomingMailAddress;
for my $Email (qw(From)) {
my @EmailAddressOnField = $Self->{ParserObject}->SplitAddressLine(
Line => $Self->{ParserObject}->GetParam( WHAT => $Email ),
);
for my $EmailAddress (@EmailAddressOnField) {
$IncomingMailAddress = $Self->{ParserObject}->GetEmailAddress(
Email => $EmailAddress,
);
}
}
my @PrivateList = $CryptObject->PrivateSearch(
Search => $IncomingMailAddress,
);
my %Decrypt;
PRIVATESEARCH:
for my $PrivateFilename (@PrivateList) {
# Try to decrypt
%Decrypt = $CryptObject->Decrypt(
Message => $DecryptBody,
SearchingNeededKey => 1,
Filename => $PrivateFilename->{Filename},
);
# Stop loop if successful
last PRIVATESEARCH if ( $Decrypt{Successful} );
}
return if !$Decrypt{Successful};
my $ParserObject = Kernel::System::EmailParser->new(
%{$Self},
Email => $Decrypt{Data},
);
$DecryptBody = $ParserObject->GetMessageBody();
if ( $Param{JobConfig}->{StoreDecryptedBody} ) {
$Param{GetParam}->{Body} = $DecryptBody;
$Param{GetParam}->{'Content-Type'} = 'text/html';
}
# Return content if successful
return $DecryptBody;
}
1;

View File

@@ -0,0 +1,69 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::Filter::DetectAttachment;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::System::Log',
);
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject";
# Get communication log object and MessageID.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# Check needed stuff.
for my $Needed (qw(JobConfig GetParam)) {
if ( !$Param{$Needed} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::DetectAttachment',
Value => "Need $Needed!",
);
return;
}
}
# Get attachments.
my @Attachments = $Self->{ParserObject}->GetAttachments();
my $AttachmentCount = 0;
for my $Attachment (@Attachments) {
if (
defined $Attachment->{ContentDisposition}
&& length $Attachment->{ContentDisposition}
)
{
$AttachmentCount++;
}
}
$Param{GetParam}->{'X-OTRS-AttachmentExists'} = ( $AttachmentCount ? 'yes' : 'no' );
$Param{GetParam}->{'X-OTRS-AttachmentCount'} = $AttachmentCount;
return 1;
}
1;

View File

@@ -0,0 +1,82 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::Filter::DetectBounceEmail;
use strict;
use warnings;
use Sisimai::Data;
use Sisimai::Message;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# Ensure that the flag X-OTRS-Bounce doesn't exist if we didn't analysed it yet.
delete $Param{GetParam}->{'X-OTRS-Bounce'};
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => ref($Self),
Value => 'Checking if is a Bounce e-mail.',
);
my $BounceMessage = Sisimai::Message->new( data => $Self->{ParserObject}->GetPlainEmail() );
return 1 if !$BounceMessage;
my $BounceData = Sisimai::Data->make( data => $BounceMessage );
return 1 if !$BounceData || !@{$BounceData};
my $MessageID = $BounceData->[0]->messageid();
return 1 if !$MessageID;
$MessageID = sprintf '<%s>', $MessageID;
$Param{GetParam}->{'X-OTRS-Bounce'} = 1;
$Param{GetParam}->{'X-OTRS-Bounce-OriginalMessageID'} = $MessageID;
$Param{GetParam}->{'X-OTRS-Bounce-ErrorMessage'} = $Param{GetParam}->{Body};
$Param{GetParam}->{'X-OTRS-Loop'} = 1;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => ref($Self),
Value => sprintf(
'Detected Bounce for e-mail "%s"',
$MessageID,
),
);
return 1;
}
1;

View File

@@ -0,0 +1,243 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::Filter::ExternalTicketNumberRecognition;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
'Kernel::System::State',
'Kernel::System::Ticket',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get communication log object and MessageID
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# checking mandatory configuration options
for my $Option (qw(NumberRegExp DynamicFieldName SenderType IsVisibleForCustomer)) {
if ( !defined $Param{JobConfig}->{$Option} && !$Param{JobConfig}->{$Option} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::ExternalTicketNumberRecognition',
Value => "Missing configuration for $Option for postmaster filter.",
);
return 1;
}
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::ExternalTicketNumberRecognition',
Value => "Starting filter '$Param{JobConfig}->{Name}'",
);
# check if sender is of interest
return 1 if !$Param{GetParam}->{From};
if ( defined $Param{JobConfig}->{FromAddressRegExp} && $Param{JobConfig}->{FromAddressRegExp} )
{
if ( $Param{GetParam}->{From} !~ /$Param{JobConfig}->{FromAddressRegExp}/i ) {
return 1;
}
}
my $NumberRegExp = $Param{JobConfig}->{NumberRegExp};
# search in the subject
if ( $Param{JobConfig}->{SearchInSubject} ) {
# try to get external ticket number from email subject
my @SubjectLines = split /\n/, $Param{GetParam}->{Subject};
LINE:
for my $Line (@SubjectLines) {
if ( $Line =~ m{$NumberRegExp}ms ) {
$Self->{Number} = $1;
last LINE;
}
}
if ( $Self->{Number} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::ExternalTicketNumberRecognition',
Value => "Found number: '$Self->{Number}' in subject",
);
}
else {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::ExternalTicketNumberRecognition',
Value => "No number found in subject: '" . join( '', @SubjectLines ) . "'",
);
}
}
# search in the body
if ( $Param{JobConfig}->{SearchInBody} ) {
# split the body into separate lines
my @BodyLines = split /\n/, $Param{GetParam}->{Body};
# traverse lines and return first match
LINE:
for my $Line (@BodyLines) {
if ( $Line =~ m{$NumberRegExp}ms ) {
# get the found element value
$Self->{Number} = $1;
last LINE;
}
}
}
# we need to have found an external number to proceed.
if ( !$Self->{Number} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::ExternalTicketNumberRecognition',
Value => "Could not find external ticket number => Ignoring",
);
return 1;
}
else {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::ExternalTicketNumberRecognition',
Value => "Found number $Self->{Number}",
);
}
# is there a ticket for this ticket number?
my %Query = (
Result => 'ARRAY',
Limit => 1,
UserID => 1,
);
# check if we should only find the ticket number in tickets with a given state type
if ( defined $Param{JobConfig}->{TicketStateTypes} && $Param{JobConfig}->{TicketStateTypes} ) {
$Query{StateTypeIDs} = [];
my @StateTypeIDs;
# if StateTypes contains semicolons, use that for split,
# otherwise split on spaces (for compat)
if ( $Param{JobConfig}->{TicketStateTypes} =~ m{;} ) {
@StateTypeIDs = split ';', $Param{JobConfig}->{TicketStateTypes};
}
else {
@StateTypeIDs = split ' ', $Param{JobConfig}->{TicketStateTypes};
}
STATETYPE:
for my $StateType (@StateTypeIDs) {
next STATETYPE if !$StateType;
my $StateTypeID = $Kernel::OM->Get('Kernel::System::State')->StateTypeLookup(
StateType => $StateType,
);
if ($StateTypeID) {
push @{ $Query{StateTypeIDs} }, $StateTypeID;
}
}
}
# dynamic field search condition
$Query{ 'DynamicField_' . $Param{JobConfig}->{'DynamicFieldName'} } = {
Equals => $Self->{Number},
};
# get ticket object
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
# search tickets
my @TicketIDs = $TicketObject->TicketSearch(%Query);
# get the first and only ticket id
my $TicketID = shift @TicketIDs;
# ok, found ticket to deal with
if ($TicketID) {
# get ticket number
my $TicketNumber = $TicketObject->TicketNumberLookup(
TicketID => $TicketID,
UserID => 1,
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::ExternalTicketNumberRecognition',
Value => "Found ticket $TicketNumber open for external number $Self->{Number}. Updating.",
);
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# build subject
my $TicketHook = $ConfigObject->Get('Ticket::Hook');
my $TicketHookDivider = $ConfigObject->Get('Ticket::HookDivider');
$Param{GetParam}->{Subject} .= " [$TicketHook$TicketHookDivider$TicketNumber]";
# set sender type and article type.
$Param{GetParam}->{'X-OTRS-FollowUp-SenderType'} = $Param{JobConfig}->{SenderType};
$Param{GetParam}->{'X-OTRS-FollowUp-IsVisibleForCustomer'} = $Param{JobConfig}->{IsVisibleForCustomer};
# also set these parameters. It could be that the follow up is rejected by Reject.pm
# (follow-ups not allowed), but the original article will still be attached to the ticket.
$Param{GetParam}->{'X-OTRS-SenderType'} = $Param{JobConfig}->{SenderType};
$Param{GetParam}->{'X-OTRS-IsVisibleForCustomer'} = $Param{JobConfig}->{IsVisibleForCustomer};
}
else {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::ExternalTicketNumberRecognition',
Value => "Creating new ticket for external ticket '$Self->{Number}'",
);
# get the dynamic field name and description from JobConfig, set as headers
my $TicketDynamicFieldName = $Param{JobConfig}->{'DynamicFieldName'};
$Param{GetParam}->{ 'X-OTRS-DynamicField-' . $TicketDynamicFieldName } = $Self->{Number};
# set sender type and article type
$Param{GetParam}->{'X-OTRS-SenderType'} = $Param{JobConfig}->{SenderType};
$Param{GetParam}->{'X-OTRS-IsVisibleForCustomer'} = $Param{JobConfig}->{IsVisibleForCustomer};
}
return 1;
}
1;

View File

@@ -0,0 +1,163 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::Filter::FollowUpArticleVisibilityCheck;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::CustomerUser',
'Kernel::System::Ticket',
'Kernel::System::Ticket::Article',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get parser object
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject!";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# This filter is not needed if there is no TicketID.
return 1 if !$Param{TicketID};
# check needed stuff
for (qw(JobConfig GetParam UserID)) {
if ( !$Param{$_} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::FollowUpArticleVisibilityCheck',
Value => "Need $_!",
);
return;
}
}
# Only run if we have a follow-up article with SenderType 'customer'.
# It could be that follow-ups have a different SenderType like 'system' for
# automatic notifications. In these cases there is no need to hide them.
# See also bug#10182 for details.
if (
!$Param{GetParam}->{'X-OTRS-FollowUp-SenderType'}
|| $Param{GetParam}->{'X-OTRS-FollowUp-SenderType'} ne 'customer'
)
{
return 1;
}
my %Ticket = $Kernel::OM->Get('Kernel::System::Ticket')->TicketGet(
TicketID => $Param{TicketID},
UserID => $Param{UserID},
);
# Check if it is a known customer, otherwise use email address from CustomerUserID field of the ticket.
my %CustomerData = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet(
User => $Ticket{CustomerUserID},
);
my $CustomerEmailAddress = $CustomerData{UserEmail} || $Ticket{CustomerUserID};
# Email sender address
my $SenderAddress = $Param{GetParam}->{'X-Sender'};
# Email Reply-To address for forwarded emails
my $ReplyToAddress;
if ( $Param{GetParam}->{ReplyTo} ) {
$ReplyToAddress = $Self->{ParserObject}->GetEmailAddress(
Email => $Param{GetParam}->{ReplyTo},
);
}
# check if current sender is customer (do nothing)
if ( $CustomerEmailAddress && $SenderAddress ) {
return 1 if lc $CustomerEmailAddress eq lc $SenderAddress;
}
my @References = $Self->{ParserObject}->GetReferences();
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
# Get all internal email articles sent by agents.
my @MetaArticleIndex = $ArticleObject->ArticleList(
TicketID => $Param{TicketID},
CommunicationChannel => 'Email',
SenderType => 'agent',
IsVisibleForCustomer => 0,
);
return 1 if !@MetaArticleIndex;
my $ArticleBackendObject = $ArticleObject->BackendForChannel( ChannelName => 'Email' );
# check if current sender got an internal forward
my $IsInternalForward;
ARTICLE:
for my $MetaArticle ( reverse @MetaArticleIndex ) {
my $Article = {
$ArticleBackendObject->ArticleGet( %{$MetaArticle} )
};
# check recipients
next ARTICLE if !$Article->{To};
# check based on recipient addresses of the article
my @ToEmailAddresses = $Self->{ParserObject}->SplitAddressLine(
Line => $Article->{To},
);
my @CcEmailAddresses = $Self->{ParserObject}->SplitAddressLine(
Line => $Article->{Cc},
);
my @EmailAdresses = ( @ToEmailAddresses, @CcEmailAddresses );
EMAIL:
for my $Email (@EmailAdresses) {
my $Recipient = $Self->{ParserObject}->GetEmailAddress(
Email => $Email,
);
if ( lc $Recipient eq lc $SenderAddress ) {
$IsInternalForward = 1;
last ARTICLE;
}
if ( $ReplyToAddress && lc $Recipient eq lc $ReplyToAddress ) {
$IsInternalForward = 1;
last ARTICLE;
}
}
# check based on Message-ID of the article
for my $Reference (@References) {
if ( $Article->{MessageID} && $Article->{MessageID} eq $Reference ) {
$IsInternalForward = 1;
last ARTICLE;
}
}
}
return 1 if !$IsInternalForward;
$Param{GetParam}->{'X-OTRS-FollowUp-IsVisibleForCustomer'} = $Param{JobConfig}->{IsVisibleForCustomer} // 0;
$Param{GetParam}->{'X-OTRS-FollowUp-SenderType'} = $Param{JobConfig}->{SenderType} || 'customer';
return 1;
}
1;

View File

@@ -0,0 +1,200 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::Filter::Match;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get parser object
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject!";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(JobConfig GetParam)) {
if ( !$Param{$_} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::Match',
Value => "Need $_!",
);
return;
}
}
# get config options
my %Config;
my @Match;
my @Set;
my $StopAfterMatch;
if ( $Param{JobConfig} && ref $Param{JobConfig} eq 'HASH' ) {
%Config = %{ $Param{JobConfig} };
if ( IsArrayRefWithData( $Config{Match} ) ) {
@Match = @{ $Config{Match} };
}
elsif ( IsHashRefWithData( $Config{Match} ) ) {
for my $Key ( sort keys %{ $Config{Match} } ) {
push @Match, {
Key => $Key,
Value => $Config{Match}->{$Key},
};
}
}
if ( IsArrayRefWithData( $Config{Set} ) ) {
@Set = @{ $Config{Set} };
}
elsif ( IsHashRefWithData( $Config{Set} ) ) {
for my $Key ( sort keys %{ $Config{Set} } ) {
push @Set, {
Key => $Key,
Value => $Config{Set}->{$Key},
};
}
}
$StopAfterMatch = $Config{StopAfterMatch} || 0;
}
my $Prefix = '';
if ( $Config{Name} ) {
$Prefix = "Filter: '$Config{Name}' ";
}
# match 'Match => ???' stuff
my $Matched = '';
my $MatchedNot = 0;
my $MatchedResult = '';
for my $Index ( 0 .. ( scalar @Match ) - 1 ) {
my $Key = $Match[$Index]->{Key};
my $Value = $Match[$Index]->{Value};
# match only email addresses
if ( $Param{GetParam}->{$Key} && $Value =~ /^EMAILADDRESS:(.*)$/ ) {
my $SearchEmail = $1;
my @EmailAddresses = $Self->{ParserObject}->SplitAddressLine(
Line => $Param{GetParam}->{$Key},
);
my $LocalMatched;
RECIPIENTS:
for my $Recipients (@EmailAddresses) {
my $Email = $Self->{ParserObject}->GetEmailAddress( Email => $Recipients );
if ( $Email =~ /^$SearchEmail$/i ) {
$LocalMatched = 1;
if ($SearchEmail) {
$MatchedResult = $SearchEmail;
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::Match',
Value => "$Prefix'$Param{GetParam}->{$Key}' =~ /$Value/i matched!",
);
last RECIPIENTS;
}
}
if ( !$LocalMatched ) {
$MatchedNot = 1;
}
else {
$Matched = 1;
}
}
# match string
elsif ( $Param{GetParam}->{$Key} && $Param{GetParam}->{$Key} =~ /$Value/i ) {
# don't lose older match values if more than one header is
# used for matching.
$Matched = 1;
if ($1) {
$MatchedResult = $1;
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::Match',
Value => "$Prefix'$Param{GetParam}->{$Key}' =~ /$Value/i matched!",
);
}
else {
$MatchedNot = 1;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::Match',
Value => "$Prefix'$Param{GetParam}->{$Key}' =~ /$Value/i matched NOT!",
);
}
}
# should I ignore the incoming mail?
if ( $Matched && !$MatchedNot ) {
for my $SetItem (@Set) {
my $Key = $SetItem->{Key};
my $Value = $SetItem->{Value};
$Value =~ s/\[\*\*\*\]/$MatchedResult/;
$Param{GetParam}->{$Key} = $Value;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::Filter::Match',
Value => $Prefix . "Set param '$Key' to '$Value' (Message-ID: $Param{GetParam}->{'Message-ID'})",
);
}
# stop after match
if ($StopAfterMatch) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::Filter::Match',
Value => $Prefix
. "Stopped filter processing because of used 'StopAfterMatch' (Message-ID: $Param{GetParam}->{'Message-ID'})",
);
return 1;
}
}
return 1;
}
1;

View File

@@ -0,0 +1,217 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::Filter::MatchDBSource;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::PostMaster::Filter',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get parser object
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject!";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(JobConfig GetParam)) {
if ( !$Param{$_} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::MatchDBSource',
Value => "Need $_!",
);
return;
}
}
# get postmaster filter object
my $PostMasterFilter = $Kernel::OM->Get('Kernel::System::PostMaster::Filter');
# get all db filters
my %JobList = $PostMasterFilter->FilterList();
for ( sort keys %JobList ) {
my %NamedCaptures;
# get config options
my %Config = $PostMasterFilter->FilterGet( Name => $_ );
my @Match;
my @Set;
if ( $Config{Match} ) {
@Match = @{ $Config{Match} };
}
if ( $Config{Set} ) {
@Set = @{ $Config{Set} };
}
my $StopAfterMatch = $Config{StopAfterMatch} || 0;
my $Prefix = '';
if ( $Config{Name} ) {
$Prefix = "Filter: '$Config{Name}' ";
}
# match 'Match => ???' stuff
my $Matched = 0; # Numbers are required because of the bitwise or in the negation.
my $MatchedNot = 0;
my $MatchedResult = '';
for my $Index ( 0 .. ( scalar @Match ) - 1 ) {
my $Key = $Match[$Index]->{Key};
my $Value = $Match[$Index]->{Value};
# match only email addresses
if ( defined $Param{GetParam}->{$Key} && $Value =~ /^EMAILADDRESS:(.*)$/ ) {
my $SearchEmail = $1;
my @EmailAddresses = $Self->{ParserObject}->SplitAddressLine(
Line => $Param{GetParam}->{$Key},
);
my $LocalMatched = 0;
RECIPIENT:
for my $Recipients (@EmailAddresses) {
my $Email = $Self->{ParserObject}->GetEmailAddress( Email => $Recipients );
next RECIPIENT if !$Email;
if ( $Email =~ /^$SearchEmail$/i ) {
$LocalMatched = 1;
if ($SearchEmail) {
$MatchedResult = $SearchEmail;
$NamedCaptures{email} = $SearchEmail;
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::MatchDBSource',
Value => "$Prefix'$Param{GetParam}->{$Key}' =~ /$Value/i matched!",
);
last RECIPIENT;
}
}
# Switch LocalMatched if Config has a negation.
if ( $Config{Not}->[$Index]->{Value} ) {
$LocalMatched = !$LocalMatched;
}
if ( !$LocalMatched ) {
$MatchedNot = 1;
}
else {
$Matched = 1;
}
}
# match string
elsif (
defined $Param{GetParam}->{$Key} &&
(
( !$Config{Not}->[$Index]->{Value} && $Param{GetParam}->{$Key} =~ m{$Value}i )
||
( $Config{Not}->[$Index]->{Value} && $Param{GetParam}->{$Key} !~ m{$Value}i )
)
)
{
# don't lose older match values if more than one header is
# used for matching.
$Matched = 1;
if ($1) {
$MatchedResult = $1;
}
if (%+) {
my @Keys = keys %+;
my @Values = values %+;
@NamedCaptures{@Keys} = @Values;
}
my $Op = $Config{Not}->[$Index]->{Value} ? '!' : "=";
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::MatchDBSource',
Value => "successful $Prefix'$Param{GetParam}->{$Key}' $Op~ /$Value/i !",
);
}
else {
$MatchedNot = 1;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::MatchDBSource',
Value => "$Prefix'$Param{GetParam}->{$Key}' =~ /$Value/i matched NOT!",
);
}
}
# should I ignore the incoming mail?
if ( $Matched && !$MatchedNot ) {
for my $SetItem (@Set) {
my $Key = $SetItem->{Key};
my $Value = $SetItem->{Value};
$Value =~ s/\[\*\*\*\]/$MatchedResult/;
$Value =~ s/\[\*\* \\(\w+) \*\*\]/$NamedCaptures{$1}/xmsg;
$Param{GetParam}->{$Key} = $Value;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::Filter::MatchDBSource',
Value => $Prefix
. "Set param '$Key' to '$Value' (Message-ID: $Param{GetParam}->{'Message-ID'}) ",
);
}
# stop after match
if ($StopAfterMatch) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::Filter::MatchDBSource',
Value => $Prefix
. "Stopped filter processing because of used 'StopAfterMatch' (Message-ID: $Param{GetParam}->{'Message-ID'}) ",
);
return 1;
}
}
}
return 1;
}
1;

View File

@@ -0,0 +1,183 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::Filter::NewTicketReject;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
'Kernel::System::Email',
'Kernel::System::Ticket',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(JobConfig GetParam)) {
if ( !$Param{$_} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::NewTicketReject',
Value => "Need $_!",
);
return;
}
}
# get config options
my %Config;
my @Match;
my @Set;
if ( $Param{JobConfig} && ref $Param{JobConfig} eq 'HASH' ) {
%Config = %{ $Param{JobConfig} };
if ( IsArrayRefWithData( $Config{Match} ) ) {
@Match = @{ $Config{Match} };
}
elsif ( IsHashRefWithData( $Config{Match} ) ) {
for my $Key ( sort keys %{ $Config{Match} } ) {
push @Match, {
Key => $Key,
Value => $Config{Match}->{$Key},
};
}
}
if ( IsArrayRefWithData( $Config{Set} ) ) {
@Set = @{ $Config{Set} };
}
elsif ( IsHashRefWithData( $Config{Set} ) ) {
for my $Key ( sort keys %{ $Config{Set} } ) {
push @Set, {
Key => $Key,
Value => $Config{Set}->{$Key},
};
}
}
}
# match 'Match => ???' stuff
my $Matched = '';
my $MatchedNot = 0;
for my $Index ( 0 .. ( scalar @Match ) - 1 ) {
my $Key = $Match[$Index]->{Key};
my $Value = $Match[$Index]->{Value};
if ( $Param{GetParam}->{$Key} && $Param{GetParam}->{$Key} =~ /$Value/i ) {
$Matched = $1 || '1';
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::NewTicketReject',
Value => "'$Param{GetParam}->{$Key}' =~ /$Value/i matched!",
);
}
else {
$MatchedNot = 1;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Filter::NewTicketReject',
Value => "'$Param{GetParam}->{$Key}' =~ /$Value/i matched NOT!",
);
}
}
if ( $Matched && !$MatchedNot ) {
# get ticket object
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
# check if new ticket
my $Tn = $TicketObject->GetTNByString( $Param{GetParam}->{Subject} );
return 1 if $Tn && $TicketObject->TicketCheckNumber( Tn => $Tn );
# set attributes if ticket is created
for my $SetItem (@Set) {
my $Key = $SetItem->{Key};
my $Value = $SetItem->{Value};
$Param{GetParam}->{$Key} = $Value;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::Filter::NewTicketReject',
Value => "Set param '$Key' to '$Value' (Message-ID: $Param{GetParam}->{'Message-ID'})",
);
}
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# send bounce mail
my $Subject = $ConfigObject->Get(
'PostMaster::PreFilterModule::NewTicketReject::Subject'
);
my $Body = $ConfigObject->Get(
'PostMaster::PreFilterModule::NewTicketReject::Body'
);
my $Sender = $ConfigObject->Get(
'PostMaster::PreFilterModule::NewTicketReject::Sender'
) || '';
$Kernel::OM->Get('Kernel::System::Email')->Send(
From => $Sender,
To => $Param{GetParam}->{From},
Subject => $Subject,
Body => $Body,
Charset => 'utf-8',
MimeType => 'text/plain',
Loop => 1,
Attachment => [
{
Filename => 'email.txt',
Content => $Param{GetParam}->{Body},
ContentType => 'application/octet-stream',
}
],
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::Filter::NewTicketReject',
Value => "Send reject mail to '$Param{GetParam}->{From}'!",
);
}
return 1;
}
1;

View File

@@ -0,0 +1,86 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::Filter::SMIMEFetchFromCustomer;
use strict;
use warnings;
use Kernel::System::EmailParser;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
'Kernel::System::Crypt::SMIME',
);
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
# Get parser object.
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject!";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# Check needed stuff.
for my $Needed (qw(JobConfig GetParam)) {
if ( !$Param{$Needed} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SMIMEFetchFromCustomer',
Value => "Need $Needed!",
);
return;
}
}
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
return 1 if !$ConfigObject->Get('SMIME');
return 1 if !$ConfigObject->Get('SMIME::FetchFromCustomer');
my $CryptObject;
eval {
$CryptObject = $Kernel::OM->Get('Kernel::System::Crypt::SMIME');
};
return 1 if !$CryptObject;
my @EmailAddressOnField = $Self->{ParserObject}->SplitAddressLine(
Line => $Self->{ParserObject}->GetParam( WHAT => 'From' ),
);
my $IncomingMailAddress;
for my $EmailAddress (@EmailAddressOnField) {
$IncomingMailAddress = $Self->{ParserObject}->GetEmailAddress(
Email => $EmailAddress,
);
}
return 1 if !$IncomingMailAddress;
my @Files = $CryptObject->FetchFromCustomer(
Search => $IncomingMailAddress,
);
return 1;
}
1;

View File

@@ -0,0 +1,789 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
# important configuration items SystemMonitoring::SetIncidentState
package Kernel::System::PostMaster::Filter::SystemMonitoring;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::DynamicField',
'Kernel::System::LinkObject',
'Kernel::System::Log',
'Kernel::System::Main',
'Kernel::System::Ticket',
'Kernel::System::DateTime',
);
#the base name for dynamic fields
our $DynamicFieldTicketTextPrefix = 'TicketFreeText';
our $DynamicFieldArticleTextPrefix = 'ArticleFreeText';
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{Debug} = $Param{Debug} || 0;
# check if CI incident state should be set automatically
# this requires the ITSMConfigurationManagement module to be installed
if ( $Kernel::OM->Get('Kernel::Config')->Get('SystemMonitoring::SetIncidentState') ) {
$Self->_IncidentStateNew();
}
# Default Settings
$Self->{Config} = {
StateRegExp => '\s*State:\s+(\S+)',
FromAddressRegExp => 'sysmon@example.com',
NewTicketRegExp => 'CRITICAL|DOWN',
CloseTicketRegExp => 'OK|UP',
CloseActionState => 'closed successful',
ClosePendingTime => 60 * 60 * 24 * 2, # 2 days
HostRegExp => '\s*Address:\s+(\d+\.\d+\.\d+\.\d+)\s*',
FreeTextHost => '1',
FreeTextService => '2',
FreeTextState => '1',
ServiceRegExp => '\s*Service:\s+(.*)\s*',
DefaultService => 'Host',
SenderType => 'system',
ArticleType => 'note-report',
};
# get communication log object and MessageID
if ( !defined $Param{CommuncationLogRequired} || $Param{CommuncationLogRequired} ) {
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
}
return $Self;
}
sub _GetDynamicFieldDefinition {
my ( $Self, %Param ) = @_;
for my $Argument (qw(Config Key Default Base Name ObjectType)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
my $Config = $Param{Config};
my $Key = $Param{Key}; #FreeTextHost the config key
my $Default = $Param{Default}; #1 the default value
my $Base = $Param{Base}; # DynamicFieldTicketTextPrefix
my $Name = $Param{Name}; #HostName
my $ObjectType = $Param{ObjectType}; #HostName
my $ConfigFreeText = $Config->{$Key};
if ( !$ConfigFreeText ) {
$ConfigFreeText = $Default;
}
if ( $ConfigFreeText =~ /^\d+$/ ) {
if ( ( $ConfigFreeText < 1 ) || ( $ConfigFreeText > 16 ) ) {
die "Bad value $ConfigFreeText for CI Config $Key!";
}
}
else {
die "Bad value $ConfigFreeText for CI Config $Key!";
}
my $FieldNameHost = $Base . $ConfigFreeText;
# define all dynamic fields for System Monitoring, these need to be changed as well if the
# config changes
return (
{
Name => $FieldNameHost,
Label => 'SystemMonitoring ' . $Name,
FieldType => 'Text',
ObjectType => $ObjectType,
Config => {
TranslatableValues => 1,
},
}
);
}
sub GetDynamicFieldsDefinition {
my ( $Self, %Param ) = @_;
my $Config = $Param{Config};
push @{ $Param{NewFields} }, $Self->_GetDynamicFieldDefinition(
Config => $Config,
Key => 'FreeTextHost',
Default => 1,
Base => $DynamicFieldTicketTextPrefix,
Name => 'HostName',
ObjectType => 'Ticket',
);
push @{ $Param{NewFields} }, $Self->_GetDynamicFieldDefinition(
Config => $Config,
Key => 'FreeTextService',
Default => 2,
Base => $DynamicFieldTicketTextPrefix,
Name => 'ServiceName',
ObjectType => 'Ticket',
);
push @{ $Param{NewFields} }, $Self->_GetDynamicFieldDefinition(
Config => $Config,
Key => 'FreeTextState',
Default => 1,
Base => $DynamicFieldArticleTextPrefix,
Name => 'StateName',
ObjectType => 'Article',
);
return 1;
}
sub _IncidentStateIncident {
my ( $Self, %Param ) = @_;
# set the CI incident state to 'Incident'
$Self->_SetIncidentState(
Name => $Self->{Host},
IncidentState => 'Incident',
);
return 1;
}
sub _IncidentStateOperational {
my ( $Self, %Param ) = @_;
# set the CI incident state to 'Operational'
$Self->_SetIncidentState(
Name => $Self->{Host},
IncidentState => 'Operational',
);
return 1;
}
# these are optional modules from the ITSM Kernel::System::GeneralCatalog and Kernel::System::ITSMConfigItem
sub _IncidentStateNew {
my ( $Self, %Param ) = @_;
# get main object
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
# require the general catalog module
if ( $MainObject->Require('Kernel::System::GeneralCatalog') ) {
# create general catalog object
$Self->{GeneralCatalogObject} = Kernel::System::GeneralCatalog->new( %{$Self} );
}
# require the config item module
if ( $MainObject->Require('Kernel::System::ITSMConfigItem') ) {
# create config item object
$Self->{ConfigItemObject} = Kernel::System::ITSMConfigItem->new( %{$Self} );
}
return 1;
}
sub _MailParse {
my ( $Self, %Param ) = @_;
if ( !$Param{GetParam} || !$Param{GetParam}->{Subject} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "Need Subject!",
);
return;
}
my $Subject = $Param{GetParam}->{Subject};
# Try to get State, Host and Service from email subject
my @SubjectLines = split /\n/, $Subject;
for my $Line (@SubjectLines) {
for my $Item (qw(State Host Service)) {
if ( $Line =~ /$Self->{Config}->{ $Item . 'RegExp' }/ ) {
$Self->{$Item} = $1;
}
}
}
# Don't Try to get State, Host and Service from email body, we want it from the subject alone
# split the body into separate lines
if ( !$Param{GetParam}->{Body} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "Need Body!",
);
return;
}
my $Body = $Param{GetParam}->{Body};
my @BodyLines = split /\n/, $Body;
# to remember if an element was found before
my %AlreadyMatched;
LINE:
for my $Line (@BodyLines) {
# Try to get State, Host and Service from email body
ELEMENT:
for my $Element (qw(State Host Service)) {
next ELEMENT if $AlreadyMatched{$Element};
my $Regex = $Self->{Config}->{ $Element . 'RegExp' };
if ( $Line =~ /$Regex/ ) {
# get the found element value
$Self->{$Element} = $1;
# remember that we found this element already
$AlreadyMatched{$Element} = 1;
}
}
}
return 1;
}
sub _LogMessage {
my ( $Self, %Param ) = @_;
if ( !$Param{MessageText} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "Need MessageText!",
);
return;
}
my $MessageText = $Param{MessageText};
# logging
# define log message
$Self->{Service} ||= "No Service";
$Self->{State} ||= "No State";
$Self->{Host} ||= "No Host";
my $LogMessage = $MessageText . " - "
. "Host: $Self->{Host}, "
. "State: $Self->{State}, "
. "Service: $Self->{Service}";
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => 'SystemMonitoring Mail: ' . $LogMessage,
);
return 1;
}
sub _TicketSearch {
my ( $Self, %Param ) = @_;
# Is there a ticket for this Host/Service pair?
my %Query = (
Result => 'ARRAY',
Limit => 1,
UserID => 1,
StateType => 'Open',
);
for my $Type (qw(Host Service)) {
my $FreeTextField = $Self->{Config}->{ 'FreeText' . $Type };
my $KeyName = "DynamicField_" . $DynamicFieldTicketTextPrefix . $FreeTextField;
my $KeyValue = $Self->{$Type};
$Query{$KeyName}->{Equals} = $KeyValue;
}
# get dynamic field object
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
# Check if dynamic fields really exists.
# If dynamic fields don't exists, TicketSearch will return all tickets
# and then the new article/ticket could take wrong place.
# The lesser of the three evils is to create a new ticket
# instead of defacing existing tickets or dropping it.
# This behavior will come true if the dynamic fields
# are named like TicketFreeTextHost. Its also bad.
my $Errors = 0;
for my $Type (qw(Host Service)) {
my $FreeTextField = $Self->{Config}->{ 'FreeText' . $Type };
my $DynamicField = $DynamicFieldObject->DynamicFieldGet(
Name => $DynamicFieldTicketTextPrefix . $FreeTextField,
);
if ( !IsHashRefWithData($DynamicField) || $FreeTextField !~ m{\d+}xms ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "DynamicField "
. $DynamicFieldTicketTextPrefix
. $FreeTextField
. " does not exists or misnamed."
. " The configuration is based on Dynamic fields, so the number of the dynamic field is expected"
. " (wrong value for dynamic field FreeText" . $Type . " is set).",
);
$Errors = 1;
}
}
my $ArticleFreeTextField = $Self->{Config}->{'FreeTextState'};
my $DynamicFieldArticle = $DynamicFieldObject->DynamicFieldGet(
Name => $DynamicFieldArticleTextPrefix . $ArticleFreeTextField,
);
if ( !IsHashRefWithData($DynamicFieldArticle) || $ArticleFreeTextField !~ m{\d+}xms ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "DynamicField "
. $DynamicFieldArticleTextPrefix
. $ArticleFreeTextField
. " does not exists or misnamed."
. " The configuration is based on dynamic fields, so the number of the dynamic field is expected"
. " (wrong value for dynamic field FreeTextState is set).",
);
$Errors = 1;
}
my @TicketIDs = $Kernel::OM->Get('Kernel::System::Ticket')->TicketSearch(%Query);
# get the first and only ticket id
my $TicketID;
if ( !$Errors && @TicketIDs ) {
$TicketID = shift @TicketIDs;
}
return $TicketID;
}
# the sub takes the param as a hash reference not as a copy, because it is updated
sub _TicketUpdate {
my ( $Self, %Param ) = @_;
for my $Needed (qw(TicketID Param)) {
if ( !$Param{$Needed} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "Need $Needed",
);
return;
}
}
my $TicketID = $Param{TicketID};
my $Param = $Param{Param};
# get ticket object
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
# get ticket number
my $TicketNumber = $TicketObject->TicketNumberLookup(
TicketID => $TicketID,
UserID => 1,
);
# build subject
$Param->{GetParam}->{Subject} = $TicketObject->TicketSubjectBuild(
TicketNumber => $TicketNumber,
Subject => $Param->{GetParam}->{Subject},
);
# set sender type and article type
$Param->{GetParam}->{'X-OTRS-FollowUp-SenderType'} = $Self->{Config}->{SenderType};
$Param->{GetParam}->{'X-OTRS-FollowUp-ArticleType'} = $Self->{Config}->{ArticleType};
# Set Article Free Field for State
my $ArticleFreeTextNumber = $Self->{Config}->{'FreeTextState'};
$Param->{GetParam}->{ 'X-OTRS-FollowUp-ArticleKey' . $ArticleFreeTextNumber } = 'State';
$Param->{GetParam}->{ 'X-OTRS-FollowUp-ArticleValue' . $ArticleFreeTextNumber } = $Self->{State};
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
if ( $Self->{State} =~ /$Self->{Config}->{CloseTicketRegExp}/ ) {
# Close Ticket Condition -> Take Close Action
if ( $Self->{Config}->{CloseActionState} ne 'OLD' ) {
$Param->{GetParam}->{'X-OTRS-FollowUp-State'} = $Self->{Config}->{CloseActionState};
# get datetime object
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime'
);
$DateTimeObject->Add(
Seconds => $Self->{Config}->{ClosePendingTime},
);
$Param->{GetParam}->{'X-OTRS-FollowUp-State-PendingTime'} = $DateTimeObject->ToString();
}
# set log message
$Self->_LogMessage( MessageText => 'Recovered' );
# if the CI incident state should be set
if ( $ConfigObject->Get('SystemMonitoring::SetIncidentState') ) {
$Self->_IncidentStateOperational();
}
}
else {
# Attach note to existing ticket
$Self->_LogMessage( MessageText => 'New Notice' );
}
# link ticket with CI, this is only possible if the ticket already exists,
# e.g. in a subsequent email request, because we need a ticket id
if ( $ConfigObject->Get('SystemMonitoring::LinkTicketWithCI') ) {
# link ticket with CI
$Self->_LinkTicketWithCI(
Name => $Self->{Host},
TicketID => $TicketID,
);
}
return 1;
}
# the sub takes the param as a hash reference not as a copy, because it is updated
sub _TicketCreate {
my ( $Self, $Param ) = @_;
# Create Ticket Condition -> Create new Ticket and record Host and Service
for my $Item (qw(Host Service)) {
# get the freetext number from config
my $TicketFreeTextNumber = $Self->{Config}->{ 'FreeText' . $Item };
# see the Kernel::System::PostMaster::NewTicket where this is read
$Param->{GetParam}->{ 'X-OTRS-TicketKey' . $TicketFreeTextNumber } = $Item;
$Param->{GetParam}->{ 'X-OTRS-TicketValue' . $TicketFreeTextNumber } = $Self->{$Item};
}
# Set Article Free Field for State
my $ArticleFreeTextNumber = $Self->{Config}->{'FreeTextState'};
$Param->{GetParam}->{ 'X-OTRS-ArticleKey' . $ArticleFreeTextNumber } = 'State';
$Param->{GetParam}->{ 'X-OTRS-ArticleValue' . $ArticleFreeTextNumber } = $Self->{State};
# set sender type and article type
$Param->{GetParam}->{'X-OTRS-SenderType'} = $Self->{Config}->{SenderType};
$Param->{GetParam}->{'X-OTRS-ArticleType'} = $Self->{Config}->{ArticleType};
# set log message
$Self->_LogMessage( MessageText => 'New Ticket' );
# if the CI incident state should be set
if ( $Kernel::OM->Get('Kernel::Config')->Get('SystemMonitoring::SetIncidentState') ) {
$Self->_IncidentStateIncident();
}
return 1;
}
# the sub takes the param as a hash reference not as a copy, because it is updated
sub _TicketDrop {
my ( $Self, $Param ) = @_;
# No existing ticket and no open condition -> drop silently
$Param->{GetParam}->{'X-OTRS-Ignore'} = 'yes';
$Self->_LogMessage(
MessageText => 'Mail Dropped, no matching ticket found, no open on this state ',
);
return 1;
}
sub Run {
my ( $Self, %Param ) = @_;
# get config options, use defaults unless value specified
if ( $Param{JobConfig} && ref $Param{JobConfig} eq 'HASH' ) {
KEY:
for my $Key ( keys( %{ $Param{JobConfig} } ) ) {
next KEY if !$Self->{Config}->{$Key};
$Self->{Config}->{$Key} = $Param{JobConfig}->{$Key};
}
}
# check if sender is of interest
return 1 if !$Param{GetParam}->{From};
return 1 if $Param{GetParam}->{From} !~ /$Self->{Config}->{FromAddressRegExp}/i;
$Self->_MailParse(%Param);
# we need State and Host to proceed
if ( !$Self->{State} || !$Self->{Host} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => 'SystemMonitoring Mail: '
. 'SystemMonitoring: Could not find host address '
. 'and/or state in mail => Ignoring',
);
return 1;
}
# Check for Service
$Self->{Service} ||= $Self->{Config}->{DefaultService};
my $TicketID = $Self->_TicketSearch();
# OK, found ticket to deal with
if ($TicketID) {
$Self->_TicketUpdate(
TicketID => $TicketID,
Param => \%Param,
);
}
elsif ( $Self->{State} =~ /$Self->{Config}->{NewTicketRegExp}/ ) {
$Self->_TicketCreate( \%Param );
}
else {
$Self->_TicketDrop( \%Param );
}
return 1;
}
sub _SetIncidentState {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(Name IncidentState )) {
if ( !$Param{$Argument} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "Need $Argument",
);
return;
}
}
# check configitem object
return if !$Self->{ConfigItemObject};
# search configitem
my $ConfigItemIDs = $Self->{ConfigItemObject}->ConfigItemSearchExtended(
Name => $Param{Name},
);
# if no config item with this name was found
if ( !$ConfigItemIDs || ref $ConfigItemIDs ne 'ARRAY' || !@{$ConfigItemIDs} ) {
# log error
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "Could not find any CI with the name '$Param{Name}'. ",
);
return;
}
# if more than one config item with this name was found
if ( scalar @{$ConfigItemIDs} > 1 ) {
# log error
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "Can not set incident state for CI with the name '$Param{Name}'. "
. "More than one CI with this name was found!",
);
return;
}
# we only found one config item
my $ConfigItemID = shift @{$ConfigItemIDs};
# get latest version data of config item
my $Version = $Self->{ConfigItemObject}->VersionGet(
ConfigItemID => $ConfigItemID,
);
return if !$Version;
return if ref $Version ne 'HASH';
# get incident state list
my $InciStateList = $Self->{GeneralCatalogObject}->ItemList(
Class => 'ITSM::Core::IncidentState',
);
return if !$InciStateList;
return if ref $InciStateList ne 'HASH';
# reverse the incident state list
my %ReverseInciStateList = reverse %{$InciStateList};
# check if incident state is valid
if ( !$ReverseInciStateList{ $Param{IncidentState} } ) {
# log error
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "Invalid incident state '$Param{IncidentState}'!",
);
return;
}
# add a new version with the new incident state
my $VersionID = $Self->{ConfigItemObject}->VersionAdd(
%{$Version},
InciStateID => $ReverseInciStateList{ $Param{IncidentState} },
UserID => 1,
);
return $VersionID;
}
sub _LinkTicketWithCI {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(Name TicketID)) {
if ( !$Param{$Argument} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "Need $Argument",
);
return;
}
}
# check configitem object
return if !$Self->{ConfigItemObject};
# search configitem
my $ConfigItemIDs = $Self->{ConfigItemObject}->ConfigItemSearchExtended(
Name => $Param{Name},
);
# if no config item with this name was found
if ( !$ConfigItemIDs || ref $ConfigItemIDs ne 'ARRAY' || !@{$ConfigItemIDs} ) {
# log error
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "Could not find any CI with the name '$Param{Name}'. ",
);
return;
}
# if more than one config item with this name was found
if ( scalar @{$ConfigItemIDs} > 1 ) {
# log error
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Filter::SystemMonitoring',
Value => "Can not set incident state for CI with the name '$Param{Name}'. "
. "More than one CI with this name was found!",
);
return;
}
# we only found one config item
my $ConfigItemID = shift @{$ConfigItemIDs};
# link the ticket with the CI
my $LinkResult = $Kernel::OM->Get('Kernel::System::LinkObject')->LinkAdd(
SourceObject => 'Ticket',
SourceKey => $Param{TicketID},
TargetObject => 'ITSMConfigItem',
TargetKey => $ConfigItemID,
Type => 'RelevantTo',
State => 'Valid',
UserID => 1,
);
return $LinkResult;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
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 L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut

View File

@@ -0,0 +1,702 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::FollowUp;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::DynamicField',
'Kernel::System::DynamicField::Backend',
'Kernel::System::Log',
'Kernel::System::Ticket',
'Kernel::System::Ticket::Article',
'Kernel::System::DateTime',
'Kernel::System::User',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get parser object
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject!";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(TicketID InmailUserID GetParam Tn AutoResponseType)) {
if ( !$Param{$_} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::FollowUp',
Value => "Need $_!",
);
return;
}
}
my %GetParam = %{ $Param{GetParam} };
# get ticket object
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
my $OwnerID = $GetParam{'X-OTRS-FollowUp-OwnerID'};
if ( $GetParam{'X-OTRS-FollowUp-Owner'} ) {
my $TmpOwnerID = $Kernel::OM->Get('Kernel::System::User')->UserLookup(
UserLogin => $GetParam{'X-OTRS-FollowUp-Owner'},
);
$OwnerID = $TmpOwnerID || $OwnerID;
}
if ($OwnerID) {
my $Success = $TicketObject->TicketOwnerSet(
TicketID => $Param{TicketID},
NewUserID => $OwnerID,
UserID => $Param{InmailUserID},
);
}
my $ResponsibleID = $GetParam{'X-OTRS-FollowUp-ResponsibleID'};
if ( $GetParam{'X-OTRS-FollowUp-Responsible'} ) {
my $TmpResponsibleID = $Kernel::OM->Get('Kernel::System::User')->UserLookup(
UserLogin => $GetParam{'X-OTRS-FollowUp-Responsible'},
);
$ResponsibleID = $TmpResponsibleID || $ResponsibleID;
}
if ($ResponsibleID) {
my $Success = $TicketObject->TicketResponsibleSet(
TicketID => $Param{TicketID},
NewUserID => $ResponsibleID,
UserID => $Param{InmailUserID},
);
}
# get ticket data
my %Ticket = $TicketObject->TicketGet(
TicketID => $Param{TicketID},
DynamicFields => 0,
);
my $Comment = $Param{Comment} || '';
my $Lock = $Param{Lock} || '';
my $AutoResponseType = $Param{AutoResponseType} || '';
# Check if owner of ticket is still valid
my %UserInfo = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
UserID => $Ticket{OwnerID},
);
# 1) check user, out of office, unlock ticket
if ( $UserInfo{OutOfOfficeMessage} ) {
$TicketObject->TicketLockSet(
TicketID => $Param{TicketID},
Lock => 'unlock',
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::FollowUp',
Value => "Ticket [$Param{Tn}] unlocked, current owner is out of office!",
);
}
# 2) check user, just lock it if user is valid and ticket was closed
elsif ( $UserInfo{ValidID} eq 1 ) {
# set lock (if ticket should be locked on follow up)
if ( $Lock && $Ticket{StateType} =~ /^close/i ) {
$TicketObject->TicketLockSet(
TicketID => $Param{TicketID},
Lock => 'lock',
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::FollowUp',
Value => "Ticket [$Param{Tn}] still locked.",
);
}
}
# 3) Unlock ticket, because current user is set to invalid
else {
$TicketObject->TicketLockSet(
TicketID => $Param{TicketID},
Lock => 'unlock',
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::FollowUp',
Value => "Ticket [$Param{Tn}] unlocked, current owner is invalid!",
);
}
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# set state
my $State = $ConfigObject->Get('PostmasterFollowUpState') || 'open';
if (
$Ticket{StateType} =~ /^close/
&& $ConfigObject->Get('PostmasterFollowUpStateClosed')
)
{
$State = $ConfigObject->Get('PostmasterFollowUpStateClosed');
}
if ( $GetParam{'X-OTRS-FollowUp-State'} ) {
$State = $GetParam{'X-OTRS-FollowUp-State'};
}
my $KeepStateHeader = $ConfigObject->Get('KeepStateHeader') || 'X-OTRS-FollowUp-State-Keep';
if (
( $Ticket{StateType} !~ /^new/ || $GetParam{'X-OTRS-FollowUp-State'} )
&& !$GetParam{$KeepStateHeader}
)
{
$TicketObject->TicketStateSet(
State => $GetParam{'X-OTRS-FollowUp-State'} || $State,
TicketID => $Param{TicketID},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value => "Ticket state will be left unchanged! State: $State.",
);
}
# set pending time
if ( $GetParam{'X-OTRS-FollowUp-State-PendingTime'} ) {
# You can specify absolute dates like "2010-11-20 00:00:00" or relative dates, based on the arrival time of the email.
# Use the form "+ $Number $Unit", where $Unit can be 's' (seconds), 'm' (minutes), 'h' (hours) or 'd' (days).
# Only one unit can be specified. Examples of valid settings: "+50s" (pending in 50 seconds), "+30m" (30 minutes),
# "+12d" (12 days). Note that settings like "+1d 12h" are not possible. You can specify "+36h" instead.
my $TargetTimeStamp = $GetParam{'X-OTRS-FollowUp-State-PendingTime'};
my ( $Sign, $Number, $Unit ) = $TargetTimeStamp =~ m{^\s*([+-]?)\s*(\d+)\s*([smhd]?)\s*$}smx;
if ($Number) {
$Sign ||= '+';
$Unit ||= 's';
my $Seconds = $Sign eq '-' ? ( $Number * -1 ) : $Number;
my %UnitMultiplier = (
s => 1,
m => 60,
h => 60 * 60,
d => 60 * 60 * 24,
);
$Seconds = $Seconds * $UnitMultiplier{$Unit};
# get datetime object
my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
$DateTimeObject->Add( Seconds => $Seconds );
$TargetTimeStamp = $DateTimeObject->ToString();
}
my $Updated = $TicketObject->TicketPendingTimeSet(
String => $TargetTimeStamp,
TicketID => $Param{TicketID},
UserID => $Param{InmailUserID},
);
# debug
if ($Updated) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value =>
"Pending time update via 'X-OTRS-FollowUp-State-PendingTime'! State-PendingTime: $GetParam{'X-OTRS-FollowUp-State-PendingTime'}.",
);
}
}
# set priority
if ( $GetParam{'X-OTRS-FollowUp-Priority'} ) {
$TicketObject->TicketPrioritySet(
TicketID => $Param{TicketID},
Priority => $GetParam{'X-OTRS-FollowUp-Priority'},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value =>
"Priority update via 'X-OTRS-FollowUp-Priority'! Priority: $GetParam{'X-OTRS-FollowUp-Priority'}.",
);
}
# set queue
if ( $GetParam{'X-OTRS-FollowUp-Queue'} ) {
$TicketObject->TicketQueueSet(
Queue => $GetParam{'X-OTRS-FollowUp-Queue'},
TicketID => $Param{TicketID},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value =>
"Queue update via 'X-OTRS-FollowUp-Queue'! Queue: $GetParam{'X-OTRS-FollowUp-Queue'}.",
);
}
# set lock
if ( $GetParam{'X-OTRS-FollowUp-Lock'} ) {
$TicketObject->TicketLockSet(
Lock => $GetParam{'X-OTRS-FollowUp-Lock'},
TicketID => $Param{TicketID},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value =>
"Lock update via 'X-OTRS-FollowUp-Lock'! Lock: $GetParam{'X-OTRS-FollowUp-Lock'}.",
);
}
# set ticket type
if ( $GetParam{'X-OTRS-FollowUp-Type'} ) {
$TicketObject->TicketTypeSet(
Type => $GetParam{'X-OTRS-FollowUp-Type'},
TicketID => $Param{TicketID},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value =>
"Type update via 'X-OTRS-FollowUp-Type'! Type: $GetParam{'X-OTRS-FollowUp-Type'}.",
);
}
# set ticket service
if ( $GetParam{'X-OTRS-FollowUp-Service'} ) {
$TicketObject->TicketServiceSet(
Service => $GetParam{'X-OTRS-FollowUp-Service'},
TicketID => $Param{TicketID},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value =>
"Services update via 'X-OTRS-FollowUp-Service'! Service: $GetParam{'X-OTRS-FollowUp-Service'}.",
);
}
# set ticket sla
if ( $GetParam{'X-OTRS-FollowUp-SLA'} ) {
$TicketObject->TicketSLASet(
SLA => $GetParam{'X-OTRS-FollowUp-SLA'},
TicketID => $Param{TicketID},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value =>
"SLA update via 'X-OTRS-FollowUp-SLA'! SLA: $GetParam{'X-OTRS-FollowUp-SLA'}.",
);
}
# get dynamic field objects
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
# dynamic fields
my $DynamicFieldList =
$DynamicFieldObject->DynamicFieldList(
Valid => 1,
ResultType => 'HASH',
ObjectType => 'Ticket',
);
# set dynamic fields for Ticket object type
DYNAMICFIELDID:
for my $DynamicFieldID ( sort keys %{$DynamicFieldList} ) {
next DYNAMICFIELDID if !$DynamicFieldID;
next DYNAMICFIELDID if !$DynamicFieldList->{$DynamicFieldID};
my $Key = 'X-OTRS-FollowUp-DynamicField-' . $DynamicFieldList->{$DynamicFieldID};
if ( defined $GetParam{$Key} && length $GetParam{$Key} ) {
# get dynamic field config
my $DynamicFieldGet = $DynamicFieldObject->DynamicFieldGet(
ID => $DynamicFieldID,
);
$DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldGet,
ObjectID => $Param{TicketID},
Value => $GetParam{$Key},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value =>
"DynamicField update via '$Key'! Value: $GetParam{$Key}.",
);
}
}
# reverse dynamic field list
my %DynamicFieldListReversed = reverse %{$DynamicFieldList};
# set ticket free text
my %Values =
(
'X-OTRS-FollowUp-TicketKey' => 'TicketFreeKey',
'X-OTRS-FollowUp-TicketValue' => 'TicketFreeText',
);
for my $Item ( sort keys %Values ) {
for my $Count ( 1 .. 16 ) {
my $Key = $Item . $Count;
if (
defined $GetParam{$Key}
&& length $GetParam{$Key}
&& $DynamicFieldListReversed{ $Values{$Item} . $Count }
)
{
# get dynamic field config
my $DynamicFieldGet = $DynamicFieldObject->DynamicFieldGet(
ID => $DynamicFieldListReversed{ $Values{$Item} . $Count },
);
if ($DynamicFieldGet) {
my $Success = $DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldGet,
ObjectID => $Param{TicketID},
Value => $GetParam{$Key},
UserID => $Param{InmailUserID},
);
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value =>
"DynamicField (TicketKey$Count) update via '$Key'! Value: $GetParam{$Key}.",
);
}
}
}
# set ticket free time
for my $Count ( 1 .. 6 ) {
my $Key = 'X-OTRS-FollowUp-TicketTime' . $Count;
if ( defined $GetParam{$Key} && length $GetParam{$Key} ) {
# get datetime object
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $GetParam{$Key}
}
);
if ( $DateTimeObject && $DynamicFieldListReversed{ 'TicketFreeTime' . $Count } ) {
# get dynamic field config
my $DynamicFieldGet = $DynamicFieldObject->DynamicFieldGet(
ID => $DynamicFieldListReversed{ 'TicketFreeTime' . $Count },
);
if ($DynamicFieldGet) {
my $Success = $DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldGet,
ObjectID => $Param{TicketID},
Value => $GetParam{$Key},
UserID => $Param{InmailUserID},
);
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value =>
"DynamicField (TicketTime$Count) update via '$Key'! Value: $GetParam{$Key}.",
);
}
}
}
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
my $ArticleBackendObject = $ArticleObject->BackendForChannel(
ChannelName => 'Email',
);
my $IsVisibleForCustomer = 1;
if ( length $GetParam{'X-OTRS-FollowUp-IsVisibleForCustomer'} ) {
$IsVisibleForCustomer = $GetParam{'X-OTRS-FollowUp-IsVisibleForCustomer'};
}
# Check if X-OTRS-FollowUp-SenderType exists, if not set default 'customer'.
if ( !$ArticleObject->ArticleSenderTypeLookup( SenderType => $GetParam{'X-OTRS-FollowUp-SenderType'} ) )
{
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::FollowUp',
Value => "Can't find valid SenderType '$GetParam{'X-OTRS-FollowUp-SenderType'}' in DB, take 'customer'",
);
$GetParam{'X-OTRS-SenderType'} = 'customer';
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value => "Going to create follow up for TicketID '$Param{TicketID}'.",
);
# do db insert
my $ArticleID = $ArticleBackendObject->ArticleCreate(
TicketID => $Param{TicketID},
SenderType => $GetParam{'X-OTRS-FollowUp-SenderType'},
IsVisibleForCustomer => $IsVisibleForCustomer,
From => $GetParam{From},
ReplyTo => $GetParam{ReplyTo},
To => $GetParam{To},
Cc => $GetParam{Cc},
Subject => $GetParam{Subject},
MessageID => $GetParam{'Message-ID'},
InReplyTo => $GetParam{'In-Reply-To'},
References => $GetParam{'References'},
ContentType => $GetParam{'Content-Type'},
Body => $GetParam{Body},
UserID => $Param{InmailUserID},
HistoryType => 'FollowUp',
HistoryComment => "\%\%$Param{Tn}\%\%$Comment",
AutoResponseType => $AutoResponseType,
OrigHeader => \%GetParam,
);
return if !$ArticleID;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value => "Follow up created for TicketID '$Param{TicketID}' (ArticleID: '$ArticleID')",
);
$Self->{CommunicationLogObject}->ObjectLookupSet(
ObjectLogType => 'Message',
TargetObjectType => 'Article',
TargetObjectID => $ArticleID,
);
my %CommunicationLogSkipAttributes = (
Body => 1,
Attachment => 1,
);
ATTRIBUTE:
for my $Attribute ( sort keys %GetParam ) {
next ATTRIBUTE if $CommunicationLogSkipAttributes{$Attribute};
my $Value = $GetParam{$Attribute};
next ATTRIBUTE if !( defined $Value ) || !( length $Value );
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value => "$Attribute: $Value",
);
}
# write plain email to the storage
$ArticleBackendObject->ArticleWritePlain(
ArticleID => $ArticleID,
Email => $Self->{ParserObject}->GetPlainEmail(),
UserID => $Param{InmailUserID},
);
# write attachments to the storage
for my $Attachment ( $Self->{ParserObject}->GetAttachments() ) {
$ArticleBackendObject->ArticleWriteAttachment(
Filename => $Attachment->{Filename},
Content => $Attachment->{Content},
ContentType => $Attachment->{ContentType},
ContentID => $Attachment->{ContentID},
ContentAlternative => $Attachment->{ContentAlternative},
Disposition => $Attachment->{Disposition},
ArticleID => $ArticleID,
UserID => $Param{InmailUserID},
);
}
# dynamic fields
$DynamicFieldList =
$DynamicFieldObject->DynamicFieldList(
Valid => 1,
ResultType => 'HASH',
ObjectType => 'Article'
);
# set dynamic fields for Article object type
DYNAMICFIELDID:
for my $DynamicFieldID ( sort keys %{$DynamicFieldList} ) {
next DYNAMICFIELDID if !$DynamicFieldID;
next DYNAMICFIELDID if !$DynamicFieldList->{$DynamicFieldID};
my $Key = 'X-OTRS-FollowUp-DynamicField-' . $DynamicFieldList->{$DynamicFieldID};
if ( defined $GetParam{$Key} && length $GetParam{$Key} ) {
# get dynamic field config
my $DynamicFieldGet = $DynamicFieldObject->DynamicFieldGet(
ID => $DynamicFieldID,
);
$DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldGet,
ObjectID => $ArticleID,
Value => $GetParam{$Key},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value => "Article DynamicField update via '$Key'! Value: $GetParam{$Key}.",
);
}
}
# reverse dynamic field list
%DynamicFieldListReversed = reverse %{$DynamicFieldList};
# set free article text
%Values =
(
'X-OTRS-FollowUp-ArticleKey' => 'ArticleFreeKey',
'X-OTRS-FollowUp-ArticleValue' => 'ArticleFreeText',
);
for my $Item ( sort keys %Values ) {
for my $Count ( 1 .. 16 ) {
my $Key = $Item . $Count;
if (
defined $GetParam{$Key}
&& length $GetParam{$Key}
&& $DynamicFieldListReversed{ $Values{$Item} . $Count }
)
{
# get dynamic field config
my $DynamicFieldGet = $DynamicFieldObject->DynamicFieldGet(
ID => $DynamicFieldListReversed{ $Values{$Item} . $Count },
);
if ($DynamicFieldGet) {
my $Success = $DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldGet,
ObjectID => $ArticleID,
Value => $GetParam{$Key},
UserID => $Param{InmailUserID},
);
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value =>
"Article DynamicField (ArticleKey) update via '$Key'! Value: $GetParam{$Key}.",
);
}
}
}
# set ticket title
if ( $GetParam{'X-OTRS-FollowUp-Title'} ) {
$TicketObject->TicketTitleUpdate(
Title => $GetParam{'X-OTRS-FollowUp-Title'},
TicketID => $Param{TicketID},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUp',
Value => "Title update via 'X-OTRS-FollowUp-Title'! Value: $GetParam{'X-OTRS-FollowUp-Title'}.",
);
}
# write log
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::FollowUp',
Value => "FollowUp Article to Ticket [$Param{Tn}] created "
. "(TicketID=$Param{TicketID}, ArticleID=$ArticleID). $Comment,",
);
return 1;
}
1;

View File

@@ -0,0 +1,75 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::FollowUpCheck::Attachments;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Ticket',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
# Ignore all inline parts as these are actually part of the email body.
my @Attachments = $Self->{ParserObject}->GetAttachments();
@Attachments = grep { defined $_->{ContentDisposition} && $_->{ContentDisposition} ne 'inline' } @Attachments;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUpCheck::Attachments',
Value => 'Searching for TicketNumber in email attachments.',
);
ATTACHMENT:
for my $Attachment (@Attachments) {
my $Tn = $TicketObject->GetTNByString( $Attachment->{Content} );
next ATTACHMENT if !$Tn;
my $TicketID = $TicketObject->TicketCheckNumber( Tn => $Tn );
if ($TicketID) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUpCheck::Attachments',
Value =>
"Found valid TicketNumber '$Tn' (TicketID '$TicketID') in email attachment '$Attachment->{Filename}'.",
);
return $TicketID;
}
}
return;
}
1;

View File

@@ -0,0 +1,66 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::FollowUpCheck::Body;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Ticket',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUpCheck::Body',
Value => 'Searching for TicketNumber in email body.',
);
my $Tn = $TicketObject->GetTNByString( $Self->{ParserObject}->GetMessageBody() );
return if !$Tn;
my $TicketID = $TicketObject->TicketCheckNumber( Tn => $Tn );
if ($TicketID) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUpCheck::Body',
Value => "Found valid TicketNumber '$Tn' (TicketID '$TicketID') in email body.",
);
return $TicketID;
}
return;
}
1;

View File

@@ -0,0 +1,154 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::FollowUpCheck::BounceEmail;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
'Kernel::System::Ticket',
'Kernel::System::Ticket::Article',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
# Get Article backend object.
$Self->{ArticleBackendObject} =
$Kernel::OM->Get('Kernel::System::Ticket::Article')->BackendForChannel( ChannelName => 'Email' );
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
$Self->_AddCommunicationLog( Message => 'Searching for header X-OTRS-Bounce.' );
return if !$Param{GetParam}->{'X-OTRS-Bounce'};
my $BounceMessageID = $Param{GetParam}->{'X-OTRS-Bounce-OriginalMessageID'};
$Self->_AddCommunicationLog(
Message => sprintf(
'Searching for article with message id "%s".',
$BounceMessageID,
),
);
# Look for the article that is associated with the BounceMessageID
my %Article = $Self->{ArticleBackendObject}->ArticleGetByMessageID(
MessageID => $BounceMessageID,
);
return if !%Article;
$Self->_AddCommunicationLog(
Message => sprintf(
'Found corresponding article ID "%s".',
$Article{ArticleID},
),
);
$Self->_SetArticleTransmissionSendError(
%Param,
ArticleID => $Article{ArticleID},
);
return $Article{TicketID};
}
sub _SetArticleTransmissionSendError {
my ( $Self, %Param ) = @_;
my $ArticleID = $Param{ArticleID};
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
my $ArticleBackendObject = $ArticleObject->BackendForChannel(
ChannelName => 'Email',
);
my $BounceError = $Param{GetParam}->{'X-OTRS-Bounce-ErrorMessage'};
my $BounceMessageID = $Param{GetParam}->{'X-OTRS-Bounce-OriginalMessageID'};
my $CurrentStatus = $ArticleBackendObject->ArticleGetTransmissionError(
ArticleID => $ArticleID,
);
if ($CurrentStatus) {
my $Result = $ArticleBackendObject->ArticleUpdateTransmissionError(
ArticleID => $ArticleID,
Message => $BounceError,
);
if ( !$Result ) {
my $ErrorMessage = sprintf(
'Error while updating transmission error for article "%s"!',
$ArticleID,
);
$Self->_AddCommunicationLog(
Message => $ErrorMessage,
Priority => 'Error',
);
}
return;
}
my $Result = $ArticleBackendObject->ArticleCreateTransmissionError(
ArticleID => $ArticleID,
MessageID => $BounceMessageID,
Message => $BounceError,
);
if ( !$Result ) {
my $ErrorMessage = sprintf(
'Error while creating transmission error for article "%s"!',
$ArticleID,
);
$Self->_AddCommunicationLog(
Message => $ErrorMessage,
Priority => 'Error',
);
return;
}
return;
}
sub _AddCommunicationLog {
my ( $Self, %Param ) = @_;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => $Param{Priority} || 'Debug',
Key => ref($Self),
Value => $Param{Message},
);
return;
}
1;

View File

@@ -0,0 +1,66 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::FollowUpCheck::RawEmail;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Ticket',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUpCheck::RawEmail',
Value => 'Searching for TicketNumber in raw email.',
);
my $Tn = $TicketObject->GetTNByString( $Self->{ParserObject}->GetPlainEmail() );
return if !$Tn;
my $TicketID = $TicketObject->TicketCheckNumber( Tn => $Tn );
if ($TicketID) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUpCheck::RawEmail',
Value => "Found valid TicketNumber '$Tn' (TicketID '$TicketID') in raw email.",
);
return $TicketID;
}
return;
}
1;

View File

@@ -0,0 +1,75 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::FollowUpCheck::References;
use strict;
use warnings;
use Kernel::System::ObjectManager; # prevent used once warning
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Ticket::Article',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUpCheck::References',
Value => 'Searching for TicketID in email references.',
);
my @References = $Self->{ParserObject}->GetReferences();
return if !@References;
my $ArticleBackendObject = $Kernel::OM->Get('Kernel::System::Ticket::Article')->BackendForChannel(
ChannelName => 'Email',
);
for my $Reference (@References) {
my %Article = $ArticleBackendObject->ArticleGetByMessageID(
MessageID => "<$Reference>",
);
if (%Article) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUpCheck::References',
Value => "Found valid TicketID '$Article{TicketID}' in email references.",
);
return $Article{TicketID};
}
}
return;
}
1;

View File

@@ -0,0 +1,66 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::FollowUpCheck::Subject;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Ticket',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUpCheck::Subject',
Value => 'Searching for TicketNumber in email subject.',
);
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
my $Subject = $Param{GetParam}->{Subject} || '';
my $Tn = $TicketObject->GetTNByString($Subject);
return if !$Tn;
my $TicketID = $TicketObject->TicketCheckNumber( Tn => $Tn );
if ($TicketID) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::FollowUpCheck::Subject',
Value => "Found valid TicketNumber '$Tn' (TicketID '$TicketID') in email subject.",
);
return $TicketID;
}
return;
}
1;

View File

@@ -0,0 +1,71 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::LoopProtection;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Main',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub SendEmail {
my ( $Self, %Param ) = @_;
# get configured backend module
my $BackendModule = $Kernel::OM->Get('Kernel::Config')->Get('LoopProtectionModule')
|| 'Kernel::System::PostMaster::LoopProtection::DB';
# get backend object
my $BackendObject = $Kernel::OM->Get($BackendModule);
if ( !$BackendObject ) {
# get main object
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
$MainObject->Die("Can't load loop protection backend module $BackendModule!");
}
return $BackendObject->SendEmail(%Param);
}
sub Check {
my ( $Self, %Param ) = @_;
# get configured backend module
my $BackendModule = $Kernel::OM->Get('Kernel::Config')->Get('LoopProtectionModule')
|| 'Kernel::System::PostMaster::LoopProtection::DB';
# get backend object
my $BackendObject = $Kernel::OM->Get($BackendModule);
if ( !$BackendObject ) {
# get main object
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
$MainObject->Die("Can't load loop protection backend module $BackendModule!");
}
return $BackendObject->Check(%Param);
}
1;

View File

@@ -0,0 +1,83 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::LoopProtection::DB;
use strict;
use warnings;
use parent 'Kernel::System::PostMaster::LoopProtectionCommon';
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::DB',
'Kernel::System::Log',
'Kernel::System::DateTime',
);
sub SendEmail {
my ( $Self, %Param ) = @_;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
my $To = $Param{To} || return;
# insert log
return if !$DBObject->Do(
SQL => "INSERT INTO ticket_loop_protection (sent_to, sent_date)"
. " VALUES ('"
. $DBObject->Quote($To)
. "', '$Self->{LoopProtectionDate}')",
);
# delete old enrties
return if !$DBObject->Do(
SQL => "DELETE FROM ticket_loop_protection WHERE "
. " sent_date != '$Self->{LoopProtectionDate}'",
);
return 1;
}
sub Check {
my ( $Self, %Param ) = @_;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
my $To = $Param{To} || return;
my $Count = 0;
# check existing logfile
my $SQL = "SELECT count(*) FROM ticket_loop_protection "
. " WHERE sent_to = '" . $DBObject->Quote($To) . "' AND "
. " sent_date = '$Self->{LoopProtectionDate}'";
$DBObject->Prepare( SQL => $SQL );
while ( my @Row = $DBObject->FetchrowArray() ) {
$Count = $Row[0];
}
my $Max = $Self->{PostmasterMaxEmailsPerAddress}{ lc $To } // $Self->{PostmasterMaxEmails};
# check possible loop
if ( $Max && $Count >= $Max ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'notice',
Message =>
"LoopProtection: send no more emails to '$To'! Max. count of $Self->{PostmasterMaxEmails} has been reached!",
);
return;
}
return 1;
}
1;

View File

@@ -0,0 +1,107 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::LoopProtection::FS;
use strict;
use warnings;
use parent 'Kernel::System::PostMaster::LoopProtectionCommon';
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
'Kernel::System::DateTime',
);
sub new {
my ( $Type, %Param ) = @_;
my $Self = $Type->SUPER::new(%Param);
$Self->{LoopProtectionLog} = $Kernel::OM->Get('Kernel::Config')->Get('LoopProtectionLog')
|| die 'No Config option "LoopProtectionLog"!';
$Self->{LoopProtectionLog} .= '-' . $Self->{LoopProtectionDate} . '.log';
return $Self;
}
sub SendEmail {
my ( $Self, %Param ) = @_;
my $To = $Param{To} || return;
# write log
## no critic
if ( open( my $Out, '>>', $Self->{LoopProtectionLog} ) ) {
my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
## use critic
print $Out "$To;" . $DateTimeObject->Format( Format => '%a %b %{day} %H:%M:%S %Y' ) . ";\n"; ## no critic
close($Out);
}
else {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "LoopProtection! Can't write '$Self->{LoopProtectionLog}': $!!",
);
}
return 1;
}
sub Check {
my ( $Self, %Param ) = @_;
my $To = $Param{To} || return;
my $Count = 0;
# check existing logfile
## no critic
if ( !open( my $In, '<', $Self->{LoopProtectionLog} ) ) {
## use critic
# create new log file
## no critic
if ( !open( my $Out, '>', $Self->{LoopProtectionLog} ) ) {
## use critic
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "LoopProtection! Can't write '$Self->{LoopProtectionLog}': $!!",
);
}
else {
close($Out);
}
}
else {
# open old log file
while (<$In>) {
my @Data = split( /;/, $_ );
if ( $Data[0] eq $To ) {
$Count++;
}
}
close($In);
}
# check possible loop
my $Max = $Self->{PostmasterMaxEmailsPerAddress}{ lc $To } // $Self->{PostmasterMaxEmails};
if ( $Max && $Count >= $Max ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'notice',
Message =>
"LoopProtection: send no more emails to '$To'! Max. count of $Self->{PostmasterMaxEmails} has been reached!",
);
return;
}
return 1;
}
1;

View File

@@ -0,0 +1,35 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::LoopProtectionCommon;
## nofilter(TidyAll::Plugin::OTRS::Perl::Time)
use strict;
use warnings;
use utf8;
our $ObjectManagerDisabled = 1;
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get config options
$Self->{PostmasterMaxEmails} = $Kernel::OM->Get('Kernel::Config')->Get('PostmasterMaxEmails') || 40;
$Self->{PostmasterMaxEmailsPerAddress} =
$Kernel::OM->Get('Kernel::Config')->Get('PostmasterMaxEmailsPerAddress') || {};
my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
$Self->{LoopProtectionDate} = $DateTimeObject->Format( Format => '%Y-%m-%d' );
return $Self;
}
1;

View File

@@ -0,0 +1,754 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::NewTicket;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::CustomerUser',
'Kernel::System::DynamicField',
'Kernel::System::DynamicField::Backend',
'Kernel::System::LinkObject',
'Kernel::System::Log',
'Kernel::System::Priority',
'Kernel::System::Queue',
'Kernel::System::State',
'Kernel::System::Ticket',
'Kernel::System::Ticket::Article',
'Kernel::System::DateTime',
'Kernel::System::Type',
'Kernel::System::User',
'Kernel::System::Service',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get parser object
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject!";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw(InmailUserID GetParam)) {
if ( !$Param{$Needed} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "Need $Needed!",
);
return;
}
}
my %GetParam = %{ $Param{GetParam} };
my $Comment = $Param{Comment} || '';
my $AutoResponseType = $Param{AutoResponseType} || '';
# get queue id and name
my $QueueID = $Param{QueueID} || die "need QueueID!";
my $Queue = $Kernel::OM->Get('Kernel::System::Queue')->QueueLookup(
QueueID => $QueueID,
);
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# get state
my $State = $ConfigObject->Get('PostmasterDefaultState') || 'new';
if ( $GetParam{'X-OTRS-State'} ) {
my $StateID = $Kernel::OM->Get('Kernel::System::State')->StateLookup(
State => $GetParam{'X-OTRS-State'},
);
if ($StateID) {
$State = $GetParam{'X-OTRS-State'};
}
else {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "State $GetParam{'X-OTRS-State'} does not exist, falling back to $State!",
);
}
}
# get priority
my $Priority = $ConfigObject->Get('PostmasterDefaultPriority') || '3 normal';
if ( $GetParam{'X-OTRS-Priority'} ) {
my $PriorityID = $Kernel::OM->Get('Kernel::System::Priority')->PriorityLookup(
Priority => $GetParam{'X-OTRS-Priority'},
);
if ($PriorityID) {
$Priority = $GetParam{'X-OTRS-Priority'};
}
else {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "Priority $GetParam{'X-OTRS-Priority'} does not exist, falling back to $Priority!",
);
}
}
my $TypeID;
if ( $GetParam{'X-OTRS-Type'} ) {
# Check if type exists
$TypeID = $Kernel::OM->Get('Kernel::System::Type')->TypeLookup( Type => $GetParam{'X-OTRS-Type'} );
if ( !$TypeID ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "Type $GetParam{'X-OTRS-Type'} does not exist, falling back to default type.",
);
}
}
# get sender email
my @EmailAddresses = $Self->{ParserObject}->SplitAddressLine(
Line => $GetParam{From},
);
for my $Address (@EmailAddresses) {
$GetParam{SenderEmailAddress} = $Self->{ParserObject}->GetEmailAddress(
Email => $Address,
);
}
$GetParam{SenderEmailAddress} //= '';
# get customer id (sender email) if there is no customer id given
if ( !$GetParam{'X-OTRS-CustomerNo'} && $GetParam{'X-OTRS-CustomerUser'} ) {
# get customer user object
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
# get customer user data form X-OTRS-CustomerUser
my %CustomerData = $CustomerUserObject->CustomerUserDataGet(
User => $GetParam{'X-OTRS-CustomerUser'},
);
if (%CustomerData) {
$GetParam{'X-OTRS-CustomerNo'} = $CustomerData{UserCustomerID};
}
}
# get customer user data form From: (sender address)
if ( !$GetParam{'X-OTRS-CustomerUser'} ) {
my %CustomerData;
if ( $GetParam{From} ) {
my @EmailAddresses = $Self->{ParserObject}->SplitAddressLine(
Line => $GetParam{From},
);
for my $Address (@EmailAddresses) {
$GetParam{EmailFrom} = $Self->{ParserObject}->GetEmailAddress(
Email => $Address,
);
}
if ( $GetParam{EmailFrom} ) {
# get customer user object
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
my %List = $CustomerUserObject->CustomerSearch(
PostMasterSearch => lc( $GetParam{EmailFrom} ),
);
for my $UserLogin ( sort keys %List ) {
%CustomerData = $CustomerUserObject->CustomerUserDataGet(
User => $UserLogin,
);
}
}
}
# take CustomerID from customer backend lookup or from from field
if ( $CustomerData{UserLogin} && !$GetParam{'X-OTRS-CustomerUser'} ) {
$GetParam{'X-OTRS-CustomerUser'} = $CustomerData{UserLogin};
# notice that UserLogin is from customer source backend
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "Take UserLogin ($CustomerData{UserLogin}) from "
. "customer source backend based on ($GetParam{'EmailFrom'}).",
);
}
if ( $CustomerData{UserCustomerID} && !$GetParam{'X-OTRS-CustomerNo'} ) {
$GetParam{'X-OTRS-CustomerNo'} = $CustomerData{UserCustomerID};
# notice that UserCustomerID is from customer source backend
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "Take UserCustomerID ($CustomerData{UserCustomerID})"
. " from customer source backend based on ($GetParam{'EmailFrom'}).",
);
}
}
# if there is no customer id found!
if (
!$GetParam{'X-OTRS-CustomerNo'}
&& $ConfigObject->Get('PostMaster::NewTicket::AutoAssignCustomerIDForUnknownCustomers')
)
{
$GetParam{'X-OTRS-CustomerNo'} = $GetParam{SenderEmailAddress};
}
# if there is no customer user found!
if ( !$GetParam{'X-OTRS-CustomerUser'} ) {
$GetParam{'X-OTRS-CustomerUser'} = $GetParam{SenderEmailAddress};
}
# get ticket owner
my $OwnerID = $GetParam{'X-OTRS-OwnerID'} || $Param{InmailUserID};
if ( $GetParam{'X-OTRS-Owner'} ) {
my $TmpOwnerID = $Kernel::OM->Get('Kernel::System::User')->UserLookup(
UserLogin => $GetParam{'X-OTRS-Owner'},
);
$OwnerID = $TmpOwnerID || $OwnerID;
}
my %Opts;
if ( $GetParam{'X-OTRS-ResponsibleID'} ) {
$Opts{ResponsibleID} = $GetParam{'X-OTRS-ResponsibleID'};
}
if ( $GetParam{'X-OTRS-Responsible'} ) {
my $TmpResponsibleID = $Kernel::OM->Get('Kernel::System::User')->UserLookup(
UserLogin => $GetParam{'X-OTRS-Responsible'},
);
$Opts{ResponsibleID} = $TmpResponsibleID || $Opts{ResponsibleID};
}
# get ticket object
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "Going to create new ticket.",
);
if ( $GetParam{'X-OTRS-Service'} ) {
my $ServiceObject = $Kernel::OM->Get('Kernel::System::Service');
# Check if service exists.
my %ServiceData = $ServiceObject->ServiceGet(
Name => $GetParam{'X-OTRS-Service'},
UserID => $Param{InmailUserID},
);
# Get all service list filtering by KeepChildren SysConfig if available.
my %ServiceList = $ServiceObject->ServiceList(
Valid => 1,
KeepChildren => $ConfigObject->Get('Ticket::Service::KeepChildren') // 0,
UserID => $Param{InmailUserID},
);
if ( $ServiceData{ServiceID} ne '' && !$ServiceList{ $ServiceData{ServiceID} } ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::NewTicket',
Value =>
"Service $GetParam{'X-OTRS-Service'} does not exists or is invalid or is a child of invalid service.",
);
$GetParam{'X-OTRS-Service'} = '';
}
}
# create new ticket
my $NewTn = $TicketObject->TicketCreateNumber();
my $TicketID = $TicketObject->TicketCreate(
TN => $NewTn,
Title => $GetParam{'X-OTRS-Title'} || $GetParam{Subject},
QueueID => $QueueID,
Lock => $GetParam{'X-OTRS-Lock'} || 'unlock',
Priority => $Priority,
State => $State,
TypeID => $TypeID,
Service => $GetParam{'X-OTRS-Service'} || '',
SLA => $GetParam{'X-OTRS-SLA'} || '',
CustomerID => $GetParam{'X-OTRS-CustomerNo'},
CustomerUser => $GetParam{'X-OTRS-CustomerUser'},
OwnerID => $OwnerID,
UserID => $Param{InmailUserID},
%Opts,
);
if ( !$TicketID ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "Ticket could not be created!",
);
return;
}
my $TicketCreateMessage = <<"Message";
New Ticket created:
TicketNumber: $NewTn
TicketID: $TicketID
Priority: $Priority
State: $State
CustomerID: $GetParam{'X-OTRS-CustomerNo'}
CustomerUser: $GetParam{'X-OTRS-CustomerUser'}
Message
for my $Value (qw(Type Service SLA Lock)) {
if ( $GetParam{ 'X-OTRS-' . $Value } ) {
$TicketCreateMessage .= "$Value: " . $GetParam{ 'X-OTRS-' . $Value } . "\n";
}
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => $TicketCreateMessage,
);
# set pending time
if ( $GetParam{'X-OTRS-State-PendingTime'} ) {
# You can specify absolute dates like "2010-11-20 00:00:00" or relative dates, based on the arrival time of the email.
# Use the form "+ $Number $Unit", where $Unit can be 's' (seconds), 'm' (minutes), 'h' (hours) or 'd' (days).
# Only one unit can be specified. Examples of valid settings: "+50s" (pending in 50 seconds), "+30m" (30 minutes),
# "+12d" (12 days). Note that settings like "+1d 12h" are not possible. You can specify "+36h" instead.
my $TargetTimeStamp = $GetParam{'X-OTRS-State-PendingTime'};
my ( $Sign, $Number, $Unit ) = $TargetTimeStamp =~ m{^\s*([+-]?)\s*(\d+)\s*([smhd]?)\s*$}smx;
if ($Number) {
$Sign ||= '+';
$Unit ||= 's';
my $Seconds = $Sign eq '-' ? ( $Number * -1 ) : $Number;
my %UnitMultiplier = (
s => 1,
m => 60,
h => 60 * 60,
d => 60 * 60 * 24,
);
$Seconds = $Seconds * $UnitMultiplier{$Unit};
# get datetime object
my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
$DateTimeObject->Add( Seconds => $Seconds );
$TargetTimeStamp = $DateTimeObject->ToString();
}
my $Set = $TicketObject->TicketPendingTimeSet(
String => $TargetTimeStamp,
TicketID => $TicketID,
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::NewTicket',
Value =>
"Pending time update via 'X-OTRS-State-PendingTime'! State-PendingTime: $GetParam{'X-OTRS-State-PendingTime'}.",
);
}
# get dynamic field objects
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
# dynamic fields
my $DynamicFieldList =
$DynamicFieldObject->DynamicFieldList(
Valid => 1,
ResultType => 'HASH',
ObjectType => 'Ticket',
);
# set dynamic fields for Ticket object type
DYNAMICFIELDID:
for my $DynamicFieldID ( sort keys %{$DynamicFieldList} ) {
next DYNAMICFIELDID if !$DynamicFieldID;
next DYNAMICFIELDID if !$DynamicFieldList->{$DynamicFieldID};
my $Key = 'X-OTRS-DynamicField-' . $DynamicFieldList->{$DynamicFieldID};
if ( defined $GetParam{$Key} && length $GetParam{$Key} ) {
# get dynamic field config
my $DynamicFieldGet = $DynamicFieldObject->DynamicFieldGet(
ID => $DynamicFieldID,
);
$DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldGet,
ObjectID => $TicketID,
Value => $GetParam{$Key},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "DynamicField update via '$Key'! Value: $GetParam{$Key}.",
);
}
}
# reverse dynamic field list
my %DynamicFieldListReversed = reverse %{$DynamicFieldList};
# set ticket free text
# for backward compatibility (should be removed in a future version)
my %Values =
(
'X-OTRS-TicketKey' => 'TicketFreeKey',
'X-OTRS-TicketValue' => 'TicketFreeText',
);
for my $Item ( sort keys %Values ) {
for my $Count ( 1 .. 16 ) {
my $Key = $Item . $Count;
if (
defined $GetParam{$Key}
&& length $GetParam{$Key}
&& $DynamicFieldListReversed{ $Values{$Item} . $Count }
)
{
# get dynamic field config
my $DynamicFieldGet = $DynamicFieldObject->DynamicFieldGet(
ID => $DynamicFieldListReversed{ $Values{$Item} . $Count },
);
if ($DynamicFieldGet) {
my $Success = $DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldGet,
ObjectID => $TicketID,
Value => $GetParam{$Key},
UserID => $Param{InmailUserID},
);
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "DynamicField (TicketKey$Count) update via '$Key'! Value: $GetParam{$Key}.",
);
}
}
}
# set ticket free time
# for backward compatibility (should be removed in a future version)
for my $Count ( 1 .. 6 ) {
my $Key = 'X-OTRS-TicketTime' . $Count;
if ( defined $GetParam{$Key} && length $GetParam{$Key} ) {
# get datetime object
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $GetParam{$Key}
}
);
if ( $DateTimeObject && $DynamicFieldListReversed{ 'TicketFreeTime' . $Count } ) {
# get dynamic field config
my $DynamicFieldGet = $DynamicFieldObject->DynamicFieldGet(
ID => $DynamicFieldListReversed{ 'TicketFreeTime' . $Count },
);
if ($DynamicFieldGet) {
my $Success = $DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldGet,
ObjectID => $TicketID,
Value => $GetParam{$Key},
UserID => $Param{InmailUserID},
);
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "DynamicField (TicketTime$Count) update via '$Key'! Value: $GetParam{$Key}.",
);
}
}
}
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
my $ArticleBackendObject = $ArticleObject->BackendForChannel(
ChannelName => 'Email',
);
my $IsVisibleForCustomer = 1;
if ( length $GetParam{'X-OTRS-IsVisibleForCustomer'} ) {
$IsVisibleForCustomer = $GetParam{'X-OTRS-IsVisibleForCustomer'};
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "Going to create new article for TicketID '$TicketID'.",
);
# Check if X-OTRS-SenderType exists, if not set default 'customer'.
if ( !$ArticleObject->ArticleSenderTypeLookup( SenderType => $GetParam{'X-OTRS-SenderType'} ) )
{
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "Can't find valid SenderType '$GetParam{'X-OTRS-SenderType'}' in DB, take 'customer'",
);
$GetParam{'X-OTRS-SenderType'} = 'customer';
}
# Create email article.
my $ArticleID = $ArticleBackendObject->ArticleCreate(
TicketID => $TicketID,
SenderType => $GetParam{'X-OTRS-SenderType'},
IsVisibleForCustomer => $IsVisibleForCustomer,
From => $GetParam{From},
ReplyTo => $GetParam{ReplyTo},
To => $GetParam{To},
Cc => $GetParam{Cc},
Subject => $GetParam{Subject},
MessageID => $GetParam{'Message-ID'},
InReplyTo => $GetParam{'In-Reply-To'},
References => $GetParam{'References'},
ContentType => $GetParam{'Content-Type'},
ContentDisposition => $GetParam{'Content-Disposition'},
Body => $GetParam{Body},
UserID => $Param{InmailUserID},
HistoryType => 'EmailCustomer',
HistoryComment => "\%\%$Comment",
OrigHeader => \%GetParam,
AutoResponseType => $AutoResponseType,
Queue => $Queue,
);
# close ticket if article create failed!
if ( !$ArticleID ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "Can't process email with MessageID <$GetParam{'Message-ID'}>! "
. "Please create a bug report with this email (From: $GetParam{From}, Located "
. "under var/spool/problem-email*) on http://bugs.otrs.org/!",
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "TicketID '$TicketID' will be deleted again!",
);
$TicketObject->TicketDelete(
TicketID => $TicketID,
UserID => $Param{InmailUserID},
);
return;
}
$Self->{CommunicationLogObject}->ObjectLookupSet(
ObjectLogType => 'Message',
TargetObjectType => 'Article',
TargetObjectID => $ArticleID,
);
if ( $Param{LinkToTicketID} ) {
my $SourceKey = $Param{LinkToTicketID};
my $TargetKey = $TicketID;
$Kernel::OM->Get('Kernel::System::LinkObject')->LinkAdd(
SourceObject => 'Ticket',
SourceKey => $SourceKey,
TargetObject => 'Ticket',
TargetKey => $TargetKey,
Type => 'Normal',
State => 'Valid',
UserID => $Param{InmailUserID},
);
}
my %CommunicationLogSkipAttributes = (
Body => 1,
Attachment => 1,
);
ATTRIBUTE:
for my $Attribute ( sort keys %GetParam ) {
next ATTRIBUTE if $CommunicationLogSkipAttributes{$Attribute};
my $Value = $GetParam{$Attribute};
next ATTRIBUTE if !( defined $Value ) || !( length $Value );
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "$Attribute: $Value",
);
}
# dynamic fields
$DynamicFieldList =
$DynamicFieldObject->DynamicFieldList(
Valid => 1,
ResultType => 'HASH',
ObjectType => 'Article',
);
# set dynamic fields for Article object type
DYNAMICFIELDID:
for my $DynamicFieldID ( sort keys %{$DynamicFieldList} ) {
next DYNAMICFIELDID if !$DynamicFieldID;
next DYNAMICFIELDID if !$DynamicFieldList->{$DynamicFieldID};
my $Key = 'X-OTRS-DynamicField-' . $DynamicFieldList->{$DynamicFieldID};
if ( defined $GetParam{$Key} && length $GetParam{$Key} ) {
# get dynamic field config
my $DynamicFieldGet = $DynamicFieldObject->DynamicFieldGet(
ID => $DynamicFieldID,
);
$DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldGet,
ObjectID => $ArticleID,
Value => $GetParam{$Key},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "Article DynamicField update via '$Key'! Value: $GetParam{$Key}.",
);
}
}
# reverse dynamic field list
%DynamicFieldListReversed = reverse %{$DynamicFieldList};
# set free article text
# for backward compatibility (should be removed in a future version)
%Values =
(
'X-OTRS-ArticleKey' => 'ArticleFreeKey',
'X-OTRS-ArticleValue' => 'ArticleFreeText',
);
for my $Item ( sort keys %Values ) {
for my $Count ( 1 .. 16 ) {
my $Key = $Item . $Count;
if (
defined $GetParam{$Key}
&& length $GetParam{$Key}
&& $DynamicFieldListReversed{ $Values{$Item} . $Count }
)
{
# get dynamic field config
my $DynamicFieldGet = $DynamicFieldObject->DynamicFieldGet(
ID => $DynamicFieldListReversed{ $Values{$Item} . $Count },
);
if ($DynamicFieldGet) {
my $Success = $DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldGet,
ObjectID => $ArticleID,
Value => $GetParam{$Key},
UserID => $Param{InmailUserID},
);
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::NewTicket',
Value => "Article DynamicField (ArticleKey) update via '$Key'! Value: $GetParam{$Key}.",
);
}
}
}
# write plain email to the storage
$ArticleBackendObject->ArticleWritePlain(
ArticleID => $ArticleID,
Email => $Self->{ParserObject}->GetPlainEmail(),
UserID => $Param{InmailUserID},
);
# write attachments to the storage
for my $Attachment ( $Self->{ParserObject}->GetAttachments() ) {
$ArticleBackendObject->ArticleWriteAttachment(
Filename => $Attachment->{Filename},
Content => $Attachment->{Content},
ContentType => $Attachment->{ContentType},
ContentID => $Attachment->{ContentID},
ContentAlternative => $Attachment->{ContentAlternative},
Disposition => $Attachment->{Disposition},
ArticleID => $ArticleID,
UserID => $Param{InmailUserID},
);
}
return $TicketID;
}
1;

View File

@@ -0,0 +1,271 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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 https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::PostMaster::Reject;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::System::DynamicField',
'Kernel::System::DynamicField::Backend',
'Kernel::System::Log',
'Kernel::System::Ticket',
'Kernel::System::Ticket::Article',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get parser object
$Self->{ParserObject} = $Param{ParserObject} || die "Got no ParserObject!";
# Get communication log object.
$Self->{CommunicationLogObject} = $Param{CommunicationLogObject} || die "Got no CommunicationLogObject!";
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(TicketID InmailUserID GetParam Tn AutoResponseType)) {
if ( !$Param{$_} ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Reject',
Value => "Need $_!",
);
return;
}
}
my %GetParam = %{ $Param{GetParam} };
# get ticket object
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
# get ticket data
my %Ticket = $TicketObject->TicketGet(
TicketID => $Param{TicketID},
DynamicFields => 0,
);
my $Comment = $Param{Comment} || '';
my $Lock = $Param{Lock} || '';
my $AutoResponseType = $Param{AutoResponseType} || '';
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
my $ArticleBackendObject = $ArticleObject->BackendForChannel(
ChannelName => 'Email',
);
# Check if X-OTRS-SenderType exists, if not set default 'customer'.
if ( !$ArticleObject->ArticleSenderTypeLookup( SenderType => $GetParam{'X-OTRS-SenderType'} ) ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Reject',
Value => "Can't find valid SenderType '$GetParam{'X-OTRS-SenderType'}' in DB, take 'customer'",
);
$GetParam{'X-OTRS-SenderType'} = 'customer';
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Reject',
Value => "Going to create new article for TicketID '$Param{TicketID}'.",
);
# do db insert
my $ArticleID = $ArticleBackendObject->ArticleCreate(
TicketID => $Param{TicketID},
IsVisibleForCustomer => $GetParam{'X-OTRS-IsVisibleForCustomer'} // 1,
SenderType => $GetParam{'X-OTRS-SenderType'},
From => $GetParam{From},
ReplyTo => $GetParam{ReplyTo},
To => $GetParam{To},
Cc => $GetParam{Cc},
Subject => $GetParam{Subject},
MessageID => $GetParam{'Message-ID'},
InReplyTo => $GetParam{'In-Reply-To'},
References => $GetParam{'References'},
ContentType => $GetParam{'Content-Type'},
Body => $GetParam{Body},
UserID => $Param{InmailUserID},
HistoryType => 'FollowUp',
HistoryComment => "\%\%$Param{Tn}\%\%$Comment",
AutoResponseType => $AutoResponseType,
OrigHeader => \%GetParam,
);
if ( !$ArticleID ) {
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Error',
Key => 'Kernel::System::PostMaster::Reject',
Value => "Article could not be created!",
);
return;
}
$Self->{CommunicationLogObject}->ObjectLookupSet(
ObjectLogType => 'Message',
TargetObjectType => 'Article',
TargetObjectID => $ArticleID,
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Reject',
Value => "Reject Follow up Ticket!",
);
my %CommunicationLogSkipAttributes = (
Body => 1,
Attachment => 1,
);
ATTRIBUTE:
for my $Attribute ( sort keys %GetParam ) {
next ATTRIBUTE if $CommunicationLogSkipAttributes{$Attribute};
my $Value = $GetParam{$Attribute};
next ATTRIBUTE if !( defined $Value ) || !( length $Value );
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Reject',
Value => "$Attribute: $Value",
);
}
# write plain email to the storage
$ArticleBackendObject->ArticleWritePlain(
ArticleID => $ArticleID,
Email => $Self->{ParserObject}->GetPlainEmail(),
UserID => $Param{InmailUserID},
);
# write attachments to the storage
for my $Attachment ( $Self->{ParserObject}->GetAttachments() ) {
$ArticleBackendObject->ArticleWriteAttachment(
Filename => $Attachment->{Filename},
Content => $Attachment->{Content},
ContentType => $Attachment->{ContentType},
ContentID => $Attachment->{ContentID},
ContentAlternative => $Attachment->{ContentAlternative},
Disposition => $Attachment->{Disposition},
ArticleID => $ArticleID,
UserID => $Param{InmailUserID},
);
}
# get dynamic field objects
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
# dynamic fields
my $DynamicFieldList =
$DynamicFieldObject->DynamicFieldList(
Valid => 0,
ResultType => 'HASH',
ObjectType => 'Article',
);
# set dynamic fields for Article object type
DYNAMICFIELDID:
for my $DynamicFieldID ( sort keys %{$DynamicFieldList} ) {
next DYNAMICFIELDID if !$DynamicFieldID;
next DYNAMICFIELDID if !$DynamicFieldList->{$DynamicFieldID};
my $Key = 'X-OTRS-FollowUp-DynamicField-' . $DynamicFieldList->{$DynamicFieldID};
if ( defined $GetParam{$Key} && length $GetParam{$Key} ) {
# get dynamic field config
my $DynamicFieldGet = $DynamicFieldObject->DynamicFieldGet(
ID => $DynamicFieldID,
);
$DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldGet,
ObjectID => $ArticleID,
Value => $GetParam{$Key},
UserID => $Param{InmailUserID},
);
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Reject',
Value => "Article DynamicField update via '$Key'! Value: $GetParam{$Key}.",
);
}
}
# reverse dynamic field list
my %DynamicFieldListReversed = reverse %{$DynamicFieldList};
# set free article text
my %Values =
(
'X-OTRS-FollowUp-ArticleKey' => 'ArticleFreeKey',
'X-OTRS-FollowUp-ArticleValue' => 'ArticleFreeText',
);
for my $Item ( sort keys %Values ) {
for my $Count ( 1 .. 16 ) {
my $Key = $Item . $Count;
if (
defined $GetParam{$Key}
&& length $GetParam{$Key}
&& $DynamicFieldListReversed{ $Values{$Item} . $Count }
)
{
# get dynamic field config
my $DynamicFieldGet = $DynamicFieldObject->DynamicFieldGet(
ID => $DynamicFieldListReversed{ $Values{$Item} . $Count },
);
if ($DynamicFieldGet) {
my $Success = $DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldGet,
ObjectID => $ArticleID,
Value => $GetParam{$Key},
UserID => $Param{InmailUserID},
);
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Debug',
Key => 'Kernel::System::PostMaster::Reject',
Value =>
"TicketKey$Count: Article DynamicField (ArticleKey) update via '$Key'! Value: $GetParam{$Key}.",
);
}
}
}
$Self->{CommunicationLogObject}->ObjectLog(
ObjectLogType => 'Message',
Priority => 'Notice',
Key => 'Kernel::System::PostMaster::Reject',
Value => "Reject FollowUp Article to Ticket [$Param{Tn}] created "
. "(TicketID=$Param{TicketID}, ArticleID=$ArticleID). $Comment"
);
return 1;
}
1;