http://www.perlmonks.org?node_id=110334
Category: E-Mail Programs
Author/Contact Info Alex Pleiner <alex@zeitform.de>, zeitform Internet Dienste
Description: Net::SMTP_auth is a small extension to G. Barr's Net::SMTP to authenticate to an SMTP server using one of the AUTH methods PLAIN, LOGIN or CRAM-MD5 (see RFC2554 for details). This module can be expanded and is a very first implementation. (Updated, yes there was a typo)
# Net::SMTP_auth.pm
#
# alex pleiner 2001, zeitform Internet Dienste
# thanks to Graham Barr <gbarr@pobox.com> for Net::SMTP
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.


package Net::SMTP_auth;

require 5.001;

use strict;
use vars qw($VERSION @ISA);
use Socket 1.3;
use Carp;
use IO::Socket;
use Net::Cmd;
use Net::Config;
use Net::SMTP;
use MIME::Base64;
use Digest::HMAC_MD5 qw(hmac_md5_hex);

$VERSION = "0.03";

@ISA = qw(Net::SMTP);

# all other method taken from Net::SMTP

sub auth_types {
  my $me = shift;

  if (exists ${*$me}{'net_smtp_esmtp'}) {

    my $esmtp = ${*$me}{'net_smtp_esmtp'};

    if(exists $esmtp->{AUTH}) {
      return $esmtp->{AUTH};
    }
  }

  return;
}



sub auth {
  my $me = shift;
  my $auth_type = shift ||
       carp 'Net::SMTP_auth: missing argument "auth_type" to method "a
+uth"';
  my $user = shift;
  my $pass = shift;

  ## go for auth login
  if (uc($auth_type) eq "LOGIN") {
    $me->_AUTH("LOGIN");
    if ( $me->code() == 334 ) {
      my $encoded_user = encode_base64($user); chomp $encoded_user;
      $me->command($encoded_user)->response();
      if ( $me->code() == 334 ) {
        my $encoded_pass = encode_base64($pass); chomp $encoded_pass;
        $me->command($encoded_pass)->response(); 
        if ( $me->code() == 235 ) {
         return 1;
        }
      }
    }

    return;

  ## go for auth cram-md5
  } elsif (uc($auth_type) eq "CRAM-MD5") { 
    $me->_AUTH("CRAM-MD5");
    if ( $me->code() == 334 ) {
      my $stamp = $me->message;
      my $hmac = hmac_md5_hex(decode_base64($stamp), $pass);
      my $answer = encode_base64($user . " " . $hmac); $answer =~ s/\n
+//g;
      $me->command($answer)->response();
      if ( $me->code() == 235 ) {
        return 1;
      }
    }

    return;

  ## go for auth plain
  } elsif (uc($auth_type) eq "PLAIN") {
    $me->_AUTH("PLAIN");
    if ( $me->code() == 334 ) {
      my $string = encode_base64("\000$user\000$pass"); $string =~ s/\
+n//g;
      $me->command($string)->response();
      if ( $me->code() == 235 ) {
        return 1;
      }
    }

    return;

  ## other auth methods not supported
  } else {
    carp "Net::SMTP_auth: authentication type \"$auth_type\" not suppo
+rted";
    return;
  }

}


sub _AUTH { shift->command("AUTH", @_)->response()  == CMD_OK } 

1;


__END__

=head1 NAME

Net::SMTP_auth - Simple Mail Transfer Protocol Client with AUTHenticat
+ion

=head1 SYNOPSIS

    use Net::SMTP_auth;

    # Constructors
    $smtp = Net::SMTP_auth->new('mailhost');
    $smtp = Net::SMTP_auth->new('mailhost', Timeout => 60);

=head1 DESCRIPTION

This module implements a client interface to the SMTP and ESMTP
protocol AUTH service extension, enabling a perl5 application to talk 
to and authenticate against SMTP servers. This documentation assumes 
that you are familiar with the concepts of the SMTP protocol described
+ 
in RFC821 and with the AUTH service extension described in RFC2554.

A new Net::SMTP_auth object must be created with the I<new> method. On
+ce
this has been done, all SMTP commands are accessed through this object
+.

The Net::SMTP_auth class is a subclass of Net::SMTP, which itself is
a subclass of Net::Cmd and IO::Socket::INET.

=head1 EXAMPLES

This example authenticates via CRAM-MD5 and sends a small message to 
the postmaster at the SMTP server known as mailhost:

    #!/usr/bin/perl -w

    use Net::SMTP_auth;

    $smtp = Net::SMTP_auth->new('mailhost');
    $smtp->auth('CRAM-MD5', 'user', 'password');

    $smtp->mail($ENV{USER});
    $smtp->to('postmaster');

    $smtp->data();
    $smtp->datasend("To: postmaster\n");
    $smtp->datasend("\n");
    $smtp->datasend("A simple test message\n");
    $smtp->dataend();

    $smtp->quit;

=head1 CONSTRUCTOR

=over 4

=item new Net::SMTP_auth [ HOST, ] [ OPTIONS ]

This is the constructor for a new Net::SMTP_auth object. It is
taken from Net::SMTP as all other methods (except I<auth> and 
I<auth_types>) are, too.

=head1 METHODS

Unless otherwise stated all methods return either a I<true> or I<false
+>
value, with I<true> meaning that the operation was a success. When a m
+ethod
states that it returns a value, failure will be returned as I<undef> o
+r an
empty list.

=over 4

=item auth_types ()

Returns the AUTH methods supported by the server in a space separated
string. This string is exacly the line given by the SMTP server after
the C<EHLO> command containing the keyword C<AUTH>.

=item auth ( AUTH, USER, PASSWORD )

Authenticates the user C<USER> via the authentication method C<AUTH>
and the password C<PASSWORD>. Returns I<true> if successful and I<fals
+e>
if the authentication failed. Remember that the connection is not clos
+ed
if the authentication fails. You may issue a different authentication 
attempt. If you once are successfully authenticated, you cannot send
the C<AUTH> command again.

=back

=head1 SEE ALSO

L<Net::SMTP> and L<Net::Cmd>

=head1 AUTHOR

Alex Pleiner <alex@zeitform.de>, zeitform Internet Dienste.
Thanks to Graham Barr <gbarr@pobox.com> for Net::SMTP.

=head1 COPYRIGHT

Copyright (c) 2001 zeitform Internet Dienste. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
Replies are listed 'Best First'.
Re: Net::SMTP_auth
by chromatic (Archbishop) on Sep 10, 2001 at 07:10 UTC
    A few things confuse me. Is there a better way to do auth_types() without the symbolic reference?

    Also, you return a lot of failures. Why not leave off some else blocks and just have a return; at the end of each function? (If you want to agree with the documentation, don't return undef explicitly. It creates a one-item list in list context, not a blank list.)

    Do you really need a regex to compare $auth_type?

    Why are you using a string comparison for numbers in auth()? Further, why not call $me->code() once, storing it in a temporary variable?

    Otherwise, things look solid. (Your _AUTH() is evidence that you're a good programmer.)

      Thanks for reply, I'll take your advices into my revision. But the _auth() is JUST evidence that I read G. Barr's code very closely, I did not do this myself. The flowers belong to him.
Re: Net::SMTP_auth
by Anonymous Monk on Jan 02, 2002 at 07:51 UTC
    Thank you very much. VERY, VERY useful.
    I only think that there is a typo in the code example:
    It is written: $smtp = Net::SMTP->new_auth('mailhost');
    I think it should be: $smtp = Net::SMTP_auth->new('mailhost');

    Thanks, I really needed exactly this module :)
      Hiya, This is so cool - happy I was when I found just the thing I needed to use Net:SMTP with a SMTP that needs auth. Unfortunately - :-( - I 'm not able to install this Net::SMTP_auth package .. ?? Can plz somebody be of any assistance on how I should persue (or better said "start") this issue ? thx,
        I could use some help too! Running on an NT server, maybe I didn't install the module correctly? Can someone help a newbie?
      I was trying to use created by you Net::SMTP_auth package with the following code:
      #!perl -w use Net::SMTP; use Net::SMTP_auth; my $serverName = "smtp.ym.phub.net.cable.rogers.com"; $smtp = Net::SMTP_auth->new($serverName, Timeout => 30, Debug =>1); $smtp->auth('levalt@rogers.com', '123456'); my $mailFrom = "levalt\@rogers.com"; my $mailTo = "levalt\@rogers.com"; $smtp->mail( $mailFrom ); $smtp->to( $mailTo ); $smtp->data(); $smtp->datasend("Subject: Updates To My Home Page\n"); $smtp->datasend("\n"); $smtp->datasend("Here are all the cool new links...\n\n"); $smtp->dataend(); $smtp->quit();
      I get an error: Unsupported authentication mechanism.

      Could you please give me a clue how to solve this problem?
      Please email me: levalt@rogers.com

      edit: jeffa - code tags
        I too am with rogers and used the following to get mine to work:
        $smtp-auth('LOGIN', 'username@rogers.com', 'password');
        
        you may be able to substitute LOGIN with PLAIN but I have not tried. Give it a whirl.
Re: Net::SMTP_auth
by Anonymous Monk on Jun 12, 2003 at 15:05 UTC
    YoudaMan. I used this Mod on another server, found it several places on the net at that time. HD crashed yesterday, now this is the only place I could locate your code. Thank God I did. Crisp, excellent module . . . very needed, very simple to install and use.
      Thank for all your posts -- I can send email using Net::SMTP_auth, but the received email have no sender email address info. Any suggestion? Thanks, Keith
Re: Net::SMTP_auth
by watcher@LBK (Initiate) on Nov 06, 2005 at 12:39 UTC
    I tried it with the following code (look behind the output), but I keep getting following output:
    C:\Documents and Settings\Isabelle\Bureaublad\bernard>perl mailstuurde +r.pl Net::SMTP_auth>>> Net::SMTP_auth(0.07) Net::SMTP_auth>>> Net::SMTP(2.29) Net::SMTP_auth>>> Net::Cmd(2.26) Net::SMTP_auth>>> Exporter(5.58) Net::SMTP_auth>>> IO::Socket::INET(1.27) Net::SMTP_auth>>> IO::Socket(1.28) Net::SMTP_auth>>> IO::Handle(1.24) Net::SMTP_auth: Unexpected EOF on command channel at mailstuurder.pl l +ine 8 Can't call method "auth" on an undefined value at mailstuurder.pl line + 9.
    And here is the code I use:
    #!/usr/bin/perl use warnings; use strict; use Net::SMTP_auth; use Net::SMTP; my $smtp = Net::SMTP_auth->new('smtp.gmail.com', Debug => 1); $smtp->auth('LOGIN', 'b.siebens@gmail.com', '********'); $smtp->mail('watcher@landbouwkring.be'); $smtp->to('b.siebens@gmail.com'); $smtp->data(); $smtp->datasend("To: b.siebens\@gmail.com"); $smtp->datasend("From: watcher\@landbouwkring.be"); $smtp->datasend("\n"); $smtp->datasend("A simple test message.\n"); $smtp->dataend(); $smtp->quit();
    Anybody knows how I can fix this error? I've tried all from Mail::Mailer to Mail::Sendmail (and some other things), but this seems to be best suited to solve my problems, so getting this to work would be quite nice.
      Hi Everyone, I need help too. My code use to work with Verizon (changed providers) but it does not work when I use my own domain. The settings work with my website on the same computer using php so I know the domain and password are valid. This is my error message:
      Can't call method "auth" on an undefined value at send_mail.pm line 31.

      Can someone please help?

      Small perl test script below

      #!/usr/bin/perl require "send_mail.pm"; $title = "my title"; $desc = 'my desc'; $link = 'http://www.google.com'; $epoch = 1287629117; $file_name = 'file.txt'; $input[2] = 'kevin@mydomain.net'; &send_mail::send_email ($title, $desc, $link, $epoch, $file_name, \$$i +nput[2]);
      Function below
      package send_mail; sub send_email (){ my ($title, $desc, $link, $epoch, $name, $emails) = @_; use POSIX qw(strftime); my $est = strftime "%a, %B %d, %Y, %X %z", localtime($epoch); my $smtp = Net::SMTP->new('mail.mydomain.net', LocalAddr => 2626, Timeout => 10, Debug => 1 ); $smtp->auth( 'kevin@mydomain.net', 'password'); $smtp->mail('kevin@mydomain.net'); # use the sender's adress here my @em = split (',',$$emails); foreach (@em){ $smtp->to($_); # recipient's address } $smtp->data(); # Start the mail $smtp->datasend("Subject: " . $title); $smtp->datasend("\n" . $desc . "\n" . $link . "\nListing time: " . $ +est ); $smtp->datasend("\n\n bunch of text here " . $name); $smtp->dataend(); # Finish sending the mail $smtp->quit; # Close the SMTP connection }