先日書いたiPhoneのメールアプリのタイムスタンプが狂う問題を修正を目的とした、Yuaiho作成のサンプルプログラム(Perlスクリプト)を配布します。サンプルを名乗ってますが、一応Yuaihoが実際に使っているスクリプトです。
配布用にコメントやメッセージ出力は割と手直しはしていますが、アルゴリズム的にはそのままです。
配布先
※免責事項は本記事の下部にあります。
ダウンロード from yuaiho.com
ダウンロード後、拡張子をplなどに変更して、実行権限を与えてください。
年月日時分秒からEpoch秒を求めているため、Timelocalモジュールが必要です。
メールファイルのファイルリスト一覧を標準入力で送り込むと、メールファイルを1つづつ開いて読み込み、Receivedヘッダ、もしくはDateヘッダからタイムスタンプを取得します。
使い方
使い方は下記の一番上の行のように、タイムスタンプが狂ったメールファイルが存在するメールボックスのcurディレクトリから呼ばれることを想定しています。
$ find ./ -type f | /tmp/dateperse.pl --debug --commit
$ ls -1 | /tmp/dateperse.pl --commit --noinfo
$ cat <filelist> | /tmp/dateperse.pl --debug --commit
$ cat <filelist(Abusolute file path)> | /tmp/dateperse.pl --absolute
引数に--commitをつけて実行すると、本当にファイルシステム上のタイムスタンプを書き換えます。--
ommit引数がない場合は、タイムスタンプ書き換えは行わず、--commitで実行する際のコマンドがテキストで表示されます。
引数に--debugをつけるとDEBUG:メッセージが出力され、より詳細な情報が出ます。ただ、私がデバッグ用に使用していたものそのままなので内容は不親切です。
引数に--noinfoを追加するとINFO:メッセージが出ません。--noinfoを追加してもエラー(ERROR:)が出るとエラーメッセージが出力されます。--commitと--noinfoを組み合わせると、エラー発生時以外は何も表示されなくなります。
標準入力から送られてくるファイルリストが絶対パスの場合は引数に--absoluteをつけてください。送られてくるファイルリストは絶対パスとして処理します。送られてくるパスをそのまま開きます。
引数に--absoluteが無い場合は標準入力から送られてくるファイルリストを相対パスとみなします。標準入力から送られて来たファイルリストの頭に、スクリプト実行時のシェルののカレントディレクトリを付け足してからファイルを開きます。
引数に-h --usage --helpを入れるとAboutメッセージと使い方が表示されます。
ファイル内に処理すべきタイムスタンプを含むヘッダが見当たらないファイルは--commitを入れて実行しても時刻変更の処理はされません。
下記は引数なしの出力例です。緑が--commit時に実行されるコマンドです。これがファイル数分出力されます。
$ find ./ -type f | /path_to_app/dateperse.pl
-----
INFO: Executing file "/path_to_maildir/cur/./1486390265.M692487P75574.mail.yuaiho.net,S=1962,W=2000:2,S"
INFO: Header read: 2003-08-02 12:58:24+0900 -> Final destinaiton time: 2003-08-02T12:58:24
/usr/bin/touch -d 2003-08-02T12:58:24 "/path_to_maildir/cur/./1486390265.M692487P75574.mail.yuaiho.net,S=1962,W=2000:2,S" >> /dev/null
-----
※ファイル名は一部架空のものです。
動作の前提となる条件
メールは下記を満たしていることを前提に作っています。下記を満たさないファイルを食わせると、不正確なタイムスタンプが生成されたり、プログラムが落ちたりします。微妙に文字色が違う部分の処理はかなり雑に書かれています。
- タイムスタンプの年表記は西暦4桁表記であること。
- タイムスタンプの月表記はJan/Feb/Marのように大文字開始の半角英字3文字であること。
- タイムスタンプの時刻はhh:mm:ddの0フィル有り24時間制表記であること。
- Date:はヘッダ内に必ず存在し、そこにはタイムスタンプが必ず書かれていること。
- Received:ヘッダのセミコロンの後には必ずタイムスタンプが存在すること。
- 時差表記は+hhmmまたは-hhmmで、hhは0〜19でmmは00から15分刻みであること。
- タイムスタンプは0以上のEpoch秒の範囲で、かつ実在する有効な日付・時刻で、整数表記であること。
(つまり2000年08月32日とか、25:05:00とか、13:75:00とか、12:00:00.000はダメです。) - ヘッダ内に空行が無いこと。
- ヘッダと本文の区切りは必ず空行であること。
MUA(Mail User Agent/メーラ)を変更しても問題なく読めるメールであれば、基本的に上記を満たしていると思います。たぶん。
主な仕様
使用可能なヘッダ
採用するタイムスタンプは「Received:ヘッダのみ」「Dateヘッダのみ」「どちらか先に見つかった方」の3つを選べます。「$g_pref_mode_header」で設定します。デフォルトでは「どちらか先に見つかった方」です。
Received:ヘッダのみを参照する場合、参照範囲をヘッダ内に絞る設定があります。絞らないと、Received:ヘッダがヘッダ全体を探しても見つからない場合、本文や添付中に書かれたものを拾う可能性があります。これは「$g_pref_mode_parse_headeronly」で設定できます。デフォルトでは絞っています。
Received:ヘッダは1番上にあるもののみを自動的に拾います。そのファイル内にReceived:で始まる行が1つでもあったか(マッチしたか)を検査してるので、最初のReceived:ヘッダより行頭側にタイムスタンプがあっても反応しません。
タイムスタンプの拾い方はかなり手抜きをしてるので注意が必要です。例えば、Feb 31とか26:58:57とかも拾ってしまいます。
あり得ないタイムスタンプを検知して除外するような機能は実装していません。あり得ないタイムスタンプを拾ってしまうとプログラムがエラーで止まります。
ファイル内に利用可能なタイムスタンプが見つからない場合、そのファイルは何も処理されません。処理されない代わりに「ERROR: File “ファイル名” has no valid hedear.」と表示されて次のファイルを処理します。
時差補正
どちらのヘッダを利用していても、最終的なメールサーバの設置場所の時差を勘案したものとなるよう時差補正をかける処理を入れています。
例えばReceived:なりDate:なりのタイムスタンプに00:00:00+0000(UTC)と書かれているメールは、時刻やタイムゾーン設定が正確なら日本時間では09:00:00に受信してサーバのディスクに書き込まれたことになります。
タイムスタンプの時分秒をそのまま信じると00:00:00ですが、UTCの00:00:00であって日本時間の00:00:00なわけではないので、時差を考慮してサーバ設置場所の正しい時刻(ローカルタイム)に補正する必要があります。
このプログラムは読み込んだタイムスタンプを一旦UTCに戻し、そこからプログラム内で設定した時差を追加することで、サーバ設置場所のローカルクロックの示す時刻になるような処理を入れています。この修正用の時差は時差は「$g_pref_tz_sec」で秒単位で設定できます。
私のサーバはUTC+0900(日本)、にありますのでデフォルトは+0900に補正する設定になってます。
この補正値は任意の時間に設定出来ますが、タイムスタンプの日付や時刻によって補正値を変えるような処理には対応してません。つまりDST(夏時間)には非対応です。DST実施国にお住まいの方は適宜改造してくださいです。
+0900 (JST)のように後ろにタイムゾーンや標準時名とかが付いてる場合もありますが、各国の標準時はたまに変わるのでこのプログラムではカッコ内のタイムゾーン名や標準時名は無視しています。というか、たまに付与されてないし。
ちなみに日本標準時であるJSTは1951年から変わってませんが、例えばここ20年のUTC+9だけ見ても2015年に北朝鮮やロシア(イルクーツク)、モンゴルに異動にあり、UTCとの時差が変更になってます。
余談ですが、古いメールだとGMT+0900とかあったと思います。UTCとGMTは厳密な定義の上では異なる存在ですが、時差を示す場合においては同一のものと考えて差し支えありません。
免責事項
サンプルプログラムの利用・改造・転載は公序良俗に反しない限り自由です。事前・事後の連絡も不要ですが、必ず利用者の自己責任で行なってください。再配布の際はオリジナルのAuther名を残していただけると作者としては嬉しいです。
意図的に有害コードを含むようなことはしておりませんが、意図しない不具合を内包している可能性があります。
Yuaihoが実際に使ったプログラムですが、一般の使用に耐えうるような充分なテストがされてるわけではありません。例外処理などももほとんどされていないか、されていても激甘です。そのため実環境に適用される際はよく検証してからご自身の責任で適用してください。間違いや事故があっても責任は取れません。
コードが汚い系の批判は、頂いてもたぶん直さないと思います。
サイドチャネル的に作ったので参考文献は特にありません。