Пример #1
0
        /// <summary>
        /// Asynchronously parses a message from the stream.
        /// </summary>
        /// <remarks>
        /// Parses a message from the stream.
        /// </remarks>
        /// <returns>The parsed message.</returns>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.FormatException">
        /// There was an error parsing the message.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public async Task <MimeMessage> ParseMessageAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            // Note: if a previously parsed MimePart's content has been read,
            // then the stream position will have moved and will need to be
            // reset.
            if (persistent && stream.Position != position)
            {
                stream.Seek(position, SeekOrigin.Begin);
            }

            // scan the from-line if we are parsing an mbox
            while (state != MimeParserState.MessageHeaders)
            {
                switch (await StepAsync(cancellationToken).ConfigureAwait(false))
                {
                case MimeParserState.Error:
                    throw new FormatException("Failed to find mbox From marker.");

                case MimeParserState.Eos:
                    throw new FormatException("End of stream.");
                }
            }

            toplevel = true;

            // parse the headers
            var beginLineNumber = lineNumber;

            if (state < MimeParserState.Content && await StepAsync(cancellationToken).ConfigureAwait(false) == MimeParserState.Error)
            {
                throw new FormatException("Failed to parse message headers.");
            }

            var message     = new MimeMessage(options, headers, RfcComplianceMode.Loose);
            var messageArgs = new MimeMessageEndEventArgs(message)
            {
                HeadersEndOffset = headerBlockEnd,
                BeginOffset      = headerBlockBegin,
                LineNumber       = beginLineNumber
            };

            OnMimeMessageBegin(messageArgs);

            if (format == MimeFormat.Mbox && options.RespectContentLength)
            {
                contentEnd = 0;

                for (int i = 0; i < headers.Count; i++)
                {
                    if (headers[i].Id != HeaderId.ContentLength)
                    {
                        continue;
                    }

                    var value = headers[i].RawValue;
                    int index = 0;

                    if (!ParseUtils.SkipWhiteSpace(value, ref index, value.Length))
                    {
                        continue;
                    }

                    if (!ParseUtils.TryParseInt32(value, ref index, value.Length, out int length))
                    {
                        continue;
                    }

                    contentEnd = GetOffset(inputIndex) + length;
                    break;
                }
            }

            var type       = GetContentType(null);
            var entity     = options.CreateEntity(type, headers, true, 0);
            var entityArgs = new MimeEntityEndEventArgs(entity)
            {
                HeadersEndOffset = headerBlockEnd,
                BeginOffset      = headerBlockBegin,
                LineNumber       = beginLineNumber
            };

            OnMimeEntityBegin(entityArgs);

            message.Body = entity;

            if (entity is Multipart)
            {
                await ConstructMultipartAsync((Multipart)entity, entityArgs, 0, cancellationToken).ConfigureAwait(false);
            }
            else if (entity is MessagePart)
            {
                await ConstructMessagePartAsync((MessagePart)entity, entityArgs, 0, cancellationToken).ConfigureAwait(false);
            }
            else
            {
                await ConstructMimePartAsync((MimePart)entity, entityArgs, cancellationToken).ConfigureAwait(false);
            }

            var endOffset = GetEndOffset(inputIndex);

            messageArgs.HeadersEndOffset = entityArgs.HeadersEndOffset = Math.Min(entityArgs.HeadersEndOffset, endOffset);
            messageArgs.EndOffset        = entityArgs.EndOffset = endOffset;

            if (boundary != BoundaryType.Eos)
            {
                if (format == MimeFormat.Mbox)
                {
                    state = MimeParserState.MboxMarker;
                }
                else
                {
                    state = MimeParserState.Complete;
                }
            }
            else
            {
                state = MimeParserState.Eos;
            }

            OnMimeEntityEnd(entityArgs);
            OnMimeMessageEnd(messageArgs);

            return(message);
        }
Пример #2
0
        async Task ConstructMessagePartAsync(MessagePart rfc822, MimeEntityEndEventArgs args, int depth, CancellationToken cancellationToken)
        {
            var beginOffset     = GetOffset(inputIndex);
            var beginLineNumber = lineNumber;

            if (bounds.Count > 0)
            {
                int atleast = Math.Max(ReadAheadSize, GetMaxBoundaryLength());

                if (await ReadAheadAsync(atleast, 0, cancellationToken).ConfigureAwait(false) <= 0)
                {
                    boundary = BoundaryType.Eos;
                    return;
                }

                unsafe
                {
                    fixed(byte *inbuf = input)
                    {
                        byte *start = inbuf + inputIndex;
                        byte *inend = inbuf + inputEnd;
                        byte *inptr = start;

                        *inend = (byte)'\n';

                        while (*inptr != (byte)'\n')
                        {
                            inptr++;
                        }

                        boundary = CheckBoundary(inputIndex, start, (int)(inptr - start));

                        switch (boundary)
                        {
                        case BoundaryType.ImmediateEndBoundary:
                        case BoundaryType.ImmediateBoundary:
                        case BoundaryType.ParentBoundary:
                            return;

                        case BoundaryType.ParentEndBoundary:
                            // ignore "From " boundaries, broken mailers tend to include these...
                            if (!IsMboxMarker(start))
                            {
                                return;
                            }
                            break;
                        }
                    }
                }
            }

            // parse the headers...
            state = MimeParserState.MessageHeaders;
            if (await StepAsync(cancellationToken).ConfigureAwait(false) == MimeParserState.Error)
            {
                // Note: this either means that StepHeaders() found the end of the stream
                // or an invalid header field name at the start of the message headers,
                // which likely means that this is not a valid MIME stream?
                boundary = BoundaryType.Eos;
                return;
            }

            var message     = new MimeMessage(options, headers, RfcComplianceMode.Loose);
            var messageArgs = new MimeMessageEndEventArgs(message, rfc822)
            {
                HeadersEndOffset = headerBlockEnd,
                BeginOffset      = headerBlockBegin,
                LineNumber       = beginLineNumber
            };

            OnMimeMessageBegin(messageArgs);

            if (preHeaderBuffer.Length > 0)
            {
                message.MboxMarker = new byte[preHeaderLength];
                Buffer.BlockCopy(preHeaderBuffer, 0, message.MboxMarker, 0, preHeaderLength);
            }

            var type       = GetContentType(null);
            var entity     = options.CreateEntity(type, headers, true, depth);
            var entityArgs = new MimeEntityEndEventArgs(entity)
            {
                HeadersEndOffset = headerBlockEnd,
                BeginOffset      = headerBlockBegin,
                LineNumber       = beginLineNumber
            };

            OnMimeEntityBegin(entityArgs);

            message.Body = entity;

            if (entity is Multipart)
            {
                await ConstructMultipartAsync((Multipart)entity, entityArgs, depth + 1, cancellationToken).ConfigureAwait(false);
            }
            else if (entity is MessagePart)
            {
                await ConstructMessagePartAsync((MessagePart)entity, entityArgs, depth + 1, cancellationToken).ConfigureAwait(false);
            }
            else
            {
                await ConstructMimePartAsync((MimePart)entity, entityArgs, cancellationToken).ConfigureAwait(false);
            }

            rfc822.Message = message;

            var endOffset = GetEndOffset(inputIndex);

            messageArgs.HeadersEndOffset = entityArgs.HeadersEndOffset = Math.Min(entityArgs.HeadersEndOffset, endOffset);
            messageArgs.EndOffset        = entityArgs.EndOffset = endOffset;

            OnMimeEntityEnd(entityArgs);
            OnMimeMessageEnd(messageArgs);

            args.Lines = GetLineCount(beginLineNumber, beginOffset, endOffset);
        }