今回はモブログに潜んでいる不具合を紹介してみたいと思います。
モブログと言ってもブログサービスに限った話ではなく、SNS の日記などを携帯から投稿したり、送信するメールに写真を添付してフォトストレージサービスにアップロードしたり、その仕組みは様々なサービスに応用されています。
では、その様々なサービスに潜んでいる不具合の内容からご説明しましょう。
・件名を「サークルKサンクス」など「全角半角英数全角」としてモブログ。
・投稿された記事のタイトルが「サークルK サンクス」となる。
「サークルK」と「サンクス」の間に半角スペースが入る。
・AU、SoftBank の端末ではこの不具合は起こらない。
こんな感じ。
なぜこの不具合が起こるか、を説明する前に
RFC2822(822) の Section 2 に目を通しておいた方がよいかもしれません。
とは言うものの、英文で量も少なくないので大事な部分を抜粋します。
2.1.1. Line Length Limits
There are two limits that this standard places on the number of
characters in a line. Each line of characters MUST be no more than
998 characters, and SHOULD be no more than 78 characters, excluding
the CRLF.
2.2.3. Long Header Fields
Each header field is logically a single line of characters comprising
the field name, the colon, and the field body. For convenience
however, and to deal with the 998/78 character limitations per line,
the field body portion of a header field can be split into a multiple
line representation; this is called "folding". The general rule is
that wherever this standard allows for folding white space (not
simply WSP characters), a CRLF may be inserted before any WSP. For
example, the header field:
(p8)
Subject: This is a test
can be represented as:
Subject: This
is a test
「2.2.3. Long Header Fields」はちょっと理解しずらいですが大事なので補足的な説明を。
ヘッダフィールド(「Subject: This is a test」や「To: precure@example.com」などのこと)は
「フィールド名」「:」「フィールドボディ」「CRLF」
という構造をしていなくてはいけません。
ヘッダにおいて「CRLF」は1つヘッダフィールドの終わりを示す特別なものです。
しかし、「2.1.1. Line Length Limits」に「SHOULD be no more than 78 characters, excluding the CRLF.」とあるように、1つのヘッダフィールドは 78文字以内にするのが推奨されます。
でも、78文字を越える場合は「CRLF」で folding することができます。
その場合は、「CRLF」に続く次の行はホワイトスペースではじめましょう。
ということです。
それでは実際に DoCoMo、AU、SoftBank のそれぞれの端末でメールを送ってみて Subject: がどうなっているか見てみましょう。
まずは、DoCoMo。
Subject: =?iso-2022-jp?B?GyRCJFUkPyRqJE8bKEI=?=PrettyCure
=?iso-2022-jp?B?GyRCJEAkaCRNISohKhsoQg==?=
1行目の「=?iso-2022-jp?B?GyRCJFUkPyRqJE8bKEI=?=PrettyCure」で 57文字。78文字の制限まで多少余裕はあるけれど、次の「=?iso-2022-jp?B?GyRCJEAkaCRNISohKhsoQg==?=」を続けられるほど余裕はないので改行します。2行目はホワイトスペースを1つ入れて「 =?iso-2022-jp?B?GyRCJEAkaCRNISohKhsoQg==?=」。
RFC2822(822) の推奨通りですね。行儀が良いです。
続いて、AU。
Subject: =?ISO-2022-JP?B?GyRCJFUkPyRqJE8bKEJQcmV0dHlDdXJlGyRCJEAkaCRNISohKhsoQg==?=
AU は改行することなく1行。ちなみに、75文字。行儀よくないです。
最後に、SoftBank。
Subject: =?ISO-2022-JP?B?GyRCJFUkPyRqJE8bKEJQcmV0dHlDdXJlGyRCJEAkaBsoQg==?=
=?ISO-2022-JP?B?GyRCJE0hKiEqGyhC?=
SoftBankも改行しています。
おまけとして、Thunderbird。
Subject: =?ISO-2022-JP?B?GyRCJFUkPyRqJE8bKEJQcmV0dHlDdXJlGyRCJEAkaCRNGyhC?=
=?ISO-2022-JP?B?GyRCISohKhsoQg==?=
きれいですね。
さて、3キャリアから送信したメールのヘッダが揃いましたが、これらをデコードしてみましょう。Perl でモブログの機能を提供しているところではおそらく、MIME::Parser を使用しているのではないでしょうか。そして、肝心な Subject: のデコードは、MIME::Words::decode_mimewords で実装されていると思います。
ということで、今回は MIME::Wrods::decode_mimewords でデコードした結果を掲載します。
DoCoMo の場合。
ふたりはPrettyCure(CRLF)
だよね!!(CRLF)
AU の場合。
ふたりはPrettyCureだよね!!(CRLF)
SoftBank の場合。
ふたりはPrettyCureだよね!!(CRLF)
ヘッダの最後には必ず CRLF が含まれます。デコードしてもその CRLF が取れることはありません。なのでアプリ側で行末の CRLF を取り除いたりしていると思います。CRLF を取り除いてみましょう。このとき、DoCoMo の途中にある変な CRLF も一緒に取れてしまいます。
DoCoMo の場合。
ふたりはPrettyCure だよね!!
AU の場合。
ふたりはPrettyCureだよね!!
SoftBank の場合。
ふたりはPrettyCureだよね!!
やっぱり、DoCoMo の端末のみ「PrettyCure」の後にホワイトスーペースが入っています。AU はそもそもホワイトスペースを挿入していないのでホワイトスペースが入るわけもありませんが、SoftBank は改行してホワイトスペース(1行目は TAB)を入れているにも関わらず、デコードしてもホワイトスーペースが入っていることはありません。なぜでしょうか?
それは、DoCoMo のエンコードの仕方に問題があるからです。
メールのヘッダは ASCII を使うようにしなくてはならないので、日本語は Base64 にエンコードしますね。3キャリアすべて Base64 にエンコードされてはいますが、DoCoMo 茸もともと ASCII である「PrettyCure」をエンコードしていないのです。
(おまけとして挙げた Thunderbird は AU、SoftBank 同様すべてを Base64 でエンコードしています。)
この状態で MIME::Words::decode_mimewords を通ると、2行目以降の行頭の半角スペースが取れません。
それはなぜか?
MIME::Words::decode_mimewords をちょっと覗いてみましょう。手元のバージョンは 5.420 です。
sub decode_mimewords {
my $encstr = shift;
my %params = @_;
my @tokens;
$@ = ''; ### error-return
### Collapse boundaries between adjacent encoded words:
$encstr =~ s{(\?\=)\s*(\=\?)}{$1$2}gs;
pos($encstr) = 0;
### print STDOUT "ENC = [", $encstr, "]\n";
### Decode:
my ($charset, $encoding, $enc, $dec);
## 以下デコード処理
鍵は真ん中あたりの「$encstr =~ s{(\?\=)\s*(\=\?)}{$1$2}gs;」。
「?=」はエンコードの終了マーク、「=?」はエンコードの開始マークになります。
なので、この行で行っている処理は、
エンコードの終了とエンコードの開始マークの間にあるホワイトスペースを取り除く
ということになります。s オプションがついているので改行は無視されて単一行として扱われていますね。前述したように、DoCoMo はそもそも ASCII である「PrettyCure」をエンコードしていません。「?=」と「=?」の間にホワイトスペース以外の「PrettyCure」があるので上記の正規表現に引っ掛からないんですね。だから、CRLF の後の行頭のホワイトスペースがとれないんです。デコード後に「変な CRLF」が残っているのも上記の正規表現に引っ掛からないからです。
結果として DoCoMo の端末で件名を「ふたりはPrettyCureだよね!!」としてモブログすると、投稿された記事のタイトルが「ふたりはPrettyCure だよね!!」となるわけです。
ここまでの説明だと
「『PrettyCure』をエンコードしない DoCoMo の端末がダメなんじゃない?」
なんて思われる方もいるかもしれませんが、そうでもないみたいです。
と言うのも、
RFC2047。
MIME の Non-ASCII Text についての取り決めです。
RFC2047 で重要な部分を
「Perlメモ - Base64エンコード・デコードする」から引用します。
1. encoded-word は 75バイト以内でなければならない.
2. encoded-word を含む行は 76バイト以内でなければならない.
3. encoded-word はそれぞれ独立してデコード可能でなければならない.
4. encoded-text をデコードした文字列の文字コードは,最後に ASCII が指定された状態でなければならない.
5. encoded-word が現れる出現位置に関する決まり.
* Subject や Comment のヘッダフィールドなどの, 'text' 内に出現.
* "(" と ")" で区切られた 'comment' 内に出現.
* From や To,CC ヘッダなどで,'phrase' 内に出現.
* 'addr-spec' 内で出現してはならない.
* 'quoted-string' 内で出現してはならない.などなど.
6. 隣り合う encoded-word の間の 'linear-white-space' は無視する.
RFC2822(822) から「ヘッダフィールドは CRLF を抜いて 78文字以内にしましょうね」と紹介しましたが、日本語 (Non-ASCII Text) を使用するときは Base64 でエンコードしますので上記の 1 と 2 から 1つのヘッダフィールドは76文字以内にしなくてはいけません。
6 は MIME::Words::decode_mimewords の
「エンコードの終了とエンコードの開始マークの間にあるホワイトスペースを取り除く」
というロジックの根拠ですね。
1 〜 6 はどれも大事なことなのでメール関係のスクリプトを書くときはおさえておきたい項目です。
そして、今回の不具合において大事なのは次の引用
RFC 2047 には本来 encoded-word に変換する必要のないもの,つまり,ASCII だけから成る単語まで変換するのは推奨できないと書かれています.ですから,実行例のように is や test. までいっしょに encoded-word に変換するのはあまりいい例とは言えません.
おそらく、上記の「推奨」を重んじて DoCoMo の端末は「PrettyCure」をエンコードしないのだと思います。
今回紹介したモブログに潜む不具合がアプリのバグなのか、RFC2047 の推奨を頑なに守る DoCoMo の端末がおかしいのかはわかりませんが、とにかく提供しているサービスに不具合があることは確かです。
担当者のみなさん、頑張って直してくださいね。
開発部「ちわ」でした。
続きを読む