Exemplo n.º 1
0
        public MimeContent GetNextContent()
        {
            string key = "";
            string value = "";

            MimeContent mime = new MimeContent();
            MemoryStream ms = new MemoryStream();

            do
            {
                switch (state)
                {
                    case MimeParserState.ReadBoundary:
                        if (buffer[idx] == '\r')
                            idx++;
                        else if (buffer[idx] != '\n')
                            value += (char)buffer[idx++];
                        else
                        {
                            idx++;
                            value = "";
                            state = MimeParserState.ReadHeaderKey;
                        }
                        break;
                    case MimeParserState.ReadHeaderKey:
                        if (buffer[idx] == '\r')
                            idx++;
                        else if (buffer[idx] == '\n')
                        {
                            idx++;
                            state = MimeParserState.ReadContent;
                        }
                        else if (buffer[idx] == ':')
                            idx++;
                        else if (buffer[idx] != ' ')
                            key += (char)buffer[idx++];
                        else
                        {
                            idx++;
                            value = "";
                            state = MimeParserState.ReadHeaderValue;
                        }
                        break;
                    case MimeParserState.ReadHeaderValue:
                        if (buffer[idx] == '\r')
                            idx++;
                        else if (buffer[idx] != '\n')
                            value += (char)buffer[idx++];
                        else
                        {
                            idx++;
                            mime.Headers.Add(key, value);
                            key = "";
                            state = MimeParserState.ReadHeaderKey;
                        }
                        break;
                    case MimeParserState.ReadContent:

                        if (buffer[idx] == '\n' && idx < buffer.Length - _boundaryBytes.Length - 2)
                        {
                            // detect if next line is boundary line or content

                            bool foundBound = true;

                            for (int i = 0; i < _boundaryBytes.Length; i++)
                            {
                                if (_boundaryBytes[i] != buffer[idx + i + 1])
                                {
                                    foundBound = false;
                                    break;
                                }
                            }

                            if (!foundBound)
                            {
                                ms.WriteByte(buffer[idx++]);
                                continue;
                            }
                            else
                            {
                                idx++;
                                state = MimeParserState.ReadBoundary;

                                if (ms.ToArray().Length > 1)
                                {
                                    mime.Content = new byte[ms.ToArray().Length - 1];
                                    Array.Copy(ms.ToArray(), mime.Content, ms.ToArray().Length - 1);
                                }

                                return mime;
                            }
                        }
                        else
                        {
                            ms.WriteByte(buffer[idx++]);
                        }

                        break;
                }
            }
            while (idx < buffer.Length);

            return null;
        }
Exemplo n.º 2
0
        unsafe MimeEntity ParseEntity(byte* inbuf)
        {
            state = MimeParserState.Headers;
            while (state < MimeParserState.Content) {
                if (Step (inbuf) == MimeParserState.Error)
                    throw new Exception ("Failed to parse entity headers.");
            }

            var type = GetContentType (null);
            BoundaryType found;

            // Note: we pass 'false' as the 'toplevel' argument here because
            // we want the entity to consume all of the headers.
            var entity = MimeEntity.Create (options, type, headers, false);
            if (entity is Multipart)
                found = ConstructMultipart ((Multipart) entity, inbuf);
            else if (entity is MessagePart)
                found = ConstructMessagePart ((MessagePart) entity, inbuf);
            else
                found = ConstructMimePart ((MimePart) entity, inbuf);

            if (found != BoundaryType.Eos)
                state = MimeParserState.Complete;
            else
                state = MimeParserState.Eos;

            return entity;
        }
Exemplo n.º 3
0
        unsafe MimeMessage ParseMessage(byte* inbuf)
        {
            BoundaryType found;

            // scan the from-line if we are parsing an mbox
            while (state != MimeParserState.MessageHeaders) {
                switch (Step (inbuf)) {
                case MimeParserState.Error:
                    throw new Exception ("Failed to find mbox From marker.");
                case MimeParserState.Eos:
                    throw new Exception ("End of stream.");
                }
            }

            // parse the headers
            while (state < MimeParserState.Content) {
                if (Step (inbuf) == MimeParserState.Error)
                    throw new Exception ("Failed to parse message headers.");
            }

            var message = new MimeMessage (options, headers);

            if (format == MimeFormat.Mbox && options.RespectContentLength) {
                bounds[0].ContentEnd = -1;

                for (int i = 0; i < headers.Count; i++) {
                    if (icase.Compare (headers[i].Field, "Content-Length") != 0)
                        continue;

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

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

                    long endOffset = GetOffset (inputIndex) + length;

                    bounds[0].ContentEnd = endOffset;
                    break;
                }
            }

            var type = GetContentType (null);
            var entity = MimeEntity.Create (options, type, headers, true);
            message.Body = entity;

            if (entity is Multipart)
                found = ConstructMultipart ((Multipart) entity, inbuf);
            else if (entity is MessagePart)
                found = ConstructMessagePart ((MessagePart) entity, inbuf);
            else
                found = ConstructMimePart ((MimePart) entity, inbuf);

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

            return message;
        }
Exemplo n.º 4
0
        unsafe BoundaryType ConstructMessagePart(MessagePart part, byte* inbuf)
        {
            BoundaryType found;

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

                if (ReadAhead (inbuf, atleast, 0) <= 0)
                    return BoundaryType.Eos;

                byte* start = inbuf + inputIndex;
                byte* inend = inbuf + inputEnd;
                byte* inptr = start;

                *inend = (byte) '\n';

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

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

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

            // parse the headers...
            state = MimeParserState.Headers;
            if (Step (inbuf) == 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?
                return BoundaryType.Eos;
            }

            var message = new MimeMessage (options, headers);
            var type = GetContentType (null);

            var entity = MimeEntity.Create (options, type, headers, true);
            message.Body = entity;

            if (entity is Multipart)
                found = ConstructMultipart ((Multipart) entity, inbuf);
            else if (entity is MessagePart)
                found = ConstructMessagePart ((MessagePart) entity, inbuf);
            else
                found = ConstructMimePart ((MimePart) entity, inbuf);

            part.Message = message;

            return found;
        }
Exemplo n.º 5
0
        unsafe BoundaryType MultipartScanSubparts(Multipart multipart, byte* inbuf)
        {
            BoundaryType found;

            do {
                // skip over the boundary marker
                if (!SkipLine (inbuf))
                    return BoundaryType.Eos;

                // parse the headers
                state = MimeParserState.Headers;
                if (Step (inbuf) == MimeParserState.Error)
                    return BoundaryType.Eos;

                //if (state == ParserState.Complete && headers.Count == 0)
                //	return BoundaryType.EndBoundary;

                var type = GetContentType (multipart.ContentType);
                var entity = MimeEntity.Create (options, type, headers, false);

                if (entity is Multipart)
                    found = ConstructMultipart ((Multipart) entity, inbuf);
                else if (entity is MessagePart)
                    found = ConstructMessagePart ((MessagePart) entity, inbuf);
                else
                    found = ConstructMimePart ((MimePart) entity, inbuf);

                multipart.Add (entity);
            } while (found == BoundaryType.ImmediateBoundary);

            return found;
        }
Exemplo n.º 6
0
        /// <summary>
        /// Sets the stream to parse.
        /// </summary>
        /// <param name="options">The parser options.</param>
        /// <param name="stream">The stream to parse.</param>
        /// <param name="format">The format of the stream.</param>
        /// <param name="persistent"><c>true</c> if the stream is persistent; otherwise <c>false</c>.</param>
        /// <remarks>
        /// <para>If <paramref name="persistent"/> is <c>true</c> and <paramref name="stream"/> is seekable, then
        /// the <see cref="MimeParser"/> will not copy the content of <see cref="MimePart"/>s into memory. Instead,
        /// it will use a <see cref="MimeKit.IO.BoundStream"/> to reference a substream of <paramref name="stream"/>.
        /// This has the potential to not only save mmeory usage, but also improve <see cref="MimeParser"/>
        /// performance.</para>
        /// <para>It should be noted, however, that disposing <paramref name="stream"/> will make it impossible
        /// for <see cref="ContentObject"/> to read the content.</para>
        /// </remarks>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        public void SetStream(ParserOptions options, Stream stream, MimeFormat format, bool persistent)
        {
            if (options == null)
                throw new ArgumentNullException ("options");

            if (stream == null)
                throw new ArgumentNullException ("stream");

            this.persistent = persistent && stream.CanSeek;
            this.options = options.Clone ();
            this.format = format;
            this.stream = stream;

            inputIndex = inputStart;
            inputEnd = inputStart;

            mboxMarkerOffset = 0;
            mboxMarkerLength = 0;

            offset = stream.CanSeek ? stream.Position : 0;
            headers.Clear ();
            headerOffset = 0;
            headerIndex = 0;

            bounds.Clear ();
            if (format == MimeFormat.Mbox) {
                bounds.Add (Boundary.CreateMboxBoundary ());
                mboxMarkerBuffer = new byte[ReadAheadSize];
                state = MimeParserState.MboxMarker;
            } else {
                state = MimeParserState.Initialized;
            }
        }
Exemplo n.º 7
0
        unsafe void StepMboxMarker(byte* inbuf)
        {
            bool complete = false;
            bool needInput;
            int left = 0;

            mboxMarkerLength = 0;

            do {
                if (ReadAhead (inbuf, Math.Max (ReadAheadSize, left), 0) <= left) {
                    // failed to find a From line; EOF reached
                    state = MimeParserState.Error;
                    inputIndex = inputEnd;
                    return;
                }

                needInput = false;

                byte* inptr = inbuf + inputIndex;
                byte* inend = inbuf + inputEnd;

                *inend = (byte) '\n';

                while (inptr < inend) {
                    byte* start = inptr;

                    // scan for the end of the line
                    while (*inptr != (byte) '\n')
                        inptr++;

                    long length = inptr - start;

                    // consume the '\n'
                    inptr++;

                    if (inptr >= inend) {
                        // we don't have enough input data
                        inputIndex = (int) (start - inbuf);
                        left = (int) length;
                        needInput = true;
                        break;
                    }

                    if (length >= 5 && IsMboxMarker (start)) {
                        long startIndex = start - inbuf;

                        mboxMarkerOffset = GetOffset ((int) startIndex);
                        mboxMarkerLength = (int) length;

                        if (mboxMarkerBuffer.Length < mboxMarkerLength)
                            Array.Resize (ref mboxMarkerBuffer, mboxMarkerLength);

                        Array.Copy (input, startIndex, mboxMarkerBuffer, 0, length);
                        complete = true;
                        break;
                    }
                }

                if (!needInput) {
                    inputIndex = (int) (inptr - inbuf);
                    left = 0;
                }
            } while (!complete);

            state = MimeParserState.MessageHeaders;
        }
Exemplo n.º 8
0
        unsafe void StepHeaders(byte* inbuf)
        {
            bool scanningFieldName = true;
            bool checkFolded = false;
            bool midline = false;
            bool valid = true;
            int left = 0;
            long length;
            bool eoln;

            ResetRawHeaderData ();
            headers.Clear ();

            do {
                if (ReadAhead (inbuf, Math.Max (ReadAheadSize, left), 0) <= left) {
                    // failed to find the end of the headers; EOF reached
                    state = MimeParserState.Error;
                    inputIndex = inputEnd;
                    return;
                }

                byte* inptr = inbuf + inputIndex;
                byte* inend = inbuf + inputEnd;
                bool needInput = false;

                *inend = (byte) '\n';

                while (inptr < inend) {
                    byte* start = inptr;

                    // if we are scanning a new line, check for a folded header
                    if (!midline && checkFolded && !(*inptr).IsBlank ()) {
                        ParseAndAppendHeader ();

                        headerOffset = GetOffset ((int) (inptr - inbuf));
                        scanningFieldName = true;
                        checkFolded = false;
                        valid = true;
                    }

                    eoln = IsEoln (inptr);
                    if (scanningFieldName && !eoln) {
                        // scan and validate the field name
                        if (*inptr != (byte) ':') {
                            *inend = (byte) ':';

                            while (*inptr != (byte) ':') {
                                if (IsBlankOrControl (*inptr)) {
                                    valid = false;
                                    break;
                                }

                                inptr++;
                            }

                            if (inptr == inend) {
                                // we don't have enough input data
                                left = (int) (inend - start);
                                inputIndex = (int) (start - inbuf);
                                needInput = true;
                                break;
                            }

                            *inend = (byte) '\n';
                        } else {
                            valid = false;
                        }

                        if (!valid) {
                            length = inptr - start;

                            if (format == MimeFormat.Mbox && length == 4 && IsMboxMarker (start)) {
                                // we've found the start of the next message...
                                inputIndex = (int) (start - inbuf);
                                state = MimeParserState.Complete;
                                headerIndex = 0;
                                return;
                            }

                            if (state == MimeParserState.MessageHeaders && headers.Count == 0) {
                                // ignore From-lines that might appear at the start of a message
                                if (length != 4 || !IsMboxMarker (start)) {
                                    inputIndex = (int) (start - inbuf);
                                    state = MimeParserState.Error;
                                    headerIndex = 0;
                                    return;
                                }
                            }
                        }
                    }

                    scanningFieldName = false;

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

                    if (inptr == inend) {
                        // we didn't manage to slurp up a full line, save what we have and refill our input buffer
                        length = inptr - start;

                        if (inptr > start) {
                            // Note: if the last byte we got was a '\r', rewind a byte
                            inptr--;
                            if (*inptr == (byte) '\r')
                                length--;
                            else
                                inptr++;
                        }

                        AppendRawHeaderData ((int) (start - inbuf), (int) length);
                        inputIndex = (int) (inptr - inbuf);
                        left = (int) (inend - inptr);
                        needInput = true;
                        midline = true;
                        break;
                    }

                    // check to see if we've reached the end of the headers
                    if (!midline && IsEoln (start)) {
                        inputIndex = (int) (inptr - inbuf) + 1;
                        state = MimeParserState.Content;
                        ParseAndAppendHeader ();
                        headerIndex = 0;
                        return;
                    }

                    length = (inptr + 1) - start;

                    AppendRawHeaderData ((int) (start - inbuf), (int) length);
                    checkFolded = true;
                    midline = false;
                    inptr++;
                }

                if (!needInput) {
                    inputIndex = (int) (inptr - inbuf);
                    left = (int) (inend - inptr);
                }
            } while (true);
        }
Exemplo n.º 9
0
        unsafe MimeParserState Step(byte* inbuf)
        {
            switch (state) {
            case MimeParserState.Error:
                break;
            case MimeParserState.Initialized:
                state = format == MimeFormat.Mbox ? MimeParserState.MboxMarker : MimeParserState.MessageHeaders;
                break;
            case MimeParserState.MboxMarker:
                StepMboxMarker (inbuf);
                break;
            case MimeParserState.MessageHeaders:
            case MimeParserState.Headers:
                StepHeaders (inbuf);
                break;
            case MimeParserState.Content:
                break;
            case MimeParserState.Complete:
                break;
            case MimeParserState.Eos:
                break;
            default:
                throw new ArgumentOutOfRangeException ();
            }

            return state;
        }
Exemplo n.º 10
0
        public MimeContent GetNextContent()
        {
            string key   = "";
            string value = "";

            MimeContent  mime = new MimeContent();
            MemoryStream ms   = new MemoryStream();

            do
            {
                switch (state)
                {
                case MimeParserState.ReadBoundary:
                    if (buffer[idx] == '\r')
                    {
                        idx++;
                    }
                    else if (buffer[idx] != '\n')
                    {
                        value += (char)buffer[idx++];
                    }
                    else
                    {
                        idx++;
                        value = "";
                        state = MimeParserState.ReadHeaderKey;
                    }
                    break;

                case MimeParserState.ReadHeaderKey:
                    if (buffer[idx] == '\r')
                    {
                        idx++;
                    }
                    else if (buffer[idx] == '\n')
                    {
                        idx++;
                        state = MimeParserState.ReadContent;
                    }
                    else if (buffer[idx] == ':')
                    {
                        idx++;
                    }
                    else if (buffer[idx] != ' ')
                    {
                        key += (char)buffer[idx++];
                    }
                    else
                    {
                        idx++;
                        value = "";
                        state = MimeParserState.ReadHeaderValue;
                    }
                    break;

                case MimeParserState.ReadHeaderValue:
                    if (buffer[idx] == '\r')
                    {
                        idx++;
                    }
                    else if (buffer[idx] != '\n')
                    {
                        value += (char)buffer[idx++];
                    }
                    else
                    {
                        idx++;
                        mime.Headers.Add(key, value);
                        key   = "";
                        state = MimeParserState.ReadHeaderKey;
                    }
                    break;

                case MimeParserState.ReadContent:

                    if (buffer[idx] == '\n' && idx < buffer.Length - _boundaryBytes.Length - 2)
                    {
                        // detect if next line is boundary line or content


                        bool foundBound = true;

                        for (int i = 0; i < _boundaryBytes.Length; i++)
                        {
                            if (_boundaryBytes[i] != buffer[idx + i + 1])
                            {
                                foundBound = false;
                                break;
                            }
                        }

                        if (!foundBound)
                        {
                            ms.WriteByte(buffer[idx++]);
                            continue;
                        }
                        else
                        {
                            idx++;
                            state = MimeParserState.ReadBoundary;

                            if (ms.ToArray().Length > 1)
                            {
                                mime.Content = new byte[ms.ToArray().Length - 1];
                                Array.Copy(ms.ToArray(), mime.Content, ms.ToArray().Length - 1);
                            }

                            return(mime);
                        }
                    }
                    else
                    {
                        ms.WriteByte(buffer[idx++]);
                    }

                    break;
                }
            }while (idx < buffer.Length);

            return(null);
        }