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

前へ << POP3 クライアントを作ってみよう(1) POP3 クライアントを作ってみよう(3) >> 次へ

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 サーバに送ります。そしてサーバから返ってくる メールの内容を受け取り表示します。

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

前へ << POP3 クライアントを作ってみよう(1) POP3 クライアントを作ってみよう(3) >> 次へ

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