Click here to Skip to main content
15,887,302 members
Articles / Programming Languages / C#
Article

POP3 Email Client (.NET 2.0)

Rate me:
Please Sign up or sign in to vote.
4.87/5 (100 votes)
4 Aug 2006CPOL5 min read 894.7K   23.4K   294   162
C# class making it easy to interact with POP3 email server, like listing of emails, download and deletion. Special attention was given to reliable connection control. The code was tested for several months with Google Gmail, including SSL.

Introduction

The .NET framework 2.0 has revamped the support of email sending with improved SMTP classes, but receiving emails is still missing. There are various articles on CodeProject for POP3 support, but all have some drawbacks such as

  • some code is not managed
  • use of DLLs without .NET source code
  • too limited functionality and error reporting
  • no tracing of server commands and responses
  • no support for SSL
  • no XML documentation, etc.

This project builds on the previous projects, but is written entirely in C# 2.0. The present first article focuses on the downloading of raw emails from a POP3 server (RFC1939). There are methods to connect to a POP3 server, to get a list of available emails, to request some emails, to delete some emails and to disconnect. For debugging and for professional use, extensive error reporting and communication tracing is provided. A future article will decompose the received raw email into body, alternative views and attachments following the MIME specification.

I used Gmail for testing, which is freely available for anyone (recommended).

This code is based on the following work:

Background

Interacting with a POP3 Server

Downloading an email from a POP3 server is rather straight forward. The communication with a POP3 server uses only few commands and is easily human readable. Once a connection, possibly with SSL, is established, the client needs to provide a user name and password to enter the POP3 state TRANSACTION, called 'connected' in Pop3MailClient.

In the connected (POP3: transaction) state, the client can execute the following commands:

  • STAT: GetMailboxStats(), number of mails and size of mailbox
  • LIST: GetEmailIdList(), a list with session message numbers and size for all emails
  • LIST [msg]: GetEmailSize(), session message number and size for just one email
  • UIDL: GetUniqueEmailIdList(), a list with unique ID numbers and session message numbers for all emails
  • NOOP: NOOP(), No operation, just check if server is still alive
  • RETR msg: GetRawEmail(), retrieve one complete email
  • DELE msg: DeleteEmail(), Mark one email for deletion once the connection is properly closed
  • RSET: UndeleteAllEmails(), POP3 server deletes unmarks the emails marked for deletion
  • QUIT: Disconnect(), POP3 server deletes emails as needed and closes TCP connection

For a better understanding, it is recommended to read the official POP3 specification, RFC1939 from IETF: Post Office Protocol - Version 3.

Error Handling & Tracing

Quite a number of things can go wrong when two computers communicate over the Internet. Therefore, solid error reporting and communication tracing is essential. Some problems, like no response form the server are fatal and throw an exception. After an exception, usually the connection is dead and needs to be rebuilt. If the error is detected by the POP3 client code, a Pop3Exception (inherited from ApplicationException) is thrown, otherwise it is a normal .NET exception. Some problems, like trying to retrieve a non existing email, raise just a Warning event. It is up to the user of the POP3 client code to decide if an exception should be thrown in the Warning event or a warning written into a log file or ... After a warning, the POP3 server is ready for the next command.

To further help with the investigation of communication problems, a Trace event is raised. It shows commands and responses exchanged between PopClient and PopServer, including warnings. It is strongly recommended to use this feature in the beginning of a project, because RFC1939 gives the server implementor great freedom. It often provides additional information which can be seen in the trace.

Using the code

Server Settings

I feel the server settings like IP address, etc. should not change within a session. The Pop3MailClient requires servername, port, should SSL be used, username and password in the constructor and they cannot be changed. If you want to connect to a different server or for a different user, create a new Pop3MailClient.

To get the demo code running, you need to enter your own credentials for username and password in the following line:

C#
// TODO: Replace username and password with your own credentials.
Pop3.Pop3MailClient DemoClient =
  new Pop3.Pop3MailClient(
    "pop.gmail.com", 995, true, "Username@gmail.com", "password");

If you don't use Gmail, of course you need to also change the servername and port number, maybe even set useSSL to false.

Reading Raw Email

The method GetRawEmail returns the complete email content for one particular message number. RFC1939 specifies that only ANSI characters can be used and therefore the raw email can be easily displayed. But of course it might look funny because of special characters or encoding. Decoding an email will be part of the next article about my Pop3MimeClient class.

AutoReconnect after server timeout

I tested the code extensively with Gmail, which sometimes simply fails to respond. If the isAutoReconnect property is set, the Pop3MailClient tries to reconnect exactly once after a timeout. That's all it usually takes, but notice that any emails marked for deletion are not deleted on the server.

Points of Interest

Efficiency

My guess is that the garbage collector spends a considerable amount of time with collecting memory. Receiving email is a lot of text processing and the idea of all these strings created and discarded gives me a creepy feeling. I hear you saying, use Stringbuilders, but they can be even slower than strings if only few operations are executed with them. Isn't it time for some recycling, i.e. reusing the same global StringBuilder for every email received? I was careful not to introduce any concurrency problems. But even the Framework itself is not reentrant ! If the repeated use of StringBuffers troubles you, just make them local.

Gmail

I had a good experience using Gmail, although I encountered 2 flaws:

  • Gmail shows maximal 250-260 mails in a mailbox, even if there are thousands. Gmail shows the oldest emails. As soon some emails get deleted (after disconnect), newer emails get available.
  • The response of Gmail is sometimes slow and sometimes there is just no response.

History

  • Update 5.August.2006: Fixed some bugs because autoreconnect didn't work properly
  • Update 1.July.2006: The code now supports connections with or without SSL. The UseSSL flag in the Pop3MailClient constructor indicates if the connection is with SSL or without
  • Version 1.0: One POP3 command I didn't need and therefore didn't implement was UIDL [id], which retrieves the message number for an unique ID

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Singapore Singapore
Retired SW Developer from Switzerland living in Singapore

Interested in WPF projects.

Comments and Discussions

 
GeneralRe: Code Format Pin
Peter Huber SG18-Nov-06 0:16
mvaPeter Huber SG18-Nov-06 0:16 
GeneralError connecting to gmail Pin
wguerram8-Nov-06 11:59
wguerram8-Nov-06 11:59 
GeneralRe: Error connecting to gmail Pin
CSharpner.com15-Jan-07 16:37
CSharpner.com15-Jan-07 16:37 
QuestionFurther Question about SSL Authentication Pin
BMWABCD24-Oct-06 9:57
BMWABCD24-Oct-06 9:57 
Questionproblem with SSL Authentication Pin
BMWABCD23-Oct-06 9:12
BMWABCD23-Oct-06 9:12 
AnswerRe: problem with SSL Authentication Pin
BMWABCD23-Oct-06 10:18
BMWABCD23-Oct-06 10:18 
GeneralGreat code Pin
Josh19911820-Oct-06 4:17
Josh19911820-Oct-06 4:17 
GeneralGet Email Headers - An Update I Wrote Pin
Stephen Adam21-Sep-06 4:31
Stephen Adam21-Sep-06 4:31 
Hello there and thanks so much for an excellent class, it really set me on the right path for a bulk mailer i'm writing (dont worry its not spam!!).

Only hassle was bounce removal, for this i only needer the headers so i've added another function "GetEmailHeader".

Maybe a ne release with this functionality would be a good idea. Added the code but anyone wants to email then please ask me. stephen.adam@gmail.com


<br />
// POP3 Client<br />
// ===========<br />
//<br />
// copyright by Peter Huber, Singapore, 2006<br />
// this code is provided as is, bugs are probable, free for any use, no responsibility accepted :-)<br />
//<br />
// based on POP3 Client as a C# Class, by Bill Dean, http://www.codeproject.com/csharp/Pop3MailClient.asp <br />
// based on Retrieve Mail From a POP3 Server Using C#, by Agus Kurniawan, http://www.codeproject.com/csharp/popapp.asp <br />
// based on Post Office Protocol - Version 3, http://www.ietf.org/rfc/rfc1939.txt<br />
<br />
using System;<br />
using System.Collections.Generic;<br />
using System.IO;<br />
using System.Net;<br />
using System.Net.Sockets;<br />
using System.Net.Security;<br />
using System.Text;<br />
<br />
namespace DiscoveryMassMailer.Resources{<br />
<br />
namespace Pop3 {<br />
  // Supporting classes and structs<br />
  // ==============================<br />
<br />
  /// <summary><br />
  /// Combines Email ID with Email UID for one email<br />
  /// The POP3 server assigns to each message a unique Email UID, which will not change for the life time<br />
  /// of the message and no other message should use the same.<br />
  /// <br />
  /// Exceptions:<br />
  /// Throws Pop3Exception if there is a serious communication problem with the POP3 server, otherwise<br />
  /// <br />
  /// </summary><br />
  public struct EmailUid {<br />
    /// <summary><br />
    /// used in POP3 commands to indicate which message (only valid in the present session)<br />
    /// </summary><br />
    public int EmailId;<br />
    /// <summary><br />
    /// Uid is always the same for a message, regardless of session<br />
    /// </summary><br />
    public string Uid;<br />
<br />
    /// <summary><br />
    /// <br />
    /// </summary><br />
    /// <param name="EmailId"></param><br />
    /// <param name="Uid"></param><br />
    public EmailUid(int EmailId, string Uid) {<br />
      this.EmailId = EmailId;<br />
      this.Uid = Uid;<br />
    }<br />
  }<br />
<br />
<br />
  /// <summary><br />
  /// If anything goes wrong within Pop3MailClient, a Pop3Exception is raised<br />
  /// </summary><br />
  public class Pop3Exception:ApplicationException {<br />
    /// <summary><br />
    /// <br />
    /// </summary><br />
    public Pop3Exception() { }<br />
    /// <summary><br />
    /// <br />
    /// </summary><br />
    /// <param name="ErrorMessage"></param><br />
    public Pop3Exception(string ErrorMessage) : base(ErrorMessage) { }<br />
  }<br />
<br />
<br />
  /// <summary><br />
  /// A pop 3 connection goes through the following states:<br />
  /// </summary><br />
  public enum Pop3ConnectionStateEnum {<br />
    /// <summary><br />
    /// undefined<br />
    /// </summary><br />
    None=0,<br />
    /// <summary><br />
    /// not connected yet to POP3 server<br />
    /// </summary><br />
    Disconnected,<br />
    /// <summary><br />
    /// TCP connection has been opened and the POP3 server has sent the greeting. POP3 server expects user name and password<br />
    /// </summary><br />
    Authorization,<br />
    /// <summary><br />
    /// client has identified itself successfully with the POP3, server has locked all messages <br />
    /// </summary><br />
    Connected,<br />
    /// <summary><br />
    /// QUIT command was sent, the server has deleted messages marked for deletion and released the resources<br />
    /// </summary><br />
    Closed<br />
  }<br />
<br />
<br />
  // Delegates for Pop3MailClient<br />
  // ============================<br />
<br />
  /// <summary><br />
  /// If POP3 Server doesn't react as expected or this code has a problem, but<br />
  /// can continue with the execution, a Warning is called.<br />
  /// </summary><br />
  /// <param name="WarningText"></param><br />
  /// <param name="Response">string received from POP3 server</param><br />
  public delegate void WarningHandler(string WarningText, string Response);<br />
<br />
<br />
  /// <summary><br />
  /// Traces all the information exchanged between POP3 client and POP3 server plus some<br />
  /// status messages from POP3 client.<br />
  /// Helpful to investigate any problem.<br />
  /// Console.WriteLine() can be used<br />
  /// </summary><br />
  /// <param name="TraceText"></param><br />
  public delegate void TraceHandler(string TraceText);<br />
<br />
<br />
  // Pop3MailClient Class<br />
  // ====================  <br />
<br />
  /// <summary><br />
  /// provides access to emails on a POP3 Server<br />
  /// </summary><br />
  public class Pop3MailClient {<br />
<br />
    //Events<br />
    //------<br />
<br />
    /// <summary><br />
    /// Called whenever POP3 server doesn't react as expected, but no runtime error is thrown.<br />
    /// </summary><br />
    public event WarningHandler Warning;<br />
<br />
    /// <summary><br />
    /// call warning event<br />
    /// </summary><br />
    /// <param name="methodName">name of the method where warning is needed</param><br />
    /// <param name="response">answer from POP3 server causing the warning</param><br />
    /// <param name="warningText">explanation what went wrong</param><br />
    /// <param name="warningParameters"></param><br />
    protected void CallWarning(string methodName, string response, string warningText, params object[] warningParameters) {<br />
      warningText = string.Format(warningText, warningParameters);<br />
      if (Warning!=null) {<br />
        Warning(methodName + ": " + warningText, response);<br />
      }<br />
      CallTrace("!! {0}", warningText);<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// Shows the communication between PopClient and PopServer, including warnings<br />
    /// </summary><br />
    public event TraceHandler Trace;<br />
<br />
    /// <summary><br />
    /// call Trace event<br />
    /// </summary><br />
    /// <param name="text">string to be traced</param><br />
    /// <param name="parameters"></param><br />
    protected void CallTrace(string text, params object[] parameters) {<br />
      if (Trace!=null) {<br />
        Trace(DateTime.Now.ToString("hh:mm:ss ") + popServer + " " + string.Format(text, parameters));<br />
      }<br />
    }<br />
<br />
    /// <summary><br />
    /// Trace information received from POP3 server<br />
    /// </summary><br />
    /// <param name="text">string to be traced</param><br />
    /// <param name="parameters"></param><br />
    protected void TraceFrom(string text, params object[] parameters) {<br />
      if (Trace!=null) {<br />
        CallTrace("   " + string.Format(text, parameters));<br />
      }<br />
    }<br />
<br />
<br />
    //Properties<br />
    //----------<br />
<br />
    /// <summary><br />
    /// Get POP3 server name<br />
    /// </summary><br />
    public string PopServer {<br />
      get { return popServer; }<br />
    }<br />
    /// <summary><br />
    /// POP3 server name<br />
    /// </summary><br />
    protected string popServer;<br />
<br />
<br />
    /// <summary><br />
    /// Get POP3 server port<br />
    /// </summary><br />
    public int Port {<br />
      get { return port; }<br />
    }<br />
    /// <summary><br />
    /// POP3 server port<br />
    /// </summary><br />
    protected int port;<br />
<br />
<br />
    /// <summary><br />
    /// Should SSL be used for connection with POP3 server ?<br />
    /// </summary><br />
    public bool UseSSL {<br />
      get { return useSSL; }<br />
    }<br />
    /// <summary><br />
    /// Should SSL be used for connection with POP3 server ?<br />
    /// </summary><br />
    private bool useSSL;<br />
<br />
<br />
    /// <summary><br />
    /// should Pop3MailClient automatically reconnect if POP3 server has dropped the <br />
    /// connection due to a timeout ?<br />
    /// </summary><br />
    public bool IsAutoReconnect {<br />
      get { return isAutoReconnect; }<br />
      set { isAutoReconnect = value; }<br />
    }<br />
    private bool isAutoReconnect = false;<br />
    //timeout has occured, we try to perform an autoreconnect<br />
    private bool isTimeoutReconnect = false;<br />
<br />
<br />
<br />
    /// <summary><br />
    /// Get / set read timeout (miliseconds)<br />
    /// </summary><br />
    public int ReadTimeout {<br />
      get { return readTimeout; }<br />
      set {<br />
        readTimeout = value;<br />
        if (pop3Stream!=null && pop3Stream.CanTimeout) {<br />
          pop3Stream.ReadTimeout = readTimeout;<br />
        }<br />
      }<br />
    }<br />
    /// <summary><br />
    /// POP3 server read timeout<br />
    /// </summary><br />
    protected int readTimeout = -1;<br />
<br />
<br />
    /// <summary><br />
    /// Get owner name of mailbox on POP3 server<br />
    /// </summary><br />
    public string Username {<br />
      get { return username; }<br />
    }<br />
    /// <summary><br />
    /// Owner name of mailbox on POP3 server<br />
    /// </summary><br />
    protected string username;<br />
<br />
<br />
    /// <summary><br />
    /// Get password for mailbox on POP3 server<br />
    /// </summary><br />
    public string Password {<br />
      get { return password; }<br />
    }<br />
    /// <summary><br />
    /// Password for mailbox on POP3 server<br />
    /// </summary><br />
    protected string password;<br />
<br />
<br />
    /// <summary><br />
    /// Get connection status with POP3 server<br />
    /// </summary><br />
    public Pop3ConnectionStateEnum Pop3ConnectionState {<br />
      get { return pop3ConnectionState; }<br />
    }<br />
    /// <summary><br />
    /// connection status with POP3 server<br />
    /// </summary><br />
    protected Pop3ConnectionStateEnum pop3ConnectionState = Pop3ConnectionStateEnum.Disconnected;<br />
<br />
<br />
    // Methods<br />
    // -------<br />
<br />
    /// <summary><br />
    /// set POP3 connection state<br />
    /// </summary><br />
    /// <param name="State"></param><br />
    protected void setPop3ConnectionState(Pop3ConnectionStateEnum State) {<br />
      pop3ConnectionState = State;<br />
      CallTrace("   Pop3MailClient Connection State {0} reached", State);<br />
    }<br />
<br />
    /// <summary><br />
    /// throw exception if POP3 connection is not in the required state<br />
    /// </summary><br />
    /// <param name="requiredState"></param><br />
    protected void EnsureState(Pop3ConnectionStateEnum requiredState) {<br />
      if (pop3ConnectionState!=requiredState) {<br />
        // wrong connection state<br />
        throw new Pop3Exception("GetMailboxStats only accepted during connection state: " + requiredState.ToString() + <br />
              "\n The connection to server "+ popServer + " is in state " + pop3ConnectionState.ToString());<br />
      }<br />
    }<br />
<br />
<br />
    //private fields<br />
    //--------------<br />
    /// <summary><br />
    /// TCP to POP3 server<br />
    /// </summary><br />
    private TcpClient serverTcpConnection;<br />
    /// <summary><br />
    /// Stream from POP3 server with or without SSL<br />
    /// </summary><br />
    private Stream pop3Stream;<br />
    /// <summary><br />
    /// Reader for POP3 message<br />
    /// </summary><br />
    protected StreamReader pop3StreamReader;<br />
    /// <summary><br />
    /// char 'array' for carriage return / line feed<br />
    /// </summary><br />
    protected string CRLF = "\r\n";<br />
<br />
<br />
    //public methods<br />
    //--------------<br />
<br />
    /// <summary><br />
    /// Make POP3 client ready to connect to POP3 server<br />
    /// </summary><br />
    /// <param name="PopServer"><example>pop.gmail.com</example></param><br />
    /// <param name="Port"><example>995</example></param><br />
    /// <param name="useSSL">True: SSL is used for connection to POP3 server</param><br />
    /// <param name="Username"><example>abc@gmail.com</example></param><br />
    /// <param name="Password">Secret</param><br />
    public Pop3MailClient(string PopServer, int Port, bool useSSL, string Username, string Password) {<br />
      this.popServer = PopServer;<br />
      this.port = Port;<br />
      this.useSSL = useSSL;<br />
      this.username = Username;<br />
      this.password = Password;<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// Connect to POP3 server<br />
    /// </summary><br />
    public void Connect() {<br />
      if (pop3ConnectionState!=Pop3ConnectionStateEnum.Disconnected &&<br />
        pop3ConnectionState!=Pop3ConnectionStateEnum.Closed && <br />
        !isTimeoutReconnect) {<br />
        CallWarning("connect", "", "Connect command received, but connection state is: " + pop3ConnectionState.ToString());<br />
      } else {<br />
        //establish TCP connection<br />
        try {<br />
          CallTrace("   Connect at port {0}", port);<br />
          serverTcpConnection = new TcpClient(popServer, port);<br />
        } catch (Exception ex) {<br />
          throw new Pop3Exception("Connection to server "+ popServer + ", port " + port + " failed.\nRuntime Error: "+ex.ToString());<br />
        }<br />
<br />
        if (useSSL) {<br />
          //get SSL stream<br />
          try {<br />
            CallTrace("   Get SSL connection");<br />
            pop3Stream = new SslStream(serverTcpConnection.GetStream(), false);<br />
            pop3Stream.ReadTimeout = readTimeout;<br />
          } catch (Exception ex) {<br />
            throw new Pop3Exception("Server " + popServer + " found, but cannot get SSL data stream.\nRuntime Error: "+ex.ToString());<br />
          }<br />
<br />
          //perform SSL authentication<br />
          try {<br />
            CallTrace("   Get SSL authentication");<br />
            ((SslStream)pop3Stream).AuthenticateAsClient(popServer);<br />
          } catch (Exception ex) {<br />
            throw new Pop3Exception("Server " + popServer + " found, but problem with SSL Authentication.\nRuntime Error: " + ex.ToString());<br />
          }<br />
        } else {<br />
          //create a stream to POP3 server without using SSL<br />
          try {<br />
            CallTrace("   Get connection without SSL");<br />
            pop3Stream = serverTcpConnection.GetStream();<br />
            pop3Stream.ReadTimeout = readTimeout;<br />
          } catch (Exception ex) {<br />
            throw new Pop3Exception("Server " + popServer + " found, but cannot get data stream (without SSL).\nRuntime Error: "+ex.ToString());<br />
          }<br />
        }<br />
        //get stream for reading from pop server<br />
        //POP3 allows only US-ASCII. The message will be translated in the proper encoding in a later step<br />
        try {<br />
          pop3StreamReader= new StreamReader(pop3Stream, Encoding.ASCII);<br />
        } catch (Exception ex) {<br />
          if (useSSL) {<br />
            throw new Pop3Exception("Server " + popServer + " found, but cannot read from SSL stream.\nRuntime Error: " + ex.ToString());<br />
          } else {<br />
            throw new Pop3Exception("Server " + popServer + " found, but cannot read from stream (without SSL).\nRuntime Error: " + ex.ToString());<br />
          }<br />
        }<br />
<br />
        //ready for authorisation<br />
        string response;<br />
        if (!readSingleLine(out response)) {<br />
          throw new Pop3Exception("Server " + popServer + " not ready to start AUTHORIZATION.\nMessage: " + response);<br />
        }<br />
        setPop3ConnectionState(Pop3ConnectionStateEnum.Authorization);<br />
<br />
        //send user name<br />
        if (!executeCommand("USER "+ username, out response)) {<br />
          throw new Pop3Exception("Server " + popServer + " doesn't accept username '" + username + "'.\nMessage: " + response);<br />
        }<br />
<br />
        //send password<br />
        if (!executeCommand("PASS " + password, out response)) {<br />
          throw new Pop3Exception("Server " + popServer + " doesn't accept password '" + password + "' for user '" + username + "'.\nMessage: " + response);<br />
        }<br />
<br />
        setPop3ConnectionState(Pop3ConnectionStateEnum.Connected);<br />
      }<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// Disconnect from POP3 Server<br />
    /// </summary><br />
    public void Disconnect() {<br />
      if (pop3ConnectionState==Pop3ConnectionStateEnum.Disconnected ||<br />
        pop3ConnectionState==Pop3ConnectionStateEnum.Closed) {<br />
        CallWarning("disconnect", "", "Disconnect received, but was already disconnected.");<br />
      } else {<br />
        //ask server to end session and possibly to remove emails marked for deletion<br />
        try {<br />
          string response;<br />
          if (executeCommand("QUIT", out response)) {<br />
            //server says everything is ok<br />
            setPop3ConnectionState(Pop3ConnectionStateEnum.Closed);<br />
          } else {<br />
            //server says there is a problem<br />
            CallWarning("Disconnect", response, "negative response from server while closing connection: " + response);<br />
            setPop3ConnectionState(Pop3ConnectionStateEnum.Disconnected);<br />
          }<br />
        } finally {<br />
          //close connection<br />
          if (pop3Stream!=null) {<br />
            pop3Stream.Close();<br />
          }<br />
<br />
          pop3StreamReader.Close();<br />
        }<br />
      }<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// Delete message from server.<br />
    /// The POP3 server marks the message as deleted.  Any future<br />
    /// reference to the message-number associated with the message<br />
    /// in a POP3 command generates an error.  The POP3 server does<br />
    /// not actually delete the message until the POP3 session<br />
    /// enters the UPDATE state.<br />
    /// </summary><br />
    /// <param name="msg_number"></param><br />
    /// <returns></returns><br />
    public bool DeleteEmail(int msg_number) {<br />
      EnsureState(Pop3ConnectionStateEnum.Connected);<br />
      string response;<br />
      if (!executeCommand("DELE " + msg_number.ToString(), out response)) {<br />
        CallWarning("DeleteEmail", response, "negative response for email (Id: {0}) delete request", msg_number);<br />
        return false;<br />
      }<br />
      return true;<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// Get a list of all Email IDs available in mailbox<br />
    /// </summary><br />
    /// <returns></returns><br />
    public bool GetEmailIdList(out List<int> EmailIds) {<br />
      EnsureState(Pop3ConnectionStateEnum.Connected);<br />
      EmailIds = new List<int>();<br />
<br />
      //get server response status line<br />
      string response;<br />
      if (!executeCommand("LIST", out response)) {<br />
        CallWarning("GetEmailIdList", response, "negative response for email list request");<br />
        return false;<br />
      }<br />
<br />
      //get every email id<br />
      int EmailId;<br />
      while (readMultiLine(out response)) {<br />
        if (int.TryParse(response.Split(' ')[0], out EmailId)) {<br />
          EmailIds.Add(EmailId);<br />
        } else {<br />
          CallWarning("GetEmailIdList", response, "first characters should be integer (EmailId)");<br />
        }<br />
      }<br />
      TraceFrom("{0} email ids received", EmailIds.Count);<br />
      return true;<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// get size of one particular email<br />
    /// </summary><br />
    /// <param name="msg_number"></param><br />
    /// <returns></returns><br />
    public int GetEmailSize(int msg_number) {<br />
      EnsureState(Pop3ConnectionStateEnum.Connected);<br />
      string response;<br />
      executeCommand("LIST " + msg_number.ToString(), out response);<br />
      int EmailSize = 0;<br />
      string[] responseSplit = response.Split(' ');<br />
      if (responseSplit.Length<2 || !int.TryParse(responseSplit[2], out EmailSize)) {<br />
        CallWarning("GetEmailSize", response, "'+OK int int' format expected (EmailId, EmailSize)");<br />
      }<br />
<br />
      return EmailSize;<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// Get a list with the unique IDs of all Email available in mailbox.<br />
    /// <br />
    /// Explanation:<br />
    /// EmailIds for the same email can change between sessions, whereas the unique Email id<br />
    /// never changes for an email.<br />
    /// </summary><br />
    /// <param name="EmailIds"></param><br />
    /// <returns></returns><br />
    public bool GetUniqueEmailIdList(out List<EmailUid> EmailIds) {<br />
      EnsureState(Pop3ConnectionStateEnum.Connected);<br />
      EmailIds = new List<EmailUid>();<br />
<br />
      //get server response status line<br />
      string response;<br />
      if (!executeCommand("UIDL ", out response)) {<br />
        CallWarning("GetUniqueEmailIdList", response, "negative response for email list request");<br />
        return false;<br />
      }<br />
<br />
      //get every email unique id<br />
      int EmailId;<br />
      while (readMultiLine(out response)) {<br />
        string[] responseSplit = response.Split(' ');<br />
        if (responseSplit.Length<2) {<br />
          CallWarning("GetUniqueEmailIdList", response, "response not in format 'int string'");<br />
        } else if (!int.TryParse(responseSplit[0], out EmailId)) {<br />
          CallWarning("GetUniqueEmailIdList", response, "first charaters should be integer (Unique EmailId)");<br />
        } else {<br />
          EmailIds.Add(new EmailUid(EmailId, responseSplit[1]));<br />
        }<br />
      }<br />
      TraceFrom("{0} unique email ids received", EmailIds.Count);<br />
      return true;<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// get a list with all currently available messages and the UIDs<br />
    /// </summary><br />
    /// <param name="EmailIds">EmailId Uid list</param><br />
    /// <returns>false: server sent negative response (didn't send list)</returns><br />
    public bool GetUniqueEmailIdList(out SortedList<string, int> EmailIds) {<br />
      EnsureState(Pop3ConnectionStateEnum.Connected);<br />
      EmailIds = new SortedList<string, int>();<br />
<br />
      //get server response status line<br />
      string response;<br />
      if (!executeCommand("UIDL", out response)) {<br />
        CallWarning("GetUniqueEmailIdList", response, "negative response for email list request");<br />
        return false;<br />
      }<br />
<br />
      //get every email unique id<br />
      int EmailId;<br />
      while (readMultiLine(out response)) {<br />
        string[] responseSplit = response.Split(' ');<br />
        if (responseSplit.Length<2) {<br />
          CallWarning("GetUniqueEmailIdList", response, "response not in format 'int string'");<br />
        } else if (!int.TryParse(responseSplit[0], out EmailId)) {<br />
          CallWarning("GetUniqueEmailIdList", response, "first charaters should be integer (Unique EmailId)");<br />
        } else {<br />
          EmailIds.Add(responseSplit[1], EmailId);<br />
        }<br />
      }<br />
      TraceFrom("{0} unique email ids received", EmailIds.Count);<br />
      return true;<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// get size of one particular email<br />
    /// </summary><br />
    /// <param name="msg_number"></param><br />
    /// <returns></returns><br />
    public int GetUniqueEmailId(EmailUid msg_number) {<br />
      EnsureState(Pop3ConnectionStateEnum.Connected);<br />
      string response;<br />
      executeCommand("LIST " + msg_number.ToString(), out response);<br />
      int EmailSize = 0;<br />
      string[] responseSplit = response.Split(' ');<br />
      if (responseSplit.Length<2 || !int.TryParse(responseSplit[2], out EmailSize)) {<br />
        CallWarning("GetEmailSize", response, "'+OK int int' format expected (EmailId, EmailSize)");<br />
      }<br />
<br />
      return EmailSize;<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// Sends an 'empty' command to the POP3 server. Server has to respond with +OK<br />
    /// </summary><br />
    /// <returns>true: server responds as expected</returns><br />
    public bool NOOP() {<br />
      EnsureState(Pop3ConnectionStateEnum.Connected);<br />
      string response;<br />
      if (!executeCommand("NOOP", out response)) {<br />
        CallWarning("NOOP", response, "negative response for NOOP request");<br />
        return false;<br />
      }<br />
      return true;<br />
    }<br />
<br />
    /// <summary><br />
    /// Should the raw content, the US-ASCII code as received, be traced<br />
    /// GetRawEmail will switch it on when it starts and off once finished<br />
    /// <br />
    /// Inheritors might use it to get the raw email<br />
    /// </summary><br />
    protected bool isTraceRawEmail = false;<br />
<br />
<br />
    /// <summary><br />
    /// contains one MIME part of the email in US-ASCII, needs to be translated in .NET string (Unicode)<br />
    /// contains the complete email in US-ASCII, needs to be translated in .NET string (Unicode)<br />
    /// For speed reasons, reuse StringBuilder<br />
    /// </summary><br />
    protected StringBuilder RawEmailSB;<br />
<br />
<br />
    /// <summary><br />
    /// Reads the complete text of a message<br />
    /// </summary><br />
    /// <param name="MessageNo">Email to retrieve</param><br />
    /// <param name="EmailText">ASCII string of complete message</param><br />
    /// <returns></returns><br />
    public bool GetRawEmail(int MessageNo, out string EmailText) {<br />
      //send 'RETR int' command to server<br />
      if (!SendRetrCommand(MessageNo)) {<br />
        EmailText = null;<br />
        return false;<br />
      }<br />
<br />
      //get the lines<br />
      string response;<br />
      int LineCounter = 0;<br />
      //empty StringBuilder<br />
      if (RawEmailSB==null) {<br />
        RawEmailSB = new StringBuilder(100000);<br />
      } else {<br />
        RawEmailSB.Length = 0;<br />
      }<br />
      isTraceRawEmail = true;<br />
      while (readMultiLine(out response)) {<br />
        LineCounter += 1;<br />
      }<br />
      EmailText = RawEmailSB.ToString();<br />
      TraceFrom("email with {0} lines,  {1} chars received", LineCounter.ToString(), EmailText.Length);<br />
      return true;<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// Reads the message header<br />
    /// </summary><br />
    /// <param name="MessageNo">Email header to retrieve</param><br />
    /// <param name="EmailText">ASCII string of message header</param><br />
    /// <returns></returns><br />
    public bool GetEmailHeader(int MessageNo, out string EmailHeader)<br />
    {<br />
        //send 'TOP 1 0' command to server<br />
        if (!SendTopCommand(MessageNo))<br />
        {<br />
            EmailHeader = null;<br />
            return false;<br />
        }<br />
<br />
        //get the lines<br />
        string response;<br />
        int LineCounter = 0;<br />
        //empty StringBuilder<br />
        if (RawEmailSB == null)<br />
        {<br />
            RawEmailSB = new StringBuilder(100000);<br />
        }<br />
        else<br />
        {<br />
            RawEmailSB.Length = 0;<br />
        }<br />
        isTraceRawEmail = true;<br />
        while (readMultiLine(out response))<br />
        {<br />
            LineCounter += 1;<br />
        }<br />
        EmailHeader = RawEmailSB.ToString();<br />
        TraceFrom("email with {0} lines,  {1} chars received", LineCounter.ToString(), EmailHeader.Length);<br />
        return true;<br />
    }<br />
<br />
<br />
    ///// <summary><br />
    ///// Requests from POP3 server a specific email and returns a stream with the message content (header and body)<br />
    ///// </summary><br />
    ///// <param name="MessageNo"></param><br />
    ///// <param name="EmailStreamReader"></param><br />
    ///// <returns>false: POP3 server cannot provide this message</returns><br />
    //public bool GetEmailStream(int MessageNo, out StreamReader EmailStreamReader) {<br />
    //  //send 'RETR int' command to server<br />
    //  if (!SendRetrCommand(MessageNo)) {<br />
    //    EmailStreamReader = null;<br />
    //    return false;<br />
    //  }<br />
    //  EmailStreamReader = sslStreamReader;<br />
    //  return true;<br />
    //}<br />
<br />
<br />
    /// <summary><br />
    /// Unmark any emails from deletion. The server only deletes email really<br />
    /// once the connection is properly closed.<br />
    /// </summary><br />
    /// <returns>true: emails are unmarked from deletion</returns><br />
    public bool UndeleteAllEmails() {<br />
      EnsureState(Pop3ConnectionStateEnum.Connected);<br />
      string response;<br />
      return executeCommand("RSET", out response);<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// Get mailbox statistics<br />
    /// </summary><br />
    /// <param name="NumberOfMails"></param><br />
    /// <param name="MailboxSize"></param><br />
    /// <returns></returns><br />
    public bool GetMailboxStats(out int NumberOfMails, out int MailboxSize) {<br />
      EnsureState(Pop3ConnectionStateEnum.Connected);<br />
<br />
      //interpret response<br />
      string response;<br />
      NumberOfMails = 0;<br />
      MailboxSize =0;<br />
      if (executeCommand("STAT", out response)) {<br />
        //got a positive response<br />
        string[] responseParts = response.Split(' ');<br />
        if (responseParts.Length<2) {<br />
          //response format wrong<br />
          throw new Pop3Exception("Server " + popServer + " sends illegally formatted response." +<br />
            "\nExpected format: +OK int int" +<br />
            "\nReceived response: " + response);<br />
        }<br />
        NumberOfMails = int.Parse(responseParts[1]);<br />
        MailboxSize   = int.Parse(responseParts[2]);<br />
        return true;<br />
      }<br />
      return false;<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// Send RETR command to POP 3 server to fetch one particular message<br />
    /// </summary><br />
    /// <param name="MessageNo">ID of message required</param><br />
    /// <returns>false: negative server respond, message not delivered</returns><br />
    protected bool SendRetrCommand(int MessageNo) {<br />
      EnsureState(Pop3ConnectionStateEnum.Connected);<br />
      // retrieve mail with message number<br />
      string response;<br />
      if (!executeCommand("RETR "+ MessageNo.ToString(), out response)) {<br />
        CallWarning("GetRawEmail", response, "negative response for email (ID: {0}) request", MessageNo);<br />
        return false;<br />
      }<br />
      return true;<br />
    }<br />
<br />
    /// <summary><br />
    /// Send TOP command to POP 3 server to fetch one particular message header<br />
    /// </summary><br />
    /// <param name="MessageNo">ID of message required</param><br />
    /// <returns>false: negative server respond, message not delivered</returns><br />
    protected bool SendTopCommand(int MessageNo)<br />
    {<br />
        EnsureState(Pop3ConnectionStateEnum.Connected);<br />
        // retrieve mail with message number<br />
        string response;<br />
        if (!executeCommand("TOP "+MessageNo.ToString()+" 0", out response))<br />
        {<br />
            CallWarning("GetEmailHeader", response, "negative response for email (ID: {0}) request", MessageNo);<br />
            return false;<br />
        }<br />
        return true;<br />
    }<br />
<br />
<br />
<br />
<br />
    //Helper methodes<br />
    //---------------<br />
<br />
    /// <summary><br />
    /// sends the 4 letter command to POP3 server (adds CRLF) and waits for the<br />
    /// response of the server<br />
    /// </summary><br />
    /// <param name="command">command to be sent to server</param><br />
    /// <param name="response">answer from server</param><br />
    /// <returns>false: server sent negative acknowledge, i.e. server could not execute command</returns><br />
<br />
    public bool isDebug = false;<br />
    private bool executeCommand(string command, out string response) {<br />
      //send command to server<br />
      byte[] commandBytes = System.Text.Encoding.ASCII.GetBytes((command + CRLF).ToCharArray());<br />
      CallTrace("Tx '{0}'", command);<br />
      bool isSupressThrow = false;<br />
      try {<br />
        pop3Stream.Write(commandBytes, 0, commandBytes.Length);<br />
        if (isDebug) {<br />
          isDebug=false;<br />
          throw new IOException("Test", new SocketException(10053));<br />
        }<br />
      } catch (IOException ex) {<br />
        //Unable to write data to the transport connection. Check if reconnection should be tried<br />
        isSupressThrow = executeReconnect(ex, command, commandBytes);<br />
        if (!isSupressThrow) {<br />
          throw;<br />
        }<br />
      }<br />
      pop3Stream.Flush();<br />
<br />
      //read response from server<br />
      response = null;<br />
      try {<br />
        response = pop3StreamReader.ReadLine();<br />
      } catch (IOException ex) {<br />
        //Unable to write data to the transport connection. Check if reconnection should be tried<br />
        isSupressThrow = executeReconnect(ex, command, commandBytes);<br />
        if (isSupressThrow) {<br />
          //wait for response one more time<br />
          response = pop3StreamReader.ReadLine();<br />
        } else {<br />
          throw;<br />
        }<br />
      }<br />
      if (response==null) {<br />
        throw new Pop3Exception("Server "+ popServer + " has not responded, timeout has occured.");<br />
      }<br />
      CallTrace("Rx '{0}'", response);<br />
      return (response.Length>0 && response[0]=='+');<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// reconnect, if there is a timeout exception and isAutoReconnect is true<br />
    /// <br />
    /// </summary><br />
   private bool executeReconnect(IOException ex, string command, byte[] commandBytes) {<br />
      if (ex.InnerException!=null && ex.InnerException is SocketException) {<br />
        //SocketException<br />
        SocketException innerEx = (SocketException)ex.InnerException;<br />
        if (innerEx.ErrorCode==10053) {<br />
          //probably timeout: An established connection was aborted by the software in your host machine.<br />
          CallWarning("ExecuteCommand", "", "probably timeout occured");<br />
          if (isAutoReconnect) {<br />
            //try to reconnect and send one more time<br />
            isTimeoutReconnect = true;<br />
            try {<br />
              CallTrace("   try to auto reconnect");<br />
              Connect();<br />
<br />
              CallTrace("   reconnect successful, try to resend command");<br />
              CallTrace("Tx '{0}'", command);<br />
              pop3Stream.Write(commandBytes, 0, commandBytes.Length);<br />
              pop3Stream.Flush();<br />
              return true;<br />
            } finally {<br />
              isTimeoutReconnect = false;<br />
            }<br />
<br />
          }<br />
        }<br />
      }<br />
      return false;<br />
    }<br />
<br />
<br />
    /*<br />
        /// <summary><br />
        /// sends the 4 letter command to POP3 server (adds CRLF)<br />
        /// </summary><br />
        /// <param name="command"></param><br />
        protected void SendCommand(string command) {<br />
          byte[] commandBytes = System.Text.Encoding.ASCII.GetBytes((command + CRLF).ToCharArray());<br />
          CallTrace("Tx '{0}'", command);<br />
          try {<br />
            pop3Stream.Write(commandBytes, 0, commandBytes.Length);<br />
          } catch (IOException ex) {<br />
            //Unable to write data to the transport connection:<br />
            if (ex.InnerException!=null && ex.InnerException is SocketException) {<br />
              //SocketException<br />
              SocketException innerEx = (SocketException)ex.InnerException;<br />
              if (innerEx.ErrorCode==10053) {<br />
                //probably timeout: An established connection was aborted by the software in your host machine.<br />
                CallWarning("SendCommand", "", "probably timeout occured");<br />
                if (isAutoReconnect) {<br />
                  //try to reconnect and send one more time<br />
                  isTimeoutReconnect = true;<br />
                  try {<br />
                    CallTrace("   try to auto reconnect");<br />
                    Connect();<br />
<br />
                    CallTrace("   reconnect successful, try to resend command");<br />
                    CallTrace("Tx '{0}'", command);<br />
                    pop3Stream.Write(commandBytes, 0, commandBytes.Length);<br />
                  } finally {<br />
                    isTimeoutReconnect = false;<br />
                  }<br />
                  return;<br />
                }<br />
              }<br />
            }<br />
            throw;<br />
          }<br />
          pop3Stream.Flush();<br />
        }<br />
     */<br />
<br />
<br />
    /// <summary><br />
    /// read single line response from POP3 server. <br />
    /// <example>Example server response: +OK asdfkjahsf</example><br />
    /// </summary><br />
    /// <param name="response">response from POP3 server</param><br />
    /// <returns>true: positive response</returns><br />
    protected bool readSingleLine(out string response) {<br />
      response = null;<br />
      try {<br />
        response = pop3StreamReader.ReadLine();<br />
      } catch (Exception ex) {<br />
        string s = ex.Message;<br />
      }<br />
      if (response==null) {<br />
        throw new Pop3Exception("Server "+ popServer + " has not responded, timeout has occured.");<br />
      }<br />
      CallTrace("Rx '{0}'", response);<br />
      return (response.Length>0 && response[0]=='+');<br />
    }<br />
<br />
<br />
    /// <summary><br />
    /// read one line in multiline mode from the POP3 server. <br />
    /// </summary><br />
    /// <param name="response">line received</param><br />
    /// <returns>false: end of message</returns><br />
    /// <returns></returns><br />
    protected bool readMultiLine(out string response) {<br />
      response = null;<br />
      response = pop3StreamReader.ReadLine();<br />
      if (response==null) {<br />
        throw new Pop3Exception("Server "+ popServer + " has not responded, probably timeout has occured.");<br />
      }<br />
      if (isTraceRawEmail) {<br />
        //collect all responses as received<br />
        RawEmailSB.Append(response + CRLF);<br />
      }<br />
      //check for byte stuffing, i.e. if a line starts with a '.', another '.' is added, unless<br />
      //it is the last line<br />
      if (response.Length>0 && response[0]=='.') {<br />
        if (response==".") {<br />
          //closing line found<br />
          return false;<br />
        }<br />
        //remove the first '.'<br />
        response = response.Substring(1, response.Length-1);<br />
      }<br />
      return true;<br />
    }<br />
<br />
  }<br />
}<br />
}<br />
<br />


"Gödel proved that any formal system that defines the primitive recursive functions must be either incomplete or inconsistent. In particular one could not prove from within the system that the system itself was consistent even though the question could be formulated within the system."

GeneralRe: Get Email Headers - An Update I Wrote Pin
Peter Huber SG21-Sep-06 4:48
mvaPeter Huber SG21-Sep-06 4:48 
GeneralGood article / Email parsing Pin
djlove27-Aug-06 7:36
djlove27-Aug-06 7:36 
GeneralRe: Good article / Email parsing Pin
Peter Huber SG27-Aug-06 9:14
mvaPeter Huber SG27-Aug-06 9:14 
GeneralRe: Good article / Email parsing Pin
Peter Huber SG17-Sep-06 10:25
mvaPeter Huber SG17-Sep-06 10:25 
GeneralRe: Good article / Email parsing Pin
djlove19-Sep-06 22:19
djlove19-Sep-06 22:19 
GeneralIndy.Sockets Pin
Nick Clarke6-Aug-06 4:11
Nick Clarke6-Aug-06 4:11 
GeneralUTF7 Pin
Orxorand2-Aug-06 21:55
Orxorand2-Aug-06 21:55 
GeneralRe: UTF7 [modified] Pin
Peter Huber SG2-Aug-06 22:24
mvaPeter Huber SG2-Aug-06 22:24 
GeneralRe: UTF7 [modified] Pin
Orxorand2-Aug-06 22:36
Orxorand2-Aug-06 22:36 
AnswerRe: UTF7 Pin
Peter Huber SG6-Aug-06 5:58
mvaPeter Huber SG6-Aug-06 5:58 
QuestionAttachments downloading Pin
dan79510-Jul-06 8:18
dan79510-Jul-06 8:18 
AnswerRe: Attachments downloading Pin
Peter Huber SG10-Jul-06 9:56
mvaPeter Huber SG10-Jul-06 9:56 
GeneralRe: Attachments downloading Pin
rusika1-Aug-06 17:59
rusika1-Aug-06 17:59 
GeneralRe: Attachments downloading Pin
Peter Huber SG2-Aug-06 4:59
mvaPeter Huber SG2-Aug-06 4:59 
GeneralRe: Attachments downloading Pin
Christopher Foster22-Aug-06 23:57
Christopher Foster22-Aug-06 23:57 
AnswerRe: Attachments downloading Pin
Peter Huber SG23-Aug-06 23:40
mvaPeter Huber SG23-Aug-06 23:40 
GeneralRe: Attachments downloading Pin
Wagner Amorim11-Nov-06 12:26
Wagner Amorim11-Nov-06 12:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.