A POP 3 Class
Written by Mike James   
Sunday, 14 March 2010
Article Index
A POP 3 Class
Log on and beyond
Headers

Getting a head(er)

Once you know how many emails there are waiting you can use the TOP command to get the header of any of them.

This is not a difficult function to write but it is our first encounter with POP3 multi-line protocols. When the server sends a multi-line response it marks the end of it with a “.” followed by a carriage return, line feed pair.

You might think that all you have to do is send the TOP command and wait for the post office to send all the headers back. The complication is that it is possible that the post office will not send all of the headers in a single block of data.

In other words, you might well start processing a partial list of headers. To make sure that this doesn’t happen you have to test the end of the data received for a “.” at the end of a line. If you don’t find this then you need to wait for the completion of the data transmission and add the next block of incoming data to the temporary buffer.

The only refinement that this routine might need is to reset the timeout after each block has been received. Alternatively if this becomes a problem in practice the client could just increase the timeout before retrieving headers. Notice that the headers are returned as one long string ready for further processing.

public string getHeader(int index)
{
Send("TOP " +
index.ToString() + " 1");
if (!WaitFor("+OK")) return "";
StringBuilder temp = new
StringBuilder(reply);
while(!reply.Contains("\r\n.\r\n"))
{
WaitFor("");
temp.Append(reply);
};
return temp.ToString();
}

The same trick of converting the reply string into a StringBuilder so that it can be built up efficiently is used. You might be wondering what the WaitFor("") is doing in the while loop. This simply reads the next block of data from the server and as we don't care what the block starts with the target string is set to the null string.

The same technique can be used to retrieve an entire email because it too is terminated by a “.” all on its own on a line. 

If you are wondering what happens if a line in the message body is just a dot at the start of the line the answer is – byte stuffing. Any dots on line of there own have a single space character added and which needs to be removed when you retieve and process the body of the message.

public string getEmail(int index)
{
Send("RETR " + index.ToString());
if(!WaitFor("+OK")) return "";
StringBuilder temp=
new StringBuilder(reply);
while(!reply.Contains("\r\n.\r\n"))
{
WaitFor("");
temp.Append(reply);
};
return temp.ToString();
}

The big problem with just downloading an email in this way is that it could be a bit on the big size if there are any file attachments included.

This could mean that you end up trying to store MBytes in RAM as a huge string. The full solution to this problem is to change the routine so that it always writes the incoming data to disk and passes the email on to the client as an unprocessed text file.

In this case a simpler solution is just not to download anything that is too big! To allow the client discover the size of an email without downloading it we can use the LIST command and the getNum function.

public int getSize(int index)
{
 Send("LIST " + index.ToString());
 if (!WaitFor("+OK")) return 0;
 getNum(ref reply);
 return getNum(ref reply);
}

Before you decide that the two calls to getNum  are a mistake it is worth pointing out that the size of the email is the second number returned and two calls are indeed necessary. The first value returned by getNum is simply an echo back of the email number.

Dumping the post

You can mark any email for delete by specifying its number and using the DELE command -

public bool delete(int index)
{
 Send("DELE "+index.ToString());
 if (!WaitFor("+OK")) return false;
 return true;
}

Notice that emails are only marked as deleted not actually removed from the mailbox by this command.

Even so you can’t manipulate emails marked as deleted. For example, if you try to get the size of an email after marking it for deletion then an error will be generated.

Also notice that marking an email for deletion doesn’t change the numbering of the emails.

If you want to actually delete all the emails that you have marked then you have to use the Quit command which also closes the connection to the post office. As long as the post office can delete the emails it returns an “+OK” and the next time you connect the marked emails are gone.

 

public bool Quit()
{
 Send("QUIT");
 if (!WaitFor("+OK")) return false;
 popTCP.Close();
 return true;
}

If you don’t want to delete the emails that you’ve marked – perhaps because an error occurred then you can use the Abort method -

public bool Abort()
{
 Send("RSET");
 if (!WaitFor("+OK")) return false;
 popTCP.Close();
 return true;
}

This closes the connection without implementing any deletes and this is the last of our methods – now to use the class.

Place a button on the form and into its click event hander add:

 POP3 pop = new POP3();
pop.user = "name";
pop.password = "password";
pop.URL = "URL";
pop.LogOn();
Console.WriteLine(
pop.getNumberOfEmails().ToString());
if (pop.getNumberOfEmails() > 0)
{
Console.WriteLine(pop.getHeader(1));
Console.WriteLine(pop.getEmail(1));
}
pop.Quit();

If you run the program you should discover that, as long as there is at least one email waiting, you can see its headers and the complete email.

To access the code for this project, once you have registered,  click on CodeBin.


The Minimum Spanning Tree In C# - Prim's or Dijkstra Algorithm

Finding the minimum spanning tree is one of the fundamental algorithms and it is important in computer science and practical programming. We take a look at the theory and the practice. 



SNTP Time Class

SNTP is a network protocol for obtaining an accurate time and it is an interesting exercise to build an SNTP client. In this article the language used is C# but it is easy enough to generalise to a la [ ... ]


Other Projects

<ASIN:0672321149>

<ASIN:1593271778>

<ASIN:0672322374>

<ASIN:0471345970>

<ASIN:1904811124>



Last Updated ( Tuesday, 16 March 2010 )