2016年11月9日水曜日

FreeBSD で,Perl で gmail からメールを送る:Net::SMTP::TLS をやめて,Net::SMTP,IO::Socket::SSL,Authen::SASL を使うことにした

 以前 FreeBSD で Net::SMTP::TLS を使うには (2013/11 現在) というブログを書いた。そこでは,FreeBSD 上で(別に Unix 系ならどこでもいいのだが…)Perl のスクリプトを使って,Gmail 経由でメールを送ることに関して記載した。それまでにも幾つかブログ上で書いていたので,その続きだったのだが,特に Perl モジュールの Net::SMTP::TLS を使ったメールの送信に関することを書いた。 しかし,この Net::SMTP::TLS というモジュールは,2006年1月以降全く更新されていない。その間に,このモジュールが参照している別のモジュールが更新され,その更新に対応していないため不具合が生じていた。 前回のブログでは,その対策について書いたのだが,いくらなんでもそろそろ Net::SMTP::TLS は使うのをやめようかな,と考え始めた。
 そこで,Net::SMTP,IO::Socket::SSL,Authen::SASL を使って Gmail からメールを送るやり方について書こう,というのが今回のお話しである。

 まず,最初に,いつものようにここに書いてある内容は,他の方のブログなどを参考にしている。 つまり私のオリジナルではない。しかし,幾つかのサイトの内容をミックスしているので,全くのコピーでもないのだが…。 今回参考にしたのは,Satoshi Shida さんの「つれづれニッキ」の中の PerlでSMTP over TLSはNet::SMTPだけでOKになっていたである。 他にも,複数の宛て先がある時の処理については,chaichanPaPa さんの「燈明日記」の中の SMTPモジュールでの複数アドレスメールToの罠を参考にした。

 さて,肝心の中身だが,以下にスクリプトを載せているので,それを見ながら解説してみよう。
#!/usr/bin/perl
use strict;
use Jcode;
use Net::SMTP; # SMTP接続後、starttls()を使う
use IO::Socket::SSL; # SSLとあるけれどもTLSしか使わない
use Authen::SASL; # SASLはSMTPに認証手順

my $mailhost = 'smtp.gmail.com';
my $mailport = 587;
my $mail_username = 'xxxxx@gmail.com';
my $mail_password = 'PASSWORD';
my $from_mail = 'xxxxx@gmail.com';

my $to_mail = 'first@address.to.send, second@address.to.send';
my $cc_mail = 'carbon_copy@address.to.send';
#
my @mailto = split(',', $to_mail);
my @mailcc = split(',', $cc_mail);
#
my $subject = 'Mail Subject';
my $message = 'This is a test mail'."\n".'from XXX';

my $smtp = Net::SMTP->new(
        $mailhost,
        Hello   =>      'mydomain',
        Port    =>      $mailport
#        Debug   =>      1
);
die "Error" unless $smtp;

my $r = $smtp->starttls(
  SSL_version=>"TLSv12",
  SSL_verify_mode=>SSL_VERIFY_PEER,
  SSL_ocsp_mode=>SSL_OCSP_TRY_STAPLE,
#  SSL_ca_path=>'etc/ssl/certs',
#  SSL_ca_file=>'etc/ssl/certs/ca-bundle.crt',
  SSL_verifycn_name => $mailhost, # defaults to PeerHost
  SSL_verifycn_schema => 'smtp',
  SSL_hostname => $mailhost, # SNI support
#  SSL_cipher_list =&g; 'PROFILE=SYSTEM',
);
die "Error" unless $r;
$r = $smtp->auth($mail_username, $mail_password);
die "Error" unless $r;

$smtp->mail($from_mail);
$smtp->to(@mailto);
if ($cc_mail ne '') {
    $smtp->cc(@mailcc);
}
#
my $header;
$header = "From: ".$from_mail."\n";
$header .= "To: ".$to_mail."\n";
if ($cc_mail ne '') {
    $header .= "Cc: ".$cc_mail."\n";
}
$header .= "Subject: ".jcode($subject)->mime_encode . "\n";
$header .= "MIME-Version: 1.0\n";
$header .= "Content-type: text/plain; charset=ISO-2022-JP\n";
$header .= "Content-Transfer-Encoding: 7bit\n\n";
#
$smtp->data();
$smtp->datasend($header);
$smtp->datasend($message);
$smtp->dataend();
$smtp->quit;
 スクリプトを見ると,5個のモジュールを使っている。 最初の strict はモジュールというよりは,文法をあまり省略しない,って感じのやつだったと思う。 2個目の Jcode は日本語の文字コードの変換用のものであり,これも確か別のやつを使えってなってたような気もするなぁ…。 残りの3個が,今回のメール送信に必要なモジュールであり,それぞれ,Net::SMTP,IO::Socket::SSL,Authen::SASLである。 やり方としては,Net::SMTP で送信のための変数を作り(もうちょっといい言い方があるはず…),その後,TLS (starttls) で認証してもらってから,Net::SMTP でメールを送る,という感じみたい。この,TLS での認証に IO::Socket::SSL と Authen::SASL が必要(らしい)。

 スクリプトでは,初めの方に
  ・メールを実際に送ってくれるサーバー(SMTPサーバー)
  ・そのサーバーにアクセスするポート番号(587番)
  ・Gmail でのユーザー名(@gmail.com を含む)
  ・Gmail のログインパスワード
  ・送信元アドレス ($from_mail)
  ・宛て先アドレス ($to_mail)
  ・Cc: の宛て先 ($cc_mail)
を指定している。 当然,上記の例ではこれらのアドレスは「架空」のものである。

 その後,「@mailto」と「@mailcc」とうい配列を作っている。 これは,宛て先のアドレスと Cc: の宛て先が複数あることを前提にしたもので,Net::SMTP を使う際に必要となる。 宛て先が1個だけの場合には配列にしなくてもうまく働く。

 「my $smtp = Net::SMTP->new(」で,送信用の変数を用意している。 その中で送信用の SMTP サーバーや,ポート番号を指定している。 また,エラーがあった場合には止まるようになっている。

 その次の「my $r = $smtp->starttls(」の所で TLS で認証を受けている。 ここでは,TLSのv1.2を指定したりしているが,実は細かいオプションしてはわかっていない。 ただ,コメントアウトしないとうまく働かなかったものがあったので,それはコメントとして表記しておいた。 そして,「$r = $smtp->auth($mail_username, $mail_password);」の部分で Gmail の認証を受けている。

 認証を受けることができれば,いよいよメールの送信となる。
$smtp->mail($from_mail);
$smtp->to(@mailto);
if ($cc_mail ne '') {
    $smtp->cc(@mailcc);
}
の部分で,メールのヘッダーパートを指定している。もし宛て先が複数ある場合には,配列を使用しないといけないらしい。 事実,「aaa@example.com, bbb@sample.co.jp」のようにコンマで区切ったままだと送信できなかった。 また,Cc: は指定がある時だけヘッダーに出力するようにしている。

 その後,メールの中身を書いているが,ここにも「From:」フィールドや「To:」,「Cc:」などのフィールド,MIME のバージョン指定などを記載している。 どうも,こちらはメールと一緒に送られるもので,ここで言うヘッダーは送信サーバーに対する指示を表しているみたい(と勝手に思っている)。 また,ここでは複数の宛て先がある場合には,コンマで区切って並べればよい。
 後は,
$smtp->data();
$smtp->datasend($header);
$smtp->datasend($message);
$smtp->dataend();
$smtp->quit;
として,メールを送信している。

 できてみると意外と簡単に送信できている。 TLS の認証の部分も言うほど大したことは記載していない。 しかし,これでうまく Gmail から TLS 認証でメールが送信できた。 実際には,Gmail のアカウントで「安全性の低いアプリへのアクセスを有効」にしないといけないので,ご注意を。

0 件のコメント: