From f3bba3ce8924356cc6a1d18011dded9afcda333e Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Wed, 18 May 2022 03:39:48 +0200 Subject: [PATCH] Adding a multipart/alternative example. --- Build.PL | 18 +++++ bin/mail_example | 56 ++++++++++++++++ lib/Mail/Example.pm | 157 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+) create mode 100644 Build.PL create mode 100644 bin/mail_example create mode 100644 lib/Mail/Example.pm diff --git a/Build.PL b/Build.PL new file mode 100644 index 0000000..0eb0438 --- /dev/null +++ b/Build.PL @@ -0,0 +1,18 @@ +use Module::Build; + +my $home = $ENV{HOME}; + +my $build = Module::Build->new( + module_name => 'Mail::Example', + license => 'MIT', + dist_author => 'Sergio Iglesias ', + dist_abstract => 'A multipart alternative example.', + requires => { + 'Email::MIME' => 0, + 'Email::Sender' => 0, + 'Types::Standard' => 0, + 'Params::ValidationCompiler' => 0, + 'Getopt::Long::Descriptive' => 0, + }, +); +$build->create_build_script; diff --git a/bin/mail_example b/bin/mail_example new file mode 100644 index 0000000..557cd1b --- /dev/null +++ b/bin/mail_example @@ -0,0 +1,56 @@ +#!/usr/bin/env perl +use v5.30.0; + +use strict; +use warnings; + +use Getopt::Long::Descriptive; +use Path::Tiny; + +use Mail::Example; + +sub get_file; + +my ( $opt, $usage ) = describe_options( + 'mail_example %o', + [ + 'user|u=s', 'The user to use while sending the mail.', { required => 1 } + ], + [ 'port|p=i', 'The port to use for smtp server.', { required => 1 } ], + [ 'host|H=s', 'The host to use for smtp server.', { required => 1 } ], + [ + 'password|P=s', + 'The password to use for smtp server.', + { required => 1 } + ], + [ 'subject|s=s', 'The subject of the message.', { required => 1 } ], + [ 'to|t=s', 'The address of the receiver.', { required => 1 } ], + [ 'text_body_file|b=s', 'The text body of the message.', { required => 1 } ], + [ 'html_body_file|B=s', 'The html body of the message.', { required => 1 } ], + [ 'help|h', 'Print this help', { shortcircuit => 1 } ], +); + +print( $usage->text ), exit if $opt->help; + +my $html = get_file $opt->html_body_file; +my $text = get_file $opt->text_body_file; + +my $mailer = Mail::Example->new( + config => { + sasl_username => $opt->user, + sasl_password => $opt->password, + smtp_host => $opt->host, + smtp_port => $opt->port, + } +); +$mailer->sendmail( + text => $text, + html => $html, + to => $opt->to, + subject => $opt->subject +); + +sub get_file { + my $file = shift; + return path($file)->slurp_utf8; +} diff --git a/lib/Mail/Example.pm b/lib/Mail/Example.pm new file mode 100644 index 0000000..6d3d817 --- /dev/null +++ b/lib/Mail/Example.pm @@ -0,0 +1,157 @@ +package Mail::Example; +use v5.30.0; + +use strict; +use warnings; + +use Encode qw/decode/; + +use Params::ValidationCompiler qw/validation_for/; +use Types::Standard qw/Str HashRef/; + +use Email::MIME; +use Email::Sender::Simple; +use Email::Sender::Transport::SMTP; + +{ + my $validator = validation_for( + params => { + config => { type => HashRef }, + } + ); + + sub new { + my $class = shift; + my %params = $validator->(@_); + my ($config) = @params{qw/config/}; + return bless { config => $config }, $class; + } +} + +sub _config { + my $self = shift; + return $self->{config}; +} + +{ + my $validator = validation_for( + params => { + text => { type => Str }, + html => { type => Str }, + to => { type => Str }, + subject => { type => Str }, + } + ); + + sub sendmail { + my $self = shift; + my %params = $validator->(@_); + my ( $text, $html, $to, $subject ) = @params{qw/text html to subject/}; + my @parts = ( + Email::MIME->create( + attributes => { + content_type => 'multipart/alternative', + encoding => 'base64', + }, + parts => [ + Email::MIME->create( + attributes => { + charset => 'UTF-8', + content_type => 'text/plain', + encoding => 'base64', + disposition => 'inline', + }, + body_str => decode( 'utf-8', $text ), + ), + Email::MIME->create( + attributes => { + charset => 'UTF-8', + content_type => 'text/html', + encoding => 'base64', + disposition => 'inline', + }, + body_str => decode( 'utf-8', $html ), + ) + ] + ) + ); + my $config = $self->_config; + my $email = Email::MIME->create( + header_str => [ + From => $config->{sasl_username}, + To => $to, + Subject => $subject, + ], + attributes => { + encoding => 'base64', + content_type => 'multipart/mixed' + }, + parts => [@parts], + ); + say Email::Sender::Simple::send( 'Email::Sender::Simple', $email, + { transport => $self->_generate_transport } ); + } +} + +sub _generate_transport { + my $self = shift; + my $config = $self->_config; + my $transport = Email::Sender::Transport::SMTP->new( + hosts => [ $config->{smtp_host} ], + ssl => 1, + port => $config->{smtp_port}, + sasl_username => $config->{sasl_username}, + sasl_password => $config->{sasl_password}, + ); + return $transport; +} +1; + +=encoding utf8 + +=head1 NAME + +Mail::Example - The mail sender module. + +=head1 SYNOPSIS + + my $mailer = Mail::Example->new; + + $mailer->sendmail( + to => 'larry@perl,org', + text => 'hola', + html => 'hola', + subject => 'Patch', + ); + +=head1 DESCRIPTION + +Mail::Example reads the config to determine the credentials +to send mail and does an abstraction around those. + +=head1 INSTANCE METHODS + +Mail::Example implements the following instance methods: + +=head2 new + + my $mailer = Mail::Example->new( config => $config); + +Instances a new mailer. + +=head1 METHODS + +Mail::Example implements the following methods: + +=head2 sendmail + + $mailer->sendmail( + to => 'larry@perl,org', + text => 'hola', + html => 'hola', + subject => 'Patch', + ); + +Sends a mail to the given mail address. + +=cut