In my last post I described how to build a simple POP3 client in C#. We implemented the standard POP3 commands like authenticating, receiving stats, retrieving and deleting messages. In this post I'll update the SimplePop3Client and implement some exception handling and pop3 session states.
POP3 session states
During a POP3 session there are the following different states:
-
Authorization
-
Transaction
-
Update
Authorization state
As soon as the connection to the mail server has been established, the session is in Authorization state. This is the state where the client is authenticating with the commands USER and PASS or APOP.
Transaction state
For all manipulations like retrieving stats, listing and retrieving emails and marking messages for deletion. If you try to use these commands when the session is not in transaction state, the server response will be "-ERR Invalid state".
Update state
As soon as the QUIT command is sent the session state is set to Update. That means that now the messages marked for deletion are really deleted. Afterwards the client is being disconnected from the mail server.
Implementing session states support
First we need a Pop3SessionState enumeration:
public enum Pop3SessionState
{
undefined = 0
Disconnected = 1
Authorization = 2
Update = 3
}
There are actually four places where the session state must be updated.
public string Connect()
{
// connect to the server
SetSessionState(Pop3SessionState.Authorization);
}
public string PASS()
{
//...
// if PASS was successful update session state
SetSessionState(Pop3SessionState.Transaction);
}
public string APOP()
{
//...
// if APOP was successful update session state
SetSessionState(Pop3SessionState.Transaction);
}
public string QUIT()
{
SetSessionState(Pop3SessionState.Update);
// send QUIT command here
SetSessionState(Pop3SessionState.Disconnected);
}
For updating the session state I use a function (SetSessionState) thus it's easier to implement session state update events if needed.
As we know each command needs a specific state. So before sending a command we can now test if the current state is adequate. For this purpose we use the function EnsureStateSession:
private bool EnsureSessionState(string command)
{
switch (command.Substring(0, 4).ToLower())
{
case "user":
case "pass":
case "apop":
return (this.State == Pop3SessionState.Authorization);
default:
return (this.State == Pop3SessionState.Transaction);
}
}
Some other improvements
Exception handling
Dealing with network connection there are pretty much potential for errors. Thus I updated the Pop3Client with some exception handling and the custom exception Pop3Exception.
public class Pop3Exception : Exception
{
public Pop3Exception() { }
public Pop3Exception(string message) : base(message) { }
public Pop3Exception(string message, Exception innerException) : base(message, innerException) { }
}
SSL connection
Implementing the possibility to use a SSL connection is really easy:
public string Connect()
{
//...
client = new TcpClient(this.Host, this.Port);
// connect to the client
client = new TcpClient(this.Host, this.Port)
if (this.UseSSL)
{
try
{
// get ssl stream
stream = new SslStream(client.GetStream(), false);
// authenticate
((SslStream)stream).AuthenticateAsClient(this.Host);
}
catch (Exception e)
{
throw new Pop3Exception(String.Concat("Host found but SSL authentication failed. May your mail server does not support SSL.", Environment.NewLine, "Error: ", e.Message), e.InnerException);
}
}
else
{
stream = client.GetStream();
}
//...
}
Updated code
Download the Pop3Client Solution: Pop3Client.zip (7.20 kb) (Yes, I renamed it from SimplePop3Client since it's that simple any more ;-)
28f8bf7d-dc8c-4be3-b46c-7a4e8ebd6172|5|4.2