]>
Commit | Line | Data |
---|---|---|
cc0e56be MT |
1 | #!/usr/bin/perl -w |
2 | ############################################################################## | |
3 | ## sendEmail | |
4 | ## Written by: Brandon Zehm <caspian@dotconf.net> | |
5 | ## | |
6 | ## License: | |
7 | ## sendEmail (hereafter referred to as "program") is free software; | |
8 | ## you can redistribute it and/or modify it under the terms of the GNU General | |
9 | ## Public License as published by the Free Software Foundation; either version | |
10 | ## 2 of the License, or (at your option) any later version. | |
11 | ## Note that when redistributing modified versions of this source code, you | |
12 | ## must ensure that this disclaimer and the above coder's names are included | |
13 | ## VERBATIM in the modified code. | |
14 | ## | |
15 | ## Disclaimer: | |
16 | ## This program is provided with no warranty of any kind, either expressed or | |
17 | ## implied. It is the responsibility of the user (you) to fully research and | |
18 | ## comprehend the usage of this program. As with any tool, it can be misused, | |
19 | ## either intentionally (you're a vandal) or unintentionally (you're a moron). | |
20 | ## THE AUTHOR(S) IS(ARE) NOT RESPONSIBLE FOR ANYTHING YOU DO WITH THIS PROGRAM | |
21 | ## or anything that happens because of your use (or misuse) of this program, | |
22 | ## including but not limited to anything you, your lawyers, or anyone else | |
23 | ## can dream up. And now, a relevant quote directly from the GPL: | |
24 | ## | |
25 | ## NO WARRANTY | |
26 | ## | |
27 | ## 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | |
28 | ## FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | |
29 | ## OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | |
30 | ## PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | |
31 | ## OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
32 | ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS | |
33 | ## TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE | |
34 | ## PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | |
35 | ## REPAIR OR CORRECTION. | |
36 | ## | |
37 | ############################################################################## | |
38 | use strict; | |
39 | use IO::Socket; | |
40 | ||
41 | ||
42 | ||
43 | ######################## | |
44 | ## Global Variables ## | |
45 | ######################## | |
46 | ||
47 | my %conf = ( | |
48 | ## General | |
49 | "programName" => $0, ## The name of this program | |
50 | "version" => '1.52', ## The version of this program | |
51 | "authorName" => 'Brandon Zehm', ## Author's Name | |
52 | "authorEmail" => 'caspian@dotconf.net', ## Author's Email Address | |
53 | "timezone" => '+0000 (GMT)', ## We always use +0000 for the time zone | |
54 | "hostname" => 'localhost', ## Used in printmsg() for all output, and in SMTP EHLO. | |
55 | "debug" => 0, ## Default debug level | |
56 | "error" => '', ## Error messages will often be stored here | |
57 | ||
58 | ## Logging | |
59 | "debug" => 0, | |
60 | "stdout" => 1, | |
61 | "logging" => 0, ## If this is true the printmsg function prints to the log file | |
62 | "logFile" => '', ## If this is specified (form the command line via -l) this file will be used for logging. | |
63 | ||
64 | ## Network | |
65 | "server" => 'localhost', ## Default SMTP server | |
66 | "port" => 25, ## Default port | |
67 | "alarm" => '', ## Default timeout for connects and reads, this gets set from $opt{'timeout'} | |
68 | ||
69 | ||
70 | "delimiter" => "----MIME delimiter for sendEmail-" ## MIME Delimiter | |
71 | . rand(1000000), ## Add some randomness to the delimiter | |
72 | "Message-ID" => rand(1000000) . "-sendEmail", ## Message-ID for email header | |
73 | "authUsername" => '', ## Username used in SMTP Auth | |
74 | "authPassword" => '', ## Password used in SMTP Auth | |
75 | ||
76 | ); | |
77 | ||
78 | ||
79 | ## This hash stores the options passed on the command line via the -o option. | |
80 | my %opt = ( | |
81 | ## Addressing | |
82 | "reply-to" => '', ## Reply-To field | |
83 | ||
84 | ## Message | |
85 | "message-file" => '', ## File to read message body from | |
86 | "message-header" => '', ## Additional email header line | |
87 | "message-format" => 'normal', ## If "raw" is specified the message is sent unmodified | |
88 | "message-charset" => 'iso-8859-1', ## Message character-set | |
89 | ||
90 | ## Network | |
91 | "timeout" => 60, ## Default timeout for connects and reads, this is copied to $conf{'alarm'} later. | |
92 | ||
93 | ); | |
94 | ||
95 | ## More variables used later in the program | |
96 | my $CRLF = "\015\012"; | |
97 | my $subject = ''; | |
98 | my $message = ''; | |
99 | my $from = ''; | |
100 | my @to = (); | |
101 | my @cc = (); | |
102 | my @bcc = (); | |
103 | my @attachments = (); | |
104 | my @attachments_names = (); | |
105 | ||
106 | ## For printing colors to the console | |
107 | my ${colorRed} = "\033[31;1m"; | |
108 | my ${colorGreen} = "\033[32;1m"; | |
109 | my ${colorCyan} = "\033[36;1m"; | |
110 | my ${colorWhite} = "\033[37;1m"; | |
111 | my ${colorNormal} = "\033[m"; | |
112 | my ${colorBold} = "\033[1m"; | |
113 | my ${colorNoBold} = "\033[0m"; | |
114 | ||
115 | ## Don't use shell escape codes on Windows systems | |
116 | if ($^O =~ /win/i) { | |
117 | ${colorRed} = ""; | |
118 | ${colorGreen} = ""; | |
119 | ${colorCyan} = ""; | |
120 | ${colorWhite} = ""; | |
121 | ${colorNormal} = ""; | |
122 | ${colorBold} = ""; | |
123 | ${colorNoBold} = ""; | |
124 | } | |
125 | ||
126 | ||
127 | ||
128 | ||
129 | ||
130 | ############################# | |
131 | ## | |
132 | ## MAIN PROGRAM | |
133 | ## | |
134 | ############################# | |
135 | ||
136 | ||
137 | ## Initialize | |
138 | initialize(); | |
139 | ||
140 | ## Process Command Line | |
141 | processCommandLine(); | |
142 | $conf{'alarm'} = $opt{'timeout'}; | |
143 | ||
144 | ## Abort program after $conf{'alarm'} seconds to avoid infinite hangs | |
145 | alarm($conf{'alarm'}) if ($^O !~ /win/i); ## alarm() doesn't work in win32 | |
146 | ||
147 | ||
148 | ||
149 | ||
150 | ################################################### | |
151 | ## Read $message from STDIN if -m was not used ## | |
152 | ################################################### | |
153 | ||
154 | if (!($message)) { | |
155 | ## Read message body from a file specified with -o message-file= | |
156 | if ($opt{'message-file'}) { | |
157 | if (! -e $opt{'message-file'}) { | |
158 | printmsg("ERROR => Message body file specified [$opt{'message-file'}] does not exist!", 0); | |
159 | printmsg("HINT => 1) check spelling of your file; 2) fully qualify the path; 3) doubble quote it", 1); | |
160 | quit("", 1); | |
161 | } | |
162 | if (! -r $opt{'message-file'}) { | |
163 | printmsg("ERROR => Message body file specified can not be read due to restricted permissions!", 0); | |
164 | printmsg("HINT => Check permissions on file specified to ensure it can be read", 1); | |
165 | quit("", 1); | |
166 | } | |
167 | if (!open(MFILE, "< " . $opt{'message-file'})) { | |
168 | printmsg("ERROR => Error opening message body file [$opt{'message-file'}]: $!", 0); | |
169 | quit("", 1); | |
170 | } | |
171 | while (<MFILE>) { | |
172 | $message .= $_; | |
173 | } | |
174 | close(MFILE); | |
175 | } | |
176 | ||
177 | ## Read message body from STDIN | |
178 | else { | |
179 | alarm($conf{'alarm'}) if ($^O !~ /win/i); ## alarm() doesn't work in win32 | |
180 | if ($conf{'stdout'}) { | |
181 | print "Reading message body from STDIN because the '-m' option was not used.\n"; | |
182 | print "If you are manually typing in a message:\n"; | |
183 | print " - First line must be received within $conf{'alarm'} seconds.\n" if ($^O !~ /win/i); | |
184 | print " - End manual input with a CTRL-D on its own line.\n\n" if ($^O !~ /win/i); | |
185 | print " - End manual input with a CTRL-Z on its own line.\n\n" if ($^O =~ /win/i); | |
186 | } | |
187 | while (<STDIN>) { ## Read STDIN into $message | |
188 | $message .= $_; | |
189 | alarm(0) if ($^O !~ /win/i); ## Disable the alarm since at least one line was received | |
190 | } | |
191 | printmsg("Message input complete.", 0); | |
192 | } | |
193 | } | |
194 | ||
195 | ## Replace bare LF's with CRLF's (\012 should always have \015 with it) | |
196 | $message =~ s/(\015)?(\012|$)/\015\012/g; | |
197 | ||
198 | ## Replace bare CR's with CRLF's (\015 should always have \012 with it) | |
199 | $message =~ s/(\015)(\012|$)?/\015\012/g; | |
200 | ||
201 | ## Check message for bare periods and encode them | |
202 | $message =~ s/(^|$CRLF)(\.{1})($CRLF|$)/$1.$2$3/g; | |
203 | ||
204 | ## Get the current date for the email header | |
205 | my ($sec,$min,$hour,$mday,$mon,$year,$day) = gmtime(); | |
206 | $year += 1900; $mon = return_month($mon); $day = return_day($day); | |
207 | my $date = sprintf("%s, %s %s %d %.2d:%.2d:%.2d %s",$day, $mday, $mon, $year, $hour, $min, $sec, $conf{'timezone'}); | |
208 | ||
209 | ||
210 | ||
211 | ||
212 | ################################## | |
213 | ## Connect to the SMTP server ## | |
214 | ################################## | |
215 | printmsg("DEBUG => Connecting to $conf{'server'}:$conf{'port'}", 1); | |
216 | $SIG{'ALRM'} = sub { | |
217 | printmsg("ERROR => Timeout while connecting to $conf{'server'}:$conf{'port'} There was no response after $conf{'alarm'} seconds.", 0); | |
218 | printmsg("HINT => Try specifying a different mail relay with the -s option.", 1); | |
219 | quit("", 1); | |
220 | }; | |
221 | alarm($conf{'alarm'}) if ($^O !~ /win/i); ## alarm() doesn't work in win32; | |
222 | my $SERVER = IO::Socket::INET->new( PeerAddr => $conf{'server'}, | |
223 | PeerPort => $conf{'port'}, | |
224 | Proto => 'tcp', | |
225 | Autoflush => 1, | |
226 | timeout => $conf{'alarm'}, | |
227 | ); | |
228 | alarm(0) if ($^O !~ /win/i); ## alarm() doesn't work in win32; | |
229 | ||
230 | ## Make sure we got connected | |
231 | if ( (!$SERVER) or (!$SERVER->opened()) ) { | |
232 | printmsg("ERROR => Connection attempt to $conf{'server'}:$conf{'port'} failed: $@", 0); | |
233 | printmsg("HINT => Try specifying a different mail relay with the -s option.", 1); | |
234 | quit("", 1); | |
235 | } | |
236 | ||
237 | ||
238 | ||
239 | ||
240 | ||
241 | ||
242 | ######################### | |
243 | ## Do the SMTP Dance ## | |
244 | ######################### | |
245 | ||
246 | ## Read initial greeting to make sure we're talking to a live SMTP server | |
247 | if (SMTPchat()) { quit($conf{'error'}, 1); } | |
248 | ||
249 | ## EHLO | |
250 | if (SMTPchat('EHLO ' . $conf{'hostname'})) { | |
251 | printmsg($conf{'error'}, 0); | |
252 | printmsg("NOTICE => EHLO command failed, attempting HELO instead"); | |
253 | if (SMTPchat('HELO ' . $conf{'hostname'})) { quit($conf{'error'}, 1); } | |
254 | if ( $conf{'authUsername'} and $conf{'authPassword'} ) { | |
255 | printmsg("WARNING => The mail server does not support ESMTP or SMTP AUTH!"); | |
256 | } | |
257 | } | |
258 | else { | |
259 | ## Do SMTP Auth if required | |
260 | if ( $conf{'authUsername'} and $conf{'authPassword'} ) { | |
261 | if (SMTPchat('AUTH LOGIN')) { quit($conf{'error'}, 1); } | |
262 | if (SMTPchat($conf{'authUsername'})) { quit($conf{'error'}, 1); } | |
263 | if (SMTPchat($conf{'authPassword'})) { quit($conf{'error'}, 1); } | |
264 | } | |
265 | } | |
266 | ||
267 | ## MAIL FROM | |
268 | if (SMTPchat('MAIL FROM:<' .(returnAddressParts($from))[1]. '>')) { quit($conf{'error'}, 1); } | |
269 | ||
270 | ## RCPT TO | |
271 | my $oneRcptAccepted = 0; | |
272 | foreach my $rcpt (@to, @cc, @bcc) { | |
273 | my ($name, $address) = returnAddressParts($rcpt); | |
274 | if (SMTPchat('RCPT TO:<' . $address . '>')) { | |
275 | printmsg("WARNING => The recipient <$address> was rejected by the mail server, error follows:", 0); | |
276 | $conf{'error'} =~ s/^ERROR/WARNING/o; | |
277 | printmsg($conf{'error'}, 0); | |
278 | } | |
279 | elsif ($oneRcptAccepted == 0) { | |
280 | $oneRcptAccepted = 1; | |
281 | } | |
282 | } | |
283 | ## If no recipients were accepted we need to exit with an error. | |
284 | if ($oneRcptAccepted == 0) { | |
285 | quit("ERROR => Exiting. No recipients were accepted for delivery by the mail server.", 1); | |
286 | } | |
287 | ||
288 | ## DATA | |
289 | if (SMTPchat('DATA')) { quit($conf{'error'}, 1); } | |
290 | ||
291 | ||
292 | ############################### | |
293 | ## Build and send the body ## | |
294 | ############################### | |
295 | printmsg("INFO => Sending message body",1); | |
296 | ||
297 | ## If the message-format is raw just send the message as-is. | |
298 | if ($opt{'message-format'} =~ /^raw$/i) { | |
299 | print $SERVER $message; | |
300 | } | |
301 | ||
302 | ## If the message-format isn't raw, then build and send the message, | |
303 | else { | |
304 | ||
305 | ## Message-ID: <MessageID> | |
306 | print $SERVER 'Message-ID: <' . $conf{'Message-ID'} . '@' . $conf{'hostname'} . '>' . $CRLF; | |
307 | ||
308 | ## From: "Name" <address@domain.com> (the pointless test below is just to keep scoping correct) | |
309 | if ($from) { | |
310 | my ($name, $address) = returnAddressParts($from); | |
311 | print $SERVER 'From: "' . $name . '" <' . $address . '>' . $CRLF; | |
312 | } | |
313 | ||
314 | ## Reply-To: | |
315 | if ($opt{'reply-to'}) { | |
316 | my ($name, $address) = returnAddressParts($opt{'reply-to'}); | |
317 | print $SERVER 'Reply-To: "' . $name . '" <' . $address . '>' . $CRLF; | |
318 | } | |
319 | ||
320 | ## To: "Name" <address@domain.com> | |
321 | if (scalar(@to) > 0) { | |
322 | print $SERVER "To:"; | |
323 | for (my $a = 0; $a < scalar(@to); $a++) { | |
324 | my $msg = ""; | |
325 | ||
326 | my ($name, $address) = returnAddressParts($to[$a]); | |
327 | $msg = " \"$name\" <$address>"; | |
328 | ||
329 | ## If we're not on the last address add a comma to the end of the line. | |
330 | if (($a + 1) != scalar(@to)) { | |
331 | $msg .= ","; | |
332 | } | |
333 | ||
334 | print $SERVER $msg . $CRLF; | |
335 | } | |
336 | } | |
337 | ## We always want a To: line so if the only recipients were bcc'd they don't see who it was sent to | |
338 | else { | |
339 | print $SERVER "To: \"Undisclosed Recipients\" <>$CRLF"; | |
340 | } | |
341 | ||
342 | if (scalar(@cc) > 0) { | |
343 | print $SERVER "Cc:"; | |
344 | for (my $a = 0; $a < scalar(@cc); $a++) { | |
345 | my $msg = ""; | |
346 | ||
347 | my ($name, $address) = returnAddressParts($cc[$a]); | |
348 | $msg = " \"$name\" <$address>"; | |
349 | ||
350 | ## If we're not on the last address add a comma to the end of the line. | |
351 | if (($a + 1) != scalar(@cc)) { | |
352 | $msg .= ","; | |
353 | } | |
354 | ||
355 | print $SERVER $msg . $CRLF; | |
356 | } | |
357 | } | |
358 | ||
359 | print $SERVER 'Subject: ' . $subject . $CRLF; ## Subject | |
360 | print $SERVER 'Date: ' . $date . $CRLF; ## Date | |
361 | print $SERVER 'X-Mailer: sendEmail-'.$conf{'version'}.$CRLF; ## X-Mailer | |
362 | ||
363 | ## Send an additional message header line if specified | |
364 | if ($opt{'message-header'}) { | |
365 | print $SERVER $opt{'message-header'} . $CRLF; | |
366 | } | |
367 | ||
368 | ## Encode all messages with MIME. | |
369 | print $SERVER "MIME-Version: 1.0$CRLF"; | |
370 | print $SERVER "Content-Type: multipart/mixed; boundary=\"$conf{'delimiter'}\"$CRLF"; | |
371 | print $SERVER "$CRLF"; | |
372 | print $SERVER "This is a multi-part message in MIME format. To properly display this message you need a MIME-Version 1.0 compliant Email program.$CRLF"; | |
373 | print $SERVER "$CRLF"; | |
374 | ||
375 | ||
376 | ## Send message body | |
377 | print $SERVER "--$conf{'delimiter'}$CRLF"; | |
378 | ## If the message contains HTML change the Content-Type | |
379 | if ($message =~ /^\s*<html>/i) { | |
380 | printmsg("Message is in HTML format", 1); | |
381 | print $SERVER "Content-Type: text/html;$CRLF"; | |
382 | } | |
383 | ## Otherwise it's a normal text email | |
384 | else { | |
385 | print $SERVER "Content-Type: text/plain;$CRLF"; | |
386 | } | |
387 | print $SERVER " charset=\"" . $opt{'message-charset'} . "\"$CRLF"; | |
388 | print $SERVER "Content-Transfer-Encoding: 7bit$CRLF"; | |
389 | print $SERVER $CRLF; | |
390 | print $SERVER $message; | |
391 | ||
392 | ||
393 | ||
394 | ## Send Attachemnts | |
395 | if ($attachments[0]) { | |
396 | ## Disable the alarm so people on modems can send big attachments | |
397 | alarm(0) if ($^O !~ /win/i); ## alarm() doesn't work in win32 | |
398 | ||
399 | ## Send the attachments | |
400 | foreach my $filename (@attachments) { | |
401 | ## This is check 2, we already checked this above, but just in case... | |
402 | if ( ! -f $filename ) { | |
403 | printmsg("ERROR => The file [$filename] doesn't exist! Email will be sent, but without that attachment.", 0); | |
404 | } | |
405 | elsif ( ! -r $filename ) { | |
406 | printmsg("ERROR => Couldn't open the file [$filename] for reading: $! Email will be sent, but without that attachment.", 0); | |
407 | } | |
408 | else { | |
409 | printmsg("DEBUG => Sending the attachment [$filename]", 1); | |
410 | send_attachment($filename); | |
411 | } | |
412 | } | |
413 | } | |
414 | ||
415 | ||
416 | ## End the mime encoded message | |
417 | print $SERVER "$CRLF--$conf{'delimiter'}--$CRLF"; | |
418 | } | |
419 | ||
420 | ||
421 | ## Tell the server we are done sending the email | |
422 | print $SERVER "$CRLF.$CRLF"; | |
423 | if (SMTPchat()) { quit($conf{'error'}, 1); } | |
424 | ||
425 | ||
426 | ||
427 | #################### | |
428 | # We are done!!! # | |
429 | #################### | |
430 | ||
431 | ## Disconnect from the server | |
432 | if (SMTPchat('QUIT')) { quit($conf{'error'}, 1); } | |
433 | close $SERVER; | |
434 | ||
435 | ||
436 | ||
437 | ||
438 | ||
439 | ||
440 | ####################################### | |
441 | ## Generate exit message/log entry ## | |
442 | ####################################### | |
443 | ||
444 | if ($conf{'debug'} or $conf{'logging'}) { | |
445 | printmsg("Generating a detailed exit message", 3); | |
446 | ||
447 | ## Put the message together | |
448 | my $output = "Email was sent successfully! From: <" . (returnAddressParts($from))[1] . "> "; | |
449 | ||
450 | if (scalar(@to) > 0) { | |
451 | $output .= "To: "; | |
452 | for ($a = 0; $a < scalar(@to); $a++) { | |
453 | $output .= "<" . (returnAddressParts($to[$a]))[1] . "> "; | |
454 | } | |
455 | } | |
456 | if (scalar(@cc) > 0) { | |
457 | $output .= "Cc: "; | |
458 | for ($a = 0; $a < scalar(@cc); $a++) { | |
459 | $output .= "<" . (returnAddressParts($cc[$a]))[1] . "> "; | |
460 | } | |
461 | } | |
462 | if (scalar(@bcc) > 0) { | |
463 | $output .= "Bcc: "; | |
464 | for ($a = 0; $a < scalar(@bcc); $a++) { | |
465 | $output .= "<" . (returnAddressParts($bcc[$a]))[1] . "> "; | |
466 | } | |
467 | } | |
468 | $output .= "Subject: [$subject] " if ($subject); | |
469 | if (scalar(@attachments_names) > 0) { | |
470 | $output .= "Attachment(s): "; | |
471 | foreach(@attachments_names) { | |
472 | $output .= "[$_] "; | |
473 | } | |
474 | } | |
475 | $output .= "Server: [$conf{'server'}:$conf{'port'}]"; | |
476 | ||
477 | ||
478 | ###################### | |
479 | # Exit the program # | |
480 | ###################### | |
481 | ||
482 | ## Print / Log the detailed message | |
483 | quit($output, 0); | |
484 | } | |
485 | else { | |
486 | ## Or the standard message | |
487 | quit("Email was sent successfully!", 0); | |
488 | } | |
489 | ||
490 | ||
491 | ||
492 | ||
493 | ||
494 | ||
495 | ||
496 | ||
497 | ||
498 | ||
499 | ||
500 | ||
501 | ||
502 | ||
503 | ||
504 | ||
505 | ||
506 | ||
507 | ||
508 | ||
509 | ||
510 | ||
511 | ||
512 | ||
513 | ||
514 | ||
515 | ||
516 | ||
517 | ||
518 | ||
519 | ||
520 | ||
521 | ||
522 | ||
523 | ||
524 | ||
525 | ||
526 | ||
527 | ############################################################################################### | |
528 | ## Function: initialize () | |
529 | ## | |
530 | ## Does all the script startup jibberish. | |
531 | ## | |
532 | ############################################################################################### | |
533 | sub initialize { | |
534 | ||
535 | ## Set STDOUT to flush immediatly after each print | |
536 | $| = 1; | |
537 | ||
538 | ## Intercept signals | |
539 | $SIG{'QUIT'} = sub { quit("EXITING: Received SIG$_[0]", 1); }; | |
540 | $SIG{'INT'} = sub { quit("EXITING: Received SIG$_[0]", 1); }; | |
541 | $SIG{'KILL'} = sub { quit("EXITING: Received SIG$_[0]", 1); }; | |
542 | $SIG{'TERM'} = sub { quit("EXITING: Received SIG$_[0]", 1); }; | |
543 | ||
544 | ## ALARM and HUP signals are not supported in Win32 | |
545 | unless ($^O =~ /win/i) { | |
546 | $SIG{'HUP'} = sub { quit("EXITING: Received SIG$_[0]", 1); }; | |
547 | $SIG{'ALRM'} = sub { quit("EXITING: Received SIG$_[0]", 1); }; | |
548 | } | |
549 | ||
550 | ## Fixup $conf{'programName'} | |
551 | $conf{'programName'} =~ s/(.)*[\/,\\]//; | |
552 | $0 = $conf{'programName'} . " " . join(" ", @ARGV); | |
553 | ||
554 | ## Fixup $conf{'hostname'} | |
555 | if ($conf{'hostname'} eq 'localhost') { | |
556 | $conf{'hostname'} = ""; | |
557 | ||
558 | if ($ENV{'HOSTNAME'}) { | |
559 | $conf{'hostname'} = lc($ENV{'HOSTNAME'}); | |
560 | } | |
561 | elsif ($ENV{'COMPUTERNAME'}) { | |
562 | $conf{'hostname'} = lc($ENV{'COMPUTERNAME'}); | |
563 | } | |
564 | else { | |
565 | ## Try the hostname module | |
566 | use Sys::Hostname; | |
567 | $conf{'hostname'} = lc(hostname()); | |
568 | } | |
569 | ||
570 | ## Assign a name of "localhost" if it can't find anything else. | |
571 | if (!$conf{'hostname'}) { | |
572 | $conf{'hostname'} = 'localhost'; | |
573 | } | |
574 | ||
575 | $conf{'hostname'} =~ s/\..*$//; ## Remove domain name if it's present | |
576 | } | |
577 | ||
578 | return(1); | |
579 | } | |
580 | ||
581 | ||
582 | ||
583 | ||
584 | ||
585 | ||
586 | ||
587 | ||
588 | ||
589 | ||
590 | ||
591 | ||
592 | ||
593 | ||
594 | ||
595 | ############################################################################################### | |
596 | ## Function: processCommandLine () | |
597 | ## | |
598 | ## Processes command line storing important data in global vars (usually %conf) | |
599 | ## | |
600 | ############################################################################################### | |
601 | sub processCommandLine { | |
602 | ||
603 | ||
604 | ############################ | |
605 | ## Process command line ## | |
606 | ############################ | |
607 | ||
608 | my @ARGS = @ARGV; ## This is so later we can re-parse the command line args later if we need to | |
609 | my $numargv = @ARGS; | |
610 | help() unless ($numargv); | |
611 | my $counter = 0; | |
612 | ||
613 | for ($counter = 0; $counter < $numargv; $counter++) { | |
614 | ||
615 | if ($ARGS[$counter] =~ /^-h$/i) { ## Help ## | |
616 | help(); | |
617 | } | |
618 | ||
619 | elsif ($ARGS[$counter] eq "") { ## Ignore null arguments | |
620 | ## Do nothing | |
621 | } | |
622 | ||
623 | elsif ($ARGS[$counter] =~ /^--help/) { ## Topical Help ## | |
624 | $counter++; | |
625 | if ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) { | |
626 | helpTopic($ARGS[$counter]); | |
627 | } | |
628 | else { | |
629 | help(); | |
630 | } | |
631 | } | |
632 | ||
633 | elsif ($ARGS[$counter] =~ /^-o$/i) { ## Options specified with -o ## | |
634 | $counter++; | |
635 | ## Loop through each option passed after the -o | |
636 | while ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) { | |
637 | ||
638 | if ($ARGS[$counter] !~ /(\S+)=(\S.*)/) { | |
639 | printmsg("WARNING => Name/Value pair [$ARGS[$counter]] is not properly formatted", 0); | |
640 | printmsg("WARNING => Arguments proceeding -o should be in the form of \"name=value\"", 0); | |
641 | } | |
642 | else { | |
643 | if (exists($opt{$1})) { | |
644 | $opt{$1} = $2; | |
645 | printmsg("DEBUG => Assigned \$opt{} key/value: $1 => $2", 3); | |
646 | } | |
647 | else { | |
648 | printmsg("WARNING => Name/Value pair [$ARGS[$counter]] will be ignored: unknown key [$1]", 0); | |
649 | printmsg("HINT => Try the --help option to find valid command line arguments", 1); | |
650 | } | |
651 | } | |
652 | $counter++; | |
653 | } $counter--; | |
654 | } | |
655 | ||
656 | elsif ($ARGS[$counter] =~ /^-f$/) { ## From ## | |
657 | $counter++; | |
658 | if ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) { $from = $ARGS[$counter]; } | |
659 | else { printmsg("WARNING => The argument after -f was not an email address!", 0); $counter--; } | |
660 | } | |
661 | ||
662 | elsif ($ARGS[$counter] =~ /^-t$/) { ## To ## | |
663 | $counter++; | |
664 | while ($ARGS[$counter] && ($ARGS[$counter] !~ /^-/)) { | |
665 | if ($ARGS[$counter] =~ /[;,]/) { | |
666 | push (@to, split(/[;,]/, $ARGS[$counter])); | |
667 | } | |
668 | else { | |
669 | push (@to,$ARGS[$counter]); | |
670 | } | |
671 | $counter++; | |
672 | } $counter--; | |
673 | } | |
674 | ||
675 | elsif ($ARGS[$counter] =~ /^-cc$/) { ## Cc ## | |
676 | $counter++; | |
677 | while ($ARGS[$counter] && ($ARGS[$counter] !~ /^-/)) { | |
678 | if ($ARGS[$counter] =~ /[;,]/) { | |
679 | push (@cc, split(/[;,]/, $ARGS[$counter])); | |
680 | } | |
681 | else { | |
682 | push (@cc,$ARGS[$counter]); | |
683 | } | |
684 | $counter++; | |
685 | } $counter--; | |
686 | } | |
687 | ||
688 | elsif ($ARGS[$counter] =~ /^-bcc$/) { ## Bcc ## | |
689 | $counter++; | |
690 | while ($ARGS[$counter] && ($ARGS[$counter] !~ /^-/)) { | |
691 | if ($ARGS[$counter] =~ /[;,]/) { | |
692 | push (@bcc, split(/[;,]/, $ARGS[$counter])); | |
693 | } | |
694 | else { | |
695 | push (@bcc,$ARGS[$counter]); | |
696 | } | |
697 | $counter++; | |
698 | } $counter--; | |
699 | } | |
700 | ||
701 | elsif ($ARGS[$counter] =~ /^-m$/) { ## Message ## | |
702 | $counter++; | |
703 | $message = ""; | |
704 | while ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) { | |
705 | if ($message) { $message .= " "; } | |
706 | $message .= $ARGS[$counter]; | |
707 | $counter++; | |
708 | } $counter--; | |
709 | ||
710 | ## Replace '\n' with $CRLF. | |
711 | ## This allows newlines with messages sent on the command line | |
712 | $message =~ s/\\n/$CRLF/g; | |
713 | } | |
714 | ||
715 | elsif ($ARGS[$counter] =~ /^-u$/) { ## Subject ## | |
716 | $counter++; | |
717 | $subject = ""; | |
718 | while ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) { | |
719 | if ($subject) { $subject .= " "; } | |
720 | $subject .= $ARGS[$counter]; | |
721 | $counter++; | |
722 | } $counter--; | |
723 | } | |
724 | ||
725 | elsif ($ARGS[$counter] =~ /^-s$/) { ## Server ## | |
726 | $counter++; | |
727 | if ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) { | |
728 | $conf{'server'} = $ARGS[$counter]; | |
729 | if ($conf{'server'} =~ /:/) { ## Port ## | |
730 | ($conf{'server'},$conf{'port'}) = split(":",$conf{'server'}); | |
731 | } | |
732 | } | |
733 | else { printmsg("WARNING - The argument after -s was not the server!", 0); $counter--; } | |
734 | } | |
735 | ||
736 | elsif ($ARGS[$counter] =~ /^-a$/) { ## Attachments ## | |
737 | $counter++; | |
738 | while ($ARGS[$counter] && ($ARGS[$counter] !~ /^-/)) { | |
739 | push (@attachments,$ARGS[$counter]); | |
740 | $counter++; | |
741 | } $counter--; | |
742 | } | |
743 | ||
744 | elsif ($ARGS[$counter] =~ /^-xu$/) { ## AuthSMTP Username ## | |
745 | $counter++; | |
746 | if ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) { | |
747 | $conf{'authUsername'} = $ARGS[$counter]; | |
748 | my $tmp = substr(pack('u', $conf{'authUsername'}), 1); ## Convert the string to uuencoded text | |
749 | chop($tmp); | |
750 | $tmp =~ tr|` -_|AA-Za-z0-9+/|; ## Translate from uuencode to base64 | |
751 | $conf{'authUsername'} = $tmp; | |
752 | } | |
753 | else { | |
754 | printmsg("WARNING => The argument after -xu was not valid username!", 0); | |
755 | $counter--; | |
756 | } | |
757 | } | |
758 | ||
759 | elsif ($ARGS[$counter] =~ /^-xp$/) { ## AuthSMTP Password ## | |
760 | $counter++; | |
761 | if ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) { | |
762 | $conf{'authPassword'} = $ARGS[$counter]; | |
763 | my $tmp = substr(pack('u', $conf{'authPassword'}), 1); ## Convert the binary to uuencoded text | |
764 | chop($tmp); | |
765 | $tmp =~ tr|` -_|AA-Za-z0-9+/|; ## Translate from uuencode to base64 | |
766 | $conf{'authPassword'} = $tmp; | |
767 | } | |
768 | else { | |
769 | printmsg("WARNING => The argument after -xp was not valid password!", 0); | |
770 | $counter--; | |
771 | } | |
772 | } | |
773 | ||
774 | elsif ($ARGS[$counter] =~ /^-l$/) { ## Logging ## | |
775 | $counter++; | |
776 | $conf{'logging'} = 1; | |
777 | if ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) { $conf{'logFile'} = $ARGS[$counter]; } | |
778 | else { printmsg("WARNING - The argument after -l was not the log file!", 0); $counter--; } | |
779 | } | |
780 | ||
781 | elsif ($ARGS[$counter] =~ s/^-v+//i) { ## Verbosity ## | |
782 | my $tmp = (length($&) - 1); | |
783 | $conf{'debug'} += $tmp; | |
784 | } | |
785 | ||
786 | elsif ($ARGS[$counter] =~ /^-q$/) { ## Quiet ## | |
787 | $conf{'stdout'} = 0; | |
788 | } | |
789 | ||
790 | else { | |
791 | printmsg("Error: \"$ARGS[$counter]\" is not a recognized option!", 0); | |
792 | help(); | |
793 | } | |
794 | ||
795 | } | |
796 | ||
797 | ||
798 | ||
799 | ||
800 | ||
801 | ||
802 | ||
803 | ||
804 | ################################################### | |
805 | ## Verify required variables are set correctly ## | |
806 | ################################################### | |
807 | ||
808 | if (!$conf{'server'}) { | |
809 | $conf{'server'} = 'localhost'; | |
810 | } | |
811 | if (!$conf{'port'}) { | |
812 | $conf{'port'} = 25; | |
813 | } | |
814 | if (!$from) { | |
815 | quit("ERROR => You must specify a 'from' field! Try --help.", 1); | |
816 | } | |
817 | if ( ((scalar(@to)) + (scalar(@cc)) + (scalar(@bcc))) <= 0) { | |
818 | quit("ERROR => You must specify at least one recipient via -t, -cc, or -bcc", 1); | |
819 | } | |
820 | ||
821 | ## Make sure email addresses look OK. | |
822 | foreach my $addr (@to, @cc, @bcc, $from, $opt{'reply-to'}) { | |
823 | if ($addr) { | |
824 | if (!returnAddressParts($addr)) { | |
825 | printmsg("ERROR => Can't use improperly formatted email address: $addr", 0); | |
826 | printmsg("HINT => Try viewing the extended help on addressing with \"--help addressing\"", 1); | |
827 | quit("", 1); | |
828 | } | |
829 | } | |
830 | } | |
831 | ||
832 | ## Make sure all attachments exist. | |
833 | foreach my $file (@attachments) { | |
834 | if ( (! -f $file) or (! -r $file) ) { | |
835 | printmsg("ERROR => The attachment [$file] doesn't exist!", 0); | |
836 | printmsg("HINT => Try specifying the full path to the file or reading extended help with \"--help message\"", 1); | |
837 | quit("", 1); | |
838 | } | |
839 | } | |
840 | ||
841 | if ($conf{'logging'} and (!$conf{'logFile'})) { | |
842 | quit("ERROR => You used -l to enable logging but didn't specify a log file!", 1); | |
843 | } | |
844 | ||
845 | if ( $conf{'authUsername'} ) { | |
846 | if (!$conf{'authPassword'}) { | |
847 | quit ("ERROR => You must supply both a username and a password to use SMTP auth.",1); | |
848 | } | |
849 | } | |
850 | ||
851 | ## Return 0 errors | |
852 | return(0); | |
853 | } | |
854 | ||
855 | ||
856 | ||
857 | ||
858 | ||
859 | ||
860 | ||
861 | ||
862 | ||
863 | ||
864 | ||
865 | ||
866 | ||
867 | ||
868 | ||
869 | ||
870 | ## getline($socketRef) | |
871 | sub getline { | |
872 | my ($socketRef) = @_; | |
873 | local ($/) = "\r\n"; | |
874 | return $$socketRef->getline; | |
875 | } | |
876 | ||
877 | ||
878 | ||
879 | ||
880 | ## Receive a (multiline?) SMTP response from ($socketRef) | |
881 | sub getResponse { | |
882 | my ($socketRef) = @_; | |
883 | my ($tmp, $reply); | |
884 | local ($/) = "\r\n"; | |
885 | return undef unless defined($tmp = getline($socketRef)); | |
886 | return("getResponse() socket is not open") unless ($$socketRef->opened); | |
887 | ## Keep reading lines if it's a multi-line response | |
888 | while ($tmp =~ /^\d{3}-/o) { | |
889 | $reply .= $tmp; | |
890 | return undef unless defined($tmp = getline($socketRef)); | |
891 | } | |
892 | $reply .= $tmp; | |
893 | $reply =~ s/\r?\n$//o; | |
894 | return $reply; | |
895 | } | |
896 | ||
897 | ||
898 | ||
899 | ||
900 | ############################################################################################### | |
901 | ## Function: SMTPchat ( [string $command] ) | |
902 | ## | |
903 | ## Description: Sends $command to the SMTP server (on SERVER) and awaits a successfull | |
904 | ## reply form the server. If the server returns an error, or does not reply | |
905 | ## within $conf{'alarm'} seconds an error is generated. | |
906 | ## NOTE: $command is optional, if no command is specified then nothing will | |
907 | ## be sent to the server, but a valid response is still required from the server. | |
908 | ## | |
909 | ## Input: [$command] A (optional) valid SMTP command (ex. "HELO") | |
910 | ## | |
911 | ## | |
912 | ## Output: Returns zero on success, or non-zero on error. | |
913 | ## Error messages will be stored in $conf{'error'} | |
914 | ## | |
915 | ## | |
916 | ## Example: SMTPchat ("HELO mail.isp.net"); | |
917 | ############################################################################################### | |
918 | sub SMTPchat { | |
919 | my ($command) = @_; | |
920 | ||
921 | printmsg("INFO => Sending: \t$command", 1) if ($command); | |
922 | ||
923 | ## Send our command | |
924 | print $SERVER "$command$CRLF" if ($command); | |
925 | ||
926 | ## Read a response from the server | |
927 | $SIG{'ALRM'} = sub { $conf{'error'} = "alarm"; $SERVER->close(); }; | |
928 | alarm($conf{'alarm'}) if ($^O !~ /win/i); ## alarm() doesn't work in win32; | |
929 | my $result = getResponse(\$SERVER); | |
930 | alarm(0) if ($^O !~ /win/i); ## alarm() doesn't work in win32; | |
931 | ||
932 | ## Generate an alert if we timed out | |
933 | if ($conf{'error'} eq "alarm") { | |
934 | $conf{'error'} = "ERROR => Timeout while reading from $conf{'server'}:$conf{'port'} There was no response after $conf{'alarm'} seconds."; | |
935 | return(1); | |
936 | } | |
937 | ||
938 | ## Make sure the server actually responded | |
939 | if (!$result) { | |
940 | $conf{'error'} = "ERROR => $conf{'server'}:$conf{'port'} returned a zero byte response to our query."; | |
941 | return(2); | |
942 | } | |
943 | ||
944 | ## Validate the response | |
945 | if (evalSMTPresponse($result)) { | |
946 | ## conf{'error'} will already be set here | |
947 | return(2); | |
948 | } | |
949 | ||
950 | ## Print the success messsage | |
951 | printmsg($conf{'error'}, 1); | |
952 | ||
953 | ## Return Success | |
954 | return(0); | |
955 | } | |
956 | ||
957 | ||
958 | ||
959 | ||
960 | ||
961 | ||
962 | ||
963 | ||
964 | ||
965 | ||
966 | ||
967 | ||
968 | ############################################################################################### | |
969 | ## Function: evalSMTPresponse (string $message ) | |
970 | ## | |
971 | ## Description: Searches $message for either an SMTP success or error code, and returns | |
972 | ## 0 on success, and the actual error code on error. | |
973 | ## | |
974 | ## | |
975 | ## Input: $message Data received from a SMTP server (ex. "220 | |
976 | ## | |
977 | ## | |
978 | ## Output: Returns zero on success, or non-zero on error. | |
979 | ## Error messages will be stored in $conf{'error'} | |
980 | ## | |
981 | ## | |
982 | ## Example: SMTPchat ("HELO mail.isp.net"); | |
983 | ############################################################################################### | |
984 | sub evalSMTPresponse { | |
985 | my ($message) = @_; | |
986 | ||
987 | ## Validate input | |
988 | if (!$message) { | |
989 | $conf{'error'} = "ERROR => No message was passed to evalSMTPresponse(). What happened?"; | |
990 | return(1) | |
991 | } | |
992 | ||
993 | printmsg("DEBUG => evalSMTPresponse() - Checking for SMTP success or error status in the message: $message ", 3); | |
994 | ||
995 | ## Look for a SMTP success code | |
996 | if ($message =~ /^([23]\d\d)/) { | |
997 | printmsg("DEBUG => evalSMTPresponse() - Found SMTP success code: $1", 2); | |
998 | $conf{'error'} = "SUCCESS => Received: \t$message"; | |
999 | return(0); | |
1000 | } | |
1001 | ||
1002 | ## Look for a SMTP error code | |
1003 | if ($message =~ /^([45]\d\d)/) { | |
1004 | printmsg("DEBUG => evalSMTPresponse() - Found SMTP error code: $1", 2); | |
1005 | $conf{'error'} = "ERROR => Received: \t$message"; | |
1006 | return($1); | |
1007 | } | |
1008 | ||
1009 | ## If no SMTP codes were found return an error of 1 | |
1010 | $conf{'error'} = "ERROR => Received a message with no success or error code. The message received was: $message"; | |
1011 | return(2); | |
1012 | ||
1013 | } | |
1014 | ||
1015 | ||
1016 | ||
1017 | ||
1018 | ||
1019 | ||
1020 | ||
1021 | ||
1022 | ||
1023 | ||
1024 | ######################################################### | |
1025 | # SUB: &return_month(0,1,etc) | |
1026 | # returns the name of the month that corrosponds | |
1027 | # with the number. returns 0 on error. | |
1028 | ######################################################### | |
1029 | sub return_month { | |
1030 | my $x = $_[0]; | |
1031 | if ($x == 0) { return 'Jan'; } | |
1032 | if ($x == 1) { return 'Feb'; } | |
1033 | if ($x == 2) { return 'Mar'; } | |
1034 | if ($x == 3) { return 'Apr'; } | |
1035 | if ($x == 4) { return 'May'; } | |
1036 | if ($x == 5) { return 'Jun'; } | |
1037 | if ($x == 6) { return 'Jul'; } | |
1038 | if ($x == 7) { return 'Aug'; } | |
1039 | if ($x == 8) { return 'Sep'; } | |
1040 | if ($x == 9) { return 'Oct'; } | |
1041 | if ($x == 10) { return 'Nov'; } | |
1042 | if ($x == 11) { return 'Dec'; } | |
1043 | return (0); | |
1044 | } | |
1045 | ||
1046 | ||
1047 | ||
1048 | ||
1049 | ||
1050 | ||
1051 | ||
1052 | ||
1053 | ||
1054 | ||
1055 | ||
1056 | ||
1057 | ||
1058 | ||
1059 | ||
1060 | ||
1061 | ######################################################### | |
1062 | # SUB: &return_day(0,1,etc) | |
1063 | # returns the name of the day that corrosponds | |
1064 | # with the number. returns 0 on error. | |
1065 | ######################################################### | |
1066 | sub return_day { | |
1067 | my $x = $_[0]; | |
1068 | if ($x == 0) { return 'Sun'; } | |
1069 | if ($x == 1) { return 'Mon'; } | |
1070 | if ($x == 2) { return 'Tue'; } | |
1071 | if ($x == 3) { return 'Wed'; } | |
1072 | if ($x == 4) { return 'Thu'; } | |
1073 | if ($x == 5) { return 'Fri'; } | |
1074 | if ($x == 6) { return 'Sat'; } | |
1075 | return (0); | |
1076 | } | |
1077 | ||
1078 | ||
1079 | ||
1080 | ||
1081 | ||
1082 | ||
1083 | ||
1084 | ||
1085 | ||
1086 | ||
1087 | ||
1088 | ||
1089 | ||
1090 | ||
1091 | ||
1092 | ||
1093 | ############################################################################################### | |
1094 | ## Function: returnAddressParts(string $address) | |
1095 | ## | |
1096 | ## Description: Returns a two element array containing the "Name" and "Address" parts of | |
1097 | ## an email address. | |
1098 | ## | |
1099 | ## Example: "Brandon Zehm <caspian@dotconf.net>" | |
1100 | ## would return: ("Brandon Zehm", "caspian@dotconf.net"); | |
1101 | ## | |
1102 | ## "caspian@dotconf.net" | |
1103 | ## would return: ("caspian@dotconf.net", "caspian@dotconf.net") | |
1104 | ############################################################################################### | |
1105 | sub returnAddressParts { | |
1106 | my $input = $_[0]; | |
1107 | my $name = ""; | |
1108 | my $address = ""; | |
1109 | ||
1110 | ## Make sure to fail if it looks totally invalid | |
1111 | if ($input !~ /(\S+\@\S+)/) { | |
1112 | $conf{'error'} = "ERROR => The address [$input] doesn't look like a valid email address, ignoring it"; | |
1113 | return(undef()); | |
1114 | } | |
1115 | ||
1116 | ## Check 1, should find addresses like: "Brandon Zehm <caspian@dotconf.net>" | |
1117 | elsif ($input =~ /^\s*(\S(.*\S)?)\s*<(\S+\@\S+)>/o) { | |
1118 | ($name, $address) = ($1, $3); | |
1119 | } | |
1120 | ||
1121 | ## Otherwise if that failed, just get the address: <caspian@dotconf.net> | |
1122 | elsif ($input =~ /<(\S+\@\S+)>/o) { | |
1123 | $name = $address = $1; | |
1124 | } | |
1125 | ||
1126 | ## Or maybe it was formatted this way: caspian@dotconf.net | |
1127 | elsif ($input =~ /(\S+\@\S+)/o) { | |
1128 | $name = $address = $1; | |
1129 | } | |
1130 | ||
1131 | ## Something stupid happened, just return an error. | |
1132 | unless ($name and $address) { | |
1133 | printmsg("ERROR => Couldn't parse the address: $input", 0); | |
1134 | printmsg("HINT => If you think this should work, consider reporting this as a bug to $conf{'authorEmail'}", 1); | |
1135 | return(undef()); | |
1136 | } | |
1137 | ||
1138 | ## Make sure there aren't invalid characters in the address, and return it. | |
1139 | my $ctrl = '\000-\037'; | |
1140 | my $nonASCII = '\x80-\xff'; | |
1141 | if ($address =~ /[<> ,;:"'\[\]\\$ctrl$nonASCII]/) { | |
1142 | printmsg("WARNING => The address [$address] seems to contain invalid characters: continuing anyway", 0); | |
1143 | } | |
1144 | return($name, $address); | |
1145 | } | |
1146 | ||
1147 | ||
1148 | ||
1149 | ||
1150 | ||
1151 | ||
1152 | ||
1153 | ||
1154 | ||
1155 | ||
1156 | ||
1157 | ||
1158 | ||
1159 | ||
1160 | ||
1161 | ||
1162 | ######################################################### | |
1163 | # SUB: send_attachment("/path/filename") | |
1164 | # Sends the mime headers and base64 encoded file | |
1165 | # to the email server. | |
1166 | ######################################################### | |
1167 | sub send_attachment { | |
1168 | my ($filename) = @_; ## Get filename passed | |
1169 | my (@fields, $y, $filename_name, $encoding, ## Local variables | |
1170 | @attachlines, $content_type); | |
1171 | my $bin = 1; | |
1172 | ||
1173 | @fields = split(/\/|\\/, $filename); ## Get the actual filename without the path | |
1174 | $filename_name = pop(@fields); | |
1175 | push @attachments_names, $filename_name; ## FIXME: This is only used later for putting in the log file | |
1176 | ||
1177 | ########################## | |
1178 | ## Autodetect Mime Type ## | |
1179 | ########################## | |
1180 | ||
1181 | @fields = split(/\./, $filename_name); | |
1182 | $encoding = $fields[$#fields]; | |
1183 | ||
1184 | if ($encoding =~ /txt|text|log|conf|^c$|cpp|^h$|inc|m3u/i) { $content_type = 'text/plain'; } | |
1185 | elsif ($encoding =~ /html|htm|shtml|shtm|asp|php|cfm/i) { $content_type = 'text/html'; } | |
1186 | elsif ($encoding =~ /sh$/i) { $content_type = 'application/x-sh'; } | |
1187 | elsif ($encoding =~ /tcl/i) { $content_type = 'application/x-tcl'; } | |
1188 | elsif ($encoding =~ /pl$/i) { $content_type = 'application/x-perl'; } | |
1189 | elsif ($encoding =~ /js$/i) { $content_type = 'application/x-javascript'; } | |
1190 | elsif ($encoding =~ /man/i) { $content_type = 'application/x-troff-man'; } | |
1191 | elsif ($encoding =~ /gif/i) { $content_type = 'image/gif'; } | |
1192 | elsif ($encoding =~ /jpg|jpeg|jpe|jfif|pjpeg|pjp/i) { $content_type = 'image/jpeg'; } | |
1193 | elsif ($encoding =~ /tif|tiff/i) { $content_type = 'image/tiff'; } | |
1194 | elsif ($encoding =~ /xpm/i) { $content_type = 'image/x-xpixmap'; } | |
1195 | elsif ($encoding =~ /bmp/i) { $content_type = 'image/x-MS-bmp'; } | |
1196 | elsif ($encoding =~ /pcd/i) { $content_type = 'image/x-photo-cd'; } | |
1197 | elsif ($encoding =~ /png/i) { $content_type = 'image/png'; } | |
1198 | elsif ($encoding =~ /aif|aiff/i) { $content_type = 'audio/x-aiff'; } | |
1199 | elsif ($encoding =~ /wav/i) { $content_type = 'audio/x-wav'; } | |
1200 | elsif ($encoding =~ /mp2|mp3|mpa/i) { $content_type = 'audio/x-mpeg'; } | |
1201 | elsif ($encoding =~ /ra$|ram/i) { $content_type = 'audio/x-pn-realaudio'; } | |
1202 | elsif ($encoding =~ /mpeg|mpg/i) { $content_type = 'video/mpeg'; } | |
1203 | elsif ($encoding =~ /mov|qt$/i) { $content_type = 'video/quicktime'; } | |
1204 | elsif ($encoding =~ /avi/i) { $content_type = 'video/x-msvideo'; } | |
1205 | elsif ($encoding =~ /zip/i) { $content_type = 'application/x-zip-compressed'; } | |
1206 | elsif ($encoding =~ /tar/i) { $content_type = 'application/x-tar'; } | |
1207 | elsif ($encoding =~ /jar/i) { $content_type = 'application/java-archive'; } | |
1208 | elsif ($encoding =~ /exe|bin/i) { $content_type = 'application/octet-stream'; } | |
1209 | elsif ($encoding =~ /ppt|pot|ppa|pps|pwz/i) { $content_type = 'application/vnd.ms-powerpoint'; } | |
1210 | elsif ($encoding =~ /mdb|mda|mde/i) { $content_type = 'application/vnd.ms-access'; } | |
1211 | elsif ($encoding =~ /xls|xlt|xlm|xld|xla|xlc|xlw|xll/i) { $content_type = 'application/vnd.ms-excel'; } | |
1212 | elsif ($encoding =~ /doc|dot/i) { $content_type = 'application/msword'; } | |
1213 | elsif ($encoding =~ /rtf/i) { $content_type = 'application/rtf'; } | |
1214 | elsif ($encoding =~ /pdf/i) { $content_type = 'application/pdf'; } | |
1215 | elsif ($encoding =~ /tex/i) { $content_type = 'application/x-tex'; } | |
1216 | elsif ($encoding =~ /latex/i) { $content_type = 'application/x-latex'; } | |
1217 | elsif ($encoding =~ /vcf/i) { $content_type = 'application/x-vcard'; } | |
1218 | else { $content_type = 'application/octet-stream'; } | |
1219 | ||
1220 | ||
1221 | ############################ | |
1222 | ## Process the attachment ## | |
1223 | ############################ | |
1224 | ||
1225 | ##################################### | |
1226 | ## Generate and print MIME headers ## | |
1227 | ##################################### | |
1228 | ||
1229 | $y = "$CRLF--$conf{'delimiter'}$CRLF"; | |
1230 | $y .= "Content-Type: $content_type;$CRLF"; | |
1231 | $y .= " name=\"$filename_name\"$CRLF"; | |
1232 | $y .= "Content-Transfer-Encoding: base64$CRLF"; | |
1233 | $y .= "Content-Disposition: attachment; filename=\"$filename_name\"$CRLF"; | |
1234 | $y .= "$CRLF"; | |
1235 | print $SERVER $y; | |
1236 | ||
1237 | ||
1238 | ########################################################### | |
1239 | ## Convert the file to base64 and print it to the server ## | |
1240 | ########################################################### | |
1241 | ||
1242 | open (FILETOATTACH, $filename) || do { | |
1243 | printmsg("ERROR => Opening the file [$filename] for attachment failed with the error: $!", 0); | |
1244 | return(1); | |
1245 | }; | |
1246 | binmode(FILETOATTACH); ## Hack to make Win32 work | |
1247 | ||
1248 | my $res = ""; | |
1249 | my $tmp = ""; | |
1250 | my $base64 = ""; | |
1251 | while (<FILETOATTACH>) { ## Read a line from the (binary) file | |
1252 | $res .= $_; | |
1253 | ||
1254 | ################################### | |
1255 | ## Convert binary data to base64 ## | |
1256 | ################################### | |
1257 | while ($res =~ s/(.{45})//s) { ## Get 45 bytes from the binary string | |
1258 | $tmp = substr(pack('u', $&), 1); ## Convert the binary to uuencoded text | |
1259 | chop($tmp); | |
1260 | $tmp =~ tr|` -_|AA-Za-z0-9+/|; ## Translate from uuencode to base64 | |
1261 | $base64 .= $tmp; | |
1262 | } | |
1263 | ||
1264 | ################################ | |
1265 | ## Print chunks to the server ## | |
1266 | ################################ | |
1267 | while ($base64 =~ s/(.{76})//s) { | |
1268 | print $SERVER "$1$CRLF"; | |
1269 | } | |
1270 | ||
1271 | } | |
1272 | ||
1273 | ################################### | |
1274 | ## Encode and send the leftovers ## | |
1275 | ################################### | |
1276 | my $padding = ""; | |
1277 | if ( ($res) and (length($res) >= 1) ) { | |
1278 | $padding = (3 - length($res) % 3) % 3; ## Set flag if binary data isn't divisible by 3 | |
1279 | $res = substr(pack('u', $res), 1); ## Convert the binary to uuencoded text | |
1280 | chop($res); | |
1281 | $res =~ tr|` -_|AA-Za-z0-9+/|; ## Translate from uuencode to base64 | |
1282 | } | |
1283 | ||
1284 | ############################ | |
1285 | ## Fix padding at the end ## | |
1286 | ############################ | |
1287 | $res = $base64 . $res; ## Get left overs from above | |
1288 | $res =~ s/.{$padding}$/'=' x $padding/e if $padding; ## Fix the end padding if flag (from above) is set | |
1289 | if ($res) { | |
1290 | while ($res =~ s/(.{1,76})//s) { ## Send it to the email server. | |
1291 | print $SERVER "$1$CRLF"; | |
1292 | } | |
1293 | } | |
1294 | ||
1295 | close (FILETOATTACH) || do { | |
1296 | printmsg("ERROR - Closing the filehandle for file [$filename] failed with the error: $!", 0); | |
1297 | return(2); | |
1298 | }; | |
1299 | ||
1300 | ## Return 0 errors | |
1301 | return(0); | |
1302 | ||
1303 | } | |
1304 | ||
1305 | ||
1306 | ||
1307 | ||
1308 | ||
1309 | ||
1310 | ||
1311 | ||
1312 | ||
1313 | ||
1314 | ||
1315 | ||
1316 | ||
1317 | ||
1318 | ||
1319 | ||
1320 | ############################################################################################### | |
1321 | ## Function: printmsg (string $message, int $level) | |
1322 | ## | |
1323 | ## Description: Handles all messages - printing them to the screen only if the messages | |
1324 | ## $level is >= the global debug level. If $conf{'logFile'} is defined it | |
1325 | ## will also log the message to that file. | |
1326 | ## | |
1327 | ## Input: $message A message to be printed, logged, etc. | |
1328 | ## $level The debug level of the message. If | |
1329 | ## not defined 0 will be assumed. 0 is | |
1330 | ## considered a normal message, 1 and | |
1331 | ## higher is considered a debug message. | |
1332 | ## | |
1333 | ## Output: Prints to STDOUT | |
1334 | ## | |
1335 | ## Assumptions: $conf{'hostname'} should be the name of the computer we're running on. | |
1336 | ## $conf{'stdout'} should be set to 1 if you want to print to stdout | |
1337 | ## $conf{'logFile'} should be a full path to a log file if you want that | |
1338 | ## $conf{'syslog'} should be 1 if you want to syslog, the syslog() function | |
1339 | ## written by Brandon Zehm should be present. | |
1340 | ## $conf{'debug'} should be an integer between 0 and 10. | |
1341 | ## | |
1342 | ## Example: printmsg("WARNING: We believe in generic error messages... NOT!", 0); | |
1343 | ############################################################################################### | |
1344 | sub printmsg { | |
1345 | ## Assign incoming parameters to variables | |
1346 | my ( $message, $level ) = @_; | |
1347 | ||
1348 | ## Make sure input is sane | |
1349 | $level = 0 if (!defined($level)); | |
1350 | $message =~ s/\s+$//sgo; | |
1351 | $message =~ s/\r?\n/, /sgo; | |
1352 | ||
1353 | ## Continue only if the debug level of the program is >= message debug level. | |
1354 | if ($conf{'debug'} >= $level) { | |
1355 | ||
1356 | ## Get the date in the format: Dec 3 11:14:04 | |
1357 | my ($sec, $min, $hour, $mday, $mon) = localtime(); | |
1358 | $mon = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')[$mon]; | |
1359 | my $date = sprintf("%s %02d %02d:%02d:%02d", $mon, $mday, $hour, $min, $sec); | |
1360 | ||
1361 | ## Print to STDOUT always if debugging is enabled, or if conf{stdout} is true. | |
1362 | if ( ($conf{'debug'} >= 1) or ($conf{'stdout'} == 1) ) { | |
1363 | print "$date $conf{'hostname'} $conf{'programName'}\[$$\]: $message\n"; | |
1364 | } | |
1365 | ||
1366 | ## Print to the log file if $conf{'logging'} is true | |
1367 | if ($conf{'logFile'}) { | |
1368 | if (openLogFile($conf{'logFile'})) { $conf{'logFile'} = ""; printmsg("ERROR => Opening the file [$conf{'logFile'}] for appending returned the error: $!", 1); } | |
1369 | print LOGFILE "$date $conf{'hostname'} $conf{'programName'}\[$$\]: $message\n"; | |
1370 | } | |
1371 | ||
1372 | } | |
1373 | ||
1374 | ## Return 0 errors | |
1375 | return(0); | |
1376 | } | |
1377 | ||
1378 | ||
1379 | ||
1380 | ||
1381 | ||
1382 | ||
1383 | ||
1384 | ||
1385 | ||
1386 | ||
1387 | ||
1388 | ||
1389 | ############################################################################################### | |
1390 | ## FUNCTION: | |
1391 | ## openLogFile ( $filename ) | |
1392 | ## | |
1393 | ## | |
1394 | ## DESCRIPTION: | |
1395 | ## Opens the file $filename and attaches it to the filehandle "LOGFILE". Returns 0 on success | |
1396 | ## and non-zero on failure. Error codes are listed below, and the error message gets set in | |
1397 | ## global variable $!. | |
1398 | ## | |
1399 | ## | |
1400 | ## Example: | |
1401 | ## openFile ("/var/log/sendEmail.log"); | |
1402 | ## | |
1403 | ############################################################################################### | |
1404 | sub openLogFile { | |
1405 | ## Get the incoming filename | |
1406 | my $filename = $_[0]; | |
1407 | ||
1408 | ## Make sure our file exists, and if the file doesn't exist then create it | |
1409 | if ( ! -f $filename ) { | |
1410 | print STDERR "NOTICE: The log file [$filename] does not exist. Creating it now with mode [0600].\n" if ($conf{'stdout'}); | |
1411 | open (LOGFILE, ">>$filename"); | |
1412 | close LOGFILE; | |
1413 | chmod (0600, $filename); | |
1414 | } | |
1415 | ||
1416 | ## Now open the file and attach it to a filehandle | |
1417 | open (LOGFILE,">>$filename") or return (1); | |
1418 | ||
1419 | ## Put the file into non-buffering mode | |
1420 | select LOGFILE; | |
1421 | $| = 1; | |
1422 | select STDOUT; | |
1423 | ||
1424 | ## Return success | |
1425 | return(0); | |
1426 | } | |
1427 | ||
1428 | ||
1429 | ||
1430 | ||
1431 | ||
1432 | ||
1433 | ||
1434 | ||
1435 | ||
1436 | ||
1437 | ||
1438 | ||
1439 | ||
1440 | ||
1441 | ||
1442 | ||
1443 | ############################################################################################### | |
1444 | ## Function: quit (string $message, int $errorLevel) | |
1445 | ## | |
1446 | ## Description: Exits the program, optionally printing $message. It | |
1447 | ## returns an exit error level of $errorLevel to the | |
1448 | ## system (0 means no errors, and is assumed if empty.) | |
1449 | ## | |
1450 | ## Example: quit("Exiting program normally", 0); | |
1451 | ############################################################################################### | |
1452 | sub quit { | |
1453 | my %incoming = (); | |
1454 | ( | |
1455 | $incoming{'message'}, | |
1456 | $incoming{'errorLevel'} | |
1457 | ) = @_; | |
1458 | $incoming{'errorLevel'} = 0 if (!defined($incoming{'errorLevel'})); | |
1459 | ||
1460 | ## Print exit message | |
1461 | if ($incoming{'message'}) { | |
1462 | printmsg($incoming{'message'}, 0); | |
1463 | } | |
1464 | ||
1465 | ## Exit | |
1466 | exit($incoming{'errorLevel'}); | |
1467 | } | |
1468 | ||
1469 | ||
1470 | ||
1471 | ||
1472 | ||
1473 | ||
1474 | ||
1475 | ||
1476 | ||
1477 | ||
1478 | ||
1479 | ||
1480 | ############################################################################################### | |
1481 | ## Function: help () | |
1482 | ## | |
1483 | ## Description: For all those newbies ;) | |
1484 | ## Prints a help message and exits the program. | |
1485 | ## | |
1486 | ############################################################################################### | |
1487 | sub help { | |
1488 | exit(1) if (!$conf{'stdout'}); | |
1489 | print <<EOM; | |
1490 | ||
1491 | ${colorBold}$conf{'programName'}-$conf{'version'} by $conf{'authorName'} <$conf{'authorEmail'}>${colorNoBold} | |
1492 | ||
1493 | Synopsis: $conf{'programName'} -f ADDRESS [options] | |
1494 | ||
1495 | ${colorRed}Required:${colorNormal} | |
1496 | -f ADDRESS from (sender) email address | |
1497 | * At least one recipient required via -t, -cc, or -bcc | |
1498 | * Message body required via -m, STDIN, or -o message-file=FILE | |
1499 | ||
1500 | ${colorGreen}Common:${colorNormal} | |
1501 | -t ADDRESS [ADDR ...] to email address(es) | |
1502 | -u SUBJECT message subject | |
1503 | -m MESSAGE message body | |
1504 | -s SERVER[:PORT] smtp mail relay, default is $conf{'server'}:$conf{'port'} | |
1505 | ||
1506 | ${colorGreen}Optional:${colorNormal} | |
1507 | -a FILE [FILE ...] file attachment(s) | |
1508 | -cc ADDRESS [ADDR ...] cc email address(es) | |
1509 | -bcc ADDRESS [ADDR ...] bcc email address(es) | |
1510 | ||
1511 | ${colorGreen}Paranormal:${colorNormal} | |
1512 | -xu USERNAME authentication user (for SMTP authentication) | |
1513 | -xp PASSWORD authentication password (for SMTP authentication) | |
1514 | -l LOGFILE log to the specified file | |
1515 | -v verbosity, use multiple times for greater effect | |
1516 | -q be quiet (no stdout output) | |
1517 | -o NAME=VALUE see extended help topic "misc" for details | |
1518 | ||
1519 | ${colorGreen}Help:${colorNormal} | |
1520 | --help TOPIC The following extended help topics are available: | |
1521 | addressing explain addressing and related options | |
1522 | message explain message body input and related options | |
1523 | misc explain -xu, -xp, and others | |
1524 | networking explain -s, etc | |
1525 | output explain logging and other output options | |
1526 | ||
1527 | EOM | |
1528 | exit(1); | |
1529 | } | |
1530 | ||
1531 | ||
1532 | ||
1533 | ||
1534 | ||
1535 | ||
1536 | ||
1537 | ||
1538 | ||
1539 | ############################################################################################### | |
1540 | ## Function: helpTopic ($topic) | |
1541 | ## | |
1542 | ## Description: For all those newbies ;) | |
1543 | ## Prints a help message and exits the program. | |
1544 | ## | |
1545 | ############################################################################################### | |
1546 | sub helpTopic { | |
1547 | exit(1) if (!$conf{'stdout'}); | |
1548 | my ($topic) = @_; | |
1549 | ||
1550 | CASE: { | |
1551 | ||
1552 | ||
1553 | ||
1554 | ||
1555 | ## ADDRESSING | |
1556 | ($topic eq 'addressing') && do { | |
1557 | print <<EOM; | |
1558 | ||
1559 | ${colorBold}ADDRESSING DOCUMENTATION${colorNormal} | |
1560 | ||
1561 | ${colorGreen}Addressing Options${colorNormal} | |
1562 | Options related to addressing: | |
1563 | -f ADDRESS | |
1564 | -t ADDRESS [ADDRESS ...] | |
1565 | -cc ADDRESS [ADDRESS ...] | |
1566 | -bcc ADDRESS [ADDRESS ...] | |
1567 | -o reply-to=ADDRESS | |
1568 | ||
1569 | -f ADDRESS | |
1570 | This required option specifies who the email is from, I.E. the sender's | |
1571 | email address. | |
1572 | ||
1573 | -t ADDRESS [ADDRESS ...] | |
1574 | This option specifies the primary recipient(s). At least one recipient | |
1575 | address must be specified via the -t, -cc. or -bcc options. | |
1576 | ||
1577 | -cc ADDRESS [ADDRESS ...] | |
1578 | This option specifies the "carbon copy" recipient(s). At least one | |
1579 | recipient address must be specified via the -t, -cc. or -bcc options. | |
1580 | ||
1581 | -bcc ADDRESS [ADDRESS ...] | |
1582 | This option specifies the "blind carbon copy" recipient(s). At least | |
1583 | one recipient address must be specified via the -t, -cc. or -bcc options. | |
1584 | ||
1585 | -o reply-to=ADDRESS | |
1586 | This option specifies that an optional "Reply-To" address should be | |
1587 | written in the email's headers. | |
1588 | ||
1589 | ||
1590 | ${colorGreen}Email Address Syntax${colorNormal} | |
1591 | Email addresses may be specified in one of two ways: | |
1592 | Full Name: "John Doe <john.doe\@gmail.com>" | |
1593 | Just Address: "john.doe\@gmail.com" | |
1594 | ||
1595 | The "Full Name" method is useful if you want a name, rather than a plain | |
1596 | email address, to be displayed in the recipient's From, To, or Cc fields | |
1597 | when they view the message. | |
1598 | ||
1599 | ||
1600 | ${colorGreen}Multiple Recipients${colorNormal} | |
1601 | The -t, -cc, and -bcc options each accept multiple addresses. They may be | |
1602 | specified by separating them by either a white space, comma, or semi-colon | |
1603 | separated list. You may also specify the -t, -cc, and -bcc options multiple | |
1604 | times, each occurance will append the new recipients to the respective list. | |
1605 | ||
1606 | Examples: | |
1607 | (I used "-t" in these examples, but it can be "-cc" or "-bcc" as well) | |
1608 | ||
1609 | * Space separated list: | |
1610 | -t jane.doe\@yahoo.com "John Doe <john.doe\@gmail.com>" | |
1611 | ||
1612 | * Semi-colon separated list: | |
1613 | -t "jane.doe\@yahoo.com; John Doe <john.doe\@gmail.com>" | |
1614 | ||
1615 | * Comma separated list: | |
1616 | -t "jane.doe\@yahoo.com, John Doe <john.doe\@gmail.com>" | |
1617 | ||
1618 | * Multiple -t, -cc, or -bcc options: | |
1619 | -t "jane.doe\@yahoo.com" -t "John Doe <john.doe\@gmail.com>" | |
1620 | ||
1621 | ||
1622 | EOM | |
1623 | last CASE; | |
1624 | }; | |
1625 | ||
1626 | ||
1627 | ||
1628 | ||
1629 | ||
1630 | ||
1631 | ## MESSAGE | |
1632 | ($topic eq 'message') && do { | |
1633 | print <<EOM; | |
1634 | ||
1635 | ${colorBold}MESSAGE DOCUMENTATION${colorNormal} | |
1636 | ||
1637 | ${colorGreen}Message Options${colorNormal} | |
1638 | Options related to the message: | |
1639 | -u SUBJECT | |
1640 | -m MESSAGE | |
1641 | -o message-file=FILE | |
1642 | -o message-header=EMAIL HEADER | |
1643 | -o message-format=raw | |
1644 | -o message-charset=CHARSET | |
1645 | ||
1646 | -u SUBJECT | |
1647 | This option allows you to specify the subject for your email message. | |
1648 | It is not required (anymore) that the subject be quoted, although it | |
1649 | is recommended. The subject will be read until an argument starting | |
1650 | with a hyphen (-) is found. | |
1651 | Examples: | |
1652 | -u "Contact information while on vacation" | |
1653 | -u New Microsoft vulnerability discovered | |
1654 | ||
1655 | -m MESSAGE | |
1656 | This option is one of three methods that allow you to specify the message | |
1657 | body for your email. The message may be specified on the command line | |
1658 | with this -m option, read from a file with the -o message-file=FILE | |
1659 | option, or read from STDIN if neither of these options are present. | |
1660 | ||
1661 | It is not required (anymore) that the message be quoted, although it is | |
1662 | recommended. The message will be read until an argument starting with a | |
1663 | hyphen (-) is found. | |
1664 | Examples: | |
1665 | -m "See you in South Beach, Hawaii. -Todd" | |
1666 | -m Please ensure that you upgrade your systems right away | |
1667 | ||
1668 | Multi-line message bodies may be specified with the -m option by putting | |
1669 | a "\\n" into the message. Example: | |
1670 | -m "This is line 1.\\nAnd this is line 2." | |
1671 | ||
1672 | HTML messages are supported, simply begin your message with "<html>" and | |
1673 | sendEmail will properly label the mime header so MUAs properly render | |
1674 | the message. | |
1675 | ||
1676 | -o message-file=FILE | |
1677 | This option is one of three methods that allow you to specify the message | |
1678 | body for your email. To use this option simply specify a text file | |
1679 | containing the body of your email message. Examples: | |
1680 | -o message-file=/root/message.txt | |
1681 | -o message-file="C:\\Program Files\\output.txt" | |
1682 | ||
1683 | -o message-header=EMAIL HEADER | |
1684 | This option allows you to specify an additional single line to insert | |
1685 | into the email headers. Do not use this unless you know what you are | |
1686 | doing! | |
1687 | Example: To scare a Microsoft Outlook user you may want to try this: | |
1688 | -o message-header="X-Message-Flag: This message contains illegal content" | |
1689 | ||
1690 | -o message-format=raw | |
1691 | This option instructs sendEmail to assume the message is already a | |
1692 | complete email message. SendEmail will not generate any headers and will | |
1693 | transmit the message as-is to the remote SMTP server. Due to the nature | |
1694 | of this option the following command line options will be ignored when this | |
1695 | one is used: | |
1696 | -u SUBJECT | |
1697 | -o message-header=EMAIL HEADER | |
1698 | -o message-charset=CHARSET | |
1699 | -a ATTACHMENT | |
1700 | ||
1701 | -o message-charset=CHARSET | |
1702 | This option allows you to specify the character-set for the message body. | |
1703 | The default is iso-8859-1. | |
1704 | ||
1705 | ||
1706 | ${colorGreen}The Message Body${colorNormal} | |
1707 | The message body may be specified in one of three ways: | |
1708 | 1) Via the -m MESSAGE command line option. | |
1709 | Example: | |
1710 | -m "This is the message body" | |
1711 | ||
1712 | 2) By putting the message body in a file and using the -o message-file=FILE | |
1713 | command line option. | |
1714 | Example: | |
1715 | -o message-file=/root/message.txt | |
1716 | ||
1717 | 3) By piping the message body to sendEmail when nither of the above command | |
1718 | line options were specified. | |
1719 | Example: | |
1720 | grep "ERROR" /var/log/messages | sendEmail -t you\@domain.com ... | |
1721 | ||
1722 | If the message body begins with "<html>" then the message will be treated as | |
1723 | an HTML message and the MIME headers will be written so that a HTML capable | |
1724 | email client will display the message in it's HTML form. | |
1725 | Any of the above methods may be used with the -o message-format=raw option | |
1726 | to deliver an already complete email message. | |
1727 | ||
1728 | ||
1729 | EOM | |
1730 | last CASE; | |
1731 | }; | |
1732 | ||
1733 | ||
1734 | ||
1735 | ||
1736 | ||
1737 | ||
1738 | ## MISC | |
1739 | ($topic eq 'misc') && do { | |
1740 | print <<EOM; | |
1741 | ||
1742 | ${colorBold}MISC DOCUMENTATION${colorNormal} | |
1743 | ||
1744 | ${colorGreen}Misc Options${colorNormal} | |
1745 | Options that don't fit anywhere else: | |
1746 | -xu USERNAME | |
1747 | -xp PASSWORD | |
1748 | -a ATTACHMENT | |
1749 | -o timeout=SECONDS | |
1750 | ||
1751 | -xu USERNAME | |
1752 | This option, in conjunction with the -xp option, allows you to specify | |
1753 | a username and password to be used with SMTP servers requiring | |
1754 | authentication (via SMTP AUTH.) | |
1755 | ||
1756 | -xp PASSWORD | |
1757 | This option, in conjunction with the -xu option, allows you to specify | |
1758 | a username and password to be used with SMTP servers requiring | |
1759 | authentication (via SMTP AUTH.) | |
1760 | ||
1761 | -a ATTACHMENT [ATTACHMENT] | |
1762 | This option allows you to attach any number of files to your email | |
1763 | message. | |
1764 | ||
1765 | -o timeout=SECONDS | |
1766 | This option sets the timeout value in seconds used for all network reads, | |
1767 | writes, and a few other things. | |
1768 | ||
1769 | ||
1770 | ${colorGreen}The Complete -o Parameter List${colorNormal} | |
1771 | -o message-file=FILE | |
1772 | -o message-header=EMAIL HEADER | |
1773 | -o message-format=raw | |
1774 | -o message-charset=CHARSET | |
1775 | -o reply-to=ADDRESS | |
1776 | -o timeout=SECONDS | |
1777 | ||
1778 | ||
1779 | EOM | |
1780 | last CASE; | |
1781 | }; | |
1782 | ||
1783 | ||
1784 | ||
1785 | ||
1786 | ||
1787 | ||
1788 | ## NETWORKING | |
1789 | ($topic eq 'networking') && do { | |
1790 | print <<EOM; | |
1791 | ||
1792 | ${colorBold}NETWORKING DOCUMENTATION${colorNormal} | |
1793 | ||
1794 | ${colorGreen}Networking Options${colorNormal} | |
1795 | Options related to networking: | |
1796 | -s SERVER[:PORT] | |
1797 | -o timeout=SECONDS | |
1798 | ||
1799 | -s SERVER[:PORT] | |
1800 | This option allows you to specify the SMTP server sendEmail should | |
1801 | connect to to deliver your email message to. If this option is not | |
1802 | specified sendEmail will try to connect to localhost:25 to deliver | |
1803 | the message. THIS IS MOST LIKELY NOT WHAT YOU WANT, AND WILL LIKELY | |
1804 | FAIL unless you have a email server (commonly known as an MTA) running | |
1805 | on your computer! | |
1806 | Typically you will need to specify your company or ISP's email server. | |
1807 | For example, if you use CableOne you will need to specify: | |
1808 | -s mail.cableone.net | |
1809 | If you have your own email server running on port 300 you would | |
1810 | probably use an option like this: | |
1811 | -s myserver.mydomain.com:300 | |
1812 | ||
1813 | -o timeout=SECONDS | |
1814 | This option sets the timeout value in seconds used for all network reads, | |
1815 | writes, and a few other things. | |
1816 | ||
1817 | ||
1818 | EOM | |
1819 | last CASE; | |
1820 | }; | |
1821 | ||
1822 | ||
1823 | ||
1824 | ||
1825 | ||
1826 | ||
1827 | ## OUTPUTO | |
1828 | ($topic eq 'output') && do { | |
1829 | print <<EOM; | |
1830 | ||
1831 | ${colorBold}OUTPUT DOCUMENTATION${colorNormal} | |
1832 | ||
1833 | ${colorGreen}Output Options${colorNormal} | |
1834 | Options related to output: | |
1835 | -l LOGFILE | |
1836 | -v | |
1837 | -q | |
1838 | ||
1839 | -l LOGFILE | |
1840 | This option allows you to specify a log file to append to. Every message | |
1841 | that is displayed to STDOUT is also written to the log file. This may be | |
1842 | used in conjunction with -q and -v. | |
1843 | ||
1844 | -q | |
1845 | This option tells sendEmail to disable printing to STDOUT. In other | |
1846 | words nothing will be printed to the console. This does not affect the | |
1847 | behavior of the -l or -v options. | |
1848 | ||
1849 | -v | |
1850 | This option allows you to increase the debug level of sendEmail. You may | |
1851 | either use this option more than once, or specify more than one v at a | |
1852 | time to obtain a debug level higher than one. Examples: | |
1853 | Specifies a debug level of 1: -v | |
1854 | Specifies a debug level of 2: -vv | |
1855 | Specifies a debug level of 2: -v -v | |
1856 | A debug level of one is recommended when doing any sort of debugging. | |
1857 | At that level you will see the entire SMTP transaction (except the | |
1858 | body of the email message), and hints will be displayed for most | |
1859 | warnings and errors. The highest debug level is three. | |
1860 | ||
1861 | ||
1862 | EOM | |
1863 | last CASE; | |
1864 | }; | |
1865 | ||
1866 | ## Unknown option selected! | |
1867 | quit("ERROR => The help topic specified is not valid!", 1); | |
1868 | }; | |
1869 | ||
1870 | exit(1); | |
1871 | } | |
1872 | ||
1873 |