3 # $Header: /home/cvs/cvsroot/freeside/rt/bin/rt-mailgate,v 1.1 2002-08-12 06:17:07 ivan Exp $
4 # (c) 1996-2001 Jesse Vincent <jesse@fsck.com>
5 # This software is redistributable under the terms of the GNU GPL
10 use vars qw($VERSION $Handle $Nobody $SystemUser);
12 $VERSION="!!RT_VERSION!!";
15 use lib "!!RT_LIB_PATH!!";
16 use lib "!!RT_ETC_PATH!!";
18 use RT::Interface::Email qw(CleanEnv LoadConfig DBConnect
22 CheckForSuspiciousSender
24 ParseMIMEEntityFromSTDIN
27 ParseCcAddressesFromHead
28 ParseSenderAddressFromHead
29 ParseErrorsToAddressFromHead
32 #Clean out all the nasties from the environment
35 #Load etc/config.pm and drop privs
38 #Connect to the database and get RT::SystemUser and RT::Nobody loaded
41 #Drop setgid permissions
42 RT::DropSetGIDPermissions();
51 #Set some sensible defaults
54 my $Action = "correspond";
56 my ($Verbose, $ReturnTid, $Debug);
57 my ($From, $TicketId, $Subject,$SquelchReplies);
59 # using --owner-from-extension, this will let you set ticket owner on create
60 my $AssignTicketTo = undef;
63 # {{{ parse commandline
65 while (my $flag = shift @ARGV) {
66 if (($flag eq '-v') or ($flag eq '--verbose')) {
69 if (($flag eq '-t') or ($flag eq '--ticketid')) {
73 if (($flag eq '-d') or ($flag eq '--debug')) {
74 $RT::Logger->debug("Debug mode enabled\n");
78 if (($flag eq '-q') or ($flag eq '--queue')) {
81 if ($flag eq '--ticket-id-from-extension') {
82 $TicketId = $ENV{'EXTENSION'};
84 if ($flag eq '--queue-from-extension') {
85 $Queue = $ENV{'EXTENSION'};
87 if ($flag eq '--owner-from-extension') {
88 $AssignTicketTo = $ENV{'EXTENSION'};
91 if (($flag eq '-a') or ($flag eq '--action')) {
92 $Action = shift @ARGV;
100 # get the current mime entity from stdin
101 my ($entity, $head) = ParseMIMEEntityFromSTDIN();
103 #Get someone to send runtime errors to;
104 my $ErrorsTo = ParseErrorsToAddressFromHead($head);
106 #Get us a current user object.
107 my $CurrentUser = GetCurrentUser($head, $entity, $ErrorsTo);
109 # We've already performed a warning and sent the mail off to somewhere safe ($RTOwner).
110 # this is _exceedingly_ unlikely but we don't want to keep going if we don't have a current user
112 unless ($CurrentUser->Id) {
116 my $MessageId = $head->get('Message-Id') ||
117 "<no-message-id-".time.rand(2000)."\@.$RT::Organization>";
119 #Pull apart the subject line
120 $Subject = $head->get('Subject') || "[no subject]";
123 # Get the ticket ID unless it's already set
124 $TicketId = ParseTicketId($Subject) unless ($TicketId);
126 #Set up a queue object
127 my $QueueObj = RT::Queue->new($CurrentUser);
128 $QueueObj->Load($Queue);
129 unless ($QueueObj->id ) {
131 MailError(To => $RT::OwnerEmail,
132 Subject => "RT Bounce: $Subject",
133 Explanation => "RT couldn't find the queue: $Queue",
138 # {{{ Lets check for mail loops of various sorts.
140 my $IsAutoGenerated = CheckForAutoGenerated($head);
142 my $IsSuspiciousSender = CheckForSuspiciousSender($head);
144 my $IsALoop = CheckForLoops($head);
147 #If the message is autogenerated, we need to know, so we can not
148 # send mail to the sender
149 if ($IsSuspiciousSender || $IsAutoGenerated || $IsALoop) {
152 $ErrorsTo = $RT::OwnerEmail;
154 #TODO: Is what we want to do here really
155 # "Make the requestor cease to get mail from RT"?
156 # This might wreak havoc with vacation-mailing users.
157 # Maybe have a "disabled for bouncing" state that gets
158 # turned off when we get a legit incoming message
163 # {{{ Warn someone if it's a loop
165 # Warn someone if it's a loop, before we drop it on the ground
167 $RT::Logger->crit("RT Received mail ($MessageId) from itself.");
169 #Should we mail it to RTOwner?
170 if ($RT::LoopsToRTOwner) {
171 MailError(To => $RT::OwnerEmail,
172 Subject => "RT Bounce: $Subject",
173 Explanation => "RT thinks this message may be a bounce",
176 #Do we actually want to store it?
177 exit unless ($RT::StoreLoops);
184 #Don't let the user stuff the RT-Squelch-Replies-To header.
185 if ($head->get('RT-Squelch-Replies-To')) {
186 $head->add('RT-Relocated-Squelch-Replies-To',
187 $head->get('RT-Squelch-Replies-To'));
188 $head->delete('RT-Squelch-Replies-To')
192 if ($SquelchReplies) {
193 ## TODO: This is a hack. It should be some other way to
194 ## indicate that the transaction should be "silent".
196 my ($Sender, $junk) = ParseSenderAddressFromHead($head);
197 $head->add('RT-Squelch-Replies-To', $Sender);
203 # {{{ If we require that the sender be found in an external DB and they're not
204 # forward this message to RTOwner
208 if ($RT::LookupSenderInExternalDatabase &&
209 $RT::SenderMustExistInExternalDatabase ) {
211 MailError(To => $RT::OwnerEmail,
212 Subject => "RT Bounce: $Subject",
213 Explanation => "RT couldn't find requestor via its external database lookup",
220 # {{{ elsif we don't have a ticket Id, we're creating a new ticket
224 elsif (!defined($TicketId)) {
226 # {{{ Create a new ticket
227 if ($Action =~ /correspond/) {
230 my @Requestors = ($CurrentUser->id);
233 if ($RT::ParseNewMessageForTicketCcs) {
234 @Cc = ParseCcAddressesFromHead(Head => $head,
235 CurrentUser => $CurrentUser,
236 QueueObj => $QueueObj );
239 my $Ticket = new RT::Ticket($CurrentUser);
240 my ($id, $Transaction, $ErrStr) =
241 $Ticket->Create ( Queue => $Queue,
243 Owner => $AssignTicketTo,
244 Requestor => \@Requestors,
249 MailError( To => $ErrorsTo,
250 Subject => "Ticket creation failed",
251 Explanation => $ErrStr,
254 $RT::Logger->error("Create failed: $id / $Transaction / $ErrStr ");
261 #TODO Return an error message
262 MailError( To => $ErrorsTo,
263 Subject => "No ticket id specified",
264 Explanation => "$Action aliases require a TicketId to work on",
268 $RT::Logger->crit("$Action aliases require a TicketId to work on ".
269 "(from ".$CurrentUser->UserObj->EmailAddress.") ".
276 # {{{ If we've got a ticket ID, update the ticket
280 # If the action is comment, add a comment.
281 if ($Action =~ /comment/i){
283 my $Ticket = new RT::Ticket($CurrentUser);
284 $Ticket->Load($TicketId);
285 unless ($Ticket->Id) {
286 MailError( To => $ErrorsTo,
287 Subject => "Comment not recorded",
288 Explanation => "Could not find a ticket with id $TicketId",
291 #Return an error message saying that Ticket "#foo" wasn't found.
294 ($status, $msg) = $Ticket->Comment(MIMEObj=>$entity);
296 #Warn the sender that we couldn't actually submit the comment.
297 MailError( To => $ErrorsTo,
298 Subject => "Comment not recorded",
305 # If the message is correspondence, add it to the ticket
306 elsif ($Action =~ /correspond/i) {
307 my $Ticket = RT::Ticket->new($CurrentUser);
308 $Ticket->Load($TicketId);
310 #TODO: Check for error conditions
311 ($status, $msg) = $Ticket->Correspond(MIMEObj => $entity);
314 #Return mail to the sender with an error
315 MailError( To => $ErrorsTo,
316 Subject => "Correspondence not recorded",
324 #Return mail to the sender with an error
325 MailError( To => $ErrorsTo,
326 Subject => "RT Configuration error",
327 Explanation => "'$Action' not a recognized action.".
328 " Your RT administrator has misconfigured ".
329 "the mail aliases which invoke RT" ,
333 $RT::Logger->crit("$Action type unknown for $MessageId");
341 $RT::Handle->Disconnect();
344 # Everything below this line is a helper sub. most of them will eventually
345 # move to Interface::Email
347 #When we call die, trap it and log->crit with the value of the die.
348 $SIG{__DIE__} = sub {
349 unless ($^S || !defined $^S ) {
350 $RT::Logger->crit("$_[0]");
351 MailError( To => $ErrorsTo,
352 Bcc => $RT::OwnerEmail,
353 Subject => "RT Critical error. Message not recorded!",
354 Explanation => "$_[0]",
360 #Get out of here if we're in an eval