LoginSignup
0
0

More than 5 years have passed since last update.

flight 001: iphone mail 文字コード判別

Last updated at Posted at 2018-10-06

.
よく「iphone mail で魔法の文字を入れて送信すれば文字化けを回避できる」
という記事を見かけるとおり、
送信側の文字コードが、通例では

旧来のメーラーは、jis ( iso-2022-jp )
昨今のメーラーは、utf8

と、ほぼ固定(一部例外有)だったけど

iphone mail は
文字コード可変送信型メーラー

に、なっている

sendmail するにしても、web に渡すにしても
メールパースする場合、なんとも厄介この上ない

ひとまずやってみたので一例

[ 食感 (要点) ]

  1. 件名 mime encode 値 と 本文 Jcode::getcode 値 を用いて判定
  2. 上記 2つ ( 件名 と 本文 ) の判定違いも注意
  3. getcode の特性上、判定できないものもある
  4. 他国語も もれなく 注意必要

[ お品書き ]

1. キッチン ( 使用環境、検証環境 )
2. 食材
3. 仕込
4. 調理 1 - 通常ケース
5. 調理 2 - 特殊 1 - ハートマーク
6. 調理 3 - 特殊 2 - 波ダッシュ、他
7. 調理 4 - イレギュラー 1 - 中国語
8. 調理 5 - イレギュラー 2 - 判定不能
9. デザート ( まとめ、あと書き )

[ 1. キッチン ( 使用環境、検証環境 ) ]

xserver x10
perl 5.16.3

iphone SE (iOS 9.3.5)
iPhone Mail (13G36)

[ 2. 食材 ]

  1. mime encode された (件名 $subject)
    例: =?UTF-8?Q?

  2. Jcode::getcode (本文 $body) の値
    例: utf8

[ 3. 仕込 ]

パース後、こんな感じで、件名 ( subject ) と 本文 ( body ) を入れておく


use Jcode;
use Encode;
use MIME::Parser;

# =============================
# 件名 

my $subject = $header->get('Subject');
chomp($subject);

$sub01 = $subject;
$sub02 = substr($sub01, 2, 3); # 3文字抜き
$sub03 = substr($sub01, 6, 2); # EUC用にハイフン後2文字抜き
$sub04 = substr($sub01, 2, 6); # 5文字抜き

# 半角 (1バイト) のみチェック ( = 1 なら半角のみ )
# ( 本来 件名に 2バイトが入ることはないが念の為 )
$sub05 = 0;
if ( $sub01 =~ /^[\x20-\x7E]+$/ ) {
    $sub05 = 1;
}

# 件名なしチェック
$sub06 = 0;
if ( $sub01 eq '' ) {
    $sub06 = 1;
}

# =============================
# 本文 ( multipart 無しだったとして ) 

$body = $entity->bodyhandle->as_string;
chomp($body);

$bo01 = Jcode::getcode($body);

# =============================

[ 4. 調理 1 - 通常ケース ]

(1) 件名、本文 ともに 日本語

sub01: =?iso-2022-jp?B?GyRCJCIbKEI=?=
sub02: iso
sub03: 20
sub04: iso-20
sub05: 1
sub06: 0
body(bo01): jis

-> 件名 iso-2022-jp, 本文 jis のシンプル

(2) 件名(例: aaa)、本文 ともに 英語(英数半角/1バイト)

sub01: aaa
sub02: a
sub03:
sub04: a
sub05: 1
sub06: 0
body(bo01): ascii

-> 件名 mime encode 無し(ascii), 本文 ascii これもシンプル

[ 5. 調理 2 - 特殊 1 ]

(1) 件名、本文 ともに ハート( ♡ )

sub01: =?euc-kr?Q?=A2=BD_su?=
sub02: euc
sub03: kr
sub04: euc-kr
sub05: 1
sub06: 0
body(bo01): euc

-> 件名 euc-kr, 本文 euc

euc-kr ( 韓国系 euc ) 初めて見ました
これいわゆる utf8 固定になると言われている文字列で、
署名に入れると utf8 になる ? ( getcode では utf8 にならないのか未検証 )

[ 6. 調理 3 - 特殊 2 ]

(1) 件名、本文 ともに 0x8160 ( sjis ):
通称 波ダッシュ

sub01: =?cp932?Q?150=81`30?=
sub02: cp9
sub03: 2?
sub04: cp932?
sub05: 1
sub06: 0
body(bo01): sjis

-> 件名 cp932, 本文 sjis

メーラーで cp932 も初めて見ました
波ダッシュは、web ページから貼り付けて送信すると入ってきます

(2) 件名、本文 ともに 0x8166 ( sjis ):
2バイトアポストロフィー ? ( 正式名分かってません )

sub01: =?cp932?Q?You=81fve?=
sub02: cp9
sub03: 2?
sub04: cp932?
sub05: 1
sub06: 0
body(bo01): sjis

-> 件名 cp932, 本文 sjis

2バイトアポストロフィー は、海外からのメールなんかでたまに入ってきて、
iso-2022-jp や utf8 で何も考えず転送送信すると文字化けてます

しょうがないので unpack して 1バイトアポストロフィーに変換します


$bod01 = $body;
$bod01 =~ s/^\s*(.*?)\s*$/$1/;
$bod02 = uc unpack("H*", $bod01);

$bod02 =~ s/8166/27/mg;

$bod03 = pack("H*", $bod02);

[ 7. 調理 4 - イレギュラー 1 ]

(1) 件名、本文 ともに 中国語

sub01: =?GB2312?Q?60_=D5=E2?=
sub02: GB2
sub03: 12
sub04: GB2312
sub05: 1
sub06: 0
body(bo01): euc

-> 件名 GB2312, 本文 euc

subject と body の判定が一致しない ...

Jcode::getcode では 以下の値が取得できるらしいです
binary / ascii / euc / sjis / jis / ucs2 / utf8 / undef

euc の場合、
euc-jp (日本系) / euc-cn (中国系) / euc-tw (台湾系) / euc-kr (韓国系)
など、いろいろあるので、このケースは、euc-cn と推測

でも、件名の GB2312 から本文も同じく以下にしたら文字化けなく OK

Encode::from_to($body, 'GB2312', 'utf8');

ただし、簡体字 / 繁体字 などの違いで、Big5 や GB18030 などに変わるのかも

[ 8. 調理 5 - イレギュラー 2 ]

件名 が 空 で、本文 の getcode が undef など判定不能 の場合は、もうどうしようもない
実際、getcode で、binary / ucs2 / undef あたりになると、どうしたものか

でも、テストケースも思いつかないので unknown 判定で、保留

[ 9. デザート ( あと書き ) ]

もちろん件名と本文が、同一文字コードではないケースも普通の話なので、
同一の判定をすることはできないけど
getcode 判定は 時として 曖昧なので、mime encode も用いて総合判断する

※以下とても注意!
(1) perl の version
(2) Jcode の version
(3) iphone の 機種
(4) iOS の version
(5) iphone mail の version
によって判定値 は 変わる可能性があるので注意が必要
(あくまでも参考程度に。最新iOSでは変わっているかも)

今後も apple 様の思うままに仕様変更はありそう

.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0