XmlMtomReader.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / WCF / Serialization / System / Xml / XmlMtomReader.cs / 1305376 / XmlMtomReader.cs

                            //------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------

namespace System.Xml 
{
    using System; 
    using System.Collections.Generic; 
    using System.Globalization;
    using System.IO; 
    using System.Runtime;
    using System.Runtime.Serialization;
    using System.Text;
    using System.Security; 
    using System.Security.Permissions;
 
    public interface IXmlMtomReaderInitializer 
    {
        void SetInput(byte[] buffer, int offset, int count, Encoding[] encodings, string contentType, XmlDictionaryReaderQuotas quotas, int maxBufferSize, OnXmlDictionaryReaderClose onClose); 
        void SetInput(Stream stream, Encoding[] encodings, string contentType, XmlDictionaryReaderQuotas quotas, int maxBufferSize, OnXmlDictionaryReaderClose onClose);
    }

    class XmlMtomReader : XmlDictionaryReader, IXmlLineInfo, IXmlMtomReaderInitializer 
    {
        Encoding[] encodings; 
        XmlDictionaryReader xmlReader; 
        XmlDictionaryReader infosetReader;
        MimeReader mimeReader; 
        Dictionary mimeParts;
        OnXmlDictionaryReaderClose onClose;
        bool readingBinaryElement;
        int maxBufferSize; 
        int bufferRemaining;
        MimePart part; 
 
        public XmlMtomReader()
        { 
        }

        internal static void DecrementBufferQuota(int maxBuffer, ref int remaining, int size)
        { 
            if (remaining - size <= 0)
            { 
                remaining = 0; 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomBufferQuotaExceeded, maxBuffer)));
            } 
            else
            {
                remaining -= size;
            } 
        }
 
        void SetReadEncodings(Encoding[] encodings) 
        {
            if (encodings == null) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encodings");

            for (int i = 0; i < encodings.Length; i++)
            { 
                if (encodings[i] == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(String.Format(CultureInfo.InvariantCulture, "encodings[{0}]", i)); 
            } 

            this.encodings = new Encoding[encodings.Length]; 
            encodings.CopyTo(this.encodings, 0);
        }

        void CheckContentType(string contentType) 
        {
            if (contentType != null && contentType.Length == 0) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.MtomContentTypeInvalid), "contentType")); 
        }
 
        public void SetInput(byte[] buffer, int offset, int count, Encoding[] encodings, string contentType, XmlDictionaryReaderQuotas quotas, int maxBufferSize, OnXmlDictionaryReaderClose onClose)
        {
            SetInput(new MemoryStream(buffer, offset, count), encodings, contentType, quotas, maxBufferSize, onClose);
        } 

        public void SetInput(Stream stream, Encoding[] encodings, string contentType, XmlDictionaryReaderQuotas quotas, int maxBufferSize, OnXmlDictionaryReaderClose onClose) 
        { 
            SetReadEncodings(encodings);
            CheckContentType(contentType); 
            Initialize(stream, contentType, quotas, maxBufferSize);
            this.onClose = onClose;
        }
 
        void Initialize(Stream stream, string contentType, XmlDictionaryReaderQuotas quotas, int maxBufferSize)
        { 
            if (stream == null) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
 
            this.maxBufferSize = maxBufferSize;
            this.bufferRemaining = maxBufferSize;

            string boundary, start, startInfo; 

            if (contentType == null) 
            { 
                MimeMessageReader messageReader = new MimeMessageReader(stream);
                MimeHeaders messageHeaders = messageReader.ReadHeaders(this.maxBufferSize, ref this.bufferRemaining); 
                ReadMessageMimeVersionHeader(messageHeaders.MimeVersion);
                ReadMessageContentTypeHeader(messageHeaders.ContentType, out boundary, out start, out startInfo);
                stream = messageReader.GetContentStream();
                if (stream == null) 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageInvalidContent)));
            } 
            else 
            {
                ReadMessageContentTypeHeader(new ContentTypeHeader(contentType), out boundary, out start, out startInfo); 
            }

            this.mimeReader = new MimeReader(stream, boundary);
            this.mimeParts = null; 
            this.readingBinaryElement = false;
 
            MimePart infosetPart = (start == null) ? ReadRootMimePart() : ReadMimePart(GetStartUri(start)); 
            byte[] infosetBytes = infosetPart.GetBuffer(this.maxBufferSize, ref this.bufferRemaining);
            int infosetByteCount = (int)infosetPart.Length; 

            Encoding encoding = ReadRootContentTypeHeader(infosetPart.Headers.ContentType, this.encodings, startInfo);
            CheckContentTransferEncodingOnRoot(infosetPart.Headers.ContentTransferEncoding);
 
            IXmlTextReaderInitializer initializer = xmlReader as IXmlTextReaderInitializer;
 
            if (initializer != null) 
                initializer.SetInput(infosetBytes, 0, infosetByteCount, encoding, quotas, null);
            else 
                xmlReader = XmlDictionaryReader.CreateTextReader(infosetBytes, 0, infosetByteCount, encoding, quotas, null);
        }

        public override XmlDictionaryReaderQuotas Quotas 
        {
            get 
            { 
                return this.xmlReader.Quotas;
            } 
        }

        void ReadMessageMimeVersionHeader(MimeVersionHeader header)
        { 
            if (header != null && header.Version != MimeVersionHeader.Default.Version)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageInvalidMimeVersion, header.Version, MimeVersionHeader.Default.Version))); 
        } 

        void ReadMessageContentTypeHeader(ContentTypeHeader header, out string boundary, out string start, out string startInfo) 
        {
            if (header == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageContentTypeNotFound)));
 
            if (String.Compare(MtomGlobals.MediaType, header.MediaType, StringComparison.OrdinalIgnoreCase) != 0
                || String.Compare(MtomGlobals.MediaSubtype, header.MediaSubtype, StringComparison.OrdinalIgnoreCase) != 0) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageNotMultipart, MtomGlobals.MediaType, MtomGlobals.MediaSubtype))); 

            string type; 
            if (!header.Parameters.TryGetValue(MtomGlobals.TypeParam, out type) || MtomGlobals.XopType != type)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageNotApplicationXopXml, MtomGlobals.XopType)));

            if (!header.Parameters.TryGetValue(MtomGlobals.BoundaryParam, out boundary)) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageRequiredParamNotSpecified, MtomGlobals.BoundaryParam)));
            if (!MailBnfHelper.IsValidMimeBoundary(boundary)) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomBoundaryInvalid, boundary))); 

            if (!header.Parameters.TryGetValue(MtomGlobals.StartParam, out start)) 
                start = null;

            if (!header.Parameters.TryGetValue(MtomGlobals.StartInfoParam, out startInfo))
                startInfo = null; 
        }
 
        Encoding ReadRootContentTypeHeader(ContentTypeHeader header, Encoding[] expectedEncodings, string expectedType) 
        {
            if (header == null) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootContentTypeNotFound)));

            if (String.Compare(MtomGlobals.XopMediaType, header.MediaType, StringComparison.OrdinalIgnoreCase) != 0
                || String.Compare(MtomGlobals.XopMediaSubtype, header.MediaSubtype, StringComparison.OrdinalIgnoreCase) != 0) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootNotApplicationXopXml, MtomGlobals.XopMediaType, MtomGlobals.XopMediaSubtype)));
 
            string charset; 
            if (!header.Parameters.TryGetValue(MtomGlobals.CharsetParam, out charset)
                || charset == null || charset.Length == 0) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootRequiredParamNotSpecified, MtomGlobals.CharsetParam)));
            Encoding encoding = null;
            for (int i = 0; i < encodings.Length; i++)
            { 
                if (String.Compare(charset, expectedEncodings[i].WebName, StringComparison.OrdinalIgnoreCase) == 0)
                { 
                    encoding = expectedEncodings[i]; 
                    break;
                } 
            }
            if (encoding == null)
            {
                // Check for alternate names 
                if (String.Compare(charset, "utf-16LE", StringComparison.OrdinalIgnoreCase) == 0)
                { 
                    for (int i = 0; i < encodings.Length; i++) 
                    {
                        if (String.Compare(expectedEncodings[i].WebName, Encoding.Unicode.WebName, StringComparison.OrdinalIgnoreCase) == 0) 
                        {
                            encoding = expectedEncodings[i];
                            break;
                        } 
                    }
                } 
                else if (String.Compare(charset, "utf-16BE", StringComparison.OrdinalIgnoreCase) == 0) 
                {
                    for (int i = 0; i < encodings.Length; i++) 
                    {
                        if (String.Compare(expectedEncodings[i].WebName, Encoding.BigEndianUnicode.WebName, StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            encoding = expectedEncodings[i]; 
                            break;
                        } 
                    } 
                }
 
                if (encoding == null)
                {
                    StringBuilder expectedCharSetStr = new StringBuilder();
                    for (int i = 0; i < encodings.Length; i++) 
                    {
                        if (expectedCharSetStr.Length != 0) 
                            expectedCharSetStr.Append(" | "); 
                        expectedCharSetStr.Append(encodings[i].WebName);
                    } 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootUnexpectedCharset, charset, expectedCharSetStr.ToString())));
                }
            }
 
            if (expectedType != null)
            { 
                string rootType; 
                if (!header.Parameters.TryGetValue(MtomGlobals.TypeParam, out rootType)
                    || rootType == null || rootType.Length == 0) 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootRequiredParamNotSpecified, MtomGlobals.TypeParam)));
                if (rootType != expectedType)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootUnexpectedType, rootType, expectedType)));
            } 

            return encoding; 
        } 

        // 7bit is default encoding in the absence of content-transfer-encoding header 
        void CheckContentTransferEncodingOnRoot(ContentTransferEncodingHeader header)
        {
            if (header != null && header.ContentTransferEncoding == ContentTransferEncoding.Other)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomContentTransferEncodingNotSupported, 
                                                                                      header.Value,
                                                                                      ContentTransferEncodingHeader.SevenBit.ContentTransferEncodingValue, 
                                                                                      ContentTransferEncodingHeader.EightBit.ContentTransferEncodingValue, 
                                                                                      ContentTransferEncodingHeader.Binary.ContentTransferEncodingValue)));
        } 

        void CheckContentTransferEncodingOnBinaryPart(ContentTransferEncodingHeader header)
        {
            if (header == null) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomContentTransferEncodingNotPresent,
                    ContentTransferEncodingHeader.Binary.ContentTransferEncodingValue))); 
            else if (header.ContentTransferEncoding != ContentTransferEncoding.Binary) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomInvalidTransferEncodingForMimePart,
                    header.Value, ContentTransferEncodingHeader.Binary.ContentTransferEncodingValue))); 
        }

        string GetStartUri(string startUri)
        { 
            if (startUri.StartsWith("<", StringComparison.Ordinal))
            { 
                if (startUri.EndsWith(">", StringComparison.Ordinal)) 
                    return startUri;
                else 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomInvalidStartUri, startUri)));
            }
            else
                return String.Format(CultureInfo.InvariantCulture, "<{0}>", startUri); 
        }
 
        public override bool Read() 
        {
            bool retVal = xmlReader.Read(); 

            if (xmlReader.NodeType == XmlNodeType.Element)
            {
                XopIncludeReader binaryDataReader = null; 
                if (xmlReader.IsStartElement(MtomGlobals.XopIncludeLocalName, MtomGlobals.XopIncludeNamespace))
                { 
                    string uri = null; 
                    while (xmlReader.MoveToNextAttribute())
                    { 
                        if (xmlReader.LocalName == MtomGlobals.XopIncludeHrefLocalName && xmlReader.NamespaceURI == MtomGlobals.XopIncludeHrefNamespace)
                            uri = xmlReader.Value;
                        else if (xmlReader.NamespaceURI == MtomGlobals.XopIncludeNamespace)
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomXopIncludeInvalidXopAttributes, xmlReader.LocalName, MtomGlobals.XopIncludeNamespace))); 
                    }
                    if (uri == null) 
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomXopIncludeHrefNotSpecified, MtomGlobals.XopIncludeHrefLocalName))); 

                    MimePart mimePart = ReadMimePart(uri); 

                    CheckContentTransferEncodingOnBinaryPart(mimePart.Headers.ContentTransferEncoding);

                    this.part = mimePart; 
                    binaryDataReader = new XopIncludeReader(mimePart, xmlReader);
                    binaryDataReader.Read(); 
 
                    xmlReader.MoveToElement();
                    if (xmlReader.IsEmptyElement) 
                    {
                        xmlReader.Read();
                    }
                    else 
                    {
                        int xopDepth = xmlReader.Depth; 
                        xmlReader.ReadStartElement(); 

                        while (xmlReader.Depth > xopDepth) 
                        {
                            if (xmlReader.IsStartElement() && xmlReader.NamespaceURI == MtomGlobals.XopIncludeNamespace)
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomXopIncludeInvalidXopElement, xmlReader.LocalName, MtomGlobals.XopIncludeNamespace)));
 
                            xmlReader.Skip();
                        } 
 
                        xmlReader.ReadEndElement();
                    } 
                }

                if (binaryDataReader != null)
                { 
                    this.xmlReader.MoveToContent();
                    this.infosetReader = this.xmlReader; 
                    this.xmlReader = binaryDataReader; 
                    binaryDataReader = null;
                } 
            }

            if (xmlReader.ReadState == ReadState.EndOfFile && infosetReader != null)
            { 
                // Read past the containing EndElement if necessary
                if (!retVal) 
                    retVal = infosetReader.Read(); 

                this.part.Release(this.maxBufferSize, ref this.bufferRemaining); 
                xmlReader = infosetReader;
                infosetReader = null;
            }
 
            return retVal;
        } 
 
        MimePart ReadMimePart(string uri)
        { 
            MimePart part = null;

            if (uri == null || uri.Length == 0)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomInvalidEmptyURI))); 

            string contentID = null; 
            if (uri.StartsWith(MimeGlobals.ContentIDScheme, StringComparison.Ordinal)) 
                contentID = String.Format(CultureInfo.InvariantCulture, "<{0}>", Uri.UnescapeDataString(uri.Substring(MimeGlobals.ContentIDScheme.Length)));
            else if (uri.StartsWith("<", StringComparison.Ordinal)) 
                contentID = uri;

            if (contentID == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomInvalidCIDUri, uri))); 

            if (mimeParts != null && mimeParts.TryGetValue(contentID, out part)) 
            { 
                if (part.ReferencedFromInfoset)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMimePartReferencedMoreThanOnce, contentID))); 
            }
            else
            {
                while (part == null && mimeReader.ReadNextPart()) 
                {
                    MimeHeaders headers = mimeReader.ReadHeaders(this.maxBufferSize, ref this.bufferRemaining); 
                    Stream contentStream = mimeReader.GetContentStream(); 
                    if (contentStream == null)
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageInvalidContentInMimePart))); 

                    ContentIDHeader contentIDHeader = (headers == null) ? null : headers.ContentID;
                    if (contentIDHeader == null || contentIDHeader.Value == null)
                    { 
                        // Skip content if Content-ID header is not present
                        int size = 256; 
                        byte[] bytes = new byte[size]; 

                        int read = 0; 
                        do
                        {
                            read = contentStream.Read(bytes, 0, size);
                        } 
                        while (read > 0);
                        continue; 
                    } 

                    string currentContentID = headers.ContentID.Value; 
                    MimePart currentPart = new MimePart(contentStream, headers);
                    if (mimeParts == null)
                        mimeParts = new Dictionary();
                    mimeParts.Add(currentContentID, currentPart); 

                    if (currentContentID.Equals(contentID)) 
                        part = currentPart; 
                    else
                        currentPart.GetBuffer(this.maxBufferSize, ref this.bufferRemaining); 
                }
                if (part == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomPartNotFound, uri)));
            } 

            part.ReferencedFromInfoset = true; 
            return part; 
        }
 
        MimePart ReadRootMimePart()
        {
            MimePart part = null;
 
            if (!mimeReader.ReadNextPart())
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootPartNotFound))); 
 
            MimeHeaders headers = mimeReader.ReadHeaders(this.maxBufferSize, ref this.bufferRemaining);
            Stream contentStream = mimeReader.GetContentStream(); 
            if (contentStream == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageInvalidContentInMimePart)));
            part = new MimePart(contentStream, headers);
 
            return part;
        } 
 
        void AdvanceToContentOnElement()
        { 
            if (NodeType != XmlNodeType.Attribute)
            {
                MoveToContent();
            } 
        }
 
        public override int AttributeCount 
        {
            get 
            {
                return xmlReader.AttributeCount;
            }
        } 

        public override string BaseURI 
        { 
            get
            { 
                return xmlReader.BaseURI;
            }
        }
 
        public override bool CanReadBinaryContent
        { 
            get 
            {
                return xmlReader.CanReadBinaryContent; 
            }
        }

        public override bool CanReadValueChunk 
        {
            get 
            { 
                return xmlReader.CanReadValueChunk;
            } 
        }

        public override bool CanResolveEntity
        { 
            get
            { 
                return xmlReader.CanResolveEntity; 
            }
        } 

        public override void Close()
        {
            xmlReader.Close(); 
            mimeReader.Close();
            OnXmlDictionaryReaderClose onClose = this.onClose; 
            this.onClose = null; 
            if (onClose != null)
            { 
                try
                {
                    onClose(this);
                } 
                catch (Exception e)
                { 
                    if (Fx.IsFatal(e)) throw; 

                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); 
                }
            }
        }
 
        public override int Depth
        { 
            get 
            {
                return xmlReader.Depth; 
            }
        }

        public override bool EOF 
        {
            get 
            { 
                return xmlReader.EOF;
            } 
        }

        public override string GetAttribute(int index)
        { 
            return xmlReader.GetAttribute(index);
        } 
 
        public override string GetAttribute(string name)
        { 
            return xmlReader.GetAttribute(name);
        }

        public override string GetAttribute(string name, string ns) 
        {
            return xmlReader.GetAttribute(name, ns); 
        } 

        public override string GetAttribute(XmlDictionaryString localName, XmlDictionaryString ns) 
        {
            return xmlReader.GetAttribute(localName, ns);
        }
#if NO 
        public override ArraySegment GetSubset(bool advance)
        { 
            return xmlReader.GetSubset(advance); 
        }
#endif 
        public override bool HasAttributes
        {
            get
            { 
                return xmlReader.HasAttributes;
            } 
        } 

        public override bool HasValue 
        {
            get
            {
                return xmlReader.HasValue; 
            }
        } 
 
        public override bool IsDefault
        { 
            get
            {
                return xmlReader.IsDefault;
            } 
        }
 
        public override bool IsEmptyElement 
        {
            get 
            {
                return xmlReader.IsEmptyElement;
            }
        } 

        public override bool IsLocalName(string localName) 
        { 
            return xmlReader.IsLocalName(localName);
        } 

        public override bool IsLocalName(XmlDictionaryString localName)
        {
            return xmlReader.IsLocalName(localName); 
        }
 
        public override bool IsNamespaceUri(string ns) 
        {
            return xmlReader.IsNamespaceUri(ns); 
        }

        public override bool IsNamespaceUri(XmlDictionaryString ns)
        { 
            return xmlReader.IsNamespaceUri(ns);
        } 
 
        public override bool IsStartElement()
        { 
            return xmlReader.IsStartElement();
        }

        public override bool IsStartElement(string localName) 
        {
            return xmlReader.IsStartElement(localName); 
        } 

        public override bool IsStartElement(string localName, string ns) 
        {
            return xmlReader.IsStartElement(localName, ns);
        }
 
        public override bool IsStartElement(XmlDictionaryString localName, XmlDictionaryString ns)
        { 
            return xmlReader.IsStartElement(localName, ns); 
        }
#if NO 
        public override bool IsStartSubsetElement()
        {
            return xmlReader.IsStartSubsetElement();
        } 
#endif
        public override string LocalName 
        { 
            get
            { 
                return xmlReader.LocalName;
            }
        }
 
        public override string LookupNamespace(string ns)
        { 
            return xmlReader.LookupNamespace(ns); 
        }
 
        public override void MoveToAttribute(int index)
        {
            xmlReader.MoveToAttribute(index);
        } 

        public override bool MoveToAttribute(string name) 
        { 
            return xmlReader.MoveToAttribute(name);
        } 

        public override bool MoveToAttribute(string name, string ns)
        {
            return xmlReader.MoveToAttribute(name, ns); 
        }
 
        public override bool MoveToElement() 
        {
            return xmlReader.MoveToElement(); 
        }

        public override bool MoveToFirstAttribute()
        { 
            return xmlReader.MoveToFirstAttribute();
        } 
 
        public override bool MoveToNextAttribute()
        { 
            return xmlReader.MoveToNextAttribute();
        }

        public override string Name 
        {
            get 
            { 
                return xmlReader.Name;
            } 
        }

        public override string NamespaceURI
        { 
            get
            { 
                return xmlReader.NamespaceURI; 
            }
        } 

        public override XmlNameTable NameTable
        {
            get 
            {
                return xmlReader.NameTable; 
            } 
        }
 
        public override XmlNodeType NodeType
        {
            get
            { 
                return xmlReader.NodeType;
            } 
        } 

        public override string Prefix 
        {
            get
            {
                return xmlReader.Prefix; 
            }
        } 
 
        public override char QuoteChar
        { 
            get
            {
                return xmlReader.QuoteChar;
            } 
        }
 
        public override bool ReadAttributeValue() 
        {
            return xmlReader.ReadAttributeValue(); 
        }

        public override object ReadContentAs(Type returnType, IXmlNamespaceResolver namespaceResolver)
        { 
            AdvanceToContentOnElement();
            return xmlReader.ReadContentAs(returnType, namespaceResolver); 
        } 

        public override byte[] ReadContentAsBase64() 
        {
            AdvanceToContentOnElement();
            return xmlReader.ReadContentAsBase64();
        } 

        public override int ReadValueAsBase64(byte[] buffer, int offset, int count) 
        { 
            AdvanceToContentOnElement();
            return xmlReader.ReadValueAsBase64(buffer, offset, count); 
        }

        public override int ReadContentAsBase64(byte[] buffer, int offset, int count)
        { 
            AdvanceToContentOnElement();
            return xmlReader.ReadContentAsBase64(buffer, offset, count); 
        } 

        public override int ReadElementContentAsBase64(byte[] buffer, int offset, int count) 
        {
            if (!readingBinaryElement)
            {
                if (IsEmptyElement) 
                {
                    Read(); 
                    return 0; 
                }
 
                ReadStartElement();
                readingBinaryElement = true;
            }
 
            int i = ReadContentAsBase64(buffer, offset, count);
 
            if (i == 0) 
            {
                ReadEndElement(); 
                readingBinaryElement = false;
            }

            return i; 
        }
 
        public override int ReadElementContentAsBinHex(byte[] buffer, int offset, int count) 
        {
            if (!readingBinaryElement) 
            {
                if (IsEmptyElement)
                {
                    Read(); 
                    return 0;
                } 
 
                ReadStartElement();
                readingBinaryElement = true; 
            }

            int i = ReadContentAsBinHex(buffer, offset, count);
 
            if (i == 0)
            { 
                ReadEndElement(); 
                readingBinaryElement = false;
            } 

            return i;
        }
 
        public override int ReadContentAsBinHex(byte[] buffer, int offset, int count)
        { 
            AdvanceToContentOnElement(); 
            return xmlReader.ReadContentAsBinHex(buffer, offset, count);
        } 

        public override bool ReadContentAsBoolean()
        {
            AdvanceToContentOnElement(); 
            return xmlReader.ReadContentAsBoolean();
        } 
 
        public override int ReadContentAsChars(char[] chars, int index, int count)
        { 
            AdvanceToContentOnElement();
            return xmlReader.ReadContentAsChars(chars, index, count);
        }
 
        public override DateTime ReadContentAsDateTime()
        { 
            AdvanceToContentOnElement(); 
            return xmlReader.ReadContentAsDateTime();
        } 

        public override decimal ReadContentAsDecimal()
        {
            AdvanceToContentOnElement(); 
            return xmlReader.ReadContentAsDecimal();
        } 
 
        public override double ReadContentAsDouble()
        { 
            AdvanceToContentOnElement();
            return xmlReader.ReadContentAsDouble();
        }
 
        public override int ReadContentAsInt()
        { 
            AdvanceToContentOnElement(); 
            return xmlReader.ReadContentAsInt();
        } 

        public override long ReadContentAsLong()
        {
            AdvanceToContentOnElement(); 
            return xmlReader.ReadContentAsLong();
        } 
#if NO 
        public override ICollection ReadContentAsList()
        { 
            AdvanceToContentOnElement();
            return xmlReader.ReadContentAsList();
        }
#endif 
        public override object ReadContentAsObject()
        { 
            AdvanceToContentOnElement(); 
            return xmlReader.ReadContentAsObject();
        } 

        public override float ReadContentAsFloat()
        {
            AdvanceToContentOnElement(); 
            return xmlReader.ReadContentAsFloat();
        } 
 
        public override string ReadContentAsString()
        { 
            AdvanceToContentOnElement();
            return xmlReader.ReadContentAsString();
        }
 
        public override string ReadInnerXml()
        { 
            return xmlReader.ReadInnerXml(); 
        }
 
        public override string ReadOuterXml()
        {
            return xmlReader.ReadOuterXml();
        } 

        public override ReadState ReadState 
        { 
            get
            { 
                if (xmlReader.ReadState != ReadState.Interactive && infosetReader != null)
                    return infosetReader.ReadState;

                return xmlReader.ReadState; 
            }
        } 
 
        public override int ReadValueChunk(char[] buffer, int index, int count)
        { 
            return xmlReader.ReadValueChunk(buffer, index, count);
        }

        public override void ResolveEntity() 
        {
            xmlReader.ResolveEntity(); 
        } 

        public override XmlReaderSettings Settings 
        {
            get
            {
                return xmlReader.Settings; 
            }
        } 
 
        public override void Skip()
        { 
            xmlReader.Skip();
        }

        public override string this[int index] 
        {
            get 
            { 
                return xmlReader[index];
            } 
        }

        public override string this[string name]
        { 
            get
            { 
                return xmlReader[name]; 
            }
        } 

        public override string this[string name, string ns]
        {
            get 
            {
                return xmlReader[name, ns]; 
            } 
        }
 
        public override string Value
        {
            get
            { 
                return xmlReader.Value;
            } 
        } 

        public override Type ValueType 
        {
            get
            {
                return xmlReader.ValueType; 
            }
        } 
 
        public override string XmlLang
        { 
            get
            {
                return xmlReader.XmlLang;
            } 
        }
 
        public override XmlSpace XmlSpace 
        {
            get 
            {
                return xmlReader.XmlSpace;
            }
        } 

        public bool HasLineInfo() 
        { 
            if (xmlReader.ReadState == ReadState.Closed)
                return false; 

            IXmlLineInfo lineInfo = xmlReader as IXmlLineInfo;
            if (lineInfo == null)
                return false; 
            return lineInfo.HasLineInfo();
        } 
 
        public int LineNumber
        { 
            get
            {
                if (xmlReader.ReadState == ReadState.Closed)
                    return 0; 

                IXmlLineInfo lineInfo = xmlReader as IXmlLineInfo; 
                if (lineInfo == null) 
                    return 0;
                return lineInfo.LineNumber; 
            }
        }

        public int LinePosition 
        {
            get 
            { 
                if (xmlReader.ReadState == ReadState.Closed)
                    return 0; 

                IXmlLineInfo lineInfo = xmlReader as IXmlLineInfo;
                if (lineInfo == null)
                    return 0; 
                return lineInfo.LinePosition;
            } 
        } 

        internal class MimePart 
        {
            Stream stream;
            MimeHeaders headers;
            byte[] buffer; 
            bool isReferencedFromInfoset;
 
            internal MimePart(Stream stream, MimeHeaders headers) 
            {
                this.stream = stream; 
                this.headers = headers;
            }

            internal Stream Stream 
            {
                get { return stream; } 
            } 

            internal MimeHeaders Headers 
            {
                get { return headers; }
            }
 
            internal bool ReferencedFromInfoset
            { 
                get { return isReferencedFromInfoset; } 
                set { isReferencedFromInfoset = value; }
            } 

            internal long Length
            {
                get { return stream.CanSeek ? stream.Length : 0; } 
            }
 
            internal byte[] GetBuffer(int maxBuffer, ref int remaining) 
            {
                if (buffer == null) 
                {
                    MemoryStream bufferedStream = stream.CanSeek ? new MemoryStream((int)stream.Length) : new MemoryStream();
                    int size = 256;
                    byte[] bytes = new byte[size]; 

                    int read = 0; 
 
                    do
                    { 
                        read = stream.Read(bytes, 0, size);
                        XmlMtomReader.DecrementBufferQuota(maxBuffer, ref remaining, read);
                        if (read > 0)
                            bufferedStream.Write(bytes, 0, read); 
                    }
                    while (read > 0); 
 
                    bufferedStream.Seek(0, SeekOrigin.Begin);
                    buffer = bufferedStream.GetBuffer(); 
                    stream = bufferedStream;
                }
                return buffer;
            } 

            internal void Release(int maxBuffer, ref int remaining) 
            { 
                remaining += (int)this.Length;
                this.headers.Release(ref remaining); 
            }
        }

        internal class XopIncludeReader : XmlDictionaryReader, IXmlLineInfo 
        {
            int chunkSize = 4096;  // Just a default.  Serves as a max chunk size. 
            int bytesRemaining; 

            MimePart part; 
            ReadState readState;
            XmlDictionaryReader parentReader;
            string stringValue;
            int stringOffset; 
            XmlNodeType nodeType;
            MemoryStream binHexStream; 
            byte[] valueBuffer; 
            int valueOffset;
            int valueCount; 
            bool finishedStream;

            public XopIncludeReader(MimePart part, XmlDictionaryReader reader)
            { 
                if (part == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("part"); 
                if (reader == null) 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
 
                this.part = part;
                this.parentReader = reader;
                this.readState = ReadState.Initial;
                this.nodeType = XmlNodeType.None; 
                this.chunkSize = Math.Min(reader.Quotas.MaxBytesPerRead, chunkSize);
                this.bytesRemaining = this.chunkSize; 
                this.finishedStream = false; 
            }
 
            public override XmlDictionaryReaderQuotas Quotas
            {
                get
                { 
                    return this.parentReader.Quotas;
                } 
            } 

            public override XmlNodeType NodeType 
            {
                get
                {
                    return (readState == ReadState.Interactive) ? nodeType : parentReader.NodeType; 
                }
            } 
 
            public override bool Read()
            { 
                bool retVal = true;
                switch (readState)
                {
                    case ReadState.Initial: 
                        readState = ReadState.Interactive;
                        nodeType = XmlNodeType.Text; 
                        break; 
                    case ReadState.Interactive:
                        if (this.finishedStream || (this.bytesRemaining == this.chunkSize && this.stringValue == null)) 
                        {
                            readState = ReadState.EndOfFile;
                            nodeType = XmlNodeType.EndElement;
                        } 
                        else
                        { 
                            this.bytesRemaining = this.chunkSize; 
                        }
                        break; 
                    case ReadState.EndOfFile:
                        nodeType = XmlNodeType.None;
                        retVal = false;
                        break; 
                }
                this.stringValue = null; 
                this.binHexStream = null; 
                this.valueOffset = 0;
                this.valueCount = 0; 
                this.stringOffset = 0;
                CloseStreams();
                return retVal;
            } 

            public override int ReadValueAsBase64(byte[] buffer, int offset, int count) 
            { 
                if (buffer == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer"); 

                if (offset < 0)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
                if (offset > buffer.Length) 
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, buffer.Length)));
                if (count < 0) 
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative))); 
                if (count > buffer.Length - offset)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset))); 

                if (this.stringValue != null)
                {
                    count = Math.Min(count, this.valueCount); 
                    if (count > 0)
                    { 
                        Buffer.BlockCopy(this.valueBuffer, this.valueOffset, buffer, offset, count); 
                        this.valueOffset += count;
                        this.valueCount -= count; 
                    }
                    return count;
                }
 
                if (this.bytesRemaining < count)
                    count = this.bytesRemaining; 
 
                int read = 0;
                if (readState == ReadState.Interactive) 
                {
                    while (read < count)
                    {
                        int actual = part.Stream.Read(buffer, offset + read, count - read); 
                        if (actual == 0)
                        { 
                            this.finishedStream = true; 
                            break;
                        } 
                        read += actual;
                    }
                }
                this.bytesRemaining -= read; 
                return read;
            } 
 
            public override int ReadContentAsBase64(byte[] buffer, int offset, int count)
            { 
                if (buffer == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");

                if (offset < 0) 
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
                if (offset > buffer.Length) 
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, buffer.Length))); 
                if (count < 0)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative))); 
                if (count > buffer.Length - offset)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset)));

                if (this.valueCount > 0) 
                {
                    count = Math.Min(count, this.valueCount); 
                    Buffer.BlockCopy(this.valueBuffer, this.valueOffset, buffer, offset, count); 
                    this.valueOffset += count;
                    this.valueCount -= count; 
                    return count;
                }

                if (this.chunkSize < count) 
                    count = this.chunkSize;
 
                int read = 0; 
                if (readState == ReadState.Interactive)
                { 
                    while (read < count)
                    {
                        int actual = part.Stream.Read(buffer, offset + read, count - read);
                        if (actual == 0) 
                        {
                            this.finishedStream = true; 
                            if (!Read()) 
                                break;
                        } 
                        read += actual;
                    }
                }
                this.bytesRemaining = this.chunkSize; 
                return read;
            } 
 
            public override int ReadContentAsBinHex(byte[] buffer, int offset, int count)
            { 
                if (buffer == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");

                if (offset < 0) 
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
                if (offset > buffer.Length) 
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, buffer.Length))); 
                if (count < 0)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative))); 
                if (count > buffer.Length - offset)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset)));

                if (this.chunkSize < count) 
                    count = this.chunkSize;
 
                int read = 0; 
                int consumed = 0;
                while (read < count) 
                {
                    if (binHexStream == null)
                    {
                        try 
                        {
                            binHexStream = new MemoryStream(new BinHexEncoding().GetBytes(this.Value)); 
                        } 
                        catch (FormatException e) // Wrap format exceptions from decoding document contents
                        { 
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(e.Message, e));
                        }
                    }
 
                    int actual = binHexStream.Read(buffer, offset + read, count - read);
                    if (actual == 0) 
                    { 
                        this.finishedStream = true;
                        if (!Read()) 
                            break;

                        consumed = 0;
                    } 

                    read += actual; 
                    consumed += actual; 
                }
 
                // Trim off the consumed chars
                if (this.stringValue != null && consumed > 0)
                {
                    this.stringValue = this.stringValue.Substring(consumed*2); 
                    this.stringOffset = Math.Max(0, this.stringOffset - consumed * 2);
 
                    this.bytesRemaining = this.chunkSize; 
                }
                return read; 
            }

            public override int ReadValueChunk(char[] chars, int offset, int count)
            { 
                if (chars == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("chars"); 
 
                if (offset < 0)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative))); 
                if (offset > chars.Length)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, chars.Length)));
                if (count < 0)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative))); 
                if (count > chars.Length - offset)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, chars.Length - offset))); 
 
                if (readState != ReadState.Interactive)
                    return 0; 

                // Copy characters from the Value property
                string str = this.Value;
                count = Math.Min(stringValue.Length - stringOffset, count); 
                if (count > 0)
                { 
                    stringValue.CopyTo(stringOffset, chars, offset, count); 
                    stringOffset += count;
                } 
                return count;
            }

            public override string Value 
            {
                get 
                { 
                    if (readState != ReadState.Interactive)
                        return String.Empty; 

                    if (stringValue == null)
                    {
                        // Compute the bytes to read 
                        int byteCount = this.bytesRemaining;
                        byteCount -= byteCount % 3; 
 
                        // Handle trailing bytes
                        if (this.valueCount > 0 && this.valueOffset > 0) 
                        {
                            Buffer.BlockCopy(this.valueBuffer, this.valueOffset, this.valueBuffer, 0, this.valueCount);
                            this.valueOffset = 0;
                        } 
                        byteCount -= this.valueCount;
 
                        // Resize buffer if needed 
                        if (this.valueBuffer == null)
                        { 
                            this.valueBuffer = new byte[byteCount];
                        }
                        else if (this.valueBuffer.Length < byteCount)
                        { 
                            Array.Resize(ref this.valueBuffer, byteCount);
                        } 
                        byte[] buffer = this.valueBuffer; 

                        // Fill up the buffer 
                        int offset = 0;
                        int read = 0;
                        while (byteCount > 0)
                        { 
                            read = part.Stream.Read(buffer, offset, byteCount);
                            if (read == 0) 
                            { 
                                this.finishedStream = true;
                                break; 
                            }

                            this.bytesRemaining -= read;
                            this.valueCount += read; 
                            byteCount -= read;
                            offset += read; 
                        } 

                        // Convert the bytes 
                        this.stringValue = Convert.ToBase64String(buffer, 0, this.valueCount);
                    }
                    return this.stringValue;
                } 
            }
 
            public override string ReadContentAsString() 
            {
                int stringContentQuota = this.Quotas.MaxStringContentLength; 
                StringBuilder sb = new StringBuilder();
                do
                {
                    string val = this.Value; 
                    if (val.Length > stringContentQuota)
                        XmlExceptionHelper.ThrowMaxStringContentLengthExceeded(this, this.Quotas.MaxStringContentLength); 
                    stringContentQuota -= val.Length; 
                    sb.Append(val);
                } while(Read()); 
                return sb.ToString();
            }

            public override int AttributeCount 
            {
                get { return 0; } 
            } 

            public override string BaseURI 
            {
                get { return parentReader.BaseURI; }
            }
 
            public override bool CanReadBinaryContent
            { 
                get { return true; } 
            }
 
            public override bool CanReadValueChunk
            {
                get { return true; }
            } 

            public override bool CanResolveEntity 
            { 
                get { return parentReader.CanResolveEntity; }
            } 

            public override void Close()
            {
                CloseStreams(); 
                readState = ReadState.Closed;
            } 
 
            void CloseStreams()
            { 
                if (binHexStream != null)
                {
                    binHexStream.Close();
                    binHexStream = null; 
                }
            } 
 
            public override int Depth
            { 
                get
                {
                    return (readState == ReadState.Interactive) ? parentReader.Depth + 1 : parentReader.Depth;
                } 
            }
 
            public override bool EOF 
            {
                get { return readState == ReadState.EndOfFile; } 
            }

            public override string GetAttribute(int index)
            { 
                return null;
            } 
 
            public override string GetAttribute(string name)
            { 
                return null;
            }

            public override string GetAttribute(string name, string ns) 
            {
                return null; 
            } 

            public override string GetAttribute(XmlDictionaryString localName, XmlDictionaryString ns) 
            {
                return null;
            }
 
            public override bool HasAttributes
            { 
                get { return false; } 
            }
 
            public override bool HasValue
            {
                get { return readState == ReadState.Interactive; }
            } 

            public override bool IsDefault 
            { 
                get { return false; }
            } 

            public override bool IsEmptyElement
            {
                get { return false; } 
            }
 
            public override bool IsLocalName(string localName) 
            {
                return false; 
            }

            public override bool IsLocalName(XmlDictionaryString localName)
            { 
                return false;
            } 
 
            public override bool IsNamespaceUri(string ns)
            { 
                return false;
            }

            public override bool IsNamespaceUri(XmlDictionaryString ns) 
            {
                return false; 
            } 

            public override bool IsStartElement() 
            {
                return false;
            }
 
            public override bool IsStartElement(string localName)
            { 
                return false; 
            }
 
            public override bool IsStartElement(string localName, string ns)
            {
                return false;
            } 

            public override bool IsStartElement(XmlDictionaryString localName, XmlDictionaryString ns) 
            { 
                return false;
            } 
#if NO
            public override bool IsStartSubsetElement()
            {
                return false; 
            }
#endif 
            public override string LocalName 
            {
                get 
                {
                    return (readState == ReadState.Interactive) ? String.Empty : parentReader.LocalName;
                }
            } 

            public override string LookupNamespace(string ns) 
            { 
                return parentReader.LookupNamespace(ns);
            } 

            public override void MoveToAttribute(int index)
            {
            } 

            public override bool MoveToAttribute(string name) 
            { 
                return false;
            } 

            public override bool MoveToAttribute(string name, string ns)
            {
                return false; 
            }
 
            public override bool MoveToElement() 
            {
                return false; 
            }

            public override bool MoveToFirstAttribute()
            { 
                return false;
            } 
 
            public override bool MoveToNextAttribute()
            { 
                return false;
            }

            public override string Name 
            {
                get 
                { 
                    return (readState == ReadState.Interactive) ? String.Empty : parentReader.Name;
                } 
            }

            public override string NamespaceURI
            { 
                get
                { 
                    return (readState == ReadState.Interactive) ? String.Empty : parentReader.NamespaceURI; 
                }
            } 

            public override XmlNameTable NameTable
            {
                get { return parentReader.NameTable; } 
            }
 
            public override string Prefix 
            {
                get 
                {
                    return (readState == ReadState.Interactive) ? String.Empty : parentReader.Prefix;
                }
            } 

            public override char QuoteChar 
            { 
                get { return parentReader.QuoteChar; }
            } 

            public override bool ReadAttributeValue()
            {
                return false; 
            }
 
            public override string ReadInnerXml() 
            {
                return ReadContentAsString(); 
            }

            public override string ReadOuterXml()
            { 
                return ReadContentAsString();
            } 
 
            public override ReadState ReadState
            { 
                get { return readState; }
            }

            public override void ResolveEntity() 
            {
            } 
 
            public override XmlReaderSettings Settings
            { 
                get { return parentReader.Settings; }
            }

            public override void Skip() 
            {
                Read(); 
            } 

            public override string this[int index] 
            {
                get { return null; }
            }
 
            public override string this[string name]
            { 
                get { return null; } 
            }
 
            public override string this[string name, string ns]
            {
                get { return null; }
            } 

            public override string XmlLang 
            { 
                get { return parentReader.XmlLang; }
            } 

            public override XmlSpace XmlSpace
            {
                get { return parentReader.XmlSpace; } 
            }
 
            public override Type ValueType 
            {
                get 
                {
                    return (readState == ReadState.Interactive) ? typeof(byte[]) : parentReader.ValueType;
                }
            } 

            bool IXmlLineInfo.HasLineInfo() 
            { 
                return ((IXmlLineInfo)parentReader).HasLineInfo();
            } 

            int IXmlLineInfo.LineNumber
            {
                get 
                {
                    return ((IXmlLineInfo)parentReader).LineNumber; 
                } 
            }
 
            int IXmlLineInfo.LinePosition
            {
                get
                { 
                    return ((IXmlLineInfo)parentReader).LinePosition;
                } 
            } 
        }
    } 

    internal class MimeMessageReader
    {
        static byte[] CRLFCRLF = new byte[] { (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' }; 

        bool getContentStreamCalled; 
        MimeHeaderReader mimeHeaderReader; 
        DelimittedStreamReader reader;
 
        public MimeMessageReader(Stream stream)
        {
            if (stream == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream"); 

            this.reader = new DelimittedStreamReader(stream); 
            this.mimeHeaderReader = new MimeHeaderReader(this.reader.GetNextStream(CRLFCRLF)); 
        }
 
        public Stream GetContentStream()
        {
            if (getContentStreamCalled)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeMessageGetContentStreamCalledAlready))); 

            mimeHeaderReader.Close(); 
 
            Stream s = reader.GetNextStream(null);
 
            getContentStreamCalled = true;

            return s;
        } 

        public MimeHeaders ReadHeaders(int maxBuffer, ref int remaining) 
        { 
            MimeHeaders headers = new MimeHeaders();
            while (mimeHeaderReader.Read(maxBuffer, ref remaining)) 
            {
                headers.Add(mimeHeaderReader.Name, mimeHeaderReader.Value, ref remaining);
            }
            return headers; 
        }
    } 
 
    internal class MimeReader
    { 
        static byte[] CRLFCRLF = new byte[] { (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' };

        byte[] boundaryBytes;
        string content; 
        Stream currentStream;
        MimeHeaderReader mimeHeaderReader; 
        DelimittedStreamReader reader; 
        byte[] scratch = new byte[2];
 
        public MimeReader(Stream stream, string boundary)
        {
            if (stream == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream"); 
            if (boundary == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("boundary"); 
 
            this.reader = new DelimittedStreamReader(stream);
            this.boundaryBytes = MimeWriter.GetBoundaryBytes(boundary); 

            // Need to ensure that the content begins with a CRLF, in case the
            // outer construct has consumed the trailing CRLF
            this.reader.Push(this.boundaryBytes, 0, 2); 
        }
 
        public void Close() 
        {
            this.reader.Close(); 
        }

        /// Gets the content preceding the first part of the MIME multi-part message
        public string Preface 
        {
            get 
            { 
                if (content == null)
                { 
                    Stream s = this.reader.GetNextStream(this.boundaryBytes);
                    content = new StreamReader(s, System.Text.Encoding.ASCII, false, 256).ReadToEnd();
                    s.Close();
                    if (content == null) 
                        content = string.Empty;
                } 
                return content; 
            }
        } 

        public Stream GetContentStream()
        {
            Fx.Assert(content != null, ""); 

            mimeHeaderReader.Close(); 
 
            return reader.GetNextStream(this.boundaryBytes);
        } 

        public bool ReadNextPart()
        {
            string content = Preface; 

            if (currentStream != null) 
            { 
                currentStream.Close();
                currentStream = null; 
            }

            Stream stream = reader.GetNextStream(CRLFCRLF);
 
            if (stream == null)
                return false; 
 
            if (BlockRead(stream, scratch, 0, 2) == 2)
            { 
                if (scratch[0] == '\r' && scratch[1] == '\n')
                {
                    if (mimeHeaderReader == null)
                        mimeHeaderReader = new MimeHeaderReader(stream); 
                    else
                        mimeHeaderReader.Reset(stream); 
                    return true; 
                }
                else if (scratch[0] == '-' && scratch[1] == '-') 
                {
                    int read = BlockRead(stream, scratch, 0, 2);

                    if (read < 2 || (scratch[0] == '\r' && scratch[1] == '\n')) 
                        return false;
                } 
            } 

            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderTruncated))); 
        }

        public MimeHeaders ReadHeaders(int maxBuffer, ref int remaining)
        { 
            MimeHeaders headers = new MimeHeaders();
            while (mimeHeaderReader.Read(maxBuffer, ref remaining)) 
            { 
                headers.Add(mimeHeaderReader.Name, mimeHeaderReader.Value, ref remaining);
            } 
            return headers;
        }

        int BlockRead(Stream stream, byte[] buffer, int offset, int count) 
        {
            int read = 0; 
            do 
            {
                int r = stream.Read(buffer, offset + read, count - read); 
                if (r == 0)
                    break;
                read += r;
            } while (read < count); 
            return read;
        } 
 
    }
 
    internal class DelimittedStreamReader
    {
        bool canGetNextStream = true;
 
        // used for closing the reader, and validating that only one stream can be reading at a time.
        DelimittedReadStream currentStream; 
 
        byte[] delimitter;
        byte[] matchBuffer; 
        byte[] scratch;

        BufferedReadStream stream;
 
        public DelimittedStreamReader(Stream stream)
        { 
            this.stream = new BufferedReadStream(stream); 
        }
 
        public void Close()
        {
            this.stream.Close();
        } 

        // Closes the current stream.  If the current stream is not the same as the caller, nothing is done. 
        void Close(DelimittedReadStream caller) 
        {
            if (currentStream == caller) 
            {
                if (delimitter == null)
                {
                    stream.Close(); 
                }
                else 
                { 
                    if (scratch == null)
                    { 
                        scratch = new byte[1024];
                    }
                    while (0 != Read(caller, this.scratch, 0, this.scratch.Length)) ;
                } 

                currentStream = null; 
            } 
        }
 
        // Gets the next logical stream delimitted by the given sequence.
        public Stream GetNextStream(byte[] delimitter)
        {
            if (currentStream != null) 
            {
                currentStream.Close(); 
                currentStream = null; 
            }
 
            if (!canGetNextStream)
                return null;

            this.delimitter = delimitter; 

            canGetNextStream = delimitter != null; 
 
            currentStream = new DelimittedReadStream(this);
 
            return currentStream;
        }

        enum MatchState 
        {
            True, 
            False, 
            InsufficientData
        } 

        MatchState MatchDelimitter(byte[] buffer, int start, int end)
        {
            if (this.delimitter.Length > end - start) 
            {
                for (int i = end - start - 1; i >= 1; i--) 
                { 
                    if (buffer[start + i] != delimitter[i])
                        return MatchState.False; 
                }
                return MatchState.InsufficientData;
            }
            for (int i = delimitter.Length - 1; i >= 1; i--) 
            {
                if (buffer[start + i] != delimitter[i]) 
                    return MatchState.False; 
            }
            return MatchState.True; 
        }

        int ProcessRead(byte[] buffer, int offset, int read)
        { 
            // nothing to process if 0 bytes were read
            if (read == 0) 
                return read; 

            for (int ptr = offset, end = offset + read; ptr < end; ptr++) 
            {
                if (buffer[ptr] == delimitter[0])
                {
                    switch (MatchDelimitter(buffer, ptr, end)) 
                    {
                        case MatchState.True: 
                            { 
                                int actual = ptr - offset;
                                ptr += this.delimitter.Length; 
                                this.stream.Push(buffer, ptr, end - ptr);
                                this.currentStream = null;
                                return actual;
                            } 
                        case MatchState.False:
                            break; 
                        case MatchState.InsufficientData: 
                            {
                                int actual = ptr - offset; 
                                if (actual > 0)
                                {
                                    this.stream.Push(buffer, ptr, end - ptr);
                                    return actual; 
                                }
                                else 
                                { 
                                    return -1;
                                } 
                            }
                    }
                }
            } 
            return read;
        } 
 
        int Read(DelimittedReadStream caller, byte[] buffer, int offset, int count)
        { 
            if (this.currentStream != caller)
                return 0;

            int read = this.stream.Read(buffer, offset, count); 
            if (read == 0)
            { 
                this.canGetNextStream = false; 
                this.currentStream = null;
                return read; 
            }

            // If delimitter is null, read until the underlying stream returns 0 bytes
            if (this.delimitter == null) 
                return read;
 
            // Scans the read data for the delimitter. If found, the number of bytes read are adjusted 
            // to account for the number of bytes up to but not including the delimitter.
            int actual = ProcessRead(buffer, offset, read); 

            if (actual < 0)
            {
                if (this.matchBuffer == null || this.matchBuffer.Length < this.delimitter.Length - read) 
                    this.matchBuffer = new byte[this.delimitter.Length - read];
 
                int matched = this.stream.ReadBlock(this.matchBuffer, 0, this.delimitter.Length - read); 

                if (MatchRemainder(read, matched)) 
                {
                    this.currentStream = null;
                    actual = 0;
                } 
                else
                { 
                    this.stream.Push(this.matchBuffer, 0, matched); 

                    int i = 1; 
                    for (; i < read; i++)
                    {
                        if (buffer[i] == this.delimitter[0])
                            break; 
                    }
 
                    if (i < read) 
                        this.stream.Push(buffer, offset + i, read - i);
 
                    actual = i;
                }
            }
 
            return actual;
        } 
 
        bool MatchRemainder(int start, int count)
        { 
            if (start + count != this.delimitter.Length)
                return false;

            for (count--; count >= 0; count--) 
            {
                if (this.delimitter[start + count] != this.matchBuffer[count]) 
                    return false; 
            }
            return true; 
        }

        internal void Push(byte[] buffer, int offset, int count)
        { 
            this.stream.Push(buffer, offset, count);
        } 
 
        class DelimittedReadStream : Stream
        { 
            DelimittedStreamReader reader;

            public DelimittedReadStream(DelimittedStreamReader reader)
            { 
                if (reader == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); 
 
                this.reader = reader;
            } 

            public override bool CanRead
            {
                get { return true; } 
            }
 
            public override bool CanSeek 
            {
                get { return false; } 
            }

            public override bool CanWrite
            { 
                get { return false; }
            } 
 
            public override long Length
            { 
#pragma warning suppress 56503 // [....], required by the XmlReader
                get { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, this.GetType().FullName))); }
            }
 
            public override long Position
            { 
                get { 
#pragma warning suppress 56503 // [....], required by the XmlReader
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, this.GetType().FullName))); 
                }
                set { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, this.GetType().FullName))); }
            }
 
            public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
            { 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, this.GetType().FullName))); 
            }
 
            public override void Close()
            {
                reader.Close(this);
            } 

            public override void EndWrite(IAsyncResult asyncResult) 
            { 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, this.GetType().FullName)));
            } 

            public override void Flush()
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, this.GetType().FullName))); 
            }
 
            public override int Read(byte[] buffer, int offset, int count) 
            {
                if (buffer == null) 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");

                if (offset < 0)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative))); 
                if (offset > buffer.Length)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, buffer.Length))); 
                if (count < 0) 
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
                if (count > buffer.Length - offset) 
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset)));

                return reader.Read(this, buffer, offset, count);
            } 

            public override long Seek(long offset, SeekOrigin origin) 
            { 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, this.GetType().FullName)));
            } 

            public override void SetLength(long value)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, this.GetType().FullName))); 
            }
 
            public override void Write(byte[] buffer, int offset, int count) 
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, this.GetType().FullName))); 
            }
        }

    } 

    internal class MimeHeaders 
    { 
        static class Constants
        { 
            public const string ContentTransferEncoding = "content-transfer-encoding";
            public const string ContentID = "content-id";
            public const string ContentType = "content-type";
            public const string MimeVersion = "mime-version"; 
        }
 
        Dictionary headers = new Dictionary(); 

 		public MimeHeaders() 
		{
		}

        public ContentTypeHeader ContentType 
        {
            get 
            { 
                MimeHeader header;
                if (headers.TryGetValue(Constants.ContentType, out header)) 
                    return header as ContentTypeHeader;
                return null;
            }
        } 

        public ContentIDHeader ContentID 
        { 
            get
            { 
                MimeHeader header;
                if (headers.TryGetValue(Constants.ContentID, out header))
                    return header as ContentIDHeader;
                return null; 
            }
        } 
 
        public ContentTransferEncodingHeader ContentTransferEncoding
        { 
            get
            {
                MimeHeader header;
                if (headers.TryGetValue(Constants.ContentTransferEncoding, out header)) 
                    return header as ContentTransferEncodingHeader;
                return null; 
            } 
        }
 
        public MimeVersionHeader MimeVersion
        {
            get
            { 
                MimeHeader header;
                if (headers.TryGetValue(Constants.MimeVersion, out header)) 
                    return header as MimeVersionHeader; 
                return null;
            } 
        }

        public void Add(string name, string value, ref int remaining)
        { 
            if (name == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); 
 
            if (value == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); 

            switch (name)
            {
                case Constants.ContentType: 
                    Add(new ContentTypeHeader(value));
                    break; 
                case Constants.ContentID: 
                    Add(new ContentIDHeader(name, value));
                    break; 
                case Constants.ContentTransferEncoding:
                    Add(new ContentTransferEncodingHeader(value));
                    break;
                case Constants.MimeVersion: 
                    Add(new MimeVersionHeader(value));
                    break; 
 
                // Skip any fields that are not recognized
                // Content-description is currently not stored since it is not used 
                default:
                    remaining += value.Length * sizeof(char);
                    break;
            } 
            remaining += name.Length * sizeof(char);
        } 
 
        public void Add(MimeHeader header)
        { 
            if (header == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("header");

            MimeHeader existingHeader; 
            if (headers.TryGetValue(header.Name, out existingHeader))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderHeaderAlreadyExists, header.Name))); 
            else 
                headers.Add(header.Name, header);
        } 

        public void Release(ref int remaining)
        {
            foreach(MimeHeader header in this.headers.Values) 
            {
                remaining += header.Value.Length * sizeof(char); 
            } 
        }
 
    }

    internal class MimeHeader
    { 
        string name;
        string value; 
 
        public MimeHeader(string name, string value)
        { 
            if (name == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name");

            this.name = name; 
            this.value = value;
        } 
 
        public string Name
        { 
            get
            {
                return this.name;
            } 
        }
 
        public string Value 
        {
            get 
            {
                return this.value;
            }
        } 
    }
 
    internal class ContentTypeHeader : MimeHeader 
    {
        public readonly static ContentTypeHeader Default = new ContentTypeHeader("application/octet-stream"); 

        public ContentTypeHeader(string value) : base("content-type", value)
        {
        } 

        string mediaType; 
        string subType; 
        Dictionary parameters;
 
        public string MediaType
        {
            get
            { 
                if (this.mediaType == null && Value != null)
                    ParseValue(); 
 
                return this.mediaType;
            } 
        }

        public string MediaSubtype
        { 
            get
            { 
                if (this.subType == null && Value != null) 
                    ParseValue();
 
                return this.subType;
            }
        }
 
        public Dictionary Parameters
        { 
            get 
            {
                if (this.parameters == null) 
                {
                    if (Value != null)
                        ParseValue();
                    else 
                        this.parameters = new Dictionary();
                } 
                return this.parameters; 
            }
        } 

        void ParseValue()
        {
            if (this.parameters == null) 
            {
                int offset = 0; 
                this.parameters = new Dictionary(); 
                this.mediaType = MailBnfHelper.ReadToken(Value, ref offset, null);
                if (offset >= Value.Length || Value[offset++] != '/') 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeContentTypeHeaderInvalid)));
                this.subType = MailBnfHelper.ReadToken(Value, ref offset, null);

                while (MailBnfHelper.SkipCFWS(Value, ref offset)) 
                {
                    if (offset >= Value.Length || Value[offset++] != ';') 
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeContentTypeHeaderInvalid))); 

                    if (!MailBnfHelper.SkipCFWS(Value, ref offset)) 
                        break;

                    string paramAttribute = MailBnfHelper.ReadParameterAttribute(Value, ref offset, null);
                    if (paramAttribute == null || offset >= Value.Length || Value[offset++] != '=') 
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeContentTypeHeaderInvalid)));
                    string paramValue = MailBnfHelper.ReadParameterValue(Value, ref offset, null); 
 
                    this.parameters.Add(paramAttribute.ToLowerInvariant(), paramValue);
                } 

                if (this.parameters.ContainsKey(MtomGlobals.StartInfoParam) )
                {
                    // This allows us to maintain back compat with Orcas clients while allowing clients 
                    // following the spec (with action inside start-info) to interop with RFC 2387
                    string startInfo = this.parameters[MtomGlobals.StartInfoParam]; 
 
                    // we're only interested in finding the action here - skipping past the content type to the first ;
                    int startInfoOffset = startInfo.IndexOf(';'); 
                    if (startInfoOffset > -1)
                    {
                        // keep going through the start-info string until we've reached the end of the stream
                        while (MailBnfHelper.SkipCFWS(startInfo, ref startInfoOffset)) 
                        {
                            // after having read through an attribute=value pair, we always expect to be at a ; 
                            if (startInfo[startInfoOffset] == ';') 
                            {
                                startInfoOffset++; 
                            }
                            else
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeContentTypeHeaderInvalid))); 
                            }
                            string paramAttribute = MailBnfHelper.ReadParameterAttribute(startInfo, ref startInfoOffset, null); 
                            if (paramAttribute == null || startInfoOffset >= startInfo.Length || startInfo[startInfoOffset++] != '=') 
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeContentTypeHeaderInvalid)));
                            string paramValue = MailBnfHelper.ReadParameterValue(startInfo, ref startInfoOffset, null); 

                            if (paramAttribute == MtomGlobals.ActionParam)
                            {
                                this.parameters[MtomGlobals.ActionParam] = paramValue; 
                            }
                        } 
                    } 
                }
 
            }
        }
    }
 
    enum ContentTransferEncoding
    { 
        SevenBit, 
        EightBit,
        Binary, 
        Other,
        Unspecified
    }
 
    internal class ContentTransferEncodingHeader : MimeHeader
    { 
        ContentTransferEncoding contentTransferEncoding; 
        string contentTransferEncodingValue;
 
        public readonly static ContentTransferEncodingHeader Binary = new ContentTransferEncodingHeader(ContentTransferEncoding.Binary, "binary");
        public readonly static ContentTransferEncodingHeader EightBit = new ContentTransferEncodingHeader(ContentTransferEncoding.EightBit, "8bit");
        public readonly static ContentTransferEncodingHeader SevenBit = new ContentTransferEncodingHeader(ContentTransferEncoding.SevenBit, "7bit");
 
        public ContentTransferEncodingHeader(string value) : base("content-transfer-encoding", value.ToLowerInvariant())
        { 
        } 

        public ContentTransferEncodingHeader(ContentTransferEncoding contentTransferEncoding, string value) : base("content-transfer-encoding", null) 
        {
            this.contentTransferEncoding = contentTransferEncoding;
            this.contentTransferEncodingValue = value;
        } 

        public ContentTransferEncoding ContentTransferEncoding 
        { 
            get
            { 
                ParseValue();
                return this.contentTransferEncoding;
            }
        } 

        public string ContentTransferEncodingValue 
        { 
            get
            { 
                ParseValue();
                return this.contentTransferEncodingValue;
            }
        } 

        void ParseValue() 
        { 
            if (this.contentTransferEncodingValue == null)
            { 
                int offset = 0;
                this.contentTransferEncodingValue = (Value.Length == 0) ? Value : ((Value[0] == '"') ? MailBnfHelper.ReadQuotedString(Value, ref offset, null) : MailBnfHelper.ReadToken(Value, ref offset, null));
                switch (this.contentTransferEncodingValue)
                { 
                    case "7bit":
                        this.contentTransferEncoding = ContentTransferEncoding.SevenBit; 
                        break; 
                    case "8bit":
                        this.contentTransferEncoding = ContentTransferEncoding.EightBit; 
                        break;
                    case "binary":
                        this.contentTransferEncoding = ContentTransferEncoding.Binary;
                        break; 
                    default:
                        this.contentTransferEncoding = ContentTransferEncoding.Other; 
                        break; 
                }
            } 
        }
    }

    internal class ContentIDHeader : MimeHeader 
    {
        public ContentIDHeader(string name, string value) : base(name, value) 
        { 
        }
    } 

    internal class MimeVersionHeader : MimeHeader
    {
        public static readonly MimeVersionHeader Default = new MimeVersionHeader("1.0"); 

        public MimeVersionHeader(string value) : base("mime-version", value) 
        { 
        }
 
        string version;

        public string Version
        { 
            get
            { 
                if (this.version == null && Value != null) 
                    ParseValue();
                return this.version; 
            }
        }

        void ParseValue() 
        {
            // shortcut for the most common case. 
            if (Value == "1.0") 
            {
                this.version = "1.0"; 
            }
            else
            {
                int offset = 0; 

                if (!MailBnfHelper.SkipCFWS(Value, ref offset)) 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeVersionHeaderInvalid))); 

                StringBuilder builder = new StringBuilder(); 
                MailBnfHelper.ReadDigits(Value, ref offset, builder);

                if ((!MailBnfHelper.SkipCFWS(Value, ref offset) || offset >= Value.Length || Value[offset++] != '.') || !MailBnfHelper.SkipCFWS(Value, ref offset))
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeVersionHeaderInvalid))); 

                builder.Append('.'); 
 
                MailBnfHelper.ReadDigits(Value, ref offset, builder);
 
                this.version = builder.ToString();
            }
        }
    } 

    internal class MimeHeaderReader 
    { 
        enum ReadState
        { 
            ReadName,
            SkipWS,
            ReadValue,
            ReadLF, 
            ReadWS,
            EOF 
        } 

        string value; 
        byte[] buffer = new byte[1024];
        int maxOffset;
        string name;
        int offset; 
        ReadState readState = ReadState.ReadName;
        Stream stream; 
 
        public MimeHeaderReader(Stream stream)
        { 
            if (stream == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");

            this.stream = stream; 
        }
 
        public string Value 
        {
            get 
            {
                return value;
            }
        } 

        public string Name 
        { 
            get
            { 
                return name;
            }
        }
 
        public void Close()
        { 
            stream.Close(); 
            readState = ReadState.EOF;
        } 

        public bool Read(int maxBuffer, ref int remaining)
        {
            name = null; 
            value = null;
 
            while (readState != ReadState.EOF) 
            {
                if (offset == maxOffset) 
                {
                    maxOffset = stream.Read(this.buffer, 0, this.buffer.Length);
                    offset = 0;
                    if (BufferEnd()) 
                        break;
                } 
                if (ProcessBuffer(maxBuffer, ref remaining)) 
                    break;
            } 

            return value != null;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls unsafe code", Safe = "Demands for FullTrust")]
        [SecuritySafeCritical] 
        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] 
        bool ProcessBuffer(int maxBuffer, ref int remaining)
        { 
            unsafe
            {
                fixed (byte* pBuffer = this.buffer)
                { 
                    byte* start = pBuffer + this.offset;
                    byte* end = pBuffer + this.maxOffset; 
                    byte* ptr = start; 

                    switch (readState) 
                    {
                        case ReadState.ReadName:
                            for (; ptr < end; ptr++)
                            { 
                                if (*ptr == ':')
                                { 
                                    AppendName(new string((sbyte*)start, 0, (int)(ptr - start)), maxBuffer, ref remaining); 
                                    ptr++;
                                    goto case ReadState.SkipWS; 
                                }
                                else
                                {
                                    // convert to lower case up front. 
                                    if (*ptr >= 'A' && *ptr <= 'Z')
                                    { 
                                        *ptr += 'a' - 'A'; 
                                    }
                                    else if (*ptr < 33 || *ptr > 126) 
                                    {
                                        if (name == null && *ptr == (byte)'\r')
                                        {
                                            ptr++; 
                                            if (ptr >= end || *ptr != '\n')
                                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderMalformedHeader))); 
                                            goto case ReadState.EOF; 
                                        }
 
                                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, (char)*ptr, ((int)*ptr).ToString("X", CultureInfo.InvariantCulture))));
                                    }
                                }
                            } 
                            AppendName(new string((sbyte*)start, 0, (int)(ptr - start)), maxBuffer, ref remaining);
                            readState = ReadState.ReadName; 
                            break; 
                        case ReadState.SkipWS:
                            for (; ptr < end; ptr++) 
                                if (*ptr != (byte)'\t' && *ptr != ' ')
                                    goto case ReadState.ReadValue;
                            readState = ReadState.SkipWS;
                            break; 
                        case ReadState.ReadValue:
                            start = ptr; 
                            for (; ptr < end; ptr++) 
                            {
                                if (*ptr == (byte)'\r') 
                                {
                                    AppendValue(new string((sbyte*)start, 0, (int)(ptr - start)), maxBuffer, ref remaining);
                                    ptr++;
                                    goto case ReadState.ReadLF; 
                                }
                                else if (*ptr == (byte)'\n') 
                                { 
                                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderMalformedHeader)));
                                } 
                            }
                            AppendValue(new string((sbyte*)start, 0, (int)(ptr - start)), maxBuffer, ref remaining);
                            readState = ReadState.ReadValue;
                            break; 
                        case ReadState.ReadLF:
                            if (ptr < end) 
                            { 
                                if (*ptr != (byte)'\n')
                                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderMalformedHeader))); 
                                ptr++;
                                goto case ReadState.ReadWS;
                            }
                            readState = ReadState.ReadLF; 
                            break;
                        case ReadState.ReadWS: 
                            if (ptr < end) 
                            {
                                if (*ptr != (byte)' ' && *ptr != (byte)'\t') 
                                {
                                    readState = ReadState.ReadName;
                                    offset = (int)(ptr - pBuffer);
                                    return true; 
                                }
                                goto case ReadState.ReadValue; 
                            } 
                            readState = ReadState.ReadWS;
                            break; 
                        case ReadState.EOF:
                            readState = ReadState.EOF;
                            offset = (int)(ptr - pBuffer);
                            return true; 
                    }
                    offset = (int)(ptr - pBuffer); 
                } 
            }
            return false; 
        }

        bool BufferEnd()
        { 
            if (maxOffset == 0)
            { 
                if (readState != ReadState.ReadWS && readState != ReadState.ReadValue) 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderMalformedHeader)));
 
                readState = ReadState.EOF;
                return true;
            }
            return false; 
        }
 
        // Resets the mail field reader to the new stream to reuse buffers 
        public void Reset(Stream stream)
        { 
            if (stream == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");

            if (readState != ReadState.EOF) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeReaderResetCalledBeforeEOF)));
 
            this.stream = stream; 
            readState = ReadState.ReadName;
            maxOffset = 0; 
            offset = 0;
        }

        // helper methods 

        void AppendValue(string value, int maxBuffer, ref int remaining) 
        { 
            XmlMtomReader.DecrementBufferQuota(maxBuffer, ref remaining, value.Length * sizeof(char));
            if (this.value == null) 
                this.value = value;
            else
                this.value += value;
        } 

        void AppendName(string value, int maxBuffer, ref int remaining) 
        { 
            XmlMtomReader.DecrementBufferQuota(maxBuffer, ref remaining, value.Length * sizeof(char));
            if (this.name == null) 
                this.name = value;
            else
                this.name += value;
        } 

    } 
 
    internal class BufferedReadStream : Stream
    { 
        Stream stream;
        byte[] storedBuffer;
        int storedLength;
        int storedOffset; 
        bool readMore;
 
        public BufferedReadStream(Stream stream) : this(stream, false) 
        {
        } 

        public BufferedReadStream(Stream stream, bool readMore)
        {
            if (stream == null) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
 
            this.stream = stream; 
            this.readMore = readMore;
        } 

        public override bool CanWrite
        {
            get { return false; } 
        }
 
        public override bool CanSeek 
        {
            get { return false; } 
        }

        public override bool CanRead
        { 
            get { return stream.CanRead; }
        } 
 
        public override long Length
        { 
            get
            {
#pragma warning suppress 56503 // [....], required by the Stream contract
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, stream.GetType().FullName))); 
            }
        } 
 
        public override long Position
        { 
            get
            {
#pragma warning suppress 56503 // [....], required by the Stream contract
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, stream.GetType().FullName))); 
            }
            set 
            { 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, stream.GetType().FullName)));
            } 
        }

        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        { 
            if (!CanRead)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ReadNotSupportedOnStream, stream.GetType().FullName))); 
 
            return stream.BeginRead(buffer, offset, count, callback, state);
        } 

        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, stream.GetType().FullName))); 
        }
 
        public override void Close() 
        {
            stream.Close(); 
        }

        public override int EndRead(IAsyncResult asyncResult)
        { 
            if (!CanRead)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ReadNotSupportedOnStream, stream.GetType().FullName))); 
 
            return stream.EndRead(asyncResult);
        } 

        public override void EndWrite(IAsyncResult asyncResult)
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, stream.GetType().FullName))); 
        }
 
        public override void Flush() 
        {
            stream.Flush(); 
        }

        public override int Read(byte[] buffer, int offset, int count)
        { 
            if (!CanRead)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ReadNotSupportedOnStream, stream.GetType().FullName))); 
 
            if (buffer == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer"); 

            if (offset < 0)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
            if (offset > buffer.Length) 
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, buffer.Length)));
            if (count < 0) 
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative))); 
            if (count > buffer.Length - offset)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset))); 

            int read = 0;
            if (this.storedOffset < this.storedLength)
            { 
                read = Math.Min(count, this.storedLength - this.storedOffset);
                Buffer.BlockCopy(this.storedBuffer, this.storedOffset, buffer, offset, read); 
                this.storedOffset += read; 
                if (read == count || !this.readMore)
                    return read; 
                offset += read;
                count -= read;
            }
            return read + stream.Read(buffer, offset, count); 
        }
 
        public override int ReadByte() 
        {
            if (this.storedOffset < this.storedLength) 
                return (int)this.storedBuffer[this.storedOffset++];
            else
                return base.ReadByte();
        } 

        public int ReadBlock(byte[] buffer, int offset, int count) 
        { 
            int read;
            int total = 0; 
            while (total < count && (read = Read(buffer, offset + total, count - total)) != 0)
            {
                total += read;
            } 
            return total;
        } 
 
        public void Push(byte[] buffer, int offset, int count)
        { 
            if (count == 0)
                return;

            if (this.storedOffset == this.storedLength) 
            {
                if (this.storedBuffer == null || this.storedBuffer.Length < count) 
                    this.storedBuffer = new byte[count]; 
                this.storedOffset = 0;
                this.storedLength = count; 
            }
            else
            {
                // if there's room to just insert before existing data 
                if (count <= this.storedOffset)
                    this.storedOffset -= count; 
                // if there's room in the buffer but need to shift things over 
                else if (count <= this.storedBuffer.Length - this.storedLength + this.storedOffset)
                { 
                    Buffer.BlockCopy(this.storedBuffer, this.storedOffset, this.storedBuffer, count, this.storedLength - this.storedOffset);
                    this.storedLength += count - this.storedOffset;
                    this.storedOffset = 0;
                } 
                else
                { 
                    byte[] newBuffer = new byte[count + this.storedLength - this.storedOffset]; 
                    Buffer.BlockCopy(this.storedBuffer, this.storedOffset, newBuffer, count, this.storedLength - this.storedOffset);
                    this.storedLength += count - this.storedOffset; 
                    this.storedOffset = 0;
                    this.storedBuffer = newBuffer;
                }
            } 
            Buffer.BlockCopy(buffer, offset, this.storedBuffer, this.storedOffset, count);
        } 
 
        public override long Seek(long offset, SeekOrigin origin)
        { 
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, stream.GetType().FullName)));
        }

        public override void SetLength(long value) 
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, stream.GetType().FullName))); 
        } 

        public override void Write(byte[] buffer, int offset, int count) 
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, stream.GetType().FullName)));
        }
    } 

    internal static class MailBnfHelper 
    { 
        static bool[] s_fqtext = new bool[128];
        static bool[] s_ttext = new bool[128]; 
        static bool[] s_digits = new bool[128];
        static bool[] s_boundary = new bool[128];

        static MailBnfHelper() 
        {
            // fqtext = %d1-9 / %d11 / %d12 / %d14-33 / %d35-91 / %d93-127 
            for (int i = 1; i <= 9; i++) { s_fqtext[i] = true; } 
            s_fqtext[11] = true;
            s_fqtext[12] = true; 
            for (int i = 14; i <= 33; i++) { s_fqtext[i] = true; }
            for (int i = 35; i <= 91; i++) { s_fqtext[i] = true; }
            for (int i = 93; i <= 127; i++) { s_fqtext[i] = true; }
 
            // ttext = %d33-126 except '()<>@,;:\"/[]?='
            for (int i = 33; i <= 126; i++) { s_ttext[i] = true; } 
            s_ttext['('] = false; 
            s_ttext[')'] = false;
            s_ttext['<'] = false; 
            s_ttext['>'] = false;
            s_ttext['@'] = false;
            s_ttext[','] = false;
            s_ttext[';'] = false; 
            s_ttext[':'] = false;
            s_ttext['\\'] = false; 
            s_ttext['"'] = false; 
            s_ttext['/'] = false;
            s_ttext['['] = false; 
            s_ttext[']'] = false;
            s_ttext['?'] = false;
            s_ttext['='] = false;
 
            // digits = %d48-57
            for (int i = 48; i <= 57; i++) 
                s_digits[i] = true; 

            // boundary = DIGIT / ALPHA / "'" / "(" / ")" / "+" / "_" / "," / "-" / "." / "/" / ":" / "=" / "?" / " " 
            // cannot end with " "
            for (int i = '0'; i <= '9'; i++) { s_boundary[i] = true; }
            for (int i = 'A'; i <= 'Z'; i++) { s_boundary[i] = true; }
            for (int i = 'a'; i <= 'z'; i++) { s_boundary[i] = true; } 
            s_boundary['\''] = true;
            s_boundary['('] = true; 
            s_boundary[')'] = true; 
            s_boundary['+'] = true;
            s_boundary['_'] = true; 
            s_boundary[','] = true;
            s_boundary['-'] = true;
            s_boundary['.'] = true;
            s_boundary['/'] = true; 
            s_boundary[':'] = true;
            s_boundary['='] = true; 
            s_boundary['?'] = true; 
            s_boundary[' '] = true;
        } 

        public static bool SkipCFWS(string data, ref int offset)
        {
            int comments = 0; 
            for (; offset < data.Length; offset++)
            { 
                if (data[offset] > 127) 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, data[offset], ((int)data[offset]).ToString("X", CultureInfo.InvariantCulture))));
                else if (data[offset] == '\\' && comments > 0) 
                    offset += 2;
                else if (data[offset] == '(')
                    comments++;
                else if (data[offset] == ')') 
                    comments--;
                else if (data[offset] != ' ' && data[offset] != '\t' && comments == 0) 
                    return true; 
            }
            return false; 
        }

        public static string ReadQuotedString(string data, ref int offset, StringBuilder builder)
        { 
            // assume first char is the opening quote
            int start = ++offset; 
            StringBuilder localBuilder = (builder != null ? builder : new StringBuilder()); 
            for (; offset < data.Length; offset++)
            { 
                if (data[offset] == '\\')
                {
                    localBuilder.Append(data, start, offset - start);
                    start = ++offset; 
                    continue;
                } 
                else if (data[offset] == '"') 
                {
                    localBuilder.Append(data, start, offset - start); 
                    offset++;
                    return (builder != null ? null : localBuilder.ToString());
                }
                else if (!(data[offset] < s_fqtext.Length && s_fqtext[data[offset]])) 
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, data[offset], ((int)data[offset]).ToString("X", CultureInfo.InvariantCulture)))); 
                } 
            }
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderMalformedHeader))); 
        }

        public static string ReadParameterAttribute(string data, ref int offset, StringBuilder builder)
        { 
            if (!SkipCFWS(data, ref offset))
                return null; 
 
            return ReadToken(data, ref offset, null);
        } 

        public static string ReadParameterValue(string data, ref int offset, StringBuilder builder)
        {
            if (!SkipCFWS(data, ref offset)) 
                return string.Empty;
 
            if (offset < data.Length && data[offset] == '"') 
                return ReadQuotedString(data, ref offset, builder);
            else 
                return ReadToken(data, ref offset, builder);
        }

        public static string ReadToken(string data, ref int offset, StringBuilder builder) 
        {
            int start = offset; 
            for (; offset < data.Length; offset++) 
            {
                if (data[offset] > s_ttext.Length) 
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, data[offset], ((int)data[offset]).ToString("X", CultureInfo.InvariantCulture))));
                }
                else if (!s_ttext[data[offset]]) 
                {
                    break; 
                } 
            }
            return data.Substring(start, offset - start); 
        }

        public static string ReadDigits(string data, ref int offset, StringBuilder builder)
        { 
            int start = offset;
            StringBuilder localBuilder = (builder != null ? builder : new StringBuilder()); 
            for (; offset < data.Length && data[offset] < s_digits.Length && s_digits[data[offset]]; offset++); 
            localBuilder.Append(data, start, offset - start);
            return (builder != null ? null : localBuilder.ToString()); 
        }

        public static bool IsValidMimeBoundary(string data)
        { 
            int length = (data == null) ? 0 : data.Length;
            if (length == 0 || length > 70 || data[length - 1] == ' ') 
                return false; 

            for (int i = 0; i < length; i++) 
            {
                if (!(data[i] < s_boundary.Length && s_boundary[data[i]]))
                    return false;
            } 

            return true; 
        } 
    }
 
}


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK