This commit was generated by cvs2svn to compensate for changes in r2523,
[freeside.git] / rt / bin / rt-mailgate
1 #!!!PERL!! -w
2
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
6
7
8 package RT;
9 use strict;
10 use vars qw($VERSION $Handle $Nobody $SystemUser);
11
12 $VERSION="!!RT_VERSION!!";
13
14
15 use lib "!!RT_LIB_PATH!!";
16 use lib "!!RT_ETC_PATH!!";
17
18 use RT::Interface::Email  qw(CleanEnv LoadConfig DBConnect
19                              GetCurrentUser
20                              GetMessageContent
21                              CheckForLoops 
22                              CheckForSuspiciousSender
23                              CheckForAutoGenerated 
24                              ParseMIMEEntityFromSTDIN
25                              ParseTicketId 
26                              MailError 
27                              ParseCcAddressesFromHead
28                              ParseSenderAddressFromHead 
29                              ParseErrorsToAddressFromHead
30                             );
31
32 #Clean out all the nasties from the environment
33 CleanEnv();
34
35 #Load etc/config.pm and drop privs
36 LoadConfig();
37
38 #Connect to the database and get RT::SystemUser and RT::Nobody loaded
39 DBConnect();
40
41 #Drop setgid permissions
42 RT::DropSetGIDPermissions();
43
44 use RT::Ticket;
45 use RT::Queue;
46 use MIME::Parser;
47 use File::Temp;
48 use Mail::Address;
49
50
51 #Set some sensible defaults 
52 my $Queue = 1;
53 my $time = time;
54 my $Action = "correspond";  
55
56 my ($Verbose, $ReturnTid, $Debug);
57 my ($From, $TicketId, $Subject,$SquelchReplies);
58
59 # using --owner-from-extension, this will let you set ticket owner on create
60 my $AssignTicketTo = undef;
61 my ($status, $msg);
62
63 # {{{ parse commandline 
64
65 while (my $flag = shift @ARGV) {
66     if (($flag eq '-v') or ($flag eq '--verbose')) {
67         $Verbose = 1;
68     }
69     if (($flag eq '-t') or ($flag eq '--ticketid')) {
70         $ReturnTid = 1;
71     }
72     
73     if (($flag eq '-d') or ($flag eq '--debug')) {
74         $RT::Logger->debug("Debug mode enabled\n");
75         $Debug = 1;
76       }
77     
78     if (($flag eq '-q') or ($flag eq '--queue')) {
79         $Queue = shift @ARGV;
80     } 
81     if ($flag eq '--ticket-id-from-extension') {
82        $TicketId = $ENV{'EXTENSION'};
83     }
84     if ($flag eq '--queue-from-extension') {
85        $Queue = $ENV{'EXTENSION'};
86     }
87     if ($flag eq '--owner-from-extension') {
88         $AssignTicketTo = $ENV{'EXTENSION'};
89     }
90
91     if (($flag eq '-a') or ($flag eq '--action')) {
92           $Action = shift @ARGV;
93     } 
94     
95     
96 }
97
98 # }}}
99
100 # get the current mime entity from stdin
101 my ($entity, $head) = ParseMIMEEntityFromSTDIN();
102
103 #Get someone to send runtime errors to;
104 my $ErrorsTo = ParseErrorsToAddressFromHead($head);
105
106 #Get us a current user object.
107 my $CurrentUser = GetCurrentUser($head, $entity, $ErrorsTo);
108
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
111
112 unless ($CurrentUser->Id) {
113         exit(1);
114 }
115
116 my $MessageId = $head->get('Message-Id') || 
117   "<no-message-id-".time.rand(2000)."\@.$RT::Organization>";
118
119 #Pull apart the subject line
120 $Subject = $head->get('Subject') || "[no subject]";
121 chomp $Subject;
122
123 # Get the ticket ID unless it's already set
124 $TicketId = ParseTicketId($Subject) unless ($TicketId);
125
126 #Set up a queue object
127 my $QueueObj = RT::Queue->new($CurrentUser);
128 $QueueObj->Load($Queue);
129 unless ($QueueObj->id ) {
130
131   MailError(To => $RT::OwnerEmail,
132                   Subject => "RT Bounce: $Subject",
133                   Explanation => "RT couldn't find the queue: $Queue",
134                   MIMEObj => $entity);
135
136 }
137
138 # {{{ Lets check for mail loops of various sorts.
139
140 my $IsAutoGenerated = CheckForAutoGenerated($head);
141
142 my $IsSuspiciousSender = CheckForSuspiciousSender($head);
143
144 my $IsALoop = CheckForLoops($head);
145
146
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) {
150     $SquelchReplies = 1;
151
152     $ErrorsTo = $RT::OwnerEmail;
153     
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
159
160 }
161
162
163 # {{{ Warn someone  if it's a loop
164
165 # Warn someone if it's a loop, before we drop it on the ground
166 if ($IsALoop) {
167     $RT::Logger->crit("RT Received mail ($MessageId) from itself.");
168     
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",
174                   MIMEObj => $entity);
175         
176         #Do we actually want to store it?
177         exit unless ($RT::StoreLoops);
178     }
179 }
180
181 # }}}
182
183
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')
189     }
190
191
192 if ($SquelchReplies) {
193     ## TODO: This is a hack.  It should be some other way to
194     ## indicate that the transaction should be "silent".
195
196     my ($Sender, $junk) = ParseSenderAddressFromHead($head);
197     $head->add('RT-Squelch-Replies-To', $Sender);
198 }
199
200 # }}}
201
202
203 # {{{ If we require that the sender be found in an external DB and they're not
204 # forward this message to RTOwner
205
206
207
208 if ($RT::LookupSenderInExternalDatabase && 
209     $RT::SenderMustExistInExternalDatabase )  {
210
211     MailError(To => $RT::OwnerEmail,
212               Subject => "RT Bounce: $Subject",
213               Explanation => "RT couldn't find requestor via its external database lookup",
214               MIMEObj => $entity);
215     
216 }
217
218 # }}}
219
220 # {{{ elsif we don't have a ticket Id, we're creating a new ticket
221
222
223
224 elsif (!defined($TicketId)) {
225     
226     # {{{ Create a new ticket
227     if ($Action =~ /correspond/) {
228         
229         #    open a new ticket 
230         my @Requestors = ($CurrentUser->id);
231         
232         my @Cc;
233         if ($RT::ParseNewMessageForTicketCcs) {
234                 @Cc = ParseCcAddressesFromHead(Head => $head, 
235                                         CurrentUser => $CurrentUser,
236                                         QueueObj => $QueueObj );
237         }
238
239         my $Ticket = new RT::Ticket($CurrentUser);
240         my ($id, $Transaction, $ErrStr) = 
241           $Ticket->Create ( Queue => $Queue,
242                             Subject => $Subject,
243                             Owner => $AssignTicketTo,
244                             Requestor => \@Requestors,
245                             Cc => \@Cc,
246                             MIMEObj => $entity
247                           );
248         if ($id == 0 ) {
249             MailError( To => $ErrorsTo,
250                        Subject => "Ticket creation failed",
251                        Explanation => $ErrStr,
252                        MIMEObj => $entity
253                      );
254             $RT::Logger->error("Create failed: $id / $Transaction / $ErrStr ");
255         }       
256     }
257
258     # }}}
259     
260     else {
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",
265                    MIMEObj => $entity
266                  );
267         
268         $RT::Logger->crit("$Action aliases require a TicketId to work on ".
269                           "(from ".$CurrentUser->UserObj->EmailAddress.") ".
270                           $MessageId);
271     }
272 }
273
274 # }}}
275
276 # {{{ If we've got a ticket ID, update the ticket
277
278 else {
279     
280     #   If the action is comment, add a comment.
281     if ($Action =~ /comment/i){
282         
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",
289                        MIMEObj => $entity
290                      );
291             #Return an error message saying that Ticket "#foo" wasn't found.
292         }
293         
294         ($status, $msg) = $Ticket->Comment(MIMEObj=>$entity);
295         unless ($status) {
296             #Warn the sender that we couldn't actually submit the comment.
297             MailError( To => $ErrorsTo,
298                        Subject => "Comment not recorded",
299                        Explanation => $msg,
300                        MIMEObj => $entity
301                      );
302         }       
303     }
304
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);
309         
310         #TODO: Check for error conditions
311         ($status, $msg) = $Ticket->Correspond(MIMEObj => $entity);
312         unless ($status) {
313
314             #Return mail to the sender with an error
315             MailError( To => $ErrorsTo,
316                        Subject => "Correspondence not recorded",
317                        Explanation => $msg,
318                        MIMEObj => $entity
319                      );
320         }
321     }
322
323     else {
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" ,
330                        MIMEObj => $entity
331                      );
332
333         $RT::Logger->crit("$Action type unknown for $MessageId");
334         
335     }
336     
337 }
338
339 # }}}
340
341 $RT::Handle->Disconnect();
342
343
344 # Everything below this line is a helper sub. most of them will eventually
345 # move to Interface::Email
346
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]",
355                    MIMEObj => $entity
356                  );
357         exit(-1);
358     }
359     else {
360         #Get out of here if we're in an eval
361         die $_[0];
362     }
363 };
364
365
366
367 1;