Mojo-SMTP-Client
view release on metacpan or search on metacpan
lib/Mojo/SMTP/Client.pm view on Meta::CPAN
my ($cmd, $arg) = splice @{ $self->{cmds} }, 0, 2;
unless ($cmd) {
# no more commands
if ($self->{stream}) {
$self->{stream}->timeout(0);
$self->{stream}->stop;
}
return $self->{finally};
}
if ( my $sub = $self->can("_cmd_$cmd") ) {
return (
$self->$sub($arg), sub {
my ($delay, $resp) = @_;
$delay->pass($resp);
$delay->steps( $self->_make_cmd_steps() );
}
);
}
croak 'unrecognized command: ', $cmd;
}
# EHLO/HELO
sub _cmd_hello {
my ($self, $arg) = @_;
weaken $self;
return (
sub {
my $delay = shift;
$self->_write_cmd('EHLO ' . $arg, CMD_EHLO);
$self->_read_response($delay->begin);
$self->{expected_code} = CMD_OK;
},
sub {
eval { $self->{resp_checker}->(@_); $_[1]->{checked} = 1 };
if (my $e = $@) {
die $e unless $e->isa('Mojo::SMTP::Client::Response');
my $delay = shift;
$self->_write_cmd('HELO ' . $arg, CMD_HELO);
$self->_read_response($delay->begin);
}
},
sub {
my ($delay, $resp) = @_;
return $delay->pass($resp) if delete $resp->{checked};
$self->{resp_checker}->($delay, $resp);
}
);
}
# STARTTLS
sub _cmd_starttls {
my ($self, $arg) = @_;
weaken $self;
require IO::Socket::SSL and IO::Socket::SSL->VERSION(0.98);
return (
sub {
my $delay = shift;
$self->_write_cmd('STARTTLS', CMD_STARTTLS);
$self->_read_response($delay->begin);
$self->{expected_code} = CMD_OK;
},
$self->{resp_checker},
sub {
my ($delay, $resp) = @_;
$self->{stream}->stop;
$self->{stream}->timeout(0);
my ($tls_cb, $tid, $loop, $sock);
my $error_handler = sub {
$loop->remove($tid);
$loop->reactor->remove($sock);
$sock = undef;
$tls_cb->($delay, undef, @_>=2 ? $_[1] : 'Inactivity timeout');
$tls_cb = $delay = undef;
};
$sock = IO::Socket::SSL->start_SSL(
$self->{stream}->steal_handle,
SSL_ca_file => $self->tls_ca,
SSL_cert_file => $self->tls_cert,
SSL_key_file => $self->tls_key,
SSL_verify_mode => $self->tls_verify,
SSL_verifycn_name => $self->address,
SSL_verifycn_scheme => $self->tls_ca ? 'smtp' : undef,
SSL_startHandshake => 0,
SSL_error_trap => $error_handler
)
or return $delay->pass(0, $IO::Socket::SSL::SSL_ERROR);
$tls_cb = $delay->begin;
$loop = $self->_ioloop;
$tid = $loop->timer($self->inactivity_timeout => $error_handler);
$loop->reactor->io($sock => sub {
if ($sock->connect_SSL) {
$loop->remove($tid);
$loop->reactor->remove($sock);
$self->_make_stream($sock, $loop);
$self->{starttls} = 1;
$sock = $loop = undef;
$tls_cb->($delay, $resp);
$tls_cb = $delay = undef;
return;
}
return $loop->reactor->watch($sock, 1, 0)
if $IO::Socket::SSL::SSL_ERROR == IO::Socket::SSL::SSL_WANT_READ();
return $loop->reactor->watch($sock, 0, 1)
if $IO::Socket::SSL::SSL_ERROR == IO::Socket::SSL::SSL_WANT_WRITE();
})->watch($sock, 0, 1);
},
sub {
my ($delay, $resp, $error) = @_;
unless ($resp) {
$self->_rm_stream();
Mojo::SMTP::Client::Exception::Stream->throw($error);
}
$delay->pass($resp);
}
);
}
# AUTH
sub _cmd_auth {
my ($self, $arg) = @_;
weaken $self;
my $type = lc($arg->{type} // 'plain');
my $set_auth_ok = sub {
my ($delay, $resp) = @_;
$self->{authorized} = 1;
$delay->pass($resp);
};
if ($type eq 'plain') {
return (
sub {
my $delay = shift;
$self->_write_cmd('AUTH PLAIN '.b64_encode(join("\0", '', $arg->{login}, $arg->{password}), ''), CMD_AUTH);
$self->_read_response($delay->begin);
$self->{expected_code} = CMD_OK;
},
$self->{resp_checker},
$set_auth_ok
);
}
if ($type eq 'login') {
return (
# start auth
sub {
my $delay = shift;
$self->_write_cmd('AUTH LOGIN', CMD_AUTH);
$self->_read_response($delay->begin);
$self->{expected_code} = CMD_MORE;
},
$self->{resp_checker},
# send username
sub {
my $delay = shift;
$self->_write_cmd(b64_encode($arg->{login}, ''), CMD_AUTH);
$self->_read_response($delay->begin);
$self->{expected_code} = CMD_MORE;
},
$self->{resp_checker},
# send password
lib/Mojo/SMTP/Client.pm view on Meta::CPAN
my ($smtp, $resp) = @_;
warn $resp->error ? 'Failed to send: '.$resp->error : 'Sent successfully';
Mojo::IOLoop->stop;
}
);
Mojo::IOLoop->start;
=back
=head1 DESCRIPTION
With C<Mojo::SMTP::Client> you can easily send emails from your Mojolicious application without
blocking of C<Mojo::IOLoop>.
=head1 EVENTS
C<Mojo::SMTP::Client> inherits all events from L<Mojo::EventEmitter> and can emit the following new ones
=head2 start
$smtp->on(start => sub {
my ($smtp) = @_;
# some servers delays first response to prevent SPAM
$smtp->inactivity_timeout(5*60);
});
Emitted whenever a new connection is about to start. You can interrupt sending by dying or throwing an exception
from this callback, C<error> attribute of the response will contain corresponding error.
=head2 response
$smtp->on(response => sub {
my ($smtp, $cmd, $resp) = @_;
if ($cmd == Mojo::SMTP::Client::CMD_CONNECT) {
# and after first response others should be fast enough
$smtp->inactivity_timeout(10);
}
});
Emitted for each SMTP response from the server. C<$cmd> is a command L<constant|/CONSTANTS> for which this
response was sent. C<$resp> is L<Mojo::SMTP::Client::Response> object. You can interrupt sending by dying or
throwing an exception from this callback, C<error> attribute of the response will contain corresponding error.
=head1 ATTRIBUTES
C<Mojo::SMTP::Client> implements the following attributes, which you can set in the constructor or get/set later
with object method call
=head2 address
Address of SMTP server (ip or domain name). Default is C<localhost>
=head2 port
Port of SMTP server. Default is C<25> for plain connection and C<465> if TLS is enabled.
=head2 tls
Enable TLS. Should be true if SMTP server expects encrypted connection. Default is false.
Proper version of L<IO::Socket::SSL> should be installed for TLS support in L<Mojo::IOLoop::Client>,
which you can find with C<mojo version> command.
=head2 tls_ca
Path to TLS certificate authority file. Also activates hostname verification.
=head2 tls_cert
Path to the TLS certificate file.
=head2 tls_key
Path to the TLS key file.
=head2 tls_verify
TLS verification mode. Use C<0> to disable verification, which turned on by default.
=head2 hello
SMTP requires that you identify yourself. This option specifies a string to pass as your mail domain.
Default is C<localhost.localdomain>
=head2 connect_timeout
Maximum amount of time in seconds establishing a connection may take before getting canceled,
defaults to the value of the C<MOJO_CONNECT_TIMEOUT> environment variable or C<10>
=head2 inactivity_timeout
Maximum amount of time in seconds a connection can be inactive before getting closed,
defaults to the value of the C<MOJO_INACTIVITY_TIMEOUT> environment variable or C<20>.
Setting the value to C<0> will allow connections to be inactive indefinitely
=head2 ioloop
Event loop object to use for blocking I/O operations, defaults to a L<Mojo::IOLoop> object
=head2 autodie
Defines should or not C<Mojo::SMTP::Client> throw exceptions for any type of errors. This only usable for
blocking usage of C<Mojo::SMTP::Client>, because non-blocking one should never die. Throwed
exception will be one of the specified in L<Mojo::SMTP::Client::Exception>. When autodie attribute
has false value you should check C<$respE<gt>error> yourself. Default is false.
=head1 METHODS
C<Mojo::SMTP::Client> inherits all methods from L<Mojo::EventEmitter> and implements the following new ones
=head2 send
$smtp->send(
from => $mail_from,
to => $rcpt_to,
data => $data,
quit => 1,
$nonblocking ? $cb : ()
);
Send specified commands to SMTP server. Arguments should be C<key =E<gt> value> pairs where C<key> is a command
and C<value> is a value for this command. C<send> understands the following commands:
=over
=item hello
Send greeting to the server. Argument to this command should contain your domain name. Keep in mind, that
C<Mojo::SMTP::Client> will automatically send greeting to the server right after connection if you not specified
C<hello> as first command for C<send>. C<Mojo::SMTP::Client> first tries C<EHLO> command for greeting and if
server doesn't accept it C<Mojo::SMTP::Client> retries with C<HELO> command.
$smtp->send(hello => 'mymail.me');
=item starttls
Upgrades connection from plain to encrypted. Some servers requires this before sending any other commands.
L<IO::Socket::SSL> 0.98+ should be installed for this to work. See also L</tls_ca>, L</tls_cert>, L</tls_key>
attributes
$smtp->tls_ca('/etc/ssl/certs/ca-certificates.crt');
$smtp->send(starttls => 1);
=item auth
Authorize on SMTP server. Argument to this command should be a reference to a hash with C<type>,
C<login> and C<password> keys. Only PLAIN and LOGIN authorization are supported as C<type> for now.
You should authorize only once per session.
$smtp->send(auth => {login => 'oleg', password => 'qwerty'}); # defaults to AUTH PLAIN
$smtp->send(auth => {login => 'oleg', password => 'qwerty', type => 'login'}); # AUTH LOGIN
=item from
From which email this message was sent. Value for this cammand should be a string with email
$smtp->send(from => 'root@cpan.org');
=item to
To which email(s) this message should be sent. Value for this cammand should be a string with email
or reference to array with email strings (for more than one recipient)
$smtp->send(to => 'oleg@cpan.org');
$smtp->send(to => ['oleg@cpan.org', 'do_not_reply@cpantesters.org']);
=item reset
After this command server should forget about any started mail transaction and reset it status as it was after response to C<EHLO>/C<HELO>.
Note: transaction considered started after C<MAIL FROM> (C<from>) command.
$smtp->send(reset => 1);
=item data
Email body to be sent. Value for this command should be a string (or reference to a string) with email body or reference to subroutine
each call of which should return some chunk of the email as string (or reference to a string) and empty string (or reference to empty string)
at the end (useful to send big emails in memory-efficient way)
$smtp->send(data => "Subject: This is my first message\r\n\r\nSent from Mojolicious app");
$smtp->send(data => sub { sysread(DATA, my $buf, 1024); $buf });
=item quit
Send C<QUIT> command to SMTP server which will close the connection. So for the next use of this server connection will be
reestablished. If you want to send several emails with this server it will be more efficient to not quit
the connection until last email will be sent.
=back
For non-blocking usage last argument to C<send> should be reference to subroutine which will be called when result will
be available. Subroutine arguments will be C<($smtp, $resp)>. Where C<$resp> is object of L<Mojo::SMTP::Client::Response> class.
First you should check C<$resp-E<gt>error> - if it has true value this means that it was error somewhere while sending.
If C<error> has false value you can get code and message for response to last command with C<$resp-E<gt>code> (number) and
C<$resp-E<gt>message> (string).
For blocking usage C<$resp> will be returned as result of C<$smtp-E<gt>send> call. C<$resp> is the same as for
non-blocking result. If L</autodie> attribute has true value C<send> will throw an exception on any error.
( run in 1.910 second using v1.01-cache-2.11-cpan-39bf76dae61 )