AJAX+Comet+空メールでメールアドレスを入力
Webフォームでメールアドレス登録する際に、誤入力防止の為のメールアドレス確認欄が必要かどうかという議論があるけど、それなら手入力しなければ良いよね、と思う。
PCブラウザの場合はAJAXが使えるので、「空メールを送信したその場でフォームに反映」とかも出来そう。
ということで作ってみた。
デモ
http://terazzo.dyndns.org/comet_maddr/
- サーバが落ちていたり電話代を滞納している時は見れません。
- 家サーバな上、ISDN回線(64kbps)なため、あまりCometぽい応答速度ではないかも。
- できれば捨てアカでやって下さい
方針
- CometサーバとしてMeteorを使用
- Meteor付属のJavaScriptコードだとコールバックでチャンネル名が取れないのでそこだけ修正する
- 入力フォーム作成時に文字列(ID)をランダム生成し、メールアドレスとMeteorのチャンネル名に使用
- メールアドレスは、qmailの拡張アドレス(hoge-XXXX@example.comなどのワイルドカード機能)を使用し、ハイフン以降の部分をチャンネル名にする
- フォーム上では、mailto:でこのメールアドレスを表示し、同時にMeteorのjoinChannel()のチャンネル名および入力フィールドのIDにも指定
- 言語はperl
環境
- Turbolinux Server 10.0 (多分何でもいい)
- Apache httpd-2.2.8(多分何でもいい)
- qmail-1.03
- perl v5.8.5(多分何でもいい)
- Meteor
- String::Random
サーバの準備
Meteorをインストールして起動(参考)
mkdir /usr/local/meteor cd /usr/local/meteor wget http://meteorserver.org/download/latest.tgz tar zxvf latest.tgz # 設定ファイルをコピー cp ./meteord.conf.dist /etc/meteord.conf # PingIntervalを変更 vi /etc/meteord.conf ----------------------------------------------------- PingInterval 60 ----------------------------------------------------- # 起動 chmod a+x meteord ./meteord
apache->meteorの転送設定
(以下をconf/httpd.confに追記)
ProxyPass /push http://localhost:4670/push
meteor/public_html以下のファイルをApacheのhtdocs下にコピー
mkdir /usr/local/apache2/htdocs/comet_maddr/ cp /usr/local/meteor/public_html/* /usr/local/apache2/htdocs/comet_maddr/ cd /usr/local/apache2/htdocs/comet_maddr/
meteor.jsをカスタマイズ(callbackでチャンネル名を取得出来るように変更。およびURLの変更)
$ diff /usr/local/meteor/public_html/meteor.js /usr/local/apache2/htdocs/comet_maddr/meteor.js 78c78 < Meteor.loadFrame("http://"+Meteor.host+((Meteor.port==80)?"":":"+Meteor.port)+"/stream.html"); --- > Meteor.loadFrame("http://"+Meteor.host+((Meteor.port==80)?"":":"+Meteor.port)+"/comet_maddr/stream.html"); 84c84 < Meteor.loadFrame("http://"+Meteor.host+((Meteor.port==80)?"":":"+Meteor.port)+"/poll.html"); --- > Meteor.loadFrame("http://"+Meteor.host+((Meteor.port==80)?"":":"+Meteor.port)+"/comet_maddr/poll.html"); 180c180 < Meteor.callbacks["process"](data); --- > Meteor.callbacks["process"](data, channel); 244,245c244,245 < return function(args) { < f(a);g(args); --- > return function() { > f(a);g.apply(g, arguments);
String::Randomの配置
mkdir -p /usr/local/apache2/htdocs/comet_maddr/lib/String/ wget -O /usr/local/apache2/htdocs/comet_maddr/lib/String/Random.pm http://search.cpan.org/src/STEVE/String-Random-0.22/lib/String/Random.pm
メールアカウントの作成、転送設定、
# アカウント作成およびID確認 groupadd maddr useradd -g maddr -d /home/maddr maddr id maddr # users/assign設定(618:614の部分はuidとgidを指定) vi /var/qmail/users/assign --------------------------------------------------- =maddr:maddr:618:614:/home/maddr::: +maddr-:maddr:618:614:/home/maddr:-:: . --------------------------------------------------- /var/qmail/bin/qmail-newu # .qmail-defaultの作成 echo '|./push_maddr.pl' > ~maddr/.qmail-default chown maddr ~maddr/.qmail-default chmod og-wx ~maddr/.qmail-default
メールを受けた際にmeteordに通知するスクリプト作成
/home/maddr/push_maddr.pl: (モードを実行可にすること)
#!/usr/bin/perl use Socket; my $port = 4671; my $host = "127.0.0.1"; my $sender = $ENV{SENDER}; my $channelname = $ENV{EXT}; my $ipaddr = inet_aton($host); my $sockaddr = pack_sockaddr_in($port,$ipaddr); socket(SOCKET, PF_INET, SOCK_STREAM, 0) || die "socket error.\n"; connect(SOCKET,$sockaddr) || die "connect $host $port error.\n"; select((select(SOCKET), $|=1)[0]); printf SOCKET "ADDMESSAGE %s %s\n", $channelname, $sender; print SOCKET "QUIT\n"; shutdown(SOCKET, 1); while(my $line = <SOCKET>) { # print $line; } close(SOCKET);
サンプルフォーム生成CGIの作成
/usr/local/apache2/htdocs/comet_maddr/index.cgi:
#!/usr/bin/perl use lib lib; use String::Random; my $rnd = new String::Random; my $hostname = "terazzo.dyndns.org"; my $post_madd_prefix = "maddr-"; my $post_madd_suffix = "\@$hostname"; my $clientId = $rnd->randregex("[0-9]{10}"); my $channelname1 = $rnd->randregex("[A-Za-z0-9]{12}"); my $channelname2 = $rnd->randregex("[A-Za-z0-9]{12}"); my $post_madd1 = $post_madd_prefix.$channelname1.$post_madd_suffix; my $post_madd2 = $post_madd_prefix.$channelname2.$post_madd_suffix; print <<EOF Content-type:text/html; charset=Shift_JIS <html> <head> <title>Mail Address Test</title> <script type="text/javascript" src="http://$hostname/comet_maddr/meteor.js"></script> <script type="text/javascript"> function start_connection() { Meteor.hostid = "$clientId"; Meteor.host = "$hostname"; Meteor.registerEventCallback("process", function (address, channelname) { document.getElementById(channelname).value=address; } ); Meteor.joinChannel("$channelname1", 5); Meteor.joinChannel("$channelname2", 5); Meteor.mode = 'longpoll'; Meteor.connect(); } </script> </head> <body onload="start_connection()"> <form> Mail Address 1: <input type="text" size="32" id="$channelname1"> <a href="mailto:$post_madd1">Set...</a><br> Mail Address 2: <input type="text" size="32" id="$channelname2"> <a href="mailto:$post_madd2">Set...</a><br> </form> </body> </html> EOF
出力されたHTML(例:)
<html> <head> <title>Mail Address Test</title> <script type="text/javascript" src="http://terazzo.dyndns.org/comet_maddr/meteor.js"></script> <script type="text/javascript"> function start_connection() { Meteor.hostid = "7396487096"; Meteor.host = "terazzo.dyndns.org"; Meteor.registerEventCallback("process", function (address, channelname) { document.getElementById(channelname).value=address; } ); Meteor.joinChannel("RlfXx2vlWun3", 5); Meteor.joinChannel("8YA1QKTETbiq", 5); Meteor.mode = 'longpoll'; Meteor.connect(); } </script> </head> <body onload="start_connection()"> <form> Mail Address 1: <input type="text" size="32" id="RlfXx2vlWun3"> <a href="mailto:maddr-RlfXx2vlWun3@terazzo.dyndns.org">Set...</a><br> Mail Address 2: <input type="text" size="32" id="8YA1QKTETbiq"> <a href="mailto:maddr-8YA1QKTETbiq@terazzo.dyndns.org">Set...</a><br> </form> </body> </html>
考察その他
- 通常の空メールの場合、入力が容易という事の他に到達が保証出来るというメリットもあるけど、今回の方法ではヘッダ偽装が可能なので、到達保証の為には再度確認メール等が必要
- 今回は入力フィールドへの自動入力だけど、静的文字列+hiddenの方が良いかも
- mailtoを使っているので、普通にサブメールアドレスを入れることが出来ない(デモは間抜け)
- 携帯メールアドレス用にはQRコードとか出せばいいかも(追記: デモに入れてみた)
さらに追記: