/// <summary> /// Constructs ENVELOPE address structure. /// </summary> /// <param name="address">Mailbox address.</param> /// <returns></returns> private static string ConstructAddress(MailboxAddress address) { /* An address structure is a parenthesized list that describes an * electronic mail address. The fields of an address structure * are in the following order: personal name, [SMTP] * at-domain-list (source route), mailbox name, and host name. */ // NOTE: all header fields and parameters must in ENCODED form !!! StringBuilder retVal = new StringBuilder(); retVal.Append("("); // personal name retVal.Append(TextUtils.QuoteString(MimeUtils.EncodeHeaderField(address.DisplayName))); // source route, always NIL (not used nowdays) retVal.Append(" NIL"); // mailbox name retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(address.LocalPart))); // host name retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(address.Domain))); retVal.Append(")"); return(retVal.ToString()); }
/// <summary> /// Construct secified mime entity ENVELOPE string. /// </summary> /// <param name="entity">Mime entity.</param> /// <returns></returns> public static string ConstructEnvelope(MimeEntity entity) { /* RFC 3501 7.4.2 * ENVELOPE * A parenthesized list that describes the envelope structure of a * message. This is computed by the server by parsing the * [RFC-2822] header into the component parts, defaulting various * fields as necessary. * * The fields of the envelope structure are in the following * order: date, subject, from, sender, reply-to, to, cc, bcc, * in-reply-to, and message-id. The date, subject, in-reply-to, * and message-id fields are strings. The from, sender, reply-to, * to, cc, and bcc fields are parenthesized lists of address * structures. * * An address structure is a parenthesized list that describes an * electronic mail address. The fields of an address structure * are in the following order: personal name, [SMTP] * at-domain-list (source route), mailbox name, and host name. * * [RFC-2822] group syntax is indicated by a special form of * address structure in which the host name field is NIL. If the * mailbox name field is also NIL, this is an end of group marker * (semi-colon in RFC 822 syntax). If the mailbox name field is * non-NIL, this is a start of group marker, and the mailbox name * field holds the group name phrase. * * If the Date, Subject, In-Reply-To, and Message-ID header lines * are absent in the [RFC-2822] header, the corresponding member * of the envelope is NIL; if these header lines are present but * empty the corresponding member of the envelope is the empty * string. * * Note: some servers may return a NIL envelope member in the * "present but empty" case. Clients SHOULD treat NIL and * empty string as identical. * * Note: [RFC-2822] requires that all messages have a valid * Date header. Therefore, the date member in the envelope can * not be NIL or the empty string. * * Note: [RFC-2822] requires that the In-Reply-To and * Message-ID headers, if present, have non-empty content. * Therefore, the in-reply-to and message-id members in the * envelope can not be the empty string. * * If the From, To, cc, and bcc header lines are absent in the * [RFC-2822] header, or are present but empty, the corresponding * member of the envelope is NIL. * * If the Sender or Reply-To lines are absent in the [RFC-2822] * header, or are present but empty, the server sets the * corresponding member of the envelope to be the same value as * the from member (the client is not expected to know to do * this). * * Note: [RFC-2822] requires that all messages have a valid * From header. Therefore, the from, sender, and reply-to * members in the envelope can not be NIL. * * ENVELOPE ("date" "subject" from sender reply-to to cc bcc "in-reply-to" "messageID") */ // NOTE: all header fields and parameters must in ENCODED form !!! StringBuilder retVal = new StringBuilder(); retVal.Append("("); // date if (entity.Header.Contains("Date:")) { retVal.Append(TextUtils.QuoteString(MimeUtils.DateTimeToRfc2822(entity.Date))); } else { retVal.Append("NIL"); } // subject if (entity.Subject != null) { retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(entity.Subject))); } else { retVal.Append(" NIL"); } // from if (entity.From != null && entity.From.Count > 0) { retVal.Append(" " + ConstructAddresses(entity.From)); } else { retVal.Append(" NIL"); } // sender // NOTE: There is confusing part, according rfc 2822 Sender: is MailboxAddress and not AddressList. if (entity.Sender != null) { retVal.Append(" ("); retVal.Append(ConstructAddress(entity.Sender)); retVal.Append(")"); } else if (entity.From != null) { retVal.Append(" " + ConstructAddresses(entity.From)); } else { retVal.Append(" NIL"); } // reply-to if (entity.ReplyTo != null) { retVal.Append(" " + ConstructAddresses(entity.ReplyTo)); } else if (entity.From != null) { retVal.Append(" " + ConstructAddresses(entity.From)); } else { retVal.Append(" NIL"); } // to if (entity.To != null && entity.To.Count > 0) { retVal.Append(" " + ConstructAddresses(entity.To)); } else { retVal.Append(" NIL"); } // cc if (entity.Cc != null && entity.Cc.Count > 0) { retVal.Append(" " + ConstructAddresses(entity.Cc)); } else { retVal.Append(" NIL"); } // bcc if (entity.Bcc != null && entity.Bcc.Count > 0) { retVal.Append(" " + ConstructAddresses(entity.Bcc)); } else { retVal.Append(" NIL"); } // in-reply-to if (entity.InReplyTo != null) { retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(entity.InReplyTo))); } else { retVal.Append(" NIL"); } // message-id if (entity.MessageID != null) { retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(entity.MessageID))); } else { retVal.Append(" NIL"); } retVal.Append(")"); return(retVal.ToString()); }
/// <summary> /// Constructs specified entity and it's childentities bodystructure string. /// </summary> /// <param name="entity">Mime entity.</param> /// <param name="bodystructure">Specifies if to construct BODY or BODYSTRUCTURE.</param> /// <returns></returns> private static string ConstructParts(MimeEntity entity, bool bodystructure) { /* RFC 3501 7.4.2 BODYSTRUCTURE * BODY A form of BODYSTRUCTURE without extension data. * * A parenthesized list that describes the [MIME-IMB] body * structure of a message. This is computed by the server by * parsing the [MIME-IMB] header fields, defaulting various fields * as necessary. * * For example, a simple text message of 48 lines and 2279 octets * can have a body structure of: ("TEXT" "PLAIN" ("CHARSET" * "US-ASCII") NIL NIL "7BIT" 2279 48) * * Multiple parts are indicated by parenthesis nesting. Instead * of a body type as the first element of the parenthesized list, * there is a sequence of one or more nested body structures. The * second element of the parenthesized list is the multipart * subtype (mixed, digest, parallel, alternative, etc.). * * For example, a two part message consisting of a text and a * BASE64-encoded text attachment can have a body structure of: * (("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 * 23)("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") * "<*****@*****.**>" "Compiler diff" * "BASE64" 4554 73) "MIXED") * * Extension data follows the multipart subtype. Extension data * is never returned with the BODY fetch, but can be returned with * a BODYSTRUCTURE fetch. Extension data, if present, MUST be in * the defined order. The extension data of a multipart body part * are in the following order: * * body parameter parenthesized list * A parenthesized list of attribute/value pairs [e.g., ("foo" * "bar" "baz" "rag") where "bar" is the value of "foo", and * "rag" is the value of "baz"] as defined in [MIME-IMB]. * * body disposition * A parenthesized list, consisting of a disposition type * string, followed by a parenthesized list of disposition * attribute/value pairs as defined in [DISPOSITION]. * * body language * A string or parenthesized list giving the body language * value as defined in [LANGUAGE-TAGS]. * * body location * A string list giving the body content URI as defined in [LOCATION]. * * Any following extension data are not yet defined in this * version of the protocol. Such extension data can consist of * zero or more NILs, strings, numbers, or potentially nested * parenthesized lists of such data. Client implementations that * do a BODYSTRUCTURE fetch MUST be prepared to accept such * extension data. Server implementations MUST NOT send such * extension data until it has been defined by a revision of this * protocol. * * The basic fields of a non-multipart body part are in the * following order: * * body type * A string giving the content media type name as defined in [MIME-IMB]. * * body subtype * A string giving the content subtype name as defined in [MIME-IMB]. * * body parameter parenthesized list * A parenthesized list of attribute/value pairs [e.g., ("foo" * "bar" "baz" "rag") where "bar" is the value of "foo" and * "rag" is the value of "baz"] as defined in [MIME-IMB]. * * body id * A string giving the content id as defined in [MIME-IMB]. * * body description * A string giving the content description as defined in [MIME-IMB]. * * body encoding * A string giving the content transfer encoding as defined in [MIME-IMB]. * * body size * A number giving the size of the body in octets. Note that * this size is the size in its transfer encoding and not the * resulting size after any decoding. * * A body type of type MESSAGE and subtype RFC822 contains, * immediately after the basic fields, the envelope structure, * body structure, and size in text lines of the encapsulated * message. * * A body type of type TEXT contains, immediately after the basic * fields, the size of the body in text lines. Note that this * size is the size in its content transfer encoding and not the * resulting size after any decoding. * * Extension data follows the basic fields and the type-specific * fields listed above. Extension data is never returned with the * BODY fetch, but can be returned with a BODYSTRUCTURE fetch. * Extension data, if present, MUST be in the defined order. * * The extension data of a non-multipart body part are in the * following order: * * body MD5 * A string giving the body MD5 value as defined in [MD5]. * * body disposition * A parenthesized list with the same content and function as * the body disposition for a multipart body part. * * body language * A string or parenthesized list giving the body language * value as defined in [LANGUAGE-TAGS]. * * body location * A string list giving the body content URI as defined in [LOCATION]. * * Any following extension data are not yet defined in this * version of the protocol, and would be as described above under * multipart extension data. * * * // We don't construct extention fields like rfc says: * Server implementations MUST NOT send such * extension data until it has been defined by a revision of this * protocol. * * * contentTypeMainMediaType - Example: 'TEXT' * contentTypeSubMediaType - Example: 'PLAIN' * conentTypeParameters - Example: '("CHARSET" "iso-8859-1" ...)' * contentID - Content-ID: header field value. * contentDescription - Content-Description: header field value. * contentEncoding - Content-Transfer-Encoding: header field value. * contentSize - mimeEntity ENCODED data size * [envelope] - NOTE: included only if contentType = "message" !!! * [contentLines] - number of ENCODED data lines. NOTE: included only if contentType = "text" !!! * * // Basic fields for multipart * (nestedMimeEntries) contentTypeSubMediaType * * // Basic fields for non-multipart * contentTypeMainMediaType contentTypeSubMediaType (conentTypeParameters) contentID contentDescription contentEncoding contentSize [envelope] [contentLine] * */ StringBuilder retVal = new StringBuilder(); // Multipart message if ((entity.ContentType & MediaType_enum.Multipart) != 0) { retVal.Append("("); // Construct child entities. foreach (MimeEntity childEntity in entity.ChildEntities) { // Construct child entity. This can be multipart or non multipart. retVal.Append(ConstructParts(childEntity, bodystructure)); } // Add contentTypeSubMediaType string contentType = entity.ContentTypeString.Split(';')[0]; if (contentType.Split('/').Length == 2) { retVal.Append(" \"" + contentType.Split('/')[1].Replace(";", "") + "\""); } else { retVal.Append(" NIL"); } retVal.Append(")"); } // Single part message else { retVal.Append("("); // NOTE: all header fields and parameters must in ENCODED form !!! // Add contentTypeMainMediaType if (entity.ContentTypeString != null) { string contentType = entity.ContentTypeString.Split(';')[0]; if (contentType.Split('/').Length == 2) { retVal.Append("\"" + entity.ContentTypeString.Split('/')[0] + "\""); } else { retVal.Append("NIL"); } } else { retVal.Append("NIL"); } // contentTypeSubMediaType if (entity.ContentTypeString != null) { string contentType = entity.ContentTypeString.Split(';')[0]; if (contentType.Split('/').Length == 2) { retVal.Append(" \"" + contentType.Split('/')[1].Replace(";", "") + "\""); } else { retVal.Append(" NIL"); } } else { retVal.Append(" NIL"); } // conentTypeParameters - Syntax: {("name" SP "value" *(SP "name" SP "value"))} if (entity.ContentTypeString != null) { ParametizedHeaderField contentTypeParameters = new ParametizedHeaderField(entity.Header.GetFirst("Content-Type:")); if (contentTypeParameters.Parameters.Count > 0) { retVal.Append(" ("); bool first = true; foreach (HeaderFieldParameter param in contentTypeParameters.Parameters) { // For first item, don't add SP if (!first) { retVal.Append(" "); } else { // Clear first flag first = false; } retVal.Append("\"" + param.Name + "\" \"" + MimeUtils.EncodeHeaderField(param.Value) + "\""); } retVal.Append(")"); } else { retVal.Append(" NIL"); } } else { retVal.Append(" NIL"); } // contentID string contentID = entity.ContentID; if (contentID != null) { retVal.Append(" \"" + MimeUtils.EncodeHeaderField(contentID) + "\""); } else { retVal.Append(" NIL"); } // contentDescription string contentDescription = entity.ContentDescription; if (contentDescription != null) { retVal.Append(" \"" + MimeUtils.EncodeHeaderField(contentDescription) + "\""); } else { retVal.Append(" NIL"); } // contentEncoding HeaderField contentEncoding = entity.Header.GetFirst("Content-Transfer-Encoding:"); if (contentEncoding != null) { retVal.Append(" \"" + MimeUtils.EncodeHeaderField(contentEncoding.Value) + "\""); } else { // If not specified, then must be 7bit. retVal.Append(" \"7bit\""); } // contentSize if (entity.DataEncoded != null) { retVal.Append(" " + entity.DataEncoded.Length); } else { retVal.Append(" 0"); } // envelope ---> FOR ContentType: message/rfc822 ONLY ### if ((entity.ContentType & MediaType_enum.Message_rfc822) != 0) { retVal.Append(" " + IMAP_Envelope.ConstructEnvelope(entity)); // TODO: BODYSTRUCTURE,LINES } // contentLines ---> FOR ContentType: text/xxx ONLY ### if ((entity.ContentType & MediaType_enum.Text) != 0) { if (entity.DataEncoded != null) { long lineCount = 0; StreamLineReader r = new StreamLineReader(new MemoryStream(entity.DataEncoded)); byte[] line = r.ReadLine(); while (line != null) { lineCount++; line = r.ReadLine(); } retVal.Append(" " + lineCount); } else { retVal.Append(" 0"); } } retVal.Append(")"); } return(retVal.ToString()); }