myamanda

博客园 首页 新随笔 联系 订阅 管理

    一、POP3协议简介

    POP3服务中包含三个阶段,它们是"身份验证"(Authorization)、"事务处理"(Transaction)和"更新"(Update)。首先,客户和服务器建立一个TCP连接(RFC 1939规定POP3服务器应该在110端口监听),服务器会发回一条欢迎信息。之后,POP3服务就进入了"身份验"阶段。RFC 1939定义了两种身份验证的方法:USER和PASS命令,以及APOP命令;目前大多数POP3服务器使用USER和PASS命令来检验用户身份的真伪性;APOP方式则是对口令进行了加密,故而安全性有所增强。如果客户方通过了服务器的身份验证,POP3服务便进入了Transaction阶段。POP3服务器将锁住相应的邮箱以便对其进行互斥访问,这样可以确保在此Transaction中,邮箱中的邮件不会被修改和增删。在Transaction阶段,客户可以向POP3服务器以任意的顺序发送以下的命令:STAT(获取邮箱信息,可用来获取邮件的数目),LIST(获取邮件的信息),RETR(取某个邮件),DELE(给某个邮件加上删除标志,实际的删除操作在Update阶段进行),NOOP(空命令,不做任何事)和RSET(将所有邮件的删除标志清除)。如果客户程序在Transaction阶段向服务器发送QUIT命令,那么就会进入Update阶段,此时服务器将删除所有在Transaction阶段用DELE命令加上删除标志的邮件。不管删除操作成功与否,服务器都会关闭TCP连接,并将邮箱解锁。

    二、登录POP3服务器

    为了从POP3服务器上收取E-mail,程序的第一步便是登录到POP3服务器。为此,我们首先来实现一个名为POP3Login的函数,该函数的原型为:function POP3Login(Host, User, Password:String;Port:Integer=110):Integer;其中,Host为POP3服务器的主机名或者它的IP地址,User为用户名,Password为该用户的密码,Port为端口号(缺省值为110)。函数POP3Login将与POP3服务器在Port端口建立TCP连接,之后便向服务器发送USER和PASS命令。如果登录成功(即PASS命令通过),则返回与POP3服务器之间的Socket描述符;否则,关闭TCP连接,并返回INVALID_SOCKET,以表示登录失败。下面是POP3Login函数的实现。

    function POP3Login(Host, User, Password:String;

    Port:Integer=110):Integer;

    var

    Sockfd:Integer;

    begin

    Result:=INVALID_SOCKET;

    Sockfd:=CreateClientSocket(Host, Port);

    if (Sockfd=INVALID_SOCKET)

     or not POP3Response(sockfd)

    then begin

    CloseSocket(Sockfd);

    MessageBox(0, ‘Cannot connect to server', nil,MB_ICONERROR);

    Exit;

     end;

    //发送USER命令

    Write_Socket(sockfd,‘USER '+User+#13#10);

    if not POP3Response(sockfd)

    then begin

    CloseSocket(sockfd);

    MessageBox(0, ‘USER failed', nil,MB_ICONERROR);

    Exit;

     end;

    //发送PASS命令

    Write_Socket(sockfd,‘PASS '+Password+#13#10);

    if not POP3Response(sockfd)

    then begin

    CloseSocket(sockfd);

    MessageBox(0, ‘PASS failed', nil,MB_ICONERROR);

   Exit;

     end;

    Result:=Sockfd;

    end;

    三、收取邮件

    接下来,我们来编写一个名叫POP3RetriveMail的函数,调用此函数就可以从POP3服务器下载邮件。POP3RetriveMail函数的原型为:function POP3RetriveMail(Host, User, Password:String;DeleteMail:Bool; Port:Integer=110):Integer;功能描述:从POP3服务器上收取电子邮件。收取的邮件将保存在当前目录的MailBox子目录下,返回值等于信箱中邮件的数目。 参数说明:Host为POP3主机名或者其IP地址,User和Password分别为用户名和口令,而布尔参数DeleteMail指示函数在收取邮件后是否将其从服务器上删除。由于POP3服务通常在110端口,我们将参数Port定义成缺省值为110的缺省参数(default parameter)。

    function POP3RetriveMail(Host, User,

    Password:String;DeleteMail:Bool;

    Port:Integer=110):Integer;

    var

    sockfd,i:integer;

    hFile, c:THandle;

    S, T:String;

    begin

    Result:=0;

    Sockfd:=POP3Login(Host, User, Password, Port);

    if Sockfd=INVALID_SOCKET then Exit;

    //Get the number of mails in the maildrop

    //POP3服务器给STAT命令的返回信息的格式为:

    //+OK 邮件数目 所有邮件总的字节数

    Write_Socket(sockfd,‘STAT'#13#10);

    S:=Socket_readline(sockfd);

    if UpperCase(Copy(S,1,3))<>‘+OK'

    then begin //error occurred

    CloseSocket(sockfd);

    Exit;

    end;

    S:=Copy(S,5,Length(S)-4);

    i:=Pos(‘',S);

    S:=Copy(S,1,i-1);

    Result:=StrToIntDef(S,0);

    for i:=1 to Result do

    begin

    //收取第i封邮件

    S:=‘RETR '+IntToStr(i)+#13#10;

    Write_Socket(sockfd,S);

    //如果RETR的返回信息的第一行是+OK的话,

    //接下来的便是邮件的内容,邮件内容都是一行行的ASCII字符串。

    //当遇到一个只包含一个"."的行时,意味着邮件内容已经结束。

    if not POP3Response(sockfd) then continue;

    S:=GetUniqueFileName; //自定义函数

    //创建一个文件,用于保存邮件

    hFile:=CreateFile(PChar(s), GENERIC_WRITE,

    FILE_SHARE_READ, nil, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL, 0);

    SetFilePointer(hFile,0,nil,FILE_BEGIN);

    T:=Socket_ReadLine(sockfd);

    While T<>‘.' do

    begin

    T:=T+#13#10;

    WriteFile(hFile,PChar(T)^,Length(T),C,nil);

    T:=Socket_ReadLine(sockfd);

    end;//while

    CloseHandle(hFile);

    //如果参数DeleteMail为TRUE则从服务器上删除此邮件

    if DeleteMail then Write_Socket(Sockfd,‘DELE '+

    IntToStr(i)+#13#10);

    end; //for

    //发送QUIT命令,使已进行了的DELE操作生效

    Write_Socket(sockfd,‘QUIT'#13#10);

    //关闭Socket(释放Socket描述符)

    Closesocket(sockfd);

    end;

    四、其他函数的的实现

    在以上的POP3Login函数和POP3RetriveMail函数中我们调用了一些自定义函数,下面是它们的实现代码。

    * CreateClientSocket函数

    function

    CreateClientSocket(Host:string;Port:integer):Integer;

    //功能:与指定的主机Host建立一个TCP连接,使用Port端口。

    //返回值:如果成功返回一个Socket描述符;否则返回

    INVALID_SOCKET。

    var

    i:integer;p:^LongInt;

    phe:pHostEnt;

    sin:sockaddr_in;

    begin

    Result:=INVALID_SOCKET;

    sin.sin_family:=AF_INET;

    sin.sin_port:=htons(Port);

    //将主机名转换为32位的IP

    phe:=gethostbyname(pchar(host));

    if phe<>nil

    then begin

     p:=Pointer(phe^.h_addr_list^);

     sin.sin_addr.s_addr:=p^;

     end

    else begin

     i:=inet_addr(PChar(Host));

     if i<>-1

     then sin.sin_addr.S_addr:=i

     else begin

     //无法获取主机Host的IP

    MessageBox(0, pChar(‘Cannot resolve '+Host), nil, MB_ICONERROR);

    Exit;

     end;

     end;

    //创建一个面向连接的字节流Socket

    Result:=socket(PF_INET,SOCK_STREAM,0);

    if (Result=INVALID_SOCKET)

    then begin

    MessageBox(0,‘socket() failed', nil,MB_ICONERROR);

    Exit;

    end;

    //使用此Socket描述符与远处的主机建立一个TCP连接

    if Connect(Result,sin,sizeof(sin))=SOCKET_ERROR

    then begin

    MessageBox(0,‘connect() failed', nil,MB_ICONERROR);

     closesocket(Result);

    Result:=INVALID_SOCKET;

     end;

    end;

    * POP3Response函数

    function POP3Response(Sockfd:Integer):Bool;

    //功能:检查POP3服务器返回的状态信息。

    //返回值:如果是+OK,则返回TRUE;否则返回FALSE。

    var

    S: string;

    begin

    S:=socket_readline(sockfd);

    if copy(s,1,3)=‘+OK' then Result:=True

    else Result:=False;

    end;

    * Write_Socket函数

    function Write_Socket(sockfd:TSocket; const s:string):Integer;

    //功能:将字符串S写入sockfd

    begin

    Result:=Send(sockfd,pointer(s)^,Length(s),0)

    end;

    * Socket_Readline函数

    function Socket_Readline(sockfd:Integer):String;

    //功能:从sockfd中读取一行(即,直至遇到换行符)。

    //返回值:返回从sockfd中所读取的一行字符。

    var

    S:String; buf:array[0..1]of Char;

    n:Cardinal;

    begin

    buf[0]:=#0;buf[1]:=#0; S:=‘';

    n:=recv(sockfd,Buf,1,0);

    while n>0 do begin

     buf[1]:=#0;

    S:=S+buf;

     if (buf[0]=#10) then Break;

     n:=recv(sockfd, buf, 1, 0);

     end;

    Result:=Trim(S);

    end;

    示例程序

    下面是一个简单的示例程序,它调用POP3RetriveMail函数收取263.net上simon_liu的邮件。

    Program GetMail;

    uses WinSock, Windows, SysUtils,

    POP3 in ‘POP3.pas';

    var

    w:TWSADATA; I:integer; S:String;

    begin

    //初始化Windows Sockets动态连接库

    if WSAStartup(2,w)<>0

    then begin

    MessageBox(0,‘Initialization failed!',‘WinSock DLL Error',MB_ICONERROR

);

    Halt;

     end;

    i:=POP3RetriveMail(‘263.net', ‘simon_liu', ‘mypass', TRUE, 110);

    //或者:i:=POP3RetriveMail(‘263.net', ‘simon_liu', ‘mypass', TRUE, 110

);

    if i=0 then MessageBox(0,‘No Message!',‘GetMail',MB_ICONINFORMATION)

    else begin

    S:=IntToStr(i)+‘messages!';

    MessageBox(0,PChar(S),‘New

    Message(s)!',MB_ICONINFORMATION);

     end;

    end.

    说明:以上代码均使用Delphi 5编写;调试环境:Windows 98/Windows 2000。

posted on 2010-01-27 10:10  myamanda  阅读(390)  评论(0编辑  收藏  举报