POP3 クライアントを作ってみよう(2)

TOP >> ネットワークプログラミングの基礎知識 >> POP3 クライアントを作ってみよう(2)

(追記) (追記ここまで)
(追記) (追記ここまで) (追記) (追記ここまで)

POP3 クライアント

POP3 クライアントのサンプルプログラムです。 これも Socket モジュールを使っているので、perl5 専用です。

このプログラムは POP3 サーバに接続し、もしメールがあればそれを表示します。 メールが届いてなければ何も行いません。

pop3-client.pl

 1: #!/usr/local/bin/perl -w
 2: 
 3: # $Id: pop3-client.pl,v 1.2 2002年02月05日 17:53:10 68user Exp $
 4: 
 5: # POP3サーバ名、ユーザ名、パスワード
 6: $connect_host = 'pop3.foo.bar.com';
 7: $username = 'USERNAME';
 8: $passwd = 'PASSWORD';
 9: 
 10: use Socket; # Socket モジュールを使う
 11: 
 12: # プロトコルは POP3 を使う
 13: $port = getservbyname('pop3', 'tcp') || 110;
 14: 
 15: # ホスト名を、IPアドレスの構造体に変換
 16: $iaddr = inet_aton($connect_host)
 17: || die "$connect_hostは存在しないホストです。\n";
 18: 
 19: # port と IP アドレスをまとめて構造体に変換
 20: $sock_addr = pack_sockaddr_in($port, $iaddr);
 21: 
 22: # ソケット生成
 23: socket(SOCKET, PF_INET, SOCK_STREAM, 0)
 24: || die "ソケットを生成できません。\n";
 25: 
 26: connect(SOCKET, $sock_addr)
 27: || die "$connect_host のポート $port に接続できません。\n";
 28: 
 29: # ファイルハンドルSOCKETをバッファリングしない
 30: select(SOCKET); $|=1; select(STDOUT);
 31: 
 32: 
 33: # POP3サーバにユーザ名とパスワードを送る
 34: print SOCKET "USER $username\r\n";
 35: print SOCKET "PASS $passwd\r\n";
 36: 
 37: # メールの一覧を取得
 38: print SOCKET "LIST\r\n";
 39: 
 40: while (<SOCKET>){
 41: # 「.」のみの行が送られてきたらループを抜ける
 42: m/^\.\r\n$/ && last;
 43: 
 44: # 「メール番号 バイト数」という行なら
 45: if ( m/^(\d+) (\d+)\r\n$/ ){
 46: $num=1ドル;
 47: $size=2ドル;
 48: # メール番号を @mails に push
 49: push(@mails, $num);
 50: }
 51: }
 52: 
 53: # 各メールについて
 54: foreach ( @mails ){
 55: # メールの内容を送信するようリクエストを送る
 56: print SOCKET "RETR $_\r\n";
 57: 
 58: while (<SOCKET>){
 59: # 「.」のみの行が送られてきたらメールの終り。
 60: m/^\.\r\n$/ && last;
 61: 
 62: print $_;
 63: }
 64: }
 65: # サーバとの接続を切る
 66: print SOCKET "QUIT\r\n";

POP3 サーバへの接続

以下がサーバへの接続処理の部分ですが、 HTTP クライアントと違うのは、bold 表記のところだけです。
$connect_host = 'pop3.foo.bar.com';
$username = 'USERNAME';
$passwd = 'PASSWORD';
use Socket;
$port = getservbyname('pop3', 'tcp') || 110;
$iaddr = inet_aton($connect_host)
	|| die "$connect_hostは存在しないホストです。\n";
$sock_addr = sockaddr_in($port, $iaddr);
socket(SOCKET, PF_INET, SOCK_STREAM, 0)
	|| die "ソケットを生成できません。\n";
connect(SOCKET, $sock_addr)
	|| die "$connect_host のポート $port に接続できません。\n";
select(SOCKET); $|=1; select(STDOUT);
しかも最初の3行はサーバ名、ユーザ名、パスワードを変数に代入している だけなので、実質1行しか違わないことになります。

HTTP クライアントでは

$port = getservbyname('http', 'tcp');
だったのが、POP3 クライアントでは
$port = getservbyname('pop3', 'tcp') || 110;
となっただけですね。一見、
$port = getservbyname('pop3', 'tcp');
と書いてもいいように思えますが、/etc/services の書き方が OS によって異なるようで、FreeBSD の /etc/services は
pop3 110/tcp
と書いてありますが、Linux や Solaris の /etc/services には
pop-3 110/tcp
となっているようです。ですから、getservbyname('pop3', 'tcp') が 失敗したときには、ポート 110 を使うようにします。
well-known port 一覧 を見る限りでは、pop-3 でなく pop3 が適切なように思うのですが...。

ユーザ認証

 34: print SOCKET "USER $username\r\n";
 35: print SOCKET "PASS $passwd\r\n";
POP3 サーバにユーザ名とパスワードを送ります。当然ですが、 ここで間違ったパスワードを送ってしまうと、メールの受信はできません。

LISTコマンド

 37: # メールの一覧を取得
 38: print SOCKET "LIST\r\n";
 39: 
 40: while (<SOCKET>){
 41: # 「.」のみの行が送られてきたらループを抜ける
 42: m/^\.\r\n$/ && last;
 43: 
 44: # 「メール番号 バイト数」という行なら
 45: if ( m/^(\d+) (\d+)\r\n$/ ){
 46: $num=1ドル;
 47: $size=2ドル;
 48: # メール番号を @mails に push
 49: push(@mails, $num);
 50: }
 51: }
まず POP3 サーバに LIST コマンドを送ります。すると
+OK 6 messages (10211 octets)
1 1518
2 1612
3 1636
(中略)
.
という返事が返ってくるので、行頭の番号を配列 @mails に格納していきます。 LIST コマンドの応答は、「.」のみの行で終わるので、正規表現「^\.\r\n」 にマッチしたとき while ループを抜けます。

各メールの内容を受け取る

 53: # 各メールについて
 54: foreach ( @mails ){
 55: # メールの内容を送信するようリクエストを送る
 56: print SOCKET "RETR $_\r\n";
 57: 
 58: while (<SOCKET>){
 59: # 「.」のみの行が送られてきたらメールの終り。
 60: m/^\.\r\n$/ && last;
 61: 
 62: print $_;
 63: }
 64: }
配列 @mails には、メッセージ番号が入っているので、各番号について RETR コマンドを POP3 サーバに送ります。そしてサーバから返ってくる メールの内容を受け取り表示します。

こちらも同様に「.」のみの行を受け取るとループを抜けます。

(追記) (追記ここまで) (追記) (追記ここまで)

TOP >> ネットワークプログラミングの基礎知識 >> POP3 クライアントを作ってみよう(2)

ご意見・ご指摘は Twitter: @68user までお願いします。



AltStyle によって変換されたページ (->オリジナル) /