1 using System;
2 using System.IO;
3 using System.Text;
4 using System.Collections;
5 using System.Globalization;
6 using System.Net.Mail;
7 
8 namespace System.Net.Mime
9 {
10     /// <summary>
11     /// Summary description for MimePart.
12     /// </summary>
13     internal class MimePart: MimeBasePart,IDisposable
14     {
15         Stream stream = null;
16         bool streamSet = false;
17         bool streamUsedOnce = false;
18         AsyncCallback readCallback;
19         AsyncCallback writeCallback;
20         const int maxBufferSize = 0x4400;  //seems optimal for send based on perf analysis
21 
MimePart()22         internal MimePart()
23         {
24         }
25 
Dispose()26         public void Dispose(){
27             if (stream != null) {
28                 stream.Close();
29             }
30         }
31 
32         internal Stream Stream {
33             get {
34                 return stream;
35             }
36         }
37 
38 
39         internal ContentDisposition ContentDisposition{
40             get{
41                 return contentDisposition;
42             }
43             set{
44                 contentDisposition = value;
45                 if(value == null){
46                     ((HeaderCollection)Headers).InternalRemove(MailHeaderInfo.GetString(MailHeaderID.ContentDisposition));
47                 }
48                 else{
49                     contentDisposition.PersistIfNeeded((HeaderCollection)Headers,true);
50                 }
51             }
52         }
53 
54         internal TransferEncoding TransferEncoding {
55             get {
56                 string value = Headers[MailHeaderInfo.GetString(MailHeaderID.ContentTransferEncoding)];
57                 if (value.Equals("base64", StringComparison.OrdinalIgnoreCase))
58                     return TransferEncoding.Base64;
59                 else if (value.Equals("quoted-printable", StringComparison.OrdinalIgnoreCase))
60                     return TransferEncoding.QuotedPrintable;
61                 else if (value.Equals("7bit", StringComparison.OrdinalIgnoreCase))
62                     return TransferEncoding.SevenBit;
63                 else if (value.Equals("8bit", StringComparison.OrdinalIgnoreCase))
64                     return TransferEncoding.EightBit;
65                 else
66                     return TransferEncoding.Unknown;
67             }
68             set {
69                 //QFE 4554
70                 if (value == TransferEncoding.Base64) {
71                     Headers[MailHeaderInfo.GetString(MailHeaderID.ContentTransferEncoding)] = "base64";
72                 }
73                 else if (value == TransferEncoding.QuotedPrintable) {
74                     Headers[MailHeaderInfo.GetString(MailHeaderID.ContentTransferEncoding)] = "quoted-printable";
75                 }
76                 else if (value == TransferEncoding.SevenBit) {
77                     Headers[MailHeaderInfo.GetString(MailHeaderID.ContentTransferEncoding)] = "7bit";
78                 }
79                 else if (value == TransferEncoding.EightBit) {
80                     Headers[MailHeaderInfo.GetString(MailHeaderID.ContentTransferEncoding)] = "8bit";
81                 }
82                 else {
83                     throw new NotSupportedException(SR.GetString(SR.MimeTransferEncodingNotSupported, value));
84                 }
85             }
86         }
87 
SetContent(Stream stream)88         internal void SetContent(Stream stream){
89             if (stream == null) {
90                 throw new ArgumentNullException("stream");
91             }
92 
93             if (streamSet) {
94                 this.stream.Close();
95                 this.stream = null;
96                 streamSet = false;
97             }
98 
99             this.stream = stream;
100             streamSet = true;
101             streamUsedOnce = false;
102             TransferEncoding = TransferEncoding.Base64;
103         }
104 
SetContent(Stream stream, string name, string mimeType)105         internal void SetContent(Stream stream, string name, string mimeType) {
106             if (stream == null) {
107                 throw new ArgumentNullException("stream");
108             }
109 
110             if (mimeType != null && mimeType != string.Empty) {
111                 contentType = new ContentType(mimeType);
112             }
113             if (name != null && name != string.Empty) {
114                 ContentType.Name = name;
115             }
116             SetContent(stream);
117         }
118 
119 
SetContent(Stream stream, ContentType contentType)120         internal void SetContent(Stream stream, ContentType contentType) {
121             if (stream == null) {
122                 throw new ArgumentNullException("stream");
123             }
124             this.contentType = contentType;
125             SetContent(stream);
126         }
127 
128 
Complete(IAsyncResult result, Exception e)129         internal void Complete(IAsyncResult result, Exception e){
130             //if we already completed and we got called again,
131             //it mean's that there was an exception in the callback and we
132             //should just rethrow it.
133 
134             MimePartContext context = (MimePartContext)result.AsyncState;
135             if (context.completed) {
136                 throw e;
137             }
138 
139             try{
140                 if(context.outputStream != null){
141                     context.outputStream.Close();
142                 }
143             }
144             catch(Exception ex){
145                 if (e == null) {
146                     e = ex;
147                 }
148             }
149             context.completed = true;
150             context.result.InvokeCallback(e);
151         }
152 
153 
ReadCallback(IAsyncResult result)154         internal void ReadCallback(IAsyncResult result)
155         {
156             if (result.CompletedSynchronously ) {
157                 return;
158             }
159 
160             ((MimePartContext)result.AsyncState).completedSynchronously = false;
161 
162             try {
163                 ReadCallbackHandler(result);
164             }
165             catch(Exception e){
166                 Complete(result,e);
167             }
168         }
169 
170 
ReadCallbackHandler(IAsyncResult result)171         internal void ReadCallbackHandler(IAsyncResult result){
172             MimePartContext context = (MimePartContext)result.AsyncState;
173             context.bytesLeft = Stream.EndRead(result);
174             if (context.bytesLeft > 0) {
175                 IAsyncResult writeResult = context.outputStream.BeginWrite(context.buffer, 0, context.bytesLeft, writeCallback, context);
176                 if (writeResult.CompletedSynchronously) {
177                     WriteCallbackHandler(writeResult);
178                 }
179             }
180             else {
181                 Complete(result,null);
182             }
183         }
184 
185 
WriteCallback(IAsyncResult result)186         internal void WriteCallback(IAsyncResult result)
187         {
188             if (result.CompletedSynchronously ) {
189                 return;
190             }
191 
192             ((MimePartContext)result.AsyncState).completedSynchronously = false;
193 
194             try {
195                 WriteCallbackHandler(result);
196             }
197             catch (Exception e) {
198                 Complete(result,e);
199             }
200         }
201 
202 
203 
WriteCallbackHandler(IAsyncResult result)204         internal void WriteCallbackHandler(IAsyncResult result){
205             MimePartContext context = (MimePartContext)result.AsyncState;
206             context.outputStream.EndWrite(result);
207             IAsyncResult readResult = Stream.BeginRead(context.buffer, 0, context.buffer.Length, readCallback, context);
208             if (readResult.CompletedSynchronously) {
209                 ReadCallbackHandler(readResult);
210             }
211         }
212 
213 
GetEncodedStream(Stream stream)214         internal Stream GetEncodedStream(Stream stream){
215             Stream outputStream = stream;
216 
217             if (TransferEncoding == TransferEncoding.Base64) {
218                 outputStream = new Base64Stream(outputStream, new Base64WriteStateInfo());
219             }
220             else if (TransferEncoding == TransferEncoding.QuotedPrintable) {
221                 outputStream = new QuotedPrintableStream(outputStream,true);
222             }
223             else if (TransferEncoding == TransferEncoding.SevenBit || TransferEncoding == TransferEncoding.EightBit) {
224                 outputStream = new EightBitStream(outputStream);
225             }
226 
227             return outputStream;
228         }
229 
ContentStreamCallbackHandler(IAsyncResult result)230         internal void ContentStreamCallbackHandler(IAsyncResult result){
231 
232             MimePartContext context = (MimePartContext)result.AsyncState;
233             Stream outputStream = context.writer.EndGetContentStream(result);
234             context.outputStream = GetEncodedStream(outputStream);
235 
236             readCallback = new AsyncCallback(ReadCallback);
237             writeCallback = new AsyncCallback(WriteCallback);
238             IAsyncResult readResult = Stream.BeginRead(context.buffer, 0, context.buffer.Length,readCallback, context);
239             if (readResult.CompletedSynchronously) {
240                 ReadCallbackHandler(readResult);
241             }
242         }
243 
244 
ContentStreamCallback(IAsyncResult result)245         internal void ContentStreamCallback(IAsyncResult result)
246         {
247             if (result.CompletedSynchronously ) {
248                 return;
249             }
250 
251             ((MimePartContext)result.AsyncState).completedSynchronously = false;
252 
253             try{
254                 ContentStreamCallbackHandler(result);
255             }
256             catch (Exception e) {
257                 Complete(result,e);
258             }
259         }
260 
261 
262         internal class MimePartContext
263         {
MimePartContext(BaseWriter writer, LazyAsyncResult result)264             internal MimePartContext(BaseWriter writer, LazyAsyncResult result)
265             {
266                 this.writer = writer;
267                 this.result = result;
268                 buffer = new byte[maxBufferSize];
269             }
270 
271             internal Stream outputStream;
272             internal LazyAsyncResult result;
273             internal int bytesLeft;
274             internal BaseWriter writer;
275             internal byte[] buffer;
276             internal bool completed;
277             internal bool completedSynchronously = true;
278         }
279 
280 
BeginSend(BaseWriter writer, AsyncCallback callback, bool allowUnicode, object state)281         internal override IAsyncResult BeginSend(BaseWriter writer, AsyncCallback callback, bool allowUnicode,
282             object state)
283         {
284             PrepareHeaders(allowUnicode);
285             writer.WriteHeaders(Headers, allowUnicode);
286             MimePartAsyncResult result = new MimePartAsyncResult(this, state, callback);
287             MimePartContext context = new MimePartContext(writer, result);
288 
289             ResetStream();
290             streamUsedOnce = true;
291             IAsyncResult contentResult = writer.BeginGetContentStream(new AsyncCallback(ContentStreamCallback),context);
292             if (contentResult.CompletedSynchronously) {
293                 ContentStreamCallbackHandler(contentResult);
294             }
295             return result;
296         }
297 
298 
Send(BaseWriter writer, bool allowUnicode)299         internal override void Send(BaseWriter writer, bool allowUnicode) {
300             if (Stream != null) {
301                 byte[] buffer = new byte[maxBufferSize];
302 
303                 PrepareHeaders(allowUnicode);
304                 writer.WriteHeaders(Headers, allowUnicode);
305 
306                 Stream outputStream = writer.GetContentStream();
307                 outputStream = GetEncodedStream(outputStream);
308 
309                 int read;
310 
311                 ResetStream();
312                 streamUsedOnce = true;
313 
314                 while ((read = Stream.Read(buffer, 0, maxBufferSize)) > 0) {
315                     outputStream.Write(buffer, 0, read);
316                 }
317                 outputStream.Close();
318             }
319         }
320 
321 
322         //Ensures that if we've used the stream once, we will either reset it to the origin, or throw.
ResetStream()323         internal void ResetStream(){
324             if (streamUsedOnce) {
325                 if (Stream.CanSeek) {
326                     Stream.Seek(0,SeekOrigin.Begin);
327                     streamUsedOnce = false;
328                 }
329                 else{
330                     throw new InvalidOperationException(SR.GetString(SR.MimePartCantResetStream));
331                 }
332             }
333         }
334     }
335 }
336