Hatena::ブログ(Diary)

Webプログラマの覚書 このページをアンテナに追加 RSSフィード

2006-09-03 Perlで日本語(ISO-2022-JP)メールを送信(まとめ)

[]Perlで日本語(ISO-2022-JP)メールを送信(まとめ)

UTF-8で作成されたメール本文をMIME::Lite及びEncodeを使用してメールを送信をする。

そこで問題となってくるのが下記の3点。

1.チルダ(全角)等の文字化け("〜 ‖ − ¢ £ ¬ ")

2.機種依存文字が含まれていた場合の対応

3.MIME::Lite で smtp送信する場合、Return-Pathが有効にならない

1.チルダ(全角)等の文字化け("〜 ‖ − ¢ £ ¬ ")に関する解決方法:

これは有名らしいのですが、Encode::JPが採用している UnicodeConsortium の写像表 に問題があるとかで、

一部の文字が化けます。チルダとか。

しようがないので こんな風に対応しておきました。

my %map = (
    "\x{ff5e}" => "\x{301c}", # 〜 (1-33, WAVE DASH)
    "\x{2225}" => "\x{2016}", # ‖ (1-34, DOUBLE VERTICAL LINE)
    "\x{ff0d}" => "\x{2212}", # − (1-61, MINUS SIGN)
    "\x{ffe0}" => "\x{00a2}", # ¢ (1-81, CENT SIGN)
    "\x{ffe1}" => "\x{00a3}", # £ (1-82, POUND SIGN)
    "\x{ffe2}" => "\x{00ac}", # ¬ (2-44, NOT SIGN)
);

foreach (keys %map){
    $val =~ s/$_/$map{$_}/g;
}

return $val;

もっとスマート?に置換を行なう場合はこの方法で、こっちのほうが恐らく早いです。

$val =~ tr/[\x{ff5e}\x{2225}\x{ff0d}\x{ffe0}\x{ffe1}\x{ffe2}]/[\x{301c}\x{2016}\x{2212}\x{00a2}\x{00a3}\x{00ac}]/;

2.機種依存文字が含まれていた場合の解決方法:

Unicodeの正規化を利用しました。

Unicode正規化に関しては下記ページを参考。

http://homepage1.nifty.com/nomenclator/unicode/normalization.htm

正規化形式に関しては今回 NFKCを利用しております。

以下が Perlのサンプルコード

use strict;
use Encode;
use Unicode::Normalize;

my $val = "UTF-8の文字列";

# * MIME ヘッダー
my $subject = Encode::encode( 'MIME-Header-ISO_2022_JP', NFKC( $val ) );

# * ISO-2022-JP
$body = Encode::encode('iso-2022-jp', NFKC($val) );

1;

3.MIME::Lite で smtp送信する場合、Return-Pathを有効にする方法:

smtpで送信する場合、Return-Pathヘッダー使えないらしい。

10分くらいソースを眺めていたんだけど、

どうも From の値を MAIL FROM で使ってると思われる。

sub send_by_smtp {
  my ($self, @args) = @_;

;# * 中略

  my $from = $self->get('From');

;# * 中略

  ### Create SMTP client:
  require Net::SMTP;
  my $smtp = MIME::Lite::SMTP->new(@args)
    or Carp::croak("Failed to connect to mail server: $!\n");

;# * ↓ココで $from をいれちゃってる
  $smtp->mail($from)
    or Carp::croak("SMTP MAIL command failed: $!\n".$smtp->message."\n");

;# * 中略

  1;

仕方が無いので、上のメソッドをオーバーライドして、

my $from = $self->get('Return-Path') || $self->get('From');

とかしてしまえばOKかな?