Mail::GnuPG ハメられました。
Mail::GnuPGはひじょーーーーにできが悪い。単純に俺が悪いだけなのかもしれないけれど。とてもハメられましたよ。
今回の要件は、httpsで受け付けるメールフォームから送信するメールをGnuPGで暗号化し、ユーザの入力から実際の受け取り手まで全ての経路で暗号化を行いたい、というもの。
メールをGnuPGで暗号化、ということで至極安直にMail::GnuPGの使用を決めたんだけど、こいつがとっても曲者だったのですよ。
GnuPG::Interface IO::Handle MIME::Entity File::Temp MIME::Parser Mail::Address
Mail::GnuPGは上記のモジュールを必要としている。これはどういうことかというと…
- GnuPG::Interface でGnuPG暗号化
- MIME::Entity で GnuPGで暗号化した結果をMIME形式のメールにする
- MIME::Parser はメールを復号化する時に使う(ので今回はあまり関係ない)
- その他のモジュールは裏方なので説明は省く。
以上から、Mail::GnuPGの中の人がやっている仕事は
となる。
「そんぐらい自分で書けるじゃん」と考えるか「それでも車輪の再発明はヤだ」と考えるかは人それぞれかもしんないけど、Mail::GnuPGはたったそれしきの事をうまくヤれない奴だった。(嘘だと思うならヤってみてくさいよ。そして願わくば俺の浅さを指摘して欲しい)
========new Create a new Mail::GnuPG instance. Arguments: Paramhash... key => gpg key id keydir => gpg configuration/key directory passphrase => primary key password # FIXME: we need more things here, maybe primary key id.
これはドキュメントのMail::GnuPGのインスタンスを作るには、という項目からの引用。"FIXME"で始まる文一つとっても「まだまだ」感が強い。
さらにさらに、今回の事件はコレだけじゃなかった。
どーやら Perl5.6.0 なんていうバージョンはもうダメなんですかね。GnuPG::Interface は、Class:MethodMakerというモジュールが必要で、こいつがコンパイル通らないったらなかったよ!!あーちきしょう!
何をどうやっても通らないので、別で入れてあった Perl5.6.1で動かすことにして、こちらでは何なくインストールできた。Perlはコーディングする分にはバージョンを気にする事はさほどない(と思う)けど、こういうモジュール類を入れる時にはけっこうあると思う。ハマっちゃうのって俺だけ?それともv5.8.6入れるべき?(いやそれはない。と信じたい。)
結果、どうしたかと言うとですね。
一瞬だけ /bin/sh で書きなおすか、なんて思ってしまった事はさておき(POSTデータのデコードが面倒な事に気が付いて思いとどまったもんで)、やっぱりPerlで作ったんだけれども、Mail::GnuPGは使わずに
#!/usr/bin/perl use Jcode; # 日本語文字コード変換 use MIME::Entity; # MIMEメール取り扱い use GnuPG::Interface; # GnuPGインタフェース use GnuPG::Handles # GnuPG I/Oハンドラ my $mess = MIME::Entity->build( Type=>'Multipart/Encrypted; protocol="application/pgp-encrypted"', From=>"$FROM", To=>"$TO", Subject=>"$SUBJ", Encoding=>"7bit"); $mess->attach(Data=>"$version", Type=>'application/pgp-encrypted', Encoding=>"7bit"); $mess->attach(Data=>"$message", Type=>'Application/Octet-Stream', Encoding=>"7bit"); open(MAIL, "|$sendmail $rcptto") || die $mess->print(\*MAIL); close(MAIL); # $mess->print(\*STDOUT); #just for debugging.
だいたいこういう感じで自分でMIMEオブジェクトを作ったよ。上記サンプル中の $message には、だいたい以下の要領で作った暗号化済みテキストをぶち込む感じ。
my $gpg = GnuPG::Interface->new(); $gpg->options->hash_init(armor=>1,homedir=>'/path/to/.gnupg'); $gpg->options->push_recipients("$rcptto"); $gpg->options->meta_interactive(0); my ($si,$so,$se,$sh,$lh,$ph)= (IO::Handler->new(),IO::Handler->new(),IO::Handler->new(), IO::Handler->new(),IO::Handler->new(),IO::Handler->new()); my $handles = GnuPG::Handles->new( stdin=>$si, stdout=>$so, stderr=>$se, status=>$sh, logger=>$lh, passphrase=>$ph); my $pid = $gpg->encrypt(handles=>$handles); print $si "$mes_plain"; close($si); my @enc = <$so>; close($so); close($se); close($sh); close($lh); close($ph); $message = join('',@enc);
そんなにHandleをnew()しなくても、と俺も思うんだけれど、開いてどこかに割り当てておかないと勝手にstdinとかstdoutにされちゃって困ったので、これで良いことにしました。もー眠いし。