Click here to Skip to main content
Click here to Skip to main content

POP3 (RFC 1939) & MIME (RFC 2045) Compliant Client

, 27 Nov 2007 BSD
Rate this:
Please Sign up or sign in to vote.
POP3 client implemented with the command and state design patterns

Introduction

Before writing this article, I wrote one on the MIMER, a MIME Compliant Parser, which described an implementation of the MIME RFC 2045 specification. This is the continuation and shows the MIMER library in action, i.e. implemented in an RFC 1939 compliant POP3 client.

Background

This POP3 client should not raise any eyebrows; even so, one could take a closer look at the way in which it is implemented. The source uses well-known GOF design patterns such as the command and state patterns. In doing so, it might be used as a "real" world example of how these patterns could be used.

Using the Code

The client as such is rather lightweight, summing up to only around 400 rows of code, mostly due to the fact that the client leaves the RFC 1939 implementation to the command and state patterns.

Screenshot - ClassDiagram1.gif

The Command Pattern

"Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations." (Gamma, E., R. Helm, R. Johnson, and J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995).

Screenshot - Command.gif

The most frequent use of this pattern, as stated above, is to "support undoable operations". This is due to the fact that the pattern turns each command into an individual object, making it possible to store it for later use. This was, however, not the motivator for me in implementing the command pattern in this case. I saw it as an excellent way to separate the POP-client from its RFC 1939 specification, making it possible to extend it in the scenario where the specification should change. In doing so, the pattern made it possible for me to also implement the log functionality.

public abstract class POPCommand
{
    public POPCommand(Stream receiver, string arguments){…}
    protected void WriteCommand(){…}
    protected void ReadResponse(){…}
    public abstract bool Execute();
}

public class NoopCommand:POPCommand
{
    public NoopCommand(Stream receiver): base(receiver, string.Empty){ }

    public override bool Execute()
    {
        try
        {
            m_Command = "NOOP" + Environment.NewLine;
            WriteCommand();
            ReadResponse();
        }
        catch (Exception ex)
        {
            throw new POPException("Could not issue NOOP command!", ex);
        }
        return m_Response.StartsWith(OK);
    }
}

The State Pattern

"Allow an object to alter its behaviour when its internal state changes. The object will appear to change its class." - See this website.

Screenshot - State.gif

The state pattern is a rather easy pattern structurally involving only two parties: the context and its states. It is, in a way, the design pattern implementation of a UML state chart. The abstract POPState class is defined as follows:

public abstract class POPState
{
    public POPState(){}
    public abstract bool IssueCommand(RFC1939.POPCommand command,
        POPClient context);
    public abstract bool IsLegalCommand(RFC1939.POPCommand command);
    public new abstract string ToString();
}

The IssueCommand method is a delegation of the context's (POPClient) IssueCommand method. The IsLegalCommand method shall be able to determine if a command is legal to execute within the current state.

class DisconnectedState:POPState
{
    public override bool IsLegalCommand(Popper.RFC1939.POPCommand command)
    {
        if (command is RFC1939.GreetingCommand)
            return true;

        return false;
    }

The states are partial replicates of the context. If the context has a method named Method(), it is the states which implement Method()'s different behaviour during the lifespan of the context. To accomplish this, the states must be allowed to change the state within which the context currently is. The state is allowed to pass the control over to another state, i.e. the context leaves the control of its behaviour over to the states.

public class POPClient:MIMER.IEndCriteriaStrategy
{
    public bool IssueCommand(RFC1939.POPCommand command)
    {
        try
        {
            if (m_CurrentState.IssueCommand(command, this))
            {
                return true;
            }
        }...

    class DisconnectedState:POPState
    {
        public override bool IssueCommand(Popper.RFC1939.POPCommand command,
            POPClient context)
        {
            if (IsLegalCommand(command))
            {
                if (command.Execute())
                {
                    context.LoggCommand(command);
                    context.CurrentState = AuthorizationState.GetInstance();
                    return true;
                }
            }
            return false;
        }

When looking at the diagram above, one might notice that the SetState method is defined as public. This is not the usual design; the SetState method should only be visible within the same assembly (I could not find the internal modifier in Visio). This is to ensure that the client code could not change the state of the context from outside.

/// <summary>
/// The state which the client currently is within.
/// </summary>
public POPState CurrentState
{
    get
    {
        return m_CurrentState;
    }
    internal set //Protect the client state from outside manipulation
    {
        m_CurrentState = value;
        OnStateChanged(this, new StateChangedEventArgs(m_CurrentState));
    }
}

The Client

The POP3 client, as such, has all the basic functions needed to retrieve and manipulate mails from a POP3 compliant server. The client also implements the basic functionality for asynchronous message retrieval; see the BeginFetchMail() and EndFetchMail() methods. See this website for more comprehensive documentation.

Finally

This was my attempt to write a POP3 compliant client. It still needs some refinement; however, the basics are there. It should not be too hard for you to modify it to fit your needs. I will continuously improve the source and you will find it here.

By the way, when building the source, be certain to build the MIME project before the Popper project since the Popper project depends on the MIMER project.

Additions

Fellow coder sergeyv2002 requested a usage example, I have now added a test application project (PopConsoleTest) available for download at the top of this article. Despite its name, it is not a Console application but a Windows application. The application is rather basic but should guide you somewhat on how to use the code library. I hope this helps.

History

  • 2007-07-28: Article created
  • 2007-11-15: Zip file & article updated

License

This article, along with any associated source code and files, is licensed under The BSD License

Share

About the Author

smithimage
Web Developer
Sweden Sweden
No Biography provided

Comments and Discussions

 
AnswerBug in /MIMER/RFC2045/ContentTypeFieldParser.cs Pinmemberdoggy_epaper.tw27-Feb-09 3:33 
GeneralRe: Bug in /MIMER/RFC2045/ContentTypeFieldParser.cs Pinmembersmithimage4-Mar-09 23:31 
AnswerThere is a bug in MIMER\RFC2047\ExtendedFieldParser.cs Pinmemberdoggy_epaper.tw4-Jun-08 6:52 
GeneralCommandIssued and CommandIssuedEventArgs Pinmemberalhambra-eidos24-Nov-07 0:27 
AnswerRe: CommandIssued and CommandIssuedEventArgs Pinmembersmithimage27-Nov-07 8:00 
GeneralRe: CommandIssued and CommandIssuedEventArgs Pinmemberalhambra-eidos27-Nov-07 10:51 
AnswerRe: CommandIssued and CommandIssuedEventArgs Pinmembersmithimage7-Dec-07 6:47 
Hi again alhambra-eidos.
 
First i must appologize for my late answer, sorry, but I have been bussy..
 
You wondered if the project have been tested with Gmail and the anser is yes, but you should not expect the test-windows-client application to behave like a propper email-client since it is just for test and does, for example, catch any exeptions.
 

This brings me to the probable cause to your problem, the test-windows-client application rather foolishly retrives all headers from all mail within the mail drop sepcified. This behaviour is the most probable cause to the exception you are seeing. Since you might have a rather extensive amount of emails in your Gmail mail-drop the server might cause an time-out or just ingore the repeated requests for haeders that the windows-test-client produces which in turn causes the underlying TcpCLient to close and disposes it self which in turn causes a 'object disposed excepotion'.
 
What you should do is to add some try catch statement around all network depending calls that the POPClient does within the test-windows-client application.
 
I have added some refinement in the code to handle some of this, but not yet published.
 
I will soon republish a new version which should handle some of this.. Allthough you should really write your own GUI around the POPCLient which handles exception and such..
 
Best regards!
QuestionUsage sample? Pinmembersergeyv200225-Oct-07 7:12 
AnswerRe: Usage sample? Pinmembersmithimage25-Oct-07 7:51 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.1411019.1 | Last Updated 27 Nov 2007
Article Copyright 2007 by smithimage
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid