// -----------------------------------------------------------------------
//
// 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