2017-09-15 6 views
1

コード体内に非ASCII文字を含む電子メールを送信する電子メールを送信することはできません(ワーキング良い):PerlのSMTP:、

:できるだけ早く私は体に非ASCII文字を追加するよう

#!/usr/bin/perl 

use utf8; 
use strict; 
use warnings; 

use Email::Sender::Simple qw(sendmail); 
use Email::Sender::Transport::SMTP(); 
use Email::Simple(); 
use open ':std', ':encoding(UTF-8)'; 

sub send_email 
{ 
    my $email_from = shift; 
    my $email_to = shift; 
    my $subject = shift; 
    my $message = shift; 

    my $smtpserver = 'smtp.gmail.com'; 
    my $smtpport = 465; 
    my $smtpuser = '[email protected]'; 
    my $password = 'secret'; 

    my $transport = Email::Sender::Transport::SMTP->new({ 
     host => $smtpserver, 
     port => $smtpport, 
     sasl_username => $email_from, 
     sasl_password => $password, 
     debug => 1, 
     ssl => 1, 
    }); 

    my $email = Email::Simple->create(
     header => [ 
      To  => $email_to, 
      From => $email_from, 
      Subject => $subject, 
     ], 
     body => $message, 
    ); 

    $email->header_set('Content-Type' => 'text/html'); 
    $email->header_set('charset' => 'UTF-8'); 
    sendmail($email, { transport => $transport }); 
} 

send_email('[email protected]', '[email protected]', 'Hello', 'test email'); 

send_email('[email protected]', '[email protected]', 'Hello', 'test email. Русский текст'); 

それはデバッグ出力の最後のメッセージでハングアップ:

Net::SMTP::_SSL=GLOB(0x8d41fa0)>>> charset: UTF-8 
Net::SMTP::_SSL=GLOB(0x8d41fa0)>>> 
Net::SMTP::_SSL=GLOB(0x8d41fa0)>>> test email. Русский текст 
Net::SMTP::_SSL=GLOB(0x8d41fa0)>>> . 

どのように修正しますか?

答えて

1

TL; TR:修正は簡単ですが、問題自体は複雑です。問題を修正するには追加します。

$email = Encode::encode('utf-8',$email->as_string) 

sendmail(...)にメールを与える前に。しかし、このような8ビットのデータをメールの中で最初に送信するときに起こりうる問題について、この答えの最後にある警告に注意してください。実際問題、もう1つはPerlでソケットにオクテット対文字の取り扱いに深く見て持って修正を理解することが


  • Email::Sender::Transport::SMTP自体がのsyswrite方法を使用していますNet::SMTPを使用していますIO::Socket::SSLまたはIO::Socket::IP(またはIO::Socket::INET)ソケット(SSLが使用されているかどうかによって異なります)。
  • syswriteはオクテットを想定しており、ソケットに書き込まれたオクテットの数が必要です。
  • ただし、Email::Simpleで構成したメールは、八重奏ではなく、UTF8フラグが設定された文字列を返します。この文字列では、ロシア語のтекстは5文字として扱われ、UTF-8で変換されると10オクテットになるため、文字数はオクテットの数とは異なります。
  • Email::Sender::Transport::SMTPは、メールのUTF8文字列をNet::SMTPに転送するだけで、syswriteの内部で使用します。長さは、lengthを使用して計算され、この場合、オクテットの数とは異なる文字の数が与えられます。しかし、ソケットサイトでは、文字列ではなくオクテットを取り、与えられた長さをオクテット数として扱います。
  • 与えられた長さを文字ではなくオクテットとして扱うので、プログラムの上位層が期待するように、最終的にサーバに送るデータが少なくなります。
  • この方法では、メール終わりのマーカー(単一ドットの行)は送信されないため、クライアントはより多くのデータを送信するのをクライアントが待っている間に、送信するデータをさらに認識しません。

例として、2つのロシア語の文字 'ий'のみで構成されるメールを受け取ります。行で終了し、それが7つの文字で構成され、エンド・オブ・メールマーカー:

ий\r\n.\r\n 

しかし、最初の2つの文字は2つのオクテット今各

и  й  \r \n . \r \n 
d0 b8 d0 b9 0d 0a 2e 0d 0a 

であるため、これらの7つの文字は、実際に9つのオクテットです、syswrite($fd,"ий\r\n.\r\n",7)は7文字の最初の7つのオクテットが、9つのオクテット長い文字列を書き込みます:

и  й  \r \n . 
d0 b8 d0 b9 0d 0a 2e 

これは、エンドのメールマーカーが不完全であることを意味します。これは、メールクライアントが送信する必要のあるデータがないことをメールクライアントが認識していない間にメールサーバーがさらにデータを待つことを意味します。基本的にアプリケーションがハングアップする原因になります。

今、誰がこれにあまりにも責任がありますか?

IO :: Socket :: SSL :: syswriteは、UTF8データを正常に処理する必要があり、要求されたものはRT#98732であると主張できます。しかし、IO :: Socket :: SSLのsyswriteのドキュメントは、バイトで動作することを明確に示しています。また、ノンブロッキングソケットを考慮したときに、正常なキャラクタベースの動作を作成することは事実上不可能であるため、このバグは拒否されました。 SSL以外のソケットでもUTF8文字列に問題があります。最初にSSLを使用しない場合、プログラムはハングすることなく、代わりにWide character in syswrite ...でクラッシュします。

次のレイヤーアップは、Net::SMTPがそのようなUTF8文字列を適切に処理すると期待しています。のみ、documentation of Net::SMTP::dataで明示的に言われている。

DATAはリストへのリファレンスまたはリストであってもよく、が何らかの符号化のオクテットに発呼者によってエンコードされる必要があり、例えば、必要とされますEncodeモジュールのencode()関数を使用します。

は今、一つはどちらか Email::Transportが正しくUTF8文字列を扱うべきであると主張する可能性やその Email::Simple::as_stringは、最初の場所でUTF8文字列を返すべきではありません。

しかし、別の層を上にすることもできます:開発者自身。 Mailは伝統的にASCIIのみであり、8BITMIME拡張子を持つメールサーバでは確実に動作するので、メール内に非ASCII文字を送信することは悪い考えです。この拡張子をサポートしていないメールサーバが関与している場合、結果は予測できません。つまり、メールが変換され(署名が破損する可能性があります)、読めないように変更したり、したがって、Email::MIMEのようなより複雑なモジュールを使い、適切なコンテンツ転送エンコーディングを設定してください。