8fa1b7eea41403f232d32a996504bfe9d9531882
[mono.git] / mcs / class / referencesource / System / net / System / Net / mail / MimeBasePart.cs
1 using System.Collections.Specialized;
2 using System.Text;
3 using System.Net.Mail;
4
5 namespace System.Net.Mime
6 {
7
8     internal class MimeBasePart
9     {
10         protected ContentType contentType;
11         protected ContentDisposition contentDisposition;
12         HeaderCollection headers;
13         internal const string defaultCharSet = "utf-8";//"iso-8859-1";
14
15         internal MimeBasePart()
16         {
17         }
18
19         internal static bool ShouldUseBase64Encoding(Encoding encoding){
20             if (encoding == Encoding.Unicode || encoding == Encoding.UTF8 || encoding == Encoding.UTF32  || encoding == Encoding.BigEndianUnicode) {
21                 return true;
22             }
23             return false;
24         }
25
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);
29         }
30
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();
34             
35             //no need to encode if it's pure ascii
36             if (IsAscii(value, false)) {
37                 return value;
38             }
39
40             if (encoding == null) {
41                 encoding = Encoding.GetEncoding(MimeBasePart.defaultCharSet);
42             }
43
44             EncodedStreamFactory factory = new EncodedStreamFactory();
45             IEncodableStream stream = factory.GetEncoderForHeader(encoding, base64Encoding, headerLength);
46             
47             byte[] buffer = encoding.GetBytes(value);
48             stream.EncodeBytes(buffer, 0, buffer.Length);
49             return stream.GetEncodedString();
50         }
51
52         internal static string DecodeHeaderValue(string value) {
53             if(value == null || value.Length == 0){
54                 return String.Empty;
55             }
56
57             string newValue = String.Empty;
58
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);
61
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] != "=")) {
71                     return value;
72                 }
73
74                 string charSet = subStrings[1];
75                 bool base64Encoding = (subStrings[2] == "B");
76                 byte[] buffer = ASCIIEncoding.ASCII.GetBytes(subStrings[3]);
77                 int newLength;
78
79                 EncodedStreamFactory encoderFactory = new EncodedStreamFactory();
80                 IEncodableStream s = encoderFactory.GetEncoderForHeader(Encoding.GetEncoding(charSet), base64Encoding, 0);
81
82                 newLength = s.DecodeBytes(buffer, 0, buffer.Length);
83
84                 Encoding encoding = Encoding.GetEncoding(charSet);
85                 newValue += encoding.GetString(buffer, 0, newLength);
86             }
87             return newValue;
88         }
89
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){
96                 return null;
97             }
98
99             string[] subStrings = value.Split('?', '\r', '\n');
100             if ((subStrings.Length < 5 || subStrings[0] != "=" || subStrings[4] != "=")) {
101                 return null;
102             }
103             string charSet = subStrings[1];
104             return Encoding.GetEncoding(charSet);
105         }
106
107         internal static bool IsAscii(string value, bool permitCROrLF) {
108             if (value == null)
109                 throw new ArgumentNullException("value");
110             
111             foreach (char c in value) {
112                 if ((int)c > 0x7f) {
113                     return false;
114                 }
115                 if (!permitCROrLF && (c=='\r' || c=='\n')) {
116                     return false;
117                 }
118             }
119             return true;
120         }
121
122         internal static bool IsAnsi(string value, bool permitCROrLF) {
123             if (value == null)
124                 throw new ArgumentNullException("value");
125             
126             foreach (char c in value) {
127                 if ((int)c > 0xff) {
128                     return false;
129                 }
130                 if (!permitCROrLF && (c=='\r' || c=='\n')) {
131                     return false;
132                 }
133             }
134             return true;
135         }
136         
137         internal string ContentID {
138             get {
139                 return Headers[MailHeaderInfo.GetString(MailHeaderID.ContentID)];
140             }
141             set {
142                 if (string.IsNullOrEmpty(value))
143                 {
144                     Headers.Remove(MailHeaderInfo.GetString(MailHeaderID.ContentID));
145                 }
146                 else
147                 {
148                     Headers[MailHeaderInfo.GetString(MailHeaderID.ContentID)] = value;
149                 }
150             }
151         }
152
153         internal string ContentLocation
154         {
155             get
156             {
157                 return Headers[MailHeaderInfo.GetString(MailHeaderID.ContentLocation)];
158             }
159             set
160             {
161                 if (string.IsNullOrEmpty(value))
162                 {
163                     Headers.Remove(MailHeaderInfo.GetString(MailHeaderID.ContentLocation));
164                 }
165                 else
166                 {
167                     Headers[MailHeaderInfo.GetString(MailHeaderID.ContentLocation)] = value;
168                 }
169             }
170         }
171
172         internal NameValueCollection Headers
173         {
174             get {
175                 //persist existing info before returning
176                 if (headers == null)
177                     headers = new HeaderCollection();
178
179                 if (contentType == null){
180                     contentType = new ContentType();
181                 }
182                 contentType.PersistIfNeeded(headers,false);
183
184                 if (contentDisposition != null)
185                     contentDisposition.PersistIfNeeded(headers,false);
186                 return headers;
187             }
188         }
189         
190         internal ContentType ContentType{
191             get{
192                 if (contentType == null){
193                     contentType = new ContentType();
194                 }
195                 return contentType;
196             }
197             set {
198                 if (value == null)
199                     throw new ArgumentNullException("value");
200
201                 contentType = value;
202                 contentType.PersistIfNeeded((HeaderCollection)Headers,true);
203             }
204         }
205
206         internal void PrepareHeaders(bool allowUnicode) {
207             contentType.PersistIfNeeded((HeaderCollection)Headers, false);
208             headers.InternalSet(MailHeaderInfo.GetString(MailHeaderID.ContentType), contentType.Encode(allowUnicode));
209
210             if (contentDisposition != null) {
211                 contentDisposition.PersistIfNeeded((HeaderCollection)Headers, false);
212                 headers.InternalSet(MailHeaderInfo.GetString(MailHeaderID.ContentDisposition), 
213                     contentDisposition.Encode(allowUnicode));
214             }
215         }
216
217         internal virtual void Send(BaseWriter writer, bool allowUnicode) { 
218             throw new NotImplementedException(); 
219         }
220         
221         internal virtual IAsyncResult BeginSend(BaseWriter writer, AsyncCallback callback, 
222             bool allowUnicode, object state) {
223             throw new NotImplementedException(); 
224         }
225  
226         internal void EndSend(IAsyncResult asyncResult) {
227
228             if (asyncResult == null) {
229                 throw new ArgumentNullException("asyncResult");
230             }
231
232             LazyAsyncResult castedAsyncResult = asyncResult as MimePartAsyncResult;
233
234             if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) {
235                 throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult");
236             }
237
238             if (castedAsyncResult.EndCalled) {
239                 throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndSend"));
240             }
241
242             castedAsyncResult.InternalWaitForCompletion();
243             castedAsyncResult.EndCalled = true;
244             if (castedAsyncResult.Result is Exception) {
245                 throw (Exception)castedAsyncResult.Result;
246             }
247         }   
248      
249         internal class MimePartAsyncResult: LazyAsyncResult {
250             internal MimePartAsyncResult(MimeBasePart part, object state, AsyncCallback callback):base(part,state,callback) {
251             }
252         }
253     }
254 }
255