65.9K
CodeProject 正在变化。 阅读更多。
Home

支持多个 POP 账户的邮件检查程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (4投票s)

2011 年 4 月 18 日

CPOL

3分钟阅读

viewsIcon

28915

downloadIcon

2068

一个用于检查多个 POP 账户的电子邮件消息的程序。

引言

此应用程序最初基于 Petr Stejskal 的开源示例 NetManager V1.1。我使用它来探索 MFC 和互联网/网络(尤其是电子邮件)。 如今,电子邮件客户端大多被基于 Web 的电子邮件服务所取代,但我认为此代码示例对于学习互联网协议的程序员来说可能很有用。 这是一个相对简单的 MFC 程序,用于检查多个 POP 账户上的电子邮件消息。 该应用程序已从 Visual C++ 6.0 升级到 VC++ 2008。

背景

为了发送电子邮件,使用了 SMTP(简单邮件传输协议)协议。一封电子邮件消息由两部分组成:标题和正文(+ 可选附件)。对于基于 ASCII 的字母表,正文(文本)以非编码形式发送(7 位 ASCII)。对于非 ASCII 数据(拉丁字母表之外的语言),使用编码:MIME 规范列出了两种二进制到文本的编码方案,quoted-printable 用于文本,base64 用于二进制附件。通常使用两种协议通过 TCP/IP 连接从远程服务器检索电子邮件

  • 互联网消息访问协议 (IMAP);
  • 邮局协议 (POP)。

电子邮件客户端和邮件服务器通常都支持这两种协议。

使用代码

通过编写此代码,我在 MFC 方面获得了一些实践,但主要探索了互联网协议的编码。因此,该代码是 MFC 和 C++ 类的混合,包含一些 C 和 Win32 API 函数。每次,我只是使用了我认为最适用于要解决的任务的方法。为了实现 POP3 协议,我改编了 Robert E. Peters 的 CPop 类,该类是 Asif Rasheed 编写的 POP3 协议包装器类的更新版本。该代码注释良好,对于具有 MFC 和 Win32 背景的人来说应该很清晰。

可能比较有趣的是一个用于接收电子邮件的函数。在慢速互联网连接(例如拨号上网)上,传输可能会卡住,导致程序挂起。因此,我在实现中添加了一些多线程,以便能够在这种情况下从工作线程中断传输(关闭套接字)。每次传输开始时,您都可以从显示的提示框窗口中停止它。 这是相应的代码(省略了一些部分)

//Function to receive mail and write it in the file:
void CMailPop3::OnReceive() 
{
    //1. To have the new socket, create the instance of CThreadInfo class 
    //   (structure/union) where we have a member of CPop3Comm class.
    //   It is done to be able to pass a pointer to the socket in worker thread:
    CThreadInfo m_CThreadInfo;

    //2.Connect the POP3 server:
    int nItem = -1;
    while((nItem = m_Servers.GetNextItem(nItem, LVNI_SELECTED)) != -1)
    {
        //1. Autorization state:////////////////////////////////////////////
        m_CThreadInfo.m_Pop3Comm.SetHost(m_aAccounts[nItem].sServer);
        csMailAccount =m_aAccounts[nItem].sServer;
        m_CThreadInfo.m_Pop3Comm.SetPort(m_aAccounts[nItem].nPort);
        m_CThreadInfo.m_Pop3Comm.SetUser(m_aAccounts[nItem].sUsername);
        userName = m_aAccounts[nItem].sUsername;
        m_CThreadInfo.m_Pop3Comm.SetPassword(m_aAccounts[nItem].sPassword);
   
        if(!m_CThreadInfo.m_Pop3Comm.Connect())
        {
            AfxMessageBox(m_CThreadInfo.m_Pop3Comm.GetErrorMessage());
            return;
        }
        //Restore the condition variable if communication was interrupted by the user
        //(it is used later in this function when output message parameters):
        nSocketClosed=0;

        //Pass the pointer to the CThreadInfo class
        //(structure/union) to the worker thread:
        AfxBeginThread(ThreadProc, &m_CThreadInfo);
   
        //2.Transaction state:
        //2.1 Get number of mails (STAT)
        if(!m_CThreadInfo.m_Pop3Comm.Statistics())
        {
            AfxMessageBox(m_CThreadInfo.m_Pop3Comm.GetErrorMessage());
            return;
        }
        m_Mails.DeleteAllItems();//clear the Mails list
        //get the number of mails
        nMailNum = m_CThreadInfo.m_Pop3Comm.GetNumberOfMails();

        int nMsgSize;//variable to calculate the size of single message

        // Open file to write the received messages
        // (output and append data).
    
        //Get the name of server selected and create an output file:
        csFileName =csMailAccount + (".dat");
        ofstream outfile(csFileName,ios::out, ios::app);

        //Get the mails:
        for(nItem = 0; nItem <nMailNum;  nItem++)
        {
            //2.2.Get information regarding the size of a single message
            //or all the messages in a mail drop (LIST):
            m_CThreadInfo.m_Pop3Comm.List();

            if ((nMailNum % 2) == 0)//if even number of messages 
            {
                nMsgSize=m_CThreadInfo.m_Pop3Comm.GetMessageSize(nItem);
            }
            else//if odd number of messages 
                nMsgSize=m_CThreadInfo.m_Pop3Comm.GetMessageSize(nItem+1);

            //2.3 Display a given message's header (TOP).
            //List box is 0-based, but message's numbering is 1-based,
            //thus, add 1 to the list view first line:
            m_CThreadInfo.m_Pop3Comm.GetTop(nItem+1,nMsgSize); 

            //Output the server's name:
            m_Mails.InsertItem(nItem, csMailAccount);
            //Output mail fields (Attachment, From, Subject, Date):
            if (m_CThreadInfo.m_Pop3Comm.t_Attachment == "text/plain;")
            {
                m_CThreadInfo.m_Pop3Comm.t_Attachment = "";
            }
      
            if (nSocketClosed ==0)
            {
                m_Mails.SetItemText(nItem, 1, m_CThreadInfo.m_Pop3Comm.t_Attachment);
                m_Mails.SetItemText(nItem, 2, m_CThreadInfo.m_Pop3Comm.t_From);
                m_Mails.SetItemText(nItem, 3, m_CThreadInfo.m_Pop3Comm.t_Subject);
                m_Mails.SetItemText(nItem, 4, m_CThreadInfo.m_Pop3Comm.t_Date);
            }
            else//if communication is interrupted by the user
            {
                m_Mails.DeleteAllItems();//clear the Mails list box
            }

            //2.4 Retrieve a message from the host(RETR):
            m_CThreadInfo.m_Pop3Comm.Retrieve(nItem+1);

            //some code here...

            //Disconnect from the server:
            m_CThreadInfo.m_Pop3Comm.Disconnect();

            //If mail is received successfully, post message to the worker thread to close 
            //the socket controlling message box (for explanation on the following code 
            //see "Remarks" section in MSDN article on PostThreadMessage API):
            PostThreadMessage(nThreadID, WM_QUIT, 0, 0);
            //to create a message queue needed to handle PostThreadMessage:
            Sleep(0);
            PostThreadMessage(nThreadID, WM_QUIT, 0, 0);
        }//end of "while" loop
    }

应用程序

该应用程序具有一个非常简单、用户友好的界面

电子邮件检查程序的功能受代码示例的目的所限制

  • 它只能读取来自多个 POP 账户的 ASCII(拉丁字母表)电子邮件消息。
  • 消息只是在编辑控件和一个文件中输出。

  • 它可以接收附件,但它们必须从消息中剪切出来,并由外部的 base64 解码应用程序或使用在线服务进行解码。
  • ASCII 以外语言的文本必须从消息中剪切出来,并由外部的 quoted printable 解码应用程序或使用在线服务进行解码。

参考文献

历史

  • 04.2009 - 使用 Visual C++ 6.0 构建。
  • 04.2011 - 升级到 Visual C++ 2008。
© . All rights reserved.