1 using System;
2 using System.IO;
3 using System.Collections.Specialized;
4 using System.Net.Mime;
5 using System.Text;
6 
7 namespace System.Net.Mail
8 {
9     /// <summary>
10     /// Summary description for MailMessage.
11     /// </summary>
12 
13 
14 
15 
16 
17     //rfc3461
18     [Flags]
19     public enum DeliveryNotificationOptions  {
20         None = 0, OnSuccess = 1, OnFailure = 2, Delay = 4, Never = (int)0x08000000
21     }
22 
23 
24     public class MailMessage:IDisposable
25     {
26         private AlternateViewCollection views;
27         AttachmentCollection attachments;
28         AlternateView bodyView = null;
29         string body = String.Empty;
30         Encoding bodyEncoding;
31         TransferEncoding bodyTransferEncoding = TransferEncoding.Unknown;
32         bool isBodyHtml = false;
33         bool disposed = false;
34         Message message;
35         DeliveryNotificationOptions deliveryStatusNotification = DeliveryNotificationOptions.None;
36 
MailMessage()37         public MailMessage() {
38             message = new Message();
39             if(Logging.On)Logging.Associate(Logging.Web, this, message);
40             string from = SmtpClient.MailConfiguration.Smtp.From;
41 
42             if (from != null && from.Length > 0) {
43                 message.From = new MailAddress(from);
44             }
45         }
46 
MailMessage(string from, string to)47         public MailMessage(string from, string to) {
48             if (from == null)
49                 throw new ArgumentNullException("from");
50 
51             if (to == null)
52                 throw new ArgumentNullException("to");
53 
54             if (from == String.Empty)
55                 throw new ArgumentException(SR.GetString(SR.net_emptystringcall,"from"), "from");
56 
57             if (to == String.Empty)
58                 throw new ArgumentException(SR.GetString(SR.net_emptystringcall,"to"), "to");
59 
60             message = new Message(from,to);
61             if(Logging.On)Logging.Associate(Logging.Web, this, message);
62         }
63 
64 
MailMessage(string from, string to, string subject, string body)65         public MailMessage(string from, string to, string subject, string body):this(from,to) {
66             Subject = subject;
67             Body = body;
68         }
69 
70 
MailMessage(MailAddress from, MailAddress to)71         public MailMessage(MailAddress from, MailAddress to) {
72             if (from == null)
73                 throw new ArgumentNullException("from");
74 
75             if (to == null)
76                 throw new ArgumentNullException("to");
77 
78             message = new Message(from,to);
79         }
80 
81 
82         public MailAddress From {
83             get {
84                 return message.From;
85             }
86             set {
87                 if (value == null) {
88                     throw new ArgumentNullException("value");
89                 }
90                 message.From = value;
91             }
92         }
93 
94         public MailAddress Sender {
95             get {
96                 return message.Sender;
97             }
98             set {
99                 message.Sender = value;
100             }
101         }
102 
103         [Obsolete("ReplyTo is obsoleted for this type.  Please use ReplyToList instead which can accept multiple addresses. http://go.microsoft.com/fwlink/?linkid=14202")]
104         public MailAddress ReplyTo {
105             get {
106                 return message.ReplyTo;
107             }
108             set {
109                 message.ReplyTo = value;
110             }
111         }
112 
113         public MailAddressCollection ReplyToList {
114             get {
115                 return message.ReplyToList;
116             }
117         }
118 
119         public MailAddressCollection To {
120             get {
121                 return message.To;
122             }
123         }
124 
125         public MailAddressCollection Bcc {
126             get {
127                 return message.Bcc;
128             }
129         }
130 
131         public MailAddressCollection CC {
132             get {
133                 return message.CC;
134             }
135         }
136 
137         public MailPriority Priority{
138             get {
139                 return message.Priority;
140             }
141             set{
142                 message.Priority = value;
143             }
144         }
145 
146         public DeliveryNotificationOptions  DeliveryNotificationOptions {
147             get{
148                 return deliveryStatusNotification;
149             }
150             set{
151                 if (7 < (uint)value && value != DeliveryNotificationOptions.Never) {
152                     throw new ArgumentOutOfRangeException("value");
153                 }
154                 deliveryStatusNotification = value;
155             }
156         }
157 
158         public string Subject {
159             get {
160                 return (message.Subject != null ? message.Subject : String.Empty);
161             }
162             set {
163                message.Subject = value;
164             }
165         }
166 
167         public Encoding SubjectEncoding {
168             get {
169                 return message.SubjectEncoding;
170             }
171             set {
172                 message.SubjectEncoding = value;
173             }
174         }
175 
176         public NameValueCollection Headers {
177             get {
178                 return message.Headers;
179             }
180         }
181 
182         public Encoding HeadersEncoding {
183             get {
184                 return message.HeadersEncoding;
185             }
186             set {
187                 message.HeadersEncoding = value;
188             }
189         }
190 
191         public string Body {
192             get {
193                 return (body != null ? body : String.Empty);
194             }
195 
196             set{
197                 body = value;
198 
199                 if (bodyEncoding == null && body != null) {
200                     if (MimeBasePart.IsAscii(body,true)) {
201                         bodyEncoding = Text.Encoding.ASCII;
202                     }
203                     else {
204                         bodyEncoding = Text.Encoding.GetEncoding(MimeBasePart.defaultCharSet);
205                     }
206                 }
207             }
208         }
209 
210         public Encoding BodyEncoding {
211             get {
212                 return bodyEncoding;
213             }
214             set {
215                 bodyEncoding = value;
216             }
217         }
218 
219         public TransferEncoding BodyTransferEncoding {
220             get {
221                 return bodyTransferEncoding;
222             }
223             set {
224                 bodyTransferEncoding = value;
225             }
226         }
227 
228 
229         public bool IsBodyHtml{
230             get{
231                 return isBodyHtml;
232             }
233             set{
234                 isBodyHtml = value;
235             }
236         }
237 
238 
239         public AttachmentCollection Attachments {
240             get {
241                 if (disposed) {
242                     throw new ObjectDisposedException(this.GetType().FullName);
243                 }
244 
245                 if (attachments == null) {
246                     attachments = new AttachmentCollection();
247                 }
248                 return attachments;
249             }
250         }
251         public AlternateViewCollection AlternateViews {
252             get {
253                 if (disposed) {
254                     throw new ObjectDisposedException(this.GetType().FullName);
255                 }
256 
257                 if (views == null) {
258                     views = new AlternateViewCollection();
259                 }
260 
261                 return views;
262             }
263         }
264 
Dispose()265         public void Dispose()
266         {
267             Dispose(true);
268         }
269 
Dispose(bool disposing)270         protected virtual void Dispose(bool disposing)
271         {
272             if (disposing && !disposed)
273             {
274                 disposed = true;
275 
276                 if(views != null){
277                     views.Dispose();
278                 }
279                 if(attachments != null){
280                     attachments.Dispose();
281                 }
282                 if(bodyView != null){
283                     bodyView.Dispose();
284                 }
285             }
286         }
287 
288 
SetContent(bool allowUnicode)289         private void SetContent(bool allowUnicode) {
290 
291             //the attachments may have changed, so we need to reset the message
292             if(bodyView != null){
293                 bodyView.Dispose();
294                 bodyView = null;
295             }
296 
297             if (AlternateViews.Count == 0 && Attachments.Count == 0) {
298                 if (!string.IsNullOrEmpty(body)) {
299                     bodyView = AlternateView.CreateAlternateViewFromString(body, bodyEncoding, (isBodyHtml?MediaTypeNames.Text.Html:null));
300                     message.Content = bodyView.MimePart;
301                 }
302             }
303             else if (AlternateViews.Count == 0 && Attachments.Count > 0){
304                 MimeMultiPart part = new MimeMultiPart(MimeMultiPartType.Mixed);
305 
306                 if (!string.IsNullOrEmpty(body)) {
307                     bodyView = AlternateView.CreateAlternateViewFromString(body, bodyEncoding, (isBodyHtml?MediaTypeNames.Text.Html:null));
308                 }
309                 else{
310                     bodyView = AlternateView.CreateAlternateViewFromString(string.Empty);
311                 }
312 
313                 part.Parts.Add(bodyView.MimePart);
314 
315                 foreach (Attachment attachment in Attachments) {
316                     if(attachment != null){
317                         //ensure we can read from the stream.
318                         attachment.PrepareForSending(allowUnicode);
319                         part.Parts.Add(attachment.MimePart);
320                     }
321                 }
322                 message.Content = part;
323             }
324             else{
325                 //
326                 // DTS issue 610361 - we should not unnecessarily use Multipart/Mixed
327                 // When there is no attachement and all the alternative views are of "Alternative" types.
328                 //
329                 MimeMultiPart part = null;
330                 MimeMultiPart viewsPart = new MimeMultiPart(MimeMultiPartType.Alternative);
331 
332                 if (!string.IsNullOrEmpty(body)) {
333                     bodyView = AlternateView.CreateAlternateViewFromString(body, bodyEncoding, null);
334                     viewsPart.Parts.Add(bodyView.MimePart);
335                 }
336 
337                 foreach (AlternateView view in AlternateViews)
338                 {
339                     //ensure we can read from the stream.
340                     if (view != null) {
341                         view.PrepareForSending(allowUnicode);
342                         if (view.LinkedResources.Count > 0)
343                         {
344                             MimeMultiPart wholeView = new MimeMultiPart(MimeMultiPartType.Related);
345                             wholeView.ContentType.Parameters["type"] = view.ContentType.MediaType;
346                             wholeView.ContentLocation = view.MimePart.ContentLocation;
347                             wholeView.Parts.Add(view.MimePart);
348 
349                             foreach (LinkedResource resource in view.LinkedResources)
350                             {
351                                 //ensure we can read from the stream.
352                                 resource.PrepareForSending(allowUnicode);
353 
354                                 wholeView.Parts.Add(resource.MimePart);
355                             }
356                             viewsPart.Parts.Add(wholeView);
357                         }
358                         else
359                         {
360                             viewsPart.Parts.Add(view.MimePart);
361                         }
362                     }
363                 }
364 
365                 if (Attachments.Count > 0)
366                 {
367                     part = new MimeMultiPart(MimeMultiPartType.Mixed);
368                     part.Parts.Add(viewsPart);
369 
370                     MimeMultiPart attachmentsPart = new MimeMultiPart(MimeMultiPartType.Mixed);
371                     foreach (Attachment attachment in Attachments)
372                     {
373                         if (attachment != null)
374                         {
375                             //ensure we can read from the stream.
376                             attachment.PrepareForSending(allowUnicode);
377                             attachmentsPart.Parts.Add(attachment.MimePart);
378                         }
379                     }
380                     part.Parts.Add(attachmentsPart);
381                     message.Content = part;
382                 } // If there is no Attachement, AND only "1" Alternate View AND !!no body!!
383                   // then in fact, this is NOT a multipart region.
384                 else if (viewsPart.Parts.Count == 1 && string.IsNullOrEmpty(body))
385                 {
386                     message.Content = viewsPart.Parts[0];
387                 }
388                 else
389                 {
390                     message.Content = viewsPart;
391                 }
392             }
393 
394             if (bodyView != null && bodyTransferEncoding != TransferEncoding.Unknown)
395             {
396                 bodyView.TransferEncoding = bodyTransferEncoding;
397             }
398         }
399 
Send(BaseWriter writer, bool sendEnvelope, bool allowUnicode)400         internal void Send(BaseWriter writer, bool sendEnvelope, bool allowUnicode) {
401             SetContent(allowUnicode);
402             message.Send(writer, sendEnvelope, allowUnicode);
403         }
404 
BeginSend(BaseWriter writer, bool sendEnvelope, bool allowUnicode, AsyncCallback callback, object state)405         internal IAsyncResult BeginSend(BaseWriter writer, bool sendEnvelope, bool allowUnicode,
406             AsyncCallback callback, object state) {
407             SetContent(allowUnicode);
408             return message.BeginSend(writer, sendEnvelope, allowUnicode, callback, state);
409         }
410 
EndSend(IAsyncResult asyncResult)411         internal void EndSend(IAsyncResult asyncResult) {
412             message.EndSend(asyncResult);
413         }
414 
BuildDeliveryStatusNotificationString()415         internal string BuildDeliveryStatusNotificationString(){
416             if(deliveryStatusNotification != DeliveryNotificationOptions.None){
417                 StringBuilder s = new StringBuilder(" NOTIFY=");
418 
419                 bool oneSet = false;
420 
421                 //none
422                 if(deliveryStatusNotification == DeliveryNotificationOptions.Never){
423                     s.Append("NEVER");
424                     return s.ToString();
425                 }
426 
427                 if((((int)deliveryStatusNotification) & (int)DeliveryNotificationOptions.OnSuccess) > 0){
428                     s.Append("SUCCESS");
429                     oneSet = true;
430                 }
431                 if((((int)deliveryStatusNotification) & (int)DeliveryNotificationOptions.OnFailure) > 0){
432                     if(oneSet){
433                         s.Append(",");
434                     }
435                     s.Append("FAILURE");
436                     oneSet = true;
437                 }
438                 if((((int)deliveryStatusNotification) & (int)DeliveryNotificationOptions.Delay) > 0){
439                     if(oneSet){
440                         s.Append(",");
441                     }
442                     s.Append("DELAY");
443                 }
444                 return s.ToString();
445             }
446             return String.Empty;
447         }
448     }
449 }
450