ツール作り

Cyrus IMAPdと腐れExchange Serverのメールをバックアップアカウントへ合成コピーした時に、腐れExchange Serverのメールがダブるので、それを消すツールを作った。

こういうちょいとしたツール作りは楽しい:-)

まあ、普通のまともなメールサーバーなら、Message IDが同じだとメールの同一性は保証されていて、Cyrus IMAPdへスプールしてもインテリジェントにduplicate markを付けて、冗長な同じメールは実データとして格納しないはずだ。

しかし、腐れExchange ServerとOutlookは意味の無いReceivedヘッダをわざわざ追加し、winmail.datとかいう何の役にも立たないゴミを付加してくれるので、さすがのCyrus IMAPdも別のメールとしか見てくれないようだ。

「簡単」と素人を騙して売りつけ、実は制御不能の腐れExchange Serverなんぞ相手にしていなければ、作る必要の無いツールな訳だが、まあ、問題解決のプロセスを考えるのが面白いからよかろう。

で、最初、PHPで組んだ時に、UIDがキーの連想配列にMessage IDを格納して、array_unique()という簡単な処理で、ダブってないMessage IDのリストが作れ、array_diffで、削除すべきメールのリストがすぐ作れた。

だが、盲点は、消されるのはダブっているメールの一方であって、必ずしも腐れExchange Serverのゴミメールじゃないという事だ。

そこで、imap_fetch_overview()で取得したサイズも配列に格納し、array_multisort()で、UID、Message ID、Sizeのそれぞれの配列をソートする。

すると、リストで、ダブっているMessage IDのトップは必ずサイズの小さいものとなり、array_unique()で生き残るのは一番最初に見つかったものなので、サイズの大きいメールが駆逐されるというアルゴリズムにした。

これで完璧だ:-D

しかし、マイクロソフトのもたらす後戻り工数って、全世界規模だとどれくらいの損害額なんだろうなあ。

退化

ファイルアップロードスクリプトが正常に動いているか、時折、ログを観察しているのだが、以前、ファイルの二重送信が原因と思っていたログパターンがまた現れた。

以前は、そのログを見て、送信ボタンの二度押しでの二重送信はJavaScriptで封じたのだが、また起こったという事は別の要因がある。

サーバログを見ると、400 BadRequestになっていた。
うーむ…、cookie関係か?二重送信っぽいので、ブラウザ2つ開いて、それぞれで送信したらどうなるんだろ?と、試験してみるとアウト!

そう言えば、セッション名は特段設定してないので、2つのブラウザで送信したら、片方が完了した時点でセッション変数は全滅するな(笑)。

という事で、セッション名を設定する事で、ブラウザ何個開いて送信しようが大丈夫になった。しかし、cookieファイルが増大していく…

cookieファイルを期限切れで破棄にするには、「アップロードが成功しました」と表示するスクリプトのヘッダ部分で、Set-Cookieを設定してブラウザに食わせてやらねばならない。

しかし、元々のあったものが、cookieの明示的な削除をして無くて、複数セッション利用時の事も全く考慮されておらず、セッション破棄の前に、色々とHTMLを表示してしまっている。

その為、いざ、cookieファイルを消そうとすると、パラメータの渡し方からロジックを洗い直さなければならず、構造からやり直しという事になる(–;

しかし、昔のわしなら、お客様の使用法を想定して、ブラウザを複数開いてファイル送信なんて事は事前に思いついたのだが、問題が起こるまで分からんというのは退化してきたのかのう。

まあ、言い訳をさせて貰うと、すでに運用されているスクリプトで、そういう事態が今まで問題に成らなかった訳が無いという思い込みがあったな。

メールゴミ箱

Exchange+Outlookで、他人がメール送信に失敗した時に、わしのマシンを踏み台にしようとする件だが、メールゴミ箱を作った。

ArGo Softwareのフリーのメールサーバで、こいつのリレーをONにしつつ、ウィルスバスターのパーソナルファイヤウォールで、ポート25の送信を禁止しておく。そうするとOutlookがわしのマシンを踏み台送信に使おうとしても、ローカルのメールサーバに投げて、そこでbounce mailでドロップされて終了。

これでお客様に何通も同じメールを送りつける内の一通は減らす事ができた訳だが、他にも同じ対策をするか微妙だな。何せ、今までLinuxのサーバにメールを投げていた時は全く何の問題も無かった訳だし、Exchange+Outlookだけが「送信したメールが届かない」等の不具合がある。

全て踏み台送信対策をした場合、送信メールが届かないという事例が多発するだろう。 Exchange+Outlookってつくづくどうしようも無いクソだなあ。早く捨てたいのう…。

踏み台送信に使われているであろうと思っていたRemoteProcefureCallについて余りよく調べられていないのだが、踏み台送信にはUDPだけでも無いようだ。OutlookはExchangeアカウントを設定すると、Exchangeの送信トレイを使うように強制されてしまうが、その送信トレイが関係している気がするな。

まあ、こんなMicrosoftのしょうもない製品に時間を取られるのもバカバカしいから、詳しい検証はしていないが、アカウント共有で複数人が同じ送信トレイを使うのは、プログラマの勘で止めておいた方が良い気がするな。

Exchangeで各自にアカウントを作り、public folderを使うというのなら、何通も同じメールを相手に送信するという馬鹿げた現象が無くなる気がするが、どうせ、この一言を言ったところで、誰もやらないだろうし、やるとなると全てわしがやらされるハメになるから言わない(^^:

Google Chrome

今更だが、ブラウザのテストに使用。
ファイル送信時にプログレスバーを表示するPHPスクリプトのテストを行った
ファイル送信でサイズオーバー検出にJavaScriptの非同期通信部を利用しているのだが、応答が帰ってくるのに5分以上もかかる。

safariも含む他のブラウザだと、すぐに応答が返ってきて、送信ファイルサイズオーバーを警告できるのだが、Chromeはこの非同期通信で送信ファイル情報がなかなか得られない。

数メガのファイルでも、大抵、最初の数秒間は情報が取得できずエラーとなる。
JavaScriptのエンジンは速いのかもしれないが、htmlエンジンはsafariをベースとしているので、それとの組み合わせで、うまくいってない部分があるみたいな気がする。

元々、safari自体が、PHPのAPCと相性があまりよろしくなく、ブラウザの戻るボタンで戻って、ファイル送信すると、APCの情報が一切取れないというのがあり、Chromeはそれに更に輪をかけて相性がよろしくない。

詳しく検証はしていないが、APCは、動作がフォームの値の順番に依存するので、ブラウザが順番が変えたりすると、期待した動作にならない事があるので、それが原因か?と思ったりする。

しかし、昔と違って、今時のブラウザはどれも長いJavaScript書いても落ち難くなったなあ。

インターネット接続共有にハメられる

会社で急にDNSが引けなくなるという現象が出た。

とりあえず、ネットワークアダプタの「修復」ボタンを押すと治るのだが、暫くするとまたDNSが引けなくなる。

ルータのDNS機能が悪いのか原因が良く分からない。

Wiresharkを入れてパケットダンプして眺めていると、ルータを差し置いて「俺がルータだ。俺がDNSだ」と喚いているヤツを発見。

で、結局、そのマシンでインターネット接続共有がONになっていたせいだった。

Windowsでインターネット接続共有をONにすると、パソコンがルータのようになるのは知っていたが、DHCPdまでやらかしてくれるとは思って無かった。

確かに家庭内で分かっててやるなら問題無いが、そういうマシンを企業のLANに繋がれてしまうと、配下のマシンを増やそうとしやがるから鬱陶しい。

しかし、今時、パソコンでpppoeで接続してる環境なんてあるのかねえ?
大抵はブロードバンドルータだから、こんな機能は要らないと思うのだが…。

例によって、何でMicrosoftはこういう問題の多い実装をやらかすのか、理解に苦しむ。基本姿勢が「人に迷惑をかけても使ってる人は便利だからいいじゃん」とかいう感じで実装してやがるから、大層ムカつくのでございます。

しかし、こういうMicrosoftの下種な実装で余計なトラブルを増やしてくれるおかげで、わしの様な者が職にあぶれずに済むんだけどね:-)

腐れOutlook

メール送信に失敗すると、他人のマシンを踏み台にして勝手にメール送信をやってくれるMS-Exchange ServerとOutlookだが、今日も誰かがメール送信に失敗したのか、わしのマシンを踏み台送信に使おうとしやがる。

昨日から、ウィルスバスター2009のパーソナルファイアウォールで、OutlookのTCP Port25とPort465を塞いで、Exchange Serverに踏み台送信されるのを防御しているが、いつまで経っても警告が出まくりで止まらない。

問題は、何のメールを送信しようとしているのか分からない事だ。送信トレイにもメールは無いのに、Outlookは何度も何度も誰かのメールをわしのアカウントを使って送信しようとする。

明日はUDPを全て通信不能にしてやるか。RemoteProcedureCallを封じてやれば、送信が止まるとは思うが、これでわしのマシンでのメール送信が止まったら、Exchangeが指令を出しているという事になるな。

仮説の裏づけ

メール送信に失敗すると、他人のマシンを勝手に操り、代理でメール送信をするなどというキチガイじみた仕様を本当にMicrosoftはやっているのか?

というのを検証する為に、Outlook2007とExchange Server2003でちょっとした実験をしてみた。

まず設定がどうなっているかであるが、
Exchangeの差出人のメールアドレスはmain@xxxxxxx.co.jp
そして、規定になっているLinuxサーバの差出人も同じmain@xxxxxxx.co.jp

このメールアドレスが会社の窓口のアドレスで、皆で共有しているアカウントだ。
Exchangeはメールがお客様に届かない事や、お客様のメールがこちらに届かない事があるので、送受信には使用しない。メールを投げるのは規定のLinuxのサーバに投げるように設定してある。

ウィルスバスター2009のパーソナルファイアウォールで、OutlookのTCPのPort25と465の送信を警告するように設定。

Outlook2007で規定のアカウントから、個人宛noizumi@xxxxxxxx.co.jpにメールを送信する。すると、ウィルスバスター2009で警告が出たので拒否。

普通なら、これで絶対にわしの個人アドレスにメールが届く事はない。
しかし、思った通り、届いたね。

ヘッダを見ると、マシン名が入っていたが勿論、わしのマシン名では無く、他人のマシン。そのマシンのOutlookはそのマシンの所有者に内緒で、裏でわしの送ったメールを代理で送信したのだ。

5回程、送信してみたが、色んなマシンが送信を代わりにやってくれた。
ウィルスバスターでOutlookのTCPの送信を全て拒否にしてたから、UDPか何かで送信しているようだ。

MS-Exchange ServerとOutlookを使うと、他人に成りすまして変なメールの出し放題ができます:-)

さすがはMicrosoft。こんな狂ってるとしか言い様が無い仕様を平気で実装するとは恐れ入る。

Microsoftって給料高いから優秀な人材が集まっているはずだが、恐らく、「どんな有能も無能の下では役に立たない」ってヤツかな。

救いようが無いMS-Exchange ServerとOutlook

会社で、Exchange Small Business Server 2003とOutlook2003、Outlook2007という環境があるのだが、それの腐りっぷりがすごい。

1つのExchange Serverのアカウントを会社の窓口アカウントとして複数人で設定しているが、お客さんのメールが届かなかったり、送信したはずのメールが送信されていなかったりと、極めて信頼性が低い。

その為、フォルダにメールを貯めるだけで、送受信はLinuxのサーバという運用になっている。

そして、お客さん宛のメールが2〜7通同じ文面で送信されるという怪現象があり、 時折、わしのマシンでOutlook2007で設定した差出人名で送信されている事があり、わしの設定がExchangeに移ってるのか?という疑惑があった。

今日、Outlook2007のログを有効にしたところ、わしは送信していないのに、勝手にこっそり裏でメール送信したログが残っていた!? 状況を収集したところ、別部署でメール送信中クラッシュしたそうで、どうやらそのメールがわしのマシンで勝手に送信されたようだ。

この事から類推するに、メール送信失敗をExchangeが関知すると、送信不能を避ける為に、その同じアカウントを共有するマシンで、代替送信をしているのではないかと思われる。

想像だが、その代替送信の仕組みとして、Exchange Serverが「メール送信失敗したヤツがいる!」と騒ぐと、その騒ぎを嗅ぎ付けたクライアントが、我先にと勝手に代理送信し、事後報告するという仕組みである為、同じ文面のメールが何通も相手に届くのではないだろうか? という気がする。

これで、差出人がわしのメールがお客様に送信されている謎の一つが分かった。わしの設定がExchange Server経由で他のメールクライアントに伝播したのでは無く、わしのOutlook2007がExchange Serverの指令を受けて、裏でこっそり他人のメールを送信していたからだ。

そういえば、Exchange Serverに投げるとマズいメールアドレスの形式もクライアントのOutlookが勝手に変更するというのがあった。

恐らく、Exchange Serverの欠陥をOutlookで補おうとした結果、他人が送信失敗したメールを他のマシンで勝手に送信するなどという、腐った実装になっているのだろう。

心底呆れた。こんな実装絶対にありえん!

Progress bar 実装

PHPでファイルアップロード時に進捗を表示するProgress barの実装をやってたりする。

PHP5のAPC(Alternative PHP Cache)で、ファイル送信時の情報取得が使えるようになったのだが、サーバがPHP4のシステムで動いているので、PHP5に置き換えられない。

PHP4とPHP5の同居が必要なのと、Apacheでモジュールを組み込むにしても、mod_phpは4と5を同時に組み込む事ができないので、それぞれを組み込んだApacheを動かす必要がある。

通常動かしているApacheとは別にもう1個別portで動かして、特定のURL以下をreverse proxyで置き換えなんてトリッキーなやり方で動作環境は確保した。

で、Progress barのソースサンプルを入手して実装しようとしたのだが、非常に難解な組み方をしていて、「何かあるな」という感じはしていたのだが、別に気にせず、原理を理解して、Ajaxのprototype.jsを使ってコードを書いた。

しかし、safariでテストした時、全然動かない。
非同期通信で呼ばれるPHPスクリプトのアクセスログが発生せず、Progress barを表示するページで、formにAjax.requestの処理の入った関数が呼ばれているかを吐き出すようにしてみると、ちゃんと呼ばれている。

しかし、Ajax.requestの行だけ、全く無視された状態になっていた。サンプルだと動く。サンプルは、ページ遷移しないか、一部が遷移するようになっていて、非同期通信でAPCの情報を取得しているのは、遷移しない部分…。

ここで、サンプルがなぜ難解な方法で組まれているのか全てを理解した。
APCの情報を取るのに、一部のブラウザではページ遷移が起こる場合、Ajax.requestなどの非同期通信のjavascriptコードが一切動かなくなるのだ。

根本的に構造の見直しが必要で、ほとんど一から作り直し(笑)

まあ、なんとかでっち上げつつあるが、今はIE6で後方互換モードでないと表示しないとかしょうもないブラウザのバグの対処に当たっている。

暫く、SI業から遠ざかっていたから動向を良く知らなかったが、Microsoftは相変わらず、世間に迷惑をかける実装を続けていたのか。実にMicrosoftらしいと感心する事しきり(笑)。

PHP

IMAPのメールサーバをimapsyncというツールで複製を取っているが、フォルダに関しては、コピー元のフォルダが基準となり、コピー先に無ければ作成して中身をコピーという動作になっている。

コピー元のフォルダが削除されたらどうなるか?と言えば、コピー先のフォルダはそのまんま。つまり、長期間複製を取っていると、コピー元で削除されたフォルダが、コピー先でどんどん増えていくという困った状態になる。

IMAPのサーバの機能としては、メールだけは「何日以上古いものは消す」という事はできるが、フォルダは消せない。

空のフォルダが溜まって来るのを何とかしないといけないので、ツールを作る事にした。

socket openとか低レベルな関数組み合わせてゴリゴリ作らなきゃいけないのかと思ったが、PHPにはIMAP Clientなる便利な関数群があった。

フォルダツリーを辿って、子フォルダが無く、メールも入ってないフォルダを削除するってのを再帰処理で組んだが、socketとかバッファとか、文字列処理とか考えなくていいからすげー楽だ。

いい世の中になったものよのう。