この日記はMozillaのプロダクトへの貢献者としての私の成果を中心に、気になったバグやWeb界隈の話題について書いていますが、 断り書きがある場合を除き、いかなる団体のオフィシャルな見解ではありません。あくまでも個人的なものです。 Mozilla Foundation、Mozilla Corporation、及び関連企業の公式情報ではないことに注意してください。

現在、XHTML 1.0 (もどき)から、HTML5なコンテンツに修正中です。古い日記は修正が完了していませんので表示が崩れます。 順次、修正していく予定ですのでしばらくお待ちください。

もずはっく日記(2019年6月)

2019年6月11日

さくらインターネットで受信したメールをサーバサイドで.mailfilterを使ってフォルダへの仕分けに挑戦してみた 初回投稿日時: 2019年06月11日01時15分46秒
最終更新日時: 2019年06月12日12時12分24秒
カテゴリ: Thunderbird 雑談
固定リンク: id=2019061100
SNS: (list)

久しぶりの更新にもかかわらず、Mozillaとは何の関係もない記事だったりします。

d-toybox.comはまあちょっと調べれば分かるようにさくらインターネットの共有サーバでして、このアドレスのメールも全てさくらインターネットのサービスで受け取っているわけです。で、家族共通のメールアカウントを作ったり、フィルタもメインスレッドで動いてしまうThunderbirdの不便さから、以前よりサーバサイドで一部のメールをフォルダに仕分けできないかと思っていたところ、Maildropというソフトで動いているので.mailfilterを書けばできるよっていう情報をいただいたのでやってみました。

とりあえず、メインで使ってるMUAのThunderbirdでInbox内にフォルダを作りました。ちなみに、この記事ではMUA内のものをフォルダ、サーバ上でのものをディレクトリと呼ぶようにしています。まだ違うところがあるかもしれませんが……

次にFilezillaで/home/ユーザ名/MailBox/メールアカウント名にアクセスしてみると、Thunderbirdで作成したフォルダたちのディレクトリが見えます。

シンプルにASCII文字だけで、例えばAmazonというフォルダを作っていると、メールアカウント名/.Amazonというディレクトリが表示されます。ディレクトリ名先頭のドット(.)に要注意ですね。

もし、Amazonの下にUSというフォルダを作っていると、メールアカウント名/.Amazon.USと、ドットをセパレータにしてMUAからは子、孫フォルダに見えているものが、親ディレクトリ直下に作られていくことが分かります。

もし、クロネコヤマトというフォルダを作っていると、メールアカウント名/.&MK8w7TDNMLMw5DDeMMg-というフォルダが出来ていました。たぶん、UTF-8か何かのバイナリ文字列をASCII文字列で表示できるようにエンコードした状態なんだと思いますが、ここは今回関係ないので調べていません(IMAP仕様で決められている修正版UTF-7だそうです)。とりあえず、非ASCII文字のフォルダはMUAから作ってみて、実際にできたディレクトリ名を参照するのが手っ取り早いと思います。既に存在しているフォルダならThunderbirdの場合、フォルダのプロパティダイアログでディレクトリ名を確認することができます。

で、例えばクロネコヤマトのサービスから飛んでくるメールは、クロネコヤマトというフォルダに移動するようにするために、以下のようにしてみました。

if (/^From: .+[@.]kuronekoyamato\.co\.jp.*/:h)
{
  to "maildir/.&MK8w7TDNMLMw5DDeMMg-/"
}

/で囲まれている部分には正規表現を記述します。便利ですね。で、その後の:hはヘッダのみを参照するオプションですが、何も無い場合にはこの値がデフォルト値となるようなので実際には必要ないようです。どのようなヘッダが存在しているのかは振り分けたいメールをThunderbirdで表示している状態でCtrl+Uすればソースが読めるので、丁寧に独自メールヘッダを付けてくれているサービスであればより細かい仕分けも可能になります。

次に、toで振り分け先のディレクトリを指定します。さくらインターネットの場合は常にmaildir/配下で良いようです(maildropのディレクトリはMaildir (Maildir++)形式とのことです)。そして、ドットから始まるフォルダ名を指定しておきます。

そして安全のためにexitは確信があっても書かない方が安全そうです。toは成功した時点で終了するそうなので、exitを書かずにフォールバックするようにしておけば、失敗した場合にはInboxにメールが配信されるようです。

しかし、これだけでは不十分なケースがあることが分かってきました。To:From:に非ASCII文字を含む場合、エンコードされますが、この際にヘッダが長くなりすぎ、折り返すことがあります。また、実例を確認していませんが、ASCII文字列だけでも長くなりすぎると同じ問題が起こると思われます。そこで、複数行のヘッダに対応するために、以下のように複数行に分かれたヘッダにも対応する必要がありそうです(最初の正規表現が無駄に冗長でしたので単純化しました)

if (/^From:(\s.*\r\n)*\s.*.+[@.]kuronekoyamato\.co\.jp.*/:h)
{
  to "maildir/.&MK8w7TDNMLMw5DDeMMg-/"
}

では次に、サービス単位で複数のメールアドレスから送信した形になっているAmazonを例に、Amazonから来たメールの中でも重要なものだけを個別のフォルダに振り分けるように、次のように書いてみました。

if (/^From:(\s.*\r\n)*\s.*shipment-tracking@amazon\.co\.jp.*/:h)
{
  to "maildir/.Amazon.&dnqQAZAad+U-/"  # Amazon/発送通知
}
elsif (/^From:(\s.*\r\n)*\s.*auto-confirm@amazon\.co\.jp.*/:h)
{
  to "maildir/.Amazon.&bOhlh3i6io0-/"  # Amazon/注文確認
}
elsif (/^From:(\s.*\r\n)*\s.+[@.]amazon\.co\.jp.*/:h)
{
  to "maildir/.Amazon/"  # Amazon.co.jpから来るその他のメール
}
elsif (/^From:(\s.*\r\n)*\s.+[@.]amazon\.com.*/:h)
{
  to "maildir/.Amazon.US/"  # USのAmazonから来るメール
}
elsif (/^From:(\s.*\r\n)*\s.+[@.]vpass\.ne\.jp.*/:h)
{
  to "maildir/.Amazon/"  # Amazonのクレカを作っていたら飛んでくるメール
}

もし、メールアドレスをサービス毎に分けているものの、ひとつのメールアカウントで管理しているのであれば、上記のようなブロックを、

if (/^To:(\s.*\r\n)*\s.*foo@d-toybox\.com.*/:h)
{
 ...
}

のようなブロックの下に入れることで、サーバ負荷が減らせられるかもしれません(実際どうなのかは知らないものの、各アドレスごとにフォルダへの振り分けが細かいほど効果はあるかと思います)。

あとはいくつか日本語で.mailfilterについて書かれているサイトを読んでいてよく分からなかった点ですが、まず、()の内側のスペースはあってもなくても良いようです。また、インデントのスペースの文字数に特に決まりも無いようです。なので、この辺は自分に馴染みのあるコーディングスタイルで書いてしまうのがミスが無くて良いのかなという感じです。

最後にあるあるだけどアホみたいにハマったことだけ列挙しておきます。

  • {}はそれ単独の行になくてはいけない(インデントは可)
  • if/elsifの条件文のパターン指定時の最後の/を忘れがち
  • 改行コードはLF
  • サーバ上での.mailfilterの権限は600
  • maildirのサブディレクトリについついアップロードしちゃう
  • Thunderbirdでは、サーバサイドで各フォルダに振り分けたメールが新着として表示されない(不便)