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();
}
}
}