using System; using System.Collections.Generic; using System.IO; using System.Net.Sockets; namespace Ant.Service.Utilities { /// /// The Pop3Client class provides a wrapper for the Pop3 commands /// that can be executed against a Pop3Server. This class will /// execute and return results for the various commands that are /// executed. /// public sealed class Pop3Client : IDisposable { private static readonly int DefaultPort = 110; private TcpClient _client; private Stream _clientStream; /// /// Traces the various command objects that executed during this objects /// lifetime. /// public event Action Trace; private void OnTrace(string message) { if (Trace != null) { Trace(message); } } private string _hostname; /// /// Gets the hostname. /// /// The hostname. public string Hostname { get { return _hostname; } } private int _port; /// /// Gets the port. /// /// The port. public int Port { get { return _port; } } private bool _useSsl; /// /// Gets a value indicating whether [use SSL]. /// /// true if [use SSL]; otherwise, false. public bool UseSsl { get { return _useSsl; } } private string _username; /// /// Gets or sets the username. /// /// The username. public string Username { get { return _username; } set { _username = value; } } private string _password; /// /// Gets or sets the password. /// /// The password. public string Password { get { return _password; } set { _password = value; } } private Pop3State _currentState; /// /// Gets the state of the current. /// /// The state of the current. public Pop3State CurrentState { get { return _currentState; } } /// /// Initializes a new instance of the class using the default POP3 port 110 /// without using SSL. /// /// The hostname. /// The username. /// The password. public Pop3Client(string hostname, string username, string password) : this(hostname, DefaultPort, false, username, password) { } /// /// Initializes a new instance of the class using the default POP3 port 110. /// /// The hostname. /// if set to true [use SSL]. /// The username. /// The password. public Pop3Client(string hostname, bool useSsl, string username, string password) : this(hostname, DefaultPort, useSsl, username, password) { } /// /// Initializes a new instance of the class. /// /// The hostname. /// The port. /// if set to true [use SSL]. /// The username. /// The password. public Pop3Client(string hostname, int port, bool useSsl, string username, string password) : this() { if (string.IsNullOrEmpty(hostname)) { throw new ArgumentNullException("hostname"); } if (port < 0) { throw new ArgumentOutOfRangeException("port"); } if (string.IsNullOrEmpty(username)) { throw new ArgumentNullException("username"); } if (string.IsNullOrEmpty(password)) { throw new ArgumentNullException("password"); } _hostname = hostname; _port = port; _useSsl = useSsl; _username = username; _password = password; } /// /// Initializes a new instance of the class. /// private Pop3Client() { _client = new TcpClient(); _currentState = Pop3State.Unknown; } /// /// Checks the connection. /// private void EnsureConnection() { if (!_client.Connected) { throw new Pop3Exception("Pop3 client is not connected."); } } /// /// Resets the state. /// /// The state. private void SetState(Pop3State state) { _currentState = state; } /// /// Ensures the response. /// /// The response. /// The error. private void EnsureResponse(Pop3Response response, string error) { if (response == null) { throw new Pop3Exception("Unable to get Response. Response object null."); } if (response.StatusIndicator) { return; } //the command execution was successful. string errorMessage = string.Empty; if (string.IsNullOrEmpty(error)) { errorMessage = response.HostMessage; } else { errorMessage = string.Concat(error, ": ", error); } throw new Pop3Exception(errorMessage); } /// /// Ensures the response. /// /// The response. private void EnsureResponse(Pop3Response response) { EnsureResponse(response, string.Empty); } /// /// Traces the command. /// /// The command. private void TraceCommand(TCommand command) where TCommand : Pop3Command where TResponse : Pop3Response { if (Trace != null) { command.Trace += delegate(string message) { OnTrace(message); }; } } /// /// Connects this instance and properly sets the /// client stream to Use Ssl if it is specified. /// private void Connect() { if (_client == null) { _client = new TcpClient(); } //If a previous quit command was issued, the client would be disposed of. if (_client.Connected) { return; } //if the connection already is established no need to reconnect. SetState(Pop3State.Unknown); ConnectResponse response; using (ConnectCommand command = new ConnectCommand(_client, _hostname, _port, _useSsl)) { TraceCommand(command); response = command.Execute(CurrentState); EnsureResponse(response); } SetClientStream(response.NetworkStream); SetState(Pop3State.Authorization); } /// /// Sets the client stream. If UseSsl true then wrap /// the client's NetworkStream in an SslStream, if UseSsl false /// then set the client stream to the NetworkStream /// private void SetClientStream(Stream networkStream) { if (_clientStream != null) { _clientStream.Dispose(); } _clientStream = networkStream; } /// /// Authenticates this instance. /// /// A successful execution of this method will result in a Current State of Transaction. /// Unsuccessful USER or PASS commands can be reattempted by resetting the Username or Password /// properties and re-execution of the methods. /// /// If the Pop3Server is unable to be connected. /// If the User command is unable to be successfully executed. /// If the Pass command is unable to be successfully executed. /// public void Authenticate() { Connect(); //execute the user command. using (UserCommand userCommand = new UserCommand(_clientStream, _username)) { ExecuteCommand(userCommand); } //execute the pass command. using (PassCommand passCommand = new PassCommand(_clientStream, _password)) { ExecuteCommand(passCommand); } _currentState = Pop3State.Transaction; } /// /// Executes the POP3 DELE command. /// /// The item. /// /// If the DELE command was unable to be executed successfully. public void Dele(Pop3ListItem item) { if (item == null) { throw new ArgumentNullException("item"); } using (DeleCommand command = new DeleCommand(_clientStream, item.MessageId)) { ExecuteCommand(command); } } /// /// Executes the POP3 NOOP command. /// /// If the NOOP command was unable to be executed successfully. public void Noop() { using (NoopCommand command = new NoopCommand(_clientStream)) { ExecuteCommand(command); } } /// /// Executes the POP3 RSET command. /// /// If the RSET command was unable to be executed successfully. public void Rset() { using (RsetCommand command = new RsetCommand(_clientStream)) { ExecuteCommand(command); } } /// /// Executes the POP3 STAT command. /// /// A Stat object containing the results of STAT command. /// If the STAT command was unable to be executed successfully. public Stat Stat() { StatResponse response; using (StatCommand command = new StatCommand(_clientStream)) { response = ExecuteCommand(command); } return new Stat(response.MessageCount, response.Octets); } /// /// Executes the POP3 List command. /// /// A generic List of Pop3Items containing the results of the LIST command. /// If the LIST command was unable to be executed successfully. public List List() { ListResponse response; using (ListCommand command = new ListCommand(_clientStream)) { response = ExecuteCommand(command); } return response.Items; } /// /// Lists the specified message. /// /// The message. /// A Pop3ListItem for the requested Pop3Item. /// If the LIST command was unable to be executed successfully for the provided message id. public Pop3ListItem List(int messageId) { ListResponse response; using (ListCommand command = new ListCommand(_clientStream, messageId)) { response = ExecuteCommand(command); } return new Pop3ListItem(response.MessageNumber, response.Octets); } /// /// Retrs the specified message. /// /// The item. /// A MimeEntity for the requested Pop3 Mail Item. public MimeEntity RetrMimeEntity(Pop3ListItem item) { if (item == null) { throw new ArgumentNullException("item"); } if (item.MessageId < 1) { throw new ArgumentOutOfRangeException("item.MessageId"); } RetrResponse response; using (RetrCommand command = new RetrCommand(_clientStream, item.MessageId)) { response = ExecuteCommand(command); } MimeReader reader = new MimeReader(response.MessageLines); return reader.CreateMimeEntity(); } public MailMessageEx Top(int messageId, int lineCount) { if (messageId < 1) { throw new ArgumentOutOfRangeException("messageId"); } if (lineCount < 0) { throw new ArgumentOutOfRangeException("lineCount"); } RetrResponse response; using (TopCommand command = new TopCommand(_clientStream, messageId, lineCount)) { response = ExecuteCommand(command); } MimeReader reader = new MimeReader(response.MessageLines); MimeEntity entity = reader.CreateMimeEntity(); MailMessageEx message = entity.ToMailMessageEx(); message.Octets = response.Octets; message.MessageNumber = messageId; return entity.ToMailMessageEx(); } /// /// Retrs the mail message ex. /// /// The item. /// public MailMessageEx RetrMailMessageEx(Pop3ListItem item) { MailMessageEx message = RetrMimeEntity(item).ToMailMessageEx(); if (message != null) { message.MessageNumber = item.MessageId; } return message; } /// /// Executes the Pop3 QUIT command. /// /// If the quit command returns a -ERR server message. public void Quit() { using (QuitCommand command = new QuitCommand(_clientStream)) { ExecuteCommand(command); if (CurrentState.Equals(Pop3State.Transaction)) { SetState(Pop3State.Update); } // Messages could have been deleted, reflect the server state. Disconnect(); //Quit command can only be called in Authorization or Transaction state, reset to Unknown. SetState(Pop3State.Unknown); } } /// /// Provides a common way to execute all commands. This method /// validates the connection, traces the command and finally /// validates the response message for a -ERR response. /// /// The command. /// The Pop3Response for the provided command /// If the HostMessage does not start with '+OK'. /// If the client is no longer connected. private TResponse ExecuteCommand(TCommand command) where TResponse : Pop3Response where TCommand : Pop3Command { EnsureConnection(); TraceCommand(command); TResponse response = (TResponse)command.Execute(CurrentState); EnsureResponse(response); return response; } /// /// Disconnects this instance. /// private void Disconnect() { if (_clientStream != null) { _clientStream.Close(); } //release underlying socket. if (_client != null) { _client.Close(); _client = null; } } public void Dispose() { Disconnect(); } } }