548 lines
18 KiB
C#
548 lines
18 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Net.Mail;
|
|
using System.Net.Sockets;
|
|
using System.Net.Security;
|
|
using System.Security.Authentication;
|
|
|
|
using Net.Mime;
|
|
|
|
namespace Net.Mail
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public sealed class Pop3Client : IDisposable
|
|
{
|
|
private static readonly int DefaultPort = 110;
|
|
|
|
private TcpClient _client;
|
|
private Stream _clientStream;
|
|
|
|
/// <summary>
|
|
/// Traces the various command objects that executed during this objects
|
|
/// lifetime.
|
|
/// </summary>
|
|
public event Action<string> Trace;
|
|
|
|
private void OnTrace(string message)
|
|
{
|
|
if (Trace != null)
|
|
{
|
|
Trace(message);
|
|
}
|
|
}
|
|
|
|
private string _hostname;
|
|
/// <summary>
|
|
/// Gets the hostname.
|
|
/// </summary>
|
|
/// <value>The hostname.</value>
|
|
public string Hostname
|
|
{
|
|
get { return _hostname; }
|
|
}
|
|
|
|
private int _port;
|
|
/// <summary>
|
|
/// Gets the port.
|
|
/// </summary>
|
|
/// <value>The port.</value>
|
|
public int Port
|
|
{
|
|
get { return _port; }
|
|
}
|
|
|
|
private bool _useSsl;
|
|
/// <summary>
|
|
/// Gets a value indicating whether [use SSL].
|
|
/// </summary>
|
|
/// <value><c>true</c> if [use SSL]; otherwise, <c>false</c>.</value>
|
|
public bool UseSsl
|
|
{
|
|
get { return _useSsl; }
|
|
}
|
|
|
|
private string _username;
|
|
/// <summary>
|
|
/// Gets or sets the username.
|
|
/// </summary>
|
|
/// <value>The username.</value>
|
|
public string Username
|
|
{
|
|
get { return _username; }
|
|
set { _username = value; }
|
|
}
|
|
|
|
private string _password;
|
|
/// <summary>
|
|
/// Gets or sets the password.
|
|
/// </summary>
|
|
/// <value>The password.</value>
|
|
public string Password
|
|
{
|
|
get { return _password; }
|
|
set { _password = value; }
|
|
}
|
|
|
|
private Pop3State _currentState;
|
|
/// <summary>
|
|
/// Gets the state of the current.
|
|
/// </summary>
|
|
/// <value>The state of the current.</value>
|
|
public Pop3State CurrentState
|
|
{
|
|
get { return _currentState; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Pop3Client"/> class using the default POP3 port 110
|
|
/// without using SSL.
|
|
/// </summary>
|
|
/// <param name="hostname">The hostname.</param>
|
|
/// <param name="username">The username.</param>
|
|
/// <param name="password">The password.</param>
|
|
public Pop3Client(string hostname, string username, string password)
|
|
: this(hostname, DefaultPort, false, username, password) { }
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Pop3Client"/> class using the default POP3 port 110.
|
|
/// </summary>
|
|
/// <param name="hostname">The hostname.</param>
|
|
/// <param name="useSsl">if set to <c>true</c> [use SSL].</param>
|
|
/// <param name="username">The username.</param>
|
|
/// <param name="password">The password.</param>
|
|
public Pop3Client(string hostname, bool useSsl, string username, string password)
|
|
: this(hostname, DefaultPort, useSsl, username, password) { }
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Pop3Client"/> class.
|
|
/// </summary>
|
|
/// <param name="hostname">The hostname.</param>
|
|
/// <param name="port">The port.</param>
|
|
/// <param name="useSsl">if set to <c>true</c> [use SSL].</param>
|
|
/// <param name="username">The username.</param>
|
|
/// <param name="password">The password.</param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Pop3Client"/> class.
|
|
/// </summary>
|
|
private Pop3Client()
|
|
{
|
|
_client = new TcpClient();
|
|
_currentState = Pop3State.Unknown;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks the connection.
|
|
/// </summary>
|
|
private void EnsureConnection()
|
|
{
|
|
if (!_client.Connected)
|
|
{
|
|
throw new Pop3Exception("Pop3 client is not connected.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the state.
|
|
/// </summary>
|
|
/// <param name="state">The state.</param>
|
|
private void SetState(Pop3State state)
|
|
{
|
|
_currentState = state;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensures the response.
|
|
/// </summary>
|
|
/// <param name="response">The response.</param>
|
|
/// <param name="error">The error.</param>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensures the response.
|
|
/// </summary>
|
|
/// <param name="response">The response.</param>
|
|
private void EnsureResponse(Pop3Response response)
|
|
{
|
|
EnsureResponse(response, string.Empty);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Traces the command.
|
|
/// </summary>
|
|
/// <param name="command">The command.</param>
|
|
private void TraceCommand<TCommand, TResponse>(TCommand command)
|
|
where TCommand : Pop3Command<TResponse> where TResponse : Pop3Response
|
|
{
|
|
if (Trace != null)
|
|
{
|
|
command.Trace += delegate(string message) { OnTrace(message); };
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Connects this instance and properly sets the
|
|
/// client stream to Use Ssl if it is specified.
|
|
/// </summary>
|
|
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<ConnectCommand, ConnectResponse>(command);
|
|
response = command.Execute(CurrentState);
|
|
EnsureResponse(response);
|
|
}
|
|
|
|
SetClientStream(response.NetworkStream);
|
|
|
|
SetState(Pop3State.Authorization);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the client stream. If UseSsl <c>true</c> then wrap
|
|
/// the client's <c>NetworkStream</c> in an <c>SslStream</c>, if UseSsl <c>false</c>
|
|
/// then set the client stream to the <c>NetworkStream</c>
|
|
/// </summary>
|
|
private void SetClientStream(Stream networkStream)
|
|
{
|
|
if (_clientStream != null)
|
|
{
|
|
_clientStream.Dispose();
|
|
}
|
|
|
|
_clientStream = networkStream;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Authenticates this instance.
|
|
/// </summary>
|
|
/// <remarks>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.</remarks>
|
|
/// <exception cref="Pop3Exception">
|
|
/// 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.
|
|
/// </exception>
|
|
public void Authenticate()
|
|
{
|
|
Connect();
|
|
|
|
//execute the user command.
|
|
using (UserCommand userCommand = new UserCommand(_clientStream, _username))
|
|
{
|
|
ExecuteCommand<Pop3Response, UserCommand>(userCommand);
|
|
}
|
|
|
|
//execute the pass command.
|
|
using (PassCommand passCommand = new PassCommand(_clientStream, _password))
|
|
{
|
|
ExecuteCommand<Pop3Response, PassCommand>(passCommand);
|
|
}
|
|
|
|
_currentState = Pop3State.Transaction;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes the POP3 DELE command.
|
|
/// </summary>
|
|
/// <param name="item">The item.</param>
|
|
/// /// <exception cref="Pop3Exception">If the DELE command was unable to be executed successfully.</exception>
|
|
public void Dele(Pop3ListItem item)
|
|
{
|
|
if (item == null)
|
|
{
|
|
throw new ArgumentNullException("item");
|
|
}
|
|
|
|
using (DeleCommand command = new DeleCommand(_clientStream, item.MessageId))
|
|
{
|
|
ExecuteCommand<Pop3Response, DeleCommand>(command);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes the POP3 NOOP command.
|
|
/// </summary>
|
|
/// <exception cref="Pop3Exception">If the NOOP command was unable to be executed successfully.</exception>
|
|
public void Noop()
|
|
{
|
|
using (NoopCommand command = new NoopCommand(_clientStream))
|
|
{
|
|
ExecuteCommand<Pop3Response, NoopCommand>(command);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes the POP3 RSET command.
|
|
/// </summary>
|
|
/// <exception cref="Pop3Exception">If the RSET command was unable to be executed successfully.</exception>
|
|
public void Rset()
|
|
{
|
|
using (RsetCommand command = new RsetCommand(_clientStream))
|
|
{
|
|
ExecuteCommand<Pop3Response, RsetCommand>(command);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes the POP3 STAT command.
|
|
/// </summary>
|
|
/// <returns>A Stat object containing the results of STAT command.</returns>
|
|
/// <exception cref="Pop3Exception">If the STAT command was unable to be executed successfully.</exception>
|
|
public Stat Stat()
|
|
{
|
|
StatResponse response;
|
|
using (StatCommand command = new StatCommand(_clientStream))
|
|
{
|
|
response = ExecuteCommand<StatResponse, StatCommand>(command);
|
|
}
|
|
|
|
return new Stat(response.MessageCount, response.Octets);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes the POP3 List command.
|
|
/// </summary>
|
|
/// <returns>A generic List of Pop3Items containing the results of the LIST command.</returns>
|
|
/// <exception cref="Pop3Exception">If the LIST command was unable to be executed successfully.</exception>
|
|
public List<Pop3ListItem> List()
|
|
{
|
|
ListResponse response;
|
|
using (ListCommand command = new ListCommand(_clientStream))
|
|
{
|
|
response = ExecuteCommand<ListResponse, ListCommand>(command);
|
|
}
|
|
return response.Items;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lists the specified message.
|
|
/// </summary>
|
|
/// <param name="message">The message.</param>
|
|
/// <returns>A <c>Pop3ListItem</c> for the requested Pop3Item.</returns>
|
|
/// <exception cref="Pop3Exception">If the LIST command was unable to be executed successfully for the provided message id.</exception>
|
|
public Pop3ListItem List(int messageId)
|
|
{
|
|
ListResponse response;
|
|
using (ListCommand command = new ListCommand(_clientStream, messageId))
|
|
{
|
|
response = ExecuteCommand<ListResponse, ListCommand>(command);
|
|
}
|
|
return new Pop3ListItem(response.MessageNumber, response.Octets);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrs the specified message.
|
|
/// </summary>
|
|
/// <param name="item">The item.</param>
|
|
/// <returns>A MimeEntity for the requested Pop3 Mail Item.</returns>
|
|
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<RetrResponse, RetrCommand>(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<RetrResponse, TopCommand>(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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrs the mail message ex.
|
|
/// </summary>
|
|
/// <param name="item">The item.</param>
|
|
/// <returns></returns>
|
|
public MailMessageEx RetrMailMessageEx(Pop3ListItem item)
|
|
{
|
|
MailMessageEx message = RetrMimeEntity(item).ToMailMessageEx();
|
|
message.MessageNumber = item.MessageId;
|
|
return message;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Executes the Pop3 QUIT command.
|
|
/// </summary>
|
|
/// <exception cref="Pop3Exception">If the quit command returns a -ERR server message.</exception>
|
|
public void Quit()
|
|
{
|
|
using (QuitCommand command = new QuitCommand(_clientStream))
|
|
{
|
|
ExecuteCommand<Pop3Response, QuitCommand>(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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="command">The command.</param>
|
|
/// <returns>The Pop3Response for the provided command</returns>
|
|
/// <exception cref="Pop3Exception">If the HostMessage does not start with '+OK'.</exception>
|
|
/// <exception cref="Pop3Exception">If the client is no longer connected.</exception>
|
|
private TResponse ExecuteCommand<TResponse, TCommand>(TCommand command)
|
|
where TResponse : Pop3Response where TCommand : Pop3Command<TResponse>
|
|
{
|
|
EnsureConnection();
|
|
TraceCommand<TCommand, TResponse>(command);
|
|
TResponse response = (TResponse)command.Execute(CurrentState);
|
|
EnsureResponse(response);
|
|
return response;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disconnects this instance.
|
|
/// </summary>
|
|
private void Disconnect()
|
|
{
|
|
if (_clientStream != null)
|
|
{
|
|
_clientStream.Close();
|
|
} //release underlying socket.
|
|
|
|
if (_client != null)
|
|
{
|
|
_client.Close();
|
|
_client = null;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Disconnect();
|
|
}
|
|
|
|
public void ClearMailBox() {
|
|
Authenticate();
|
|
List<Pop3ListItem> list = List();
|
|
foreach (Pop3ListItem item in list) {
|
|
Noop();
|
|
Dele(item);
|
|
}
|
|
Quit();
|
|
}
|
|
}
|
|
}
|