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

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

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

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

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

に、なっている

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

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

### **[ 食感 (要点) ]**

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

### **[ お品書き ]**
 [1. キッチン ( 使用環境、検証環境 ) ] (#1. キッチン ( 使用環境、検証環境 ))
 [2. 食材] (#2. 食材)
 [3. 仕込 ] (#3. 仕込)
 [4. 調理 1 - 通常ケース ] (#4. 調理 1 - 通常ケース)
 [5. 調理 2 - 特殊 1 - ハートマーク] (#5. 調理 2 - 特殊 1)
 [6. 調理 3 - 特殊 2 - 波ダッシュ、他] (#6. 調理 3 - 特殊 2)
 [7. 調理 4 - イレギュラー 1 - 中国語] (#7. 調理 4 - イレギュラー 1)
 [8. 調理 5 - イレギュラー 2 - 判定不能] (#8. 調理 5 - イレギュラー 2)
 [9. デザート ( まとめ、あと書き ) ] (#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 ) を入れておく


```perl

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バイトアポストロフィーに変換します

```perl

$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

```perl
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 様の思うままに仕様変更はありそう

.