POPはIETFにより標準化されている電子メールの受信プロトコルです。 現在はPOPのバージョン3、通称POP3が広く利用されています。ここではこの POP3の動作を説明した後に、POP3を利用したメール受信プログラムについて 解説します。
電子メールはメールサーバ間をSMTPを使って転送されます。従って、メールの 受け取り側もSMTPを使って受信すれば簡単です。しかし、受け取り側は常時接 続されているは限りません。例えば電話回線により接続されている場合もあり ます。従って、届いたメールはメールサーバ上で一時的に保持してもらい、必 要に応じて、そのメールを取り出す機構が必要となりました。それがPOPです。
POPによるメール受け取りでは、POPに対応したサーバとクライアントのあ いだで行われます。両者はTCPを利用して通信を行い、その際のポート番号は 110を利用するのが一般的です。クライアントはサーバにメールの到着を問い 合わせます。そして、メールが届いているときはそのメールを読み出します。 POPとはこのときのクライアントとサーバのあいだの通信手順を定めたもので す。
POPの手順はクライアントからサーバにメッセージを送り、サーバへのログ インを行います。そして、サーバにメールが届いているかを調べます。そして、 必要に応じてメールを取り出します。このとき、クライアントからサーバへの メッセージはUSER、PASS、STAT、LIST、 RETRなどを含む40文字以下の文字列であり、それに対するサーバの 返答は成功、不成功に応じて+OK、+ERRから始める文字列 となります。どちらも文字列の終わりは<CRLF>となります。
POP3の動作はメール呼び出しが正当なものかどうかを調べる認証と、 その後に行われるメール呼び出しの二つステップから構成されています。
メールは他人に読まれては困ります。このため、メールサーバはメール呼 び出す要求があったときに、そのメールの受取人からの呼び出しであるかを調 べる必要があります。POP3ではログインと同じで秘密のパスワードを使って判 定します。つまり、メールを呼び出す前にPOPクライアントはユーザ名とパス ワードをPOPサーバに伝えます。そして、ユーザ名とパスワードが一致したら POPサーバは、POPクライアントによるメール呼び出し要求に従いメールをPOP クライアントに送ります。
このPOP3の認証方法として、 USERとPASSコマンドを使う方法で説明していきます。
POPクライアントはTCPによりPOPサーバに接続します。そして、POPクライ アントはUSERコマンドによりメールアカウント名(ログイン名)をPOP サーバにおくります。
USER メールアカウント名<CRLF> |
POPサーバは返答として+OK <CRLF>というメッセージを送り返 します。なお、メールサーバ(POPサーバ)にはメールボックスがあり、POPサー バ届いたメールはこのメールボックスがためられています。しかし、メールア カウント名に対応したメールボックスがなければPOPサーバはメールを返しよ うがありません。この場合は+OK <CRLF>ではなく+ERR <CRLF>を送り返し、USERを受け取る前の状態に戻ります。
POPクライアントは+OK <CRLF>を受け取るとパスワードを送ります。
PASS パスワード<CRLF> |
POPサーバは、パスワードが正しくないときは+ERR <CRLF>を 送り返し、USERを受け取る前の状態に戻ります。正しいときは +OK <CRLF>を返します。これでPOPサーバはPOPクライアントからの メール呼び出し要求を受理するようになります。
クライアント | USER name |
サーバ | +OK |
クライアント | PASS password |
サーバ | +OK |
USERとPASSコマンドを利用した認証はパスワードがそのま まネットワークを通ることになります。従って、第3者に読まれてしまう危険 性をもっています。特に、新着メールの確認は所定時間毎に自動で行うことが 多いですが、その場合はパスワードが盗まれる可能性は高くなります。
従って、安全な認証方法が必要になり、それを実現するのがAPOPコ マンドです。これは暗号技術を利用したものであり、POPサーバとPOPクライア ント双方が共有している暗号鍵をもとに、使い捨てのパスワードを作り、それ をPOPサーバに送るという方法です。
コラム:暗号とは
コラム:UNIXの暗号技術
コラム:共通鍵暗号系と公開鍵暗号系
POPクライアントはSTAT、LIST、RETR、 DELEコマンドをPOPサーバに送ってメールを呼び出しを行います。こ れらのコマンドは認証されたPOPクライアントから以外から送っても、POPサー バはコマンドを実行してくれません。
メールボックス内のメール数と全体データ量(バイト数)の 問い合わせるコマンドです。
STAT<CRLF> |
POPサーバからの返答メッセージは以下のようになります。+OK、 nn、mmは一個のスペースで区切られ、nnはメー ルボックスにたまっているメールの数、mmはそれらのメールの合計 データ量(バイト数)となります。mmの後に付加情報を追加すること もできますが、その返答メッセージの終わりは<CRLF>となる 必要があります。
+OK nn mm<CRLF> |
例をあげます。これはメールボックスに2つのメールがあり、 それらの合計バイト数が320の場合です。
クライアント | STAT<CRLF> |
サーバ | +OK 2 320<CRLF> |
STATコマンドと同様に認証が完了していないと使えないコマンドで す。このLISTコマンドはメールボックス中の各メールのデータサイ ズを調べるコマンドです。
LIST<CRLF> |
特定のメールのデータサイズを調べるときは、以下のようにLISTコ マンドのつぎに一個のスペースをあけて、そのメールの番号を引数として与え ます。
LIST nn<CRLF> |
POPサーバは認証済みのPOPクライアントから、この コマンドを受け取ると、複数行の返答メッセージを返します。一行目は +OK<CRLF>となります。二行目以降は各メールのデータサイズ をnn mm<CRLF>の形式で返します。ここでnnはメー ルの番号、mmはそのメールのデータサイズ(バイト数)です。 そして、最後の行はメール情報の終わりを示すため.<CRLF>で 終わります。なお、メールボックスにメールがないときは +OK<CRLF>のあとに.<CRLF>を返します。
+OK<CRLF> | |
nn mm<CRLF> | (メール数分) | .<CRLF> |
例をあげます。これはメールボックスに2つのメールがあり、 それらの合計バイト数が320の場合です。
クライアント | LIST<CRLF> |
サーバ | +OK 2 messages<CRLF> |
サーバ | 1 120<CRLF> |
サーバ | 2 200<CRLF> |
サーバ | .<CRLF> |
特定のメールを調べるときは以下のようになります。
クライアント | LIST 2<CRLF> |
サーバ | +OK 2 200<CRLF> |
クライアント | LIST 3<CRLF> |
サーバ | -ERR no such message, only 2 messages in maildrop<CRLF> |
RETRコマンドはメールを呼び出すコマンドです。ただし、認証済み のPOPクライアントからのRETRコマンドだけが有効です。 以下のようにRETRコマンドのつぎに一個のスペースをあけて、 読み出したいメールの番号nnを引数として与えます。
RETR nn<CRLF> |
POPサーバは認証済みのPOPクライアントから、RETRコマンドを受け 取ると、複数行の返答メッセージを返します。最初の行は +OK<CRLF>となります。二行目以降はメールの中身となります が、最後の行にはメール情報の終わりを示すため.<CRLF>とな ります。なお、メールボックスに該当のメールがないときは +OK<CRLF>のあとに.<CRLF>を返します。
例をみていきましょう。
クライアント | RETR 1<CRLF> |
サーバ | +OK 120<CRLF> |
<メールの中身> | .<CRLF> |
未完
ここではメール呼び出しにおける、POPクライアントとPOPサーバの メッセージのやりとりを時間順に見てみます。
サーバ | <wait for connection on TCP port 110> |
クライアント | <open connection> |
サーバ | +OK POP3 server ready <1896.697170952@dbc.mtview.ca.us> |
クライアント | APOP mrose c4c9334bac560ecc979e58001b3e22fb |
サーバ | +OK mrose's maildrop has 2 messages (320 octets) |
クライアント | STAT |
サーバ | +OK 2 320 |
クライアント | LIST |
サーバ | +OK 2 messages (320 octets) |
サーバ | 1 120 |
サーバ | 2 200 |
サーバ | . |
クライアント | RETR 1 |
サーバ | +OK 120 octets |
サーバ | <the POP3 server sends message 1> |
サーバ | . |
クライアント | DELE 1 |
サーバ | +OK message 1 deleted |
クライアント | RETR 2 |
サーバ | +OK 200 octets |
サーバ | <the POP3 server sends message 2> |
サーバ | . |
クライアント | DELE 2 |
サーバ | +OK message 2 deleted |
クライアント | QUIT |
サーバ | +OK dewey POP3 server signing off (maildrop empty) |
クライアント | <close connection> |
サーバ | <wait for next connection> |
POPの例題として、POPクライアントプログラムを示します。これは第一引 数として与えたPOPサーバから、第二引数のメールアカウント名と第三引数の パスワードにより認証を行い、そのメールアカウント宛に届いたメールをすべ て呼び出して画面に表示するものです。
通信接続部分は他のTCPクラ イアントプログラムと同じであり、重要なのはPOPによるメッセージ手順の部 分です。そのプログラム例を下記にあげます(下記のプログラムはJava言語版 です。これをC言語に直すだけです。あと、最初のメールしか呼び出してくれ ません)。
import java.io.*;
import java.lang.*;
import java.net.*;
import java.util.*;
public class POP3 {
static final int DEFAULT_PORT = 110;
static final String CRLF = "\r\n";
String user;
String password;
Socket sock = null;
BufferedReader input = null;
DataOutputStream output = null;
public static void main(String[] args) {
if( args.length != 3 ) {
System.out.println(
"Usage: java POP3 pop-server user password");
}
else {
new POP3(args[0], args[1], args[2]);
}
}
public POP3(String popserver, String user, String password) {
this.user = user;
this.password = password;
try {
connect(popserver, DEFAULT_PORT);
login();
retract();
close();
}
catch (ProtocolException e) {
}
}
public void connect(String popserver, int port) throws ProtocolException {
try {
sock = new Socket(popserver, port);
input = new BufferedReader(new InputStreamReader(sock.getInputStream()));
output = new DataOutputStream(sock.getOutputStream());
}
catch (IOException e) {
throw new ProtocolException("Can't connect POP3-server");
}
}
public void login() throws ProtocolException {
try{
output.writeBytes("USER " + user + CRLF);
if(responseError(input.readLine())) {
throw new ProtocolException("Login denied");
}
output.writeBytes("PASS " + password + CRLF);
if(responseError(input.readLine())) {
throw new ProtocolException("Login denied");
}
}
catch(IOException e) {
throw new ProtocolException("Can't communicate POP3-server") ;
}
}
public void retract() throws ProtocolException {
try{
output.writeBytes("STAT" + CRLF) ;
output.flush();
String line = input.readLine();
if(!responseError(line)) {
StringTokenizer st = new StringTokenizer(line);
st.nextToken();
String number = null;
output.writeBytes("RETR 1" + CRLF) ;
output.flush();
String buf = null;
while((buf = input.readLine()) != null) {
System.out.println(buf);
}
}
}
catch(IOException e) {}
throw new ProtocolException("Can't communicate POP3-server");
}
public void close() throws ProtocolException {
try {
output.writeBytes("QUIT" + CRLF) ;
output.flush();
sock.close();
}
catch(IOException e) {
throw new ProtocolException("Can't communicate POP3-server");
}
}
private boolean responseError(String s) throws IOException {
StringTokenizer st = new StringTokenizer(s) ;
String stf = st.nextToken() ;
return stf.equals("-ERR") ;
}
}
プログラム中で重要な部分をみていきます。
下記の部分は認証を行う部分です。userにはメールアカウント名、 passwordにはパスワードが格納されています。
public void login() throws ProtocolException {
try{
output.writeBytes("USER " + user + CRLF);
if(responseError(input.readLine())) {
throw new ProtocolException("Login denied");
}
output.writeBytes("PASS " + password + CRLF);
if(responseError(input.readLine())) {
throw new ProtocolException("Login denied");
}
}
catch(IOException e) {
e.printStackTrace();
}
}
下記はメールの呼び出し部分(未完)
public void retract() throws ProtocolException {
try{
output.writeBytes("STAT" + CRLF) ;
output.flush();
String line = input.readLine();
if(!responseError(line)) {
StringTokenizer st = new StringTokenizer(line);
st.nextToken();
String number = null;
output.writeBytes("RETR 1" + CRLF) ;
output.flush();
String buf = null;
while((buf = input.readLine()) != null) {
System.out.println(buf);
}
}
}
catch(IOException e) {}
throw new ProtocolException("Can't communicate POP3-server");
}