/*
最近写了一个自动收邮件的机器人,原来一开始偷懒"娶"了 COM 组件 JMail:
《封装 JMail 4.4 的 POP3 为 .Net 组件 (.dll 程序集),实现 "邮件(附件) 到达" 等 "事件"!》
后来经人介绍认识了 OpenPOP.Net
我就移情别恋,再后来我们就相爱了,再后来我就修理她:
加一个小功能(红色字体代码部分),可用于收取邮件时监测数据流量!
老规矩: 我的代码随便 Copy & Paste 到任意一个 .cs 文件中 csc 即可测试! (结构不够清晰,目的就是方便测试代码)
*/
//OpenPOP.Net
namespace OpenPOP.MIMEParser
{
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Globalization;
/// <summary>
/// Summary description for Attachment.
/// </summary>
public class Attachment : IComparable
{
#region Member Variables
private string _contentType = null;
private string _contentCharset = null;
private string _contentFormat = null;
private string _contentTransferEncoding = null;
private string _contentDescription = null;
private string _contentDisposition = null;
private string _contentFileName = "";
private string _defaultFileName = "body.htm";
private string _defaultFileName2 = "body*.htm";
private string _defaultReportFileName = "report.htm";
private string _defaultMIMEFileName = "body.eml";
private string _defaultMSTNEFFileName = "winmail.dat";
private string _contentID = null;
private long _contentLength = 0;
private string _rawAttachment = null;
private bool _inBytes = false;
private byte[] _rawBytes = null;
#endregion
#region Properties
/// <summary>
/// raw attachment content bytes
/// </summary>
public byte[] RawBytes
{
get
{
return _rawBytes;
}
set
{
_rawBytes = value;
}
}
/// <summary>
/// whether attachment is in bytes
/// </summary>
public bool InBytes
{
get
{
return _inBytes;
}
set
{
_inBytes = value;
}
}
/// <summary>
/// Content length
/// </summary>
public long ContentLength
{
get
{
return _contentLength;
}
}
/// <summary>
/// verify the attachment whether it is a real attachment or not
/// </summary>
/// <remarks>this is so far not comprehensive and needs more work to finish</remarks>
public bool NotAttachment
{
get
{
/* if (_contentDisposition==null||_contentType==null)
return true;
else
return (_contentDisposition.IndexOf("attachment")==-1 && _contentType.IndexOf("text/plain")!=-1); */
/* if (_contentType==null)
return true;
else
return (_contentFileName!="");*/
if ((_contentType == null || _contentFileName == "") && _contentID == null) //&&_contentType.ToLower().IndexOf("text/")!=-1)
return true;
else
return false;
}
}
/// <summary>
/// Content format
/// </summary>
public string ContentFormat
{
get
{
return _contentFormat;
}
}
/// <summary>
/// Content charset
/// </summary>
public string ContentCharset
{
get
{
return _contentCharset;
}
}
/// <summary>
/// default file name
/// </summary>
public string DefaultFileName
{
get
{
return _defaultFileName;
}
set
{
_defaultFileName = value;
}
}
/// <summary>
/// default file name 2
/// </summary>
public string DefaultFileName2
{
get
{
return _defaultFileName2;
}
set
{
_defaultFileName2 = value;
}
}
/// <summary>
/// default report file name
/// </summary>
public string DefaultReportFileName
{
get
{
return _defaultReportFileName;
}
set
{
_defaultReportFileName = value;
}
}
/// <summary>
/// default MIME File Name
/// </summary>
public string DefaultMIMEFileName
{
get
{
return _defaultMIMEFileName;
}
set
{
_defaultMIMEFileName = value;
}
}
/// <summary>
/// Content Type
/// </summary>
public string ContentType
{
get
{
return _contentType;
}
}
/// <summary>
/// Content Transfer Encoding
/// </summary>
public string ContentTransferEncoding
{
get
{
return _contentTransferEncoding;
}
}
/// <summary>
/// Content Description
/// </summary>
public string ContentDescription
{
get
{
return _contentDescription;
}
}
/// <summary>
/// Content File Name
/// </summary>
public string ContentFileName
{
get
{
return _contentFileName;
}
set
{
_contentFileName = value;
}
}
/// <summary>
/// Content Disposition
/// </summary>
public string ContentDisposition
{
get
{
return _contentDisposition;
}
}
/// <summary>
/// Content ID
/// </summary>
public string ContentID
{
get
{
return _contentID;
}
}
/// <summary>
/// Raw Attachment
/// </summary>
public string RawAttachment
{
get
{
return _rawAttachment;
}
}
/// <summary>
/// decoded attachment in bytes
/// </summary>
public byte[] DecodedAttachment
{
get
{
return DecodedAsBytes();
}
}
#endregion
/// <summary>
/// release all objects
/// </summary>
~Attachment()
{
_rawBytes = null;
_rawAttachment = null;
}
/// <summary>
/// New Attachment
/// </summary>
/// <param name="bytAttachment">attachment bytes content</param>
/// <param name="lngFileLength">file length</param>
/// <param name="strFileName">file name</param>
/// <param name="strContentType">content type</param>
public Attachment(byte[] bytAttachment, long lngFileLength, string strFileName, string strContentType)
{
_inBytes = true;
_rawBytes = bytAttachment;
_contentLength = lngFileLength;
_contentFileName = strFileName;
_contentType = strContentType;
}
/// <summary>
/// New Attachment
/// </summary>
/// <param name="bytAttachment">attachment bytes content</param>
/// <param name="strFileName">file name</param>
/// <param name="strContentType">content type</param>
public Attachment(byte[] bytAttachment, string strFileName, string strContentType)
{
_inBytes = true;
_rawBytes = bytAttachment;
_contentLength = bytAttachment.Length;
_contentFileName = strFileName;
_contentType = strContentType;
}
/// <summary>
/// New Attachment
/// </summary>
/// <param name="strAttachment">attachment content</param>
/// <param name="strContentType">content type</param>
/// <param name="blnParseHeader">whether only parse the header or not</param>
public Attachment(string strAttachment, string strContentType, bool blnParseHeader)
{
if (!blnParseHeader)
{
_contentFileName = _defaultMSTNEFFileName;
_contentType = strContentType;
}
this.NewAttachment(strAttachment, blnParseHeader);
}
/// <summary>
/// New Attachment
/// </summary>
/// <param name="strAttachment">attachment content</param>
public Attachment(string strAttachment)
{
this.NewAttachment(strAttachment, true);
}
/// <summary>
/// create attachment
/// </summary>
/// <param name="strAttachment">raw attachment text</param>
/// <param name="blnParseHeader">parse header</param>
private void NewAttachment(string strAttachment, bool blnParseHeader)
{
_inBytes = false;
if (strAttachment == null)
throw new ArgumentNullException("strAttachment");
StringReader srReader = new StringReader(strAttachment);
if (blnParseHeader)
{
string strLine = srReader.ReadLine();
while (Utility.IsNotNullTextEx(strLine))
{
ParseHeader(srReader, ref strLine);
if (Utility.IsOrNullTextEx(strLine))
break;
else
strLine = srReader.ReadLine();
}
}
this._rawAttachment = srReader.ReadToEnd();
_contentLength = this._rawAttachment.Length;
}
/// <summary>
/// Parse header fields and set member variables
/// </summary>
/// <param name="srReader">string reader</param>
/// <param name="strLine">header line</param>
private void ParseHeader(StringReader srReader, ref string strLine)
{
string[] array = Utility.GetHeadersValue(strLine); //Regex.Split(strLine,":");
string[] values = Regex.Split(array[1], ";"); //array[1].Split(';');
string strRet = null;
switch (array[0].ToUpper())
{
case "CONTENT-TYPE":
if (values.Length > 0)
_contentType = values[0].Trim();
if (values.Length > 1)
{
_contentCharset = Utility.GetQuotedValue(values[1], "=", "charset");
}
if (values.Length > 2)
{
_contentFormat = Utility.GetQuotedValue(values[2], "=", "format");
}
_contentFileName = Utility.ParseFileName(strLine);
if (_contentFileName == "")
{
strRet = srReader.ReadLine();
if (strRet == "")
{
strLine = "";
break;
}
_contentFileName = Utility.ParseFileName(strLine);
if (_contentFileName == "")
ParseHeader(srReader, ref strRet);
}
break;
case "CONTENT-TRANSFER-ENCODING":
_contentTransferEncoding = Utility.SplitOnSemiColon(array[1])[0].Trim();
break;
case "CONTENT-DESCRIPTION":
_contentDescription = Utility.DecodeText(Utility.SplitOnSemiColon(array[1])[0].Trim());
break;
case "CONTENT-DISPOSITION":
if (values.Length > 0)
_contentDisposition = values[0].Trim();
///<bug>reported by grandepuffo @ https://sourceforge.net/forum/message.php?msg_id=2589759
//_contentFileName=values[1];
if (values.Length > 1)
{
_contentFileName = values[1];
}
else
{
_contentFileName = "";
}
if (_contentFileName == "")
_contentFileName = srReader.ReadLine();
_contentFileName = _contentFileName.Replace("\t", "");
_contentFileName = Utility.GetQuotedValue(_contentFileName, "=", "filename");
_contentFileName = Utility.DecodeText(_contentFileName);
break;
case "CONTENT-ID":
_contentID = Utility.SplitOnSemiColon(array[1])[0].Trim('<').Trim('>');
break;
}
}
/// <summary>
/// verify the encoding
/// </summary>
/// <param name="encoding">encoding to verify</param>
/// <returns>true if encoding</returns>
private bool IsEncoding(string encoding)
{
return _contentTransferEncoding.ToLower().IndexOf(encoding.ToLower()) != -1;
}
/// <summary>
/// Decode the attachment to text
/// </summary>
/// <returns>Decoded attachment text</returns>
public string DecodeAsText()
{
string decodedAttachment = null;
try
{
if (_contentType.ToLower() == "message/rfc822".ToLower())
decodedAttachment = Utility.DecodeText(_rawAttachment);
else if (_contentTransferEncoding != null)
{
decodedAttachment = _rawAttachment;
if (!IsEncoding("7bit"))
{
if (IsEncoding("8bit") && _contentCharset != null & _contentCharset != "")
decodedAttachment = Utility.Change(decodedAttachment, _contentCharset);
if (Utility.IsQuotedPrintable(_contentTransferEncoding))
decodedAttachment = DecodeQP.ConvertHexContent(decodedAttachment);
else if (IsEncoding("8bit"))
decodedAttachment = decodedAttachment;
else
decodedAttachment = Utility.deCodeB64s(Utility.RemoveNonB64(decodedAttachment));
}
}
else if (_contentCharset != null)
decodedAttachment = Utility.Change(_rawAttachment, _contentCharset); //Encoding.Default.GetString(Encoding.GetEncoding(_contentCharset).GetBytes(_rawAttachment));
else
decodedAttachment = _rawAttachment;
}
catch
{
decodedAttachment = _rawAttachment;
}
return decodedAttachment;
}
/// <summary>
/// decode attachment to be a message object
/// </summary>
/// <returns>message</returns>
public Message DecodeAsMessage()
{
bool blnRet = false;
return new Message(ref blnRet, "", false, _rawAttachment, false);
}
/// <summary>
/// Decode the attachment to bytes
/// </summary>
/// <returns>Decoded attachment bytes</returns>
public byte[] DecodedAsBytes()
{
if (_rawAttachment == null)
return null;
if (_contentFileName != "")
{
byte[] decodedBytes = null;
if (_contentType != null && _contentType.ToLower() == "message/rfc822".ToLower())
decodedBytes = Encoding.Default.GetBytes(Utility.DecodeText(_rawAttachment));
else if (_contentTransferEncoding != null)
{
string bytContent = _rawAttachment;
if (!IsEncoding("7bit"))
{
if (IsEncoding("8bit") && _contentCharset != null & _contentCharset != "")
bytContent = Utility.Change(bytContent, _contentCharset);
if (Utility.IsQuotedPrintable(_contentTransferEncoding))
decodedBytes = Encoding.Default.GetBytes(DecodeQP.ConvertHexContent(bytContent));
else if (IsEncoding("8bit"))
decodedBytes = Encoding.Default.GetBytes(bytContent);
else
decodedBytes = Convert.FromBase64String(Utility.RemoveNonB64(bytContent));
}
else
decodedBytes = Encoding.Default.GetBytes(bytContent);
}
else if (_contentCharset != null)
decodedBytes = Encoding.Default.GetBytes(Utility.Change(_rawAttachment, _contentCharset)); //Encoding.Default.GetString(Encoding.GetEncoding(_contentCharset).GetBytes(_rawAttachment));
else
decodedBytes = Encoding.Default.GetBytes(_rawAttachment);
return decodedBytes;
}
else
{
return null;
}
}
public int CompareTo(object attachment)
{
return (this.RawAttachment.CompareTo(((Attachment) (attachment)).RawAttachment));
}
}
public enum MessageImportanceType
{
HIGH = 5,
NORMAL = 3,
LOW = 1
}
/// <summary>
/// Decoding Quoted-Printable text
///
/// </summary>
public class DecodeQP
{
public DecodeQP()
{
}
/// <summary>
/// Decoding Quoted-Printable string
/// </summary>
/// <param name="Hexstring">Quoted-Printable encoded string</param>
/// <param name="encode">encoding method</param>
/// <returns>decoded string</returns>
public static string ConvertHexToString(string Hexstring, string Encoding)
{
try
{
return ConvertHexToString(Hexstring, System.Text.Encoding.GetEncoding(Encoding));
}
catch
{
return ConvertHexContent(Hexstring);
}
}
/// <summary>
/// Decoding Quoted-Printable string
/// </summary>
/// <param name="Hexstring">Quoted-Printable encoded string</param>
/// <param name="encode">encoding method</param>
/// <returns>decoded string</returns>
public static string ConvertHexToString(string Hexstring, Encoding encode)
{
try
{
if (Hexstring == null || Hexstring.Equals("")) return "";
if (Hexstring.StartsWith("=")) Hexstring = Hexstring.Substring(1);
string[] aHex = Hexstring.Split(new char[1] {'='});
byte[] abyte = new Byte[aHex.Length];
for (int i = 0; i < abyte.Length; i++)
{
// Console.WriteLine(aHex[i]);
abyte[i] = (byte) int.Parse(aHex[i], NumberStyles.HexNumber);
}
return encode.GetString(abyte);
}
catch
{
return Hexstring;
}
}
/// <summary>
/// Decoding Quoted-Printable string at a position
/// </summary>
/// <param name="Hexstring">Quoted-Printable encoded string</param>
/// <param name="encode">encoding method, "Default" is suggested</param>
/// <param name="nStart">position to start, normally 0</param>
/// <returns>decoded string</returns>
public static string ConvertHexContent(string Hexstring, Encoding encode, long nStart)
{
if (nStart >= Hexstring.Length) return Hexstring;
//to hold string to be decoded
StringBuilder sbHex = new StringBuilder();
sbHex.Append("");
//to hold decoded string
StringBuilder sbEncoded = new StringBuilder();
sbEncoded.Append("");
//wether we reach Quoted-Printable string
bool isBegin = false;
string temp;
int i = (int) nStart;
while (i < Hexstring.Length)
{
//init next loop
sbHex.Remove(0, sbHex.Length);
isBegin = false;
int count = 0;
while (i < Hexstring.Length)
{
temp = Hexstring.Substring(i, 1); //before reaching Quoted-Printable string, one char at a time
if (temp.StartsWith("="))
{
temp = Hexstring.Substring(i, 3); //get 3 chars
if (temp.EndsWith("\r\n")) //return char
{
if (isBegin && (count%2 == 0))
break;
// sbEncoded.Append("");
i = i + 3;
}
else if (!temp.EndsWith("3D"))
{
sbHex.Append(temp);
isBegin = true; //we reach Quoted-Printable string, put it into buffer
i = i + 3;
count++;
}
else //if it ends with 3D, it is "="
{
if (isBegin && (count%2 == 0)) //wait until even items to handle all character sets
break;
sbEncoded.Append("=");
i = i + 3;
}
}
else
{
if (isBegin) //we have got the how Quoted-Printable string, break it
break;
sbEncoded.Append(temp); //not Quoted-Printable string, put it into buffer
i++;
}
}
//decode Quoted-Printable string
sbEncoded.Append(ConvertHexToString(sbHex.ToString(), encode));
}
return sbEncoded.ToString();
}
/// <summary>
/// Decoding Quoted-Printable string using default encoding and begin at 0
/// </summary>
/// <param name="Hexstring">Quoted-Printable encoded string</param>
/// <returns>decoded string</returns>
public static string ConvertHexContent(string Hexstring)
{
if (Hexstring == null || Hexstring.Equals("")) return Hexstring;
return ConvertHexContent(Hexstring, Encoding.Default, 0);
}
}
/// <summary>
/// Message Parser.
/// </summary>
public class Message
{
#region Member Variables
private ArrayList _attachments = new ArrayList();
private string _rawHeader = null;
private string _rawMessage = null;
private string _rawMessageBody = null;
private int _attachmentCount = 0;
private string _replyTo = null;
private string _replyToEmail = null;
private string _from = null;
private string _fromEmail = null;
private string _date = null;
private string _dateTimeInfo = null;
private string _subject = null;
private string[] _to = new string[0];
private string[] _cc = new string[0];
private string[] _bcc = new string[0];
private ArrayList _keywords = new ArrayList();
private string _contentType = null;
private string _contentCharset = null;
private string _reportType = null;
private string _contentTransferEncoding = null;
private bool _html = false;
private long _contentLength = 0;
private string _contentEncoding = null;
private string _returnPath = null;
private string _mimeVersion = null;
private string _received = null;
private string _importance = null;
private string _messageID = null;
private string _attachmentboundry = null;
private string _attachmentboundry2 = null;
private bool _hasAttachment = false;
private string _dispositionNotificationTo = null;
private ArrayList _messageBody = new ArrayList();
private string _basePath = null;
private bool _autoDecodeMSTNEF = false;
private Hashtable _customHeaders = new Hashtable();
#endregion
#region Properties
/// <summary>
/// custom headers
/// </summary>
public Hashtable CustomHeaders
{
get
{
return _customHeaders;
}
set
{
_customHeaders = value;
}
}
/// <summary>
/// whether auto decoding MS-TNEF attachment files
/// </summary>
public bool AutoDecodeMSTNEF
{
get
{
return _autoDecodeMSTNEF;
}
set
{
_autoDecodeMSTNEF = value;
}
}
/// <summary>
/// path to extract MS-TNEF attachment files
/// </summary>
public string BasePath
{
get
{
return _basePath;
}
set
{
try
{
if (value.EndsWith("\\"))
_basePath = value;
else
_basePath = value + "\\";
}
catch
{
}
}
}
/// <summary>
/// message keywords
/// </summary>
public ArrayList Keywords
{
get
{
return _keywords;
}
}
/// <summary>
/// disposition notification
/// </summary>
public string DispositionNotificationTo
{
get
{
return _dispositionNotificationTo;
}
}
/// <summary>
/// received server
/// </summary>
public string Received
{
get
{
return _received;
}
}
/// <summary>
/// importance level
/// </summary>
public string Importance
{
get
{
return _importance;
}
}
/// <summary>
/// importance level type
/// </summary>
public MessageImportanceType ImportanceType
{
get
{
switch (_importance.ToUpper())
{
case "5":
case "HIGH":
return MessageImportanceType.HIGH;
case "3":
case "NORMAL":
return MessageImportanceType.NORMAL;
case "1":
case "LOW":
return MessageImportanceType.LOW;
default:
return MessageImportanceType.NORMAL;
}
}
}
/// <summary>
/// Content Charset
/// </summary>
public string ContentCharset
{
get
{
return _contentCharset;
}
}
/// <summary>
/// Content Transfer Encoding
/// </summary>
public string ContentTransferEncoding
{
get
{
return _contentTransferEncoding;
}
}
/// <summary>
/// Message Bodies
/// </summary>
public ArrayList MessageBody
{
get
{
return _messageBody;
}
}
/// <summary>
/// Attachment Boundry
/// </summary>
public string AttachmentBoundry
{
get
{
return _attachmentboundry;
}
}
/// <summary>
/// Alternate Attachment Boundry
/// </summary>
public string AttachmentBoundry2
{
get
{
return _attachmentboundry2;
}
}
/// <summary>
/// Attachment Count
/// </summary>
public int AttachmentCount
{
get
{
return _attachmentCount;
}
}
/// <summary>
/// Attachments
/// </summary>
public ArrayList Attachments
{
get
{
return _attachments;
}
}
/// <summary>
/// CC
/// </summary>
public string[] CC
{
get
{
return _cc;
}
}
/// <summary>
/// BCC
/// </summary>
public string[] BCC
{
get
{
return _bcc;
}
}
/// <summary>
/// TO
/// </summary>
public string[] TO
{
get
{
return _to;
}
}
/// <summary>
/// Content Encoding
/// </summary>
public string ContentEncoding
{
get
{
return _contentEncoding;
}
}
/// <summary>
/// Content Length
/// </summary>
public long ContentLength
{
get
{
return _contentLength;
}
}
/// <summary>
/// Content Type
/// </summary>
public string ContentType
{
get
{
return _contentType;
}
}
/// <summary>
/// Report Type
/// </summary>
public string ReportType
{
get
{
return _reportType;
}
}
/// <summary>
/// HTML
/// </summary>
public bool HTML
{
get
{
return _html;
}
}
/// <summary>
/// Date
/// </summary>
public string Date
{
get
{
return _date;
}
}
/// <summary>
/// DateTime Info
/// </summary>
public string DateTimeInfo
{
get
{
return _dateTimeInfo;
}
}
/// <summary>
/// From name
/// </summary>
public string From
{
get
{
return _from;
}
}
/// <summary>
/// From Email
/// </summary>
public string FromEmail
{
get
{
return _fromEmail;
}
}
/// <summary>
/// Reply to name
/// </summary>
public string ReplyTo
{
get
{
return _replyTo;
}
}
/// <summary>
/// Reply to email
/// </summary>
public string ReplyToEmail
{
get
{
return _replyToEmail;
}
}
/// <summary>
/// whether has attachment
/// </summary>
public bool HasAttachment
{
get
{
return _hasAttachment;
}
}
/// <summary>
/// raw message body
/// </summary>
public string RawMessageBody
{
get
{
return _rawMessageBody;
}
}
/// <summary>
/// Message ID
/// </summary>
public string MessageID
{
get
{
return _messageID;
}
}
/// <summary>
/// MIME version
/// </summary>
public string MimeVersion
{
get
{
return _mimeVersion;
}
}
/// <summary>
/// raw header
/// </summary>
public string RawHeader
{
get
{
return _rawHeader;
}
}
/// <summary>
/// raw message
/// </summary>
public string RawMessage
{
get
{
return _rawMessage;
}
}
/// <summary>
/// return path
/// </summary>
public string ReturnPath
{
get
{
return _returnPath;
}
}
/// <summary>
/// subject
/// </summary>
public string Subject
{
get
{
return _subject;
}
}
#endregion
/// <summary>
/// release all objects
/// </summary>
~Message()
{
_attachments.Clear();
_attachments = null;
_keywords.Clear();
_keywords = null;
_messageBody.Clear();
_messageBody = null;
_customHeaders.Clear();
_customHeaders = null;
}
/// <summary>
/// New Message
/// </summary>
/// <param name="blnFinish">reference for the finishing state</param>
/// <param name="strBasePath">path to extract MS-TNEF attachment files</param>
/// <param name="blnAutoDecodeMSTNEF">whether auto decoding MS-TNEF attachments</param>
/// <param name="blnOnlyHeader">whether only decode the header without body</param>
/// <param name="strEMLFile">file of email content to load from</param>
public Message(ref bool blnFinish, string strBasePath, bool blnAutoDecodeMSTNEF, bool blnOnlyHeader, string strEMLFile)
{
string strMessage = null;
if (Utility.ReadPlainTextFromFile(strEMLFile, ref strMessage))
{
NewMessage(ref blnFinish, strBasePath, blnAutoDecodeMSTNEF, strMessage, blnOnlyHeader);
}
else
blnFinish = true;
}
/// <summary>
/// New Message
/// </summary>
/// <param name="blnFinish">reference for the finishing state</param>
/// <param name="strBasePath">path to extract MS-TNEF attachment files</param>
/// <param name="blnAutoDecodeMSTNEF">whether auto decoding MS-TNEF attachments</param>
/// <param name="strMessage">raw message content</param>
/// <param name="blnOnlyHeader">whether only decode the header without body</param>
public Message(ref bool blnFinish, string strBasePath, bool blnAutoDecodeMSTNEF, string strMessage, bool blnOnlyHeader)
{
NewMessage(ref blnFinish, strBasePath, blnAutoDecodeMSTNEF, strMessage, blnOnlyHeader);
}
/// <summary>
/// New Message
/// </summary>
/// <param name="blnFinish">reference for the finishing state</param>
/// <param name="strMessage">raw message content</param>
/// <param name="blnOnlyHeader">whether only decode the header without body</param>
public Message(ref bool blnFinish, string strMessage, bool blnOnlyHeader)
{
NewMessage(ref blnFinish, "", false, strMessage, blnOnlyHeader);
}
/// <summary>
/// New Message
/// </summary>
/// <param name="blnFinish">reference for the finishing state</param>
/// <param name="strMessage">raw message content</param>
public Message(ref bool blnFinish, string strMessage)
{
NewMessage(ref blnFinish, "", false, strMessage, false);
}
/// <summary>
/// get valid attachment
/// </summary>
/// <param name="intAttachmentNumber">attachment index in the attachments collection</param>
/// <returns>attachment</returns>
public Attachment GetAttachment(int intAttachmentNumber)
{
if (intAttachmentNumber < 0 || intAttachmentNumber > _attachmentCount || intAttachmentNumber > _attachments.Count)
{
Utility.LogError("GetAttachment():attachment not exist");
throw new ArgumentOutOfRangeException("intAttachmentNumber");
}
return (Attachment) _attachments[intAttachmentNumber];
}
/// <summary>
/// New Message
/// </summary>
/// <param name="blnFinish">reference for the finishing state</param>
/// <param name="strBasePath">path to extract MS-TNEF attachment files</param>
/// <param name="blnAutoDecodeMSTNEF">whether auto decoding MS-TNEF attachments</param>
/// <param name="strMessage">raw message content</param>
/// <param name="blnOnlyHeader">whether only decode the header without body</param>
/// <returns>construction result whether successfully new a message</returns>
private bool NewMessage(ref bool blnFinish, string strBasePath, bool blnAutoDecodeMSTNEF, string strMessage, bool blnOnlyHeader)
{
StringReader srdReader = new StringReader(strMessage);
StringBuilder sbdBuilder = new StringBuilder();
_basePath = strBasePath;
_autoDecodeMSTNEF = blnAutoDecodeMSTNEF;
_rawMessage = strMessage;
string strLine = srdReader.ReadLine();
while (Utility.IsNotNullTextEx(strLine))
{
sbdBuilder.Append(strLine + "\r\n");
ParseHeader(sbdBuilder, srdReader, ref strLine);
if (Utility.IsOrNullTextEx(strLine))
break;
else
strLine = srdReader.ReadLine();
}
_rawHeader = sbdBuilder.ToString();
SetAttachmentBoundry2(_rawHeader);
if (_contentLength == 0)
_contentLength = strMessage.Length; //_rawMessageBody.Length;
if (blnOnlyHeader == false)
{
_rawMessageBody = srdReader.ReadToEnd().Trim();
//the auto reply mail by outlook uses ms-tnef format
if ((_hasAttachment == true && _attachmentboundry != null) || MIMETypes.IsMSTNEF(_contentType))
{
set_attachments();
if (this.Attachments.Count > 0)
{
Attachment at = this.GetAttachment(0);
if (at != null && at.NotAttachment)
this.GetMessageBody(at.DecodeAsText());
else
{
}
//in case body parts as text[0] html[1]
if (this.Attachments.Count > 1 && !this.IsReport())
{
at = this.GetAttachment(1);
if (at != null && at.NotAttachment)
this.GetMessageBody(at.DecodeAsText());
else
{
}
}
}
else
{
}
}
else
{
GetMessageBody(_rawMessageBody);
}
}
blnFinish = true;
return true;
}
/// <summary>
/// parse message body
/// </summary>
/// <param name="strBuffer">raw message body</param>
/// <returns>message body</returns>
public string GetTextBody(string strBuffer)
{
if (strBuffer.EndsWith("\r\n."))
return strBuffer.Substring(0, strBuffer.Length - "\r\n.".Length);
else
return strBuffer;
}
/// <summary>
/// parse message body
/// </summary>
/// <param name="strBuffer">raw message body</param>
public void GetMessageBody(string strBuffer)
{
int end, begin;
string body;
string encoding = "";
begin = end = 0;
_messageBody.Clear();
try
{
if (Utility.IsOrNullTextEx(strBuffer))
return;
else if (Utility.IsOrNullTextEx(_contentType) && _contentTransferEncoding == null)
{
_messageBody.Add(GetTextBody(strBuffer));
}
else if (_contentType != null && _contentType.IndexOf("digest") >= 0)
{
// this is a digest method
//ParseDigestMessage(strBuffer);
_messageBody.Add(GetTextBody(strBuffer));
}
else if (_attachmentboundry2 == null)
{
body = GetTextBody(strBuffer);
if (Utility.IsQuotedPrintable(_contentTransferEncoding))
{
body = DecodeQP.ConvertHexContent(body);
}
else if (Utility.IsBase64(_contentTransferEncoding))
{
body = Utility.deCodeB64s(Utility.RemoveNonB64(body));
}
else if (Utility.IsNotNullText(_contentCharset))
{
body = Encoding.GetEncoding(_contentCharset).GetString(Encoding.Default.GetBytes(body));
}
_messageBody.Add(Utility.RemoveNonB64(body));
}
else
{
begin = 0;
while (begin != -1)
{
// find "\r\n\r\n" denoting end of header
begin = strBuffer.IndexOf("--" + _attachmentboundry2, begin);
if (begin != -1)
{
encoding = MIMETypes.GetContentTransferEncoding(strBuffer, begin);
begin = strBuffer.IndexOf("\r\n\r\n", begin + 1); //strBuffer.LastIndexOfAny(ALPHABET.ToCharArray());
// find end of text
end = strBuffer.IndexOf("--" + _attachmentboundry2, begin + 1);
if (begin != -1)
{
if (end != -1)
{
begin += 4;
if (begin >= end)
continue;
else if (this._contentEncoding != null && this._contentEncoding.IndexOf("8bit") != -1)
body = Utility.Change(strBuffer.Substring(begin, end - begin - 2), _contentCharset);
else
body = strBuffer.Substring(begin, end - begin - 2);
}
else
{
body = strBuffer.Substring(begin);
}
if (Utility.IsQuotedPrintable(encoding))
{
string ret = body;
ret = DecodeQP.ConvertHexContent(ret);
_messageBody.Add(ret);
}
else if (Utility.IsBase64(encoding))
{
string ret = Utility.RemoveNonB64(body);
ret = Utility.deCodeB64s(ret);
if (ret != "\0")
_messageBody.Add(ret);
else
_messageBody.Add(body);
}
else
_messageBody.Add(body);
if (end == -1) break;
}
else
{
break;
}
}
else
{
if (_messageBody.Count == 0)
{
_messageBody.Add(strBuffer);
}
break;
}
}
}
}
catch (Exception e)
{
Utility.LogError("GetMessageBody():" + e.Message);
_messageBody.Add(Utility.deCodeB64s(strBuffer));
}
if (_messageBody.Count > 1)
_html = true;
}
/// <summary>
/// verify if the message is a report
/// </summary>
/// <returns>if it is a report message, return true, else, false</returns>
public bool IsReport()
{
if (Utility.IsNotNullText(_contentType))
return (_contentType.ToLower().IndexOf("report".ToLower()) != -1);
else
return false;
}
/// <summary>
/// verify if the attachment is MIME Email file
/// </summary>
/// <param name="attItem">attachment</param>
/// <returns>if MIME Email file, return true, else, false</returns>
public bool IsMIMEMailFile(Attachment attItem)
{
try
{
return (attItem.ContentFileName.ToLower().EndsWith(".eml".ToLower()) || attItem.ContentType.ToLower() == "message/rfc822".ToLower());
}
catch (Exception e)
{
Utility.LogError("IsMIMEMailFile():" + e.Message);
return false;
}
}
/// <summary>
/// translate pictures url within the body
/// </summary>
/// <param name="strBody">message body</param>
/// <param name="hsbFiles">pictures collection</param>
/// <returns>translated message body</returns>
public string TranslateHTMLPictureFiles(string strBody, Hashtable hsbFiles)
{
try
{
for (int i = 0; i < this.AttachmentCount; i++)
{
Attachment att = this.GetAttachment(i);
if (Utility.IsPictureFile(att.ContentFileName) == true)
{
if (Utility.IsNotNullText(att.ContentID))
//support for embedded pictures
strBody = strBody.Replace("cid:" + att.ContentID, hsbFiles[att.ContentFileName].ToString());
strBody = strBody.Replace(att.ContentFileName, hsbFiles[att.ContentFileName].ToString());
}
}
}
catch (Exception e)
{
Utility.LogError("TranslateHTMLPictureFiles():" + e.Message);
}
return strBody;
}
/// <summary>
/// translate pictures url within the body
/// </summary>
/// <param name="strBody">message body</param>
/// <param name="strPath">path of the pictures</param>
/// <returns>translated message body</returns>
public string TranslateHTMLPictureFiles(string strBody, string strPath)
{
try
{
if (!strPath.EndsWith("\\"))
{
strPath += "\\";
}
for (int i = 0; i < this.AttachmentCount; i++)
{
Attachment att = this.GetAttachment(i);
if (Utility.IsPictureFile(att.ContentFileName) == true)
{
if (Utility.IsNotNullText(att.ContentID))
//support for embedded pictures
strBody = strBody.Replace("cid:" + att.ContentID, strPath + att.ContentFileName);
strBody = strBody.Replace(att.ContentFileName, strPath + att.ContentFileName);
}
}
}
catch (Exception e)
{
Utility.LogError("TranslateHTMLPictureFiles():" + e.Message);
}
return strBody;
}
/// <summary>
/// Get the proper attachment file name
/// </summary>
/// <param name="attItem">attachment</param>
/// <returns>propery attachment file name</returns>
public string GetAttachmentFileName(Attachment attItem)
{
int items = 0;
//return unique body file names
for (int i = 0; i < _attachments.Count; i++)
{
if (attItem.ContentFileName == attItem.DefaultFileName)
{
items++;
attItem.ContentFileName = attItem.DefaultFileName2.Replace("*", items.ToString());
}
}
string name = attItem.ContentFileName;
return (name == null || name == "" ? (IsReport() == true ? (this.IsMIMEMailFile(attItem) == true ? attItem.DefaultMIMEFileName : attItem.DefaultReportFileName) : (attItem.ContentID != null ? attItem.ContentID : attItem.DefaultFileName)) : name);
}
/// <summary>
/// save attachments to a defined path
/// </summary>
/// <param name="strPath">path to have attachments to be saved to</param>
/// <returns>true if save successfully, false if failed</returns>
public bool SaveAttachments(string strPath)
{
if (Utility.IsNotNullText(strPath))
{
try
{
bool blnRet = true;
if (!strPath.EndsWith("\\"))
{
strPath += "\\";
}
for (int i = 0; i < this.Attachments.Count; i++)
{
Attachment att = GetAttachment(i);
blnRet = SaveAttachment(att, strPath + GetAttachmentFileName(att));
if (!blnRet)
break;
}
return blnRet;
}
catch (Exception e)
{
Utility.LogError(e.Message);
return false;
}
}
else
return false;
}
/// <summary>
/// save attachment to file
/// </summary>
/// <param name="attItem">Attachment</param>
/// <param name="strFileName">File to be saved to</param>
/// <returns>true if save successfully, false if failed</returns>
public bool SaveAttachment(Attachment attItem, string strFileName)
{
byte[] da;
try
{
// FileStream fs=File.Create(strFileName);
// byte[] da;
// if(attItem.ContentFileName.Length>0)
// {
// da=attItem.DecodedAttachment;
// }
// else
// {
// this.GetMessageBody(attItem.DecodeAttachmentAsText());
// da=Encoding.Default.GetBytes((string)this.MessageBody[this.MessageBody.Count-1]);
// }
// fs.Write(da,0,da.Length);
// fs.Close();
// return true;
if (attItem.InBytes)
{
da = attItem.RawBytes;
}
else if (attItem.ContentFileName.Length > 0)
{
da = attItem.DecodedAttachment;
}
else if (attItem.ContentType.ToLower() == "message/rfc822".ToLower())
{
da = Encoding.Default.GetBytes(attItem.RawAttachment);
}
else
{
this.GetMessageBody(attItem.DecodeAsText());
da = Encoding.Default.GetBytes((string) this.MessageBody[this.MessageBody.Count - 1]);
}
return Utility.SaveByteContentToFile(strFileName, da);
}
catch
{
/*Utility.LogError("SaveAttachment():"+e.Message);
return false;*/
da = Encoding.Default.GetBytes(attItem.RawAttachment);
return Utility.SaveByteContentToFile(strFileName, da);
}
}
/// <summary>
/// set attachments
/// </summary>
private void set_attachments()
{
int indexOf_attachmentstart = 0;
int indexOfAttachmentEnd = 0;
bool processed = false;
Attachment att = null;
SetAttachmentBoundry2(_rawMessageBody);
while (!processed)
{
if (Utility.IsNotNullText(_attachmentboundry))
{
indexOf_attachmentstart = _rawMessageBody.IndexOf(_attachmentboundry, indexOf_attachmentstart) + _attachmentboundry.Length;
if (_rawMessageBody == "" || indexOf_attachmentstart < 0) return;
indexOfAttachmentEnd = _rawMessageBody.IndexOf(_attachmentboundry, indexOf_attachmentstart + 1);
}
else
{
indexOfAttachmentEnd = -1;
}
//if(indexOfAttachmentEnd<0)return;
if (indexOfAttachmentEnd != -1)
{
}
else if (indexOfAttachmentEnd == -1 && !processed && _attachmentCount == 0)
{
processed = true;
indexOfAttachmentEnd = _rawMessageBody.Length;
}
else
return;
if (indexOf_attachmentstart == indexOfAttachmentEnd - 9)
{
indexOf_attachmentstart = 0;
processed = true;
}
string strLine = _rawMessageBody.Substring(indexOf_attachmentstart, (indexOfAttachmentEnd - indexOf_attachmentstart - 2));
bool isMSTNEF;
isMSTNEF = MIMETypes.IsMSTNEF(_contentType);
att = new Attachment(strLine.Trim(), _contentType, !isMSTNEF);
//ms-tnef format might contain multiple attachments
if (MIMETypes.IsMSTNEF(att.ContentType) && AutoDecodeMSTNEF && !isMSTNEF)
{
Utility.LogError("set_attachments():found ms-tnef file");
TNEFParser tnef = new TNEFParser();
TNEFAttachment tatt = new TNEFAttachment();
Attachment attNew = null;
tnef.Verbose = false;
tnef.BasePath = this.BasePath;
//tnef.LogFilePath=this.BasePath + "OpenPOP.TNEF.log";
if (tnef.OpenTNEFStream(att.DecodedAsBytes()))
{
if (tnef.Parse())
{
for (IDictionaryEnumerator i = tnef.Attachments().GetEnumerator(); i.MoveNext(); )
{
tatt = (TNEFAttachment) i.Value;
attNew = new Attachment(tatt.FileContent, tatt.FileLength, tatt.FileName, MIMETypes.GetMimeType(tatt.FileName));
_attachmentCount++;
_attachments.Add(attNew);
}
}
else
Utility.LogError("set_attachments():ms-tnef file parse failed");
}
else
Utility.LogError("set_attachments():ms-tnef file open failed");
}
else
{
_attachmentCount++;
_attachments.Add(att);
}
indexOf_attachmentstart++;
}
}
/// <summary>
/// Set alternative attachment boundry
/// </summary>
/// <param name="strBuffer">raw message</param>
private void SetAttachmentBoundry2(string strBuffer)
{
int indexOfAttachmentBoundry2Begin = 0;
int indexOfAttachmentBoundry2End = 0;
indexOfAttachmentBoundry2Begin = strBuffer.ToLower().IndexOf("Multipart/Alternative".ToLower());
if (indexOfAttachmentBoundry2Begin != -1)
{
/* indexOfAttachmentBoundry2Begin=strBuffer.IndexOf("boundary=\"");
indexOfAttachmentBoundry2End=strBuffer.IndexOf("\"",indexOfAttachmentBoundry2Begin+10);
if(indexOfAttachmentBoundry2Begin!=-1&&indexOfAttachmentBoundry2End!=-1)
_attachmentboundry2=strBuffer.Substring(indexOfAttachmentBoundry2Begin+10,indexOfAttachmentBoundry2End-indexOfAttachmentBoundry2Begin-10).Trim();
*/
indexOfAttachmentBoundry2Begin = strBuffer.IndexOf("boundary=");
if (indexOfAttachmentBoundry2Begin != -1)
{
int p = strBuffer.IndexOf("\r\n", indexOfAttachmentBoundry2Begin);
string s = strBuffer.Substring(indexOfAttachmentBoundry2Begin + 29, 4);
indexOfAttachmentBoundry2End = strBuffer.IndexOf("\r\n", indexOfAttachmentBoundry2Begin + 9);
if (indexOfAttachmentBoundry2End == -1)
indexOfAttachmentBoundry2End = strBuffer.Length;
_attachmentboundry2 = Utility.RemoveQuote(strBuffer.Substring(indexOfAttachmentBoundry2Begin + 9, indexOfAttachmentBoundry2End - indexOfAttachmentBoundry2Begin - 9));
}
}
else
{
_attachmentboundry2 = _attachmentboundry;
}
}
/// <summary>
/// Save message content to eml file
/// </summary>
/// <param name="strFile"></param>
/// <returns></returns>
public bool SaveToMIMEEmailFile(string strFile, bool blnReplaceExists)
{
return Utility.SavePlainTextToFile(strFile, _rawMessage, blnReplaceExists);
}
/// <summary>
/// parse multi-line header
/// </summary>
/// <param name="sbdBuilder">string builder to hold header content</param>
/// <param name="srdReader">string reader to get each line of the header</param>
/// <param name="strValue">first line content</param>
/// <param name="strLine">reference header line</param>
/// <param name="alCollection">collection to hold every content line</param>
private void ParseStreamLines(StringBuilder sbdBuilder
, StringReader srdReader
, string strValue
, ref string strLine
, ArrayList alCollection)
{
string strFormmated;
int intLines = 0;
alCollection.Add(strValue);
sbdBuilder.Append(strLine);
strLine = srdReader.ReadLine();
while (strLine.Trim() != "" && (strLine.StartsWith("\t") || strLine.StartsWith(" ")))
{
strFormmated = strLine.Substring(1);
alCollection.Add(Utility.DecodeLine(strFormmated));
sbdBuilder.Append(strLine);
strLine = srdReader.ReadLine();
intLines++;
}
if (strLine != "")
{
sbdBuilder.Append(strLine);
}
else if (intLines == 0)
{
strLine = srdReader.ReadLine();
sbdBuilder.Append(strLine);
}
ParseHeader(sbdBuilder, srdReader, ref strLine);
}
/// <summary>
/// parse multi-line header
/// </summary>
/// <param name="sbdBuilder">string builder to hold header content</param>
/// <param name="srdReader">string reader to get each line of the header</param>
/// <param name="strName">collection key</param>
/// <param name="strValue">first line content</param>
/// <param name="strLine">reference header line</param>
/// <param name="hstCollection">collecti