Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Howto Pop3 in C#: Listing Error! Bookmark Not Defined.: Pop3 Exception Class

Download as pdf or txt
Download as pdf or txt
You are on page 1of 6

www.kbcafe.

com

HowTo POP3 in C#
by Randy Charles Morin
This is the second in a series of articles on Internet programming with Microsoft's new
C# programming language. In the first article, I wrote a simple SMTP class. In this
article, I'm going to write a simple POP3 class. The SMTP class that I wrote was not very
useful, except maybe as an exercise, as there already exists a similar SMTP class in the
Web.Mail namespace of the dot-NET framework called SmtpMail. Our POP3 class in
this article will be a little more useful as it doesn't already exist in the dot-NET
framework. I have encountered many POP3 C# classes in my searches of the Internet and
most were sufficient to begin programming email clients.
I usually begin writing new classes by introducing an exception class that I can use to
throw and catch all exceptions of the class.
Listing Error! Bookmark not defined.: POP3 Exception Class

public class Pop3Exception : System.ApplicationException


{
public Pop3Exception(string str)
:base(str)
{
}
};

I will not explain the exception class, but rather I expect the reader have enough expertise
with C# to understand this exception class before reading the rest of the article.
Next I created a small class that defines a POP3 message.
Listing Error! Bookmark not defined.: POP3 Message Class
public class Pop3Message
{
public long number;
public long bytes;
public bool retrieved;
public string message;
};

When you retrieve lists of POP3 messages from a POP3 server, the list includes a
message number and number of bytes. You can then use the message number to retrieve
the message content. You'll see this later when we define our List and Retrieve methods.
We derive our Pop3 class from the System.Net.Sockets.TcpClient class in the dot-NET
framework.
Listing Error! Bookmark not defined.: POP3 Class Declaration
public class Pop3 : System.Net.Sockets.TcpClient

Copyright 2001-2002 Randy Charles Morin


www.kbcafe.com

The TcpClient class and the other classes in the System.Net.Sockets namespace of the
dot-NET framework are great encapsulations of the familiar function-oriented socket
library.
The first method of our Pop3 class is the Connect method. This method takes a server
name, username and password parameter to connect to a remote (sometimes local) POP3
server.
Listing Error! Bookmark not defined.: POP3 Connect Method
public void Connect(string server, string username, string password)
{
string message;
string response;

Connect(server, 110);
response = Response();
if (response.Substring(0, 3) != "+OK")
{
throw new Pop3Exception(response);
}

message = "USER " + username + "\r\n";


Write(message);
response = Response();
if (response.Substring(0, 3) != "+OK")
{
throw new Pop3Exception(response);
}

message = "PASS " + password + "\r\n";


Write(message);
response = Response();
if (response.Substring(0, 3) != "+OK")
{
throw new Pop3Exception(response);
}
}

We begin by calling the TcpClient.Connect method passing the server name and the 110
port. The 110 port number is the well known port number for POP3 operations. What that
means is that POP3 servers by default should listen for connections on port 110. When
the POP3 server connects to a client, it should immediately respond with the +OK
acknowledgement message. Next we send two messages, USER and PASS, back to the
server. The POP3 server should acknowledge a successful login by acknowledging both
messages. If the POP3 server returns anything but +OK, then the message will contain
the reason for the failure. In the advent of a failure, I attach that failure message to our
exception class and throw it back to the client.
It should be noted that some POP3 servers don't require authentication and may reject the
calls to USER and PASS. I haven't encountered such a POP3 server, but the protocol
allows it. In those cases, you'll have to slightly modify the class to make things work.
Any use of our Pop3 class should begin with a call to Connect and end with a class to
Disconnect.
Listing Error! Bookmark not defined.: POP3 Disconnect Method
public void Disconnect()
{
string message;
string response;

Copyright 2001-2002 Randy Charles Morin


www.kbcafe.com

message = "QUIT\r\n";
Write(message);
response = Response();
if (response.Substring(0, 3) != "+OK")
{
throw new Pop3Exception(response);
}
}

The Disconnect method sends a QUIT message to the POP3 server.


Between the Connect and Disconnect class, the client may call three other methods, List,
Retrieve and Delete, any number of times. The client will usually begin by calling our
List method to retrieve an array of messages that are queued on the POP3 server.
Listing Error! Bookmark not defined.: POP3 List Method
public ArrayList List()
{
string message;
string response;

ArrayList retval = new ArrayList();

message = "LIST\r\n";
Write(message);
response = Response();
if (response.Substring(0, 3) != "+OK")
{
throw new Pop3Exception(response);
}

while (true)
{
response = Response();
if (response == ".\r\n")
{
return retval;
}
else
{
Pop3Message msg = new Pop3Message();
char[] seps = { ' ' };
string[] values = response.Split(seps);
msg.number = Int32.Parse(values[0]);
msg.bytes = Int32.Parse(values[1]);
msg.retrieved = false;
retval.Add(msg);
continue;
}
}
}

After sending the LIST message to the POP3 server, the server will respond with a +OK
acknowledgement, followed by several lines representing one message each and finally
by a line with a single period indicating the end of the messages. Each message line has
two numbers, the first indicating the unique number of the message and the second
indicating the message size in bytes.
Our List method will return a list of Pop3Message objects. The objects will only contain
the message number and size of each message. In order to retrieve the full message, you
can pass the message object to the Retrieve method. The Retrieve method will then
respond with another Pop3Message containing the message content.

Copyright 2001-2002 Randy Charles Morin


www.kbcafe.com

Listing Error! Bookmark not defined.: POP3 Retrieve Method


public Pop3Message Retrieve(Pop3Message rhs)
{
string message;
string response;

Pop3Message msg = new Pop3Message();


msg.bytes = rhs.bytes;
msg.number = rhs.number;

message = "RETR " + rhs.number + "\r\n";


Write(message);
response = Response();
if (response.Substring(0, 3) != "+OK")
{
throw new Pop3Exception(response);
}

msg.retrieved = true;
while (true)
{
response = Response();
if (response == ".\r\n")
{
break;
}
else
{
msg.message += response;
}
}

return msg;
}

To retrieve a message from a POP3 server, we send a RETR message with the unique
message number. The server then responds with the +OK acknowledgement, the message
content and finally the single period terminating line.
Retrieving a message does not remove the message from the POP3 server. A further call
to LIST will still return the message. To remove a message from POP3 server, you have
to cal the Delete method.
Listing Error! Bookmark not defined.: POP3 Delete Method
public void Delete(Pop3Message rhs)
{
string message;
string response;

message = "DELE " + rhs.number + "\r\n";


Write(message);
response = Response();
if (response.Substring(0, 3) != "+OK")
{
throw new Pop3Exception(response);
}
}

The Delete method sends a DELE message with the message number to the POP3 server.
The server will respond with the +OK acknowledgment message, if successful.
The List, Retrieve and Delete methods used two private methods, Write and Response, to
send and receive messages from the POP3 server.

Copyright 2001-2002 Randy Charles Morin


www.kbcafe.com

Listing Error! Bookmark not defined.: POP3 Write Method


private void Write(string message)
{
System.Text.ASCIIEncoding en = new System.Text.ASCIIEncoding() ;

byte[] WriteBuffer = new byte[1024] ;


WriteBuffer = en.GetBytes(message) ;

NetworkStream stream = GetStream() ;


stream.Write(WriteBuffer,0,WriteBuffer.Length);

Debug.WriteLine("WRITE:" + message);
}

C# native strings, like Java native strings, are UNICODE. We therefore need to encode
and decode the strings to and from ASCII. After encoding the string, we can then retrieve
the socket stream by calling the TcpClient.GetStream method. I finish the Write method
by called the Debug.Writeline method. This sends the string to the debug stream for help
with debugging.
Listing Error! Bookmark not defined.: POP3 Response Method
private string Response()
{
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
byte []serverbuff = new Byte[1024];
NetworkStream stream = GetStream();
int count = 0;
while (true)
{
byte []buff = new Byte[2];
int bytes = stream.Read( buff, 0, 1 );
if (bytes == 1)
{
serverbuff[count] = buff[0];
count++;

if (buff[0] == '\n')
{
break;
}
}
else
{
break;
};
};

string retval = enc.GetString( serverbuff, 0, count );


Debug.WriteLine("READ:" + retval);
return retval;
}

The Response method is similar to the Write method accept that we retrieve bytes from
the stream before decoding them. Again we call the Debug.WriteLine method to send the
read data to the debug stream and help with debugging.
Note that we have a limitation in the Response method. We can only retrieve or send up
to 1024 bytes at a time. I'll fix this in a later release. If you intend to use this in
production, then you'll have to do the same first.
Using the new class is pretty easy.

Copyright 2001-2002 Randy Charles Morin


www.kbcafe.com

Listing Error! Bookmark not defined.: Usage


static void Main(string[] args)
{
try
{
Pop3 obj = new Pop3();
obj.Connect("mail.xxx.com", "yyy", "zzz");
ArrayList list = obj.List();
foreach ( Pop3Message msg in list )
{
Pop3Message msg2 = obj.Retrieve(msg);
System.Console.WriteLine("Message {0}: {1}",
msg2.number, msg2.message);
}
obj.Disconnect();
}
catch ( Pop3Exception e )
{
System.Console.WriteLine(e.ToString());
}
catch ( System.Exception e)
{
System.Console.WriteLine(e.ToString());
}
}

Instantiate a new object, then call the List method. The List method will return an array
of Pop3Message objects. You can then iterate through the Pop3Message objects and
retrieve each in turn. Finally, you call the Disconnect method to release the socket.
POP3 is described in RFC 1939. You can read the full specification from the IETF
website [http://www.ietf.org/rfc/rfc1939.txt?number=1939].

About the Author


Randy Charles Morin is the Chief Architect of SportMarkets Development from Toronto,
Canada and lives with his wife, Bernadette and two kids, Adelaine and Brayden in
Brampton, Canada. He is the author of the KBCafe.com website
[http://www.kbcafe.com], many programming books and many articles.

Copyright 2001-2002 Randy Charles Morin

You might also like