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

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

Rate me:
Please Sign up or sign in to vote.
4.70/5 (9 votes)
27 Nov 2007BSD4 min read 63.1K   645   50   9
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.

C#
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:

C#
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.

C#
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.

C#
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.

C#
/// <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


Written By
Web Developer
Sweden Sweden
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
AnswerBug in /MIMER/RFC2045/ContentTypeFieldParser.cs Pin
Will_保哥27-Feb-09 2:33
Will_保哥27-Feb-09 2:33 
GeneralRe: Bug in /MIMER/RFC2045/ContentTypeFieldParser.cs Pin
smithimage4-Mar-09 22:31
smithimage4-Mar-09 22:31 
AnswerThere is a bug in MIMER\RFC2047\ExtendedFieldParser.cs Pin
Will_保哥4-Jun-08 5:52
Will_保哥4-Jun-08 5:52 
GeneralCommandIssued and CommandIssuedEventArgs Pin
kiquenet.com23-Nov-07 23:27
professionalkiquenet.com23-Nov-07 23:27 
AnswerRe: CommandIssued and CommandIssuedEventArgs Pin
smithimage27-Nov-07 7:00
smithimage27-Nov-07 7:00 
GeneralRe: CommandIssued and CommandIssuedEventArgs Pin
kiquenet.com27-Nov-07 9:51
professionalkiquenet.com27-Nov-07 9:51 
AnswerRe: CommandIssued and CommandIssuedEventArgs Pin
smithimage7-Dec-07 5:47
smithimage7-Dec-07 5:47 
QuestionUsage sample? Pin
sergeyv200225-Oct-07 6:12
sergeyv200225-Oct-07 6:12 
AnswerRe: Usage sample? Pin
smithimage25-Oct-07 6:51
smithimage25-Oct-07 6:51 

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.