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






4.60/5 (4投票s)
一个用于检查多个 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 解码应用程序或使用在线服务进行解码。
参考文献
- NetManager V1.1,Petr Stejskal 著。
- Updated POP3 wrapper class,Robert E. Peters 著。
- MSDN
历史
- 04.2009 - 使用 Visual C++ 6.0 构建。
- 04.2011 - 升级到 Visual C++ 2008。