// ----------------------------------------------------------------------- // // Copyright (C) 2003-2006 Angel Marin // // This file is part of SharpMimeTools // // SharpMimeTools is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // SharpMimeTools is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with SharpMimeTools; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // ----------------------------------------------------------------------- using System; namespace anmar.SharpMimeTools { /// /// /// public class SharpMimeTools { #if LOG private static log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); #endif private static System.String[] _date_formats = new System.String[] { @"dddd, d MMM yyyy H:m:s zzz", @"ddd, d MMM yyyy H:m:s zzz", @"d MMM yyyy H:m:s zzz", @"dddd, d MMM yy H:m:s zzz", @"ddd, d MMM yy H:m:s zzz", @"d MMM yy H:m:s zzz", @"dddd, d MMM yyyy H:m zzz", @"ddd, d MMM yyyy H:m zzz", @"d MMM yyyy H:m zzz", @"dddd, d MMM yy H:m zzz", @"ddd, d MMM yy H:m zzz", @"d MMM yy H:m zzz" }; internal static System.String GetFileName ( System.String name ) { if ( name==null || name.Length==0 ) return name; name = name.Replace("\t", ""); try { name = System.IO.Path.GetFileName(name); } catch ( System.ArgumentException ) { // Remove invalid chars foreach ( char ichar in System.IO.Path.InvalidPathChars ) { name = name.Replace ( ichar.ToString(), System.String.Empty ); } name = System.IO.Path.GetFileName(name); } try { System.IO.FileInfo fi = new System.IO.FileInfo(name); if ( fi!=null ) fi = null; } catch ( System.ArgumentException ) { name = null; #if LOG if ( log.IsErrorEnabled ) { log.Error(System.String.Concat("Filename [", name, "] is not allowed by the filesystem")); } #endif } return name; } /// /// Parses a from a charset name /// /// charset to parse /// A that represents the given charset public static System.Text.Encoding parseCharSet ( System.String charset ) { try { return System.Text.Encoding.GetEncoding (charset); } catch ( System.Exception ) { return null; } } internal static System.Enum ParseEnum ( System.Type t, System.Object s, System.Enum defaultvalue ) { if ( s==null ) return defaultvalue; System.Enum value = defaultvalue; if ( System.Enum.IsDefined(t, s) ) { value = (System.Enum)System.Enum.Parse(t, s.ToString()); } return value; } /// /// Parse a rfc 2822 address specification. rfc 2822 section 3.4 /// /// field body to parse /// A collection of public static System.Collections.IEnumerable parseFrom ( System.String from ) { return anmar.SharpMimeTools.SharpMimeAddressCollection.Parse (from); } /// /// Parse a rfc 2822 name-address specification. rfc 2822 section 3.4 /// /// address /// 1 is display-name; 2 is addr-spec /// the requested public static System.String parseFrom ( System.String from, int part ) { int pos; if ( from==null || from.Length<1) { return System.String.Empty; } switch (part) { case 1: pos = from.LastIndexOf('<'); pos = (pos<0)?from.Length:pos; from = from.Substring (0, pos).Trim(); from = anmar.SharpMimeTools.SharpMimeTools.parserfc2047Header ( from ); return from; case 2: pos = from.LastIndexOf('<')+1; return from.Substring(pos, from.Length-pos).Trim(new char[]{'<','>',' '}); } return from; } /// /// Parse a rfc 2822 date and time specification. rfc 2822 section 3.3 /// /// rfc 2822 date-time /// A from the parsed header body public static System.DateTime parseDate ( System.String date ) { if ( date==null || date.Equals(System.String.Empty) ) return System.DateTime.MinValue; System.DateTime msgDateTime; date = anmar.SharpMimeTools.SharpMimeTools.uncommentString (date); msgDateTime = new System.DateTime (0); try { // TODO: Complete the list date = date.Replace("UT", "+0000"); date = date.Replace("GMT", "+0000"); date = date.Replace("EDT", "-0400"); date = date.Replace("EST", "-0500"); date = date.Replace("CDT", "-0500"); date = date.Replace("MDT", "-0600"); date = date.Replace("MST", "-0600"); date = date.Replace("EST", "-0700"); date = date.Replace("PDT", "-0700"); date = date.Replace("PST", "-0800"); date = date.Replace("AM", System.String.Empty); date = date.Replace("PM", System.String.Empty); int rpos = date.LastIndexOfAny(new Char[]{' ', '\t'}); if (rpos>0 && rpos != date.Length - 6) date = date.Substring(0, rpos + 1) + "-0000"; date = date.Insert(date.Length-2, ":"); msgDateTime = DateTime.ParseExact(date, _date_formats, System.Globalization.CultureInfo.CreateSpecificCulture("en-us"), System.Globalization.DateTimeStyles.AllowInnerWhite); #if LOG } catch ( System.Exception e ) { if ( log.IsErrorEnabled ) log.Error(System.String.Concat("Error parsing date: [", date, "]"), e); #else } catch ( System.Exception ) { #endif msgDateTime = new System.DateTime (0); } return msgDateTime; } /// /// Parse a rfc 2822 header field with parameters /// /// field name /// field body to parse /// A from the parsed field body public static System.Collections.Specialized.StringDictionary parseHeaderFieldBody ( System.String field, System.String fieldbody ) { if ( fieldbody==null ) return null; // FIXME: rewrite parseHeaderFieldBody to being regexp based. fieldbody = anmar.SharpMimeTools.SharpMimeTools.uncommentString (fieldbody); System.Collections.Specialized.StringDictionary fieldbodycol = new System.Collections.Specialized.StringDictionary (); System.String[] words = fieldbody.Split(new Char[]{';'}); if ( words.Length>0 ) { fieldbodycol.Add (field.ToLower(), words[0].ToLower()); for (int i=1; i /// Parse and decode rfc 2047 header body /// /// header body to parse /// parsed public static System.String parserfc2047Header ( System.String header ) { header = header.Replace ("\"", System.String.Empty); header = anmar.SharpMimeTools.SharpMimeTools.rfc2047decode(header); return header; } /// /// Decode rfc 2047 definition of quoted-printable /// /// charset to use when decoding /// string to decode /// the decoded public static System.String QuotedPrintable2Unicode ( System.String charset, System.String orig ) { System.Text.Encoding enc = anmar.SharpMimeTools.SharpMimeTools.parseCharSet (charset); if ( enc==null || orig==null ) return orig; anmar.SharpMimeTools.SharpMimeTools.QuotedPrintable2Unicode ( enc, ref orig ); return orig; } /// /// Decode rfc 2047 definition of quoted-printable /// /// to use /// string to decode public static void QuotedPrintable2Unicode ( System.Text.Encoding enc, ref System.String orig ) { if ( enc==null || orig==null ) return; System.Text.StringBuilder decoded = new System.Text.StringBuilder(orig); int i = 0; System.String hexNumber; System.Byte[] ch = new System.Byte[1]; while ( i < decoded.Length - 2 ) { System.String decodedItem = null; if ( decoded[i] == '=' ) { hexNumber = decoded.ToString(i+1, 2); if ( hexNumber.Equals(ABNF.CRLF) ) { decodedItem = System.String.Empty; // Do not replace 3D(=) } else if ( hexNumber.ToUpper().Equals("3D") ) { decodedItem = null; } else { try { //TODO: this ugly workaround should disapear ch[0] = System.Convert.ToByte(hexNumber, 16); decodedItem = enc.GetString ( ch ); } catch ( System.Exception ) {} } if ( decodedItem!=null ) decoded.Replace( "=" + hexNumber, decodedItem ); } if ( decodedItem!=null ) i+=decodedItem.Length; else i++; } decoded.Replace("=3D", "="); decoded.Replace("=3d", "="); orig = decoded.ToString(); return; } /// /// rfc 2047 header body decoding /// /// string to decode /// the decoded public static System.String rfc2047decode ( System.String word ) { System.String[] words; System.String[] wordetails; System.Text.RegularExpressions.Regex rfc2047format = new System.Text.RegularExpressions.Regex (@"(=\?[\-a-zA-Z0-9]+\?[qQbB]\?[a-zA-Z0-9=_\-\.$%&/\'\\!:;{}\+\*\|@#~`^]+\?=)\s*", System.Text.RegularExpressions.RegexOptions.ECMAScript); // No rfc2047 format if ( !rfc2047format.IsMatch (word) ){ #if LOG if ( log.IsDebugEnabled ) log.Debug ("Not a RFC 2047 string: " + word); #endif return word; } #if LOG if ( log.IsDebugEnabled ) log.Debug ("Decoding 2047 string: " + word); #endif words = rfc2047format.Split ( word ); word = System.String.Empty; rfc2047format = new System.Text.RegularExpressions.Regex (@"=\?([\-a-zA-Z0-9]+)\?([qQbB])\?([a-zA-Z0-9=_\-\.$%&/\'\\!:;{}\+\*\|@#~`^]+)\?=", System.Text.RegularExpressions.RegexOptions.ECMAScript); for ( int i=0; i /// Remove rfc 2822 comments /// /// string to uncomment /// // TODO: refactorize this public static System.String uncommentString ( System.String fieldValue ) { if ( fieldValue==null || fieldValue.Equals(System.String.Empty) ) return fieldValue; if ( fieldValue.IndexOf('(')==-1 ) return fieldValue.Trim(); const int a = 0; const int b = 1; const int c = 2; System.Text.StringBuilder buf = new System.Text.StringBuilder(); int leftSqureCount = 0; bool isQuotedPair = false; int state = a; for (int i = 0; i < fieldValue.Length; i ++) { switch (state) { case a: if (fieldValue[i] == '"') { state = c; System.Diagnostics.Debug.Assert(!isQuotedPair, "quoted-pair"); } else if (fieldValue[i] == '(') { state = b; leftSqureCount ++; System.Diagnostics.Debug.Assert(!isQuotedPair, "quoted-pair"); } break; case b: if (!isQuotedPair) { if (fieldValue[i] == '(') leftSqureCount ++; else if (fieldValue[i] == ')') { leftSqureCount --; if (leftSqureCount == 0) { buf.Append(' '); state = a; continue; } } } break; case c: if (!isQuotedPair) { if (fieldValue[i] == '"') state = a; } break; default: break; } if (state != a) { //quoted-pair if (isQuotedPair) isQuotedPair = false; else isQuotedPair = fieldValue[i] == '\\'; } if (state != b) buf.Append(fieldValue[i]); } return buf.ToString().Trim(); } /// /// Encodes a Message-ID or Content-ID following RFC 2392 rules. /// /// with the Message-ID or Content-ID. /// with the value encoded as RFC 2392 dictates. public static System.String Rfc2392Url ( System.String input) { if ( input==null || input.Length<4 ) return input; if ( input.Length>2 && input[0]=='<' && input[input.Length-1]=='>' ) input = input.Substring(1, input.Length-2); if ( input.IndexOf('/')!=-1 ) { input = input.Replace("/", "%2f"); } return input; } /// /// Decodes the provided uuencoded string. /// /// with the uuendoced content. /// A with the uudecoded content. Or the null reference if content can't be decoded. /// The input string must contain the begin and end delimiters. public static System.Byte[] UuDecode ( System.String input ) { if ( input==null || input.Length==0 ) return null; System.IO.StringReader reader = new System.IO.StringReader(input); System.IO.MemoryStream stream = null; System.Byte[] output = null; for ( System.String line=reader.ReadLine(); line!=null; line=reader.ReadLine() ) { // Found the start point of uuencoded content if ( line.Length>10 && line[0]=='b' && line[1]=='e' && line[2]=='g' && line[3]=='i' && line[4]=='n' && line[5]==' ' && line[9]==' ' ) { stream = new System.IO.MemoryStream(); continue; } // Not within uuencoded content if ( stream==null ) continue; // Content finished if ( line.Length==3 && line=="end" ) { stream.Flush(); output = stream.ToArray(); stream.Close(); stream = null; break; } // Decode and write uuencoded line UuDecodeLine(line, stream); } reader.Close(); reader = null; if ( stream!=null ) { stream.Close(); stream = null; } return output; } /// /// Decodes the provided uuencoded line. /// /// with the uuendoced line. /// where decoded should be written. /// true if content has been decoded and false otherwise. public static bool UuDecodeLine ( System.String s, System.IO.Stream stream ) { if ( s==null || s.Length==0 || stream==null || !stream.CanWrite ) return false; System.Byte[] input = System.Text.Encoding.ASCII.GetBytes(s); int length = input.Length; int length_output = 0; // Full line, so it has length info in the first byte if ( (length%4)==1 ) { length_output = ((input[0]-0x20) & 0x3f); } // Wrong input if ( length==0 || length_output<=0 ) return false; // Decode each four characters of input to three octets of output for ( int i=1, pos=0; i>4)); pos++; if ( pos>2)); pos++; } if ( pos