/// <summary> /// Glue method to create a bodypart from a MIMEPart instance. /// </summary> /// <param name="mimePart">The MIMEPart instance to create the /// bodypart instance from.</param> /// <returns>An initialized instance of the Bodypart class.</returns> private static Bodypart BodypartFromMIME(MIMEPart mimePart) { NameValueCollection contentType = ParseMIMEField( mimePart.header["Content-Type"]); Bodypart p = new Bodypart(null); Match m = Regex.Match(contentType["value"], "(.+)/(.+)"); if (m.Success) { p.Type = ContentTypeMap.fromString(m.Groups[1].Value); p.Subtype = m.Groups[2].Value; } p.Encoding = ContentTransferEncodingMap.fromString( mimePart.header["Content-Transfer-Encoding"]); p.Id = mimePart.header["Content-Id"]; foreach (string k in contentType.AllKeys) { p.Parameters.Add(k, contentType[k]); } p.Size = mimePart.body.Length; if (mimePart.header["Content-Disposition"] != null) { NameValueCollection disposition = ParseMIMEField( mimePart.header["Content-Disposition"]); p.Disposition.Type = ContentDispositionTypeMap.fromString( disposition["value"]); p.Disposition.Filename = disposition["Filename"]; foreach (string k in disposition.AllKeys) { p.Disposition.Attributes.Add(k, disposition[k]); } } return(p); }
/// <summary> /// Reads the message body of a MIME multipart message. /// </summary> /// <param name="boundary">The boundary string which separates /// the different parts which make up the multipart-message</param> /// <param name="nested">True if recursive call</param> /// <returns>A list of the MIME parts composing the multipart /// message</returns> /// <remarks>Each MIME part consists of its own set of header /// fields and a body.</remarks> private MIMEPart[] ReadMultipartBody(string boundary, bool nested = false) { List <MIMEPart> parts = new List <MIMEPart>(); string s_boundary = "--" + boundary, e_boundary = "--" + boundary + "--"; /* skip everything up to the first boundary */ string response = GetResponse(); while (!response.StartsWith(s_boundary)) { response = GetResponse(); } /* read MIME parts enclosed in boundary strings */ while (response.StartsWith(s_boundary)) { MIMEPart part = new MIMEPart(); /* read content-header of part */ part.header = ReadMailHeader(); /* account for nested multipart content */ NameValueCollection contentType = ParseMIMEField( part.header["Content-Type"]); if (contentType["Boundary"] != null) { parts.AddRange(ReadMultipartBody(contentType["boundary"], true)); } /* read content-body of part */ while (!(response = GetResponse()).StartsWith(s_boundary)) { part.body = part.body + response + "\r\n"; } /* add MIME part to the list unless body is null which means the body was * nested multipart content */ if (part.body != null) { parts.Add(part); } /* if the boundary is actually the end boundary, we're done */ if (response.StartsWith(e_boundary)) { break; } } if (nested == false) { /* FETCH closing bracket may be last character of response */ if (!response.EndsWith(")")) { /* next read should return closing bracket from FETCH command then */ if ((response = GetResponse()) != ")") { throw new BadServerResponseException(response); } } } return(parts.ToArray()); }
/// <summary> /// Parses the body of a multipart MIME mail message. /// </summary> /// <param name="reader">An instance of the StringReader class initialized /// with a string containing the body of the mail message.</param> /// <param name="boundary">The boundary value as is present as part of /// the Content-Type header field in multipart mail messages.</param> /// <returns>An array of initialized MIMEPart instances representing /// the various parts of the MIME mail message.</returns> private static MIMEPart[] ParseMIMEParts(StringReader reader, string boundary) { List <MIMEPart> list = new List <MIMEPart>(); string start = "--" + boundary, end = "--" + boundary + "--", line; // Skip everything up to the first boundary while ((line = reader.ReadLine()) != null) { if (line.StartsWith(start)) { break; } } // Read MIME parts delimited by boundary strings while (line != null && line.StartsWith(start)) { MIMEPart p = new MIMEPart(); // Read the part header StringBuilder header = new StringBuilder(); while (!String.IsNullOrEmpty(line = reader.ReadLine())) { header.AppendLine(line); } p.header = ParseMailHeader(header.ToString()); // Account for nested multipart content NameValueCollection contentType = ParseMIMEField(p.header["Content-Type"]); if (contentType["Boundary"] != null) { list.AddRange(ParseMIMEParts(reader, contentType["boundary"])); } // Read the part body StringBuilder body = new StringBuilder(); while ((line = reader.ReadLine()) != null) { if (line.StartsWith(start)) { break; } body.AppendLine(line); } p.body = body.ToString(); // Add the MIME part to the list unless body is null which means the // body contained nested multipart content if (!String.IsNullOrEmpty(p.body)) { list.Add(p); } // If the boundary is actually the end boundary, we're done if (line == null || line.StartsWith(end)) { break; } } return(list.ToArray()); }
/// <summary> /// Adds the parts of a MIME multi-part message to an instance of the /// MailMessage class. MIME parts are either added to the AlternateViews /// or to the Attachments collections depending on their type. /// </summary> /// <param name="m">The MailMessage instance to operate on</param> /// <param name="parts">An array of MIME parts</param> private void AddMIMEPartsToMessage(MailMessage m, MIMEPart[] parts) { bool setBodyFields = false; for (int i = 0; i < parts.Length; i++) { MIMEPart p = parts[i]; NameValueCollection contentType = ParseMIMEField( p.header["Content-Type"]); string transferEnc = p.header["Content-Transfer-Encoding"] ?? "none"; Encoding encoding = Util.GetEncoding(contentType["Charset"]); byte[] bytes = encoding.GetBytes(p.body); /* decode content if it was encoded */ switch (transferEnc.ToLower()) { case "quoted-printable": bytes = encoding.GetBytes(Util.QPDecode(p.body, encoding)); break; case "base64": bytes = Util.Base64Decode(p.body); break; } /* Put the first MIME part that contains text into the Body fields of the * MailMessage instance */ if (setBodyFields == false && contentType["value"].ToLower().Contains("text")) { m.Body = encoding.GetString(bytes); m.BodyEncoding = encoding; m.IsBodyHtml = contentType["value"].ToLower() .Contains("text/html"); setBodyFields = true; continue; } NameValueCollection disposition = ParseMIMEField( p.header["Content-Disposition"] ?? ""); if (disposition["value"].ToLower() == "attachment") { m.Attachments.Add(CreateAttachment(p.header, bytes, disposition["filename"] ?? ("attachment" + i.ToString()))); } else { m.AlternateViews.Add(CreateAlternateView(p.header, bytes)); } } }
/// <summary> /// Glue method to create a bodypart from a MIMEPart instance. /// </summary> /// <param name="mimePart"> /// The MIMEPart instance to create the /// bodypart instance from. /// </param> /// <returns>An initialized instance of the Bodypart class.</returns> private static Bodypart BodypartFromMIME(MIMEPart mimePart) { var contentType = ParseMIMEField( mimePart.header["Content-Type"]); var p = new Bodypart(null); var m = Regex.Match(contentType["value"], "(.+)/(.+)"); if (m.Success) { p.Type = ContentTypeMap.fromString(m.Groups[1].Value); p.Subtype = m.Groups[2].Value; } p.Encoding = ContentTransferEncodingMap.fromString( mimePart.header["Content-Transfer-Encoding"]); p.Id = mimePart.header["Content-Id"]; foreach (var k in contentType.AllKeys) p.Parameters.Add(k, contentType[k]); p.Size = mimePart.body.Length; if (mimePart.header["Content-Disposition"] != null) { var disposition = ParseMIMEField( mimePart.header["Content-Disposition"]); p.Disposition.Type = ContentDispositionTypeMap.fromString( disposition["value"]); p.Disposition.Filename = disposition["Filename"]; foreach (var k in disposition.AllKeys) p.Disposition.Attributes.Add(k, disposition[k]); } return p; }
/// <summary> /// Parses the body of a multipart MIME mail message. /// </summary> /// <param name="reader"> /// An instance of the StringReader class initialized /// with a string containing the body of the mail message. /// </param> /// <param name="boundary"> /// The boundary value as is present as part of /// the Content-Type header field in multipart mail messages. /// </param> /// <returns> /// An array of initialized MIMEPart instances representing /// the various parts of the MIME mail message. /// </returns> private static MIMEPart[] ParseMIMEParts(StringReader reader, string boundary) { var list = new List<MIMEPart>(); string start = "--" + boundary, end = "--" + boundary + "--", line; // Skip everything up to the first boundary while ((line = reader.ReadLine()) != null) { if (line.StartsWith(start)) break; } // Read MIME parts delimited by boundary strings while (line != null && line.StartsWith(start)) { var p = new MIMEPart(); // Read the part header var header = new StringBuilder(); while (!string.IsNullOrEmpty(line = reader.ReadLine())) header.AppendLine(line); p.header = ParseMailHeader(header.ToString()); // Account for nested multipart content var contentType = ParseMIMEField(p.header["Content-Type"]); if (contentType["Boundary"] != null) list.AddRange(ParseMIMEParts(reader, contentType["boundary"])); // Read the part body var body = new StringBuilder(); while ((line = reader.ReadLine()) != null) { if (line.StartsWith(start)) break; body.AppendLine(line); } p.body = body.ToString(); // Add the MIME part to the list unless body is null which means the // body contained nested multipart content if (!string.IsNullOrEmpty(p.body)) list.Add(p); // If the boundary is actually the end boundary, we're done if (line == null || line.StartsWith(end)) break; } return list.ToArray(); }
/// <summary> /// Parses the body of a multipart MIME mail message. /// </summary> /// <param name="reader">An instance of the StringReader class initialized with a string /// containing the body of the mail message.</param> /// <param name="boundary">The boundary value as is present as part of the Content-Type header /// field in multipart mail messages.</param> /// <returns>An array of initialized MIMEPart instances representing the various parts of the /// MIME mail message.</returns> static MIMEPart[] ParseMIMEParts(StringReader reader, string boundary) { List<MIMEPart> list = new List<MIMEPart>(); string start = "--" + boundary, end = "--" + boundary + "--", line; // Skip everything up to the first boundary. while ((line = reader.ReadLine()) != null) { if (line.StartsWith(start)) break; } // Read the MIME parts which are delimited by boundary strings. while (line != null && line.StartsWith(start)) { MIMEPart p = new MIMEPart(); // Read the part header. StringBuilder header = new StringBuilder(); while (!String.IsNullOrEmpty(line = reader.ReadLine())) header.AppendLine(line); p.header = ParseMailHeader(header.ToString()); // Account for nested multipart content. NameValueCollection contentType = ParseMIMEField(p.header["Content-Type"]); if (contentType["Boundary"] != null) list.AddRange(ParseMIMEParts(reader, contentType["boundary"])); // Read the part body. StringBuilder body = new StringBuilder(); while ((line = reader.ReadLine()) != null) { if (line.StartsWith(start)) break; body.AppendLine(line); } p.body = body.ToString(); // Add the MIME part to the list unless body is null or empty which means the body // contained nested multipart content. if(p.body != null && p.body.Trim() != String.Empty) list.Add(p); // If this boundary is the end boundary, we're done. if (line == null || line.StartsWith(end)) break; } return list.ToArray(); }
/// <summary> /// Creates a new instance of the MailMessage class and initializes it using /// the specified header and body information. /// </summary> /// <param name="header">A collection of mail and MIME headers</param> /// <param name="body">The mail body. May be null in case the message /// is a MIME multi-part message in which case the MailMessage's body will /// be set to the body of the first MIME part.</param> /// <param name="parts">An array of MIME parts making up the message. If the /// message is not a MIME multi-part message, this can be set to null. /// </param> /// <returns>An initialized instance of the MailMessage class</returns> private MailMessage CreateMailmessage(NameValueCollection header, string body, MIMEPart[] parts) { MailMessage m = new MailMessage(); NameValueCollection contentType = ParseMIMEField( header["Content-Type"]); /* NameValueCollection throws an exception if adding an empty string as * value which can happen, if reading a mail message with an empty subject * for instance */ foreach (string key in header) { string value = header.GetValues(key)[0]; if (value != String.Empty) m.Headers.Add(key, value); } if (parts != null) { /* This takes care of setting the Body, BodyEncoding and IsBodyHtml fields also */ AddMIMEPartsToMessage(m, parts); } else { /* charset attribute should be part of content-type */ m.BodyEncoding = Util.GetEncoding(contentType["charset"]); m.Body = body; m.IsBodyHtml = contentType["value"].Contains("text/html"); } Match ma = Regex.Match(header["Subject"], @"=\?([A-Za-z0-9\-]+)"); if (ma.Success) { /* encoded-word subject */ m.SubjectEncoding = Util.GetEncoding( ma.Groups[1].Value); m.Subject = Util.DecodeWords(header["Subject"]); } else { m.SubjectEncoding = Encoding.ASCII; m.Subject = header["Subject"]; } m.Priority = header["Priority"] != null ? PriorityMapping[header["Priority"]] : MailPriority.Normal; SetAddressFields(m, header); return m; }
/// <summary> /// Reads the message body of a MIME multipart message. /// </summary> /// <param name="boundary">The boundary string which separates /// the different parts which make up the multipart-message</param> /// <param name="nested">True if recursive call</param> /// <returns>A list of the MIME parts composing the multipart /// message</returns> /// <remarks>Each MIME part consists of its own set of header /// fields and a body.</remarks> private MIMEPart[] ReadMultipartBody(string boundary, bool nested = false) { List<MIMEPart> parts = new List<MIMEPart>(); string s_boundary = "--" + boundary, e_boundary = "--" + boundary + "--"; /* skip everything up to the first boundary */ string response = GetResponse(); while (!response.StartsWith(s_boundary)) response = GetResponse(); /* read MIME parts enclosed in boundary strings */ while (response.StartsWith(s_boundary)) { MIMEPart part = new MIMEPart(); /* read content-header of part */ part.header = ReadMailHeader(); /* account for nested multipart content */ NameValueCollection contentType = ParseMIMEField( part.header["Content-Type"]); if (contentType["Boundary"] != null) parts.AddRange(ReadMultipartBody(contentType["boundary"], true)); /* read content-body of part */ while (!(response = GetResponse()).StartsWith(s_boundary)) part.body = part.body + response + "\r\n"; /* add MIME part to the list unless body is null which means the body was nested multipart content */ if(part.body != null) parts.Add(part); /* if the boundary is actually the end boundary, we're done */ if (response.StartsWith(e_boundary)) break; } if (nested == false) { /* FETCH closing bracket may be last character of response */ if (!response.EndsWith(")")) { /* next read should return closing bracket from FETCH command then */ if ((response = GetResponse()) != ")") throw new BadServerResponseException(response); } } return parts.ToArray(); }