Check Your Email With Style
Have you ever wanted to write your own POP3 email client? Well now you can with
the help of my C# example. This article explains how to use .NET to connect to
a POP3 mail server, and the different commands to use with the server to get
the results you want.
Connecting
To connect to another machine on the Internet like a mail server, you will need
a TCP socket. Using the System.Net.Sockets collection
of objects, you can do this with ease. The main object we will need is the
Socket class. Here is an example of how to create this object and
connect to the mail server.
Socket _socket;
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Connect(new IPEndPoint(Dns.Resolve("mail.yourdomain.com").AddressList[0], 110));
|
With the connection created, it's almost time to talk to the server, but first
we have to create the communication method. This method has to be able to send
a message and receive a response. I will use a asynchronous socket during the
receive process to ensure the data is handled properly. When using the
Socket object, the message has to be converted into a
byte array. This next example explains how to convert the
string and send it through our Socket.
POP3 mail servers require a carriage return after the message so it knows when
to respond, so instead of adding it to every message, we will put it here.
_socket.Send(Encoding.ASCII.GetBytes(_lastCommand + " " + Message + "\r\n"));
|
Enough with sending, it is now time to see if the server has responded with a
message and grab that message from the stream. The example below will grab
portions of the message and add them to a StringBuilder
object, then loop back and see if more data is available . This allows our
method to be used for receiving small or large messages including email.
private void SocketReceive(IAsyncResult _asyncResult)
{
State _state = null;
Socket _receiveSocket = null;
int _bytesRead = 0;
string _receivedMessage = null;
try
{
_state = (State) _asyncResult.AsyncState;
_receiveSocket = _state.Handler;
if(_receiveSocket.Connected)
{
_bytesRead = _receiveSocket.EndReceive(_asyncResult);
if(_bytesRead > 0)
{
_state.MessageBuilder.Append(Encoding.ASCII.GetString(_state.Buffer,0,_bytesRead));
_receivedMessage = _state.MessageBuilder.ToString();
}
if(_receivedMessage != null &&
(((_lastCommand == "LIST" || _lastCommand == "RETR") &&
_receivedMessage.Substring(_receivedMessage.Length - EOF.Length, EOF.Length) == EOF) ||
(_lastCommand != "LIST" && _lastCommand != "RETR")))
{
ParseMessage(_receivedMessage);
_state.MessageBuilder.Remove(0,_receivedMessage.Length);
}
_socket.BeginReceive(_state.Buffer,0,_state.BufferSize,0, new AsyncCallback(SocketReceive),_state);
}
}
catch
{
}
}
|
Communicating With The Post Office Protocol - Version 3
Ok, Ok, Ok. We now have our send and receive process done, we can now start
communicating with our POP3 server.
Here is the list of POP3 commands we will use to communicate and descriptions
of them:
-
USER command is used to send the server your username.
-
PASS command sends your password and responds with the status of
your login.
-
LIST will return the total amount of messages and the total size of
the emails. Each line after the totals is the individual message IDs and the
size of the message.
-
RETR along with the message ID will grab and return the entire
email message from the server.
-
DELE along with the message ID will delete the message from the
server.
-
QUIT command will finialize the processes on the server and close
the maildrop.
The POP3 server will respond two ways.
-
+OK with a message.
-
-ERR with an exception.
Check Your Email
Now that we have connected, built our communication methods, and know what to
send the server, lets check our email. This method is fired from a delegate in
the receive method once a message has been completely downloaded. The main purpose
of this method is to parse the message and send the next message to the mail
server.
private void OnParseMessage(string Message)
{
if(Message.Substring(0,OK.Length).Trim() == OK)
{
switch(_lastCommand)
{
case null:
Send("USER",_username);
break;
case "USER":
Send("PASS", _password);
break;
case "PASS":
Send("STAT","");
break;
case "STAT":
int _start = Message.IndexOf(OK) + OK.Length + " ".Length;
if(_start >= 0)
{
_messages = new int[int.Parse(Message.Substring(_start,Message.IndexOf(" ",_start) - _start))];
Send("LIST","");
}
else
{
this.LoginError();
}
break;
case "LIST":
int i = 0;
while(Message.Substring(0,1) != ".")
{
if(Message.Substring(0,OK.Length) != OK)
{
_messages[i] = int.Parse(Message.Substring(0, Message.IndexOf(" ", 0)));
if(_currentMessage < 0)
{
_currentMessage = i;
}
i++;
}
Message = Message.Remove(0, Message.IndexOf("\r\n") + "\r\n".Length);
}
if(_currentMessage > -1)
{
Send("RETR",_messages[_currentMessage].ToString());
}
else
{
this.MessagesFinished(_mailTable);
}
break;
case "RETR":
if(_mailTable == null)
{
_mailTable = new Hashtable();
}
_mailTable.Add(_messages[_currentMessage], Message);
this.MessageFound(_messages[_currentMessage]);
_currentMessage++;
if(_currentMessage == _messages.Length)
{
this.MessagesFinished(_mailTable);
}
else
{
Send("RETR",_messages[_currentMessage].ToString());
break;
}
break;
}
}
else
{
if(_lastCommand == "USER" || _lastCommand == "PASS")
{
this.LoginError();
}
}
}
|
Conclusion
During this process I hope you have learned a few things. One, how to check
your email in C#, and the other, how easy it is to communicate to different
servers using the protocols they are designed around in code. Other servers
like FTP or IMAP will need simular classes made to communicate with them and
with this example, you should be able to create your own classes to uses them.