2017-09-15 1 views
1

코드 본문에 비 ASCII 문자로 이메일을 보내 이메일을 보낼 수 없습니다 (작업 좋은) :펄 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 : 수정은 간단하지만 문제 자체는 복잡합니다. sendmail(...)로 메일을주기 전에

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

: 문제의 추가를 해결하려면. 그러나이 문제의 끝 부분에있는 경고문을 먼저 메일 안에 이처럼 8 비트 데이터를 보낼 때 발생할 수있는 문제에 대해 알아 두십시오.


실제로 문제 하나가 Perl로 소켓에 옥텟 대 ​​문자의 처리에 더 깊은보고가 수정 이해하기 :

  • Email::Sender::Transport::SMTP 자체가의 syswrite 방법을 사용 Net::SMTP를 사용을 SSL을 사용했는지 여부에 따라 IO::Socket::SSL 또는 IO::Socket::IP (또는 IO::Socket::INET) 소켓
  • syswrite은 옥텟을 예상하며 소켓에 쓰여지는 옥텟의 수를 예상합니다.
  • 그러나 Email::Simple으로 생성 한 메일은 옥텟이 아니라 UTF8 플래그가 설정된 문자열을 반환합니다. 이 문자열에서 러시아어 текст은 5 문자로 처리되는 반면 UTF-8로 변환하면 10 옥텟이기 때문에 문자 수는 옥텟 수와 다릅니다.
  • Email::Sender::Transport::SMTP은 이메일의 UTF8 문자열을 Net::SMTP으로 전달하며 syswrite 안에 사용합니다. 길이는이 경우 옥텟 수와 다른 문자 수를 제공하는 length을 사용하여 계산됩니다. 그러나 소켓 사이트에서는 문자열 밖의 옥텟을 가져 오지 않고 주어진 길이를 옥텟 수로 취급합니다.
  • 주어진 길이를 문자가 아닌 옥텟으로 처리하므로 궁극적으로 프로그램의 상위 계층에서 예상 한대로 적은 양의 데이터를 서버로 보냅니다.
  • 이렇게하면 메일 끝 마커 (단일 점이있는 줄)가 전송되지 않으므로 클라이언트가 더 많은 데이터를 보낼 때까지 서버가 클라이언트가 더 많은 데이터를 보내길 기다리는 중입니다.

예를 들어 두 개의 러시아어 문자 'ий'로만 구성된 메일을 가져옵니다.라인 끝과 끝 메일 마커는 7 개 문자로 구성로 :

ий\r\n.\r\n 

그러나 처음 두 문자가 두 옥텟 지금 각

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

때문에,이 7 자 실제로 9 옥텟이다 이것은 최종의 메일 마커가 불완전하다는 것을 의미

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

:하는 syswrite($fd,"ий\r\n.\r\n",7)는 첫 번째 7 문자의 7 옥텟 만 9 진수에게 긴 문자열을 작성합니다. 그리고 이것은 메일 클라이언트가 더 많은 데이터를 기다리는 동안 전송할 데이터가 더 이상 없다는 것을 의미합니다. 기본적으로 응용 프로그램이 중단됩니다.

지금 누가이 책임이 너무 큽니까?

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 전용이며 메일 내에서 비 ​​ASCII 문자를 보내는 것은 나쁜 생각입니다. 8BITMIME 확장자를 가진 메일 서버에서만 안정적으로 작동하기 때문입니다. 이 확장자를 지원하지 않는 메일 서버가 관련되어 있으면 결과를 예측할 수 없습니다. 즉, 메일을 변형 (서명이 깨질 수 있음)하거나 읽을 수 없도록 변경하거나 어딘가에서 잃어 버릴 수 있습니다. 그러므로 Email::MIME과 같은 좀 더 복잡한 모듈을 사용하고 적절한 콘텐츠 전송 인코딩을 설정하십시오.