h8rt3rmin8r

smtp-cli.perl

Oct 1st, 2018
558
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/perl
  2.  
  3. #
  4. # Command line SMTP client with SSL, STARTTLS, SMTP-AUTH and IPv6 support.
  5. # Michal Ludvig, 2003-2018
  6. # See http://smtp-cli.logix.cz for details
  7. # and https://github.com/mludvig/smtp-cli for code.
  8. # Thanks to all contributors for ideas and fixes!
  9. #
  10. # Source Code: https://raw.githubusercontent.com/mludvig/smtp-cli/HEAD/smtp-cli
  11. # Mirror: https://pastebin.com/raw/4wTJ36fs
  12. #
  13.  
  14. my $version = "3.9";
  15.  
  16. #
  17. # ChangeLog:
  18. # * Version 3.9    (2018-04-06)
  19. #   - Don't attempt to use IO::Socket::INET6 with --ipv4.
  20. #
  21. # * Version 3.8    (2017-07-05)
  22. #   - New parameter --local-addr
  23. #   - Support body and attachment reading from non-regular files
  24. #   - Various protocol fixes
  25. #
  26. # * Version 3.7    (2014-11-21)
  27. #   - Support STDIN input with --body-plain=- or --body-html=-
  28. #
  29. # * Version 3.6    (2013-07-11)
  30. #   - Improved compatibility with perl < 5.10 and perl >= 5.18
  31. #   - Added support for more chars in user-part of email address.
  32. #
  33. # * Version 3.5    (2013-05-08)
  34. #   - Improved compliance with SMTP RFC 5321
  35. #   - New parameter --text-encoding
  36. #
  37. # * Version 3.4    (2013-02-05)
  38. #   - Ok, ok, support both File::Type and File::LibMagic
  39. #
  40. # * Version 3.3    (2012-07-30)
  41. #   - Moved from File::Type to File::LibMagic
  42. #     (File::Type is no longer maintained and not available
  43. #      in EPEL for RHEL 6)
  44. #
  45. # * Version 3.2    (2012-06-26)
  46. #   - Fixed syntax error
  47. #
  48. # * Version 3.1    (2012-06-25)
  49. #   - New --add-header, --replace-header and --remove-header options.
  50. #   - Improved compatibility with new IO::Socket::SSL releases.
  51. #
  52. # * Version 3.0    (2012-01-24)
  53. #   - Support for server SSL verification agains CA root cert.
  54. #   - Use "Content-Disposition: attachment" for all attachments
  55. #     unless --attach-inline was used.
  56. #   - No longer default to --server=localhost
  57. #   - Support for --charset=<charset> affecting all text/* parts.
  58. #   - Ensure "To: undisclosed-recipients:;" if sending only to Bcc.
  59. #
  60. # * Version 2.9    (2011-09-02)
  61. #   - Fixed problem when using IPv6 addresses with --server.
  62. #     For example with --server 2001:db8::123 it was connecting
  63. #     to server 2001:db8:: port 123. Fixed now.
  64. #
  65. # * Version 2.8    (2011-01-05)
  66. #   - Added --ssl to support for SSMTP (SMTP over SSL). This is
  67. #     turned on by default when --port=465.
  68. #
  69. # * Version 2.7    (2010-09-08)
  70. #   - Added support for Cc header (--cc=...)
  71. #   - Addressess (From, To, Cc) can now contain a "display name",
  72. #     for example --from="Michal Ludvig <michal@logix.cz>"
  73. #   - Support for --mail-from and --rcpt-to addresses independent
  74. #     on --from, --to, --cc and --bcc
  75. #   - Fixed warnings in Perl 5.12
  76. #
  77. # * Version 2.6    (2009-08-05)
  78. #   - Message building fixed for plaintext+attachment case.
  79. #   - Auto-enable AUTH as soon as --user parameter is used.
  80. #     (previously --enable-auth or --auth-plain had to be used
  81. #      together with --user, that was confusing).
  82. #   - New --print-only parameter for displaying the composed
  83. #     MIME message without sending.
  84. #   - All(?) non-standard modules are now optional.
  85. #   - Displays local and remote address on successfull connect.
  86. #
  87. # * Version 2.5    (2009-07-21)
  88. #   - IPv6 support provided the required modules are
  89. #     available.
  90. #
  91. # * Version 2.1    (2008-12-08)
  92. #   - Make the MIME modules optional. Simply disable
  93. #     the required functionality if they're not available.
  94. #
  95. # * Version 2.0    (2008-11-18)
  96. #   - Support for message building through MIME::Lite,
  97. #     including attachments, multipart, etc.
  98. #
  99. # * Version 1.1    (2006-08-26)
  100. #   - STARTTLS and AUTH support
  101. #
  102. # * Version 1.0
  103. #   - First public version
  104. #
  105. # This program is licensed under GNU Public License v3 (GPLv3)
  106. #
  107.  
  108. ## Require Perl 5.8 or higher -> we need open(.., .., \$variable) construct
  109. require 5.008;
  110.  
  111. use strict;
  112. use IO::Socket::INET;
  113. use MIME::Base64 qw(encode_base64 decode_base64);
  114. use Getopt::Long;
  115. use Socket qw(:DEFAULT :crlf);
  116.  
  117. my @valid_encodings = ("7bit", "8bit", "binary", "base64", "quoted-printable");
  118.  
  119. my ($user, $pass, $host, $port, $addr_family, $localaddr,
  120.     $use_login, $use_plain, $use_cram_md5,
  121.     $ehlo_ok, $auth_ok, $starttls_ok, $ssl, $verbose,
  122.     $hello_host, $datasrc,
  123.     $mail_from, @rcpt_to, $from, @to, @cc, @bcc,
  124.     $missing_modules_ok, $missing_modules_count,
  125.     $subject, $body_plain, $body_html, $charset, $text_encoding, $print_only,
  126.     @attachments, @attachments_inline,
  127.     @add_headers, @replace_headers, @remove_headers,
  128.     $ssl_ca_file, $ssl_ca_path,
  129.     $sock, $built_message);
  130.  
  131. $host = undef;
  132. $port = 'smtp(25)';
  133. $addr_family = AF_UNSPEC;
  134. $localaddr = undef;
  135. $hello_host = 'localhost';
  136. $verbose = 0;
  137. $use_login = 0;
  138. $use_plain = 0;
  139. $use_cram_md5 = 0;
  140. $starttls_ok = 1;
  141. $ssl = undef;
  142. $auth_ok = 0;
  143. $ehlo_ok = 1;
  144. $missing_modules_ok = 0;
  145. $missing_modules_count = 0;
  146. $charset = undef;
  147. $text_encoding = "quoted-printable";
  148. $print_only = 0;
  149.  
  150. # Get command line options.
  151. GetOptions (
  152.     'host|server=s' => \$host,
  153.     'port=i' => \$port,
  154.     '4|ipv4' => sub { $addr_family = AF_INET; },
  155.     '6|ipv6' => sub { $addr_family = AF_INET6; },
  156.     'local-addr=s' => \$localaddr,
  157.     'user=s' => \$user, 'password=s' => \$pass,
  158.     'auth-login' => \$use_login,
  159.     'auth-plain' => \$use_plain,
  160.     'auth-cram-md5' => \$use_cram_md5,
  161.     'disable-ehlo' => sub { $ehlo_ok = 0; },
  162.     'force-ehlo' => sub { $ehlo_ok = 2; },
  163.     'hello-host|ehlo-host|helo-host=s' => \$hello_host,
  164.     'auth|enable-auth' => \$auth_ok,
  165.     'disable-starttls|disable-tls|disable-ssl' =>
  166.         sub { $starttls_ok = 0; },
  167.     'ssl' => sub { $ssl = 1 },
  168.     'disable-ssl' => sub { $ssl = 0 },
  169.     'mail-from=s' => \$mail_from,
  170.     'rcpt-to=s' => \@rcpt_to,
  171.     'from=s' => \$from,
  172.     'to=s' => \@to,
  173.     'cc=s' => \@cc,
  174.     'bcc=s' => \@bcc,
  175.     'data=s' => \$datasrc,
  176.     'subject=s' => \$subject,
  177.     'body|body-plain=s' => \$body_plain,
  178.     'body-html=s' => \$body_html,
  179.     'charset=s' => \$charset,
  180.     'text-encoding=s' => \$text_encoding,
  181.     'attachment|attach=s' => \@attachments,
  182.     'attachment-inline|attach-inline=s' => \@attachments_inline,
  183.     'add-header=s' => \@add_headers,
  184.     'replace-header=s' => \@replace_headers,
  185.     'remove-header=s' => \@remove_headers,
  186.     'print-only' => \$print_only,
  187.     'missing-modules-ok' => \$missing_modules_ok,
  188.     'ssl-ca-file=s' => \$ssl_ca_file,
  189.     'ssl-ca-path=s' => \$ssl_ca_path,
  190.     'v|verbose+' => \$verbose,
  191.     'version' => sub { &version() },
  192.     'help' => sub { &usage() } );
  193.  
  194. #### Try to load optional modules
  195.  
  196. ## IO::Socket::SSL and Net::SSLeay are optional
  197. my $have_ssl = eval { require IO::Socket::SSL; require Net::SSLeay; 1; };
  198. if (not $have_ssl and not $missing_modules_ok) {
  199.     warn("!!! IO::Socket::SSL and/or Net::SSLeay modules are not found\n");
  200.     warn("!!! These modules are required for SSL and STARTTLS support\n");
  201.     $missing_modules_count += 2;
  202. }
  203.  
  204. ## IO::Socket::INET6 and Socket6 are optional
  205. my $socket6 = eval { require IO::Socket::INET6; require Socket6; 1; };
  206. if (not $socket6 and not ($addr_family == AF_INET)) {
  207.     if ($addr_family == AF_INET6) {
  208.         die("!!! IO::Socket::INET6 and Socket6 modules are not found\nIPv6 support is not available\n");
  209.     }
  210.     if (not $missing_modules_ok) {
  211.         warn("!!! IO::Socket::INET6 -- optional module not found\n");
  212.         warn("!!! Socket6 -- optional module not found\n");
  213.         warn("!!! These modules are required for IPv6 support\n\n");
  214.         $missing_modules_count += 2;
  215.     }
  216. }
  217.  
  218. ## MIME::Lite dependency is optional
  219. my $mime_lite = eval { require MIME::Lite; 1; };
  220. if (not $mime_lite and not $missing_modules_ok) {
  221.     warn("!!! MIME::Lite -- optional module not found\n");
  222.     warn("!!! Used for composing messages from --subject, --body, --attachment, etc.\n\n");
  223.     $missing_modules_count++;
  224. }
  225.  
  226. ## File::LibMagic dependency is optional
  227. my $file_libmagic = eval { require File::LibMagic; File::LibMagic->new(); };
  228.  
  229. ## File::Type dependency is optional
  230. ## Not needed if File::LibMagic is available
  231. my $file_type = eval { require File::Type; File::Type->new(); };
  232.  
  233. if (not $file_libmagic and not $file_type and not $missing_modules_ok) {
  234.     warn("!!! Neither File::LibMagic nor File::Type module found.\n");
  235.     warn("!!! Used for guessing MIME types of attachments. Optional.\n\n");
  236.     $missing_modules_count++;
  237. }
  238.  
  239. ## Term::ReadKey dependency is optional
  240. my $have_term_readkey = eval { require Term::ReadKey; 1; };
  241. if (not $have_term_readkey and not $missing_modules_ok) {
  242.     warn("!!! Term::ReadKey -- optional module not found\n");
  243.     warn("!!! Used for hidden reading SMTP password from the terminal\n\n");
  244.     $missing_modules_count++;
  245. }
  246.  
  247. my $have_hmac_md5 = eval { require Digest::HMAC_MD5; 1; };
  248. if (not $have_hmac_md5 and not $missing_modules_ok) {
  249.     if ($use_cram_md5) {
  250.         die("!!! CRAM-MD5 authentication is not available because Digest::HMAC_MD5 module is missing\n");
  251.     }
  252.     warn("!!! Digest::HMAC_MD5 -- optional module missing\n");
  253.     warn("!!! Used for CRAM-MD5 authentication method\n");
  254.     $missing_modules_count++;
  255. }
  256.  
  257. ## Advise about --missing-modules-ok parameter
  258. if ($missing_modules_count) {
  259.     warn("!!! Use --missing-modules-ok if you don't need the above listed modules\n");
  260.     warn("!!! and don't want to see this message again.\n\n");
  261. }
  262.  
  263. ## Make sure we've got a server name to connect to
  264. if (not defined($host)) {
  265.     if (not $print_only) {
  266.         die("Error: Specify the SMTP server with --server=hostname[:port]\n");
  267.     } else {
  268.         # We're printing to stdout only, let's assign just about any
  269.         # hostname to satisfy the next few tests.
  270.         $host = "localhost";
  271.     }
  272. }
  273.  
  274. ## Make sure the --text-encoding value is valid
  275. if (not grep(/^$text_encoding$/, @valid_encodings))
  276. {
  277.     die ("The --text-encoding value is invalid: $text_encoding\nMust be one of: " . join(', ', @valid_encodings) . "\n");
  278. }
  279.  
  280. ## Accept hostname with port number as host:port
  281. ## Either it's a hostname:port or 1.2.3.4:port or [2001:db8::1]:port.
  282. ## Don't parse 2001:db8::1 as $host=2001:db8:: and $port=1!
  283. if (($host =~ /^([^:]+):([:alnum:]+)$/) or
  284.     ($host =~ /^\[([[:xdigit:]:]+)\]:([:alnum:]+)$/))
  285. {
  286.     $host = $1;
  287.     $port = $2;
  288. }
  289.  
  290. ## Automatically start in SSL mode if port == 465 (SSMTP)
  291. if (not defined($ssl)) {
  292.     $ssl = ($port == 465);
  293. }
  294.  
  295. # Extract $mail_from address from $from
  296. if (not defined($mail_from) and defined($from)) {
  297.     $mail_from = &find_email_addr($from) or
  298.         die ("The --from string does not contain a valid email address: $from\n");
  299. }
  300.  
  301. # Extract @rcpt_to list from @to, @cc and @bcc
  302. if (not @rcpt_to) {
  303.     foreach my $rcpt (@to, @cc, @bcc) {
  304.         my $rcpt_addr = &find_email_addr($rcpt);
  305.         if (not defined($rcpt_addr)) {
  306.             warn("No valid email address found in: $rcpt\n");
  307.             next;
  308.         }
  309.         push(@rcpt_to, $rcpt_addr);
  310.     }
  311. }
  312.  
  313. # Ensure "To: undisclosed-recipients:;" when sending only to Bcc's
  314. if (not @to and not @cc) {
  315.     push(@to, "undisclosed-recipients:;");
  316. }
  317.  
  318. # Build the MIME message if required
  319. if (defined($subject) or defined($body_plain) or defined($body_html) or
  320.         @attachments or @attachments_inline) {
  321.     if (not $mime_lite) {
  322.         die("Module MIME::Lite is not available. Unable to build the message, sorry.\n".
  323.             "Use --data and provide a complete email payload including headers instead.\n");
  324.     }
  325.     if (defined($datasrc)) {
  326.         die("Requested building a message and at the same time used --data parameter.\n".
  327.             "That's not possible, sorry.\n");
  328.     }
  329.     if (defined($body_plain)) {
  330.         if (-e $body_plain) {
  331.             local $/=undef;
  332.             open(FILE, $body_plain);
  333.             $body_plain = <FILE>;
  334.             close(FILE);
  335.         } elsif ($body_plain eq "-") {
  336.             local $/=undef;
  337.             $body_plain = <STDIN>;
  338.         }
  339.     }
  340.     if (defined($body_html)) {
  341.         if (-e $body_html) {
  342.             local $/=undef;
  343.             open(FILE, $body_html);
  344.             $body_html = <FILE>;
  345.             close(FILE);
  346.         } elsif ($body_html eq "-") {
  347.             local $/=undef;
  348.             $body_html = <STDIN>;
  349.         }
  350.     }
  351.     my $message = &build_message();
  352.  
  353.     open(BUILT_MESSAGE, "+>", \$built_message);
  354.     $datasrc = "///built_message";
  355.     if ($print_only) {
  356.         $message->print();
  357.         exit(0);
  358.     } else {
  359.         $message->print(\*BUILT_MESSAGE);
  360.     }
  361.     seek(BUILT_MESSAGE, 0, 0);
  362. }
  363.  
  364. # Username was given -> enable AUTH
  365. if ($user)
  366.     { $auth_ok = 1; }
  367.  
  368. # If at least one --auth-* option was given, enable AUTH.
  369. if ($use_login + $use_plain + $use_cram_md5 > 0)
  370.     { $auth_ok = 1; }
  371.  
  372. # If --enable-auth was given, enable all AUTH methods.
  373. elsif ($auth_ok && ($use_login + $use_plain + $use_cram_md5 == 0))
  374. {
  375.     $use_login = 1;
  376.     $use_plain = 1;
  377.     $use_cram_md5 = 1 if ($have_hmac_md5);
  378. }
  379.  
  380. # Exit if user haven't specified username for AUTH.
  381. if ($auth_ok && !defined ($user))
  382.     { die ("SMTP AUTH support requested without --user\n"); }
  383.  
  384. # Ask for password if it wasn't supplied on the command line.
  385. if ($auth_ok && defined ($user) && !defined ($pass))
  386. {
  387.     if ($have_term_readkey) {
  388.         # Set echo off.
  389.         Term::ReadKey::ReadMode (2);
  390.     } else {
  391.         warn ("Module Term::ReadKey not available - password WILL NOT be hidden!!!\n");
  392.     }
  393.     printf ("Enter password for %s@%s : ", $user, $host);
  394.     $pass = <>;
  395.     if ($have_term_readkey) {
  396.         # Restore echo.
  397.         Term::ReadKey::ReadMode (0);
  398.         printf ("\n");
  399.     }
  400.     exit if (! defined ($pass));
  401.     chop ($pass);
  402. }
  403.  
  404. # Connect to the SMTP server.
  405. my %connect_args = (
  406.     PeerAddr => $host,
  407.     PeerPort => $port,
  408.     Proto => 'tcp',
  409.     Timeout => 5);
  410.  
  411. if (defined($localaddr)) {
  412.   $connect_args{'LocalAddr'} = $localaddr;
  413. }
  414.  
  415. if ($addr_family == AF_INET) {
  416.     # If the user requested --ipv4 don't even bother with INET6 module
  417.     # (although it should work some users reported problems)
  418.     $sock = IO::Socket::INET->new(%connect_args) or die ("Connect failed: $@\n");
  419. } else {
  420.     # Either --ipv6 or no preference - do the best we can
  421.     $connect_args{'Domain'} = $addr_family;
  422.     $sock = IO::Socket::INET6->new(%connect_args) or die ("Connect failed: $@\n");
  423. }
  424.  
  425. if ($verbose >= 1) {
  426.     my $addr_fmt = "%s";
  427.     $addr_fmt = "[%s]" if ($sock->sockhost() =~ /:/); ## IPv6 connection
  428.  
  429.     printf ("Connection from $addr_fmt:%s to $addr_fmt:%s\n",
  430.         $sock->sockhost(), $sock->sockport(),
  431.         $sock->peerhost(), $sock->peerport());
  432. }
  433.  
  434. if ($ssl) {
  435.     printf ("Starting SMTP/SSL...\n") if ($verbose >= 1);
  436.     &socket_to_ssl($sock);
  437. }
  438.  
  439. my ($code, $text);
  440. my (%features);
  441.  
  442. # Wait for the welcome message of the server.
  443. ($code, $text) = &get_line ($sock);
  444. die ("Unknown welcome string: '$code $text'\n") if ($code != 220);
  445. $ehlo_ok-- if ($text !~ /ESMTP/);
  446.  
  447. # Send EHLO
  448. &say_hello ($sock, $ehlo_ok, $hello_host, \%features) or exit (1);
  449.  
  450. # Run the SMTP session
  451. my $exitcode = &run_smtp ();
  452.  
  453. # Good bye...
  454. &send_line ($sock, "QUIT\n");
  455. ($code, $text) = &get_line ($sock);
  456. die ("Unknown QUIT response '$code'.\n") if ($code != 221);
  457.  
  458. exit $exitcode;
  459.  
  460. # This is the main SMTP "engine".
  461. sub run_smtp
  462. {
  463.     # See if we could start encryption
  464.     if ((defined ($features{'STARTTLS'}) || defined ($features{'TLS'})) && $starttls_ok && !$have_ssl)
  465.     {
  466.         warn ("Module IO::Socket::SSL is missing - STARTTLS support disabled.\n");
  467.         warn ("Use --disable-starttls or install the modules to avoid this warning.\n");
  468.         undef ($features{'STARTTLS'});
  469.         undef ($features{'TLS'});
  470.     }
  471.  
  472.     if ((defined ($features{'STARTTLS'}) || defined ($features{'TLS'})) && $starttls_ok)
  473.     {
  474.         printf ("Starting TLS...\n") if ($verbose >= 1);
  475.  
  476.         &send_line ($sock, "STARTTLS\n");
  477.         ($code, $text) = &get_line ($sock);
  478.         die ("Unknown STARTTLS response '$code'.\n") if ($code != 220);
  479.  
  480.         &socket_to_ssl($sock);
  481.  
  482.         # Send EHLO again (required by the SMTP standard).
  483.         &say_hello ($sock, $ehlo_ok, $hello_host, \%features) or return 0;
  484.     }
  485.  
  486.     # See if we should authenticate ourself
  487.     if (defined ($features{'AUTH'}) && $auth_ok)
  488.     {
  489.         printf ("AUTH method (%s): ", $features{'AUTH'}) if ($verbose >= 1);
  490.  
  491.         ## Try DIGEST-MD5 first
  492.         # Actually we won't. It never worked reliably here.
  493.         # After all DIGEST-MD5 is on a way to deprecation
  494.         # see this thread: http://www.imc.org/ietf-sasl/mail-archive/msg02996.html
  495.  
  496.         # Instead use CRAM-MD5 if supported by the server
  497.         if ($features{'AUTH'} =~ /CRAM-MD5/i && $use_cram_md5)
  498.         {
  499.             printf ("using CRAM-MD5\n") if ($verbose >= 1);
  500.             &send_line ($sock, "AUTH CRAM-MD5\n");
  501.             ($code, $text) = &get_line ($sock);
  502.             if ($code != 334)
  503.                 { die ("AUTH CRAM-MD5 failed: $code $text\n"); }
  504.  
  505.             my $response = &encode_cram_md5 ($text, $user, $pass);
  506.             &send_line ($sock, "%s\n", $response);
  507.             ($code, $text) = &get_line ($sock);
  508.             if ($code != 235)
  509.                 { die ("AUTH CRAM-MD5 failed: $code $text\n"); }
  510.         }
  511.         # Eventually try LOGIN method
  512.         elsif ($features{'AUTH'} =~ /LOGIN/i && $use_login)
  513.         {
  514.             printf ("using LOGIN\n") if ($verbose >= 1);
  515.             &send_line ($sock, "AUTH LOGIN\n");
  516.             ($code, $text) = &get_line ($sock);
  517.             if ($code != 334)
  518.                 { die ("AUTH LOGIN failed: $code $text\n"); }
  519.  
  520.             &send_line ($sock, "%s\n", encode_base64 ($user, ""));
  521.  
  522.             ($code, $text) = &get_line ($sock);
  523.             if ($code != 334)
  524.                 { die ("AUTH LOGIN failed: $code $text\n"); }
  525.  
  526.             &send_line ($sock, "%s\n", encode_base64 ($pass, ""));
  527.  
  528.             ($code, $text) = &get_line ($sock);
  529.             if ($code != 235)
  530.                 { die ("AUTH LOGIN failed: $code $text\n"); }
  531.         }
  532.         # Or finally PLAIN if nothing else was supported.
  533.         elsif ($features{'AUTH'} =~ /PLAIN/i && $use_plain)
  534.         {
  535.             printf ("using PLAIN\n") if ($verbose >= 1);
  536.             &send_line ($sock, "AUTH PLAIN %s\n",
  537.                 encode_base64 ("$user\0$user\0$pass", ""));
  538.             ($code, $text) = &get_line ($sock);
  539.             if ($code != 235)
  540.                 { die ("AUTH PLAIN failed: $code $text\n"); }
  541.         }
  542.         # Complain otherwise.
  543.         else
  544.         {
  545.             warn ("No supported authentication method\n".
  546.                   "advertised by the server.\n");
  547.             return 1;
  548.         }
  549.  
  550.         printf ("Authentication of $user\@$host succeeded\n") if ($verbose >= 1);
  551.     }
  552.  
  553.     # We can do a relay-test now if a recipient was set.
  554.     if ($#rcpt_to >= 0)
  555.     {
  556.         if (!defined ($mail_from))
  557.         {
  558.             warn ("From: address not set. Using empty one.\n");
  559.             $mail_from = "";
  560.         }
  561.         &send_line ($sock, "MAIL FROM:<%s>\n", $mail_from);
  562.         ($code, $text) = &get_line ($sock);
  563.         if ($code != 250)
  564.         {
  565.             warn ("MAIL FROM <$mail_from> failed: '$code $text'\n");
  566.             return 1;
  567.         }
  568.  
  569.         my $i;
  570.         for ($i=0; $i <= $#rcpt_to; $i++)
  571.         {
  572.             &send_line ($sock, "RCPT TO:<%s>\n", $rcpt_to[$i]);
  573.             ($code, $text) = &get_line ($sock);
  574.             if ($code != 250)
  575.             {
  576.                 warn ("RCPT TO <".$rcpt_to[$i]."> ".
  577.                       "failed: '$code $text'\n");
  578.                 return 0;
  579.             }
  580.         }
  581.     }
  582.  
  583.     # Wow, we should even send something!
  584.     if (defined ($datasrc))
  585.     {
  586.         if ($datasrc eq "///built_message")
  587.         {
  588.             *MAIL = *BUILT_MESSAGE;
  589.         }
  590.         elsif ($datasrc eq "-")
  591.         {
  592.             *MAIL = *STDIN;
  593.         }
  594.         elsif (!open (MAIL, $datasrc))
  595.         {
  596.             warn ("Can't open file '$datasrc'\n");
  597.             return 0;
  598.         }
  599.  
  600.         &send_line ($sock, "DATA\n");
  601.         ($code, $text) = &get_line ($sock);
  602.         if ($code != 354)
  603.         {
  604.             warn ("DATA failed: '$code $text'\n");
  605.             return 0;
  606.         }
  607.  
  608.         while (<MAIL>)
  609.         {
  610.             my $line = $_;
  611.             # RFC 5321 section 4.5.2 - leading dot must be doubled
  612.             $line =~ s/^\./\.\./;
  613.             # RFC 5321 section 2.3.8 - ensure CR-LF line ending
  614.             $line =~ s/[\r\n]+$/$CRLF/;
  615.             $sock->print ($line);
  616.         }
  617.  
  618.         close (MAIL);
  619.  
  620.         $sock->printf ("$CRLF.$CRLF");
  621.  
  622.         ($code, $text) = &get_line ($sock);
  623.         if ($code != 250)
  624.         {
  625.             warn ("DATA not send: '$code $text'\n");
  626.             return 0;
  627.         }
  628.     }
  629.  
  630.     # Perfect. Everything succeeded!
  631.     return 1;
  632. }
  633.  
  634. # Get one line of response from the server.
  635. sub get_one_line ($)
  636. {
  637.     my $sock = shift;
  638.     my ($code, $sep, $text) = ($sock->getline() =~ /(\d+)(.)([^\r]*)/);
  639.     my $more;
  640.     $more = ($sep eq "-");
  641.     if ($verbose)
  642.         { printf ("[%d] '%s'\n", $code, $text); }
  643.     return ($code, $text, $more);
  644. }
  645.  
  646. # Get concatenated lines of response from the server.
  647. sub get_line ($)
  648. {
  649.     my $sock = shift;
  650.     my ($code, $text, $more) = &get_one_line ($sock);
  651.     while ($more) {
  652.         my ($code2, $line);
  653.         ($code2, $line, $more) = &get_one_line ($sock);
  654.         $text .= " $line";
  655.         die ("Error code changed from $code to $code2. That's illegal.\n") if ($code ne $code2);
  656.     }
  657.     return ($code, $text);
  658. }
  659.  
  660. # Send one line back to the server
  661. sub send_line ($@)
  662. {
  663.     my $socket = shift;
  664.     my @args = @_;
  665.  
  666.     if ($verbose)
  667.         { printf ("> "); printf (@args); }
  668.     $args[0] =~ s/\n/$CRLF/g;
  669.     $socket->printf (@args);
  670. }
  671.  
  672. sub socket_to_ssl($)
  673. {
  674.     if (!$have_ssl) {
  675.         die ("SSL/TLS support is not available due to missing modules. Sorry.\n");
  676.     }
  677.  
  678.     # Do Net::SSLeay initialization
  679.     Net::SSLeay::load_error_strings();
  680.     Net::SSLeay::SSLeay_add_ssl_algorithms();
  681.     Net::SSLeay::randomize();
  682.  
  683.     if (! IO::Socket::SSL->start_SSL($sock, {
  684.         SSL_ca_file => $ssl_ca_file,
  685.         SSL_ca_path => $ssl_ca_path,
  686.         SSL_verify_mode => (defined($ssl_ca_file) or defined($ssl_ca_path)) ? 0x01 : 0x00,
  687.     }))
  688.     {
  689.         die ("SSL/TLS: ".IO::Socket::SSL::errstr()."\n");
  690.     }
  691.  
  692.     if ($verbose >= 1)
  693.     {
  694.         printf ("Using cipher: %s\n", $sock->get_cipher ());
  695.         printf ("%s", $sock->dump_peer_certificate());
  696.     }
  697. }
  698.  
  699. # Helper function to encode CRAM-MD5 challenge
  700. sub encode_cram_md5 ($$$)
  701. {
  702.     my ($ticket64, $username, $password) = @_;
  703.     my $ticket = decode_base64($ticket64) or
  704.         die ("Unable to decode Base64 encoded string '$ticket64'\n");
  705.  
  706.     print "Decoded CRAM-MD5 challenge: $ticket\n" if ($verbose > 1);
  707.     my $password_md5 = Digest::HMAC_MD5::hmac_md5_hex($ticket, $password);
  708.     return encode_base64 ("$username $password_md5", "");
  709. }
  710.  
  711. # Store all server's ESMTP features to a hash.
  712. sub say_hello ($$$$)
  713. {
  714.     my ($sock, $ehlo_ok, $hello_host, $featref) = @_;
  715.     my ($feat, $param);
  716.     my $hello_cmd = $ehlo_ok > 0 ? "EHLO" : "HELO";
  717.  
  718.     &send_line ($sock, "$hello_cmd $hello_host\n");
  719.     my ($code, $text, $more) = &get_one_line ($sock);
  720.  
  721.     if ($code != 250)
  722.     {
  723.         warn ("$hello_cmd failed: '$code $text'\n");
  724.         return 0;
  725.     }
  726.  
  727.     # Empty the hash
  728.     %{$featref} = ();
  729.  
  730.     ($feat, $param) = ($text =~ /^(\w+)[= ]*(.*)$/);
  731.     $featref->{$feat} = $param;
  732.  
  733.     # Load all features presented by the server into the hash
  734.     while ($more == 1)
  735.     {
  736.         ($code, $text, $more) = &get_one_line ($sock);
  737.         ($feat, $param) = ($text =~ /^(\w+)[= ]*(.*)$/);
  738.         $featref->{$feat} = $param;
  739.     }
  740.  
  741.     return 1;
  742. }
  743.  
  744. sub find_email_addr($)
  745. {
  746.     my $addr = shift;
  747.     if ($addr =~ /([A-Z0-9._%=#+-]+@(?:[A-Z0-9-]+\.)+[A-Z]+)\b/i) {
  748.         return $1;
  749.     }
  750.     return undef;
  751. }
  752.  
  753. sub guess_mime_type($)
  754. {
  755.     my $filename = shift;
  756.     if (defined($file_libmagic)) {
  757.         ## Use File::LibMagic if possible
  758.         return $file_libmagic->checktype_filename($filename);
  759.     } elsif (defined($file_type)) {
  760.         ## Use File::Type if possible
  761.         return $file_type->mime_type($filename);
  762.     } else {
  763.         ## Module File::LibMagic is not available
  764.         ## Still recognise some common extensions
  765.         return "image/jpeg" if ($filename =~ /\.jpe?g/i);
  766.         return "image/gif" if ($filename =~ /\.gif/i);
  767.         return "image/png" if ($filename =~ /\.png/i);
  768.         return "text/plain" if ($filename =~ /\.txt/i);
  769.         return "application/zip" if ($filename =~ /\.zip/i);
  770.         return "application/x-gzip" if ($filename =~ /\.t?gz/i);
  771.         return "application/x-bzip" if ($filename =~ /\.t?bz2?/i);
  772.     }
  773.     return "application/octet-stream";
  774. }
  775.  
  776. sub basename($)
  777. {
  778.     my $path = shift;
  779.     my @parts = split(/\//, $path);
  780.     return $parts[$#parts];
  781. }
  782.  
  783. sub prepare_attachment($)
  784. {
  785.     my $attachment = shift;
  786.     my ($path, $mime_type);
  787.  
  788.     if (-e $attachment) {
  789.         $path = $attachment;
  790.         $mime_type = guess_mime_type($attachment);
  791.     } elsif ($attachment =~ /(.*)@([^@]*)$/ and -e $1) {
  792.         $path = $1;
  793.         $mime_type = $2;
  794.     }
  795.     return ($path, $mime_type);
  796. }
  797.  
  798. sub attach_attachments($$@)
  799. {
  800.     my $message = shift;
  801.     my $disposition = shift;
  802.     my @attachments = @_;
  803.  
  804.     foreach my $attachment (@attachments) {
  805.         my ($path, $mime_type) = prepare_attachment($attachment);
  806.         if (not defined($path)) {
  807.             warn("$attachment: File not found. Ignoring.\n");
  808.             next;
  809.         }
  810.         $message->attach(
  811.             Type => $mime_type,
  812.             Path => $path,
  813.             Id   => basename($path),
  814.             Disposition => $disposition,
  815.         );
  816.     }
  817. }
  818.  
  819. sub safe_attach($$)
  820. {
  821.     my ($message, $part) = @_;
  822.     ## Remove some headers when $part is becoming a subpart of $message
  823.     $part->delete("Date");
  824.     $part->delete("X-Mailer");
  825.     $part->attr("MIME-Version" => undef);
  826.     $message->attach($part);
  827.     return $message;
  828. }
  829.  
  830. sub mime_message($$)
  831. {
  832.     my ($type, $data) = @_;
  833.  
  834.     ## Set QP encoding for text/* types, let MIME::Lite decide for all other types.
  835.     my $encoding = $type =~ /^text\// ? $text_encoding : undef;
  836.     my $message = MIME::Lite->new(
  837.         Type    => $type,
  838.         Encoding=> $encoding,
  839.         Data    => $data);
  840.     $message->attr('content-type.charset' => $charset) if (($type =~ /^text\//i) and defined($charset));
  841.     return $message;
  842. }
  843.  
  844. sub build_message
  845. {
  846.     my ($part_plain, $part_html, $part_body, $message);
  847.  
  848.     if (@attachments_inline) {
  849.         if (not defined($body_html)) {
  850.             die("Inline attachments (--attach-inline) must be used with --body-html\n");
  851.         }
  852.         $part_html = MIME::Lite->new(Type => 'multipart/related');
  853.         $part_html->attach(Type => 'text/html', Data => $body_html);
  854.         attach_attachments($part_html, "inline", @attachments_inline);
  855.         $message = $part_html;
  856.         # undefine $body_html to prevent confusion in the next if()
  857.         undef($body_html);
  858.     }
  859.  
  860.     if (defined($body_html)) {
  861.         $part_html = mime_message('text/html', $body_html);
  862.         $message = $part_html;
  863.     }
  864.  
  865.     if (defined($body_plain)) {
  866.         $part_plain = mime_message('text/plain', $body_plain);
  867.         $message = $part_plain;
  868.     }
  869.  
  870.     if (defined($part_plain) and defined($part_html)) {
  871.         $part_body = mime_message("multipart/alternative", undef);
  872.         safe_attach($part_body, $part_plain);
  873.         safe_attach($part_body, $part_html);
  874.         $message = $part_body;
  875.     }
  876.  
  877.     if (@attachments) {
  878.         if (defined($message)) {
  879.             # We already have some plaintext and/or html content built
  880.             # => make it the first part of multipart/mixed
  881.             my $message_body = $message;
  882.             $message = mime_message("multipart/mixed", undef);
  883.             safe_attach($message, $message_body);
  884.             attach_attachments($message, "attachment", @attachments);
  885.         } elsif ($#attachments == 0) {
  886.             # Only one single attachment - let it be the body
  887.             my ($path, $mime_type) = prepare_attachment($attachments[0]);
  888.             if (not defined($path)) {
  889.                 die($attachments[0].": File not found. No other message parts defined. Aborting.\n");
  890.             }
  891.             $message = MIME::Lite->new(
  892.                 Type => $mime_type,
  893.                 Path => $path);
  894.         } else {
  895.             # Message consisting only of attachments
  896.             $message = mime_message("multipart/mixed", undef);
  897.             attach_attachments($message, "attachment", @attachments);
  898.         }
  899.     }
  900.  
  901.     # Last resort - empty plaintext message
  902.     if (!defined($message)) {
  903.         $message = mime_message("TEXT", "");
  904.     }
  905.  
  906.     $message->replace("From" => $from);
  907.     $message->replace("To" => join(", ", @to));
  908.     $message->replace("Cc" => join(", ", @cc));
  909.     $message->replace("Subject" => $subject);
  910.     $message->replace("X-Mailer" => "smtp-cli $version, see http://smtp-cli.logix.cz");
  911.     $message->replace("Message-ID" => "<".time()."-".int(rand(999999))."\@smtp-cli>");
  912.  
  913.     for my $header (@add_headers) {
  914.         my ($hdr, $val) = ($header =~ /^([^:]+):\s*(.*)$/);
  915.         die("Not a valid header format: ${header}\n") if (not $hdr or not $val);
  916.         $message->add($hdr => $val);
  917.     }
  918.     for my $header (@replace_headers) {
  919.         my ($hdr, $val) = ($header =~ /^([^:]+):\s*(.*)$/);
  920.         die("Not a valid header format: ${header}\n") if (not $hdr or not $val);
  921.         $message->replace($hdr => $val);
  922.     }
  923.     for my $header (@remove_headers) {
  924.         my ($hdr) = ($header =~ /^([^:\s]+)/);
  925.         $message->replace($header => "");
  926.     }
  927.  
  928.     return $message;
  929. }
  930.  
  931. sub version ()
  932. {
  933.     print "smtp-cli version $version\n";
  934.     exit (0);
  935. }
  936.  
  937. sub usage ()
  938. {
  939.     printf (
  940. "Simple SMTP client written in Perl that supports advanced
  941. features like STARTTLS and SMTP-AUTH and IPv6. It can also
  942. create messages from components (files, text snippets) and
  943. attach files.
  944.  
  945. Version: smtp-cli v$version
  946.  
  947. Author: Michal Ludvig <mludvig\@logix.net.nz> (c) 2003-2017
  948.        http://smtp-cli.logix.cz
  949.  
  950. Usage: smtp-cli [--options]
  951.  
  952.        --server=<hostname>[:<port>]
  953.                                Host name or IP address of the SMTP server.
  954.                                May include the port after colon, alternatively
  955.                                use --port.
  956.        --port=<number>         Port where the SMTP server is listening.
  957.                                (default: 25)
  958.        -4 or --ipv4            Use standard IP (IPv4) protocol.
  959.        -6 or --ipv6            Use IPv6 protocol. For hosts that have
  960.                                both IPv6 and IPv4 addresses the IPv6
  961.                                connection is tried first.
  962.        --local-addr=<address>  Specify local address (by default the OS chooses)
  963.  
  964.        --hello-host=<string>   String to use in the EHLO/HELO command.
  965.        --disable-ehlo          Don't use ESMTP EHLO command, only HELO.
  966.        --force-ehlo            Use EHLO even if server doesn't say ESMTP.
  967.  
  968.        Transport encryption (TLS)
  969.        --disable-starttls      Don't use encryption even if the remote
  970.                                host offers it.
  971.        --ssl                   Start in SMTP/SSL mode (aka SSMTP).
  972.                                Default when --port=465
  973.        --disable-ssl           Don't start SSMTP even if --port=465
  974.        --ssl-ca-file=<filename>
  975.                                Verify the server's SSL certificate against
  976.                                a trusted CA root certificate file.
  977.        --ssl-ca-path=<dirname> Similar to --ssl-ca-file but will look for
  978.                                the appropriate root certificate file in
  979.                                the given directory. The certificates must
  980.                                must be stored one per file with hash-links
  981.                                generated by, for example, c_rehash script
  982.                                from OpenSSL.
  983.  
  984.        Authentication options (AUTH)
  985.        --user=<username>       Username for SMTP authentication.
  986.        --pass=<password>       Corresponding password.
  987.        --auth-login            Enable only AUTH LOGIN method.
  988.        --auth-plain            Enable only AUTH PLAIN method.
  989.        --auth-cram-md5         Enable only AUTH CRAM-MD5 method.
  990.        --auth                  Enable all supported methods. This is
  991.                                normally not needed, --user enables
  992.                                everything as well.
  993.  
  994.        Sender / recipient
  995.        --from=\"Display Name <add\@re.ss>\"
  996.                                Sender's name address (or address only).
  997.        --to=\"Display Name <add\@re.ss>\"
  998.        --cc=\"Display Name <add\@re.ss>\"
  999.        --bcc=\"Display Name <add\@re.ss>\"
  1000.                                Message recipients. Each parameter can be
  1001.                                used multiple times.
  1002.                                The --bcc addresses won't apprear in
  1003.                                the composed message.
  1004.  
  1005.        SMTP Envelope sender / recipient
  1006.        (rarely needed, use --from, --to, --cc and --bcc instead)
  1007.        --mail-from=<address>   Address to use in MAIL FROM command.
  1008.                                Use --from instead, unless you want
  1009.                                a different address in the envelope and
  1010.                                in the headers.
  1011.        --rcpt-to=<address>     Address to use in RCPT TO command. Can be
  1012.                                used multiple times. Normally not needed,
  1013.                                use --to, --cc and --bcc instead.
  1014.                                If set the --to, --cc and --bcc will only
  1015.                                be used for composing the message body and
  1016.                                not for delivering the messages.
  1017.  
  1018.        Send a complete RFC822-compliant email message:
  1019.        --data=<filename>       Name of file to send after DATA command.
  1020.                                With \"--data=-\" the script will read
  1021.                                standard input (useful e.g. for pipes).
  1022.  
  1023.        Alternatively build email a message from provided components:
  1024.        --subject=<subject>     Subject of the message
  1025.        --body-plain=<text|filename>
  1026.        --body-html=<text|filename>
  1027.                                Plaintext and/or HTML body of the message
  1028.                                If both are provided the message is sent
  1029.                                as multipart.
  1030.        --charset=<charset>     Character set used for Subject and Body,
  1031.                                for example UTF-8, ISO-8859-2, KOI8-R, etc.
  1032.        --text-encoding=<encoding>
  1033.                                Enforce Content-Transfer-Encoding for text
  1034.                                parts of the email, including body and
  1035.                                attachments. Must be one of:
  1036.                                ".join(", ", @valid_encodings)."
  1037.                                The default is: quoted-printable
  1038.        --attach=<filename>[\@<MIME/Type>]
  1039.                                Attach a given filename.
  1040.                                MIME-Type of the attachment is guessed
  1041.                                by default guessed but can optionally
  1042.                                be specified after '\@' delimiter.
  1043.                                For instance: --attach mail.log\@text/plain
  1044.                                Parameter can be used multiple times.
  1045.        --attach-inline=<filename>[\@<MIME/Type>]
  1046.                                Attach a given filename (typically a picture)
  1047.                                as a 'related' part to the above 'body-html'.
  1048.                                Refer to these pictures as <img src='cid:filename'>
  1049.                                in the 'body-html' contents.
  1050.                                See --attach for details about MIME-Type.
  1051.                                Can be used multiple times.
  1052.        --add-header=\"Header: value\"
  1053.        --replace-header=\"Header: value\"
  1054.        --remove-header=\"Header\"
  1055.                                Add, Replace or Remove pretty much any header
  1056.                                in the email. For example to set a different
  1057.                                Mailer use --replace-header=\"X-Mailer: Blah\",
  1058.                                to remove it altogether --remove-header=X-Mailer
  1059.                                or to add a completely custom header use
  1060.                                --add-header=\"X-Something: foo bar\".
  1061.        --print-only            Dump the composed MIME message to standard
  1062.                                output. This is useful mainly for debugging
  1063.                                or in the case you need to run the message
  1064.                                through some filter before sending.
  1065.  
  1066.        Other options
  1067.        --verbose[=<number>]    Be more verbose, print the SMTP session.
  1068.        --missing-modules-ok    Don't complain about missing optional modules.
  1069.        --version               Print: smtp-cli version $version
  1070.        --help                  Guess what is this option for ;-)
  1071.  
  1072. PayPal donations: http://smtp-cli.logix.cz/donate
  1073.                  Thanks in advance for your support!
  1074.  
  1075. ");
  1076.     exit (0);
  1077. }
RAW Paste Data