Click here to Skip to main content
15,878,809 members
Articles / Programming Languages / C#

STUN Client

Rate me:
Please Sign up or sign in to vote.
4.83/5 (36 votes)
20 Apr 2007CPOL 321.8K   14.8K   85  
STUN client C# implementation with sample application
using System;
using System.Collections;
using System.Text;

using LumiSoft.Net.Mime;

namespace LumiSoft.Net.IMAP
{
	/// <summary>
	/// IMAP ENVELOPE STRUCTURE (date, subject, from, sender, reply-to, to, cc, bcc, in-reply-to, and message-id).
	///  Defined in RFC 3501 7.4.2.
	/// </summary>
	public class IMAP_Envelope
	{
		private DateTime         m_Date      = DateTime.MinValue;
		private string           m_Subject   = null;
		private MailboxAddress[] m_From      = null;
		private MailboxAddress   m_Sender    = null;
		private MailboxAddress[] m_ReplyTo   = null;
		private MailboxAddress[] m_To        = null;
		private MailboxAddress[] m_Cc        = null;
		private MailboxAddress[] m_Bcc       = null;
		private string           m_InReplyTo = null;
		private string           m_MessageID = null;

		/// <summary>
		/// Default constructor.
		/// </summary>
		public IMAP_Envelope()
		{
		}


		#region static method ConstructEnvelope

		/// <summary>
		/// Construct secified mime entity ENVELOPE string.
		/// </summary>
		/// <param name="entity">Mime entity.</param>
		/// <returns></returns>
		public static string ConstructEnvelope(MimeEntity entity)
		{
			/* RFC 3501 7.4.2
				ENVELOPE
					A parenthesized list that describes the envelope structure of a
					message.  This is computed by the server by parsing the
					[RFC-2822] header into the component parts, defaulting various
					fields as necessary.

					The fields of the envelope structure are in the following
					order: date, subject, from, sender, reply-to, to, cc, bcc,
					in-reply-to, and message-id.  The date, subject, in-reply-to,
					and message-id fields are strings.  The from, sender, reply-to,
					to, cc, and bcc fields are parenthesized lists of address
					structures.

					An address structure is a parenthesized list that describes an
					electronic mail address.  The fields of an address structure
					are in the following order: personal name, [SMTP]
					at-domain-list (source route), mailbox name, and host name.

					[RFC-2822] group syntax is indicated by a special form of
					address structure in which the host name field is NIL.  If the
					mailbox name field is also NIL, this is an end of group marker
					(semi-colon in RFC 822 syntax).  If the mailbox name field is
					non-NIL, this is a start of group marker, and the mailbox name
					field holds the group name phrase.

					If the Date, Subject, In-Reply-To, and Message-ID header lines
					are absent in the [RFC-2822] header, the corresponding member
					of the envelope is NIL; if these header lines are present but
					empty the corresponding member of the envelope is the empty
					string.
					
						Note: some servers may return a NIL envelope member in the
						"present but empty" case.  Clients SHOULD treat NIL and
						empty string as identical.

						Note: [RFC-2822] requires that all messages have a valid
						Date header.  Therefore, the date member in the envelope can
						not be NIL or the empty string.

						Note: [RFC-2822] requires that the In-Reply-To and
						Message-ID headers, if present, have non-empty content.
						Therefore, the in-reply-to and message-id members in the
						envelope can not be the empty string.

					If the From, To, cc, and bcc header lines are absent in the
					[RFC-2822] header, or are present but empty, the corresponding
					member of the envelope is NIL.

					If the Sender or Reply-To lines are absent in the [RFC-2822]
					header, or are present but empty, the server sets the
					corresponding member of the envelope to be the same value as
					the from member (the client is not expected to know to do
					this).

						Note: [RFC-2822] requires that all messages have a valid
						From header.  Therefore, the from, sender, and reply-to
						members in the envelope can not be NIL.
		 
					ENVELOPE ("date" "subject" from sender reply-to to cc bcc "in-reply-to" "messageID")
			*/

			// NOTE: all header fields and parameters must in ENCODED form !!!

			StringBuilder retVal = new StringBuilder();
			retVal.Append("(");

			// date
			if(entity.Header.Contains("Date:")){
				retVal.Append(TextUtils.QuoteString(MimeUtils.DateTimeToRfc2822(entity.Date)));
			}
			else{
				retVal.Append("NIL");
			}

			// subject
			if(entity.Subject != null){
				retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(entity.Subject)));
			}
			else{
				retVal.Append(" NIL");
			}

			// from
			if(entity.From != null && entity.From.Count > 0){
				retVal.Append(" " + ConstructAddresses(entity.From));
			}
			else{
				retVal.Append(" NIL");
			}

			// sender	
			//	NOTE: There is confusing part, according rfc 2822 Sender: is MailboxAddress and not AddressList.
			if(entity.Sender != null){
				retVal.Append(" (");

				retVal.Append(ConstructAddress(entity.Sender));

				retVal.Append(")");
			}
			else if(entity.From != null){
				retVal.Append(" " + ConstructAddresses(entity.From));
			}
			else{
				retVal.Append(" NIL");
			}

			// reply-to
			if(entity.ReplyTo != null){
				retVal.Append(" " + ConstructAddresses(entity.ReplyTo));
			}
			else if(entity.From != null){
				retVal.Append(" " + ConstructAddresses(entity.From));
			}
			else{
				retVal.Append(" NIL");
			}

			// to
			if(entity.To != null && entity.To.Count > 0){
				retVal.Append(" " + ConstructAddresses(entity.To));
			}
			else{
				retVal.Append(" NIL");
			}

			// cc
			if(entity.Cc != null && entity.Cc.Count > 0){
				retVal.Append(" " + ConstructAddresses(entity.Cc));
			}
			else{
				retVal.Append(" NIL");
			}

			// bcc
			if(entity.Bcc != null && entity.Bcc.Count > 0){
				retVal.Append(" " + ConstructAddresses(entity.Bcc));
			}
			else{
				retVal.Append(" NIL");
			}

			// in-reply-to			
			if(entity.InReplyTo != null){
				retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(entity.InReplyTo)));
			}
			else{
				retVal.Append(" NIL");
			}

			// message-id
			if(entity.MessageID != null){
				retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(entity.MessageID)));
			}
			else{
				retVal.Append(" NIL");
			}

			retVal.Append(")");

			return retVal.ToString();			
		}

		#endregion


		#region method Parse

		/// <summary>
		/// Parses ENVELOPE from IMAP envelope string.
		/// </summary>
		/// <param name="envelopeString">Envelope string.</param>
		public void Parse(string envelopeString)
		{
			if(envelopeString.StartsWith("(")){
				envelopeString = envelopeString.Substring(1);
			}
			if(envelopeString.EndsWith(")")){
				envelopeString = envelopeString.Substring(0,envelopeString.Length - 1);
			}

			string word = "";
			StringReader r = new StringReader(envelopeString);

			#region Date

			// Date
			word = r.ReadWord();
			if(word == null){
				throw new Exception("Invalid IMAP ENVELOPE structure !");
			}
			if(word.ToUpper() == "NIL"){
				m_Date = DateTime.MinValue;
			}
			else{
                try{
				    m_Date = MimeUtils.ParseDate(word);
                }
                catch{
                    // Failed to parse date, return minimum.
                    m_Date = DateTime.MinValue;
                }
			}

			#endregion

			#region Subject

			// Subject
			word = r.ReadWord();
			if(word == null){
				throw new Exception("Invalid IMAP ENVELOPE structure !");
			}
			if(word.ToUpper() == "NIL"){
				m_Subject = null;
			}
			else{
				m_Subject = Core.CanonicalDecode(word);
			}

			#endregion

			#region From

			// From
			m_From = ParseAddresses(r);

			#endregion

			#region Sender

			// Sender
			//	NOTE: There is confusing part, according rfc 2822 Sender: is MailboxAddress and not AddressList.
			MailboxAddress[] sender = ParseAddresses(r);
			if(sender != null && sender.Length > 0){
				m_Sender = sender[0];
			}
			else{
				m_Sender = null;
			}

			#endregion

			#region ReplyTo

			// ReplyTo
			m_ReplyTo = ParseAddresses(r);

			#endregion

			#region To

			// To
			m_To = ParseAddresses(r);

			#endregion

			#region Cc

			// Cc
			m_Cc = ParseAddresses(r);

			#endregion

			#region Bcc

			// Bcc
			m_Bcc = ParseAddresses(r);

			#endregion

			#region InReplyTo

			// InReplyTo
			r.ReadToFirstChar();
			word = r.ReadWord();
			if(word == null){
				throw new Exception("Invalid IMAP ENVELOPE structure !");
			}
			if(word.ToUpper() == "NIL"){
				m_InReplyTo = null;
			}
			else{
				m_InReplyTo = word;
			}

			#endregion

			#region MessageID

			// MessageID
			r.ReadToFirstChar();
			word = r.ReadWord();
			if(word == null){
				throw new Exception("Invalid IMAP ENVELOPE structure !");
			}
			if(word.ToUpper() == "NIL"){
				m_MessageID = null;
			}
			else{
				m_MessageID = word;
			}

			#endregion
		}

		#endregion

		#region method ParseAddresses

		/// <summary>
		/// Parses addresses from IMAP ENVELOPE addresses structure.
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		private MailboxAddress[] ParseAddresses(StringReader r)
		{
			r.ReadToFirstChar();
			if(r.StartsWith("NIL",false)){
				// Remove NIL
				r.ReadSpecifiedLength("NIL".Length);

				return null;
			}
			else{
				r.ReadToFirstChar();

				// This must be ((address)[*(address)])
				if(!r.StartsWith("(")){
					throw new Exception("Invalid IMAP ENVELOPE structure !");
				}
				else{
					// Read addresses
					string addressesString = r.ReadParenthesized();

					ArrayList addresses = new ArrayList();
					StringReader rAddresses = new StringReader(addressesString.Trim());
					// Now we have (address)[*(address)], read addresses
					while(rAddresses.StartsWith("(")){
						addresses.Add(ParseAddress(rAddresses.ReadParenthesized()));

						rAddresses.ReadToFirstChar();
					}

					MailboxAddress[] retVal = new MailboxAddress[addresses.Count];
					addresses.CopyTo(retVal);

					return retVal;
				}
			}
		}

		#endregion

		#region method ParseAddress

		/// <summary>
		/// Parses address from IMAP ENVELOPE address structure.
		/// </summary>
		/// <param name="addressString">Address structure string.</param>
		/// <returns></returns>
		private MailboxAddress ParseAddress(string addressString)
		{
			/* RFC 3501 7.4.2 ENVELOPE
				An address structure is a parenthesized list that describes an
				electronic mail address.  The fields of an address structure
				are in the following order: personal name, [SMTP]
				at-domain-list (source route), mailbox name, and host name.
			*/

			StringReader r = new StringReader(addressString.Trim());
			string personalName = "";
			string emailAddress = "";
            
			// personal name
			if(r.StartsWith("NIL",false)){
				// Remove NIL
				r.ReadSpecifiedLength("NIL".Length);
			}
			else{
				personalName = Core.CanonicalDecode(r.ReadWord());
			}

			// source route, always NIL (not used nowdays)
			r.ReadWord();

			// mailbox name
			if(r.StartsWith("NIL",false)){
				// Remove NIL
				r.ReadSpecifiedLength("NIL".Length);
			}
			else{
				emailAddress = r.ReadWord() + "@";
			}

			// host name
			if(r.StartsWith("NIL",false)){
				// Remove NIL
				r.ReadSpecifiedLength("NIL".Length);
			}
			else{
				emailAddress += r.ReadWord();
			}

			return new MailboxAddress(personalName,emailAddress);
		}

		#endregion


		#region private static method ConstructAddresses

		/// <summary>
		/// Constructs ENVELOPE addresses structure.
		/// </summary>
		/// <param name="addressList">Address list.</param>
		/// <returns></returns>
		private static string ConstructAddresses(AddressList addressList)
		{
			StringBuilder retVal = new StringBuilder();
			retVal.Append("(");

			foreach(MailboxAddress address in addressList.Mailboxes){
				retVal.Append(ConstructAddress(address));
			}

			retVal.Append(")");

			return retVal.ToString();
		}

		#endregion

		#region private static method ConstructAddress

		/// <summary>
		/// Constructs ENVELOPE address structure.
		/// </summary>
		/// <param name="address">Mailbox address.</param>
		/// <returns></returns>
		private static string ConstructAddress(MailboxAddress address)
		{
			/* An address structure is a parenthesized list that describes an
			   electronic mail address.  The fields of an address structure
			   are in the following order: personal name, [SMTP]
			   at-domain-list (source route), mailbox name, and host name.
			*/

			// NOTE: all header fields and parameters must in ENCODED form !!!

			StringBuilder retVal = new StringBuilder();
			retVal.Append("(");

			// personal name
			retVal.Append(TextUtils.QuoteString(MimeUtils.EncodeHeaderField(address.DisplayName)));

			// source route, always NIL (not used nowdays)
			retVal.Append(" NIL");

			// mailbox name
			retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(address.LocalPart)));

			// host name
			retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(address.Domain)));

			retVal.Append(")");

			return retVal.ToString();
		}

		#endregion


		#region Properties Implementation

		/// <summary>
		/// Gets header field "<b>Date:</b>" value. Returns DateTime.MinValue if no date or date parsing fails.
		/// </summary>
		public DateTime Date
		{
			get{ return m_Date; }
		}

		/// <summary>
		/// Gets header field "<b>Subject:</b>" value. Returns null if value isn't set.
		/// </summary>
		public string Subject
		{
			get{ return m_Subject; }
		}

		/// <summary>
		/// Gets header field "<b>From:</b>" value. Returns null if value isn't set.
		/// </summary>
		public MailboxAddress[] From
		{
			get{ return m_From; }
		}

		/// <summary>
		/// Gets header field "<b>Sender:</b>" value. Returns null if value isn't set.
		/// </summary>
		public MailboxAddress Sender
		{
			get{ return m_Sender; }
		}

		/// <summary>
		/// Gets header field "<b>Reply-To:</b>" value. Returns null if value isn't set.
		/// </summary>
		public MailboxAddress[] ReplyTo
		{
			get{ return m_ReplyTo; }
		}

		/// <summary>
		/// Gets header field "<b>To:</b>" value. Returns null if value isn't set.
		/// </summary>
		public MailboxAddress[] To
		{
			get{ return m_To; }
		}

		/// <summary>
		/// Gets header field "<b>Cc:</b>" value. Returns null if value isn't set.
		/// </summary>
		public MailboxAddress[] Cc
		{
			get{ return m_Cc; }
		}

		/// <summary>
		/// Gets header field "<b>Bcc:</b>" value. Returns null if value isn't set.
		/// </summary>
		public MailboxAddress[] Bcc
		{
			get{ return m_Bcc; }
		}
		
		/// <summary>
		/// Gets header field "<b>In-Reply-To:</b>" value. Returns null if value isn't set.
		/// </summary>
		public string InReplyTo
		{
			get{ return m_InReplyTo; }
		}

		/// <summary>
		/// Gets header field "<b>Message-ID:</b>" value. Returns null if value isn't set.
		/// </summary>
		public string MessageID
		{
			get{ return m_MessageID; }
		}

		#endregion

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Estonia Estonia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions