Click here to Skip to main content
15,879,326 members
Articles / Programming Languages / C#

A C# Implementation of Mime De/encode

Rate me:
Please Sign up or sign in to vote.
4.00/5 (16 votes)
21 Aug 2005CPOL 150.6K   4.2K   38  
A C# implementation of Mime de/encode
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Security;

namespace mimelib
{
	/// <summary>
	/// <para>
	/// Robust and fast implementation of Quoted Printable
	/// Multipart Internet Mail Encoding (MIME) which encodes every 
	/// character, not just "special characters" for transmission over 
	/// SMTP.
	/// </para>
	/// <para>
	/// More information on the quoted-printable encoding can be found
	/// here: http://www.freesoft.org/CIE/RFC/1521/6.htm
	/// </para>
	/// </summary>
	/// <remarks>
	/// <para>
	/// detailed in: RFC 1521
	/// </para>
	/// <para>
	/// more info: http://www.freesoft.org/CIE/RFC/1521/6.htm
	/// </para>
	/// <para>
	/// The QuotedPrintable class encodes and decodes strings and files
	/// that either were encoded or need encoded in the Quoted-Printable
	/// MIME encoding for Internet mail. The encoding methods of the class
	/// use pointers wherever possible to guarantee the fastest possible 
	/// encoding times for any size file or string. The decoding methods 
	/// use only the .NET framework classes.
	/// </para>
	/// <para>
	/// The Quoted-Printable implementation
	/// is robust which means it encodes every character to ensure that the
	/// information is decoded properly regardless of machine or underlying
	/// operating system or protocol implementation. The decode can recognize
	/// robust encodings as well as minimal encodings that only encode special
	/// characters and any implementation in between. Internally, the
	/// class uses a regular expression replace pattern to decode a quoted-
	/// printable string or file.
	/// </para>
	/// </remarks>
	/// <example>
	/// This example shows how to quoted-printable encode an html file and then
	/// decode it.
	/// <code>
	/// string encoded = QuotedPrintable.EncodeFile(
	/// 	@"C:\WEBS\wwwroot\index.html"
	/// 	);
	/// 
	/// string decoded = QuotedPrintable.Decode(encoded);
	/// 
	/// Console.WriteLine(decoded);
	/// </code>
	/// </example>
	public class QuotedPrintable
	{
		private QuotedPrintable()
		{
		}

		/// <summary>
		/// Gets the maximum number of characters per quoted-printable
		/// line as defined in the RFC minus 1 to allow for the =
		/// character (soft line break).
		/// </summary>
		/// <remarks>
		/// (Soft Line Breaks): The Quoted-Printable encoding REQUIRES 
		/// that encoded lines be no more than 76 characters long. If 
		/// longer lines are to be encoded with the Quoted-Printable 
		/// encoding, 'soft' line breaks must be used. An equal sign 
		/// as the last character on a encoded line indicates such a 
		/// non-significant ('soft') line break in the encoded text.
		/// </remarks>
		public const int RFC_1521_MAX_CHARS_PER_LINE = 75;

		/// <summary>
		/// Encodes a very large string into the Quoted-Printable
		/// encoding for transmission via SMTP
		/// </summary>
		/// <param name="toencode">
		/// the very large string to encode
		/// </param>
		/// <returns>The Quoted-Printable encoded string</returns>
		/// <exception cref="ObjectDisposedException">
		/// A problem occurred while attempting to read the encoded 
		/// string.
		/// </exception>
		/// <exception cref="OutOfMemoryException">
		/// There is insufficient memory to allocate a buffer for the
		/// returned string. 
		/// </exception>
		/// <exception cref="ArgumentNullException">
		/// A string is passed in as a null reference.
		/// </exception>
		/// <exception cref="IOException">
		/// An I/O error occurs, such as the stream being closed.
		/// </exception>  
		/// <exception cref="ArgumentOutOfRangeException">
		/// The charsperline argument is less than or equal to 0.
		/// </exception>
		/// <remarks>
		/// This method encodes a large string into the quoted-printable
		/// encoding and then properly formats it into lines of 76 characters
		/// using the <see cref="FormatEncodedString"/> method.
		/// </remarks>
		public static string Encode(string toencode)
		{
			return Encode(toencode, RFC_1521_MAX_CHARS_PER_LINE);
		}

		/// <summary>
		/// Encodes a very large string into the Quoted-Printable
		/// encoding for transmission via SMTP
		/// </summary>
		/// <param name="toencode">
		/// the very large string to encode
		/// </param>
		/// <param name="charsperline">
		/// the number of chars per line after encoding
		/// </param>
		/// <returns>The Quoted-Printable encoded string</returns>
		/// <exception cref="ObjectDisposedException">
		/// A problem occurred while attempting to read the encoded 
		/// string.
		/// </exception>
		/// <exception cref="OutOfMemoryException">
		/// There is insufficient memory to allocate a buffer for the
		/// returned string. 
		/// </exception>
		/// <exception cref="ArgumentNullException">
		/// A string is passed in as a null reference.
		/// </exception>
		/// <exception cref="IOException">
		/// An I/O error occurs, such as the stream being closed.
		/// </exception>  
		/// <exception cref="ArgumentOutOfRangeException">
		/// The charsperline argument is less than or equal to 0.
		/// </exception>
		/// <remarks>
		/// This method encodes a large string into the quoted-printable
		/// encoding and then properly formats it into lines of 
		/// charsperline characters using the <see cref="FormatEncodedString"/> 
		/// method.
		/// </remarks>
		public static string Encode(string toencode, int charsperline)
		{
			if (toencode == null)
				throw new ArgumentNullException();

			if (charsperline <= 0)
				throw new ArgumentOutOfRangeException();

			string line, encodedHtml = "";
			StringReader sr = new StringReader(toencode);
			try
			{
				while((line=sr.ReadLine())!=null)
					encodedHtml += EncodeSmallLine(line);

				return FormatEncodedString(encodedHtml, charsperline);
			}
			finally
			{
				sr.Close();
				sr = null;
			}
		}

		/// <summary>
		/// Encodes a file's contents into a string using
		/// the Quoted-Printable encoding.
		/// </summary>
		/// <param name="filepath">
		/// The path to the file to encode.
		/// </param>
		/// <returns>The Quoted-Printable encoded string</returns>
		/// <exception cref="ObjectDisposedException">
		/// A problem occurred while attempting to encode the 
		/// string.
		/// </exception>
		/// <exception cref="OutOfMemoryException">
		/// There is insufficient memory to allocate a buffer for the
		/// returned string. 
		/// </exception>
		/// <exception cref="ArgumentNullException">
		/// A string is passed in as a null reference.
		/// </exception>
		/// <exception cref="IOException">
		/// An I/O error occurs, such as the stream being closed.
		/// </exception>  
		/// <exception cref="FileNotFoundException">
		/// The file was not found.
		/// </exception>
		/// <exception cref="SecurityException">
		/// The caller does not have the required permission to open
		/// the file specified in filepath.
		/// </exception>
		/// <exception cref="UnauthorizedAccessException">
		/// filepath is read-only or a directory.
		/// </exception>
		/// <remarks>
		/// This method encodes a file's text into the quoted-printable
		/// encoding and then properly formats it into lines of 76 characters
		/// using the <see cref="FormatEncodedString"/> method.
		/// </remarks>
		public static string EncodeFile(string filepath)
		{
			return EncodeFile(filepath, RFC_1521_MAX_CHARS_PER_LINE);
		}

		/// <summary>
		/// Encodes a file's contents into a string using
		/// the Quoted-Printable encoding.
		/// </summary>
		/// <param name="filepath">
		/// The path to the file to encode.
		/// </param>
		/// <param name="charsperline">
		/// the number of chars per line after encoding
		/// </param>
		/// <returns>The Quoted-Printable encoded string</returns>
		/// <exception cref="ObjectDisposedException">
		/// A problem occurred while attempting to encode the 
		/// string.
		/// </exception>
		/// <exception cref="OutOfMemoryException">
		/// There is insufficient memory to allocate a buffer for the
		/// returned string. 
		/// </exception>
		/// <exception cref="ArgumentNullException">
		/// A string is passed in as a null reference.
		/// </exception>
		/// <exception cref="IOException">
		/// An I/O error occurs, such as the stream being closed.
		/// </exception>  
		/// <exception cref="FileNotFoundException">
		/// The file was not found.
		/// </exception>
		/// <exception cref="SecurityException">
		/// The caller does not have the required permission to open
		/// the file specified in filepath.
		/// </exception>
		/// <exception cref="UnauthorizedAccessException">
		/// filepath is read-only or a directory.
		/// </exception>
		/// <remarks>
		/// This method encodes a file's text into the quoted-printable
		/// encoding and then properly formats it into lines of 
		/// charsperline characters using the <see cref="FormatEncodedString"/> 
		/// method.
		/// </remarks>
		public static string EncodeFile(string filepath, int charsperline)
		{
			if (filepath == null)
				throw new ArgumentNullException();

			string encodedHtml = "", line;
			FileInfo f = new FileInfo(filepath);
			
			if (! f.Exists)
				throw new FileNotFoundException();

			StreamReader sr = f.OpenText();
			try
			{
				while((line=sr.ReadLine())!=null)
					encodedHtml += EncodeSmallLine(line);

				return FormatEncodedString(encodedHtml, charsperline);
			}
			finally
			{
				sr.Close();
				sr = null;
				f = null;
			}
		}

		/// <summary>
		/// Encodes a small string into the Quoted-Printable encoding
		/// for transmission via SMTP. The string is not split
		/// into lines of X characters like the string that the 
		/// Encode method returns.
		/// </summary>
		/// <param name="s">
		/// The string to encode.
		/// </param>
		/// <returns>The Quoted-Printable encoded string</returns>
		/// <exception cref="ArgumentNullException">
		/// A string is passed in as a null reference.
		/// </exception>
		/// <remarks>
		/// This method encodes a small string into the quoted-printable
		/// encoding. The resultant encoded string has NOT been separated
		/// into lined results using the <see cref="FormatEncodedString"/>
		/// method.
		/// </remarks>
		public unsafe static string EncodeSmall(string s)
		{
			if (s == null)
				throw new ArgumentNullException();

			string result = "";
			fixed (char* pChar = s)
			{
				char* pCurrent = pChar;
				do
				{
					int code = (*pCurrent);
					result += String.Format("={0}", code.ToString("X2"));
					pCurrent++;
				}
				while (*pCurrent != 0);
			}
			return result;
		}

		/// <summary>
		/// Encodes a small string with an appended newline into the 
		/// Quoted-Printable encoding for transmission via SMTP. The 
		/// string is not split into lines of X characters like the 
		/// string that the Encode or the EncodeFile methods return.
		/// </summary>
		/// <param name="s">
		/// The string to encode.
		/// </param>
		/// <returns>The Quoted-Printable encoded string</returns>
		/// <exception cref="ArgumentNullException">
		/// A string is passed in as a null reference.
		/// </exception>
		/// <remarks>
		/// This method encodes a small string into the quoted-printable
		/// encoding. The resultant encoded string has NOT been separated
		/// into lined results using the <see cref="FormatEncodedString"/>
		/// method.
		/// </remarks>
		public static string EncodeSmallLine(string s)
		{
			if (s == null)
				throw new ArgumentNullException();

			return EncodeSmall(s + "\r\n");
		}

		/// <summary>
		/// Formats a quoted-printable string into lines equal to maxcharlen,
		/// following all protocol rules such as byte stuffing. This method is
		/// called automatically by the Encode method and the EncodeFile method.
		/// </summary>
		/// <param name="qpstr">
		/// the quoted-printable encoded string.
		/// </param>
		/// <param name="maxcharlen">
		/// the number of chars per line after encoding.
		/// </param>
		/// <returns>
		/// The properly formatted Quoted-Printable encoded string in lines of
		/// 76 characters as defined by the RFC.</returns>
		/// <exception cref="ArgumentNullException">
		/// A string is passed in as a null reference.
		/// </exception>
		/// <exception cref="IOException">
		/// An I/O error occurs, such as the stream being closed.
		/// </exception>  
		/// <remarks>
		/// Formats a quoted-printable encoded string into lines of
		/// maxcharlen characters for transmission via SMTP.
		/// </remarks>
		public unsafe static string FormatEncodedString(string qpstr, int maxcharlen)
		{
			if (qpstr == null)
				throw new ArgumentNullException();

			string strout = "";
			StringWriter qpsw = new StringWriter();
			try
			{
				fixed(char* pChr = qpstr)
				{
					char* pCurrent = pChr;
					int i = 0;
					do
					{
						strout += pCurrent->ToString();
						i++;
						if (i==maxcharlen)
						{
							qpsw.WriteLine("{0}=", strout);
							qpsw.Flush();

							i=0;
							strout = "";
						}
						pCurrent++;
					}
					while(*pCurrent != 0);
				}
				qpsw.WriteLine(strout);
				qpsw.Flush();

				return qpsw.ToString();
			}
			finally
			{
				qpsw.Close();
				qpsw = null;
			}
		}

		static string HexDecoderEvaluator(Match m)
		{
			string hex = m.Groups[2].Value;
			int iHex = Convert.ToInt32(hex, 16);
			char c = (char) iHex;
			return c.ToString();
		}

		static string HexDecoder(string line)
		{
			if (line == null)
				throw new ArgumentNullException();

			//parse looking for =XX where XX is hexadecimal
			Regex re = new Regex(
				"(\\=([0-9A-F][0-9A-F]))", 
				RegexOptions.IgnoreCase
			);
			return re.Replace(line, new MatchEvaluator(HexDecoderEvaluator));
		}

		/// <summary>
		/// decodes an entire file's contents into plain text that 
		/// was encoded with quoted-printable.
		/// </summary>
		/// <param name="filepath">
		/// The path to the quoted-printable encoded file to decode.
		/// </param>
		/// <returns>The decoded string.</returns>
		/// <exception cref="ObjectDisposedException">
		/// A problem occurred while attempting to decode the 
		/// encoded string.
		/// </exception>
		/// <exception cref="OutOfMemoryException">
		/// There is insufficient memory to allocate a buffer for the
		/// returned string. 
		/// </exception>
		/// <exception cref="ArgumentNullException">
		/// A string is passed in as a null reference.
		/// </exception>
		/// <exception cref="IOException">
		/// An I/O error occurs, such as the stream being closed.
		/// </exception>  
		/// <exception cref="FileNotFoundException">
		/// The file was not found.
		/// </exception>
		/// <exception cref="SecurityException">
		/// The caller does not have the required permission to open
		/// the file specified in filepath.
		/// </exception>
		/// <exception cref="UnauthorizedAccessException">
		/// filepath is read-only or a directory.
		/// </exception>
		/// <remarks>
		/// Decodes a quoted-printable encoded file into a string
		/// of unencoded text of any size.
		/// </remarks>
		public static string DecodeFile(string filepath)
		{
			if (filepath == null)
				throw new ArgumentNullException();

			string decodedHtml = "", line;
			FileInfo f = new FileInfo(filepath);

			if (! f.Exists)
				throw new FileNotFoundException();

			StreamReader sr = f.OpenText();
			try
			{
				while((line=sr.ReadLine())!=null)
					decodedHtml += Decode(line);

				return decodedHtml;
			}
			finally
			{
				sr.Close();
				sr = null;
				f = null;
			}
		}

		/// <summary>
		/// Decodes a Quoted-Printable string of any size into 
		/// it's original text.
		/// </summary>
		/// <param name="encoded">
		/// The encoded string to decode.
		/// </param>
		/// <returns>The decoded string.</returns>
		/// <exception cref="ArgumentNullException">
		/// A string is passed in as a null reference.
		/// </exception>
		/// <remarks>
		/// Decodes a quoted-printable encoded string into a string
		/// of unencoded text of any size.
		/// </remarks>
		public static string Decode(string encoded)
		{
			if (encoded == null)
				throw new ArgumentNullException();

			string line;
			StringWriter sw = new StringWriter();
			StringReader sr = new StringReader(encoded);
			try
			{
				while((line=sr.ReadLine())!=null)
				{
					if (line.EndsWith("="))
						sw.Write(HexDecoder(line.Substring(0, line.Length-1)));
					else
						sw.WriteLine(HexDecoder(line));

					sw.Flush();
				}
				return sw.ToString();
			}
			finally
			{
				sw.Close();
				sr.Close();
				sw = null;
				sr = null;
			}
		}
	}
}

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
Web Developer
China China
I think I have to start coding my life now...Smile | :)

Comments and Discussions