POP3 Client almost complete


The test driven development of the POP3 client code is almost complete. The development proceeded in a similar manner to the server code and I'm left with the same thing to write; the message store...

I was quite pleased that the entire client development could be done without needing to connect to a real POP3 server at all. In fact, up until last night, I didn't have any code that the client could use to talk over sockets.

The client uses two interfaces to read and write to and from the server. The interfaces are pretty simple, they just contain a Write() method and a ReadLine() function. The client is written in terms of these interfaces and the test harnesses were written using a simple connector class to connect the client to the server.

After writing the client I wrote a simple "Mail collector" class that takes a client and a message store, obtains a mailbox in the message store, connects the client to the server and retrieves each mail and stores it in the mailbox. Again this could be written and tested by building on top of our existing test code.

So the time had finally come to connect to a real mail server. Main looked a bit like this:

CUsesWinsock usesWinsock;
   CSocketConnection connection;
   connection.Connect("mail.mydomain.com", 110);
   CNullMessageStore messageStore;
   CSynchronousPOP3MailCollector collector(connection, messageStore, 1);
   const string username = "username";
   const string password = "password";
   CSynchronousPOP3MailCollector::CollectionResult result = collector.CollectMail(username, password, false);
   cout << "Downloaded " << result.numMessages << " messages (" << result.totalSize << " bytes)" << endl;
catch(CException &e)

The CNullMessageStore implements a message store that accepts and ignores new messages. The CSocketConnection implements the input and output interfaces that the client requires in terms of sockets.

So, now I need to write a message store that lets me save these mail messages to disk. Once that's done I can get on with the meat of this project, the filter itself. This will read messages from one mailbox, apply filtering rules of some sort and then place messages in another mailbox. The idea being that the client pulls messages into a mailbox, the filter does its thing and the messages can then be retrieved via the server... Once that works we'll look at making the client code operate in a 'push' async manner so that we can package the client and server in the same Windows Service and have the client use the same sockets infrastructure as the server. The sync version of the mail collect's 'collect mail' function has given us the structure required by the async client's state machine;

   CollectionResult result;   
   CSmartUpdateMailbox mailbox(m_messageStore, username);
   CPOP3Client::AuthenticationMethod method = m_client.Authenticate(username, password);
   if (method != CPOP3Client::None)
      CPOP3Client::StatResult stat = m_client.STAT();
      for (size_t i = 0; i < stat.numMessages; ++i)
         const CPOP3Client::MessageIndex index = i + 1;
         const string message = m_client.RETR(index);
         if (deleteAfterCollection)
      result.numMessages = stat.numMessages;
      result.totalSize = stat.totalSize;
      throw CException(_T("CSynchronousPOP3MailCollector::CollectMail()"), _T("Failed to authenticate with server"));
   return result;

Leave a comment