Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Email checking program with support for multiple POP accounts

4.60/5 (4 votes)
18 Apr 2011CPOL3 min read 28.5K   2.1K  
A program to check for email messages with multiple POP accounts.

Introduction

This application initially was based on an Open Source sample NetManager V1.1 by Petr Stejskal. I used it to explore both MFC and Internet/Networking (email in particular). Email clients nowadays are mostly replaced by Web based email services, but I think this code sample can be useful for programmers studying Internet protocols. This is a relatively simple MFC program to check for email messages on multiple POP accounts. The application was upgraded from Visual C++ 6.0 to VC++ 2008.

Background

To send email, the SMTP (Simple Mail Transfer Protocol) protocol is used. An email message consists of two parts: header and body (+ optional attachment). For ASCII based alphabets, the body (text) is sent non encoded (7-bit ASCII). For non-ASCII data (languages outside the Latin alphabet), encoding is used: the MIME specification lists two binary-to-text encoding schemes quoted-printable for text and base64 for binary attachments. Two protocols are mostly used to retrieve email from a remote server over a TCP/IP connection:

  • Internet message access protocol (IMAP);
  • Post Office Protocol (POP).

Email clients and mail servers usually support both protocols.

Using the code

By writing this code, I got some practice in MFC, but mostly explored the coding of Internet protocols. Thus, the code is a mix of MFC and C++ classes with some C and Win32 API functions. Every time, I simply used what I thought was best applicable for a task to be solved. For implementation of the POP3 protocol, I adapted the CPop class by Robert E. Peters that is an update from the POP3 protocol wrapper class written by Asif Rasheed. The code is well commented and should be clear to people with an MFC and Win32 background.

May be of some interest is a function to receive email. On a slow internet connection (such as dial-up), the transmission can get stuck with the program hanging. So, I added a bit of multithreading to the implementation to be able to interrupt the transmission (to close the socket) from the worker thread in such situations. Every time the transmission begins, you can stop it from the message box window shown. Here is the corresponding code (with some omissions):

C++
//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
    }

The application

The application has a very simple user friendly interface:

Image 1

The features of the email check program are limited by the purpose of the code sample:

  • It can read only ASCII (Latin alphabet) email messages from multiple POP accounts.
  • The messages are simply output in an Edit Control and a file.

Image 2

  • It can receive attachments but they have to be cut out of the message and decoded by an external base64 decoding application or using an online service.
  • Text in languages outside ASCII have to be cut out of the message and decoded by an external quoted printable decoding application or using an online service.

References

History

  • 04.2009 - Built in Visual C++ 6.0.
  • 04.2011 - Upgraded to Visual C++ 2008.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)