まだ重たいCMSをお使いですか?
毎秒1000リクエスト を捌く超高速CMS「adiary

2008/02/08(金)2行に渡るメールヘッダの正しい処理

問題発生

ある方より、メール投稿利用時に長い日本語タイトルを付けると、途中に半角スペースが入ってしまうというバグ報告を受けました。

Subject: =?ISO-2022-JP?B?GyRCPmFHKz5hRys+YUcrPmFHKz5hRys+YUcrPmFHKz5hGyhC?=
 =?ISO-2022-JP?B?GyRCRys+YUcrPmFHKz5hRys+YUcrPmFHKz5hRys+YUcrPmFHKz5hGyhC?=

のようにMIMEエンコードが長くなり空白が入ったときに、この空白がデコードされてもそのまま残ってしまうことが原因です。やっつけならこの空白を除去するだけで良いのですが、やっつけプログラムは最低なのできちんと調査してみました。

長いメールヘッダの規定

RFC 2822「Internet Message Format」には次のように規定されています。

2.1.1. 行の長さの制限

それぞれの行の文字はCRLFを除いて、決して998文字以下でなければならず(MUST)、78文字以下であるべきである(SHOULD)。

2.2.3. 長いヘッダフィールド

それぞれのヘッダフィールドは(略)単一論理行である。しかしながら便宜のため、文字数制限のために、ヘッダフィールドの一部であるフィールドボディは複数行に分割して表現でき、これをfoldingと呼ぶ。どこであれfoldingのホワイトスペース(WSPではない)があるとき、WSP(SPACE/0x20またはTAB/0x09)の前にCRLFを挿入してよい。例えば、ヘッダフィールド:

Subject: This is a test

は以下のように表現できる。

Subject: This
 is a test

(中略)

このfoldingされたヘッダフィールドの複数行表現を1行表現にする過程をunfolding と呼ぶ。unfolding はWSPがすぐ後に続くあらゆるCRLFを単に削除することでなされる。

つまりRFCの規定に従い、最初に例示したメールヘッダは次のように復元されます。

Subject: =?ISO-2022-JP?B?(略)?= =?ISO-2022-JP?B?(略)?=

ここまでは正しい復元過程であり、やっつけ実装として考えた「単純に複数行ヘッダの2行目以降の先頭空白の除去」は誤りであることがわかります。実際、長い英字タイトルに1文字だけ空白を入れた「aa(略)aaa bbb(略)」というメールを送信したところ

Subject: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaa  ←※実際にはここまで1行
 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

というように78文字の制限を超えてメールヘッダが作られました。単純な先頭空白除去では、このような改行位置が半角の文になっているメールを受信した際に問題が発生します*1。そもそもRFCの規定で「CRLFを単に削除する」となっているのですから、そこは正しく従わねばなりません。

*1 : ちなはにnPOPはこの実装でした。

MIMEの規定

では「長い日本語タイトルの途中に空白が入る問題」はどこに実装の誤りがあるのでしょうか。これについて、RFC 2047「非ASCIIテクストのためのメッセージヘッダ拡張」を参照します。

6.2. 'encoded-word' のディスプレイ

複数の 'encoded-word' を含むある特定のヘッダフィールドを表示する時は、隣接する 'encoded-word' を分離するあらゆる 'linear-white-space' は無視される。

'encoded-word'は、いわゆるMIMEエンコードのことです。linear-white-space はRFC 822により次にように定義されています。

HTAB        =  <ASCII HT, horizontal-tab>   ; (文字コード:9)
LWSP-char   =  SPACE / HTAB                 ; semantics = SPACE
linear-white-space =  1*([CRLF] LWSP-char)  ; semantics = SPACE

[CRLF] は CRLF があってもなくても良い。(0回以上1回未満の繰り返し)
1*(element) はelementの少なくとも1つ以上の繰り返し。

まとめると「=?ISO-2022-JP?B?(略)?=」ブロックと「=?ISO-2022-JP?B?(略)?=」ブロックの間にある、"CR LF SPACE(又はTAB)"の1つ以上の繰り返しは削除すべしということになります。

正しいメールヘッダ復元の実装

Perl正規表現で次にように行います。

$hl = (1つのヘッダフィールド(1行または複数行))
while ($hl =~ /(.*?=\?ISO-2022-JP\?B\?[A-Za-z0-9\+\/=]*\?=)(?:(?:\r\n)?[\t ])+(=\?ISO-2022-JP\?B\?[A-Za-z0-9\+\/=]*\?=.*)/i) {
	$hl="$1$2";
}
$hl =~ s/=\?ISO-2022-JP\?B\?([A-Za-z0-9\+\/=]*)\?=/ &ベース64デコード($1) /ieg;

ベース64デコード関数は別に用意してください。(たとえばこれ

(?:(?:\r\n)?\r\n[\t ])+

上記が、linear-white-spaceに相当します。ただ、メールデータを読み込む状況によっては \n \r \r\n のいずれになってプログラムに渡されるかわかりませんので、若干厳密な実装ではなくなりますが、

$hl =~ s/\r\n|\r/\n/g;

しておいてから、

(?:\n?[\t ])+

の方が良いと思います。さらに言えば "RFC 2822" の規定を先に適用して、先に改行コードを除去してから、MIMEエンコード間の空白除去を行っても動作としては変わりません。それをベースにした adiary の実装を最後に示します。

$hl =~ s/\x00//g;
my @buf;
# MIMEデコードをしてエスケープ表記"\0 num \0"に置き換え
$hl =~ s/=\?[\w\-]+\?B\?([A-Za-z0-9\+\/=]*)\?=/
	push(@buf, $self->base64decode($1));
	"\x00$#buf\x00";
/ieg;
# RFC 2047
$hl =~ s/\x00[\t ]+\x00/\x00\x00/g;
# エスケープ復元
$hl =~ s/\x00(\d+)\x00/$buf[$1]/g;

2008/01/15(火)Apache 2.2/worker で ServerLimit が効かない?

PerlInterpMaxを解析後、正しくApacheに ServerLimitを設定しているにも関わらず、またデータベース接続エラーが。ps ax すると、httpd(Apache)のプロセスが9個ぐらい起動してました(汗*1

あれバグ? それとも仕様??

検索してみても、同様の報告しかみつかりませんでした*2。調べてみると

    MaxRequestsPerChild   1000

を越えたときに、古いプロセスがうまく殺せていないようです。mod_perl が影響しているかどうかは不明です。とりあえず、MaxRequestsPerChild 0 にして様子をみようと思います。

*1 : サービスを受付ない親プロセスが1つ多く起動しますが、それは除く

*2 : このサーバはSolarisではないですが

推測

MySQL環境で同様の設定もしても再現しないことから、おそらく PostgreSQL で Connection を永続させているため、ネットワークコネクションがすべて切れない(Apacheがコネクション=クライアント接続中)と勘違いしてプロセスが Kill されないせいと思われます。*3

PostgreSQL への接続に UNIX ドメインソケットを使っているせいでタイムアウトの設定ができない(汗

*3 : MySQLではコネクションを永続させても、MySQL側が自動的にタイムアウトをして接続を切る。

2007/12/26(水)AGPLv3 第13条の日本語訳

AGPLv3は、第13条以外はGPLv3とほとんど相違ないとのことなので、第13条のみ翻訳(not 直訳)しました。翻訳の正確さは一切保証しません。突っ込みはどうぞ。

13 リモートネットワークにおけるやりとり(GNU GPLと共に)

13. Remote Network Interaction; Use with the GNU General Public License.

Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph.

このライセンスの他の規定にかかわらず、あなたがプログラムを変更した場合、コンピューターネットワーク(またはあなたの改変したプログラムがサポートするやりとり)を介してすべてのやりとりを行うユーザーに対し、このプログラムはあなたが変更したバージョンであり、標準的または慣例的なソフトウェアコピー手段により、無料のネットワークサーバから対応するあなたが変更したバージョンのソースコードにアクセスし受信できることを目立つ場所に掲示しなければならない。GNU GPLv3によって保護される任意の著作物に対応するソースは、以下の項目に従うものとする。

Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License.

このライセンスの他の規定にかかわらず、あなたはGNU GPLv3の元でライセンスされ保護された著作物を結果としての単一の著作物となるようにリンクまたは内包することを許可する。その際、このライセンスの条件は、保護された著作物の一部に対して引き続き適用される。しかしながら、複合物の残りの部分(残りの著作物)については、GPLv3によって決定される。

注意

ちょーいい加減な訳ですので、あしからず。

2006/11/06(月)Yahoo blog検索のXML-RPC

Yahoo blog検索のXML-RPC

Weblogs.com: API Interfaceの「weblogUpdates.ping」におけるオプショナルパラメーターである3番目、4番目のパラメーター(参考)を送信すると、Yahoo blog検索のXML-RPCインターフェイスは、何も返答せずにコネクションを切ります

adiaryβ12配布物において、Yahoo blog検索に対し更新通知Pingを送信するようにしていましたが、エラーが返されていました。次以降のバージョンでは削除の予定です。

2006/11/06(月)IE7における標準準拠モード

adiary各テーマをIE7で表示テスト

曰く「IE7ではCSSの問題を解決した」らしく、現在配布されている adiary ではその言葉を信用してIE7の場合は標準準拠モードでレンダリングするよう設定されています。日本語正式版がリリースされましたので、実際のところどうなっているのか確認してみました。

konomiテーマの場合

ie7_konomi.gif

記事下側の枠線が表示されません。

shukujoテーマの場合

ie7_syukujo.gif

:after :beforeの擬似要素に対応していないために、名前欄などのカスタマイズが有効になっていません。おまけに「_ hack」(アンダースコアハック)が無効化されているため、本来表示すべき文字も表示されません。またリンクの表示位置がずれまくっていて無惨です。

umiテーマの場合

ie7_umi.gif

固定幅の左右余白が変動するのが本来の動きなのですが、本文部分の左余白が変化せずなぜかスクロールバーが表示されています。また「ログイン」の部分がクリックできなければいけないのですが、マウスをどこにもっていってもクリックできません。

結論

どこの標準に準拠したのか非常に謎です。やっつけにやっつけを重ねてるんじゃなくて、レンダリングエンジンをきちんと1から作り直したらどうかと思うんですが。ということで、adiaryでは今後、IE7はIE6などと同じようなCSS解釈を行う互換モードでレンダリングさせることに決定しました。

なお当然のことながら、Firefox、Operaでは何ら問題なく表示されます。