summaryrefslogtreecommitdiff
path: root/rt/bin/rt-mailgate
blob: e6f0d95c5d3963605889ce9398c95c8a4a60d8e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
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;