phuuuu hoch 2

This commit is contained in:
BlubbFish 2010-07-10 13:13:50 +00:00
parent 07fabd1413
commit 1bb953789a
111 changed files with 17673 additions and 90 deletions

View File

@ -0,0 +1,23 @@
namespace MailServer.IMAP
{
/// <summary>
/// IMAP flags store type.
/// </summary>
public enum IMAP_Flags_SetType
{
/// <summary>
/// Flags are added to existing ones.
/// </summary>
Add = 1,
/// <summary>
/// Flags are removed from existing ones.
/// </summary>
Remove = 3,
/// <summary>
/// Flags are replaced.
/// </summary>
Replace = 4,
}
}

View File

@ -0,0 +1,257 @@
using System;
using System.Collections.Generic;
namespace MailServer.IMAP
{
/// <summary>
/// IMAP sequence-set. RFC 3501.
/// <code>
/// Examples:
/// 2 -> seq-number (2)
/// 2:4 -> seq-range (from 2 - 4)
/// 2:* -> seq-range (from 2 to last)
/// 2,3,10:* -> sequence-set (seq-number,seq-number,seq-range)
/// (2,3, 10 - last)
///
/// NOTES:
/// *) comma separates sequence parts
/// *) * means maximum value.
/// </code>
/// </summary>
public class IMAP_SequenceSet
{
#region class Range
/// <summary>
/// Implements range.
/// </summary>
private class Range
{
private long m_Start = 0;
private long m_End = 0;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="value">Range value.</param>
public Range(long value)
{
m_Start = value;
m_End = value;
}
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="start">Range start.</param>
/// <param name="end">Range end.</param>
public Range(long start, long end)
{
m_Start = start;
m_End = end;
}
#region Properties Implementation
/// <summary>
/// Gets range start.
/// </summary>
public long Start
{
get { return m_Start; }
}
/// <summary>
/// Gets range end.
/// </summary>
public long End
{
get { return m_End; }
}
#endregion
}
#endregion
private List<Range> m_pSequenceParts = null;
private string m_SequenceString = "";
/// <summary>
/// Default constructor.
/// </summary>
public IMAP_SequenceSet()
{
m_pSequenceParts = new List<Range>();
}
#region method Parse
/// <summary>
/// Parses sequence-set from specified string. Throws exception if invalid sequnce-set value.
/// </summary>
/// <param name="sequenceSetString">Sequence-set string.</param>
public void Parse(string sequenceSetString)
{
Parse(sequenceSetString, long.MaxValue);
}
/// <summary>
/// Parses sequence-set from specified string. Throws exception if invalid sequnce-set value.
/// </summary>
/// <param name="sequenceSetString">Sequence-set string.</param>
/// <param name="seqMaxValue">Maximum value. This if for replacement of * value.</param>
public void Parse(string sequenceSetString, long seqMaxValue)
{
/* RFC 3501
seq-number = nz-number / "*"
; message sequence number (COPY, FETCH, STORE
; commands) or unique identifier (UID COPY,
; UID FETCH, UID STORE commands).
; * represents the largest number in use. In
; the case of message sequence numbers, it is
; the number of messages in a non-empty mailbox.
; In the case of unique identifiers, it is the
; unique identifier of the last message in the
; mailbox or, if the mailbox is empty, the
; mailbox's current UIDNEXT value.
; The server should respond with a tagged BAD
; response to a command that uses a message
; sequence number greater than the number of
; messages in the selected mailbox. This
; includes "*" if the selected mailbox is empty.
seq-range = seq-number ":" seq-number
; two seq-number values and all values between
; these two regardless of order.
; Example: 2:4 and 4:2 are equivalent and indicate
; values 2, 3, and 4.
; Example: a unique identifier sequence range of
; 3291:* includes the UID of the last message in
; the mailbox, even if that value is less than 3291.
sequence-set = (seq-number / seq-range) *("," sequence-set)
; set of seq-number values, regardless of order.
; Servers MAY coalesce overlaps and/or execute the
; sequence in any order.
; Example: a message sequence number set of
; 2,4:7,9,12:* for a mailbox with 15 messages is
; equivalent to 2,4,5,6,7,9,12,13,14,15
; Example: a message sequence number set of *:4,5:7
; for a mailbox with 10 messages is equivalent to
; 10,9,8,7,6,5,4,5,6,7 and MAY be reordered and
; overlap coalesced to be 4,5,6,7,8,9,10.
*/
//--- Validate sequence-set --------------------------------------------------------//
string[] sequenceSets = sequenceSetString.Trim().Split(',');
foreach (string sequenceSet in sequenceSets)
{
// seq-range
if (sequenceSet.IndexOf(":") > -1)
{
string[] rangeParts = sequenceSet.Split(':');
if (rangeParts.Length == 2)
{
long start = Parse_Seq_Number(rangeParts[0], seqMaxValue);
long end = Parse_Seq_Number(rangeParts[1], seqMaxValue);
if (start <= end)
{
m_pSequenceParts.Add(new Range(start, end));
}
else
{
m_pSequenceParts.Add(new Range(end, start));
}
}
else
{
throw new Exception("Invalid <seq-range> '" + sequenceSet + "' value !");
}
}
// seq-number
else
{
m_pSequenceParts.Add(new Range(Parse_Seq_Number(sequenceSet, seqMaxValue)));
}
}
//-----------------------------------------------------------------------------------//
m_SequenceString = sequenceSetString;
}
#endregion
#region method Contains
/// <summary>
/// Gets if sequence set contains specified number.
/// </summary>
/// <param name="seqNumber">Number to check.</param>
public bool Contains(long seqNumber)
{
foreach (Range range in m_pSequenceParts)
{
if (seqNumber >= range.Start && seqNumber <= range.End)
{
return true;
}
}
return false;
}
#endregion
#region method ToSequenceSetString
/// <summary>
/// Converts IMAP_SequenceSet to IMAP sequence-set string.
/// </summary>
/// <returns></returns>
public string ToSequenceSetString()
{
return m_SequenceString;
}
#endregion
#region method Parse_Seq_Number
/// <summary>
/// Parses seq-number from specified value. Throws exception if invalid seq-number value.
/// </summary>
/// <param name="seqNumberValue">Integer number or *.</param>
/// <param name="seqMaxValue">Maximum value. This if for replacement of * value.</param>
private long Parse_Seq_Number(string seqNumberValue, long seqMaxValue)
{
seqNumberValue = seqNumberValue.Trim();
// * max value
if (seqNumberValue == "*")
{
// Replace it with max value
return seqMaxValue;
}
// Number
else
{
try
{
return Convert.ToInt64(seqNumberValue);
}
catch
{
throw new Exception("Invalid <seq-number> '" + seqNumberValue + "' value !");
}
}
}
#endregion
}
}

View File

@ -2,6 +2,7 @@
using System.IO;
using System.Text;
using MailServer.IMAP.Server;
using MailServer.Misc;
namespace MailServer.IMAP
{

View File

@ -0,0 +1,66 @@
using System;
namespace MailServer.IMAP.Server
{
/// <summary>
/// Provides data for DeleteFolderACL event.
/// </summary>
public class IMAP_DELETEACL_eArgs
{
private IMAP_Session m_pSession = null;
private string m_pFolderName = "";
private string m_UserName = "";
private string m_ErrorText = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="session">Owner IMAP session.</param>
/// <param name="folderName">Folder name which ACL to delete.</param>
/// <param name="userName">User name which ACL to delete.</param>
public IMAP_DELETEACL_eArgs(IMAP_Session session, string folderName, string userName)
{
m_pSession = session;
m_pFolderName = folderName;
m_UserName = userName;
}
#region Properties Implementation
/// <summary>
/// Gets current IMAP session.
/// </summary>
public IMAP_Session Session
{
get { return m_pSession; }
}
/// <summary>
/// Gets folder name which ACL to delete.
/// </summary>
public string Folder
{
get { return m_pFolderName; }
}
/// <summary>
/// Gets user name which ACL to delete.
/// </summary>
public string UserName
{
get { return m_UserName; }
}
/// <summary>
/// Gets or sets error text returned to connected client.
/// </summary>
public string ErrorText
{
get { return m_ErrorText; }
set { m_ErrorText = value; }
}
#endregion
}
}

View File

@ -0,0 +1,48 @@
using System;
namespace MailServer.IMAP.Server
{
/// <summary>
/// IMAP folder.
/// </summary>
public class IMAP_Folder
{
private string m_Folder = "";
private bool m_Selectable = true;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="folder">Full path to folder, path separator = '/'. Eg. Inbox/myFolder .</param>
/// <param name="selectable">Gets or sets if folder is selectable(SELECT command can select this folder).</param>
public IMAP_Folder(string folder, bool selectable)
{
m_Folder = folder;
m_Selectable = selectable;
}
#region Properties Implementation
/// <summary>
/// Gets IMAP folder name. Eg. Inbox, Inbox/myFolder, ... .
/// </summary>
public string Folder
{
get { return m_Folder; }
}
/// <summary>
/// Gets or sets if folder is selectable (SELECT command can select this folder).
/// </summary>
public bool Selectable
{
get { return m_Selectable; }
set { m_Selectable = value; }
}
#endregion
}
}

View File

@ -0,0 +1,212 @@
using System;
using System.Collections;
using System.Text.RegularExpressions;
namespace MailServer.IMAP.Server
{
/// <summary>
/// IMAP folders collection.
/// </summary>
public class IMAP_Folders
{
private IMAP_Session m_pSession = null;
private ArrayList m_Mailboxes = null;
private string m_RefName = "";
private string m_Mailbox = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="session">Owner IMAP session.</param>
/// <param name="referenceName">Folder Path. Eg. Inbox\.</param>
/// <param name="folder">Folder name.</param>
public IMAP_Folders(IMAP_Session session, string referenceName, string folder)
{
m_pSession = session;
m_Mailboxes = new ArrayList();
m_RefName = referenceName;
m_Mailbox = folder.Replace("\\", "/");
}
#region method Add
/// <summary>
/// Adds folder to folders list.
/// </summary>
/// <param name="folder">Full path to folder, path separator = '/'. Eg. Inbox/myFolder .</param>
/// <param name="selectable">Gets or sets if folder is selectable(SELECT command can select this folder).</param>
public void Add(string folder, bool selectable)
{
folder = folder.Replace("\\", "/");
string folderPattern = m_RefName + m_Mailbox;
if (m_RefName != "" && !m_RefName.EndsWith("/") && !m_Mailbox.StartsWith("/"))
{
folderPattern = m_RefName + "/" + m_Mailbox;
}
if (FolderMatches(folderPattern, IMAP_Utils.Decode_IMAP_UTF7_String(folder)))
{
m_Mailboxes.Add(new IMAP_Folder(folder, selectable));
}
}
#endregion
// TODO: move to some global utility method
#region method AstericMatch
/// <summary>
/// Checks if specified text matches to specified asteric pattern.
/// </summary>
/// <param name="pattern">Asteric pattern. Foe example: *xxx,*xxx*,xx*aa*xx, ... .</param>
/// <param name="text">Text to match.</param>
/// <returns></returns>
public bool AstericMatch(string pattern, string text)
{
pattern = pattern.ToLower();
text = text.ToLower();
if (pattern == "")
{
pattern = "*";
}
while (pattern.Length > 0)
{
// *xxx[*xxx...]
if (pattern.StartsWith("*"))
{
// *xxx*xxx
if (pattern.IndexOf("*", 1) > -1)
{
string indexOfPart = pattern.Substring(1, pattern.IndexOf("*", 1) - 1);
if (text.IndexOf(indexOfPart) == -1)
{
return false;
}
text = text.Substring(text.IndexOf(indexOfPart) + indexOfPart.Length + 1);
pattern = pattern.Substring(pattern.IndexOf("*", 1) + 1);
}
// *xxx This is last pattern
else
{
return text.EndsWith(pattern.Substring(1));
}
}
// xxx*[xxx...]
else if (pattern.IndexOfAny(new char[] { '*' }) > -1)
{
string startPart = pattern.Substring(0, pattern.IndexOfAny(new char[] { '*' }));
// Text must startwith
if (!text.StartsWith(startPart))
{
return false;
}
text = text.Substring(text.IndexOf(startPart) + startPart.Length);
pattern = pattern.Substring(pattern.IndexOfAny(new char[] { '*' }));
}
// xxx
else
{
return text == pattern;
}
}
return true;
}
#endregion
#region method FolderMatches
/// <summary>
/// Gets if folder matches to specified folder pattern.
/// </summary>
/// <param name="folderPattern">Folder pattern. * and % between path separators have same meaning (asteric pattern).
/// If % is at the end, then matches only last folder child folders and not child folder child folders.</param>
/// <param name="folder">Folder name with full path.</param>
/// <returns></returns>
private bool FolderMatches(string folderPattern, string folder)
{
folderPattern = folderPattern.ToLower();
folder = folder.ToLower();
string[] folderParts = folder.Split('/');
string[] patternParts = folderPattern.Split('/');
// pattern is more nested than folder
if (folderParts.Length < patternParts.Length)
{
return false;
}
// This can happen only if * at end
else if (folderParts.Length > patternParts.Length && !folderPattern.EndsWith("*"))
{
return false;
}
else
{
// Loop patterns
for (int i = 0; i < patternParts.Length; i++)
{
string patternPart = patternParts[i].Replace("%", "*");
// This is asteric pattern
if (patternPart.IndexOf('*') > -1)
{
if (!AstericMatch(patternPart, folderParts[i]))
{
return false;
}
// else process next pattern
}
// No *, this must be exact match
else
{
if (folderParts[i] != patternPart)
{
return false;
}
}
}
}
return true;
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets current IMAP session.
/// </summary>
public IMAP_Session Session
{
get { return m_pSession; }
}
/// <summary>
/// Gest list of IMAP folders.
/// </summary>
public IMAP_Folder[] Folders
{
get
{
IMAP_Folder[] retVal = new IMAP_Folder[m_Mailboxes.Count];
m_Mailboxes.CopyTo(retVal);
return retVal;
}
}
#endregion
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.Collections;
namespace MailServer.IMAP.Server
{
/// <summary>
/// Provides data for GetFolderACL event.
/// </summary>
public class IMAP_GETACL_eArgs
{
private IMAP_Session m_pSession = null;
private string m_pFolderName = "";
private Hashtable m_ACLs = null;
private string m_ErrorText = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="session">Owner IMAP session.</param>
/// <param name="folderName">Folder name which ACL to get.</param>
public IMAP_GETACL_eArgs(IMAP_Session session, string folderName)
{
m_pSession = session;
m_pFolderName = folderName;
m_ACLs = new Hashtable();
}
#region Properties Implementation
/// <summary>
/// Gets current IMAP session.
/// </summary>
public IMAP_Session Session
{
get { return m_pSession; }
}
/// <summary>
/// Gets folder name which ACL to get.
/// </summary>
public string Folder
{
get { return m_pFolderName; }
}
/// <summary>
/// Gets ACL collection. Key = userName, Value = IMAP_ACL_Flags.
/// </summary>
public Hashtable ACL
{
get { return m_ACLs; }
}
/// <summary>
/// Gets or sets error text returned to connected client.
/// </summary>
public string ErrorText
{
get { return m_ErrorText; }
set { m_ErrorText = value; }
}
#endregion
}
}

View File

@ -0,0 +1,79 @@
using System;
namespace MailServer.IMAP.Server
{
/// <summary>
/// Provides data for GetUserACL event.
/// </summary>
public class IMAP_GetUserACL_eArgs
{
private IMAP_Session m_pSession = null;
private string m_pFolderName = "";
private string m_UserName = "";
private IMAP_ACL_Flags m_ACL_Flags = 0;
private string m_ErrorText = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="session">Owner IMAP session.</param>
/// <param name="folderName">Folder name which ACL to get.</param>
/// <param name="userName">User name which ACL to get.</param>
public IMAP_GetUserACL_eArgs(IMAP_Session session, string folderName, string userName)
{
m_pSession = session;
m_pFolderName = folderName;
m_UserName = userName;
}
#region Properties Implementation
/// <summary>
/// Gets current IMAP session.
/// </summary>
public IMAP_Session Session
{
get { return m_pSession; }
}
/// <summary>
/// Gets folder name which ACL to get.
/// </summary>
public string Folder
{
get { return m_pFolderName; }
}
/// <summary>
/// Gets user name which ACL to get.
/// </summary>
public string UserName
{
get { return m_UserName; }
}
/// <summary>
/// Gets or sets user permissions(ACL) for specified folder.
/// </summary>
public IMAP_ACL_Flags ACL
{
get { return m_ACL_Flags; }
set { m_ACL_Flags = value; }
}
/// <summary>
/// Gets or sets error text returned to connected client.
/// </summary>
public string ErrorText
{
get { return m_ErrorText; }
set { m_ErrorText = value; }
}
#endregion
}
}

View File

@ -0,0 +1,88 @@
using System;
namespace MailServer.IMAP.Server
{
/// <summary>
/// Provides data for SetFolderACL event.
/// </summary>
public class IMAP_SETACL_eArgs
{
private IMAP_Session m_pSession = null;
private string m_pFolderName = "";
private string m_UserName = "";
private IMAP_Flags_SetType m_FlagsSetType = IMAP_Flags_SetType.Replace;
private IMAP_ACL_Flags m_ACL_Flags = IMAP_ACL_Flags.None;
private string m_ErrorText = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="session">Owner IMAP session.</param>
/// <param name="folderName">Folder name which ACL to set.</param>
/// <param name="userName">User name which ACL to set.</param>
/// <param name="flagsSetType">Specifies how flags must be stored.</param>
/// <param name="aclFlags">Flags which to store.</param>
public IMAP_SETACL_eArgs(IMAP_Session session, string folderName, string userName, IMAP_Flags_SetType flagsSetType, IMAP_ACL_Flags aclFlags)
{
m_pSession = session;
m_pFolderName = folderName;
m_UserName = userName;
m_FlagsSetType = flagsSetType;
m_ACL_Flags = aclFlags;
}
#region Properties Implementation
/// <summary>
/// Gets current IMAP session.
/// </summary>
public IMAP_Session Session
{
get { return m_pSession; }
}
/// <summary>
/// Gets folder name which ACL to set.
/// </summary>
public string Folder
{
get { return m_pFolderName; }
}
/// <summary>
/// Gets user name which ACL to set.
/// </summary>
public string UserName
{
get { return m_UserName; }
}
/// <summary>
/// Gets how ACL flags must be stored.
/// </summary>
public IMAP_Flags_SetType FlagsSetType
{
get { return m_FlagsSetType; }
}
/// <summary>
/// Gets ACL flags. NOTE: See this.FlagsSetType how to store flags.
/// </summary>
public IMAP_ACL_Flags ACL
{
get { return m_ACL_Flags; }
}
/// <summary>
/// Gets or sets error text returned to connected client.
/// </summary>
public string ErrorText
{
get { return m_ErrorText; }
set { m_ErrorText = value; }
}
#endregion
}
}

View File

@ -5,7 +5,7 @@ using System.Text;
namespace MailServer.IMAP.Server
{
public partial class Server
public partial class IMAP_Server
{
/// <summary>
/// Raises event ValidateIP event.

View File

@ -5,7 +5,7 @@ using System.Text;
namespace MailServer.IMAP.Server
{
public partial class Server
public partial class IMAP_Server
{
/// <summary>
/// Occurs when new computer connected to IMAP server.

View File

@ -5,7 +5,7 @@ using System.Text;
namespace MailServer.IMAP.Server
{
public partial class Server
public partial class IMAP_Server
{
/// <summary>
/// Gets or sets server supported authentication types.

View File

@ -5,7 +5,7 @@ using System.Text;
namespace MailServer.IMAP.Server
{
public partial class Server
public partial class IMAP_Server
{
/// <summary>
/// Initialize and start new session here. Session isn't added to session list automatically,

View File

@ -7,7 +7,7 @@ namespace MailServer.IMAP.Server
/// <summary>
/// IMAP server componet.
/// </summary>
public partial class Server : SocketServer
public partial class IMAP_Server : SocketServer
{
private int m_MaxConnectionsPerIP = 0;
private SaslAuthTypes m_SupportedAuth = SaslAuthTypes.All;
@ -16,7 +16,7 @@ namespace MailServer.IMAP.Server
/// <summary>
/// Defalut constructor.
/// </summary>
public Server() : base()
public IMAP_Server() : base()
{
this.BindInfo = new IPBindInfo[]{new IPBindInfo("",IPAddress.Any,143,SslMode.None,null)};
}

View File

@ -1,8 +1,15 @@
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections;
using System.Text;
using MailServer.Misc.SocketServer;
using MailServer.Misc;
using System.Text;
using System.IO;
using MailServer.Misc.Auth;
namespace MailServer.IMAP.Server
{
@ -167,7 +174,7 @@ namespace MailServer.IMAP.Server
#endregion
private bool m_Disposed = false;
private Server m_pServer = null;
private IMAP_Server m_pServer = null;
private string m_SelectedMailbox = "";
private IMAP_SelectedFolder m_pSelectedFolder = null;
private int m_BadCmdCount = 0;
@ -180,7 +187,7 @@ namespace MailServer.IMAP.Server
/// <param name="socket">Server connected socket.</param>
/// <param name="bindInfo">BindInfo what accepted socket.</param>
/// <param name="server">Reference to server.</param>
internal IMAP_Session(string sessionID, SocketEx socket, IPBindInfo bindInfo, Server server)
internal IMAP_Session(string sessionID, SocketEx socket, IPBindInfo bindInfo, IMAP_Server server)
: base(sessionID, socket, bindInfo, server)
{
m_pServer = server;
@ -2967,7 +2974,7 @@ namespace MailServer.IMAP.Server
SearchGroup searchCriteria = new SearchGroup();
try
{
searchCriteria.Parse(new StringReader(argsText));
searchCriteria.Parse(new Misc.StringReader(argsText));
}
catch (Exception x)
{

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MailServer.IMAP.Server
{
/// <summary>
/// Provides data to event GetMessagesInfo.
/// </summary>
public class IMAP_eArgs_GetMessagesInfo
{
private IMAP_Session m_pSession = null;
private IMAP_SelectedFolder m_pFolderInfo = null;
private string m_ErrorText = null;
/// <summary>
/// Default constructor.
/// </summary>
public IMAP_eArgs_GetMessagesInfo(IMAP_Session session, IMAP_SelectedFolder folder)
{
m_pSession = session;
m_pFolderInfo = folder;
}
#region Properties Implementation
/// <summary>
/// Gets current IMAP session.
/// </summary>
public IMAP_Session Session
{
get { return m_pSession; }
}
/// <summary>
/// Gets folder info.
/// </summary>
public IMAP_SelectedFolder FolderInfo
{
get { return m_pFolderInfo; }
}
/// <summary>
/// Gets or sets custom error text, which is returned to client. Null value means no error.
/// </summary>
public string ErrorText
{
get { return m_ErrorText; }
set { m_ErrorText = value; }
}
#endregion
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace MailServer.IMAP.Server
{
/// <summary>
/// Provides data for GetUserQuota event.
/// </summary>
public class IMAP_eArgs_GetQuota
{
private IMAP_Session m_pSession = null;
private long m_MaxMailboxSize = 0;
private long m_MailboxSize = 0;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="session">Owner IMAP session.</param>
public IMAP_eArgs_GetQuota(IMAP_Session session)
{
m_pSession = session;
}
#region Properties Implementation
/// <summary>
/// Gets current IMAP session.
/// </summary>
public IMAP_Session Session
{
get { return m_pSession; }
}
/// <summary>
/// Gets user name.
/// </summary>
public string UserName
{
get { return m_pSession.UserName; }
}
/// <summary>
/// Gets or sets maximum mailbox size.
/// </summary>
public long MaxMailboxSize
{
get { return m_MaxMailboxSize; }
set { m_MaxMailboxSize = value; }
}
/// <summary>
/// Gets or sets current mailbox size.
/// </summary>
public long MailboxSize
{
get { return m_MailboxSize; }
set { m_MailboxSize = value; }
}
#endregion
}
}

View File

@ -0,0 +1,255 @@
using System;
using System.Collections;
using MailServer.Misc;
using MailServer.Misc.Mail;
namespace MailServer.IMAP.Server
{
/// <summary>
/// IMAP search command grouped(parenthesized) search-key collection.
/// </summary>
internal class SearchGroup
{
private ArrayList m_pSearchKeys = null;
/// <summary>
/// Default constructor.
/// </summary>
public SearchGroup()
{
m_pSearchKeys = new ArrayList();
}
#region method Parse
/// <summary>
/// Parses search key from current position.
/// </summary>
/// <param name="reader"></param>
public void Parse(StringReader reader)
{
//Remove spaces from string start
reader.ReadToFirstChar();
if (reader.StartsWith("("))
{
reader = new StringReader(reader.ReadParenthesized().Trim());
}
//--- Start parsing search keys --------------//
while (reader.Available > 0)
{
object searchKey = ParseSearchKey(reader);
if (searchKey != null)
{
m_pSearchKeys.Add(searchKey);
}
}
//--------------------------------------------//
}
#endregion
#region method IsHeaderNeeded
/// <summary>
/// Gets if message Header is needed for matching.
/// </summary>
/// <returns></returns>
public bool IsHeaderNeeded()
{
foreach (object searchKey in m_pSearchKeys)
{
if (SearchGroup.IsHeaderNeededForKey(searchKey))
{
return true;
}
}
return false;
}
#endregion
#region method IsBodyTextNeeded
/// <summary>
/// Gets if message body text is needed for matching.
/// </summary>
/// <returns></returns>
public bool IsBodyTextNeeded()
{
foreach (object searchKey in m_pSearchKeys)
{
if (SearchGroup.IsBodyTextNeededForKey(searchKey))
{
return true;
}
}
return false;
}
#endregion
#region static method ParseSearchKey
/// <summary>
/// Parses SearchGroup or SearchItem from reader. If reader starts with (, then parses searchGroup, otherwise SearchItem.
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
internal static object ParseSearchKey(StringReader reader)
{
//Remove spaces from string start
reader.ReadToFirstChar();
// SearchGroup
if (reader.StartsWith("("))
{
SearchGroup searchGroup = new SearchGroup();
searchGroup.Parse(reader);
return searchGroup;
}
// SearchItem
else
{
return SearchKey.Parse(reader);
}
}
#endregion
#region static method Match_Key_Value
/// <summary>
/// Gets if specified message matches to specified search key.
/// </summary>
/// <param name="searchKey">SearchKey or SearchGroup.</param>
/// <param name="no">IMAP message sequence number.</param>
/// <param name="uid">IMAP message UID.</param>
/// <param name="size">IMAP message size in bytes.</param>
/// <param name="internalDate">IMAP message INTERNALDATE (dateTime when server stored message).</param>
/// <param name="flags">IMAP message flags.</param>
/// <param name="message">Mime message main header only.</param>
/// <param name="bodyText">Message body text.</param>
/// <returns></returns>
internal static bool Match_Key_Value(object searchKey, long no, long uid, long size, DateTime internalDate, IMAP_MessageFlags flags, Mail_Message message, string bodyText)
{
if (searchKey.GetType() == typeof(SearchKey))
{
if (!((SearchKey)searchKey).Match(no, uid, size, internalDate, flags, message, bodyText))
{
return false;
}
}
else if (searchKey.GetType() == typeof(SearchGroup))
{
if (!((SearchGroup)searchKey).Match(no, uid, size, internalDate, flags, message, bodyText))
{
return false;
}
}
return true;
}
#endregion
#region static method IsHeaderNeededForKey
/// <summary>
/// Gets if message header is needed for matching.
/// </summary>
/// <param name="searchKey"></param>
/// <returns></returns>
internal static bool IsHeaderNeededForKey(object searchKey)
{
if (searchKey.GetType() == typeof(SearchKey))
{
if (((SearchKey)searchKey).IsHeaderNeeded())
{
return true;
}
}
else if (searchKey.GetType() == typeof(SearchGroup))
{
if (((SearchGroup)searchKey).IsHeaderNeeded())
{
return true;
}
}
return false;
}
#endregion
#region static method IsBodyTextNeededForKey
/// <summary>
/// Gets if message body text is needed for matching.
/// </summary>
/// <param name="searchKey"></param>
/// <returns></returns>
internal static bool IsBodyTextNeededForKey(object searchKey)
{
if (searchKey.GetType() == typeof(SearchKey))
{
if (((SearchKey)searchKey).IsBodyTextNeeded())
{
return true;
}
}
else if (searchKey.GetType() == typeof(SearchGroup))
{
if (((SearchGroup)searchKey).IsBodyTextNeeded())
{
return true;
}
}
return false;
}
#endregion
#region method Match
/// <summary>
/// Gets if specified message matches with this class search-key.
/// </summary>
/// <param name="no">IMAP message sequence number.</param>
/// <param name="uid">IMAP message UID.</param>
/// <param name="size">IMAP message size in bytes.</param>
/// <param name="internalDate">IMAP message INTERNALDATE (dateTime when server stored message).</param>
/// <param name="flags">IMAP message flags.</param>
/// <param name="message">Mime message main header only.</param>
/// <param name="bodyText">Message body text.</param>
/// <returns></returns>
public bool Match(long no, long uid, long size, DateTime internalDate, IMAP_MessageFlags flags, Mail_Message message, string bodyText)
{
// We must match all keys, if one fails, no need to check others
foreach (object searckKey in m_pSearchKeys)
{
if (!Match_Key_Value(searckKey, no, uid, size, internalDate, flags, message, bodyText))
{
return false;
}
}
return true;
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,55 @@
using System;
namespace MailServer.IMAP.Server
{
/// <summary>
/// Summary description for SharedRootFolders_EventArgs.
/// </summary>
public class SharedRootFolders_EventArgs
{
private IMAP_Session m_pSession = null;
private string[] m_SharedRootFolders = null;
private string[] m_PublicRootFolders = null;
/// <summary>
/// Default constructor.
/// </summary>
public SharedRootFolders_EventArgs(IMAP_Session session)
{
m_pSession = session;
}
#region Properties Implementation
/// <summary>
/// Gets reference to smtp session.
/// </summary>
public IMAP_Session Session
{
get { return m_pSession; }
}
/// <summary>
/// Gets or sets users shared root folders. Ususaly there is only one root folder 'Shared Folders'.
/// </summary>
public string[] SharedRootFolders
{
get { return m_SharedRootFolders; }
set { m_SharedRootFolders = value; }
}
/// <summary>
/// Gets or sets public root folders. Ususaly there is only one root folder 'Public Folders'.
/// </summary>
public string[] PublicRootFolders
{
get { return m_PublicRootFolders; }
set { m_PublicRootFolders = value; }
}
#endregion
}
}

View File

@ -45,38 +45,123 @@
</ItemGroup>
<ItemGroup>
<Compile Include="IMAP\IMAP_ACL_Flags.cs" />
<Compile Include="IMAP\IMAP_Flags_SetType.cs" />
<Compile Include="IMAP\IMAP_SequenceSet.cs" />
<Compile Include="IMAP\IMAP_Utils.cs" />
<Compile Include="IMAP\Server\AuthUser_EventArgs\AuthUser_EventArgs.cs" />
<Compile Include="IMAP\Server\IMAP_DELETEACL_eArgs\IMAP_DELETEACL_eArgs.cs" />
<Compile Include="IMAP\Server\IMAP_eArgs_GetMessagesInfo\IMAP_eArgs_GetMessagesInfo.cs" />
<Compile Include="IMAP\Server\IMAP_eArgs_GetQuota\IMAP_eArgs_GetQuota.cs" />
<Compile Include="IMAP\Server\IMAP_Folders\IMAP_Folders.cs" />
<Compile Include="IMAP\Server\IMAP_Folder\IMAP_Folder.cs" />
<Compile Include="IMAP\Server\IMAP_GETACL_eArgs\IMAP_GETACL_eArgs.cs" />
<Compile Include="IMAP\Server\IMAP_GetUserACL_eArgs\IMAP_GetUserACL_eArgs.cs" />
<Compile Include="IMAP\Server\IMAP_MessageCollection\IMAP_MessageCollection.cs" />
<Compile Include="IMAP\Server\IMAP_MessageFlags.cs" />
<Compile Include="IMAP\Server\IMAP_Message\IMAP_Message.cs" />
<Compile Include="IMAP\Server\IMAP_SelectedFolder\IMAP_SelectedFolder.cs" />
<Compile Include="IMAP\Server\IMAP_Session\IMAP_Session.cs" />
<Compile Include="IMAP\Server\Server\Server.cs">
<Compile Include="IMAP\Server\IMAP_Server\IMAP_Server.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="IMAP\Server\Server\Server.Delegates.cs" />
<Compile Include="IMAP\Server\Server\Server.Events.cs">
<SubType>Code</SubType>
<Compile Include="IMAP\Server\IMAP_Server\IMAP_Server.Delegates.cs" />
<Compile Include="IMAP\Server\IMAP_Server\IMAP_Server.Events.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="IMAP\Server\Server\Server.EventsDeclarations.cs">
<SubType>Code</SubType>
<Compile Include="IMAP\Server\IMAP_Server\IMAP_Server.EventsDeclarations.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="IMAP\Server\Server\Server.Properties.cs">
<SubType>Code</SubType>
<Compile Include="IMAP\Server\IMAP_Server\IMAP_Server.Properties.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="IMAP\Server\Server\Server.Session.cs">
<SubType>Code</SubType>
<Compile Include="IMAP\Server\IMAP_Server\IMAP_Server.Session.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Misc\Base64\Base64.cs" />
<Compile Include="IMAP\Server\IMAP_SETACL_eArgs\IMAP_SETACL_eArgs.cs" />
<Compile Include="IMAP\Server\SearchGroup\SearchGroup.cs" />
<Compile Include="IMAP\Server\SearchKey\SearchKey.cs" />
<Compile Include="IMAP\Server\SharedRootFolders_EventArgs\SharedRootFolders_EventArgs.cs" />
<Compile Include="Misc\AsyncOP.cs" />
<Compile Include="Misc\Auth\AuthHelper\AuthHelper.cs" />
<Compile Include="Misc\EventArgs.cs" />
<Compile Include="Misc\IO\Base64Stream\Base64Stream.cs" />
<Compile Include="Misc\IO\Base64\Base64.cs" />
<Compile Include="Misc\BindInfoProtocol.cs" />
<Compile Include="Misc\CircleCollection\CircleCollection.cs" />
<Compile Include="Misc\commonDelegate.cs" />
<Compile Include="Misc\Core\Core.cs" />
<Compile Include="Misc\DNS\DnsCache\DnsCache.cs" />
<Compile Include="Misc\DNS\DnsServerResponse\DnsServerResponse.cs" />
<Compile Include="Misc\DNS\DNS_ClientException\DNS_ClientException.cs" />
<Compile Include="Misc\DNS\Dns_Client\Dns_Client.cs" />
<Compile Include="Misc\DNS\DNS_rr_AAAA\DNS_rr_AAAA.cs" />
<Compile Include="Misc\DNS\DNS_rr_A\DNS_rr_A.cs" />
<Compile Include="Misc\DNS\DNS_rr_base\DNS_rr_base.cs" />
<Compile Include="Misc\DNS\DNS_rr_CNAME\DNS_rr_CNAME.cs" />
<Compile Include="Misc\DNS\DNS_rr_HINFO\DNS_rr_HINFO.cs" />
<Compile Include="Misc\DNS\DNS_rr_MX\DNS_rr_MX.cs" />
<Compile Include="Misc\DNS\DNS_rr_NAPTR\DNS_rr_NAPTR.cs" />
<Compile Include="Misc\DNS\DNS_rr_NS\DNS_rr_NS.cs" />
<Compile Include="Misc\DNS\DNS_rr_PTR\DNS_rr_PTR.cs" />
<Compile Include="Misc\DNS\DNS_rr_SOA\DNS_rr_SOA.cs" />
<Compile Include="Misc\DNS\DNS_rr_SRV\DNS_rr_SRV.cs" />
<Compile Include="Misc\DNS\DNS_rr_TXT\DNS_rr_TXT.cs" />
<Compile Include="Misc\DNS\OPCODE.cs" />
<Compile Include="Misc\DNS\QTYPE.cs" />
<Compile Include="Misc\DNS\RCODE.cs" />
<Compile Include="Misc\Error_EventArgs\Error_EventArgs.cs" />
<Compile Include="Misc\IO\DataSizeExceededException.cs" />
<Compile Include="Misc\IO\IncompleteDataException.cs" />
<Compile Include="Misc\IO\LineSizeExceededException.cs" />
<Compile Include="Misc\IO\OuotedPrintableStream\QuotedPrintableStream.cs" />
<Compile Include="Misc\IO\ReadWriteControlledStream.cs" />
<Compile Include="Misc\IO\SizeExceededAction.cs" />
<Compile Include="Misc\IO\SmartStream\SmartStream.cs" />
<Compile Include="Misc\IPBindInfo\IPBindInfo.cs" />
<Compile Include="Misc\Mail\Mail_Message\Mail_Message.cs" />
<Compile Include="Misc\MIME\MIME_b.cs" />
<Compile Include="Misc\MIME\MIME_b_Application.cs" />
<Compile Include="Misc\MIME\MIME_b_Audio.cs" />
<Compile Include="Misc\MIME\MIME_b_Image.cs" />
<Compile Include="Misc\MIME\MIME_b_Message.cs" />
<Compile Include="Misc\MIME\MIME_b_MessageDeliveryStatus.cs" />
<Compile Include="Misc\MIME\MIME_b_MessageRfc822.cs" />
<Compile Include="Misc\MIME\MIME_b_Multipart.cs" />
<Compile Include="Misc\MIME\MIME_b_MultipartAlternative.cs" />
<Compile Include="Misc\MIME\MIME_b_MultipartDigest.cs" />
<Compile Include="Misc\MIME\MIME_b_MultipartEncrypted.cs" />
<Compile Include="Misc\MIME\MIME_b_MultipartFormData.cs" />
<Compile Include="Misc\MIME\MIME_b_MultipartMixed.cs" />
<Compile Include="Misc\MIME\MIME_b_MultipartParallel.cs" />
<Compile Include="Misc\MIME\MIME_b_MultipartRelated.cs" />
<Compile Include="Misc\MIME\MIME_b_MultipartReport.cs" />
<Compile Include="Misc\MIME\MIME_b_MultipartSigned.cs" />
<Compile Include="Misc\MIME\MIME_b_Provider.cs" />
<Compile Include="Misc\MIME\MIME_b_SinglepartBase.cs" />
<Compile Include="Misc\MIME\MIME_b_Text.cs" />
<Compile Include="Misc\MIME\MIME_b_Unknown.cs" />
<Compile Include="Misc\MIME\MIME_b_Video.cs" />
<Compile Include="Misc\MIME\MIME_DispositionTypes .cs" />
<Compile Include="Misc\MIME\MIME_EncodedWordEncoding.cs" />
<Compile Include="Misc\MIME\MIME_Encoding_EncodedWord.cs" />
<Compile Include="Misc\MIME\MIME_Entity.cs" />
<Compile Include="Misc\MIME\MIME_EntityCollection.cs" />
<Compile Include="Misc\MIME\MIME_h.cs" />
<Compile Include="Misc\MIME\MIME_h_Collection.cs" />
<Compile Include="Misc\MIME\MIME_h_ContentDisposition.cs" />
<Compile Include="Misc\MIME\MIME_h_ContentType.cs" />
<Compile Include="Misc\MIME\MIME_h_Parameter.cs" />
<Compile Include="Misc\MIME\MIME_h_ParameterCollection.cs" />
<Compile Include="Misc\MIME\MIME_h_Provider.cs" />
<Compile Include="Misc\MIME\MIME_h_Unparsed.cs" />
<Compile Include="Misc\MIME\MIME_h_Unstructured.cs" />
<Compile Include="Misc\MIME\MIME_MediaTypes.cs" />
<Compile Include="Misc\MIME\MIME_Message.cs" />
<Compile Include="Misc\MIME\MIME_Reader.cs" />
<Compile Include="Misc\MIME\MIME_TransferEncodings.cs" />
<Compile Include="Misc\MIME\MIME_Utils.cs" />
<Compile Include="Misc\Net_Utils\Net_Utils.cs" />
<Compile Include="Misc\ParseException.cs" />
<Compile Include="Misc\SaslAuthTypes.cs" />
<Compile Include="Misc\SocketServer\Log_EventArgs\Log_EventArgs.cs" />
<Compile Include="Misc\SocketServer\ReadExecption\ReadExecption.cs" />
@ -117,6 +202,9 @@
<SubType>Component</SubType>
</Compile>
<Compile Include="Misc\SSLMode.cs" />
<Compile Include="Misc\StreamLineReader\StreamLineReader.cs" />
<Compile Include="Misc\StringReader\StringReader.cs" />
<Compile Include="Misc\TextUtils\TextUtils.cs" />
<Compile Include="Misc\TimerEx\TimerEx.cs">
<SubType>Component</SubType>
</Compile>

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MailServer.Misc
{
/// <summary>
/// This is base class for asynchronous operation.
/// </summary>
public abstract class AsyncOP
{
/// <summary>
/// Default constructor.
/// </summary>
public AsyncOP()
{
}
#region Properties implementation
/// <summary>
/// Gets if this object is disposed.
/// </summary>
public abstract bool IsDisposed
{
get;
}
/// <summary>
/// Gets if asynchronous operation has completed.
/// </summary>
public abstract bool IsCompleted
{
get;
}
/// <summary>
/// Gets if operation completed synchronously.
/// </summary>
public abstract bool IsCompletedSynchronously
{
get;
}
#endregion
}
}

View File

@ -0,0 +1,213 @@
using System;
using System.Text;
using System.Security.Cryptography;
namespace MailServer.Misc.Auth
{
/// <summary>
/// Provides helper methods for authentications(APOP,CRAM-MD5,DIGEST-MD5).
/// </summary>
//[Obsolete()]
public class AuthHelper
{
#region method Apop
/// <summary>
/// Calculates APOP authentication compare value.
/// </summary>
/// <param name="password">Password.</param>
/// <param name="passwordTag">Password tag.</param>
/// <returns>Returns value what must be used for comparing passwords.</returns>
public static string Apop(string password, string passwordTag)
{
/* RFC 1939 7. APOP
*
* value = Hex(Md5(passwordTag + password))
*/
return Hex(Md5(passwordTag + password));
}
#endregion
#region method Cram_Md5
/// <summary>
/// Calculates CRAM-MD5 authentication compare value.
/// </summary>
/// <param name="password">Password.</param>
/// <param name="hashKey">Hash calculation key</param>
/// <returns>Returns value what must be used for comparing passwords.</returns>
public static string Cram_Md5(string password, string hashKey)
{
/* RFC 2195 AUTH CRAM-MD5
*
* value = Hex(HmacMd5(hashKey,password))
*/
return Hex(HmacMd5(hashKey, password));
}
#endregion
#region method Digest_Md5
/// <summary>
/// Calculates DIGEST-MD5 authentication compare value.
/// </summary>
/// <param name="client_server">Specifies if client or server value calculated.
/// Client and server has diffrent calculation method.</param>
/// <param name="realm">Use domain or machine name for this.</param>
/// <param name="userName">User name.</param>
/// <param name="password">Password.</param>
/// <param name="nonce">Server password tag.</param>
/// <param name="cnonce">Client password tag.</param>
/// <param name="digest_uri"></param>
/// <returns>Returns value what must be used for comparing passwords.</returns>
public static string Digest_Md5(bool client_server, string realm, string userName, string password, string nonce, string cnonce, string digest_uri)
{
/* RFC 2831 AUTH DIGEST-MD5
*
* qop = "auth"; // We support auth only auth-int and auth-conf isn't supported
* nc = "00000001"
*
* A1 = Md5(userName + ":" + realm + ":" + passw) + ":" + nonce + ":" + cnonce
* A2(client response) = "AUTHENTICATE:" + digest_uri
* A2(server response) = ":" + digest_uri
*
* resp-value = Hex(Md5(Hex(Md5(a1)) + ":" + (nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + Hex(Md5(a2)))));
*/
// string realm = "elwood.innosoft.com";
// string userName = "chris";
// string passw = "secret";
// string nonce = "OA6MG9tEQGm2hh";
// string cnonce = "OA6MHXh6VqTrRk";
// string digest_uri = "imap/elwood.innosoft.com";
string qop = "auth";
string nc = "00000001";
//****
string a1 = Md5(userName + ":" + realm + ":" + password) + ":" + nonce + ":" + cnonce;
string a2 = "";
if (client_server)
{
a2 = "AUTHENTICATE:" + digest_uri;
}
else
{
a2 = ":" + digest_uri;
}
return Hex(Md5(Hex(Md5(a1)) + ":" + (nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + Hex(Md5(a2)))));
}
#endregion
#region method Create_Digest_Md5_ServerResponse
/// <summary>
/// Creates AUTH Digest-md5 server response what server must send to client.
/// </summary>
/// <param name="realm">Use domain or machine name for this.</param>
/// <param name="nonce">Server password tag. Random hex string is suggested.</param>
/// <returns></returns>
public static string Create_Digest_Md5_ServerResponse(string realm, string nonce)
{
return "realm=\"" + realm + "\",nonce=\"" + nonce + "\",qop=\"auth\",algorithm=md5-sess";
}
#endregion
#region method GenerateNonce
/// <summary>
/// Generates random nonce value.
/// </summary>
/// <returns></returns>
public static string GenerateNonce()
{
return Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16);
}
#endregion
#region method HmacMd5
/// <summary>
/// Calculates keyed md5 hash from specifieed text and with specified hash key.
/// </summary>
/// <param name="hashKey"></param>
/// <param name="text"></param>
/// <returns></returns>
public static string HmacMd5(string hashKey, string text)
{
HMACMD5 kMd5 = new HMACMD5(Encoding.Default.GetBytes(text));
return Encoding.Default.GetString(kMd5.ComputeHash(Encoding.ASCII.GetBytes(hashKey)));
}
#endregion
#region method Md5
/// <summary>
/// Calculates md5 hash from specified string.
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Md5(string text)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] hash = md5.ComputeHash(Encoding.Default.GetBytes(text));
return System.Text.Encoding.Default.GetString(hash);
}
#endregion
#region method Hex
/// <summary>
/// Converts specified string to hexa string.
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Hex(string text)
{
return BitConverter.ToString(Encoding.Default.GetBytes(text)).ToLower().Replace("-", "");
}
#endregion
#region method Base64en
/// <summary>
/// Encodes specified string to base64 string.
/// </summary>
/// <param name="text">Text to encode.</param>
/// <returns>Returns encoded string.</returns>
public static string Base64en(string text)
{
return Convert.ToBase64String(Encoding.Default.GetBytes(text));
}
#endregion
#region method Base64de
/// <summary>
/// Decodes specified base64 string.
/// </summary>
/// <param name="text">Base64 string to decode.</param>
/// <returns>Returns decoded string.</returns>
public static string Base64de(string text)
{
return Encoding.Default.GetString(Convert.FromBase64String(text));
}
#endregion
}
}

1440
MailServer/Misc/Core/Core.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace MailServer.Misc.DNS
{
/// <summary>
/// DNS client exception.
/// </summary>
public class DNS_ClientException : Exception
{
private RCODE m_RCode;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="rcode">DNS server returned error code.</param>
public DNS_ClientException(RCODE rcode) : base("Dns error: " + rcode + ".")
{
m_RCode = rcode;
}
#region Properties implementation
/// <summary>
/// Gets DNS server returned error code.
/// </summary>
public RCODE ErrorCode
{
get{ return m_RCode; }
}
#endregion
}
}

View File

@ -0,0 +1,62 @@
using System;
using System.Net;
namespace MailServer.Misc.DNS
{
/// <summary>
/// A record class.
/// </summary>
[Serializable]
public class DNS_rr_A : DNS_rr_base
{
private IPAddress m_IP = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="ip">IP address.</param>
/// <param name="ttl">TTL value.</param>
public DNS_rr_A(IPAddress ip, int ttl)
: base(QTYPE.A, ttl)
{
m_IP = ip;
}
#region static method Parse
/// <summary>
/// Parses resource record from reply data.
/// </summary>
/// <param name="reply">DNS server reply data.</param>
/// <param name="offset">Current offset in reply data.</param>
/// <param name="rdLength">Resource record data length.</param>
/// <param name="ttl">Time to live in seconds.</param>
public static DNS_rr_A Parse(byte[] reply, ref int offset, int rdLength, int ttl)
{
// IPv4 = byte byte byte byte
byte[] ip = new byte[rdLength];
Array.Copy(reply, offset, ip, 0, rdLength);
offset += rdLength;
return new DNS_rr_A(new IPAddress(ip), ttl);
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets host IP address.
/// </summary>
public IPAddress IP
{
get { return m_IP; }
}
#endregion
}
}

View File

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
namespace MailServer.Misc.DNS
{
/// <summary>
/// DNS AAAA resource record.
/// </summary>
public class DNS_rr_AAAA : DNS_rr_base
{
private IPAddress m_IP = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="ip">IP address.</param>
/// <param name="ttl">Time to live in seconds.</param>
public DNS_rr_AAAA(IPAddress ip,int ttl) : base(QTYPE.AAAA,ttl)
{
m_IP = ip;
}
#region static method Parse
/// <summary>
/// Parses resource record from reply data.
/// </summary>
/// <param name="reply">DNS server reply data.</param>
/// <param name="offset">Current offset in reply data.</param>
/// <param name="rdLength">Resource record data length.</param>
/// <param name="ttl">Time to live in seconds.</param>
public static DNS_rr_AAAA Parse(byte[] reply,ref int offset,int rdLength,int ttl)
{
// IPv6 = 16xbyte
byte[] ip = new byte[rdLength];
Array.Copy(reply,offset,ip,0,rdLength);
return new DNS_rr_AAAA(new IPAddress(ip),ttl);
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets host IP address.
/// </summary>
public IPAddress IP
{
get{ return m_IP; }
}
#endregion
}
}

View File

@ -0,0 +1,60 @@
using System;
namespace MailServer.Misc.DNS
{
/// <summary>
/// CNAME record class.
/// </summary>
[Serializable]
public class DNS_rr_CNAME : DNS_rr_base
{
private string m_Alias = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="alias">Alias.</param>
/// <param name="ttl">TTL value.</param>
public DNS_rr_CNAME(string alias,int ttl) : base(QTYPE.CNAME,ttl)
{
m_Alias = alias;
}
#region static method Parse
/// <summary>
/// Parses resource record from reply data.
/// </summary>
/// <param name="reply">DNS server reply data.</param>
/// <param name="offset">Current offset in reply data.</param>
/// <param name="rdLength">Resource record data length.</param>
/// <param name="ttl">Time to live in seconds.</param>
public static DNS_rr_CNAME Parse(byte[] reply,ref int offset,int rdLength,int ttl)
{
string name = "";
if(Dns_Client.GetQName(reply,ref offset,ref name)){
return new DNS_rr_CNAME(name,ttl);
}
else{
throw new ArgumentException("Invalid CNAME resource record data !");
}
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets alias.
/// </summary>
public string Alias
{
get{ return m_Alias; }
}
#endregion
}
}

View File

@ -0,0 +1,86 @@
using System;
namespace MailServer.Misc.DNS
{
/// <summary>
/// HINFO record.
/// </summary>
public class DNS_rr_HINFO : DNS_rr_base
{
private string m_CPU = "";
private string m_OS = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="cpu">Host CPU.</param>
/// <param name="os">Host OS.</param>
/// <param name="ttl">TTL value.</param>
public DNS_rr_HINFO(string cpu,string os,int ttl) : base(QTYPE.HINFO,ttl)
{
m_CPU = cpu;
m_OS = os;
}
#region static method Parse
/// <summary>
/// Parses resource record from reply data.
/// </summary>
/// <param name="reply">DNS server reply data.</param>
/// <param name="offset">Current offset in reply data.</param>
/// <param name="rdLength">Resource record data length.</param>
/// <param name="ttl">Time to live in seconds.</param>
public static DNS_rr_HINFO Parse(byte[] reply,ref int offset,int rdLength,int ttl)
{
/* RFC 1035 3.3.2. HINFO RDATA format
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ CPU /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ OS /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
CPU A <character-string> which specifies the CPU type.
OS A <character-string> which specifies the operating
system type.
Standard values for CPU and OS can be found in [RFC-1010].
*/
// CPU
string cpu = Dns_Client.ReadCharacterString(reply,ref offset);
// OS
string os = Dns_Client.ReadCharacterString(reply,ref offset);
return new DNS_rr_HINFO(cpu,os,ttl);
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets host's CPU.
/// </summary>
public string CPU
{
get{ return m_CPU; }
}
/// <summary>
/// Gets host's OS.
/// </summary>
public string OS
{
get{ return m_OS; }
}
#endregion
}
}

View File

@ -0,0 +1,126 @@
using System;
namespace MailServer.Misc.DNS
{
/// <summary>
/// MX record class.
/// </summary>
[Serializable]
public class DNS_rr_MX : DNS_rr_base,IComparable
{
private int m_Preference = 0;
private string m_Host = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="preference">MX record preference.</param>
/// <param name="host">Mail host dns name.</param>
/// <param name="ttl">TTL value.</param>
public DNS_rr_MX(int preference,string host,int ttl) : base(QTYPE.MX,ttl)
{
m_Preference = preference;
m_Host = host;
}
#region static method Parse
/// <summary>
/// Parses resource record from reply data.
/// </summary>
/// <param name="reply">DNS server reply data.</param>
/// <param name="offset">Current offset in reply data.</param>
/// <param name="rdLength">Resource record data length.</param>
/// <param name="ttl">Time to live in seconds.</param>
public static DNS_rr_MX Parse(byte[] reply,ref int offset,int rdLength,int ttl)
{
/* RFC 1035 3.3.9. MX RDATA format
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| PREFERENCE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ EXCHANGE /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
where:
PREFERENCE
A 16 bit integer which specifies the preference given to
this RR among others at the same owner. Lower values
are preferred.
EXCHANGE
A <domain-name> which specifies a host willing to act as
a mail exchange for the owner name.
*/
int pref = reply[offset++] << 8 | reply[offset++];
string name = "";
if(Dns_Client.GetQName(reply,ref offset,ref name)){
return new DNS_rr_MX(pref,name,ttl);
}
else{
throw new ArgumentException("Invalid MX resource record data !");
}
}
#endregion
#region IComparable Implementation
/// <summary>
/// Compares the current instance with another object of the same type.
/// </summary>
/// <param name="obj">An object to compare with this instance. </param>
/// <returns>Returns 0 if two objects are equal, returns negative value if this object is less,
/// returns positive value if this object is grater.</returns>
public int CompareTo(object obj)
{
if(obj == null){
throw new ArgumentNullException("obj");
}
if(!(obj is DNS_rr_MX)){
throw new ArgumentException("Argument obj is not MX_Record !");
}
DNS_rr_MX mx = (DNS_rr_MX)obj;
if(this.Preference > mx.Preference){
return 1;
}
else if(this.Preference < mx.Preference){
return -1;
}
else{
return 0;
}
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets MX record preference. The lower number is the higher priority server.
/// </summary>
public int Preference
{
get{ return m_Preference; }
}
/// <summary>
/// Gets mail host dns name.
/// </summary>
public string Host
{
get{ return m_Host; }
}
#endregion
}
}

View File

@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace MailServer.Misc.DNS
{
/// <summary>
/// NAPRT(Naming Authority Pointer) resource record. Defined in RFC 3403.
/// </summary>
[Serializable]
public class DNS_rr_NAPTR : DNS_rr_base
{
private int m_Order = 0;
private int m_Preference = 0;
private string m_Flags = "";
private string m_Services = "";
private string m_Regexp = "";
private string m_Replacement = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="order">Oorder in which the NAPTR records MUST be processed.</param>
/// <param name="preference">Order in which NAPTR records with equal Order values SHOULD be processed.</param>
/// <param name="flags">Flags which control the rewriting and interpretation of the fields in the record.</param>
/// <param name="services">Services related to this record.</param>
/// <param name="regexp">Regular expression that is applied to the original string.</param>
/// <param name="replacement">Regular expressions replacement value.</param>
/// <param name="ttl">Time to live value in seconds.</param>
public DNS_rr_NAPTR(int order,int preference,string flags,string services,string regexp,string replacement,int ttl) : base(QTYPE.NAPTR,ttl)
{
m_Order = order;
m_Preference = preference;
m_Flags = flags;
m_Services = services;
m_Regexp = regexp;
m_Replacement = replacement;
}
#region static method Parse
/// <summary>
/// Parses resource record from reply data.
/// </summary>
/// <param name="reply">DNS server reply data.</param>
/// <param name="offset">Current offset in reply data.</param>
/// <param name="rdLength">Resource record data length.</param>
/// <param name="ttl">Time to live in seconds.</param>
public static DNS_rr_NAPTR Parse(byte[] reply,ref int offset,int rdLength,int ttl)
{
/* RFC 3403.
The packet format for the NAPTR record is as follows
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ORDER |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| PREFERENCE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ FLAGS /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ SERVICES /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ REGEXP /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ REPLACEMENT /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
int order = reply[offset++] << 8 | reply[offset++];
int preference = reply[offset++] << 8 | reply[offset++];
string flags = Dns_Client.ReadCharacterString(reply,ref offset);
string services = Dns_Client.ReadCharacterString(reply,ref offset);
string regexp = Dns_Client.ReadCharacterString(reply,ref offset);
string replacement = "";
Dns_Client.GetQName(reply,ref offset,ref replacement);
return new DNS_rr_NAPTR(order,preference,flags,services,regexp,replacement,ttl);
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets order in which the NAPTR records MUST be processed in order to accurately
/// represent the ordered list of Rules.
/// </summary>
public int Order
{
get{ return m_Order; }
}
/// <summary>
/// Gets the order in which NAPTR records with equal Order values SHOULD be processed,
/// low numbers being processed before high numbers.
/// </summary>
public int Preference
{
get{ return m_Preference; }
}
/// <summary>
/// Gets flags which control the rewriting and interpretation of the fields in the record.
/// </summary>
public string Flags
{
get{ return m_Flags; }
}
/// <summary>
/// Gets services related to this record. Known values can be get from: http://www.iana.org/assignments/enum-services.
/// </summary>
public string Services
{
get{ return m_Services; }
}
/// <summary>
/// Gets regular expression that is applied to the original string held by the client in order to
/// construct the next domain name to lookup.
/// </summary>
public string Regexp
{
get{ return m_Regexp; }
}
/// <summary>
/// Gets regular expressions replacement value.
/// </summary>
public string Replacement
{
get{ return m_Replacement; }
}
#endregion
}
}

View File

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MailServer.Misc.DNS
{
/// <summary>
/// NS record class.
/// </summary>
[Serializable]
public class DNS_rr_NS : DNS_rr_base
{
private string m_NameServer = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="nameServer">Name server name.</param>
/// <param name="ttl">TTL value.</param>
public DNS_rr_NS(string nameServer, int ttl)
: base(QTYPE.NS, ttl)
{
m_NameServer = nameServer;
}
#region static method Parse
/// <summary>
/// Parses resource record from reply data.
/// </summary>
/// <param name="reply">DNS server reply data.</param>
/// <param name="offset">Current offset in reply data.</param>
/// <param name="rdLength">Resource record data length.</param>
/// <param name="ttl">Time to live in seconds.</param>
public static DNS_rr_NS Parse(byte[] reply, ref int offset, int rdLength, int ttl)
{
// Name server name
string name = "";
if (Dns_Client.GetQName(reply, ref offset, ref name))
{
return new DNS_rr_NS(name, ttl);
}
else
{
throw new ArgumentException("Invalid NS resource record data !");
}
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets name server name.
/// </summary>
public string NameServer
{
get { return m_NameServer; }
}
#endregion
}
}

View File

@ -0,0 +1,60 @@
using System;
namespace MailServer.Misc.DNS
{
/// <summary>
/// PTR record class.
/// </summary>
[Serializable]
public class DNS_rr_PTR : DNS_rr_base
{
private string m_DomainName = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="domainName">DomainName.</param>
/// <param name="ttl">TTL value.</param>
public DNS_rr_PTR(string domainName,int ttl) : base(QTYPE.PTR,ttl)
{
m_DomainName = domainName;
}
#region static method Parse
/// <summary>
/// Parses resource record from reply data.
/// </summary>
/// <param name="reply">DNS server reply data.</param>
/// <param name="offset">Current offset in reply data.</param>
/// <param name="rdLength">Resource record data length.</param>
/// <param name="ttl">Time to live in seconds.</param>
public static DNS_rr_PTR Parse(byte[] reply,ref int offset,int rdLength,int ttl)
{
string name = "";
if(Dns_Client.GetQName(reply,ref offset,ref name)){
return new DNS_rr_PTR(name,ttl);
}
else{
throw new ArgumentException("Invalid PTR resource record data !");
}
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets domain name.
/// </summary>
public string DomainName
{
get{ return m_DomainName; }
}
#endregion
}
}

View File

@ -0,0 +1,204 @@
using System;
namespace MailServer.Misc.DNS
{
/// <summary>
/// SOA record class.
/// </summary>
[Serializable]
public class DNS_rr_SOA : DNS_rr_base
{
private string m_NameServer = "";
private string m_AdminEmail = "";
private long m_Serial = 0;
private long m_Refresh = 0;
private long m_Retry = 0;
private long m_Expire = 0;
private long m_Minimum = 0;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="nameServer">Name server.</param>
/// <param name="adminEmail">Zone administrator email.</param>
/// <param name="serial">Version number of the original copy of the zone.</param>
/// <param name="refresh">Time interval(in seconds) before the zone should be refreshed.</param>
/// <param name="retry">Time interval(in seconds) that should elapse before a failed refresh should be retried.</param>
/// <param name="expire">Time value(in seconds) that specifies the upper limit on the time interval that can elapse before the zone is no longer authoritative.</param>
/// <param name="minimum">Minimum TTL(in seconds) field that should be exported with any RR from this zone.</param>
/// <param name="ttl">TTL value.</param>
public DNS_rr_SOA(string nameServer,string adminEmail,long serial,long refresh,long retry,long expire,long minimum,int ttl) : base(QTYPE.SOA,ttl)
{
m_NameServer = nameServer;
m_AdminEmail = adminEmail;
m_Serial = serial;
m_Refresh = refresh;
m_Retry = retry;
m_Expire = expire;
m_Minimum = minimum;
}
#region static method Parse
/// <summary>
/// Parses resource record from reply data.
/// </summary>
/// <param name="reply">DNS server reply data.</param>
/// <param name="offset">Current offset in reply data.</param>
/// <param name="rdLength">Resource record data length.</param>
/// <param name="ttl">Time to live in seconds.</param>
public static DNS_rr_SOA Parse(byte[] reply,ref int offset,int rdLength,int ttl)
{
/* RFC 1035 3.3.13. SOA RDATA format
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ MNAME /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ RNAME /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| SERIAL |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| REFRESH |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| RETRY |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| EXPIRE |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| MINIMUM |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
where:
MNAME The <domain-name> of the name server that was the
original or primary source of data for this zone.
RNAME A <domain-name> which specifies the mailbox of the
person responsible for this zone.
SERIAL The unsigned 32 bit version number of the original copy
of the zone. Zone transfers preserve this value. This
value wraps and should be compared using sequence space
arithmetic.
REFRESH A 32 bit time interval before the zone should be
refreshed.
RETRY A 32 bit time interval that should elapse before a
failed refresh should be retried.
EXPIRE A 32 bit time value that specifies the upper limit on
the time interval that can elapse before the zone is no
longer authoritative.
MINIMUM The unsigned 32 bit minimum TTL field that should be
exported with any RR from this zone.
*/
//---- Parse record -------------------------------------------------------------//
// MNAME
string nameserver = "";
Dns_Client.GetQName(reply,ref offset,ref nameserver);
// RNAME
string adminMailBox = "";
Dns_Client.GetQName(reply,ref offset,ref adminMailBox);
char[] adminMailBoxAr = adminMailBox.ToCharArray();
for(int i=0;i<adminMailBoxAr.Length;i++){
if(adminMailBoxAr[i] == '.'){
adminMailBoxAr[i] = '@';
break;
}
}
adminMailBox = new string(adminMailBoxAr);
// SERIAL
long serial = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | reply[offset++];
// REFRESH
long refresh = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | reply[offset++];
// RETRY
long retry = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | reply[offset++];
// EXPIRE
long expire = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | reply[offset++];
// MINIMUM
long minimum = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | reply[offset++];
//--------------------------------------------------------------------------------//
return new DNS_rr_SOA(nameserver,adminMailBox,serial,refresh,retry,expire,minimum,ttl);
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets name server.
/// </summary>
public string NameServer
{
get{ return m_NameServer; }
}
/// <summary>
/// Gets zone administrator email.
/// </summary>
public string AdminEmail
{
get{ return m_AdminEmail; }
}
/// <summary>
/// Gets version number of the original copy of the zone.
/// </summary>
public long Serial
{
get{ return m_Serial; }
}
/// <summary>
/// Gets time interval(in seconds) before the zone should be refreshed.
/// </summary>
public long Refresh
{
get{ return m_Refresh; }
}
/// <summary>
/// Gets time interval(in seconds) that should elapse before a failed refresh should be retried.
/// </summary>
public long Retry
{
get{ return m_Retry; }
}
/// <summary>
/// Gets time value(in seconds) that specifies the upper limit on the time interval that can elapse before the zone is no longer authoritative.
/// </summary>
public long Expire
{
get{ return m_Expire; }
}
/// <summary>
/// Gets minimum TTL(in seconds) field that should be exported with any RR from this zone.
/// </summary>
public long Minimum
{
get{ return m_Minimum; }
}
#endregion
}
}

View File

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace MailServer.Misc.DNS
{
/// <summary>
/// DNS SRV record. SRV record specifies the location of services. Defined in RFC 2782.
/// </summary>
[Serializable]
public class DNS_rr_SRV : DNS_rr_base
{
private int m_Priority = 1;
private int m_Weight = 1;
private int m_Port = 0;
private string m_Target = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="priority">Service priority.</param>
/// <param name="weight">Weight value.</param>
/// <param name="port">Service port.</param>
/// <param name="target">Service provider host name or IP address.</param>
/// <param name="ttl">Time to live value in seconds.</param>
public DNS_rr_SRV(int priority,int weight,int port,string target,int ttl) : base(QTYPE.SRV,ttl)
{
m_Priority = priority;
m_Weight = weight;
m_Port = port;
m_Target = target;
}
#region static method Parse
/// <summary>
/// Parses resource record from reply data.
/// </summary>
/// <param name="reply">DNS server reply data.</param>
/// <param name="offset">Current offset in reply data.</param>
/// <param name="rdLength">Resource record data length.</param>
/// <param name="ttl">Time to live in seconds.</param>
public static DNS_rr_SRV Parse(byte[] reply,ref int offset,int rdLength,int ttl)
{
// Priority Weight Port Target
// Priority
int priority = reply[offset++] << 8 | reply[offset++];
// Weight
int weight = reply[offset++] << 8 | reply[offset++];
// Port
int port = reply[offset++] << 8 | reply[offset++];
// Target
string target = "";
Dns_Client.GetQName(reply,ref offset,ref target);
return new DNS_rr_SRV(priority,weight,port,target,ttl);
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets service priority. Lowest value means greater priority.
/// </summary>
public int Priority
{
get{ return m_Priority; }
}
/// <summary>
/// Gets weight. The weight field specifies a relative weight for entries with the same priority.
/// Larger weights SHOULD be given a proportionately higher probability of being selected.
/// </summary>
public int Weight
{
get{ return m_Weight; }
}
/// <summary>
/// Port where service runs.
/// </summary>
public int Port
{
get{ return m_Port; }
}
/// <summary>
/// Service provider host name or IP address.
/// </summary>
public string Target
{
get{ return m_Target; }
}
#endregion
}
}

View File

@ -0,0 +1,57 @@
using System;
namespace MailServer.Misc.DNS
{
/// <summary>
/// TXT record class.
/// </summary>
[Serializable]
public class DNS_rr_TXT : DNS_rr_base
{
private string m_Text = "";
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="text">Text.</param>
/// <param name="ttl">TTL value.</param>
public DNS_rr_TXT(string text,int ttl) : base(QTYPE.TXT,ttl)
{
m_Text = text;
}
#region static method Parse
/// <summary>
/// Parses resource record from reply data.
/// </summary>
/// <param name="reply">DNS server reply data.</param>
/// <param name="offset">Current offset in reply data.</param>
/// <param name="rdLength">Resource record data length.</param>
/// <param name="ttl">Time to live in seconds.</param>
public static DNS_rr_TXT Parse(byte[] reply,ref int offset,int rdLength,int ttl)
{
// TXT RR
string text = Dns_Client.ReadCharacterString(reply,ref offset);
return new DNS_rr_TXT(text,ttl);
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets text.
/// </summary>
public string Text
{
get{ return m_Text; }
}
#endregion
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MailServer.Misc.DNS
{
/// <summary>
/// Base class for DNS records.
/// </summary>
public abstract class DNS_rr_base
{
private QTYPE m_Type = QTYPE.A;
private int m_TTL = -1;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="recordType">Record type (A,MX, ...).</param>
/// <param name="ttl">TTL (time to live) value in seconds.</param>
public DNS_rr_base(QTYPE recordType, int ttl)
{
m_Type = recordType;
m_TTL = ttl;
}
#region Properties Implementation
/// <summary>
/// Gets record type (A,MX,...).
/// </summary>
public QTYPE RecordType
{
get { return m_Type; }
}
/// <summary>
/// Gets TTL (time to live) value in seconds.
/// </summary>
public int TTL
{
get { return m_TTL; }
}
#endregion
}
}

View File

@ -0,0 +1,193 @@
using System;
using System.IO;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace MailServer.Misc.DNS
{
#region struct CacheEntry
/// <summary>
/// Dns cache entry.
/// </summary>
[Serializable]
internal struct DnsCacheEntry
{
private DnsServerResponse m_pResponse;
private DateTime m_Time;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="answers">Dns answers.</param>
/// <param name="addTime">Entry add time.</param>
public DnsCacheEntry(DnsServerResponse answers,DateTime addTime)
{
m_pResponse = answers;
m_Time = addTime;
}
/// <summary>
/// Gets dns answers.
/// </summary>
public DnsServerResponse Answers
{
get{ return m_pResponse; }
}
/// <summary>
/// Gets entry add time.
/// </summary>
public DateTime Time
{
get{ return m_Time; }
}
}
#endregion
/// <summary>
/// This class implements dns query cache.
/// </summary>
public class DnsCache
{
private static Hashtable m_pCache = null;
private static long m_CacheTime = 10000;
/// <summary>
/// Default constructor.
/// </summary>
static DnsCache()
{
m_pCache = new Hashtable();
}
#region method GetFromCache
/// <summary>
/// Tries to get dns records from cache, if any.
/// </summary>
/// <param name="qname"></param>
/// <param name="qtype"></param>
/// <returns>Returns null if not in cache.</returns>
public static DnsServerResponse GetFromCache(string qname,int qtype)
{
try{
if(m_pCache.Contains(qname + qtype)){
DnsCacheEntry entry = (DnsCacheEntry)m_pCache[qname + qtype];
// If cache object isn't expired
if(entry.Time.AddSeconds(m_CacheTime) > DateTime.Now){
return entry.Answers;
}
}
}
catch{
}
return null;
}
#endregion
#region method AddToCache
/// <summary>
/// Adds dns records to cache. If old entry exists, it is replaced.
/// </summary>
/// <param name="qname"></param>
/// <param name="qtype"></param>
/// <param name="answers"></param>
public static void AddToCache(string qname,int qtype,DnsServerResponse answers)
{
if(answers == null){
return;
}
try{
lock(m_pCache){
// Remove old cache entry, if any.
if(m_pCache.Contains(qname + qtype)){
m_pCache.Remove(qname + qtype);
}
m_pCache.Add(qname + qtype,new DnsCacheEntry(answers,DateTime.Now));
}
}
catch{
}
}
#endregion
#region method ClearCache
/// <summary>
/// Clears DNS cache.
/// </summary>
public static void ClearCache()
{
lock(m_pCache){
m_pCache.Clear();
}
}
#endregion
#region method SerializeCache
/// <summary>
/// Serializes current cache.
/// </summary>
/// <returns>Return serialized cache.</returns>
public static byte[] SerializeCache()
{
lock(m_pCache){
MemoryStream retVal = new MemoryStream();
BinaryFormatter b = new BinaryFormatter();
b.Serialize(retVal,m_pCache);
return retVal.ToArray();
}
}
#endregion
#region method DeSerializeCache
/// <summary>
/// DeSerializes stored cache.
/// </summary>
/// <param name="cacheData">This value must be DnsCache.SerializeCache() method value.</param>
public static void DeSerializeCache(byte[] cacheData)
{
lock(m_pCache){
MemoryStream retVal = new MemoryStream(cacheData);
BinaryFormatter b = new BinaryFormatter();
m_pCache = (Hashtable)b.Deserialize(retVal);
}
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets or sets how long(seconds) to cache dns query.
/// </summary>
public static long CacheTime
{
get{ return m_CacheTime; }
set{ m_CacheTime = value; }
}
#endregion
}
}

View File

@ -0,0 +1,389 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace MailServer.Misc.DNS
{
/// <summary>
/// This class represents dns server response.
/// </summary>
[Serializable]
public class DnsServerResponse
{
private bool m_Success = true;
private int m_ID = 0;
private RCODE m_RCODE = RCODE.NO_ERROR;
private List<DNS_rr_base> m_pAnswers = null;
private List<DNS_rr_base> m_pAuthoritiveAnswers = null;
private List<DNS_rr_base> m_pAdditionalAnswers = null;
internal DnsServerResponse(bool connectionOk, int id, RCODE rcode, List<DNS_rr_base> answers, List<DNS_rr_base> authoritiveAnswers, List<DNS_rr_base> additionalAnswers)
{
m_Success = connectionOk;
m_ID = id;
m_RCODE = rcode;
m_pAnswers = answers;
m_pAuthoritiveAnswers = authoritiveAnswers;
m_pAdditionalAnswers = additionalAnswers;
}
#region method GetARecords
/// <summary>
/// Gets IPv4 host addess records.
/// </summary>
/// <returns></returns>
public DNS_rr_A[] GetARecords()
{
List<DNS_rr_A> retVal = new List<DNS_rr_A>();
foreach (DNS_rr_base record in m_pAnswers)
{
if (record.RecordType == QTYPE.A)
{
retVal.Add((DNS_rr_A)record);
}
}
return retVal.ToArray();
}
#endregion
#region method GetNSRecords
/// <summary>
/// Gets name server records.
/// </summary>
/// <returns></returns>
public DNS_rr_NS[] GetNSRecords()
{
List<DNS_rr_NS> retVal = new List<DNS_rr_NS>();
foreach (DNS_rr_base record in m_pAnswers)
{
if (record.RecordType == QTYPE.NS)
{
retVal.Add((DNS_rr_NS)record);
}
}
return retVal.ToArray();
}
#endregion
#region method GetCNAMERecords
/// <summary>
/// Gets CNAME records.
/// </summary>
/// <returns></returns>
public DNS_rr_CNAME[] GetCNAMERecords()
{
List<DNS_rr_CNAME> retVal = new List<DNS_rr_CNAME>();
foreach (DNS_rr_base record in m_pAnswers)
{
if (record.RecordType == QTYPE.CNAME)
{
retVal.Add((DNS_rr_CNAME)record);
}
}
return retVal.ToArray();
}
#endregion
#region method GetSOARecords
/// <summary>
/// Gets SOA records.
/// </summary>
/// <returns></returns>
public DNS_rr_SOA[] GetSOARecords()
{
List<DNS_rr_SOA> retVal = new List<DNS_rr_SOA>();
foreach (DNS_rr_base record in m_pAnswers)
{
if (record.RecordType == QTYPE.SOA)
{
retVal.Add((DNS_rr_SOA)record);
}
}
return retVal.ToArray();
}
#endregion
#region method GetPTRRecords
/// <summary>
/// Gets PTR records.
/// </summary>
/// <returns></returns>
public DNS_rr_PTR[] GetPTRRecords()
{
List<DNS_rr_PTR> retVal = new List<DNS_rr_PTR>();
foreach (DNS_rr_base record in m_pAnswers)
{
if (record.RecordType == QTYPE.PTR)
{
retVal.Add((DNS_rr_PTR)record);
}
}
return retVal.ToArray();
}
#endregion
#region method GetHINFORecords
/// <summary>
/// Gets HINFO records.
/// </summary>
/// <returns></returns>
public DNS_rr_HINFO[] GetHINFORecords()
{
List<DNS_rr_HINFO> retVal = new List<DNS_rr_HINFO>();
foreach (DNS_rr_base record in m_pAnswers)
{
if (record.RecordType == QTYPE.HINFO)
{
retVal.Add((DNS_rr_HINFO)record);
}
}
return retVal.ToArray();
}
#endregion
#region method GetMXRecords
/// <summary>
/// Gets MX records.(MX records are sorted by preference, lower array element is prefered)
/// </summary>
/// <returns></returns>
public DNS_rr_MX[] GetMXRecords()
{
List<DNS_rr_MX> mx = new List<DNS_rr_MX>();
foreach (DNS_rr_base record in m_pAnswers)
{
if (record.RecordType == QTYPE.MX)
{
mx.Add((DNS_rr_MX)record);
}
}
// Sort MX records by preference.
DNS_rr_MX[] retVal = mx.ToArray();
Array.Sort(retVal);
return retVal;
}
#endregion
#region method GetTXTRecords
/// <summary>
/// Gets text records.
/// </summary>
/// <returns></returns>
public DNS_rr_TXT[] GetTXTRecords()
{
List<DNS_rr_TXT> retVal = new List<DNS_rr_TXT>();
foreach (DNS_rr_base record in m_pAnswers)
{
if (record.RecordType == QTYPE.TXT)
{
retVal.Add((DNS_rr_TXT)record);
}
}
return retVal.ToArray();
}
#endregion
#region method GetAAAARecords
/// <summary>
/// Gets IPv6 host addess records.
/// </summary>
/// <returns></returns>
public DNS_rr_AAAA[] GetAAAARecords()
{
List<DNS_rr_AAAA> retVal = new List<DNS_rr_AAAA>();
foreach (DNS_rr_base record in m_pAnswers)
{
if (record.RecordType == QTYPE.AAAA)
{
retVal.Add((DNS_rr_AAAA)record);
}
}
return retVal.ToArray();
}
#endregion
#region method GetSRVRecords
/// <summary>
/// Gets SRV resource records.
/// </summary>
/// <returns></returns>
public DNS_rr_SRV[] GetSRVRecords()
{
List<DNS_rr_SRV> retVal = new List<DNS_rr_SRV>();
foreach (DNS_rr_base record in m_pAnswers)
{
if (record.RecordType == QTYPE.SRV)
{
retVal.Add((DNS_rr_SRV)record);
}
}
return retVal.ToArray();
}
#endregion
#region method GetNAPTRRecords
/// <summary>
/// Gets NAPTR resource records.
/// </summary>
/// <returns></returns>
public DNS_rr_NAPTR[] GetNAPTRRecords()
{
List<DNS_rr_NAPTR> retVal = new List<DNS_rr_NAPTR>();
foreach (DNS_rr_base record in m_pAnswers)
{
if (record.RecordType == QTYPE.NAPTR)
{
retVal.Add((DNS_rr_NAPTR)record);
}
}
return retVal.ToArray();
}
#endregion
#region method FilterRecords
/// <summary>
/// Filters out specified type of records from answer.
/// </summary>
/// <param name="answers"></param>
/// <param name="type"></param>
/// <returns></returns>
private List<DNS_rr_base> FilterRecordsX(List<DNS_rr_base> answers, QTYPE type)
{
List<DNS_rr_base> retVal = new List<DNS_rr_base>();
foreach (DNS_rr_base record in answers)
{
if (record.RecordType == type)
{
retVal.Add(record);
}
}
return retVal;
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets if connection to dns server was successful.
/// </summary>
public bool ConnectionOk
{
get { return m_Success; }
}
/// <summary>
/// Gets DNS transaction ID.
/// </summary>
public int ID
{
get { return m_ID; }
}
/// <summary>
/// Gets dns server response code.
/// </summary>
public RCODE ResponseCode
{
get { return m_RCODE; }
}
/// <summary>
/// Gets all resource records returned by server (answer records section + authority records section + additional records section).
/// NOTE: Before using this property ensure that ConnectionOk=true and ResponseCode=RCODE.NO_ERROR.
/// </summary>
public DNS_rr_base[] AllAnswers
{
get
{
List<DNS_rr_base> retVal = new List<DNS_rr_base>();
retVal.AddRange(m_pAnswers.ToArray());
retVal.AddRange(m_pAuthoritiveAnswers.ToArray());
retVal.AddRange(m_pAdditionalAnswers.ToArray());
return retVal.ToArray();
}
}
/// <summary>
/// Gets dns server returned answers. NOTE: Before using this property ensure that ConnectionOk=true and ResponseCode=RCODE.NO_ERROR.
/// </summary>
/// <code>
/// // NOTE: DNS server may return diffrent record types even if you query MX.
/// // For example you query lumisoft.ee MX and server may response:
/// // 1) MX - mail.lumisoft.ee
/// // 2) A - lumisoft.ee
/// //
/// // Before casting to right record type, see what type record is !
///
///
/// foreach(DnsRecordBase record in Answers){
/// // MX record, cast it to MX_Record
/// if(record.RecordType == QTYPE.MX){
/// MX_Record mx = (MX_Record)record;
/// }
/// }
/// </code>
public DNS_rr_base[] Answers
{
get { return m_pAnswers.ToArray(); }
}
/// <summary>
/// Gets name server resource records in the authority records section. NOTE: Before using this property ensure that ConnectionOk=true and ResponseCode=RCODE.NO_ERROR.
/// </summary>
public DNS_rr_base[] AuthoritiveAnswers
{
get { return m_pAuthoritiveAnswers.ToArray(); }
}
/// <summary>
/// Gets resource records in the additional records section. NOTE: Before using this property ensure that ConnectionOk=true and ResponseCode=RCODE.NO_ERROR.
/// </summary>
public DNS_rr_base[] AdditionalAnswers
{
get { return m_pAdditionalAnswers.ToArray(); }
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MailServer.Misc.DNS
{
/// <summary>
///
/// </summary>
internal enum OPCODE
{
/// <summary>
/// A standard query.
/// </summary>
QUERY = 0,
/// <summary>
/// An inverse query. Obsoleted by RFC 3425.
/// </summary>
IQUERY = 1,
/// <summary>
/// A server status request.
/// </summary>
STATUS = 2,
}
}

View File

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MailServer.Misc.DNS
{
/// <summary>
/// Query type.
/// </summary>
public enum QTYPE
{
/// <summary>
/// IPv4 host address
/// </summary>
A = 1,
/// <summary>
/// An authoritative name server.
/// </summary>
NS = 2,
// MD = 3, Obsolete
// MF = 4, Obsolete
/// <summary>
/// The canonical name for an alias.
/// </summary>
CNAME = 5,
/// <summary>
/// Marks the start of a zone of authority.
/// </summary>
SOA = 6,
// MB = 7, EXPERIMENTAL
// MG = 8, EXPERIMENTAL
// MR = 9, EXPERIMENTAL
// NULL = 10, EXPERIMENTAL
/* /// <summary>
/// A well known service description.
/// </summary>
WKS = 11, */
/// <summary>
/// A domain name pointer.
/// </summary>
PTR = 12,
/// <summary>
/// Host information.
/// </summary>
HINFO = 13,
/*
/// <summary>
/// Mailbox or mail list information.
/// </summary>
MINFO = 14, */
/// <summary>
/// Mail exchange.
/// </summary>
MX = 15,
/// <summary>
/// Text strings.
/// </summary>
TXT = 16,
/// <summary>
/// IPv6 host address.
/// </summary>
AAAA = 28,
/// <summary>
/// SRV record specifies the location of services.
/// </summary>
SRV = 33,
/// <summary>
/// NAPTR(Naming Authority Pointer) record.
/// </summary>
NAPTR = 35,
/// <summary>
/// All records what server returns.
/// </summary>
ANY = 255,
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MailServer.Misc.DNS
{
/// <summary>
/// Dns server reply codes.
/// </summary>
public enum RCODE
{
/// <summary>
/// No error condition.
/// </summary>
NO_ERROR = 0,
/// <summary>
/// Format error - The name server was unable to interpret the query.
/// </summary>
FORMAT_ERRROR = 1,
/// <summary>
/// Server failure - The name server was unable to process this query due to a problem with the name server.
/// </summary>
SERVER_FAILURE = 2,
/// <summary>
/// Name Error - Meaningful only for responses from an authoritative name server, this code signifies that the
/// domain name referenced in the query does not exist.
/// </summary>
NAME_ERROR = 3,
/// <summary>
/// Not Implemented - The name server does not support the requested kind of query.
/// </summary>
NOT_IMPLEMENTED = 4,
/// <summary>
/// Refused - The name server refuses to perform the specified operation for policy reasons.
/// </summary>
REFUSED = 5,
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace MailServer.Misc
{
/// <summary>
/// This class universal event arguments for transporting single value.
/// </summary>
/// <typeparam name="T">Event data.</typeparam>
public class EventArgs<T> : EventArgs
{
private T m_pValue;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="value">Event data.</param>
public EventArgs(T value)
{
m_pValue = value;
}
#region Properties implementation
/// <summary>
/// Gets event data.
/// </summary>
public T Value
{
get { return m_pValue; }
}
#endregion
}
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MailServer.Misc
namespace MailServer.Misc.IO
{
/// <summary>
/// This class implements base64 encoder/decoder. Defined in RFC 4648.

View File

@ -0,0 +1,539 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
namespace MailServer.Misc.IO
{
/// <summary>
/// This class implements base64 encoder/decoder. Defined in RFC 4648.
/// </summary>
public class Base64Stream : Stream, IDisposable
{
#region BASE64_ENCODE_TABLE
private readonly static byte[] BASE64_ENCODE_TABLE = new byte[]{
(byte)'A',(byte)'B',(byte)'C',(byte)'D',(byte)'E',(byte)'F',(byte)'G',(byte)'H',(byte)'I',(byte)'J',
(byte)'K',(byte)'L',(byte)'M',(byte)'N',(byte)'O',(byte)'P',(byte)'Q',(byte)'R',(byte)'S',(byte)'T',
(byte)'U',(byte)'V',(byte)'W',(byte)'X',(byte)'Y',(byte)'Z',(byte)'a',(byte)'b',(byte)'c',(byte)'d',
(byte)'e',(byte)'f',(byte)'g',(byte)'h',(byte)'i',(byte)'j',(byte)'k',(byte)'l',(byte)'m',(byte)'n',
(byte)'o',(byte)'p',(byte)'q',(byte)'r',(byte)'s',(byte)'t',(byte)'u',(byte)'v',(byte)'w',(byte)'x',
(byte)'y',(byte)'z',(byte)'0',(byte)'1',(byte)'2',(byte)'3',(byte)'4',(byte)'5',(byte)'6',(byte)'7',
(byte)'8',(byte)'9',(byte)'+',(byte)'/'
};
#endregion
#region BASE64_DECODE_TABLE
private readonly static short[] BASE64_DECODE_TABLE = new short[]{
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0 - 9
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //10 - 19
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //20 - 29
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //30 - 39
-1,-1,-1,62,-1,-1,-1,63,52,53, //40 - 49
54,55,56,57,58,59,60,61,-1,-1, //50 - 59
-1,-1,-1,-1,-1, 0, 1, 2, 3, 4, //60 - 69
5, 6, 7, 8, 9,10,11,12,13,14, //70 - 79
15,16,17,18,19,20,21,22,23,24, //80 - 89
25,-1,-1,-1,-1,-1,-1,26,27,28, //90 - 99
29,30,31,32,33,34,35,36,37,38, //100 - 109
39,40,41,42,43,44,45,46,47,48, //110 - 119
49,50,51,-1,-1,-1,-1,-1 //120 - 127
};
#endregion
private bool m_IsDisposed = false;
private bool m_IsFinished = false;
private Stream m_pStream = null;
private bool m_IsOwner = false;
private bool m_AddLineBreaks = true;
private FileAccess m_AccessMode = FileAccess.ReadWrite;
private int m_EncodeBufferOffset = 0;
private int m_OffsetInEncode3x8Block = 0;
private byte[] m_pEncode3x8Block = new byte[3];
private byte[] m_pEncodeBuffer = new byte[78];
private byte[] m_pDecodedBlock = null;
private int m_DecodedBlockOffset = 0;
private int m_DecodedBlockCount = 0;
private Base64 m_pBase64 = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="stream">Stream which to encode/decode.</param>
/// <param name="owner">Specifies if Base64Stream is owner of <b>stream</b>.</param>
/// <param name="addLineBreaks">Specifies if encoder inserts CRLF after each 76 bytes.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null reference.</exception>
public Base64Stream(Stream stream, bool owner, bool addLineBreaks)
: this(stream, owner, addLineBreaks, FileAccess.ReadWrite)
{
}
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="stream">Stream which to encode/decode.</param>
/// <param name="owner">Specifies if Base64Stream is owner of <b>stream</b>.</param>
/// <param name="addLineBreaks">Specifies if encoder inserts CRLF after each 76 bytes.</param>
/// <param name="access">This stream access mode.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null reference.</exception>
public Base64Stream(Stream stream, bool owner, bool addLineBreaks, FileAccess access)
{
if (stream == null)
{
throw new ArgumentNullException("stream");
}
m_pStream = stream;
m_IsOwner = owner;
m_AddLineBreaks = addLineBreaks;
m_AccessMode = access;
m_pDecodedBlock = new byte[8000];
m_pBase64 = new Base64();
}
#region method Dispose
/// <summary>
/// Celans up any resources being used.
/// </summary>
public new void Dispose()
{
if (m_IsDisposed)
{
return;
}
try
{
Finish();
}
catch
{
}
m_IsDisposed = true;
if (m_IsOwner)
{
m_pStream.Close();
}
}
#endregion
#region override method Flush
/// <summary>
/// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
public override void Flush()
{
if (m_IsDisposed)
{
throw new ObjectDisposedException("Base64Stream");
}
}
#endregion
#region override method Seek
/// <summary>
/// Sets the position within the current stream. This method is not supported and always throws a NotSupportedException.
/// </summary>
/// <param name="offset">A byte offset relative to the <b>origin</b> parameter.</param>
/// <param name="origin">A value of type SeekOrigin indicating the reference point used to obtain the new position.</param>
/// <returns>The new position within the current stream.</returns>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
/// <exception cref="NotSupportedException">Is raised when this method is accessed.</exception>
public override long Seek(long offset, SeekOrigin origin)
{
if (m_IsDisposed)
{
throw new ObjectDisposedException("Base64Stream");
}
throw new NotSupportedException();
}
#endregion
#region override method SetLength
/// <summary>
/// Sets the length of the current stream. This method is not supported and always throws a NotSupportedException.
/// </summary>
/// <param name="value">The desired length of the current stream in bytes.</param>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
/// <exception cref="Seek">Is raised when this method is accessed.</exception>
public override void SetLength(long value)
{
if (m_IsDisposed)
{
throw new ObjectDisposedException("Base64Stream");
}
throw new NotSupportedException();
}
#endregion
#region override method Read
/// <summary>
/// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
/// </summary>
/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
/// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.</returns>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
/// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null reference.</exception>
/// <exception cref="ArgumentOutOfRangeException">Is raised when any of the arguments has out of valid range.</exception>
/// <exception cref="NotSupportedException">Is raised when reading not supported.</exception>
public override int Read(byte[] buffer, int offset, int count)
{
if (m_IsDisposed)
{
throw new ObjectDisposedException("Base64Stream");
}
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException("offset", "Argument 'offset' value must be >= 0.");
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", "Argument 'count' value must be >= 0.");
}
if (offset + count > buffer.Length)
{
throw new ArgumentOutOfRangeException("count", "Argument 'count' is bigger than than argument 'buffer' can store.");
}
if ((m_AccessMode & FileAccess.Read) == 0)
{
throw new NotSupportedException();
}
// We havn't any decoded data left, decode new data block.
if ((m_DecodedBlockCount - m_DecodedBlockOffset) == 0)
{
byte[] readBuffer = new byte[m_pDecodedBlock.Length + 3];
int readedCount = m_pStream.Read(readBuffer, 0, readBuffer.Length - 3);
// We reached end of stream, no more data.
if (readedCount == 0)
{
return 0;
}
// Decode block must contain only integral 4-byte base64 blocks.
// Count base64 chars.
int base64Count = 0;
for (int i = 0; i < readedCount; i++)
{
byte b = readBuffer[i];
if (b == '=' || BASE64_DECODE_TABLE[b] != -1)
{
base64Count++;
}
}
// Read while last block is full 4-byte base64 block.
while ((base64Count % 4) != 0)
{
int b = m_pStream.ReadByte();
// End of stream reached.
if (b == -1)
{
break;
}
else if (b == '=' || BASE64_DECODE_TABLE[b] != -1)
{
readBuffer[readedCount++] = (byte)b;
base64Count++;
}
}
// Decode block.
m_DecodedBlockCount = m_pBase64.Decode(readBuffer, 0, readedCount, m_pDecodedBlock, 0, true);
m_DecodedBlockOffset = 0;
}
int available = m_DecodedBlockCount - m_DecodedBlockOffset;
int countToCopy = Math.Min(count, available);
Array.Copy(m_pDecodedBlock, m_DecodedBlockOffset, buffer, offset, countToCopy);
m_DecodedBlockOffset += countToCopy;
return countToCopy;
}
#endregion
#region override method Write
/// <summary>
/// Encodes a sequence of bytes, writes to the current stream and advances the current position within this stream by the number of bytes written.
/// </summary>
/// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
/// <param name="count">The number of bytes to be written to the current stream.</param>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
/// <exception cref="InvalidOperationException">Is raised when this.Finish has been called and this method is accessed.</exception>
/// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null reference.</exception>
/// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
/// <exception cref="NotSupportedException">Is raised when reading not supported.</exception>
public override void Write(byte[] buffer, int offset, int count)
{
if (m_IsDisposed)
{
throw new ObjectDisposedException(this.GetType().Name);
}
if (m_IsFinished)
{
throw new InvalidOperationException("Stream is marked as finished by calling Finish method.");
}
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentException("Invalid argument 'offset' value.");
}
if (count < 0 || count > (buffer.Length - offset))
{
throw new ArgumentException("Invalid argument 'count' value.");
}
if ((m_AccessMode & FileAccess.Write) == 0)
{
throw new NotSupportedException();
}
/* RFC 4648.
Base64 is processed from left to right by 4 6-bit byte block, 4 6-bit byte block
are converted to 3 8-bit bytes.
If base64 4 byte block doesn't have 3 8-bit bytes, missing bytes are marked with =.
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w (pad) =
15 P 32 g 49 x
16 Q 33 h 50 y
NOTE: 4 base64 6-bit bytes = 3 8-bit bytes
// | 6-bit | 6-bit | 6-bit | 6-bit |
// | 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 |
// | 8-bit | 8-bit | 8-bit |
*/
int encodeBufSize = m_pEncodeBuffer.Length;
// Process all bytes.
for (int i = 0; i < count; i++)
{
m_pEncode3x8Block[m_OffsetInEncode3x8Block++] = buffer[offset + i];
// 3x8-bit encode block is full, encode it.
if (m_OffsetInEncode3x8Block == 3)
{
m_pEncodeBuffer[m_EncodeBufferOffset++] = BASE64_ENCODE_TABLE[m_pEncode3x8Block[0] >> 2];
m_pEncodeBuffer[m_EncodeBufferOffset++] = BASE64_ENCODE_TABLE[(m_pEncode3x8Block[0] & 0x03) << 4 | m_pEncode3x8Block[1] >> 4];
m_pEncodeBuffer[m_EncodeBufferOffset++] = BASE64_ENCODE_TABLE[(m_pEncode3x8Block[1] & 0x0F) << 2 | m_pEncode3x8Block[2] >> 6];
m_pEncodeBuffer[m_EncodeBufferOffset++] = BASE64_ENCODE_TABLE[(m_pEncode3x8Block[2] & 0x3F)];
// Encode buffer is full, write buffer to underlaying stream (we reserved 2 bytes for CRLF).
if (m_EncodeBufferOffset >= (encodeBufSize - 2))
{
if (m_AddLineBreaks)
{
m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte)'\r';
m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte)'\n';
}
m_pStream.Write(m_pEncodeBuffer, 0, m_EncodeBufferOffset);
m_EncodeBufferOffset = 0;
}
m_OffsetInEncode3x8Block = 0;
}
}
}
#endregion
#region method Finish
/// <summary>
/// Completes encoding. Call this method if all data has written and no more data.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
public void Finish()
{
if (m_IsDisposed)
{
throw new ObjectDisposedException(this.GetType().Name);
}
if (m_IsFinished)
{
return;
}
m_IsFinished = true;
// PADD left-over, if any. Write encode buffer to underlaying stream.
if (m_OffsetInEncode3x8Block == 1)
{
m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte)BASE64_ENCODE_TABLE[m_pEncode3x8Block[0] >> 2];
m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte)BASE64_ENCODE_TABLE[(m_pEncode3x8Block[0] & 0x03) << 4];
m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte)'=';
m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte)'=';
}
else if (m_OffsetInEncode3x8Block == 2)
{
m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte)BASE64_ENCODE_TABLE[m_pEncode3x8Block[0] >> 2];
m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte)BASE64_ENCODE_TABLE[(m_pEncode3x8Block[0] & 0x03) << 4 | m_pEncode3x8Block[1] >> 4];
m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte)BASE64_ENCODE_TABLE[(m_pEncode3x8Block[1] & 0x0F) << 2];
m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte)'=';
}
if (m_EncodeBufferOffset > 0)
{
m_pStream.Write(m_pEncodeBuffer, 0, m_EncodeBufferOffset);
}
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets if this object is disposed.
/// </summary>
public bool IsDisposed
{
get { return m_IsDisposed; }
}
/// <summary>
/// Gets a value indicating whether the current stream supports reading.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
public override bool CanRead
{
get
{
if (m_IsDisposed)
{
throw new ObjectDisposedException("SmartStream");
}
return true;
}
}
/// <summary>
/// Gets a value indicating whether the current stream supports seeking.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
public override bool CanSeek
{
get
{
if (m_IsDisposed)
{
throw new ObjectDisposedException("SmartStream");
}
return false;
}
}
/// <summary>
/// Gets a value indicating whether the current stream supports writing.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
public override bool CanWrite
{
get
{
if (m_IsDisposed)
{
throw new ObjectDisposedException("SmartStream");
}
return false;
}
}
/// <summary>
/// Gets the length in bytes of the stream. This method is not supported and always throws a NotSupportedException.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
/// <exception cref="NotSupportedException">Is raised when this property is accessed.</exception>
public override long Length
{
get
{
if (m_IsDisposed)
{
throw new ObjectDisposedException("SmartStream");
}
throw new NotSupportedException();
}
}
/// <summary>
/// Gets or sets the position within the current stream. This method is not supported and always throws a NotSupportedException.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
/// <exception cref="NotSupportedException">Is raised when this property is accessed.</exception>
public override long Position
{
get
{
if (m_IsDisposed)
{
throw new ObjectDisposedException("SmartStream");
}
throw new NotSupportedException();
}
set
{
if (m_IsDisposed)
{
throw new ObjectDisposedException("SmartStream");
}
throw new NotSupportedException();
}
}
#endregion
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MailServer.Misc.IO
{
/// <summary>
/// The exception that is thrown when maximum allowed data size has exceeded.
/// </summary>
public class DataSizeExceededException : Exception
{
/// <summary>
/// Default constructor.
/// </summary>
public DataSizeExceededException()
: base()
{
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MailServer.Misc.IO
{
/// <summary>
/// The exception that is thrown when incomplete data received.
/// For example for ReadPeriodTerminated() method reaches end of stream before getting period terminator.
/// </summary>
public class IncompleteDataException : Exception
{
/// <summary>
/// Default constructor.
/// </summary>
public IncompleteDataException()
: base()
{
}
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="message">Exception message text.</param>
public IncompleteDataException(string message)
: base(message)
{
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace MailServer.Misc.IO
{
/// <summary>
/// The exception that is thrown when maximum allowed line size has exceeded.
/// </summary>
public class LineSizeExceededException : Exception
{
/// <summary>
/// Default coonstructor.
/// </summary>
public LineSizeExceededException()
: base()
{
}
}
}

View File

@ -0,0 +1,331 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
namespace MailServer.Misc.IO
{
/// <summary>
/// Implements RFC 2045 6.7. Quoted-Printable stream.
/// </summary>
public class QuotedPrintableStream : Stream
{
private SmartStream m_pStream = null;
private FileAccess m_AccessMode = FileAccess.ReadWrite;
private byte[] m_pDecodedBuffer = null;
private int m_DecodedOffset = 0;
private int m_DecodedCount = 0;
private byte[] m_pEncodedBuffer = null;
private int m_EncodedCount = 0;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="stream">Source stream.</param>
/// <param name="access">Specifies stream access mode.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null reference.</exception>
public QuotedPrintableStream(SmartStream stream, FileAccess access)
{
if (stream == null)
{
throw new ArgumentNullException("stream");
}
m_pStream = stream;
m_AccessMode = access;
m_pDecodedBuffer = new byte[32000];
m_pEncodedBuffer = new byte[78];
}
#region override method Flush
/// <summary>
/// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
public override void Flush()
{
if (m_EncodedCount > 0)
{
m_pStream.Write(m_pEncodedBuffer, 0, m_EncodedCount);
m_EncodedCount = 0;
}
}
#endregion
#region override method Seek
/// <summary>
/// Sets the position within the current stream. This method is not supported and always throws a NotSupportedException.
/// </summary>
/// <param name="offset">A byte offset relative to the <b>origin</b> parameter.</param>
/// <param name="origin">A value of type SeekOrigin indicating the reference point used to obtain the new position.</param>
/// <returns>The new position within the current stream.</returns>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
/// <exception cref="NotSupportedException">Is raised when this method is accessed.</exception>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
#endregion
#region override method SetLength
/// <summary>
/// Sets the length of the current stream. This method is not supported and always throws a NotSupportedException.
/// </summary>
/// <param name="value">The desired length of the current stream in bytes.</param>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
/// <exception cref="NotSupportedException">Is raised when this method is accessed.</exception>
public override void SetLength(long value)
{
throw new NotSupportedException();
}
#endregion
#region override method Read
/// <summary>
/// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
/// </summary>
/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
/// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.</returns>
/// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null reference.</exception>
/// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
/// <exception cref="NotSupportedException">Is raised when reading not supported.</exception>
public override int Read(byte[] buffer, int offset, int count)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentException("Invalid argument 'offset' value.");
}
if (offset + count > buffer.Length)
{
throw new ArgumentException("Invalid argument 'count' value.");
}
if ((m_AccessMode & FileAccess.Read) == 0)
{
throw new NotSupportedException();
}
while (true)
{
// Read next quoted-printable line and decode it.
if (m_DecodedOffset >= m_DecodedCount)
{
m_DecodedOffset = 0;
m_DecodedCount = 0;
SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[32000], SizeExceededAction.ThrowException);
m_pStream.ReadLine(readLineOP, false);
// IO error reading line.
if (readLineOP.Error != null)
{
throw readLineOP.Error;
}
// We reached end of stream.
else if (readLineOP.BytesInBuffer == 0)
{
return 0;
}
// Decode quoted-printable line.
else
{
// Process bytes.
bool softLineBreak = false;
int lineLength = readLineOP.LineBytesInBuffer;
for (int i = 0; i < readLineOP.LineBytesInBuffer; i++)
{
byte b = readLineOP.Buffer[i];
// We have soft line-break.
if (b == '=' && i == (lineLength - 1))
{
softLineBreak = true;
}
// We should have =XX char.
else if (b == '=')
{
byte b1 = readLineOP.Buffer[++i];
byte b2 = readLineOP.Buffer[++i];
m_pDecodedBuffer[m_DecodedCount++] = byte.Parse(new string(new char[] { (char)b1, (char)b2 }), System.Globalization.NumberStyles.HexNumber);
}
// Normal char.
else
{
m_pDecodedBuffer[m_DecodedCount++] = b;
}
}
// Add hard line break only if there was one in original data.
if (readLineOP.LineBytesInBuffer != readLineOP.BytesInBuffer && !softLineBreak)
{
m_pDecodedBuffer[m_DecodedCount++] = (byte)'\r';
m_pDecodedBuffer[m_DecodedCount++] = (byte)'\n';
}
}
}
// We some decoded data, return it.
if (m_DecodedOffset < m_DecodedCount)
{
int countToCopy = Math.Min(count, m_DecodedCount - m_DecodedOffset);
Array.Copy(m_pDecodedBuffer, m_DecodedOffset, buffer, offset, countToCopy);
m_DecodedOffset += countToCopy;
return countToCopy;
}
}
}
#endregion
#region override method Write
/// <summary>
/// Encodes a sequence of bytes, writes to the current stream and advances the current position within this stream by the number of bytes written.
/// </summary>
/// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
/// <param name="count">The number of bytes to be written to the current stream.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null reference.</exception>
/// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
/// <exception cref="NotSupportedException">Is raised when reading not supported.</exception>
public override void Write(byte[] buffer, int offset, int count)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentException("Invalid argument 'offset' value.");
}
if (offset + count > buffer.Length)
{
throw new ArgumentException("Invalid argument 'count' value.");
}
if ((m_AccessMode & FileAccess.Write) == 0)
{
throw new NotSupportedException();
}
// Process bytes.
for (int i = 0; i < count; i++)
{
byte b = buffer[offset + i];
// We don't need to encode byte.
if ((b >= 33 && b <= 60) || (b >= 62 && b <= 126))
{
// Maximum allowed quoted-printable line length reached, do soft line break.
if (m_EncodedCount >= 75)
{
m_pEncodedBuffer[m_EncodedCount++] = (byte)'=';
m_pEncodedBuffer[m_EncodedCount++] = (byte)'\r';
m_pEncodedBuffer[m_EncodedCount++] = (byte)'\n';
// Write encoded data to underlying stream.
Flush();
}
m_pEncodedBuffer[m_EncodedCount++] = b;
}
// We need to encode byte.
else
{
// Maximum allowed quote-printable line length reached, do soft line break.
if (m_EncodedCount >= 73)
{
m_pEncodedBuffer[m_EncodedCount++] = (byte)'=';
m_pEncodedBuffer[m_EncodedCount++] = (byte)'\r';
m_pEncodedBuffer[m_EncodedCount++] = (byte)'\n';
// Write encoded data to underlying stream.
Flush();
}
// Encode byte.
m_pEncodedBuffer[m_EncodedCount++] = (byte)'=';
m_pEncodedBuffer[m_EncodedCount++] = (byte)(b >> 4).ToString("x")[0];
m_pEncodedBuffer[m_EncodedCount++] = (byte)(b & 0xF).ToString("x")[0];
}
}
}
#endregion
#region Properties implementation
/// <summary>
/// Gets a value indicating whether the current stream supports reading.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
public override bool CanRead
{
get { return (m_AccessMode & FileAccess.Read) != 0; }
}
/// <summary>
/// Gets a value indicating whether the current stream supports seeking.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
public override bool CanSeek
{
get { return false; }
}
/// <summary>
/// Gets a value indicating whether the current stream supports writing.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
public override bool CanWrite
{
get { return (m_AccessMode & FileAccess.Write) != 0; }
}
/// <summary>
/// Gets the length in bytes of the stream. This method is not supported and always throws a NotSupportedException.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
/// <exception cref="NotSupportedException">Is raised when this property is accessed.</exception>
public override long Length
{
get
{
throw new NotSupportedException();
}
}
/// <summary>
/// Gets or sets the position within the current stream. This method is not supported and always throws a NotSupportedException.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
/// <exception cref="NotSupportedException">Is raised when this property is accessed.</exception>
public override long Position
{
get
{
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
}
#endregion
}
}

View File

@ -0,0 +1,198 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
namespace MailServer.Misc.IO
{
/// <summary>
/// This class implements read,write or read-write access stream.
/// </summary>
public class ReadWriteControlledStream : Stream
{
private Stream m_pStream = null;
private FileAccess m_AccessMode = FileAccess.ReadWrite;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="stream">Source stream.</param>
/// <param name="access">This stream access mode.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null reference.</exception>
public ReadWriteControlledStream(Stream stream, FileAccess access)
{
if (stream == null)
{
throw new ArgumentNullException("stream");
}
m_pStream = stream;
m_AccessMode = access;
}
#region override method Flush
/// <summary>
/// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
public override void Flush()
{
m_pStream.Flush();
}
#endregion
#region override method Seek
/// <summary>
/// Sets the position within the current stream. This method is not supported and always throws a NotSupportedException.
/// </summary>
/// <param name="offset">A byte offset relative to the <b>origin</b> parameter.</param>
/// <param name="origin">A value of type SeekOrigin indicating the reference point used to obtain the new position.</param>
/// <returns>The new position within the current stream.</returns>
public override long Seek(long offset, SeekOrigin origin)
{
return m_pStream.Seek(offset, origin);
}
#endregion
#region override method SetLength
/// <summary>
/// Sets the length of the current stream. This method is not supported and always throws a NotSupportedException.
/// </summary>
/// <param name="value">The desired length of the current stream in bytes.</param>
public override void SetLength(long value)
{
m_pStream.SetLength(value);
}
#endregion
#region override method Read
/// <summary>
/// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
/// </summary>
/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
/// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.</returns>
/// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null reference.</exception>
/// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
/// <exception cref="NotSupportedException">Is raised when reading not supported.</exception>
public override int Read(byte[] buffer, int offset, int count)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentException("Invalid argument 'offset' value.");
}
if (offset + count > buffer.Length)
{
throw new ArgumentException("Invalid argument 'count' value.");
}
if ((m_AccessMode & FileAccess.Read) == 0)
{
throw new NotSupportedException();
}
return m_pStream.Read(buffer, offset, count);
}
#endregion
#region override method Write
/// <summary>
/// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
/// </summary>
/// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
/// <param name="count">The number of bytes to be written to the current stream.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null reference.</exception>
/// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
/// <exception cref="NotSupportedException">Is raised when reading not supported.</exception>
public override void Write(byte[] buffer, int offset, int count)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentException("Invalid argument 'offset' value.");
}
if (offset + count > buffer.Length)
{
throw new ArgumentException("Invalid argument 'count' value.");
}
if ((m_AccessMode & FileAccess.Write) == 0)
{
throw new NotSupportedException();
}
m_pStream.Write(buffer, offset, count);
}
#endregion
#region Properties implementation
/// <summary>
/// Gets a value indicating whether the current stream supports reading.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
public override bool CanRead
{
get { return (m_AccessMode & FileAccess.Read) != 0; }
}
/// <summary>
/// Gets a value indicating whether the current stream supports seeking.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
public override bool CanSeek
{
get { return m_pStream.CanSeek; }
}
/// <summary>
/// Gets a value indicating whether the current stream supports writing.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
public override bool CanWrite
{
get { return (m_AccessMode & FileAccess.Write) != 0; }
}
/// <summary>
/// Gets the length in bytes of the stream. This method is not supported and always throws a NotSupportedException.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
public override long Length
{
get { return m_pStream.Length; }
}
/// <summary>
/// Gets or sets the position within the current stream. This method is not supported and always throws a NotSupportedException.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
public override long Position
{
get { return m_pStream.Position; }
set { m_pStream.Position = value; }
}
#endregion
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MailServer.Misc.IO
{
/// <summary>
/// Specifies action what is done if requested action exceeds maximum allowed size.
/// </summary>
public enum SizeExceededAction
{
/// <summary>
/// Throws exception at once when maximum size exceeded.
/// </summary>
ThrowException = 1,
/// <summary>
/// Junks all data what exceeds maximum allowed size and after requested operation completes,
/// throws exception.
/// </summary>
JunkAndThrowException = 2,
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class holds MIME content disposition types. Defined in RFC 2183.

View File

@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This enum specifies MIME RFC 2047 'encoded-word' encoding method.

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// Implements 'encoded-word' encoding. Defined in RFC 2047.

View File

@ -3,10 +3,9 @@ using System.IO;
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using LumiSoft.Net.MIME;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// Represents a MIME entity. Defined in RFC 2045 2.4.

View File

@ -3,7 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// Represents MIME child entity collection in multipart/xxx entity.

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class holds well known Content-Type header field media types. For example: text/plain, application/octet-stream.

View File

@ -3,9 +3,9 @@ using System.IO;
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// Represents a MIME message. Defined in RFC 2045 2.3.

View File

@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class holds MIME content transfer encodings. Defined in RFC 2045 6.

View File

@ -3,9 +3,9 @@ using System.IO;
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class is base class for MIME entity bodies.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME application/xxx bodies. Defined in RFC 2046 4.2.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME audio/xxx bodies. Defined in RFC 2046 4.3.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME image/xxx bodies. Defined in RFC 2046 4.2.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME message/xxx bodies. Defined in RFC 2046 5.2.

View File

@ -3,10 +3,10 @@ using System.IO;
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using LumiSoft.Net.Mail;
using MailServer.Misc.Mail;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME <b>message/delivery-status</b> body. Defined in RFC 3464.

View File

@ -3,10 +3,10 @@ using System.IO;
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using LumiSoft.Net.Mail;
using MailServer.Misc.Mail;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME message/rfc822 body. Defined in RFC 2046 5.2.1.

View File

@ -3,9 +3,9 @@ using System.IO;
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME application/xxx bodies. Defined in RFC 2046 5.1.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME multipart/alternative body. Defined in RFC 2046 5.1.4.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME multipart/digest body. Defined in RFC 2046 5.1.5.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME multipart/encrypted body. Defined in rfc 1847.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME multipart/from-data body. Defined in RFC 2046.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME multipart/mixed body. Defined in RFC 2046 5.1.3.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME message/parallel bodies. Defined in RFC 2046 5.1.6.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME multipart/related body. Defined in RFC 2387.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME multipart/report body. Defined in RFC 3462.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME multipart/signed body. Defined in rfc 1847.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represent MIME entity body provider.

View File

@ -3,9 +3,9 @@ using System.IO;
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class is base class for singlepart media bodies like: text,video,audio,image.

View File

@ -3,9 +3,9 @@ using System.IO;
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME text/xxx bodies. Defined in RFC 2045.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME unknown bodies.

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME video/xxx bodies. Defined in RFC 2046 4.4.

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This is base class for MIME header fields. Defined in RFC 2045 3.

View File

@ -4,9 +4,9 @@ using System.Collections;
using System.Collections.Generic;
using System.Text;
using LumiSoft.Net.IO;
using MailServer.Misc.IO;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME header fields collection. Defined in RFC 2045.

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// Represents "Content-Disposition:" header. Defined in RFC 2183.

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// Represents "Content-Type:" header. Defined in RFC 2045 5.1.

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// Represents MIME header field parameter.

View File

@ -3,7 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// Represents MIME header field parameters collection.

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents MIME headers provider.

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represent header field what parsing has failed.

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace LumiSoft.Net.MIME
namespace MailServer.Misc.MIME
{
/// <summary>
/// This class represents normal unstructured text header field.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Text;
using MailServer.Misc.MIME;
namespace MailServer.Misc.Mail
{
/// <summary>
/// This class provides mail message related utility methods.
/// </summary>
public class Mail_Utils
{
#region static method SMTP_Mailbox
/// <summary>
/// Reads SMTP "Mailbox" from the specified MIME reader.
/// </summary>
/// <param name="reader">MIME reader.</param>
/// <returns>Returns SMTP "Mailbox" or null if no SMTP mailbox available.</returns>
/// <exception cref="ArgumentNullException">Is raised when <b>reader</b> is null reference.</exception>
internal static string SMTP_Mailbox(MIME_Reader reader)
{
if(reader == null){
throw new ArgumentNullException("reader");
}
// TODO:
/* RFC 5321.
Mailbox = Local-part "@" ( Domain / address-literal )
Local-part = Dot-string / Quoted-string ; MAY be case-sensitive
Dot-string = Atom *("." Atom)
*/
StringBuilder retVal = new StringBuilder();
if(reader.Peek(true) == '\"'){
retVal.Append("\"" + reader.QuotedString() + "\"");
}
else{
retVal.Append(reader.DotAtom());
}
if(reader.Peek(true) != '@'){
return null;
}
else{
// Eat "@".
reader.Char(true);
retVal.Append('@');
retVal.Append(reader.DotAtom());
}
return retVal.ToString();
}
#endregion
}
}

View File

@ -0,0 +1,229 @@
using System;
using System.Collections.Generic;
using System.Text;
using MailServer.Misc.MIME;
namespace MailServer.Misc.Mail
{
/// <summary>
/// This class represent generic <b>address-list</b> header fields. For example: To header.
/// </summary>
/// <example>
/// <code>
/// RFC 5322.
/// header = "FiledName:" address-list CRLF
/// address-list = (address *("," address))
/// address = mailbox / group
/// </code>
/// </example>
public class Mail_h_AddressList : MIME_h
{
private string m_ParseValue = null;
private string m_Name = null;
private Mail_t_AddressList m_pAddresses = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="fieldName">Header field name. For example: "To".</param>
/// <param name="values">Addresses collection.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>filedName</b> or <b>values</b> is null reference.</exception>
/// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
public Mail_h_AddressList(string fieldName,Mail_t_AddressList values)
{
if(fieldName == null){
throw new ArgumentNullException("fieldName");
}
if(fieldName == string.Empty){
throw new ArgumentException("Argument 'fieldName' value must be specified.");
}
if(values == null){
throw new ArgumentNullException("values");
}
m_Name = fieldName;
m_pAddresses = values;
}
#region static method Parse
/// <summary>
/// Parses header field from the specified value.
/// </summary>
/// <param name="value">Header field value. Header field name must be included. For example: 'Content-Type: text/plain'.</param>
/// <returns>Returns parsed header field.</returns>
/// <exception cref="ArgumentNullException">Is raised when <b>value</b> is null reference.</exception>
/// <exception cref="ParseException">Is raised when header field parsing errors.</exception>
public static Mail_h_AddressList Parse(string value)
{
if(value == null){
throw new ArgumentNullException("value");
}
string[] name_value = value.Split(new char[]{':'},2);
if(name_value.Length != 2){
throw new ParseException("Invalid header field value '" + value + "'.");
}
MIME_Reader r = new MIME_Reader(name_value[1].Trim());
/* RFC 5322 3.4.
address = mailbox / group
mailbox = name-addr / addr-spec
name-addr = [display-name] angle-addr
angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr
group = display-name ":" [group-list] ";" [CFWS]
display-name = phrase
mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list
address-list = (address *("," address)) / obs-addr-list
group-list = mailbox-list / CFWS / obs-group-list
*/
Mail_h_AddressList retVal = new Mail_h_AddressList(name_value[0],new Mail_t_AddressList());
while(true){
string word = r.QuotedReadToDelimiter(new char[]{',','<',':'});
// We processed all data.
if(word == null && r.Available == 0){
break;
}
// group
else if(r.Peek(true) == ':'){
Mail_t_Group group = new Mail_t_Group(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word)) : null);
// Consume ':'
r.Char(true);
while(true){
word = r.QuotedReadToDelimiter(new char[]{',','<',':',';'});
// We processed all data.
if((word == null && r.Available == 0) || r.Peek(false) == ';'){
break;
}
// In valid address list value.
else if(word == string.Empty){
throw new ParseException("Invalid address-list value '" + value + "'.");
}
// name-addr
else if(r.Peek(true) == '<'){
group.Members.Add(new Mail_t_Mailbox(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word)) : null,r.ReadParenthesized()));
}
// addr-spec
else{
group.Members.Add(new Mail_t_Mailbox(null,word));
}
// We reached at the end of group.
if(r.Peek(true) == ';'){
r.Char(true);
break;
}
// We have more addresses.
if(r.Peek(true) == ','){
r.Char(false);
}
}
retVal.m_pAddresses.Add(group);
}
// name-addr
else if(r.Peek(true) == '<'){
retVal.m_pAddresses.Add(new Mail_t_Mailbox(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word.Trim())) : null,r.ReadParenthesized()));
}
// addr-spec
else{
retVal.m_pAddresses.Add(new Mail_t_Mailbox(null,word));
}
// We have more addresses.
if(r.Peek(true) == ','){
r.Char(false);
}
}
retVal.m_ParseValue = value;
retVal.m_pAddresses.AcceptChanges();
return retVal;
}
#endregion
#region override method ToString
/// <summary>
/// Returns header field as string.
/// </summary>
/// <param name="wordEncoder">8-bit words ecnoder. Value null means that words are not encoded.</param>
/// <param name="parmetersCharset">Charset to use to encode 8-bit characters. Value null means parameters not encoded.</param>
/// <returns>Returns header field as string.</returns>
public override string ToString(MIME_Encoding_EncodedWord wordEncoder,Encoding parmetersCharset)
{
if(this.IsModified){
StringBuilder retVal = new StringBuilder();
retVal.Append(this.Name + ": ");
for(int i=0;i<m_pAddresses.Count;i++){
if(i > 0){
retVal.Append("\t");
}
// Don't add ',' for last item.
if(i == (m_pAddresses.Count - 1)){
retVal.Append(m_pAddresses[i].ToString(wordEncoder) + "\r\n");
}
else{
retVal.Append(m_pAddresses[i].ToString(wordEncoder) + ",\r\n");
}
}
return retVal.ToString();
}
else{
return m_ParseValue;
}
}
#endregion
#region Properties implementation
/// <summary>
/// Gets if this header field is modified since it has loaded.
/// </summary>
/// <remarks>All new added header fields has <b>IsModified = true</b>.</remarks>
/// <exception cref="ObjectDisposedException">Is riased when this class is disposed and this property is accessed.</exception>
public override bool IsModified
{
get{ return m_pAddresses.IsModified; }
}
/// <summary>
/// Gets header field name. For example "To".
/// </summary>
public override string Name
{
get{ return m_Name; }
}
/// <summary>
/// Gets addresses collection.
/// </summary>
public Mail_t_AddressList Addresses
{
get{ return m_pAddresses; }
}
#endregion
}
}

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Text;
using MailServer.Misc.MIME;
namespace MailServer.Misc.Mail
{
/// <summary>
/// Represents "Disposition-Notification-Options:" header. Defined in RFC 2298 2.2.
/// </summary>
public class Mail_h_DispositionNotificationOptions : MIME_h
{
/// <summary>
/// Default constructor.
/// </summary>
public Mail_h_DispositionNotificationOptions()
{
}
/*
Disposition-Notification-Options = "Disposition-Notification-Options" ":" disposition-notification-parameters
disposition-notification-parameters = parameter *(";" parameter)
parameter = attribute "=" importance "," 1#value
importance = "required" / "optional"
*/
#region override method ToString
/// <summary>
/// Returns header field as string.
/// </summary>
/// <param name="wordEncoder">8-bit words ecnoder. Value null means that words are not encoded.</param>
/// <param name="parmetersCharset">Charset to use to encode 8-bit characters. Value null means parameters not encoded.</param>
/// <returns>Returns header field as string.</returns>
public override string ToString(MIME_Encoding_EncodedWord wordEncoder,Encoding parmetersCharset)
{
return "TODO:";
}
#endregion
#region Properties implementation
/// <summary>
/// Gets if this header field is modified since it has loaded.
/// </summary>
/// <remarks>All new added header fields has <b>IsModified = true</b>.</remarks>
/// <exception cref="ObjectDisposedException">Is riased when this class is disposed and this property is accessed.</exception>
public override bool IsModified
{
get{ return true; } //m_pAddresses.IsModified; }
}
/// <summary>
/// Gets header field name. For example "Sender".
/// </summary>
public override string Name
{
get{ return "Disposition-Notification-Options"; }
}
/// <summary>
/// Gets or sets mailbox address.
/// </summary>
public string Address
{
get{ return "TODO:"; }
}
#endregion
}
}

View File

@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.Text;
using MailServer.Misc.MIME;
namespace MailServer.Misc.Mail
{
/// <summary>
/// This class represent generic <b>mailbox</b> header fields. For example: Sender: header.
/// </summary>
/// <example>
/// <code>
/// RFC 5322.
/// header = "FiledName:" mailbox CRLF
/// </code>
/// </example>
public class Mail_h_Mailbox : MIME_h
{
private string m_ParseValue = null;
private string m_Name = null;
private Mail_t_Mailbox m_pAddress = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="fieldName">Header field name. For example: "Sender".</param>
/// <param name="mailbox">Mailbox value.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>filedName</b> or <b>mailbox</b> is null reference.</exception>
/// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
public Mail_h_Mailbox(string fieldName,Mail_t_Mailbox mailbox)
{
if(fieldName == null){
throw new ArgumentNullException("fieldName");
}
if(fieldName == string.Empty){
throw new ArgumentException("Argument 'fieldName' value must be specified.");
}
if(mailbox == null){
throw new ArgumentNullException("mailbox");
}
m_Name = fieldName;
m_pAddress = mailbox;
}
#region static method Parse
/// <summary>
/// Parses header field from the specified value.
/// </summary>
/// <param name="value">Header field value. Header field name must be included. For example: 'Sender: john.doe@domain.com'.</param>
/// <returns>Returns parsed header field.</returns>
/// <exception cref="ArgumentNullException">Is raised when <b>value</b> is null reference.</exception>
/// <exception cref="ParseException">Is raised when header field parsing errors.</exception>
public static Mail_h_Mailbox Parse(string value)
{
if(value == null){
throw new ArgumentNullException("value");
}
string[] name_value = value.Split(new char[]{':'},2);
if(name_value.Length != 2){
throw new ParseException("Invalid header field value '" + value + "'.");
}
MIME_Reader r = new MIME_Reader(name_value[1].Trim());
string word = r.QuotedReadToDelimiter(new char[]{',','<',':'});
// Invalid value.
if(word == null){
throw new ParseException("Invalid header field value '" + value + "'.");
}
// name-addr
else if(r.Peek(true) == '<'){
Mail_h_Mailbox h = new Mail_h_Mailbox(name_value[0],new Mail_t_Mailbox(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word)) : null,r.ReadParenthesized()));
h.m_ParseValue = value;
return h;
}
// addr-spec
else{
Mail_h_Mailbox h = new Mail_h_Mailbox(name_value[0],new Mail_t_Mailbox(null,word));
h.m_ParseValue = value;
return h;
}
}
#endregion
#region override method ToString
/// <summary>
/// Returns header field as string.
/// </summary>
/// <param name="wordEncoder">8-bit words ecnoder. Value null means that words are not encoded.</param>
/// <param name="parmetersCharset">Charset to use to encode 8-bit characters. Value null means parameters not encoded.</param>
/// <returns>Returns header field as string.</returns>
public override string ToString(MIME_Encoding_EncodedWord wordEncoder,Encoding parmetersCharset)
{
if(m_ParseValue != null){
return m_ParseValue;
}
else{
return m_Name + ": " + m_pAddress.ToString(wordEncoder) + "\r\n";
}
}
#endregion
#region Properties implementation
/// <summary>
/// Gets if this header field is modified since it has loaded.
/// </summary>
/// <remarks>All new added header fields has <b>IsModified = true</b>.</remarks>
/// <exception cref="ObjectDisposedException">Is riased when this class is disposed and this property is accessed.</exception>
public override bool IsModified
{
get{ return false; }
}
/// <summary>
/// Gets header field name. For example "Sender".
/// </summary>
public override string Name
{
get{ return m_Name; }
}
/// <summary>
/// Gets mailbox address.
/// </summary>
public Mail_t_Mailbox Address
{
get{ return m_pAddress; }
}
#endregion
}
}

View File

@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.Text;
using MailServer.Misc.MIME;
namespace MailServer.Misc.Mail
{
/// <summary>
/// This class represent generic <b>mailbox-list</b> header fields. For example: From header.
/// </summary>
/// <example>
/// <code>
/// RFC 5322.
/// header = "FiledName:" mailbox-list CRLF
/// mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list
/// </code>
/// </example>
public class Mail_h_MailboxList : MIME_h
{
private string m_ParseValue = null;
private string m_Name = null;
private Mail_t_MailboxList m_pAddresses = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="filedName">Header field name. For example: "To".</param>
/// <param name="values">Addresses collection.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>filedName</b> or <b>values</b> is null reference.</exception>
/// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
public Mail_h_MailboxList(string filedName,Mail_t_MailboxList values)
{
if(filedName == null){
throw new ArgumentNullException("filedName");
}
if(filedName == string.Empty){
throw new ArgumentException("Argument 'filedName' value must be specified.");
}
if(values == null){
throw new ArgumentNullException("values");
}
m_Name = filedName;
m_pAddresses = values;
}
#region static method Parse
/// <summary>
/// Parses header field from the specified value.
/// </summary>
/// <param name="value">Header field value. Header field name must be included. For example: 'Content-Type: text/plain'.</param>
/// <returns>Returns parsed header field.</returns>
/// <exception cref="ArgumentNullException">Is raised when <b>value</b> is null reference.</exception>
/// <exception cref="ParseException">Is raised when header field parsing errors.</exception>
public static Mail_h_MailboxList Parse(string value)
{
if(value == null){
throw new ArgumentNullException("value");
}
string[] name_value = value.Split(new char[]{':'},2);
if(name_value.Length != 2){
throw new ParseException("Invalid header field value '" + value + "'.");
}
/* RFC 5322 3.4.
mailbox = name-addr / addr-spec
name-addr = [display-name] angle-addr
angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr
display-name = phrase
mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list
*/
MIME_Reader r = new MIME_Reader(name_value[1].Trim());
Mail_h_MailboxList retVal = new Mail_h_MailboxList(name_value[0],new Mail_t_MailboxList());
while(true){
string word = r.QuotedReadToDelimiter(new char[]{',','<',':'});
// We processed all data.
if(word == null && r.Available == 0){
break;
}
// name-addr
else if(r.Peek(true) == '<'){//Console.WriteLine("aaa:'" + r.ReadParenthesized() + "'");
retVal.m_pAddresses.Add(new Mail_t_Mailbox(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word.Trim())) : null,r.ReadParenthesized()));
}
// addr-spec
else{
retVal.m_pAddresses.Add(new Mail_t_Mailbox(null,word));
}
// We have more addresses.
if(r.Peek(true) == ','){
r.Char(false);
}
}
retVal.m_ParseValue = value;
retVal.m_pAddresses.AcceptChanges();
return retVal;
}
#endregion
#region override method ToString
/// <summary>
/// Returns header field as string.
/// </summary>
/// <param name="wordEncoder">8-bit words ecnoder. Value null means that words are not encoded.</param>
/// <param name="parmetersCharset">Charset to use to encode 8-bit characters. Value null means parameters not encoded.</param>
/// <returns>Returns header field as string.</returns>
public override string ToString(MIME_Encoding_EncodedWord wordEncoder,Encoding parmetersCharset)
{
if(this.IsModified){
StringBuilder retVal = new StringBuilder();
retVal.Append(this.Name + ": ");
for(int i=0;i<m_pAddresses.Count;i++){
if(i > 0){
retVal.Append("\t");
}
// Don't add ',' for last item.
if(i == (m_pAddresses.Count - 1)){
retVal.Append(m_pAddresses[i].ToString(wordEncoder) + "\r\n");
}
else{
retVal.Append(m_pAddresses[i].ToString(wordEncoder) + ",\r\n");
}
}
return retVal.ToString();
}
else{
return m_ParseValue;
}
}
#endregion
#region Properties implementation
/// <summary>
/// Gets if this header field is modified since it has loaded.
/// </summary>
/// <remarks>All new added header fields has <b>IsModified = true</b>.</remarks>
/// <exception cref="ObjectDisposedException">Is riased when this class is disposed and this property is accessed.</exception>
public override bool IsModified
{
get{ return m_pAddresses.IsModified; }
}
/// <summary>
/// Gets header field name. For example "From".
/// </summary>
public override string Name
{
get{ return m_Name; }
}
/// <summary>
/// Gets addresses collection.
/// </summary>
public Mail_t_MailboxList Addresses
{
get{ return m_pAddresses; }
}
#endregion
}
}

View File

@ -0,0 +1,464 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using MailServer.Misc.MIME;
namespace MailServer.Misc.Mail
{
/// <summary>
/// Represents "Received:" header. Defined in RFC 5321 4.4.
/// </summary>
/// <remarks>
/// <code>
/// RFC 5321 4.4.
/// Time-stamp-line = "Received:" FWS Stamp CRLF
///
/// Stamp = From-domain By-domain Opt-info [CFWS] ";" FWS date-time
/// ; where "date-time" is as defined in RFC 5322 [4]
/// ; but the "obs-" forms, especially two-digit
/// ; years, are prohibited in SMTP and MUST NOT be used.
///
/// From-domain = "FROM" FWS Extended-Domain
///
/// By-domain = CFWS "BY" FWS Extended-Domain
///
/// Extended-Domain = Domain / ( Domain FWS "(" TCP-info ")" ) / ( address-literal FWS "(" TCP-info ")" )
///
/// TCP-info = address-literal / ( Domain FWS address-literal )
/// ; Information derived by server from TCP connection not client EHLO.
///
/// Opt-info = [Via] [With] [ID] [For] [Additional-Registered-Clauses]
///
/// Via = CFWS "VIA" FWS Link
///
/// With = CFWS "WITH" FWS Protocol
///
/// ID = CFWS "ID" FWS ( Atom / msg-id )
/// ; msg-id is defined in RFC 5322 [4]
///
/// For = CFWS "FOR" FWS ( Path / Mailbox )
///
/// Additional-Registered-Clauses = CFWS Atom FWS String
///
/// Link = "TCP" / Addtl-Link
///
/// Addtl-Link = Atom
///
/// Protocol = "ESMTP" / "SMTP" / Attdl-Protocol
///
/// Mailbox = Local-part "@" ( Domain / address-literal )
/// </code>
/// </remarks>
public class Mail_h_Received : MIME_h
{
private bool m_IsModified = false;
private string m_ParseValue = null;
private string m_From = "";
private Mail_t_TcpInfo m_pFrom_TcpInfo = null;
private string m_By = "";
private Mail_t_TcpInfo m_pBy_TcpInfo = null;
private string m_Via = null;
private string m_With = null;
private string m_ID = null;
private string m_For = null;
private DateTime m_Time;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="from">Host from where message was received.</param>
/// <param name="by">Host name what received message.</param>
/// <param name="time">Date time when message was received.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>from</b> or <b>by</b> is null reference.</exception>
/// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
public Mail_h_Received(string from,string by,DateTime time)
{
if(from == null){
throw new ArgumentNullException("from");
}
if(from == string.Empty){
throw new ArgumentException("Argument 'from' value must be specified.","from");
}
if(by == null){
throw new ArgumentNullException("by");
}
if(by == string.Empty){
throw new ArgumentException("Argument 'by' value must be specified.","by");
}
m_From = from;
m_By = by;
m_Time = time;
}
#region static method Parse
/// <summary>
/// Parses header field from the specified value.
/// </summary>
/// <param name="value">Header field value. Header field name must be included. For example: 'Sender: john.doe@domain.com'.</param>
/// <returns>Returns parsed header field.</returns>
/// <exception cref="ArgumentNullException">Is raised when <b>value</b> is null reference.</exception>
/// <exception cref="ParseException">Is raised when header field parsing errors.</exception>
public static Mail_h_Received Parse(string value)
{
if(value == null){
throw new ArgumentNullException("value");
}
string[] name_value = value.Split(new char[]{':'},2);
if(name_value.Length != 2){
throw new ParseException("Invalid header field value '" + value + "'.");
}
Mail_h_Received retVal = new Mail_h_Received("a","b",DateTime.MinValue);
MIME_Reader r = new MIME_Reader(name_value[1]);
while(true){
string word = r.Word();
// We processed all data.
if(word == null && r.Available == 0){
break;
}
// We have comment, just eat it.
else if(r.StartsWith("(")){
r.ReadParenthesized();
}
// We have date-time.
else if(r.StartsWith(";")){
// Eat ';'
r.Char(false);
retVal.m_Time = MIME_Utils.ParseRfc2822DateTime(r.ToEnd());
}
else{
// We have some unexpected char like: .,= ... . Just eat it.
if(word == null){
r.Char(true);
continue;
}
word = word.ToUpperInvariant();
if(word == "FROM"){
retVal.m_From = r.DotAtom();
r.ToFirstChar();
if(r.StartsWith("(")){
string[] parts = r.ReadParenthesized().Split(' ');
if(parts.Length == 1){
if(Net_Utils.IsIPAddress(parts[0])){
retVal.m_pFrom_TcpInfo = new Mail_t_TcpInfo(IPAddress.Parse(parts[0]),null);
}
}
else if(parts.Length == 2){
if(Net_Utils.IsIPAddress(parts[1])){
retVal.m_pFrom_TcpInfo = new Mail_t_TcpInfo(IPAddress.Parse(parts[1]),parts[0]);
}
}
}
}
else if(word == "BY"){
retVal.m_By = r.DotAtom();
r.ToFirstChar();
if(r.StartsWith("(")){
string[] parts = r.ReadParenthesized().Split(' ');
if(parts.Length == 1){
if(Net_Utils.IsIPAddress(parts[0])){
retVal.m_pBy_TcpInfo = new Mail_t_TcpInfo(IPAddress.Parse(parts[0]),null);
}
}
else if(parts.Length == 2){
if(Net_Utils.IsIPAddress(parts[1])){
retVal.m_pBy_TcpInfo = new Mail_t_TcpInfo(IPAddress.Parse(parts[1]),parts[0]);
}
}
}
}
else if(word == "VIA"){
retVal.m_Via = r.Word();
}
else if(word == "WITH"){
retVal.m_With = r.Word();
}
else if(word == "ID"){
// msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS]
if(r.StartsWith("<")){
retVal.m_ID = r.ReadParenthesized();
}
else{
retVal.m_ID = r.Atom();
}
}
else if(word == "FOR"){
r.ToFirstChar();
// path / angle-address
if(r.StartsWith("<")){
retVal.m_For = r.ReadParenthesized();
}
else{
string mailbox = Mail_Utils.SMTP_Mailbox(r);
if(mailbox == null){
throw new ParseException("Invalid Received: For parameter value '" + r.ToEnd() + "'.");
}
retVal.m_For = mailbox;
}
}
// Unknown, just eat value.
else{
r.Word();
}
}
}
retVal.m_ParseValue = value;
return retVal;
}
#endregion
#region override method ToString
/// <summary>
/// Returns header field as string.
/// </summary>
/// <param name="wordEncoder">8-bit words ecnoder. Value null means that words are not encoded.</param>
/// <param name="parmetersCharset">Charset to use to encode 8-bit characters. Value null means parameters not encoded.</param>
/// <returns>Returns header field as string.</returns>
public override string ToString(MIME_Encoding_EncodedWord wordEncoder,Encoding parmetersCharset)
{
if(this.IsModified){
StringBuilder retVal = new StringBuilder();
retVal.Append("Received: ");
retVal.Append("from " + m_From);
if(m_pFrom_TcpInfo != null){
retVal.Append(" (" + m_pFrom_TcpInfo.ToString() + ")");
}
retVal.Append(" by " + m_By);
if(m_pBy_TcpInfo != null){
retVal.Append(" (" + m_pBy_TcpInfo.ToString() + ")");
}
if(!string.IsNullOrEmpty(m_Via)){
retVal.Append(" via " + m_Via);
}
if(!string.IsNullOrEmpty(m_With)){
retVal.Append(" with " + m_With);
}
if(!string.IsNullOrEmpty(m_ID)){
retVal.Append(" id " + m_ID);
}
if(!string.IsNullOrEmpty(m_For)){
retVal.Append(" for " + m_For);
}
retVal.Append("; " + MIME_Utils.DateTimeToRfc2822(m_Time));
retVal.Append("\r\n");
/* Some servers doesnt like uppercase.
retVal.Append("Received: ");
retVal.Append("FROM " + m_From);
if(m_pFrom_TcpInfo != null){
retVal.Append(" (" + m_pFrom_TcpInfo.ToString() + ")");
}
retVal.Append(" BY " + m_By);
if(m_pBy_TcpInfo != null){
retVal.Append(" (" + m_pBy_TcpInfo.ToString() + ")");
}
if(!string.IsNullOrEmpty(m_Via)){
retVal.Append(" VIA " + m_Via);
}
if(!string.IsNullOrEmpty(m_With)){
retVal.Append(" WITH " + m_With);
}
if(!string.IsNullOrEmpty(m_ID)){
retVal.Append(" ID " + m_ID);
}
if(!string.IsNullOrEmpty(m_For)){
retVal.Append(" FOR " + m_For);
}
retVal.Append("; " + MIME_Utils.DateTimeToRfc2822(m_Time));
retVal.Append("\r\n");*/
return retVal.ToString();
}
else{
return m_ParseValue;
}
}
#endregion
#region Properties implementation
/// <summary>
/// Gets if this header field is modified since it has loaded.
/// </summary>
/// <remarks>All new added header fields has <b>IsModified = true</b>.</remarks>
/// <exception cref="ObjectDisposedException">Is riased when this class is disposed and this property is accessed.</exception>
public override bool IsModified
{
get{ return m_IsModified; }
}
/// <summary>
/// Returns always "Received".
/// </summary>
public override string Name
{
get { return "Received"; }
}
/// <summary>
/// Gets or sets host from where message was received.
/// </summary>
/// <remarks>Normally this is just EHLO/HELO host name what client reported to SMTP server.</remarks>
/// <exception cref="ArgumentNullException">Is raised when null reference passed.</exception>
/// <exception cref="ArgumentException">Is raised when invalid value passed.</exception>
public string From
{
get{ return m_From; }
set{
if(value == null){
throw new ArgumentNullException("From");
}
if(value == string.Empty){
throw new ArgumentException("Property 'From' value must be specified","From");
}
m_From = value;
m_IsModified = true;
}
}
/// <summary>
/// Gets or sets From TCP-Info value. Value null means not specified.
/// </summary>
/// <remarks>This value is message sender host IP and optional dns host name.
/// This value is based on server connection info, not client reported info(EHLO/HELO).
/// </remarks>
public Mail_t_TcpInfo From_TcpInfo
{
get{ return m_pFrom_TcpInfo; }
set{
m_pFrom_TcpInfo = value;
m_IsModified = true;
}
}
/// <summary>
/// Gets or sets host name what received message.
/// </summary>
/// <exception cref="ArgumentNullException">Is raised when null reference passed.</exception>
/// <exception cref="ArgumentException">Is raised when invalid value passed.</exception>
public string By
{
get{ return m_By; }
set{
if(value == null){
throw new ArgumentNullException("By");
}
if(value == string.Empty){
throw new ArgumentException("Property 'By' value must be specified","By");
}
m_By = value;
m_IsModified = true;
}
}
/// <summary>
/// Gets or sets By TCP-Info value. Value null means not specified.
/// </summary>
/// <remarks>RFC defines it, but i don't see any point about that value.</remarks>
public Mail_t_TcpInfo By_TcpInfo
{
get{ return m_pBy_TcpInfo; }
set{
m_pBy_TcpInfo = value;
m_IsModified = true;
}
}
/// <summary>
/// Gets or sets non-internet transport. Value null means not specified.
/// </summary>
public string Via
{
get{ return m_Via; }
set{
m_Via = value;
m_IsModified = true;
}
}
/// <summary>
/// Gets or sets receiving protocol. Value null means not specified.
/// </summary>
public string With
{
get{ return m_With; }
set{
m_With = value;
m_IsModified = true;
}
}
/// <summary>
/// Gets or sets ID value. Value null means not specified.
/// </summary>
public string ID
{
get{ return m_ID; }
set{
m_ID = value;
m_IsModified = true;
}
}
/// <summary>
/// Gets or sets mailbox for who message was received. Value null means not specified.
/// </summary>
public string For
{
get{ return m_For; }
set{
m_For = value;
m_IsModified = true;
}
}
/// <summary>
/// Gets or sets time when message was received.
/// </summary>
public DateTime Time
{
get{ return m_Time; }
set{
m_Time = value;
m_IsModified = true;
}
}
#endregion
}
}

Some files were not shown because too many files have changed in this diff Show More