=head1 NAME
- RT::Interface::CLI - helper functions for creating a commandline RT interface
+ RT::EmailParser - helper functions for parsing parts from incoming
+ email messages
=head1 SYNOPSIS
#If this instance of RT sent it our, we don't want to take it in
my $RTLoop = $head->get("X-RT-Loop-Prevention") || "";
chomp($RTLoop); #remove that newline
- if ( $RTLoop =~ /^$RT::rtname/ ) {
+ if ( $RTLoop =~ /^\Q$RT::rtname\E/o ) {
return (1);
}
# }}}
+=head2 ParseMIMEEntityFromScalar $message
+
+Takes either a scalar or a reference to a scalr which contains a stringified MIME message.
+Parses it.
+
+Returns true if it wins.
+Returns false if it loses.
+
+
+=cut
sub ParseMIMEEntityFromScalar {
my $self = shift;
my $message = shift;
- # Create a new parser object:
+ $self->_DoParse('parse_data', $message);
- my $parser = MIME::Parser->new();
- $self->_SetupMIMEParser($parser);
-
-
- # TODO: XXX 3.0 we really need to wrap this in an eval { }
- unless ( $self->{'entity'} = $parser->parse_data($message) ) {
- # Try again, this time without extracting nested messages
- $parser->extract_nested_messages(0);
- unless ( $self->{'entity'} = $parser->parse_data($message) ) {
- $RT::Logger->crit("couldn't parse MIME stream");
- return ( undef);
- }
- }
- $self->_PostProcessNewEntity();
- return (1);
}
# {{{ ParseMIMEEntityFromFilehandle *FH
my $self = shift;
my $filehandle = shift;
+ $self->_DoParse('parse', $filehandle);
+
+}
+
+# }}}
+
+# {{{ ParseMIMEEntityFromFile
+
+=head2 ParseMIMEEntityFromFile
+
+Parses a mime entity from a filename passed in as an argument
+
+=cut
+
+sub ParseMIMEEntityFromFile {
+ my $self = shift;
+
+ my $file = shift;
+ $self->_DoParse('parse_open', $file);
+}
+
+# }}}
+
+# {{{ _DoParse
+
+=head2 _DoParse PARSEMETHOD CONTENT
+
+
+A helper for the various parsers to turn around and do the dispatch to the actual parser
+
+=cut
+
+sub _DoParse {
+ my $self = shift;
+ my $method = shift;
+ my $file = shift;
+
# Create a new parser object:
my $parser = MIME::Parser->new();
# TODO: XXX 3.0 we really need to wrap this in an eval { }
- unless ( $self->{'entity'} = $parser->parse($filehandle) ) {
+ unless ( $self->{'entity'} = $parser->$method($file) ) {
# Try again, this time without extracting nested messages
$parser->extract_nested_messages(0);
- unless ( $self->{'entity'} = $parser->parse($filehandle) ) {
+ unless ( $self->{'entity'} = $parser->$method($file) ) {
$RT::Logger->crit("couldn't parse MIME stream");
return ( undef);
}
# }}}
+
# {{{ _PostProcessNewEntity
=head2 _PostProcessNewEntity
my $Subject = shift;
- if ( $Subject =~ s/\[$RT::rtname \#(\d+)\s*\]//i ) {
+ if ( $Subject =~ s/\[\Q$RT::rtname\E\s+\#(\d+)\s*\]//i ) {
my $id = $1;
$RT::Logger->debug("Found a ticket ID. It's $id");
return ($id);
# }}}
-# {{{ sub MailError
-
-=head2 MailError { }
-
-
-# TODO this doesn't belong here.
-# TODO doc this
-
-
-=cut
-
-
-sub MailError {
- my $self = shift;
-
- my %args = (
- To => $RT::OwnerEmail,
- Bcc => undef,
- From => $RT::CorrespondAddress,
- Subject => 'There has been an error',
- Explanation => 'Unexplained error',
- MIMEObj => undef,
- LogLevel => 'crit',
- @_
- );
-
- $RT::Logger->log(
- level => $args{'LogLevel'},
- message => $args{'Explanation'}
- );
- my $entity = MIME::Entity->build(
- Type => "multipart/mixed",
- From => $args{'From'},
- Bcc => $args{'Bcc'},
- To => $args{'To'},
- Subject => $args{'Subject'},
- 'X-RT-Loop-Prevention' => $RT::rtname,
- );
-
- $entity->attach( Data => $args{'Explanation'} . "\n" );
-
- my $mimeobj = $args{'MIMEObj'};
- $mimeobj->sync_headers();
- $entity->add_part($mimeobj);
-
- if ( $RT::MailCommand eq 'sendmailpipe' ) {
- open( MAIL, "|$RT::SendmailPath $RT::SendmailArguments" ) || return (0);
- print MAIL $entity->as_string;
- close(MAIL);
- }
- else {
- $entity->send( $RT::MailCommand, $RT::MailParams );
- }
-}
-
-# }}}
-
-
-
-# {{{ sub GetCurrentUser
-
-sub GetCurrentUser {
- my $self = shift;
- my $ErrorsTo = shift;
-
- my %UserInfo = ();
-
- #Suck the address of the sender out of the header
- my ( $Address, $Name ) = $self->ParseSenderAddressFromHead();
-
- my $tempuser = RT::User->new($RT::SystemUser);
-
- #This will apply local address canonicalization rules
- $Address = $tempuser->CanonicalizeEmailAddress($Address);
-
- #If desired, synchronize with an external database
- my $UserFoundInExternalDatabase = 0;
-
- # Username is the 'Name' attribute of the user that RT uses for things
- # like authentication
- my $Username = undef;
- ( $UserFoundInExternalDatabase, %UserInfo ) =
- $self->LookupExternalUserInfo( $Address, $Name );
-
- $Address = $UserInfo{'EmailAddress'};
- $Username = $UserInfo{'Name'};
-
- #Get us a currentuser object to work with.
- my $CurrentUser = RT::CurrentUser->new();
-
- # First try looking up by a username, if we got one from the external
- # db lookup. Next, try looking up by email address. Failing that,
- # try looking up by users who have this user's email address as their
- # username.
-
- if ($Username) {
- $CurrentUser->LoadByName($Username);
- }
-
- unless ( $CurrentUser->Id ) {
- $CurrentUser->LoadByEmail($Address);
- }
-
- #If we can't get it by email address, try by name.
- unless ( $CurrentUser->Id ) {
- $CurrentUser->LoadByName($Address);
- }
-
- unless ( $CurrentUser->Id ) {
-
- #If we couldn't load a user, determine whether to create a user
-
- # {{{ If we require an incoming address to be found in the external
- # user database, reject the incoming message appropriately
- if ( $RT::SenderMustExistInExternalDatabase
- && !$UserFoundInExternalDatabase ) {
-
- my $Message =
- "Sender's email address was not found in the user database.";
-
- # {{{ This code useful only if you've defined an AutoRejectRequest template
-
- require RT::Template;
- my $template = new RT::Template($RT::Nobody);
- $template->Load('AutoRejectRequest');
- $Message = $template->Content || $Message;
-
- # }}}
-
- MailError(
- To => $ErrorsTo,
- Subject => "Ticket Creation failed: user could not be created",
- Explanation => $Message,
- MIMEObj => $self->Entity,
- LogLevel => 'notice' );
-
- return ($CurrentUser);
-
- }
-
- # }}}
-
- else {
- my $NewUser = RT::User->new($RT::SystemUser);
-
- my ( $Val, $Message ) = $NewUser->Create(
- Name => ( $Username || $Address ),
- EmailAddress => $Address,
- RealName => "$Name",
- Password => undef,
- Privileged => 0,
- Comments => 'Autocreated on ticket submission'
- );
-
- unless ($Val) {
-
- # Deal with the race condition of two account creations at once
- #
- if ($Username) {
- $NewUser->LoadByName($Username);
- }
-
- unless ( $NewUser->Id ) {
- $NewUser->LoadByEmail($Address);
- }
-
- unless ( $NewUser->Id ) {
- MailError(To => $ErrorsTo,
- Subject => "User could not be created",
- Explanation =>
- "User creation failed in mailgateway: $Message",
- MIMEObj => $self->Entity,
- LogLevel => 'crit' );
- }
- }
- }
-
- #Load the new user object
- $CurrentUser->LoadByEmail($Address);
-
- unless ( $CurrentUser->id ) {
- $RT::Logger->warning(
- "Couldn't load user '$Address'." . "giving up" );
- MailError(
- To => $ErrorsTo,
- Subject => "User could not be loaded",
- Explanation =>
- "User '$Address' could not be loaded in the mail gateway",
- MIMEObj => $self->Entity,
- LogLevel => 'crit' );
-
- }
- }
-
- return ($CurrentUser);
-
-}
-
-# }}}
# {{{ ParseCcAddressesFromHead
## Over max size and return them
sub _SetupMIMEParser {
- my $self = shift;
+ my $self = shift;
my $parser = shift;
- my $AttachmentDir = File::Temp::tempdir( TMPDIR => 1, CLEANUP => 1 );
# Set up output directory for files:
- $parser->output_dir("$AttachmentDir");
- #If someone includes a message, don't extract it
+ my $tmpdir = File::Temp::tempdir( TMPDIR => 1, CLEANUP => 1 );
+ push ( @{ $self->{'AttachmentDirs'} }, $tmpdir );
+ $parser->output_dir($tmpdir);
+ $parser->filer->ignore_filename(1);
+
+ #If someone includes a message, extract it
$parser->extract_nested_messages(1);
+ $parser->extract_uuencode(1); ### default is false
+
# Set up the prefix for files with auto-generated names:
$parser->output_prefix("part");
- # If content length is <= 50000 bytes, store each msg as in-core scalar;
- # Else, write to a disk file (the default action):
+ # do _not_ store each msg as in-core scalar;
- $parser->output_to_core(50000);
+ $parser->output_to_core(0);
}
+
# }}}
+sub DESTROY {
+ my $self = shift;
+ File::Path::rmtree([@{$self->{'AttachmentDirs'}}],0,1);
+}
+
+
+
eval "require RT::EmailParser_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/EmailParser_Vendor.pm});
eval "require RT::EmailParser_Local";