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 
QuestionUsage sample? Pinmembersergeyv200225-Oct-07 7:12 
AnswerRe: Usage sample? Pinmembersmithimage25-Oct-07 7:51 
Hi there sergeyv2002.
 
You are right an example would be very explanatory, thank you for the tip. I have some test code which I could add to the article. However I do not have the time to update this article in the immediate future but I will do it soon enough.
 
Are there anything particular about the libraries you are wondering about which might be able to help you with directly?
 

 
Best regards
Smithimage

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
Web04 | 2.8.141223.1 | Last Updated 27 Nov 2007
Article Copyright 2007 by smithimage
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid