Files
watrbx-game-engine/Client.LegacyAssemblies/Roblox.Common/Email.cs
T
2025-09-18 17:55:52 -04:00

1493 lines
54 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.Net.Security;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
namespace Roblox.Common
{
public class Email
{
private static string defaultSMTPServer = global::Roblox.Common.Properties.Settings.Default.SMTPServer;
private const string linebreak = "\r\n";
static Email()
{
EventLog.WriteEntry(
LogInstaller.SourceName,
String.Format("Roblox.Common.Email.SMTPServer is {0}", defaultSMTPServer),
EventLogEntryType.Information
);
}
public static void SendEmail(string to, string from, string subject, string body)
{
SendEmail(to, from, subject, body, defaultSMTPServer);
}
public static void SendEmail(string to, string from, string subject, string body, string smtpServer)
{
if (to.Trim().Length == 0)
return;
var smtpClient = new SmtpClient(smtpServer);
using (var message = new MailMessage(from, to, subject, body))
{
smtpClient.Send(message);
}
}
public static void SendMimeEmail(string to, string from, string subject, string plaintextbody, string htmlbody)
{
ContentType mimeType = new System.Net.Mime.ContentType("text/html");
AlternateView alternate = AlternateView.CreateAlternateViewFromString(htmlbody, mimeType);
SmtpClient client = new SmtpClient(defaultSMTPServer);
client.Credentials = CredentialCache.DefaultNetworkCredentials;
using (var message = new MailMessage(from, to, subject, plaintextbody))
{
message.AlternateViews.Add(alternate);
client.Send(message);
}
}
public static void SendMimeEmail(string to, string from, string subject, string plaintextbody, string htmlbody, string smtpServer)
{
if (to.Trim().Length == 0)
return;
ContentType mimeType = new System.Net.Mime.ContentType("text/html");
AlternateView alternate = AlternateView.CreateAlternateViewFromString(htmlbody, mimeType);
SmtpClient client = new SmtpClient(smtpServer);
client.Credentials = CredentialCache.DefaultNetworkCredentials;
using (var message = new MailMessage(from, to, subject, plaintextbody))
{
message.AlternateViews.Add(alternate);
client.Send(message);
}
}
public static MailMessageEx ParseEmail(string rawEmail)
{
Mime.MimeReader reader = new Roblox.Common.Mime.MimeReader(Regex.Split(rawEmail, linebreak));
Mime.MimeEntity mime = reader.CreateMimeEntity();
return mime.ToMailMessageEx();
}
}
// POP3 Client
// ===========
//
// copyright by Peter Huber, Singapore, 2006
// this code is provided as is, bugs are probable, free for any use, no responsibility accepded :-)
//
// based on POP3 Client as a C# Class, by Bull Dean, http://www.codeproject.com/csharp/Pop3MailClient.asp
// based on Retrieve Mail From a POP3 Server using C#, by Agus Kurniawan, http://www.codeproject.com/csharp/popapp.asp
// based on Post Office Protocol - Version 3, http://www.ietf.org/rfc/rfc1939.txt
namespace Mail.Pop3
{
// Supporting classes and structs
// ==============================
/// <summary>
/// Combines Email ID with Email UID for one email
/// The POP3 server assigns to each message a unique Email UID, which will not change for the life time
/// of the message and no other message should use the same.
///
/// Exceptions:
/// Throws Pop3Exception if there is a serious communication problem with the POP3 server, otherwise
///
/// </summary>
public struct EmailUid
{
/// <summary>
/// used in POP3 commands to indicate which message (only valid in the present session)
/// </summary>
public int EmailId;
/// <summary>
/// Uid is always the same for a message, regardless of session
/// </summary>
public string Uid;
/// <summary>
///
/// </summary>
/// <param name="EmailId"></param>
/// <param name="Uid"></param>
public EmailUid(int EmailId, string Uid)
{
this.EmailId = EmailId;
this.Uid = Uid;
}
}
/// <summary>
/// If anything goes wrong within Pop3MailClient, a Pop3Exception is raised
/// </summary>
public class Pop3Exception : ApplicationException
{
/// <summary>
///
/// </summary>
public Pop3Exception() { }
/// <summary>
///
/// </summary>
/// <param name="ErrorMessage"></param>
public Pop3Exception(string ErrorMessage) : base(ErrorMessage) { }
}
/// <summary>
/// A pop 3 connection goes through the following states:
/// </summary>
public enum Pop3ConnectionStateEnum
{
/// <summary>
/// undefined
/// </summary>
None = 0,
/// <summary>
/// not connected yet to POP3 server
/// </summary>
Disconnected,
/// <summary>
/// TCP connection has been opened and the POP3 server has sent the greeting. POP3 server expects user name and password
/// </summary>
Authorization,
/// <summary>
/// client has identified itself successfully with the POP3, server has locked all messages
/// </summary>
Connected,
/// <summary>
/// QUIT command was sent, the server has deleted messages marked for deletion and released the resources
/// </summary>
Closed
}
// Delegates for Pop3MailClient
// ============================
/// <summary>
/// If POP3 Server doesn't react as expected or this code has a problem, but
/// can continue with the execution, a Warning is called.
/// </summary>
/// <param name="WarningText"></param>
/// <param name="Response">string received from POP3 server</param>
public delegate void WarningHandler(string WarningText, string Response);
/// <summary>
/// Traces all the information exchanged between POP3 client and POP3 server plus some
/// status messages from POP3 client.
/// Helpful to investigate any problem.
/// Console.WriteLine() can be used
/// </summary>
/// <param name="TraceText"></param>
public delegate void TraceHandler(string TraceText);
// Pop3MailClient Class
// ====================
/// <summary>
/// provides access to emails on a POP3 Server
/// </summary>
public class Pop3MailClient : IDisposable
{
//Events
//------
/// <summary>
/// Called whenever POP3 server doesn't react as expected, but no runtime error is thrown.
/// </summary>
public event WarningHandler Warning;
/// <summary>
/// call warning event
/// </summary>
/// <param name="methodName">name of the method where warning is needed</param>
/// <param name="response">answer from POP3 server causing the warning</param>
/// <param name="warningText">explanation what went wrong</param>
/// <param name="warningParameters"></param>
protected void CallWarning(string methodName, string response, string warningText, params object[] warningParameters)
{
warningText = string.Format(warningText, warningParameters);
if (Warning != null)
{
Warning(methodName + ": " + warningText, response);
}
CallTrace("!! {0}", warningText);
}
/// <summary>
/// Shows the communication between PopClient and PopServer, including warnings
/// </summary>
public event TraceHandler Trace;
/// <summary>
/// call Trace event
/// </summary>
/// <param name="text">string to be traced</param>
/// <param name="parameters"></param>
protected void CallTrace(string text, params object[] parameters)
{
if (Trace != null)
{
Trace(DateTime.Now.ToString("hh:mm:ss ") + popServer + " " + string.Format(text, parameters));
}
}
/// <summary>
/// Trace information received from POP3 server
/// </summary>
/// <param name="text">string to be traced</param>
/// <param name="parameters"></param>
protected void TraceFrom(string text, params object[] parameters)
{
if (Trace != null)
{
CallTrace(" " + string.Format(text, parameters));
}
}
//Properties
//----------
/// <summary>
/// Get POP3 server name
/// </summary>
public string PopServer
{
get { return popServer; }
}
/// <summary>
/// POP3 server name
/// </summary>
protected string popServer;
/// <summary>
/// Get POP3 server port
/// </summary>
public int Port
{
get { return port; }
}
/// <summary>
/// POP3 server port
/// </summary>
protected int port;
/// <summary>
/// Should SSL be used for connection with POP3 server ?
/// </summary>
public bool UseSSL
{
get { return useSSL; }
}
/// <summary>
/// Should SSL be used for connection with POP3 server ?
/// </summary>
private bool useSSL;
/// <summary>
/// should Pop3MailClient automatically reconnect if POP3 server has dropped the
/// connection due to a timeout ?
/// </summary>
public bool IsAutoReconnect
{
get { return isAutoReconnect; }
set { isAutoReconnect = value; }
}
private bool isAutoReconnect = false;
//timeout has occured, we try to perform an autoreconnect
private bool isTimeoutReconnect = false;
/// <summary>
/// Get / set read timeout (miliseconds)
/// </summary>
public int ReadTimeout
{
get { return readTimeout; }
set
{
readTimeout = value;
if (pop3Stream != null && pop3Stream.CanTimeout)
{
pop3Stream.ReadTimeout = readTimeout;
}
}
}
/// <summary>
/// POP3 server read timeout
/// </summary>
protected int readTimeout = -1;
/// <summary>
/// Get owner name of mailbox on POP3 server
/// </summary>
public string Username
{
get { return username; }
}
/// <summary>
/// Owner name of mailbox on POP3 server
/// </summary>
protected string username;
/// <summary>
/// Get password for mailbox on POP3 server
/// </summary>
public string Password
{
get { return password; }
}
/// <summary>
/// Password for mailbox on POP3 server
/// </summary>
protected string password;
/// <summary>
/// Get connection status with POP3 server
/// </summary>
public Pop3ConnectionStateEnum Pop3ConnectionState
{
get { return pop3ConnectionState; }
}
/// <summary>
/// connection status with POP3 server
/// </summary>
protected Pop3ConnectionStateEnum pop3ConnectionState = Pop3ConnectionStateEnum.Disconnected;
// Methods
// -------
/// <summary>
/// set POP3 connection state
/// </summary>
/// <param name="State"></param>
protected void setPop3ConnectionState(Pop3ConnectionStateEnum State)
{
pop3ConnectionState = State;
CallTrace(" Pop3MailClient Connection State {0} reached", State);
}
/// <summary>
/// throw exception if POP3 connection is not in the required state
/// </summary>
/// <param name="requiredState"></param>
protected void EnsureState(Pop3ConnectionStateEnum requiredState)
{
if (pop3ConnectionState != requiredState)
{
// wrong connection state
throw new Pop3Exception("GetMailboxStats only accepted during connection state: " + requiredState.ToString() +
"\n The connection to server " + popServer + " is in state " + pop3ConnectionState.ToString());
}
}
//private fields
//--------------
/// <summary>
/// TCP to POP3 server
/// </summary>
private TcpClient serverTcpConnection;
/// <summary>
/// Stream from POP3 server with or without SSL
/// </summary>
private Stream pop3Stream;
/// <summary>
/// Reader for POP3 message
/// </summary>
protected StreamReader pop3StreamReader;
/// <summary>
/// char 'array' for carriage return / line feed
/// </summary>
protected string CRLF = "\r\n";
//public methods
//--------------
/// <summary>
/// Make POP3 client ready to connect to POP3 server
/// </summary>
/// <param name="PopServer"><example>pop.gmail.com</example></param>
/// <param name="Port"><example>995</example></param>
/// <param name="useSSL">True: SSL is used for connection to POP3 server</param>
/// <param name="Username"><example>abc@gmail.com</example></param>
/// <param name="Password">Secret</param>
public Pop3MailClient(string PopServer, int Port, bool useSSL, string Username, string Password)
{
this.popServer = PopServer;
this.port = Port;
this.useSSL = useSSL;
this.username = Username;
this.password = Password;
}
/// <summary>
/// Connect to POP3 server
/// </summary>
public void Connect()
{
if (pop3ConnectionState != Pop3ConnectionStateEnum.Disconnected &&
pop3ConnectionState != Pop3ConnectionStateEnum.Closed &&
!isTimeoutReconnect)
{
CallWarning("connect", "", "Connect command received, but connection state is: " + pop3ConnectionState.ToString());
}
else
{
//establish TCP connection
try
{
CallTrace(" Connect at port {0}", port);
serverTcpConnection = new TcpClient(popServer, port);
}
catch (Exception ex)
{
throw new Pop3Exception("Connection to server " + popServer + ", port " + port + " failed.\nRuntime Error: " + ex.ToString());
}
if (useSSL)
{
//get SSL stream
try
{
CallTrace(" Get SSL connection");
pop3Stream = new SslStream(serverTcpConnection.GetStream(), false);
pop3Stream.ReadTimeout = readTimeout;
}
catch (Exception ex)
{
throw new Pop3Exception("Server " + popServer + " found, but cannot get SSL data stream.\nRuntime Error: " + ex.ToString());
}
//perform SSL authentication
try
{
CallTrace(" Get SSL authentication");
((SslStream)pop3Stream).AuthenticateAsClient(popServer);
}
catch (Exception ex)
{
throw new Pop3Exception("Server " + popServer + " found, but problem with SSL Authentication.\nRuntime Error: " + ex.ToString());
}
}
else
{
//create a stream to POP3 server without using SSL
try
{
CallTrace(" Get connection without SSL");
pop3Stream = serverTcpConnection.GetStream();
pop3Stream.ReadTimeout = readTimeout;
}
catch (Exception ex)
{
throw new Pop3Exception("Server " + popServer + " found, but cannot get data stream (without SSL).\nRuntime Error: " + ex.ToString());
}
}
//get stream for reading from pop server
//POP3 allows only US-ASCII. The message will be translated in the proper encoding in a later step
try
{
pop3StreamReader = new StreamReader(pop3Stream, Encoding.ASCII);
}
catch (Exception ex)
{
if (useSSL)
{
throw new Pop3Exception("Server " + popServer + " found, but cannot read from SSL stream.\nRuntime Error: " + ex.ToString());
}
else
{
throw new Pop3Exception("Server " + popServer + " found, but cannot read from stream (without SSL).\nRuntime Error: " + ex.ToString());
}
}
//ready for authorisation
string response;
if (!readSingleLine(out response))
{
throw new Pop3Exception("Server " + popServer + " not ready to start AUTHORIZATION.\nMessage: " + response);
}
setPop3ConnectionState(Pop3ConnectionStateEnum.Authorization);
//send user name
if (!executeCommand("USER " + username, out response))
{
throw new Pop3Exception("Server " + popServer + " doesn't accept username '" + username + "'.\nMessage: " + response);
}
//send password
if (!executeCommand("PASS " + password, out response))
{
throw new Pop3Exception("Server " + popServer + " doesn't accept password '" + password + "' for user '" + username + "'.\nMessage: " + response);
}
setPop3ConnectionState(Pop3ConnectionStateEnum.Connected);
}
}
/// <summary>
/// Disconnect from POP3 Server
/// </summary>
public void Disconnect()
{
if (pop3ConnectionState == Pop3ConnectionStateEnum.Disconnected ||
pop3ConnectionState == Pop3ConnectionStateEnum.Closed)
{
CallWarning("disconnect", "", "Disconnect received, but was already disconnected.");
}
else
{
//ask server to end session and possibly to remove emails marked for deletion
try
{
string response;
if (executeCommand("QUIT", out response))
{
//server says everything is ok
setPop3ConnectionState(Pop3ConnectionStateEnum.Closed);
}
else
{
//server says there is a problem
CallWarning("Disconnect", response, "negative response from server while closing connection: " + response);
setPop3ConnectionState(Pop3ConnectionStateEnum.Disconnected);
}
}
finally
{
//close connection
if (pop3Stream != null)
{
pop3Stream.Close();
}
pop3StreamReader.Close();
}
}
}
/// <summary>
/// Delete message from server.
/// The POP3 server marks the message as deleted. Any future
/// reference to the message-number associated with the message
/// in a POP3 command generates an error. The POP3 server does
/// not actually delete the message until the POP3 session
/// enters the UPDATE state.
/// </summary>
/// <param name="msg_number"></param>
/// <returns></returns>
public bool DeleteEmail(int msg_number)
{
EnsureState(Pop3ConnectionStateEnum.Connected);
string response;
if (!executeCommand("DELE " + msg_number.ToString(), out response))
{
CallWarning("DeleteEmail", response, "negative response for email (Id: {0}) delete request", msg_number);
return false;
}
return true;
}
/// <summary>
/// Get a list of all Email IDs available in mailbox
/// </summary>
/// <returns></returns>
public bool GetEmailIdList(out List<int> EmailIds)
{
EnsureState(Pop3ConnectionStateEnum.Connected);
EmailIds = new List<int>();
//get server response status line
string response;
if (!executeCommand("LIST", out response))
{
CallWarning("GetEmailIdList", response, "negative response for email list request");
return false;
}
//get every email id
int EmailId;
while (readMultiLine(out response))
{
if (int.TryParse(response.Split(' ')[0], out EmailId))
{
EmailIds.Add(EmailId);
}
else
{
CallWarning("GetEmailIdList", response, "first characters should be integer (EmailId)");
}
}
TraceFrom("{0} email ids received", EmailIds.Count);
return true;
}
/// <summary>
/// get size of one particular email
/// </summary>
/// <param name="msg_number"></param>
/// <returns></returns>
public int GetEmailSize(int msg_number)
{
EnsureState(Pop3ConnectionStateEnum.Connected);
string response;
executeCommand("LIST " + msg_number.ToString(), out response);
int EmailSize = 0;
string[] responseSplit = response.Split(' ');
if (responseSplit.Length < 2 || !int.TryParse(responseSplit[2], out EmailSize))
{
CallWarning("GetEmailSize", response, "'+OK int int' format expected (EmailId, EmailSize)");
}
return EmailSize;
}
/// <summary>
/// Get a list with the unique IDs of all Email available in mailbox.
///
/// Explanation:
/// EmailIds for the same email can change between sessions, whereas the unique Email id
/// never changes for an email.
/// </summary>
/// <param name="EmailIds"></param>
/// <returns></returns>
public bool GetUniqueEmailIdList(out List<EmailUid> EmailIds)
{
EnsureState(Pop3ConnectionStateEnum.Connected);
EmailIds = new List<EmailUid>();
//get server response status line
string response;
if (!executeCommand("UIDL ", out response))
{
CallWarning("GetUniqueEmailIdList", response, "negative response for email list request");
return false;
}
//get every email unique id
int EmailId;
while (readMultiLine(out response))
{
string[] responseSplit = response.Split(' ');
if (responseSplit.Length < 2)
{
CallWarning("GetUniqueEmailIdList", response, "response not in format 'int string'");
}
else if (!int.TryParse(responseSplit[0], out EmailId))
{
CallWarning("GetUniqueEmailIdList", response, "first charaters should be integer (Unique EmailId)");
}
else
{
EmailIds.Add(new EmailUid(EmailId, responseSplit[1]));
}
}
TraceFrom("{0} unique email ids received", EmailIds.Count);
return true;
}
/// <summary>
/// get a list with all currently available messages and the UIDs
/// </summary>
/// <param name="EmailIds">EmailId Uid list</param>
/// <returns>false: server sent negative response (didn't send list)</returns>
public bool GetUniqueEmailIdList(out SortedList<string, int> EmailIds)
{
EnsureState(Pop3ConnectionStateEnum.Connected);
EmailIds = new SortedList<string, int>();
//get server response status line
string response;
if (!executeCommand("UIDL", out response))
{
CallWarning("GetUniqueEmailIdList", response, "negative response for email list request");
return false;
}
//get every email unique id
int EmailId;
while (readMultiLine(out response))
{
string[] responseSplit = response.Split(' ');
if (responseSplit.Length < 2)
{
CallWarning("GetUniqueEmailIdList", response, "response not in format 'int string'");
}
else if (!int.TryParse(responseSplit[0], out EmailId))
{
CallWarning("GetUniqueEmailIdList", response, "first charaters should be integer (Unique EmailId)");
}
else
{
EmailIds.Add(responseSplit[1], EmailId);
}
}
TraceFrom("{0} unique email ids received", EmailIds.Count);
return true;
}
/// <summary>
/// get size of one particular email
/// </summary>
/// <param name="msg_number"></param>
/// <returns></returns>
public int GetUniqueEmailId(EmailUid msg_number)
{
EnsureState(Pop3ConnectionStateEnum.Connected);
string response;
executeCommand("LIST " + msg_number.ToString(), out response);
int EmailSize = 0;
string[] responseSplit = response.Split(' ');
if (responseSplit.Length < 2 || !int.TryParse(responseSplit[2], out EmailSize))
{
CallWarning("GetEmailSize", response, "'+OK int int' format expected (EmailId, EmailSize)");
}
return EmailSize;
}
/// <summary>
/// Sends an 'empty' command to the POP3 server. Server has to respond with +OK
/// </summary>
/// <returns>true: server responds as expected</returns>
public bool NOOP()
{
EnsureState(Pop3ConnectionStateEnum.Connected);
string response;
if (!executeCommand("NOOP", out response))
{
CallWarning("NOOP", response, "negative response for NOOP request");
return false;
}
return true;
}
/// <summary>
/// Should the raw content, the US-ASCII code as received, be traced
/// GetRawEmail will switch it on when it starts and off once finished
///
/// Inheritors might use it to get the raw email
/// </summary>
protected bool isTraceRawEmail = false;
/// <summary>
/// contains one MIME part of the email in US-ASCII, needs to be translated in .NET string (Unicode)
/// contains the complete email in US-ASCII, needs to be translated in .NET string (Unicode)
/// For speed reasons, reuse StringBuilder
/// </summary>
protected StringBuilder RawEmailSB;
/// <summary>
/// Reads the complete text of a message
/// </summary>
/// <param name="MessageNo">Email to retrieve</param>
/// <param name="EmailText">ASCII string of complete message</param>
/// <returns></returns>
public bool GetRawEmail(int MessageNo, out string EmailText)
{
//send 'RETR int' command to server
if (!SendRetrCommand(MessageNo))
{
EmailText = null;
return false;
}
//get the lines
string response;
int LineCounter = 0;
//empty StringBuilder
if (RawEmailSB == null)
{
RawEmailSB = new StringBuilder(100000);
}
else
{
RawEmailSB.Length = 0;
}
isTraceRawEmail = true;
while (readMultiLine(out response))
{
LineCounter += 1;
}
EmailText = RawEmailSB.ToString();
TraceFrom("email with {0} lines, {1} chars received", LineCounter.ToString(), EmailText.Length);
return true;
}
///// <summary>
///// Requests from POP3 server a specific email and returns a stream with the message content (header and body)
///// </summary>
///// <param name="MessageNo"></param>
///// <param name="EmailStreamReader"></param>
///// <returns>false: POP3 server cannot provide this message</returns>
//public bool GetEmailStream(int MessageNo, out StreamReader EmailStreamReader) {
// //send 'RETR int' command to server
// if (!SendRetrCommand(MessageNo)) {
// EmailStreamReader = null;
// return false;
// }
// EmailStreamReader = sslStreamReader;
// return true;
//}
/// <summary>
/// Unmark any emails from deletion. The server only deletes email really
/// once the connection is properly closed.
/// </summary>
/// <returns>true: emails are unmarked from deletion</returns>
public bool UndeleteAllEmails()
{
EnsureState(Pop3ConnectionStateEnum.Connected);
string response;
return executeCommand("RSET", out response);
}
/// <summary>
/// Get mailbox statistics
/// </summary>
/// <param name="NumberOfMails"></param>
/// <param name="MailboxSize"></param>
/// <returns></returns>
public bool GetMailboxStats(out int NumberOfMails, out int MailboxSize)
{
EnsureState(Pop3ConnectionStateEnum.Connected);
//interpret response
string response;
NumberOfMails = 0;
MailboxSize = 0;
if (executeCommand("STAT", out response))
{
//got a positive response
string[] responseParts = response.Split(' ');
if (responseParts.Length < 2)
{
//response format wrong
throw new Pop3Exception("Server " + popServer + " sends illegally formatted response." +
"\nExpected format: +OK int int" +
"\nReceived response: " + response);
}
NumberOfMails = int.Parse(responseParts[1]);
MailboxSize = int.Parse(responseParts[2]);
return true;
}
return false;
}
/// <summary>
/// Send RETR command to POP 3 server to fetch one particular message
/// </summary>
/// <param name="MessageNo">ID of message required</param>
/// <returns>false: negative server respond, message not delivered</returns>
protected bool SendRetrCommand(int MessageNo)
{
EnsureState(Pop3ConnectionStateEnum.Connected);
// retrieve mail with message number
string response;
if (!executeCommand("RETR " + MessageNo.ToString(), out response))
{
CallWarning("GetRawEmail", response, "negative response for email (ID: {0}) request", MessageNo);
return false;
}
return true;
}
//Helper methodes
//---------------
/// <summary>
/// sends the 4 letter command to POP3 server (adds CRLF) and waits for the
/// response of the server
/// </summary>
/// <param name="command">command to be sent to server</param>
/// <param name="response">answer from server</param>
/// <returns>false: server sent negative acknowledge, i.e. server could not execute command</returns>
public bool isDebug = false;
private bool executeCommand(string command, out string response)
{
//send command to server
byte[] commandBytes = System.Text.Encoding.ASCII.GetBytes((command + CRLF).ToCharArray());
CallTrace("Tx '{0}'", command);
bool isSupressThrow = false;
try
{
pop3Stream.Write(commandBytes, 0, commandBytes.Length);
if (isDebug)
{
isDebug = false;
throw new IOException("Test", new SocketException(10053));
}
}
catch (IOException ex)
{
//Unable to write data to the transport connection. Check if reconnection should be tried
isSupressThrow = executeReconnect(ex, command, commandBytes);
if (!isSupressThrow)
{
throw;
}
}
pop3Stream.Flush();
//read response from server
response = null;
try
{
response = pop3StreamReader.ReadLine();
}
catch (IOException ex)
{
//Unable to write data to the transport connection. Check if reconnection should be tried
isSupressThrow = executeReconnect(ex, command, commandBytes);
if (isSupressThrow)
{
//wait for response one more time
response = pop3StreamReader.ReadLine();
}
else
{
throw;
}
}
if (response == null)
{
throw new Pop3Exception("Server " + popServer + " has not responded, timeout has occured.");
}
CallTrace("Rx '{0}'", response);
return (response.Length > 0 && response[0] == '+');
}
/// <summary>
/// reconnect, if there is a timeout exception and isAutoReconnect is true
///
/// </summary>
private bool executeReconnect(IOException ex, string command, byte[] commandBytes)
{
if (ex.InnerException != null && ex.InnerException is SocketException)
{
//SocketException
SocketException innerEx = (SocketException)ex.InnerException;
if (innerEx.ErrorCode == 10053)
{
//probably timeout: An established connection was aborted by the software in your host machine.
CallWarning("ExecuteCommand", "", "probably timeout occured");
if (isAutoReconnect)
{
//try to reconnect and send one more time
isTimeoutReconnect = true;
try
{
CallTrace(" try to auto reconnect");
Connect();
CallTrace(" reconnect successful, try to resend command");
CallTrace("Tx '{0}'", command);
pop3Stream.Write(commandBytes, 0, commandBytes.Length);
pop3Stream.Flush();
return true;
}
finally
{
isTimeoutReconnect = false;
}
}
}
}
return false;
}
/*
/// <summary>
/// sends the 4 letter command to POP3 server (adds CRLF)
/// </summary>
/// <param name="command"></param>
protected void SendCommand(string command) {
byte[] commandBytes = System.Text.Encoding.ASCII.GetBytes((command + CRLF).ToCharArray());
CallTrace("Tx '{0}'", command);
try {
pop3Stream.Write(commandBytes, 0, commandBytes.Length);
} catch (IOException ex) {
//Unable to write data to the transport connection:
if (ex.InnerException!=null && ex.InnerException is SocketException) {
//SocketException
SocketException innerEx = (SocketException)ex.InnerException;
if (innerEx.ErrorCode==10053) {
//probably timeout: An established connection was aborted by the software in your host machine.
CallWarning("SendCommand", "", "probably timeout occured");
if (isAutoReconnect) {
//try to reconnect and send one more time
isTimeoutReconnect = true;
try {
CallTrace(" try to auto reconnect");
Connect();
CallTrace(" reconnect successful, try to resend command");
CallTrace("Tx '{0}'", command);
pop3Stream.Write(commandBytes, 0, commandBytes.Length);
} finally {
isTimeoutReconnect = false;
}
return;
}
}
}
throw;
}
pop3Stream.Flush();
}
*/
/// <summary>
/// read single line response from POP3 server.
/// <example>Example server response: +OK asdfkjahsf</example>
/// </summary>
/// <param name="response">response from POP3 server</param>
/// <returns>true: positive response</returns>
protected bool readSingleLine(out string response)
{
response = null;
try
{
response = pop3StreamReader.ReadLine();
}
catch (Exception ex)
{
string s = ex.Message;
}
if (response == null)
{
throw new Pop3Exception("Server " + popServer + " has not responded, timeout has occured.");
}
CallTrace("Rx '{0}'", response);
return (response.Length > 0 && response[0] == '+');
}
/// <summary>
/// read one line in multiline mode from the POP3 server.
/// </summary>
/// <param name="response">line received</param>
/// <returns>false: end of message</returns>
/// <returns></returns>
protected bool readMultiLine(out string response)
{
response = null;
response = pop3StreamReader.ReadLine();
if (response == null)
{
throw new Pop3Exception("Server " + popServer + " has not responded, probably timeout has occured.");
}
if (isTraceRawEmail)
{
//collect all responses as received
RawEmailSB.Append(response + CRLF);
}
//check for byte stuffing, i.e. if a line starts with a '.', another '.' is added, unless
//it is the last line
if (response.Length > 0 && response[0] == '.')
{
if (response == ".")
{
//closing line found
return false;
}
//remove the first '.'
response = response.Substring(1, response.Length - 1);
}
return true;
}
#region | IDisposable Members |
public void Dispose()
{
Disconnect();
}
#endregion | IDisposable Members |
}
}
/// <summary>
/// This class adds a few internet mail headers not already exposed by the
/// System.Net.MailMessage. It also provides support to encapsulate the
/// nested mail attachments in the Children collection.
/// </summary>
public class MailMessageEx : MailMessage
{
public const string EmailRegexPattern = "(['\"]{1,}.+['\"]{1,}\\s+)?<?[\\w\\.\\-]+@[^\\.][\\w\\.\\-]+\\.[a-z]{2,}>?";
private long _octets;
public long Octets
{
get { return _octets; }
set { _octets = value; }
}
private int _messageNumber;
/// <summary>
/// Gets or sets the message number for the MailMessage on the POP3 server.
/// </summary>
/// <value>The message number.</value>
public int MessageNumber
{
get { return _messageNumber; }
internal set { _messageNumber = value; }
}
private static readonly char[] AddressDelimiters = new char[] { ',', ';' };
private List<MailMessageEx> _children;
/// <summary>
/// Gets the children MailMessage attachments.
/// </summary>
/// <value>The children MailMessage attachments.</value>
public List<MailMessageEx> Children
{
get
{
return _children;
}
}
public string PlainTextBody
{
get
{
if (ContentType.MediaType == Mime.MediaTypes.TextPlain)
return Body;
//Otherwise, find the text/plain mime part and return it's content
foreach (AlternateView view in AlternateViews)
{
if (view.ContentType.MediaType == Mime.MediaTypes.TextPlain)
{
System.IO.StreamReader rdr = new System.IO.StreamReader(view.ContentStream);
return (rdr.ReadToEnd());
}
}
//No Plain Text part was found, return an empty string
return "";
}
}
/// <summary>
/// Gets the delivery date.
/// </summary>
/// <value>The delivery date.</value>
public DateTime DeliveryDate
{
get
{
string date = GetHeader(MailHeaders.Date);
if (string.IsNullOrEmpty(date))
{
return DateTime.MinValue;
}
return Convert.ToDateTime(date);
}
}
/// <summary>
/// Gets the return address.
/// </summary>
/// <value>The return address.</value>
public MailAddress ReturnAddress
{
get
{
string replyTo = GetHeader(MailHeaders.ReplyTo);
if (string.IsNullOrEmpty(replyTo))
{
return null;
}
return CreateMailAddress(replyTo);
}
}
/// <summary>
/// Gets the routing.
/// </summary>
/// <value>The routing.</value>
public string Routing
{
get { return GetHeader(MailHeaders.Received); }
}
/// <summary>
/// Gets the message id.
/// </summary>
/// <value>The message id.</value>
public string MessageId
{
get { return GetHeader(MailHeaders.MessageId); }
}
public string ReplyToMessageId
{
get { return GetHeader(MailHeaders.InReplyTo, true); }
}
/// <summary>
/// Gets the mime version.
/// </summary>
/// <value>The mime version.</value>
public string MimeVersion
{
get { return GetHeader(Mime.MimeHeaders.MimeVersion); }
}
/// <summary>
/// Gets the content id.
/// </summary>
/// <value>The content id.</value>
public string ContentId
{
get { return GetHeader(Mime.MimeHeaders.ContentId); }
}
/// <summary>
/// Gets the content description;
/// </summary>
/// <value>The content description.</value>
public string ContentDescription
{
get { return GetHeader(Mime.MimeHeaders.ContentDescription); }
}
/// <summary>
/// Gets the content disposition.
/// </summary>
/// <value>The content disposition.</value>
public ContentDisposition ContentDisposition
{
get
{
string contentDisposition = GetHeader(Mime.MimeHeaders.ContentDisposition);
if (string.IsNullOrEmpty(contentDisposition))
{
return null;
}
return new ContentDisposition(contentDisposition);
}
}
/// <summary>
/// Gets the type of the content.
/// </summary>
/// <value>The type of the content.</value>
public ContentType ContentType
{
get
{
string contentType = GetHeader(Mime.MimeHeaders.ContentType);
if (string.IsNullOrEmpty(contentType))
{
return null;
}
return Mime.MimeReader.GetContentType(contentType);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="MailMessageEx"/> class.
/// </summary>
public MailMessageEx()
: base()
{
_children = new List<MailMessageEx>();
}
/// <summary>
/// Gets the header.
/// </summary>
/// <param name="header">The header.</param>
/// <returns></returns>
private string GetHeader(string header)
{
return GetHeader(header, false);
}
private string GetHeader(string header, bool stripBrackts)
{
if (stripBrackts)
{
return Mime.MimeEntity.TrimBrackets(Headers[header]);
}
return Headers[header];
}
/// <summary>
/// Creates the mail message from entity.
/// </summary>
/// <param name="entity">The entity.</param>
/// <returns></returns>
public static MailMessageEx CreateMailMessageFromEntity(Mime.MimeEntity entity)
{
MailMessageEx message = new MailMessageEx();
string value;
foreach(string key in entity.Headers.AllKeys)
{
value = entity.Headers[key];
if (value.Equals(string.Empty))
{
value = " ";
}
message.Headers.Add(key.ToLowerInvariant(), value);
switch (key.ToLowerInvariant())
{
case MailHeaders.Bcc:
MailMessageEx.PopulateAddressList(value, message.Bcc);
break;
case MailHeaders.Cc:
MailMessageEx.PopulateAddressList(value, message.CC);
break;
case MailHeaders.From:
message.From = MailMessageEx.CreateMailAddress(value);
break;
case MailHeaders.ReplyTo:
message.ReplyTo = MailMessageEx.CreateMailAddress(value);
break;
case MailHeaders.Subject:
message.Subject = value;
break;
case MailHeaders.To:
MailMessageEx.PopulateAddressList(value, message.To);
break;
}
}
return message;
}
/// <summary>
/// Creates the mail address
/// </summary>
/// <param name="address">The address</param>
/// <returns></returns>
public static MailAddress CreateMailAddress(string address)
{
try
{
return new MailAddress(address.Trim('\t'));
}
catch (FormatException e)
{
throw new ApplicationException("Unable to create mail address from provided string: " + address, e);
}
}
/// <summary>
/// Populates the address list.
/// </summary>
/// <param name="addressList">The address list.</param>
/// <param name="recipients">The recipients.</param>
public static void PopulateAddressList(string addressList, MailAddressCollection recipients)
{
foreach (MailAddress address in GetMailAddresses(addressList))
{
recipients.Add(address);
}
}
public static IEnumerable<MailAddress> GetMailAddresses(string addressList)
{
Regex email = new Regex(EmailRegexPattern);
foreach (Match match in email.Matches(addressList))
{
yield return CreateMailAddress(match.Value);
}
/*
string[] addresses = addressList.Split(AddressDelimiters);
foreach (string address in addresses)
{
yield return CreateMailAddress(address);
}*/
}
}
public static class MailHeaders
{
public const string Bcc = "bcc";
public const string Cc = "cc";
public const string Date = "date";
public const string From = "from";
public const string Importance = "importance";
public const string InReplyTo = "in-reply-to";
public const string MessageId = "message-id";
public const string Received = "received";
public const string ReplyTo = "reply-to";
public const string Subject = "subject";
public const string To = "to";
}
}