Beispiel #1
0
        public void MultipartParserNesting(string boundary)
        {
            for (var nesting = 0; nesting < 16; nesting++)
            {
                string nested = CreateNestedBuffer(nesting);

                byte[] data = CreateBuffer(boundary, nested);

                for (var cnt = 1; cnt <= data.Length; cnt++)
                {
                    MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
                    Assert.NotNull(parser);

                    int                       totalBytesConsumed;
                    List <string>             bodyParts;
                    MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
                    Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                    Assert.Equal(data.Length, totalBytesConsumed);

                    Assert.Equal(2, bodyParts.Count);
                    Assert.Equal(0, bodyParts[0].Length);
                    Assert.Equal(nested.Length, bodyParts[1].Length);
                }
            }
        }
        public async Task MimeMultipartParserTestMultipartContent(string boundary)
        {
            MultipartContent content = new MultipartContent("mixed", boundary);

            content.Add(new StringContent("A"));
            content.Add(new StringContent("B"));
            content.Add(new StringContent("C"));

            MemoryStream memStream = new MemoryStream();
            await content.CopyToAsync(memStream);

            memStream.Position = 0;
            byte[] data = memStream.ToArray();

            for (var readSize = 1; readSize <= data.Length; readSize++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(boundary);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(parser, data, readSize, out bodyParts, out totalBytesConsumed);
                Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                Assert.Equal(data.Length, totalBytesConsumed);

                Assert.Equal(4, bodyParts.Count);
                Assert.Empty(bodyParts[0]);

                Assert.EndsWith("A", bodyParts[1]);
                Assert.EndsWith("B", bodyParts[2]);
                Assert.EndsWith("C", bodyParts[3]);
            }
        }
Beispiel #3
0
        public void MimeMultipartParserTestMultipartContent(string boundary)
        {
            MultipartContent content = new MultipartContent("mixed", boundary);

            content.Add(new StringContent("A"));
            content.Add(new StringContent("B"));
            content.Add(new StringContent("C"));

            MemoryStream memStream = new MemoryStream();

            content.CopyToAsync(memStream).Wait();
            memStream.Position = 0;
            byte[] data = memStream.ToArray();

            for (var cnt = 1; cnt <= data.Length; cnt++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
                Assert.NotNull(parser);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
                Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                Assert.Equal(data.Length, totalBytesConsumed);

                Assert.Equal(4, bodyParts.Count);
                Assert.Empty(bodyParts[0]);

                Assert.True(bodyParts[1].EndsWith("A"));
                Assert.True(bodyParts[2].EndsWith("B"));
                Assert.True(bodyParts[3].EndsWith("C"));
            }
        }
Beispiel #4
0
        public void MaxMessageSizeIsExact()
        {
            string boundary = "--A";

            byte[] data = CreateBuffer(boundary, "cool");

            for (var readSize = 1; readSize <= data.Length; readSize++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(boundary, data.Length);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(
                    parser,
                    data,
                    2,
                    out bodyParts,
                    out totalBytesConsumed
                    );
                Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                Assert.Equal(data.Length, totalBytesConsumed);

                Assert.Equal(2, bodyParts.Count);
                Assert.Empty(bodyParts[0]);
            }
        }
Beispiel #5
0
        public void MultipartParserNearMatches(
            string boundary,
            bool withExtraWhitespace,
            bool withExtraCRLF,
            string nearBoundaryBody
            )
        {
            byte[] data = CreateBuffer(
                boundary,
                withExtraWhitespace,
                withExtraCRLF,
                nearBoundaryBody
                );

            for (var readSize = 1; readSize <= data.Length; readSize++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(boundary);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(
                    parser,
                    data,
                    readSize,
                    out bodyParts,
                    out totalBytesConsumed
                    );
                Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                Assert.Equal(data.Length, totalBytesConsumed);

                Assert.Equal(2, bodyParts.Count);
                Assert.Equal(0, bodyParts[0].Length);
                Assert.Equal(nearBoundaryBody, bodyParts[1]);
            }
        }
Beispiel #6
0
        public void MultipartParserMultipleShortBodyPartsWithLws(string boundary)
        {
            string[] text = new string[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };

            byte[] data = CreateBuffer(boundary, true, text);

            for (var cnt = 1; cnt <= data.Length; cnt++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
                Assert.NotNull(parser);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
                Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                Assert.Equal(data.Length, totalBytesConsumed);

                Assert.Equal(text.Length + 1, bodyParts.Count);
                Assert.Equal(0, bodyParts[0].Length);

                for (var check = 0; check < text.Length; check++)
                {
                    Assert.Equal(1, bodyParts[check + 1].Length);
                    Assert.Equal(text[check], bodyParts[check + 1]);
                }
            }
        }
Beispiel #7
0
        public void MultipartParserEmptyBuffer(string boundary)
        {
            byte[] data = CreateBuffer(boundary);

            for (var readSize = 1; readSize <= data.Length; readSize++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(boundary);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(
                    parser,
                    data,
                    readSize,
                    out bodyParts,
                    out totalBytesConsumed
                    );
                Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                Assert.Equal(data.Length, totalBytesConsumed);

                Assert.Equal(2, bodyParts.Count);
                Assert.Equal(0, bodyParts[0].Length);
                Assert.Equal(0, bodyParts[1].Length);
            }
        }
Beispiel #8
0
        public void MultipartParserMultipleLongBodyParts(string boundary)
        {
            const string middleText = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";

            string[] text = new string[] {
                "A" + middleText + "A",
                "B" + middleText + "B",
                "C" + middleText + "C",
                "D" + middleText + "D",
                "E" + middleText + "E",
                "F" + middleText + "F",
                "G" + middleText + "G",
                "H" + middleText + "H",
                "I" + middleText + "I",
                "J" + middleText + "J",
                "K" + middleText + "K",
                "L" + middleText + "L",
                "M" + middleText + "M",
                "N" + middleText + "N",
                "O" + middleText + "O",
                "P" + middleText + "P",
                "Q" + middleText + "Q",
                "R" + middleText + "R",
                "S" + middleText + "S",
                "T" + middleText + "T",
                "U" + middleText + "U",
                "V" + middleText + "V",
                "W" + middleText + "W",
                "X" + middleText + "X",
                "Y" + middleText + "Y",
                "Z" + middleText + "Z"
            };

            byte[] data = CreateBuffer(boundary, text);

            for (var cnt = 1; cnt <= data.Length; cnt++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
                Assert.NotNull(parser);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
                Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                Assert.Equal(data.Length, totalBytesConsumed);

                Assert.Equal(text.Length + 1, bodyParts.Count);
                Assert.Equal(0, bodyParts[0].Length);

                for (var check = 0; check < text.Length; check++)
                {
                    Assert.Equal(text[check].Length, bodyParts[check + 1].Length);
                    Assert.Equal(text[check], bodyParts[check + 1]);
                }
            }
        }
Beispiel #9
0
        public void MultipartParserNearMatches(string boundary)
        {
            string[] text = new string[] {
                "AAA" + LF,
                "AAA" + CR,
                "AAA" + CRLF,
                "AAA" + CRLF + CRLF,
                "AAA" + CRLF + Dash,
                "AAA" + CRLF + Dash + CR,
                "AAA" + CRLF + Dash + CRLF,
                CR + Dash + "AAA",
                CRLF + Dash + "AAA",
                CRLF + DashDash + "AAA" + CR + "AAA",
                CRLF,
                "AAA",
                "AAA" + CRLF,
                CRLF + CRLF,
                CRLF + CRLF + CRLF,
                "AAA" + DashDash + "AAA",
                CRLF + "AAA" + DashDash + "AAA" + DashDash,
                CRLF + DashDash + "AAA" + CRLF,
                CRLF + DashDash + "AAA" + CRLF + CRLF,
                CRLF + DashDash + "AAA" + DashDash + CRLF,
                CRLF + DashDash + "AAA" + DashDash + CRLF + CRLF
            };

            byte[] data = CreateBuffer(boundary, text);

            for (var cnt = 1; cnt <= data.Length; cnt++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
                Assert.NotNull(parser);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
                Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                Assert.Equal(data.Length, totalBytesConsumed);

                Assert.Equal(text.Length + 1, bodyParts.Count);
                Assert.Equal(0, bodyParts[0].Length);

                for (var check = 0; check < text.Length; check++)
                {
                    Assert.Equal(text[check].Length, bodyParts[check + 1].Length);
                    Assert.Equal(text[check], bodyParts[check + 1]);
                }
            }
        }
        public void MimeMultipartParserTestDataTooBig(string boundary)
        {
            byte[] data = CreateBuffer(boundary);

            for (var cnt = 1; cnt <= data.Length; cnt++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(ParserData.MinMessageSize, boundary);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
                Assert.Equal(MimeMultipartParser.State.DataTooBig, state);
                Assert.Equal(ParserData.MinMessageSize, totalBytesConsumed);
            }
        }
Beispiel #11
0
        private static MimeMultipartParser.State ParseBufferInSteps(MimeMultipartParser parser, byte[] buffer, int readsize, out List <string> bodyParts, out int totalBytesConsumed)
        {
            MimeMultipartParser.State state = MimeMultipartParser.State.Invalid;
            totalBytesConsumed = 0;
            bodyParts          = new List <string>();
            bool isFinal = false;

            byte[] currentBodyPart   = new byte[32 * 1024];
            int    currentBodyLength = 0;

            while (totalBytesConsumed <= buffer.Length)
            {
                int    size        = Math.Min(buffer.Length - totalBytesConsumed, readsize);
                byte[] parseBuffer = new byte[size];
                Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size);

                int bytesConsumed = 0;
                ArraySegment <byte> out1;
                ArraySegment <byte> out2;
                state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed, out out1, out out2, out isFinal);
                totalBytesConsumed += bytesConsumed;

                Buffer.BlockCopy(out1.Array, out1.Offset, currentBodyPart, currentBodyLength, out1.Count);
                currentBodyLength += out1.Count;

                Buffer.BlockCopy(out2.Array, out2.Offset, currentBodyPart, currentBodyLength, out2.Count);
                currentBodyLength += out2.Count;

                if (state == MimeMultipartParser.State.BodyPartCompleted)
                {
                    var bPart = new byte[currentBodyLength];
                    Buffer.BlockCopy(currentBodyPart, 0, bPart, 0, currentBodyLength);
                    bodyParts.Add(Encoding.UTF8.GetString(bPart));
                    currentBodyLength = 0;
                    if (isFinal)
                    {
                        break;
                    }
                }
                else if (state != MimeMultipartParser.State.NeedMoreData)
                {
                    return(state);
                }
            }

            Assert.True(isFinal);
            return(state);
        }
        public void MultipartParserNearMatches(string boundary, string nearBoundaryBody)
        {
            byte[] data = CreateBuffer(boundary, nearBoundaryBody);

            for (var cnt = 1; cnt <= data.Length; cnt++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
                Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                Assert.Equal(data.Length, totalBytesConsumed);

                Assert.Equal(2, bodyParts.Count);
                Assert.Equal(0, bodyParts[0].Length);
                Assert.Equal(nearBoundaryBody, bodyParts[1]);
            }
        }
Beispiel #13
0
        public void MultipartParserSingleShortBodyPart(string boundary)
        {
            byte[] data = CreateBuffer(boundary, "A");

            for (var cnt = 1; cnt <= data.Length; cnt++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
                Assert.NotNull(parser);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
                Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                Assert.Equal(data.Length, totalBytesConsumed);

                Assert.Equal(2, bodyParts.Count);
                Assert.Equal(0, bodyParts[0].Length);
                Assert.Equal(1, bodyParts[1].Length);
                Assert.Equal("A", bodyParts[1]);
            }
        }
Beispiel #14
0
        public void MultipartParserMultipleLongBodyParts(
            string boundary,
            bool withExtraWhitespace,
            bool withExtraCRLF,
            string[] multipleLongBodies
            )
        {
            byte[] data = CreateBuffer(
                boundary,
                withExtraWhitespace,
                withExtraCRLF,
                multipleLongBodies
                );

            for (var readSize = 1; readSize <= data.Length; readSize++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(boundary);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(
                    parser,
                    data,
                    readSize,
                    out bodyParts,
                    out totalBytesConsumed
                    );
                Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                Assert.Equal(data.Length, totalBytesConsumed);

                Assert.Equal(multipleLongBodies.Length + 1, bodyParts.Count);
                Assert.Equal(0, bodyParts[0].Length);

                for (var check = 0; check < multipleLongBodies.Length; check++)
                {
                    Assert.Equal(multipleLongBodies[check], bodyParts[check + 1]);
                }
            }
        }
        public void MultipartParserMultipleShortBodyPartsWithLws(string boundary, string[] multipleShortBodies)
        {
            byte[] data = CreateBuffer(boundary, true, multipleShortBodies);

            for (var cnt = 1; cnt <= data.Length; cnt++)
            {
                MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);

                int                       totalBytesConsumed;
                List <string>             bodyParts;
                MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
                Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
                Assert.Equal(data.Length, totalBytesConsumed);

                Assert.Equal(multipleShortBodies.Length + 1, bodyParts.Count);
                Assert.Equal(0, bodyParts[0].Length);

                for (var check = 0; check < multipleShortBodies.Length; check++)
                {
                    Assert.Equal(multipleShortBodies[check], bodyParts[check + 1]);
                }
            }
        }
        /// <summary>
        /// Parses the data provided and generates parsed MIME body part bodies in the form of <see cref="ArraySegment{T}"/> which are ready to 
        /// write to the output stream.
        /// </summary>
        /// <param name="data">The data to parse</param>
        /// <param name="bytesRead">The number of bytes available in the input data</param>
        /// <returns>Parsed <see cref="MimeBodyPart"/> instances.</returns>
        public IEnumerable<MimeBodyPart> ParseBuffer(byte[] data, int bytesRead)
        {
            int bytesConsumed = 0;
            bool isFinal = false;

            if (bytesRead == 0)
            {
                CleanupCurrentBodyPart();
                throw new IOException(Properties.Resources.ReadAsMimeMultipartUnexpectedTermination);
            }

            // Make sure we remove an old array segments.
            _currentBodyPart.Segments.Clear();

            while (bytesConsumed < bytesRead)
            {
                _mimeStatus = _mimeParser.ParseBuffer(data, bytesRead, ref bytesConsumed, out _parsedBodyPart[0], out _parsedBodyPart[1], out isFinal);
                if (_mimeStatus != MimeMultipartParser.State.BodyPartCompleted && _mimeStatus != MimeMultipartParser.State.NeedMoreData)
                {
                    CleanupCurrentBodyPart();
                    throw new IOException(RS.Format(Properties.Resources.ReadAsMimeMultipartParseError, bytesConsumed, data));
                }

                // First body is empty preamble which we just ignore
                if (_isFirst)
                {
                    if (_mimeStatus == MimeMultipartParser.State.BodyPartCompleted)
                    {
                        _isFirst = false;
                    }

                    continue;
                }

                // Parse the two array segments containing parsed body parts that the MIME parser gave us
                foreach (ArraySegment<byte> part in _parsedBodyPart)
                {
                    if (part.Count == 0)
                    {
                        continue;
                    }

                    if (_bodyPartHeaderStatus != ParserState.Done)
                    {
                        int headerConsumed = part.Offset;
                        _bodyPartHeaderStatus = _currentBodyPart.HeaderParser.ParseBuffer(part.Array, part.Count + part.Offset, ref headerConsumed);
                        if (_bodyPartHeaderStatus == ParserState.Done)
                        {
                            // Add the remainder as body part content
                            _currentBodyPart.Segments.Add(new ArraySegment<byte>(part.Array, headerConsumed, part.Count + part.Offset - headerConsumed));
                        }
                        else if (_bodyPartHeaderStatus != ParserState.NeedMoreData)
                        {
                            CleanupCurrentBodyPart();
                            throw new IOException(RS.Format(Properties.Resources.ReadAsMimeMultipartHeaderParseError, headerConsumed, part.Array));
                        }
                    }
                    else
                    {
                        // Add the data as body part content
                        _currentBodyPart.Segments.Add(part);
                    }
                }

                if (_mimeStatus == MimeMultipartParser.State.BodyPartCompleted)
                {
                    // If body is completed then swap current body part
                    MimeBodyPart completed = _currentBodyPart;
                    completed.IsComplete = true;
                    completed.IsFinal = isFinal;

                    _currentBodyPart = new MimeBodyPart(_streamProvider, _maxBodyPartHeaderSize);
                    _mimeStatus = MimeMultipartParser.State.NeedMoreData;
                    _bodyPartHeaderStatus = ParserState.NeedMoreData;
                    yield return completed;
                }
                else
                {
                    // Otherwise return what we have 
                    yield return _currentBodyPart;
                }
            }
        }
Beispiel #17
0
        /// <summary>
        /// Parses the data provided and generates parsed MIME body part bodies in the form of <see cref="ArraySegment{T}"/> which are ready to
        /// write to the output stream.
        /// </summary>
        /// <param name="data">The data to parse</param>
        /// <param name="bytesRead">The number of bytes available in the input data</param>
        /// <returns>Parsed <see cref="MimeBodyPart"/> instances.</returns>
        public IEnumerable <MimeBodyPart> ParseBuffer(byte[] data, int bytesRead)
        {
            int  bytesConsumed = 0;
            bool isFinal       = false;

            // There's a special case here - if we've reached the end of the message and there's no optional
            // CRLF, then we're out of bytes to read, but we have finished the message.
            //
            // If IsWaitingForEndOfMessage is true and we're at the end of the stream, then we're going to
            // call into the parser again with an empty array as the buffer to signal the end of the parse.
            // Then the final boundary segment will be marked as complete.
            if (bytesRead == 0 && !_mimeParser.IsWaitingForEndOfMessage)
            {
                CleanupCurrentBodyPart();
                throw new IOException(Properties.Resources.ReadAsMimeMultipartUnexpectedTermination);
            }

            // Make sure we remove an old array segments.
            _currentBodyPart.Segments.Clear();

            while (_mimeParser.CanParseMore(bytesRead, bytesConsumed))
            {
                _mimeStatus = _mimeParser.ParseBuffer(data, bytesRead, ref bytesConsumed, out _parsedBodyPart[0], out _parsedBodyPart[1], out isFinal);
                if (_mimeStatus != MimeMultipartParser.State.BodyPartCompleted && _mimeStatus != MimeMultipartParser.State.NeedMoreData)
                {
                    CleanupCurrentBodyPart();
                    throw Error.InvalidOperation(Properties.Resources.ReadAsMimeMultipartParseError, bytesConsumed, data);
                }

                // First body is empty preamble which we just ignore
                if (_isFirst)
                {
                    if (_mimeStatus == MimeMultipartParser.State.BodyPartCompleted)
                    {
                        _isFirst = false;
                    }

                    continue;
                }

                // Parse the two array segments containing parsed body parts that the MIME parser gave us
                foreach (ArraySegment <byte> part in _parsedBodyPart)
                {
                    if (part.Count == 0)
                    {
                        continue;
                    }

                    if (_bodyPartHeaderStatus != ParserState.Done)
                    {
                        int headerConsumed = part.Offset;
                        _bodyPartHeaderStatus = _currentBodyPart.HeaderParser.ParseBuffer(part.Array, part.Count + part.Offset, ref headerConsumed);
                        if (_bodyPartHeaderStatus == ParserState.Done)
                        {
                            // Add the remainder as body part content
                            _currentBodyPart.Segments.Add(new ArraySegment <byte>(part.Array, headerConsumed, part.Count + part.Offset - headerConsumed));
                        }
                        else if (_bodyPartHeaderStatus != ParserState.NeedMoreData)
                        {
                            CleanupCurrentBodyPart();
                            throw Error.InvalidOperation(Properties.Resources.ReadAsMimeMultipartHeaderParseError, headerConsumed, part.Array);
                        }
                    }
                    else
                    {
                        // Add the data as body part content
                        _currentBodyPart.Segments.Add(part);
                    }
                }

                if (_mimeStatus == MimeMultipartParser.State.BodyPartCompleted)
                {
                    // If body is completed then swap current body part
                    MimeBodyPart completed = _currentBodyPart;
                    completed.IsComplete = true;
                    completed.IsFinal    = isFinal;

                    _currentBodyPart = new MimeBodyPart(_streamProvider, _maxBodyPartHeaderSize, _content);

                    _mimeStatus           = MimeMultipartParser.State.NeedMoreData;
                    _bodyPartHeaderStatus = ParserState.NeedMoreData;
                    yield return(completed);
                }
                else
                {
                    // Otherwise return what we have
                    yield return(_currentBodyPart);
                }
            }
        }
        /// <summary>
        /// Parses the data provided and generates parsed MIME body part bodies in the form of <see cref="ArraySegment{T}"/> which are ready to 
        /// write to the output stream.
        /// </summary>
        /// <param name="data">The data to parse</param>
        /// <param name="bytesRead">The number of bytes available in the input data</param>
        /// <returns>Parsed <see cref="MimeBodyPart"/> instances.</returns>
        public IEnumerable<MimeBodyPart> ParseBuffer(byte[] data, int bytesRead)
        {
            int bytesConsumed = 0;
            bool isFinal = false;

            // There's a special case here - if we've reached the end of the message and there's no optional
            // CRLF, then we're out of bytes to read, but we have finished the message. 
            //
            // If IsWaitingForEndOfMessage is true and we're at the end of the stream, then we're going to 
            // call into the parser again with an empty array as the buffer to signal the end of the parse. 
            // Then the final boundary segment will be marked as complete. 
            if (bytesRead == 0 && !this._mimeParser.IsWaitingForEndOfMessage)
            {
                this.CleanupCurrentBodyPart();
                throw new IOException(Resources.ReadAsMimeMultipartUnexpectedTermination);
            }

            // Make sure we remove an old array segments.
            this._currentBodyPart.Segments.Clear();

            while (this._mimeParser.CanParseMore(bytesRead, bytesConsumed))
            {
                this._mimeStatus = this._mimeParser.ParseBuffer(data, bytesRead, ref bytesConsumed, out this._parsedBodyPart[0], out this._parsedBodyPart[1], out isFinal);
                if (this._mimeStatus != MimeMultipartParser.State.BodyPartCompleted && this._mimeStatus != MimeMultipartParser.State.NeedMoreData)
                {
                    this.CleanupCurrentBodyPart();
                    throw Error.InvalidOperation(Resources.ReadAsMimeMultipartParseError, bytesConsumed, data);
                }

                // First body is empty preamble which we just ignore
                if (this._isFirst)
                {
                    if (this._mimeStatus == MimeMultipartParser.State.BodyPartCompleted)
                    {
                        this._isFirst = false;
                    }

                    continue;
                }

                // Parse the two array segments containing parsed body parts that the MIME parser gave us
                foreach (ArraySegment<byte> part in this._parsedBodyPart)
                {
                    if (part.Count == 0)
                    {
                        continue;
                    }

                    if (this._bodyPartHeaderStatus != ParserState.Done)
                    {
                        int headerConsumed = part.Offset;
                        this._bodyPartHeaderStatus = this._currentBodyPart.HeaderParser.ParseBuffer(part.Array, part.Count + part.Offset, ref headerConsumed);
                        if (this._bodyPartHeaderStatus == ParserState.Done)
                        {
                            // Add the remainder as body part content
                            this._currentBodyPart.Segments.Add(new ArraySegment<byte>(part.Array, headerConsumed, part.Count + part.Offset - headerConsumed));
                        }
                        else if (this._bodyPartHeaderStatus != ParserState.NeedMoreData)
                        {
                            this.CleanupCurrentBodyPart();
                            throw Error.InvalidOperation(Resources.ReadAsMimeMultipartHeaderParseError, headerConsumed, part.Array);
                        }
                    }
                    else
                    {
                        // Add the data as body part content
                        this._currentBodyPart.Segments.Add(part);
                    }
                }

                if (this._mimeStatus == MimeMultipartParser.State.BodyPartCompleted)
                {
                    // If body is completed then swap current body part
                    MimeBodyPart completed = this._currentBodyPart;
                    completed.IsComplete = true;
                    completed.IsFinal = isFinal;

                    this._currentBodyPart = new MimeBodyPart(this._streamProvider, this._maxBodyPartHeaderSize, this._content);

                    this._mimeStatus = MimeMultipartParser.State.NeedMoreData;
                    this._bodyPartHeaderStatus = ParserState.NeedMoreData;
                    yield return completed;
                }
                else
                {
                    // Otherwise return what we have 
                    yield return this._currentBodyPart;
                }
            }
        }
        /// <summary>
        /// Parses the data provided and generates parsed MIME body part bodies in the form of <see cref="ArraySegment{T}"/> which are ready to
        /// write to the output stream.
        /// </summary>
        /// <param name="data">The data to parse</param>
        /// <param name="bytesRead">The number of bytes available in the input data</param>
        /// <returns>Parsed <see cref="MimeBodyPart"/> instances.</returns>
        public IEnumerable <MimeBodyPart> ParseBuffer(byte[] data, int bytesRead)
        {
            int  bytesConsumed = 0;
            bool isFinal       = false;

            if (bytesRead == 0)
            {
                CleanupCurrentBodyPart();
                throw new IOException(Properties.Resources.ReadAsMimeMultipartUnexpectedTermination);
            }

            // Make sure we remove an old array segments.
            _currentBodyPart.Segments.Clear();

            while (bytesConsumed < bytesRead)
            {
                _mimeStatus = _mimeParser.ParseBuffer(data, bytesRead, ref bytesConsumed, out _parsedBodyPart[0], out _parsedBodyPart[1], out isFinal);
                if (_mimeStatus != MimeMultipartParser.State.BodyPartCompleted && _mimeStatus != MimeMultipartParser.State.NeedMoreData)
                {
                    CleanupCurrentBodyPart();
                    throw new IOException(RS.Format(Properties.Resources.ReadAsMimeMultipartParseError, bytesConsumed, data));
                }

                // First body is empty preamble which we just ignore
                if (_isFirst)
                {
                    if (_mimeStatus == MimeMultipartParser.State.BodyPartCompleted)
                    {
                        _isFirst = false;
                    }

                    continue;
                }

                // Parse the two array segments containing parsed body parts that the MIME parser gave us
                foreach (ArraySegment <byte> part in _parsedBodyPart)
                {
                    if (part.Count == 0)
                    {
                        continue;
                    }

                    if (_bodyPartHeaderStatus != ParserState.Done)
                    {
                        int headerConsumed = part.Offset;
                        _bodyPartHeaderStatus = _currentBodyPart.HeaderParser.ParseBuffer(part.Array, part.Count + part.Offset, ref headerConsumed);
                        if (_bodyPartHeaderStatus == ParserState.Done)
                        {
                            // Add the remainder as body part content
                            _currentBodyPart.Segments.Add(new ArraySegment <byte>(part.Array, headerConsumed, part.Count + part.Offset - headerConsumed));
                        }
                        else if (_bodyPartHeaderStatus != ParserState.NeedMoreData)
                        {
                            CleanupCurrentBodyPart();
                            throw new IOException(RS.Format(Properties.Resources.ReadAsMimeMultipartHeaderParseError, headerConsumed, part.Array));
                        }
                    }
                    else
                    {
                        // Add the data as body part content
                        _currentBodyPart.Segments.Add(part);
                    }
                }

                if (_mimeStatus == MimeMultipartParser.State.BodyPartCompleted)
                {
                    // If body is completed then swap current body part
                    MimeBodyPart completed = _currentBodyPart;
                    completed.IsComplete = true;
                    completed.IsFinal    = isFinal;

                    _currentBodyPart      = new MimeBodyPart(_streamProvider, _maxBodyPartHeaderSize);
                    _mimeStatus           = MimeMultipartParser.State.NeedMoreData;
                    _bodyPartHeaderStatus = ParserState.NeedMoreData;
                    yield return(completed);
                }
                else
                {
                    // Otherwise return what we have
                    yield return(_currentBodyPart);
                }
            }
        }