如何编码 e-mail-adresses 中的特殊字符

How to encode special characters in e-mail-adresses

E-Mail-Adresses不只包含这部分:

localpart@domain.tld

下一行的完整字符串(包括引号之间的部分、引号本身和尖括号)也是一个有效地址:

"John Doe" <localpart@domain.tld>

当我用自己的名字替换 "John Doe" 时,我得到了一个地址,我可以将其输入 E-Mail-Client 而不会收到任何投诉(注意 »ö« 我的姓是 non-ascii-character):

"Hubert Schölnast" <localpart@domain.tld>

看起来(对于像 Thunderbird 这样的标准 e-mail 客户端的用户来说),好像引用部分中的特殊字符是可以的。

但是当我用 cpan-module Email::Valid 检查 perl-script 中这个完整的 e-mail 地址时,我得到一个错误,说这个地址不符合 rfc822 的规则,并且这个模块的文档说,rfc822 不允许 non-ascii-character 在 e-mail 地址的任何部分。 (当我省略字母 ö 或将其替换为 ascii-letter 时,检查表明地址有效。)

所以显然任何 e-mail 客户端在发送 e-mail 到 smtp-server 之前必须对 e-mail 地址进行编码,并且在收到新的 smtp-server 时必须对其进行解码e-mail 并向用户显示 header-information。但是我不知道这是怎么做到的,我真的用谷歌搜索了。

我需要这个 encoding-algorithm 因为我想写一个 perl-script 接受任何有效的 e-mail-地址(在引用部分也有特殊字符)然后发送 e-mails 到那些地址。

使用MIME::Words对地址、主题等进行编码或解码

例如创建电子邮件时:

#!/usr/bin/perl
use strict;
use warnings;
use utf8;

use MIME::Words qw{ encode_mimeword };

my $encoded = encode_mimeword('Hubert Schölnast');

要用地址对名称进行编码,请使用 encode_mimewords

处理电子邮件时,请改用 decode_mimewords

Perl 核心有 Encode.pm:

#!/usr/bin/perl
use strict;
use warnings;
use Encode;

my $from_header = decode_utf8 q{From: "Hubert Schölnast" <localpart@domain.tld>};
print encode('MIME_Header', $from_header);

1;
__END__
From: "=?UTF-8?B?SHViZXJ0IFNjaMO2bG5hc3Q=?=" <localpart@domain.tld>

背后有很多要求RFC822/2822,这使得处理电子邮件变得困难。

RFC2822 也禁止消息中的每行超过 998 个字符。 必须通过缩进续行来将长行拆分为多行。

这意味着只要我们在 转换特殊字符并在 header 标签之前 之后修改它们,我们就必须注意行的长度。


编辑

从Encode.pm版本2.80开始,MIME-Header编码被重写以符合RFC2047,我上面贴的原始代码现在不能用了。

参见:https://metacpan.org/pod/Encode::MIME::Header#BUGS

最 straight-foward 的选择是同时使用 Email::MIMEEmail::Address::XS,但这些包不在核心中:

#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use open qw/:std :encoding(UTF-8)/;

use Email::Address::XS;
use Email::MIME::Header::AddressList;

my $address = Email::Address::XS->new('Hubert Schölnast' => 'localpart@domain.tld');
my $addr_list = Email::MIME::Header::AddressList->new($address);

print $addr_list->as_mime_string;

1;
__END__
=?UTF-8?B?SHViZXJ0IFNjaMO2bG5hc3Q=?= <localpart@domain.tld>