import rt 2.0.14
[freeside.git] / rt / bin / rt-mailgate
diff --git a/rt/bin/rt-mailgate b/rt/bin/rt-mailgate
new file mode 100755 (executable)
index 0000000..e6f0d95
--- /dev/null
@@ -0,0 +1,367 @@
+#!!!PERL!! -w
+
+# $Header: /home/cvs/cvsroot/freeside/rt/bin/rt-mailgate,v 1.1 2002-08-12 06:17:07 ivan Exp $
+# (c) 1996-2001 Jesse Vincent <jesse@fsck.com>
+# This software is redistributable under the terms of the GNU GPL
+
+
+package RT;
+use strict;
+use vars qw($VERSION $Handle $Nobody $SystemUser);
+
+$VERSION="!!RT_VERSION!!";
+
+
+use lib "!!RT_LIB_PATH!!";
+use lib "!!RT_ETC_PATH!!";
+
+use RT::Interface::Email  qw(CleanEnv LoadConfig DBConnect
+                            GetCurrentUser
+                            GetMessageContent
+                            CheckForLoops 
+                            CheckForSuspiciousSender
+                            CheckForAutoGenerated 
+                            ParseMIMEEntityFromSTDIN
+                            ParseTicketId 
+                            MailError 
+                            ParseCcAddressesFromHead
+                            ParseSenderAddressFromHead 
+                            ParseErrorsToAddressFromHead
+                           );
+
+#Clean out all the nasties from the environment
+CleanEnv();
+
+#Load etc/config.pm and drop privs
+LoadConfig();
+
+#Connect to the database and get RT::SystemUser and RT::Nobody loaded
+DBConnect();
+
+#Drop setgid permissions
+RT::DropSetGIDPermissions();
+
+use RT::Ticket;
+use RT::Queue;
+use MIME::Parser;
+use File::Temp;
+use Mail::Address;
+
+
+#Set some sensible defaults 
+my $Queue = 1;
+my $time = time;
+my $Action = "correspond";  
+
+my ($Verbose, $ReturnTid, $Debug);
+my ($From, $TicketId, $Subject,$SquelchReplies);
+
+# using --owner-from-extension, this will let you set ticket owner on create
+my $AssignTicketTo = undef;
+my ($status, $msg);
+
+# {{{ parse commandline 
+
+while (my $flag = shift @ARGV) {
+    if (($flag eq '-v') or ($flag eq '--verbose')) {
+       $Verbose = 1;
+    }
+    if (($flag eq '-t') or ($flag eq '--ticketid')) {
+       $ReturnTid = 1;
+    }
+    
+    if (($flag eq '-d') or ($flag eq '--debug')) {
+       $RT::Logger->debug("Debug mode enabled\n");
+       $Debug = 1;
+      }
+    
+    if (($flag eq '-q') or ($flag eq '--queue')) {
+       $Queue = shift @ARGV;
+    } 
+    if ($flag eq '--ticket-id-from-extension') {
+       $TicketId = $ENV{'EXTENSION'};
+    }
+    if ($flag eq '--queue-from-extension') {
+       $Queue = $ENV{'EXTENSION'};
+    }
+    if ($flag eq '--owner-from-extension') {
+        $AssignTicketTo = $ENV{'EXTENSION'};
+    }
+
+    if (($flag eq '-a') or ($flag eq '--action')) {
+         $Action = shift @ARGV;
+    } 
+    
+    
+}
+
+# }}}
+
+# get the current mime entity from stdin
+my ($entity, $head) = ParseMIMEEntityFromSTDIN();
+
+#Get someone to send runtime errors to;
+my $ErrorsTo = ParseErrorsToAddressFromHead($head);
+
+#Get us a current user object.
+my $CurrentUser = GetCurrentUser($head, $entity, $ErrorsTo);
+
+# We've already performed a warning and sent the mail off to somewhere safe ($RTOwner).
+#  this is _exceedingly_ unlikely but we don't want to keep going if we don't have a current user
+
+unless ($CurrentUser->Id) {
+       exit(1);
+}
+
+my $MessageId = $head->get('Message-Id') || 
+  "<no-message-id-".time.rand(2000)."\@.$RT::Organization>";
+
+#Pull apart the subject line
+$Subject = $head->get('Subject') || "[no subject]";
+chomp $Subject;
+
+# Get the ticket ID unless it's already set
+$TicketId = ParseTicketId($Subject) unless ($TicketId);
+
+#Set up a queue object
+my $QueueObj = RT::Queue->new($CurrentUser);
+$QueueObj->Load($Queue);
+unless ($QueueObj->id ) {
+
+  MailError(To => $RT::OwnerEmail,
+                  Subject => "RT Bounce: $Subject",
+                  Explanation => "RT couldn't find the queue: $Queue",
+                  MIMEObj => $entity);
+
+}
+
+# {{{ Lets check for mail loops of various sorts.
+
+my $IsAutoGenerated = CheckForAutoGenerated($head);
+
+my $IsSuspiciousSender = CheckForSuspiciousSender($head);
+
+my $IsALoop = CheckForLoops($head);
+
+
+#If the message is autogenerated, we need to know, so we can not 
+# send mail to the sender
+if ($IsSuspiciousSender || $IsAutoGenerated || $IsALoop) {
+    $SquelchReplies = 1;
+
+    $ErrorsTo = $RT::OwnerEmail;
+    
+    #TODO: Is what we want to do here really 
+    #  "Make the requestor cease to get mail from RT"?
+    # This might wreak havoc with vacation-mailing users.
+    # Maybe have a "disabled for bouncing" state that gets
+    # turned off when we get a legit incoming message
+
+}
+
+
+# {{{ Warn someone  if it's a loop
+
+# Warn someone if it's a loop, before we drop it on the ground
+if ($IsALoop) {
+    $RT::Logger->crit("RT Received mail ($MessageId) from itself.");
+    
+    #Should we mail it to RTOwner?
+    if ($RT::LoopsToRTOwner) {
+       MailError(To => $RT::OwnerEmail,
+                 Subject => "RT Bounce: $Subject",
+                 Explanation => "RT thinks this message may be a bounce",
+                 MIMEObj => $entity);
+       
+       #Do we actually want to store it?
+       exit unless ($RT::StoreLoops);
+    }
+}
+
+# }}}
+
+
+   #Don't let the user stuff the RT-Squelch-Replies-To header.
+    if ($head->get('RT-Squelch-Replies-To')) {
+        $head->add('RT-Relocated-Squelch-Replies-To',
+                   $head->get('RT-Squelch-Replies-To'));
+        $head->delete('RT-Squelch-Replies-To')
+    }
+
+
+if ($SquelchReplies) {
+    ## TODO: This is a hack.  It should be some other way to
+    ## indicate that the transaction should be "silent".
+
+    my ($Sender, $junk) = ParseSenderAddressFromHead($head);
+    $head->add('RT-Squelch-Replies-To', $Sender);
+}
+
+# }}}
+
+
+# {{{ If we require that the sender be found in an external DB and they're not
+# forward this message to RTOwner
+
+
+
+if ($RT::LookupSenderInExternalDatabase && 
+    $RT::SenderMustExistInExternalDatabase )  {
+
+    MailError(To => $RT::OwnerEmail,
+             Subject => "RT Bounce: $Subject",
+             Explanation => "RT couldn't find requestor via its external database lookup",
+             MIMEObj => $entity);
+    
+}
+
+# }}}
+
+# {{{ elsif we don't have a ticket Id, we're creating a new ticket
+
+
+
+elsif (!defined($TicketId)) {
+    
+    # {{{ Create a new ticket
+    if ($Action =~ /correspond/) {
+       
+       #    open a new ticket 
+       my @Requestors = ($CurrentUser->id);
+       
+       my @Cc;
+       if ($RT::ParseNewMessageForTicketCcs) {
+               @Cc = ParseCcAddressesFromHead(Head => $head, 
+                                       CurrentUser => $CurrentUser,
+                                       QueueObj => $QueueObj );
+       }
+
+       my $Ticket = new RT::Ticket($CurrentUser);
+       my ($id, $Transaction, $ErrStr) = 
+         $Ticket->Create ( Queue => $Queue,
+                           Subject => $Subject,
+                            Owner => $AssignTicketTo,
+                           Requestor => \@Requestors,
+                           Cc => \@Cc,
+                           MIMEObj => $entity
+                         );
+       if ($id == 0 ) {
+           MailError( To => $ErrorsTo,
+                      Subject => "Ticket creation failed",
+                      Explanation => $ErrStr,
+                      MIMEObj => $entity
+                    );
+           $RT::Logger->error("Create failed: $id / $Transaction / $ErrStr ");
+       }       
+    }
+
+    # }}}
+    
+    else {
+       #TODO Return an error message
+       MailError( To => $ErrorsTo,
+                  Subject => "No ticket id specified",
+                  Explanation => "$Action aliases require a TicketId to work on",
+                  MIMEObj => $entity
+                );
+       
+       $RT::Logger->crit("$Action aliases require a TicketId to work on ".
+                         "(from ".$CurrentUser->UserObj->EmailAddress.") ".
+                         $MessageId);
+    }
+}
+
+# }}}
+
+# {{{ If we've got a ticket ID, update the ticket
+
+else {
+    
+    #   If the action is comment, add a comment.
+    if ($Action =~ /comment/i){
+       
+       my $Ticket = new RT::Ticket($CurrentUser);
+       $Ticket->Load($TicketId);
+       unless ($Ticket->Id) {
+           MailError( To => $ErrorsTo,
+                      Subject => "Comment not recorded",
+                      Explanation => "Could not find a ticket with id $TicketId",
+                      MIMEObj => $entity
+                    );
+           #Return an error message saying that Ticket "#foo" wasn't found.
+       }
+       
+       ($status, $msg) = $Ticket->Comment(MIMEObj=>$entity);
+       unless ($status) {
+           #Warn the sender that we couldn't actually submit the comment.
+           MailError( To => $ErrorsTo,
+                      Subject => "Comment not recorded",
+                      Explanation => $msg,
+                      MIMEObj => $entity
+                    );
+       }       
+    }
+
+    # If the message is correspondence, add it to the ticket
+    elsif ($Action =~ /correspond/i) {
+       my $Ticket = RT::Ticket->new($CurrentUser);
+       $Ticket->Load($TicketId);
+       
+       #TODO: Check for error conditions
+       ($status, $msg) = $Ticket->Correspond(MIMEObj => $entity);
+       unless ($status) {
+
+           #Return mail to the sender with an error
+           MailError( To => $ErrorsTo,
+                      Subject => "Correspondence not recorded",
+                      Explanation => $msg,
+                      MIMEObj => $entity
+                    );
+       }
+    }
+
+    else {
+       #Return mail to the sender with an error
+           MailError( To => $ErrorsTo,
+                      Subject => "RT Configuration error",
+                      Explanation => "'$Action' not a recognized action.".
+                                     " Your RT administrator has misconfigured ".
+                                     "the mail aliases which invoke RT" ,
+                      MIMEObj => $entity
+                    );
+
+       $RT::Logger->crit("$Action type unknown for $MessageId");
+       
+    }
+    
+}
+
+# }}}
+
+$RT::Handle->Disconnect();
+
+
+# Everything below this line is a helper sub. most of them will eventually
+# move to Interface::Email
+
+#When we call die, trap it and log->crit with the value of the die.
+$SIG{__DIE__}  = sub {
+    unless ($^S || !defined $^S ) {
+        $RT::Logger->crit("$_[0]");
+       MailError( To => $ErrorsTo,  
+                  Bcc => $RT::OwnerEmail,
+                  Subject => "RT Critical error. Message not recorded!",
+                  Explanation => "$_[0]",
+                  MIMEObj => $entity
+                );
+       exit(-1);
+    }
+    else {
+        #Get out of here if we're in an eval
+        die $_[0];
+    }
+};
+
+
+
+1;