/// <summary> /// Constructs a message from a byte array.<br/> /// <br/> /// The headers are always parsed, but if <paramref name="parseBody"/> is <see langword="false"/>, the body is not parsed. /// </summary> /// <param name="rawMessageContent">The byte array which is the message contents to parse</param> /// <param name="parseBody"> /// <see langword="true"/> if the body should be parsed, /// <see langword="false"/> if only headers should be parsed out of the <paramref name="rawMessageContent"/> byte array /// </param> /// <param name="parsingErrorHandler">(Optional) It is notifified when an error occurs while parsing something in the message. /// If it is not null, the handler handles the error on the specific element without stopping the message parsing process</param> public Message(byte[] rawMessageContent, bool parseBody, IParsingErrorHandler parsingErrorHandler = null) { RawMessage = rawMessageContent; // Find the headers and the body parts of the byte array MessageHeader headersTemp; byte[] body; HeaderExtractor.ExtractHeadersAndBody(rawMessageContent, out headersTemp, out body, parsingErrorHandler); // Set the Headers property Headers = headersTemp; // Should we also parse the body? if (parseBody) { // Parse the body into a MessagePart MessagePart = new MessagePart(body, Headers, parsingErrorHandler); } }
/// <summary> /// Used to construct the topmost message part /// </summary> /// <param name="rawBody">The body that needs to be parsed</param> /// <param name="headers">The headers that should be used from the message</param> /// <param name="parsingErrorHandler">(Optional) It is notifified when an error occurs while parsing something in the message. /// If it is not null, the handler handles the error on the specific element without stopping the message parsing process</param> /// <exception cref="ArgumentNullException">If <paramref name="rawBody"/> or <paramref name="headers"/> is <see langword="null"/></exception> internal MessagePart(byte[] rawBody, MessageHeader headers, IParsingErrorHandler parsingErrorHandler = null) { if (rawBody == null) { throw new ArgumentNullException("rawBody"); } if (headers == null) { throw new ArgumentNullException("headers"); } _parsingErrorHandler = parsingErrorHandler; ContentType = headers.ContentType; ContentDescription = headers.ContentDescription; ContentTransferEncoding = headers.ContentTransferEncoding; ContentId = headers.ContentId; ContentDisposition = headers.ContentDisposition; FileName = FindFileName(ContentType, ContentDisposition, "(no name)"); try { BodyEncoding = ParseBodyEncoding(ContentType.CharSet); } catch (FormatException ex) { BodyEncoding = Encoding.UTF8; if (_parsingErrorHandler != null) { _parsingErrorHandler.HandleParseError(new ParseError(ex, ContentType.CharSet, string.Format("On body encoding. Encoding set to {0}", BodyEncoding.EncodingName))); } else { throw; } } ParseBody(rawBody); }
/// <summary> /// Extract the header part and body part of a message.<br/> /// The headers are then parsed to a strongly typed <see cref="MessageHeader"/> object. /// </summary> /// <param name="fullRawMessage">The full message in bytes where header and body needs to be extracted from</param> /// <param name="headers">The extracted header parts of the message</param> /// <param name="body">The body part of the message</param> /// <param name="parsingErrorHandler">(Optional) It is notifified when an error occurs while parsing an header. /// If it is not null, the handler handles the error on the specific header without stopping the message parsing process</param> /// <exception cref="ArgumentNullException">If <paramref name="fullRawMessage"/> is <see langword="null"/></exception> public static void ExtractHeadersAndBody(byte[] fullRawMessage, out MessageHeader headers, out byte[] body, IParsingErrorHandler parsingErrorHandler = null) { if(fullRawMessage == null) throw new ArgumentNullException("fullRawMessage"); // Find the end location of the headers int endOfHeaderLocation = FindHeaderEndPosition(fullRawMessage); // The headers are always in ASCII - therefore we can convert the header part into a string // using US-ASCII encoding string headersString = Encoding.ASCII.GetString(fullRawMessage, 0, endOfHeaderLocation); // Now parse the headers to a NameValueCollection NameValueCollection headersUnparsedCollection = ExtractHeaders(headersString); // Use the NameValueCollection to parse it into a strongly-typed MessageHeader header headers = new MessageHeader(headersUnparsedCollection, parsingErrorHandler); // Since we know where the headers end, we also know where the body is // Copy the body part into the body parameter body = new byte[fullRawMessage.Length - endOfHeaderLocation]; Array.Copy(fullRawMessage, endOfHeaderLocation, body, 0, body.Length); }
/// <summary> /// Used to construct the topmost message part /// </summary> /// <param name="rawBody">The body that needs to be parsed</param> /// <param name="headers">The headers that should be used from the message</param> /// <param name="parsingErrorHandler">(Optional) It is notifified when an error occurs while parsing something in the message. /// If it is not null, the handler handles the error on the specific element without stopping the message parsing process</param> /// <exception cref="ArgumentNullException">If <paramref name="rawBody"/> or <paramref name="headers"/> is <see langword="null"/></exception> internal MessagePart(byte[] rawBody, MessageHeader headers, IParsingErrorHandler parsingErrorHandler = null) { if(rawBody == null) throw new ArgumentNullException("rawBody"); if(headers == null) throw new ArgumentNullException("headers"); _parsingErrorHandler = parsingErrorHandler; ContentType = headers.ContentType; ContentDescription = headers.ContentDescription; ContentTransferEncoding = headers.ContentTransferEncoding; ContentId = headers.ContentId; ContentDisposition = headers.ContentDisposition; FileName = FindFileName(ContentType, ContentDisposition, "(no name)"); try { BodyEncoding = ParseBodyEncoding(ContentType.CharSet); } catch (FormatException ex) { BodyEncoding = Encoding.UTF8; if (_parsingErrorHandler != null) { _parsingErrorHandler.HandleParseError(new ParseError(ex, ContentType.CharSet, string.Format("On body encoding. Encoding set to {0}", BodyEncoding.EncodingName))); } else { throw; } } ParseBody(rawBody); }
/// <summary> /// Extract the header part and body part of a message.<br/> /// The headers are then parsed to a strongly typed <see cref="MessageHeader"/> object. /// </summary> /// <param name="fullRawMessage">The full message in bytes where header and body needs to be extracted from</param> /// <param name="headers">The extracted header parts of the message</param> /// <param name="body">The body part of the message</param> /// <param name="parsingErrorHandler">(Optional) It is notifified when an error occurs while parsing an header. /// If it is not null, the handler handles the error on the specific header without stopping the message parsing process</param> /// <exception cref="ArgumentNullException">If <paramref name="fullRawMessage"/> is <see langword="null"/></exception> public static void ExtractHeadersAndBody(byte[] fullRawMessage, out MessageHeader headers, out byte[] body, IParsingErrorHandler parsingErrorHandler = null) { if (fullRawMessage == null) { throw new ArgumentNullException("fullRawMessage"); } // Find the end location of the headers int endOfHeaderLocation = FindHeaderEndPosition(fullRawMessage); // The headers are always in ASCII - therefore we can convert the header part into a string // using US-ASCII encoding string headersString = Encoding.ASCII.GetString(fullRawMessage, 0, endOfHeaderLocation); // Now parse the headers to a NameValueCollection NameValueCollection headersUnparsedCollection = ExtractHeaders(headersString); // Use the NameValueCollection to parse it into a strongly-typed MessageHeader header headers = new MessageHeader(headersUnparsedCollection, parsingErrorHandler); // Since we know where the headers end, we also know where the body is // Copy the body part into the body parameter body = new byte[fullRawMessage.Length - endOfHeaderLocation]; Array.Copy(fullRawMessage, endOfHeaderLocation, body, 0, body.Length); }
/// <summary> /// Convenience constructor for <see cref="Mime.Message(byte[], bool, IParsingErrorHandler)"/>.<br/> /// <br/> /// Creates a message from a byte array. The full message including its body is parsed. /// </summary> /// <param name="rawMessageContent">The byte array which is the message contents to parse</param> /// <param name="parsingErrorHandler">(Optional) It is notifified when an error occurs while parsing something in the message. /// If it is not null, the handler handles the error on the specific element without stopping the message parsing process</param> public Message(byte[] rawMessageContent, IParsingErrorHandler parsingErrorHandler = null) : this(rawMessageContent, true, parsingErrorHandler) { }
/// <summary> /// Parses a <see cref="NameValueCollection"/> to a MessageHeader /// </summary> /// <param name="headers">The collection that should be traversed and parsed</param> /// <param name="parsingErrorHandler">(Optional) It is notifified when an error occurs while parsing an header. /// If it is not null, the handler handles the error on the specific header without stopping the message parsing process</param> /// <returns>A valid MessageHeader object</returns> /// <exception cref="ArgumentNullException">If <paramref name="headers"/> is <see langword="null"/></exception> internal MessageHeader(NameValueCollection headers, IParsingErrorHandler parsingErrorHandler = null) { _parsingErrorHandler = parsingErrorHandler; if (headers == null) throw new ArgumentNullException("headers"); // Create empty lists as defaults. We do not like null values // List with an initial capacity set to zero will be replaced // when a corrosponding header is found To = new List<RfcMailAddress>(0); Cc = new List<RfcMailAddress>(0); Bcc = new List<RfcMailAddress>(0); Received = new List<Received>(); Keywords = new List<string>(); InReplyTo = new List<string>(0); References = new List<string>(0); DispositionNotificationTo = new List<RfcMailAddress>(); UnknownHeaders = new NameValueCollection(); // Default importancetype is Normal (assumed if not set) Importance = MailPriority.Normal; // 7BIT is the default ContentTransferEncoding (assumed if not set) ContentTransferEncoding = ContentTransferEncoding.SevenBit; // text/plain; charset=us-ascii is the default ContentType ContentType = new ContentType("text/plain; charset=us-ascii"); // Now parse the actual headers ParseHeaders(headers); }
/// <summary> /// Loads a <see cref="Message"/> from a <see cref="Stream"/> containing a raw email. /// </summary> /// <param name="messageStream">The <see cref="Stream"/> from which to load the raw <see cref="Message"/></param> /// <param name="parsingErrorHandler">(Optional) It is notifified when an error occurs while parsing something in the message. /// If it is not null, the handler handles the error on the specific element without stopping the message parsing process</param> /// <exception cref="ArgumentNullException">If <paramref name="messageStream"/> is <see langword="null"/></exception> /// <exception>Other exceptions relevant to <see cref="Stream.Read"/> might be thrown as well</exception> /// <returns>A <see cref="Message"/> with the content loaded from the <paramref name="messageStream"/></returns> public static Message Load(Stream messageStream, IParsingErrorHandler parsingErrorHandler = null) { if (messageStream == null) throw new ArgumentNullException("messageStream"); using (MemoryStream outStream = new MemoryStream()) { #if DOTNET4 // TODO: Enable using native v4 framework methods when support is formally added. messageStream.CopyTo(outStream); #else int bytesRead; byte[] buffer = new byte[4096]; while ((bytesRead = messageStream.Read(buffer, 0, 4096)) > 0) { outStream.Write(buffer, 0, bytesRead); } #endif byte[] content = outStream.ToArray(); return new Message(content, parsingErrorHandler); } }
/// <summary> /// Loads a <see cref="Message"/> from a file containing a raw email. /// </summary> /// <param name="file">The File location to load the <see cref="Message"/> from. The file must exist.</param> /// <param name="parsingErrorHandler">(Optional) It is notifified when an error occurs while parsing something in the message. /// If it is not null, the handler handles the error on the specific element without stopping the message parsing process</param> /// <exception cref="ArgumentNullException">If <paramref name="file"/> is <see langword="null"/></exception> /// <exception cref="FileNotFoundException">If <paramref name="file"/> does not exist</exception> /// <exception>Other exceptions relevant to a <see cref="FileStream"/> might be thrown as well</exception> /// <returns>A <see cref="Message"/> with the content loaded from the <paramref name="file"/></returns> public static Message Load(FileInfo file, IParsingErrorHandler parsingErrorHandler = null) { if (file == null) throw new ArgumentNullException("file"); if (!file.Exists) throw new FileNotFoundException("Cannot load message from non-existent file", file.FullName); using (FileStream stream = new FileStream(file.FullName, FileMode.Open)) { return Load(stream, parsingErrorHandler); } }
/// <summary> /// Fetches a message from the server and parses it /// </summary> /// <param name="messageNumber"> /// Message number on server, which may not be marked as deleted.<br/> /// Must be inside the range [1, messageCount] /// </param> /// <param name="parsingErrorHandler">(Optional) It is notifified when an error occurs while parsing something in the message. /// If it is not null, the handler handles the error on the specific element without stopping the message parsing process</param> /// <returns>The message, containing the email message</returns> /// <exception cref="PopServerException">If the server did not accept the command sent to fetch the message</exception> public Message GetMessage(int messageNumber, IParsingErrorHandler parsingErrorHandler = null) { AssertDisposed(); ValidateMessageNumber(messageNumber); if (State != ConnectionState.Transaction) throw new InvalidUseException("Cannot fetch a message, when the user has not been authenticated yet"); byte[] messageContent = GetMessageAsBytes(messageNumber); return new Message(messageContent, parsingErrorHandler); }