1 using System.Collections.Specialized;
5 namespace System.Net.Mime
8 internal class MimeBasePart
10 protected ContentType contentType;
11 protected ContentDisposition contentDisposition;
12 HeaderCollection headers;
13 internal const string defaultCharSet = "utf-8";//"iso-8859-1";
15 internal MimeBasePart()
19 internal static bool ShouldUseBase64Encoding(Encoding encoding){
20 if (encoding == Encoding.Unicode || encoding == Encoding.UTF8 || encoding == Encoding.UTF32 || encoding == Encoding.BigEndianUnicode) {
26 //use when the length of the header is not known or if there is no header
27 internal static string EncodeHeaderValue(string value, Encoding encoding, bool base64Encoding){
28 return MimeBasePart.EncodeHeaderValue(value, encoding, base64Encoding, 0);
31 //used when the length of the header name itself is known (i.e. Subject : )
32 internal static string EncodeHeaderValue(string value, Encoding encoding, bool base64Encoding, int headerLength) {
33 StringBuilder newString = new StringBuilder();
35 //no need to encode if it's pure ascii
36 if (IsAscii(value, false)) {
40 if (encoding == null) {
41 encoding = Encoding.GetEncoding(MimeBasePart.defaultCharSet);
44 EncodedStreamFactory factory = new EncodedStreamFactory();
45 IEncodableStream stream = factory.GetEncoderForHeader(encoding, base64Encoding, headerLength);
47 byte[] buffer = encoding.GetBytes(value);
48 stream.EncodeBytes(buffer, 0, buffer.Length);
49 return stream.GetEncodedString();
52 internal static string DecodeHeaderValue(string value) {
53 if(value == null || value.Length == 0){
57 string newValue = String.Empty;
59 //split strings, they may be folded. If they are, decode one at a time and append the results
60 string[] substringsToDecode = value.Split(new char[] { '\r', '\n', ' ' }, StringSplitOptions.RemoveEmptyEntries);
62 foreach (string foldedSubString in substringsToDecode) {
63 //an encoded string has as specific format in that it must start and end with an
64 //'=' char and contains five parts, separated by '?' chars.
65 //the first and last part are therefore '=', the second part is the byte encoding (B or Q)
66 //the third is the unicode encoding type, and the fourth is encoded message itself. '?' is not valid inside of
67 //an encoded string other than as a separator for these five parts.
68 //If this check fails, the string is either not encoded or cannot be decoded by this method
69 string[] subStrings = foldedSubString.Split('?');
70 if ((subStrings.Length != 5 || subStrings[0] != "=" || subStrings[4] != "=")) {
74 string charSet = subStrings[1];
75 bool base64Encoding = (subStrings[2] == "B");
76 byte[] buffer = ASCIIEncoding.ASCII.GetBytes(subStrings[3]);
79 EncodedStreamFactory encoderFactory = new EncodedStreamFactory();
80 IEncodableStream s = encoderFactory.GetEncoderForHeader(Encoding.GetEncoding(charSet), base64Encoding, 0);
82 newLength = s.DecodeBytes(buffer, 0, buffer.Length);
84 Encoding encoding = Encoding.GetEncoding(charSet);
85 newValue += encoding.GetString(buffer, 0, newLength);
90 // Detect the encoding: "=?encoding?BorQ?content?="
91 // "=?utf-8?B?RmlsZU5hbWVf55CG0Y3Qq9C60I5jw4TRicKq0YIM0Y1hSsSeTNCy0Klh?="; // 3.5
92 // With the addition of folding in 4.0, there may be multiple lines with encoding, only detect the first:
93 // "=?utf-8?B?RmlsZU5hbWVf55CG0Y3Qq9C60I5jw4TRicKq0YIM0Y1hSsSeTNCy0Klh?=\r\n =?utf-8?B??=";
94 internal static Encoding DecodeEncoding(string value) {
95 if (value == null || value.Length == 0){
99 string[] subStrings = value.Split('?', '\r', '\n');
100 if ((subStrings.Length < 5 || subStrings[0] != "=" || subStrings[4] != "=")) {
103 string charSet = subStrings[1];
104 return Encoding.GetEncoding(charSet);
107 internal static bool IsAscii(string value, bool permitCROrLF) {
109 throw new ArgumentNullException("value");
111 foreach (char c in value) {
115 if (!permitCROrLF && (c=='\r' || c=='\n')) {
122 internal static bool IsAnsi(string value, bool permitCROrLF) {
124 throw new ArgumentNullException("value");
126 foreach (char c in value) {
130 if (!permitCROrLF && (c=='\r' || c=='\n')) {
137 internal string ContentID {
139 return Headers[MailHeaderInfo.GetString(MailHeaderID.ContentID)];
142 if (string.IsNullOrEmpty(value))
144 Headers.Remove(MailHeaderInfo.GetString(MailHeaderID.ContentID));
148 Headers[MailHeaderInfo.GetString(MailHeaderID.ContentID)] = value;
153 internal string ContentLocation
157 return Headers[MailHeaderInfo.GetString(MailHeaderID.ContentLocation)];
161 if (string.IsNullOrEmpty(value))
163 Headers.Remove(MailHeaderInfo.GetString(MailHeaderID.ContentLocation));
167 Headers[MailHeaderInfo.GetString(MailHeaderID.ContentLocation)] = value;
172 internal NameValueCollection Headers
175 //persist existing info before returning
177 headers = new HeaderCollection();
179 if (contentType == null){
180 contentType = new ContentType();
182 contentType.PersistIfNeeded(headers,false);
184 if (contentDisposition != null)
185 contentDisposition.PersistIfNeeded(headers,false);
190 internal ContentType ContentType{
192 if (contentType == null){
193 contentType = new ContentType();
199 throw new ArgumentNullException("value");
202 contentType.PersistIfNeeded((HeaderCollection)Headers,true);
206 internal void PrepareHeaders(bool allowUnicode) {
207 contentType.PersistIfNeeded((HeaderCollection)Headers, false);
208 headers.InternalSet(MailHeaderInfo.GetString(MailHeaderID.ContentType), contentType.Encode(allowUnicode));
210 if (contentDisposition != null) {
211 contentDisposition.PersistIfNeeded((HeaderCollection)Headers, false);
212 headers.InternalSet(MailHeaderInfo.GetString(MailHeaderID.ContentDisposition),
213 contentDisposition.Encode(allowUnicode));
217 internal virtual void Send(BaseWriter writer, bool allowUnicode) {
218 throw new NotImplementedException();
221 internal virtual IAsyncResult BeginSend(BaseWriter writer, AsyncCallback callback,
222 bool allowUnicode, object state) {
223 throw new NotImplementedException();
226 internal void EndSend(IAsyncResult asyncResult) {
228 if (asyncResult == null) {
229 throw new ArgumentNullException("asyncResult");
232 LazyAsyncResult castedAsyncResult = asyncResult as MimePartAsyncResult;
234 if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) {
235 throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult");
238 if (castedAsyncResult.EndCalled) {
239 throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndSend"));
242 castedAsyncResult.InternalWaitForCompletion();
243 castedAsyncResult.EndCalled = true;
244 if (castedAsyncResult.Result is Exception) {
245 throw (Exception)castedAsyncResult.Result;
249 internal class MimePartAsyncResult: LazyAsyncResult {
250 internal MimePartAsyncResult(MimeBasePart part, object state, AsyncCallback callback):base(part,state,callback) {