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