XmlMtomWriter.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

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

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

namespace System.Xml 
{
    using System; 
    using System.Xml; 
    using System.Xml.XPath;
    using System.IO; 
    using System.Text;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Threading; 
    using System.Runtime.Serialization;
 
    public interface IXmlMtomWriterInitializer 
    {
        void SetOutput(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo, string boundary, string startUri, bool writeMessageHeaders, bool ownsStream); 
    }

    class XmlMtomWriter : XmlDictionaryWriter, IXmlMtomWriterInitializer
    { 
        // Maximum number of bytes that are inlined as base64 data without being MTOM-optimized as xop:Include
        const int MaxInlinedBytes = 767;  // 768 will be the first MIMEd length 
 
        int maxSizeInBytes;
        XmlDictionaryWriter writer; 
        XmlDictionaryWriter infosetWriter;
        MimeWriter mimeWriter;
        Encoding encoding;
        bool isUTF8; 
        string contentID;
        string contentType; 
        string initialContentTypeForRootPart; 
        string initialContentTypeForMimeMessage;
        MemoryStream contentTypeStream; 
        List mimeParts;
        IList binaryDataChunks;
        int depth;
        int totalSizeOfMimeParts; 
        int sizeOfBufferedBinaryData;
        char[] chars; 
        byte[] bytes; 
        bool isClosed;
        bool ownsStream; 

        public XmlMtomWriter()
        {
        } 

        public void SetOutput(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo, string boundary, string startUri, bool writeMessageHeaders, bool ownsStream) 
        { 
            if (encoding == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encoding"); 
            if (maxSizeInBytes < 0)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("maxSizeInBytes", SR.GetString(SR.ValueMustBeNonNegative)));
            this.maxSizeInBytes = maxSizeInBytes;
            this.encoding = encoding; 
            this.isUTF8 = IsUTF8Encoding(encoding);
            Initialize(stream, startInfo, boundary, startUri, writeMessageHeaders, ownsStream); 
        } 

        XmlDictionaryWriter Writer 
        {
            get
            {
                if (!IsInitialized) 
                {
                    Initialize(); 
                } 
                return writer;
            } 
        }

        bool IsInitialized
        { 
            get { return (initialContentTypeForRootPart == null); }
        } 
 
        void Initialize(Stream stream, string startInfo, string boundary, string startUri, bool writeMessageHeaders, bool ownsStream)
        { 
            if (stream == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
            if (startInfo == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("startInfo"); 
            if (boundary == null)
                boundary = GetBoundaryString(); 
            if (startUri == null) 
                startUri = GenerateUriForMimePart(0);
            if (!MailBnfHelper.IsValidMimeBoundary(boundary)) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.MtomBoundaryInvalid, boundary), "boundary"));

            this.ownsStream = ownsStream;
            this.isClosed = false; 
            this.depth = 0;
            this.totalSizeOfMimeParts = 0; 
            this.sizeOfBufferedBinaryData = 0; 
            this.binaryDataChunks = null;
            this.contentType = null; 
            this.contentTypeStream = null;
            this.contentID = startUri;
            if (this.mimeParts != null)
                this.mimeParts.Clear(); 
            this.mimeWriter = new MimeWriter(stream, boundary);
            this.initialContentTypeForRootPart = GetContentTypeForRootMimePart(this.encoding, startInfo); 
            if (writeMessageHeaders) 
                this.initialContentTypeForMimeMessage = GetContentTypeForMimeMessage(boundary, startUri, startInfo);
        } 

        void Initialize()
        {
            if (this.isClosed) 
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.XmlWriterClosed)));
 
            if (this.initialContentTypeForRootPart != null) 
            {
                if (this.initialContentTypeForMimeMessage != null) 
                {
                    mimeWriter.StartPreface();
                    mimeWriter.WriteHeader(MimeGlobals.MimeVersionHeader, MimeGlobals.DefaultVersion);
                    mimeWriter.WriteHeader(MimeGlobals.ContentTypeHeader, this.initialContentTypeForMimeMessage); 
                    this.initialContentTypeForMimeMessage = null;
                } 
 
                WriteMimeHeaders(this.contentID, this.initialContentTypeForRootPart, this.isUTF8 ? MimeGlobals.Encoding8bit : MimeGlobals.EncodingBinary);
 
                Stream infosetContentStream = this.mimeWriter.GetContentStream();
                IXmlTextWriterInitializer initializer = writer as IXmlTextWriterInitializer;
                if (initializer == null)
                    writer = XmlDictionaryWriter.CreateTextWriter(infosetContentStream, this.encoding, this.ownsStream); 
                else
                    initializer.SetOutput(infosetContentStream, this.encoding, this.ownsStream); 
 
                this.contentID = null;
                this.initialContentTypeForRootPart = null; 
            }
        }

        static string GetBoundaryString() 
        {
            return MimeBoundaryGenerator.Next(); 
        } 

        internal static bool IsUTF8Encoding(Encoding encoding) 
        {
            return encoding.WebName == "utf-8";
        }
 
        static string GetContentTypeForMimeMessage(string boundary, string startUri, string startInfo)
        { 
            StringBuilder contentTypeBuilder = new StringBuilder( 
                String.Format(CultureInfo.InvariantCulture, "{0}/{1};{2}=\"{3}\";{4}=\"{5}\"",
                    MtomGlobals.MediaType, MtomGlobals.MediaSubtype, 
                    MtomGlobals.TypeParam, MtomGlobals.XopType,
                    MtomGlobals.BoundaryParam, boundary));

            if (startUri != null && startUri.Length > 0) 
                contentTypeBuilder.AppendFormat(CultureInfo.InvariantCulture, ";{0}=\"<{1}>\"", MtomGlobals.StartParam, startUri);
 
            if (startInfo != null && startInfo.Length > 0) 
                contentTypeBuilder.AppendFormat(CultureInfo.InvariantCulture, ";{0}=\"{1}\"", MtomGlobals.StartInfoParam, startInfo);
 
            return contentTypeBuilder.ToString();
        }

        static string GetContentTypeForRootMimePart(Encoding encoding, string startInfo) 
        {
            string contentType = String.Format(CultureInfo.InvariantCulture, "{0};{1}={2}", MtomGlobals.XopType, MtomGlobals.CharsetParam, CharSet(encoding)); 
 
            if (startInfo != null)
                contentType = String.Format(CultureInfo.InvariantCulture, "{0};{1}=\"{2}\"", contentType, MtomGlobals.TypeParam, startInfo); 

            return contentType;
        }
 
        static string CharSet(Encoding enc)
        { 
            string name = enc.WebName; 
            if (String.Compare(name, Encoding.UTF8.WebName, StringComparison.OrdinalIgnoreCase) == 0)
                return name; 
            if (String.Compare(name, Encoding.Unicode.WebName, StringComparison.OrdinalIgnoreCase) == 0)
                return "utf-16LE";
            if (String.Compare(name, Encoding.BigEndianUnicode.WebName, StringComparison.OrdinalIgnoreCase) == 0)
                return "utf-16BE"; 
            return name;
        } 
 
        public override void WriteStartElement(string prefix, string localName, string ns)
        { 
            WriteBase64InlineIfPresent();
            ThrowIfElementIsXOPInclude(prefix, localName, ns);
            Writer.WriteStartElement(prefix, localName, ns);
            depth++; 
        }
 
        public override void WriteStartElement(string prefix, XmlDictionaryString localName, XmlDictionaryString ns) 
        {
            if (localName == null) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("localName");

            WriteBase64InlineIfPresent();
            ThrowIfElementIsXOPInclude(prefix, localName.Value, ns == null ? null : ns.Value); 
            Writer.WriteStartElement(prefix, localName, ns);
            depth++; 
        } 

        void ThrowIfElementIsXOPInclude(string prefix, string localName, string ns) 
        {
            if (ns == null)
            {
                XmlBaseWriter w = this.Writer as XmlBaseWriter; 
                if (w != null)
                    ns = w.LookupNamespace(prefix); 
            } 

            if (localName == MtomGlobals.XopIncludeLocalName && ns == MtomGlobals.XopIncludeNamespace) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomDataMustNotContainXopInclude, MtomGlobals.XopIncludeLocalName, MtomGlobals.XopIncludeNamespace)));
        }

        public override void WriteEndElement() 
        {
            WriteXOPInclude(); 
            Writer.WriteEndElement(); 
            depth--;
            WriteXOPBinaryParts(); 
        }

        public override void WriteFullEndElement()
        { 
            WriteXOPInclude();
            Writer.WriteFullEndElement(); 
            depth--; 
            WriteXOPBinaryParts();
        } 

        public override void WriteValue(IStreamProvider value)
        {
            if (value == null) 
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
 
            if (Writer.WriteState == WriteState.Element) 
            {
                if (binaryDataChunks == null) 
                {
                    binaryDataChunks = new List();
                    contentID = GenerateUriForMimePart((mimeParts == null) ? 1 : mimeParts.Count + 1);
                } 
                binaryDataChunks.Add(new MtomBinaryData(value));
            } 
            else 
                Writer.WriteValue(value);
        } 

        public override void WriteBase64(byte[] buffer, int index, int count)
        {
            if (Writer.WriteState == WriteState.Element) 
            {
                if (buffer == null) 
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("buffer")); 

                // Not checking upper bound because it will be caught by "count".  This is what XmlTextWriter does. 
                if (index < 0)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("index", SR.GetString(SR.ValueMustBeNonNegative)));

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

                if (binaryDataChunks == null) 
                {
                    binaryDataChunks = new List();
                    contentID = GenerateUriForMimePart((mimeParts == null) ? 1 : mimeParts.Count + 1);
                } 

                int totalSize = ValidateSizeOfMessage(maxSizeInBytes, 0, totalSizeOfMimeParts); 
                totalSize += ValidateSizeOfMessage(maxSizeInBytes, totalSize, sizeOfBufferedBinaryData); 
                totalSize += ValidateSizeOfMessage(maxSizeInBytes, totalSize, count);
                sizeOfBufferedBinaryData += count; 
                binaryDataChunks.Add(new MtomBinaryData(buffer, index, count));
            }
            else
                Writer.WriteBase64(buffer, index, count); 
        }
 
        internal static int ValidateSizeOfMessage(int maxSize, int offset, int size) 
        {
            if (size > (maxSize - offset)) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomExceededMaxSizeInBytes, maxSize)));
            return size;
        }
 
        void WriteBase64InlineIfPresent()
        { 
            if (binaryDataChunks != null) 
            {
                WriteBase64Inline(); 
            }
        }

        void WriteBase64Inline() 
        {
            foreach (MtomBinaryData data in binaryDataChunks) 
            { 
                if (data.type == MtomBinaryDataType.Provider)
                { 
                    Writer.WriteValue(data.provider);
                }
                else
                { 
                    Writer.WriteBase64(data.chunk, 0, data.chunk.Length);
                } 
            } 
            this.sizeOfBufferedBinaryData = 0;
            binaryDataChunks = null; 
            contentType = null;
            contentID = null;
        }
 
        void WriteXOPInclude()
        { 
            if (binaryDataChunks == null) 
                return;
 
            bool inline = true;
            long size = 0;
            foreach (MtomBinaryData data in binaryDataChunks)
            { 
                long len = data.Length;
                if (len < 0 || len > (MaxInlinedBytes - size)) 
                { 
                    inline = false;
                    break; 
                }
                size += len;
            }
 
            if (inline)
                WriteBase64Inline(); 
            else 
            {
                if (mimeParts == null) 
                    mimeParts = new List();

                MimePart mimePart = new MimePart(binaryDataChunks, contentID, contentType, MimeGlobals.EncodingBinary, sizeOfBufferedBinaryData, maxSizeInBytes);
                mimeParts.Add(mimePart); 

                totalSizeOfMimeParts += ValidateSizeOfMessage(maxSizeInBytes, totalSizeOfMimeParts, mimePart.sizeInBytes); 
                totalSizeOfMimeParts += ValidateSizeOfMessage(maxSizeInBytes, totalSizeOfMimeParts, mimeWriter.GetBoundarySize()); 

                Writer.WriteStartElement(MtomGlobals.XopIncludePrefix, MtomGlobals.XopIncludeLocalName, MtomGlobals.XopIncludeNamespace); 
                Writer.WriteStartAttribute(MtomGlobals.XopIncludeHrefLocalName, MtomGlobals.XopIncludeHrefNamespace);
                Writer.WriteValue(String.Format(CultureInfo.InvariantCulture, "{0}{1}", MimeGlobals.ContentIDScheme, Uri.EscapeDataString(contentID)));
                Writer.WriteEndAttribute();
                Writer.WriteEndElement(); 
                binaryDataChunks = null;
                sizeOfBufferedBinaryData = 0; 
                contentType = null; 
                contentID = null;
            } 
        }

        public static string GenerateUriForMimePart(int index)
        { 
            return String.Format(CultureInfo.InvariantCulture, "http://tempuri.org/{0}/{1}", index, DateTime.Now.Ticks);
        } 
 
        void WriteXOPBinaryParts()
        { 
            if (depth > 0 || mimeWriter.WriteState == MimeWriterState.Closed)
                return;

            if (Writer.WriteState != WriteState.Closed) 
                Writer.Flush();
 
            if (mimeParts != null) 
            {
                foreach (MimePart part in mimeParts) 
                {
                    WriteMimeHeaders(part.contentID, part.contentType, part.contentTransferEncoding);
                    Stream s = mimeWriter.GetContentStream();
                    int blockSize = 256; 
                    int bytesRead = 0;
                    byte[] block = new byte[blockSize]; 
                    Stream stream = null; 
                    foreach (MtomBinaryData data in part.binaryData)
                    { 
                        if (data.type == MtomBinaryDataType.Provider)
                        {
                            stream = data.provider.GetStream();
                            if (stream == null) 
                                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlInvalidStream)));
                            while (true) 
                            { 
                                bytesRead = stream.Read(block, 0, blockSize);
                                if (bytesRead > 0) 
                                    s.Write(block, 0, bytesRead);
                                else
                                    break;
                                if (blockSize < 65536 && bytesRead == blockSize) 
                                {
                                    blockSize = blockSize * 16; 
                                    block = new byte[blockSize]; 
                                }
                            } 

                            data.provider.ReleaseStream(stream);
                        }
                        else 
                        {
                            s.Write(data.chunk, 0, data.chunk.Length); 
                        } 
                    }
                } 
                mimeParts.Clear();
            }
            mimeWriter.Close();
        } 

        void WriteMimeHeaders(string contentID, string contentType, string contentTransferEncoding) 
        { 
            mimeWriter.StartPart();
            if (contentID != null) 
                mimeWriter.WriteHeader(MimeGlobals.ContentIDHeader, String.Format(CultureInfo.InvariantCulture, "<{0}>", contentID));
            if (contentTransferEncoding != null)
                mimeWriter.WriteHeader(MimeGlobals.ContentTransferEncodingHeader, contentTransferEncoding);
            if (contentType != null) 
                mimeWriter.WriteHeader(MimeGlobals.ContentTypeHeader, contentType);
        } 
#if NO 
        public override bool CanSubsetElements
        { 
            get { return Writer.CanSubsetElements; }
        }
#endif
        public override void Close() 
        {
            if (!this.isClosed) 
            { 
                this.isClosed = true;
                if (IsInitialized) 
                {
                    WriteXOPInclude();
                    if (Writer.WriteState == WriteState.Element ||
                        Writer.WriteState == WriteState.Attribute || 
                        Writer.WriteState == WriteState.Content)
                    { 
                        Writer.WriteEndDocument(); 
                    }
                    Writer.Flush(); 
                    depth = 0;
                    WriteXOPBinaryParts();
                    Writer.Close();
                } 
            }
        } 
 
        void CheckIfStartContentTypeAttribute(string localName, string ns)
        { 
            if (localName != null && localName == MtomGlobals.MimeContentTypeLocalName
                && ns != null && (ns == MtomGlobals.MimeContentTypeNamespace200406 || ns == MtomGlobals.MimeContentTypeNamespace200505))
            {
                contentTypeStream = new MemoryStream(); 
                this.infosetWriter = Writer;
                this.writer = XmlDictionaryWriter.CreateBinaryWriter(contentTypeStream); 
                Writer.WriteStartElement("Wrapper"); 
                Writer.WriteStartAttribute(localName, ns);
            } 
        }

        void CheckIfEndContentTypeAttribute()
        { 
            if (contentTypeStream != null)
            { 
                Writer.WriteEndAttribute(); 
                Writer.WriteEndElement();
                Writer.Flush(); 
                contentTypeStream.Position = 0;
                XmlReader contentTypeReader = XmlDictionaryReader.CreateBinaryReader(contentTypeStream, null, XmlDictionaryReaderQuotas.Max, null, null);
                while (contentTypeReader.Read())
                { 
                    if (contentTypeReader.IsStartElement("Wrapper"))
                    { 
                        contentType = contentTypeReader.GetAttribute(MtomGlobals.MimeContentTypeLocalName, MtomGlobals.MimeContentTypeNamespace200406); 
 			if (contentType == null)
                        { 
                            contentType = contentTypeReader.GetAttribute(MtomGlobals.MimeContentTypeLocalName, MtomGlobals.MimeContentTypeNamespace200505);
                        }
                        break;
                    } 
                }
                this.writer = infosetWriter; 
                this.infosetWriter = null; 
                contentTypeStream = null;
                if (contentType != null) 
                    Writer.WriteString(contentType);
            }
        }
 
#if NO
        public override bool ElementSubsetting 
        { 
            get
            { 
                return Writer.ElementSubsetting;
            }
            set
            { 
                Writer.ElementSubsetting = value;
            } 
        } 
#endif
        public override void Flush() 
        {
            if (IsInitialized)
                Writer.Flush();
        } 

        public override string LookupPrefix(string ns) 
        { 
            return Writer.LookupPrefix(ns);
        } 

        public override XmlWriterSettings Settings
        {
            get 
            {
                return Writer.Settings; 
            } 
        }
 
        public override void WriteAttributes(XmlReader reader, bool defattr)
        {
            Writer.WriteAttributes(reader, defattr);
        } 

        public override void WriteBinHex(byte[] buffer, int index, int count) 
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteBinHex(buffer, index, count); 
        }

        public override void WriteCData(string text)
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteCData(text); 
        } 

        public override void WriteCharEntity(char ch) 
        {
            WriteBase64InlineIfPresent();
            Writer.WriteCharEntity(ch);
        } 

        public override void WriteChars(char[] buffer, int index, int count) 
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteChars(buffer, index, count); 
        }

        public override void WriteComment(string text)
        { 
            // Don't write comments after the document element
            if (depth == 0 && mimeWriter.WriteState == MimeWriterState.Closed) 
                return; 

            WriteBase64InlineIfPresent(); 
            Writer.WriteComment(text);
        }

        public override void WriteDocType(string name, string pubid, string sysid, string subset) 
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteDocType(name, pubid, sysid, subset); 
        }
#if NO 
        public override void WriteElementSubset(ArraySegment buffer)
        {
            Writer.WriteElementSubset(buffer);
        } 
#endif
        public override void WriteEndAttribute() 
        { 
            CheckIfEndContentTypeAttribute();
            Writer.WriteEndAttribute(); 
        }

        public override void WriteEndDocument()
        { 
            WriteXOPInclude();
            Writer.WriteEndDocument(); 
            depth = 0; 
            WriteXOPBinaryParts();
        } 

        public override void WriteEntityRef(string name)
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteEntityRef(name);
        } 
 
        public override void WriteName(string name)
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteName(name);
        }
 
        public override void WriteNmToken(string name)
        { 
            WriteBase64InlineIfPresent(); 
            Writer.WriteNmToken(name);
        } 

        protected override void WriteTextNode(XmlDictionaryReader reader, bool attribute)
        {
            Type type = reader.ValueType; 
            if (type == typeof(string))
            { 
                if (reader.CanReadValueChunk) 
                {
                    if (chars == null) 
                    {
                        chars = new char[256];
                    }
                    int count; 
                    while ((count = reader.ReadValueChunk(chars, 0, chars.Length)) > 0)
                    { 
                        this.WriteChars(chars, 0, count); 
                    }
                } 
                else
                {
                    WriteString(reader.Value);
                } 
                if (!attribute)
                { 
                    reader.Read(); 
                }
            } 
            else if (type == typeof(byte[]))
            {
                if (reader.CanReadBinaryContent)
                { 
                    // Its best to read in buffers that are a multiple of 3 so we don't break base64 boundaries when converting text
                    if (bytes == null) 
                    { 
                        bytes = new byte[384];
                    } 
                    int count;
                    while ((count = reader.ReadValueAsBase64(bytes, 0, bytes.Length)) > 0)
                    {
                        this.WriteBase64(bytes, 0, count); 
                    }
                } 
                else 
                {
                    WriteString(reader.Value); 
                }
                if (!attribute)
                {
                    reader.Read(); 
                }
            } 
            else 
            {
                base.WriteTextNode(reader, attribute); 
            }
        }

        public override void WriteNode(XPathNavigator navigator, bool defattr) 
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteNode(navigator, defattr); 
        }
 
        public override void WriteProcessingInstruction(string name, string text)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteProcessingInstruction(name, text); 
        }
 
        public override void WriteQualifiedName(string localName, string namespaceUri) 
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteQualifiedName(localName, namespaceUri);
        }

        public override void WriteRaw(char[] buffer, int index, int count) 
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteRaw(buffer, index, count); 
        }
 
        public override void WriteRaw(string data)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteRaw(data); 
        }
 
        public override void WriteStartAttribute(string prefix, string localName, string ns) 
        {
            Writer.WriteStartAttribute(prefix, localName, ns); 
            CheckIfStartContentTypeAttribute(localName, ns);
        }

        public override void WriteStartAttribute(string prefix, XmlDictionaryString localName, XmlDictionaryString ns) 
        {
            Writer.WriteStartAttribute(prefix, localName, ns); 
            if (localName != null && ns != null) 
                CheckIfStartContentTypeAttribute(localName.Value, ns.Value);
        } 

        public override void WriteStartDocument()
        {
            Writer.WriteStartDocument(); 
        }
 
        public override void WriteStartDocument(bool standalone) 
        {
            Writer.WriteStartDocument(standalone); 
        }

        public override WriteState WriteState
        { 
            get
            { 
                return Writer.WriteState; 
            }
        } 

        public override void WriteString(string text)
        {
            // Don't write whitespace after the document element 
            if (depth == 0 && mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(text))
                return; 
 
            WriteBase64InlineIfPresent();
            Writer.WriteString(text); 
        }

        public override void WriteString(XmlDictionaryString value)
        { 
            // Don't write whitespace after the document element
            if (depth == 0 && mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(value.Value)) 
                return; 

            WriteBase64InlineIfPresent(); 
            Writer.WriteString(value);
        }

        public override void WriteSurrogateCharEntity(char lowChar, char highChar) 
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteSurrogateCharEntity(lowChar, highChar); 
        }
 
        public override void WriteWhitespace(string whitespace)
        {
            // Don't write whitespace after the document element
            if (depth == 0 && mimeWriter.WriteState == MimeWriterState.Closed) 
                return;
 
            WriteBase64InlineIfPresent(); 
            Writer.WriteWhitespace(whitespace);
        } 

        public override void WriteValue(object value)
        {
            IStreamProvider sp = value as IStreamProvider; 
            if (sp != null)
            { 
                WriteValue(sp); 
            }
            else 
            {
                WriteBase64InlineIfPresent();
                Writer.WriteValue(value);
            } 
        }
 
        public override void WriteValue(string value) 
        {
            // Don't write whitespace after the document element 
            if (depth == 0 && mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(value))
                return;

            WriteBase64InlineIfPresent(); 
            Writer.WriteValue(value);
        } 
 
        public override void WriteValue(bool value)
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
 
        public override void WriteValue(DateTime value)
        { 
            WriteBase64InlineIfPresent(); 
            Writer.WriteValue(value);
        } 

        public override void WriteValue(double value)
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteValue(value);
        } 
 
        public override void WriteValue(int value)
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
 
        public override void WriteValue(long value)
        { 
            WriteBase64InlineIfPresent(); 
            Writer.WriteValue(value);
        } 

#if DECIMAL_FLOAT_API
        public override void WriteValue(decimal value)
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value); 
        } 

        public override void WriteValue(float value) 
        {
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        } 
#endif
        public override void WriteValue(XmlDictionaryString value) 
        { 
            // Don't write whitespace after the document element
            if (depth == 0 && mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(value.Value)) 
                return;

            WriteBase64InlineIfPresent();
            Writer.WriteValue(value); 
        }
 
        public override void WriteXmlnsAttribute(string prefix, string ns) 
        {
            Writer.WriteXmlnsAttribute(prefix, ns); 
        }

        public override void WriteXmlnsAttribute(string prefix, XmlDictionaryString ns)
        { 
            Writer.WriteXmlnsAttribute(prefix, ns);
        } 
 
        public override string XmlLang
        { 
            get
            {
                return Writer.XmlLang;
            } 
        }
 
        public override XmlSpace XmlSpace 
        {
            get 
            {
                return Writer.XmlSpace;
            }
        } 

        static class MimeBoundaryGenerator 
        { 
            static long id;
            static string prefix; 

            static MimeBoundaryGenerator()
            {
                prefix = string.Concat(Guid.NewGuid().ToString(), "+id="); 
            }
 
            internal static string Next() 
            {
                long nextId = Interlocked.Increment(ref id); 
                return String.Format(CultureInfo.InvariantCulture, "{0}{1}", prefix, nextId);
            }
        }
 
        class MimePart
        { 
            internal IList binaryData; 
            internal string contentID;
            internal string contentType; 
            internal string contentTransferEncoding;
            internal int sizeInBytes;

            internal MimePart(IList binaryData, string contentID, string contentType, string contentTransferEncoding, int sizeOfBufferedBinaryData, int maxSizeInBytes) 
            {
                this.binaryData = binaryData; 
                this.contentID = contentID; 
                this.contentType = contentType ?? MtomGlobals.DefaultContentTypeForBinary;
                this.contentTransferEncoding = contentTransferEncoding; 
                this.sizeInBytes = GetSize(contentID, contentType, contentTransferEncoding, sizeOfBufferedBinaryData, maxSizeInBytes);
            }

            static int GetSize(string contentID, string contentType, string contentTransferEncoding, int sizeOfBufferedBinaryData, int maxSizeInBytes) 
            {
                int size = XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, 0, MimeGlobals.CRLF.Length * 3); 
                if (contentTransferEncoding != null) 
                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, MimeWriter.GetHeaderSize(MimeGlobals.ContentTransferEncodingHeader, contentTransferEncoding, maxSizeInBytes));
                if (contentType != null) 
                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, MimeWriter.GetHeaderSize(MimeGlobals.ContentTypeHeader, contentType, maxSizeInBytes));
                if (contentID != null)
                {
                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, MimeWriter.GetHeaderSize(MimeGlobals.ContentIDHeader, contentID, maxSizeInBytes)); 
                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, 2); // include '<' and '>'
                } 
                size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, sizeOfBufferedBinaryData); 
                return size;
            } 
        }
    }

 
    internal static class MtomGlobals
    { 
        internal static string XopIncludeLocalName = "Include"; 
        internal static string XopIncludeNamespace = "http://www.w3.org/2004/08/xop/include";
        internal static string XopIncludePrefix = "xop"; 
        internal static string XopIncludeHrefLocalName = "href";
        internal static string XopIncludeHrefNamespace = String.Empty;
        internal static string MediaType = "multipart";
        internal static string MediaSubtype = "related"; 
        internal static string BoundaryParam = "boundary";
        internal static string TypeParam = "type"; 
        internal static string XopMediaType = "application"; 
        internal static string XopMediaSubtype = "xop+xml";
        internal static string XopType = "application/xop+xml"; 
        internal static string StartParam = "start";
        internal static string StartInfoParam = "start-info";
        internal static string ActionParam = "action";
        internal static string CharsetParam = "charset"; 
        internal static string MimeContentTypeLocalName = "contentType";
        internal static string MimeContentTypeNamespace200406 = "http://www.w3.org/2004/06/xmlmime"; 
        internal static string MimeContentTypeNamespace200505 = "http://www.w3.org/2005/05/xmlmime"; 
        internal static string DefaultContentTypeForBinary = "application/octet-stream";
    } 

    internal static class MimeGlobals
    {
        internal static string MimeVersionHeader = "MIME-Version"; 
        internal static string DefaultVersion = "1.0";
        internal static string ContentIDScheme = "cid:"; 
        internal static string ContentIDHeader = "Content-ID"; 
        internal static string ContentTypeHeader = "Content-Type";
        internal static string ContentTransferEncodingHeader = "Content-Transfer-Encoding"; 
        internal static string EncodingBinary = "binary";
        internal static string Encoding8bit = "8bit";
        internal static byte[] COLONSPACE = new byte[] { (byte)':', (byte)' ' };
        internal static byte[] DASHDASH = new byte[] { (byte)'-', (byte)'-' }; 
        internal static byte[] CRLF = new byte[] { (byte)'\r', (byte)'\n' };
        // Per RFC2045, preceding CRLF sequence is part of the boundary. MIME boundary tags begin with -- 
        internal static byte[] BoundaryPrefix = new byte[] { (byte)'\r', (byte)'\n', (byte)'-', (byte)'-' }; 
    }
 
    enum MimeWriterState
    {
        Start,
        StartPreface, 
        StartPart,
        Header, 
        Content, 
        Closed,
    } 

    internal class MimeWriter
    {
        Stream stream; 
        byte[] boundaryBytes;
        MimeWriterState state; 
        BufferedWrite bufferedWrite; 
        Stream contentStream;
 
        internal MimeWriter(Stream stream, string boundary)
        {
            if (stream == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream"); 
            if (boundary == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("boundary"); 
 
            this.stream = stream;
            this.boundaryBytes = MimeWriter.GetBoundaryBytes(boundary); 
            this.state = MimeWriterState.Start;
            this.bufferedWrite = new BufferedWrite();
        }
 
        internal static int GetHeaderSize(string name, string value, int maxSizeInBytes)
        { 
            if (name == null) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name");
            if (value == null) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");

            int size = XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, 0, MimeGlobals.COLONSPACE.Length + MimeGlobals.CRLF.Length);
            size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, name.Length); 
            size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, value.Length);
            return size; 
        } 

        internal static byte[] GetBoundaryBytes(string boundary) 
        {
            byte[] boundaryBytes = new byte[boundary.Length + MimeGlobals.BoundaryPrefix.Length];
            for (int i = 0; i < MimeGlobals.BoundaryPrefix.Length; i++)
                boundaryBytes[i] = MimeGlobals.BoundaryPrefix[i]; 
            Encoding.ASCII.GetBytes(boundary, 0, boundary.Length, boundaryBytes, MimeGlobals.BoundaryPrefix.Length);
            return boundaryBytes; 
        } 

        internal MimeWriterState WriteState 
        {
            get
            {
                return state; 
            }
        } 
 
        internal int GetBoundarySize()
        { 
            return this.boundaryBytes.Length;
        }

        internal void StartPreface() 
        {
            if (state != MimeWriterState.Start) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeWriterInvalidStateForStartPreface, state.ToString()))); 

            state = MimeWriterState.StartPreface; 
        }

        internal void StartPart()
        { 
            switch (state)
            { 
                case MimeWriterState.StartPart: 
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeWriterInvalidStateForStartPart, state.ToString()))); 
                default:
                    break;
            }
 
            state = MimeWriterState.StartPart;
 
            if (contentStream != null) 
            {
                contentStream.Flush(); 
                contentStream = null;
            }

            bufferedWrite.Write(boundaryBytes); 
            bufferedWrite.Write(MimeGlobals.CRLF);
        } 
 
        internal void Close()
        { 
            switch (state)
            {
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeWriterInvalidStateForClose, state.ToString()))); 
                default:
                    break; 
            } 

            state = MimeWriterState.Closed; 

            if (contentStream != null)
            {
                contentStream.Flush(); 
                contentStream = null;
            } 
 
            bufferedWrite.Write(boundaryBytes);
            bufferedWrite.Write(MimeGlobals.DASHDASH); 
            bufferedWrite.Write(MimeGlobals.CRLF);

            Flush();
        } 

        void Flush() 
        { 
            if (bufferedWrite.Length > 0)
            { 
                stream.Write(bufferedWrite.GetBuffer(), 0, bufferedWrite.Length);
                bufferedWrite.Reset();
            }
        } 

        internal void WriteHeader(string name, string value) 
        { 
            if (name == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); 
            if (value == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");

            switch (state) 
            {
                case MimeWriterState.Start: 
                case MimeWriterState.Content: 
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeWriterInvalidStateForHeader, state.ToString()))); 
                default:
                    break;
            }
 
            state = MimeWriterState.Header;
 
            bufferedWrite.Write(name); 
            bufferedWrite.Write(MimeGlobals.COLONSPACE);
            bufferedWrite.Write(value); 
            bufferedWrite.Write(MimeGlobals.CRLF);
        }

        internal Stream GetContentStream() 
        {
            switch (state) 
            { 
                case MimeWriterState.Start:
                case MimeWriterState.Content: 
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeWriterInvalidStateForContent, state.ToString())));
                default:
                    break; 
            }
 
            state = MimeWriterState.Content; 

            bufferedWrite.Write(MimeGlobals.CRLF); 

            Flush();

            contentStream = stream; 

            return contentStream; 
        } 
    }
 
    internal class BufferedWrite
    {
        byte[] buffer;
        int offset; 

        internal BufferedWrite() : this(256) 
        { 
        }
 
        internal BufferedWrite(int initialSize)
        {
            buffer = new byte[initialSize];
        } 

        void EnsureBuffer(int count) 
        { 
            int currSize = buffer.Length;
            if (count > currSize - offset) 
            {
                int newSize = currSize;
                do
                { 
                    if (newSize == Int32.MaxValue)
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.WriteBufferOverflow))); 
                    newSize = (newSize < Int32.MaxValue / 2) ? newSize * 2 : Int32.MaxValue; 
                }
                while (count > newSize - offset); 
                byte[] newBuffer = new byte[newSize];
                Buffer.BlockCopy(buffer, 0, newBuffer, 0, offset);
                buffer = newBuffer;
            } 
        }
 
        internal int Length 
        {
            get 
            {
                return offset;
            }
        } 

        internal byte[] GetBuffer() 
        { 
            return buffer;
        } 

        internal void Reset()
        {
            offset = 0; 
        }
 
        internal void Write(byte[] value) 
        {
            Write(value, 0, value.Length); 
        }

        internal void Write(byte[] value, int index, int count)
        { 
            EnsureBuffer(count);
            Buffer.BlockCopy(value, index, buffer, offset, count); 
            offset += count; 
        }
 
        internal void Write(string value)
        {
            Write(value, 0, value.Length);
        } 

        internal void Write(string value, int index, int count) 
        { 
            EnsureBuffer(count);
            for (int i = 0; i < count; i++) 
            {
                char c = value[index + i];
                if ((ushort)c > 0xFF)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, c, ((int)c).ToString("X", CultureInfo.InvariantCulture)))); 
                buffer[offset + i] = (byte)c;
            } 
            offset += count; 
        }
 
#if NO
        internal void Write(byte value)
        {
            EnsureBuffer(1); 
            buffer[offset++] = value;
        } 
 
        internal void Write(char value)
        { 
            EnsureBuffer(1);
            if ((ushort)value > 0xFF)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, value, ((int)value).ToString("X", CultureInfo.InvariantCulture)))));
            buffer[offset++] = (byte)value; 
        }
 
        internal void Write(int value) 
        {
            Write(value.ToString()); 
        }

        internal void Write(char[] value)
        { 
            Write(value, 0, value.Length);
        } 
 
        internal void Write(char[] value, int index, int count)
        { 
            EnsureBuffer(count);
            for (int i = 0; i < count; i++)
            {
                char c = value[index + i]; 
                if ((ushort)c > 0xFF)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, c, ((int)c).ToString("X", CultureInfo.InvariantCulture))))); 
                buffer[offset + i] = (byte)c; 
            }
            offset += count; 
        }

#endif
 
    }
 
    enum MtomBinaryDataType { Provider, Segment } 

    class MtomBinaryData 
    {
        internal MtomBinaryDataType type;

        internal IStreamProvider provider; 
        internal byte[] chunk;
 
        internal MtomBinaryData(IStreamProvider provider) 
        {
            this.type = MtomBinaryDataType.Provider; 
            this.provider = provider;
        }

        internal MtomBinaryData(byte[] buffer, int offset, int count) 
        {
            this.type = MtomBinaryDataType.Segment; 
            this.chunk = new byte[count]; 
            Buffer.BlockCopy(buffer, offset, this.chunk, 0, count);
        } 

        internal long Length
        {
            get 
            {
                if (this.type == MtomBinaryDataType.Segment) 
                    return this.chunk.Length; 

                return -1; 
            }
        }
    }
} 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------

namespace System.Xml 
{
    using System; 
    using System.Xml; 
    using System.Xml.XPath;
    using System.IO; 
    using System.Text;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Threading; 
    using System.Runtime.Serialization;
 
    public interface IXmlMtomWriterInitializer 
    {
        void SetOutput(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo, string boundary, string startUri, bool writeMessageHeaders, bool ownsStream); 
    }

    class XmlMtomWriter : XmlDictionaryWriter, IXmlMtomWriterInitializer
    { 
        // Maximum number of bytes that are inlined as base64 data without being MTOM-optimized as xop:Include
        const int MaxInlinedBytes = 767;  // 768 will be the first MIMEd length 
 
        int maxSizeInBytes;
        XmlDictionaryWriter writer; 
        XmlDictionaryWriter infosetWriter;
        MimeWriter mimeWriter;
        Encoding encoding;
        bool isUTF8; 
        string contentID;
        string contentType; 
        string initialContentTypeForRootPart; 
        string initialContentTypeForMimeMessage;
        MemoryStream contentTypeStream; 
        List mimeParts;
        IList binaryDataChunks;
        int depth;
        int totalSizeOfMimeParts; 
        int sizeOfBufferedBinaryData;
        char[] chars; 
        byte[] bytes; 
        bool isClosed;
        bool ownsStream; 

        public XmlMtomWriter()
        {
        } 

        public void SetOutput(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo, string boundary, string startUri, bool writeMessageHeaders, bool ownsStream) 
        { 
            if (encoding == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encoding"); 
            if (maxSizeInBytes < 0)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("maxSizeInBytes", SR.GetString(SR.ValueMustBeNonNegative)));
            this.maxSizeInBytes = maxSizeInBytes;
            this.encoding = encoding; 
            this.isUTF8 = IsUTF8Encoding(encoding);
            Initialize(stream, startInfo, boundary, startUri, writeMessageHeaders, ownsStream); 
        } 

        XmlDictionaryWriter Writer 
        {
            get
            {
                if (!IsInitialized) 
                {
                    Initialize(); 
                } 
                return writer;
            } 
        }

        bool IsInitialized
        { 
            get { return (initialContentTypeForRootPart == null); }
        } 
 
        void Initialize(Stream stream, string startInfo, string boundary, string startUri, bool writeMessageHeaders, bool ownsStream)
        { 
            if (stream == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
            if (startInfo == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("startInfo"); 
            if (boundary == null)
                boundary = GetBoundaryString(); 
            if (startUri == null) 
                startUri = GenerateUriForMimePart(0);
            if (!MailBnfHelper.IsValidMimeBoundary(boundary)) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.MtomBoundaryInvalid, boundary), "boundary"));

            this.ownsStream = ownsStream;
            this.isClosed = false; 
            this.depth = 0;
            this.totalSizeOfMimeParts = 0; 
            this.sizeOfBufferedBinaryData = 0; 
            this.binaryDataChunks = null;
            this.contentType = null; 
            this.contentTypeStream = null;
            this.contentID = startUri;
            if (this.mimeParts != null)
                this.mimeParts.Clear(); 
            this.mimeWriter = new MimeWriter(stream, boundary);
            this.initialContentTypeForRootPart = GetContentTypeForRootMimePart(this.encoding, startInfo); 
            if (writeMessageHeaders) 
                this.initialContentTypeForMimeMessage = GetContentTypeForMimeMessage(boundary, startUri, startInfo);
        } 

        void Initialize()
        {
            if (this.isClosed) 
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.XmlWriterClosed)));
 
            if (this.initialContentTypeForRootPart != null) 
            {
                if (this.initialContentTypeForMimeMessage != null) 
                {
                    mimeWriter.StartPreface();
                    mimeWriter.WriteHeader(MimeGlobals.MimeVersionHeader, MimeGlobals.DefaultVersion);
                    mimeWriter.WriteHeader(MimeGlobals.ContentTypeHeader, this.initialContentTypeForMimeMessage); 
                    this.initialContentTypeForMimeMessage = null;
                } 
 
                WriteMimeHeaders(this.contentID, this.initialContentTypeForRootPart, this.isUTF8 ? MimeGlobals.Encoding8bit : MimeGlobals.EncodingBinary);
 
                Stream infosetContentStream = this.mimeWriter.GetContentStream();
                IXmlTextWriterInitializer initializer = writer as IXmlTextWriterInitializer;
                if (initializer == null)
                    writer = XmlDictionaryWriter.CreateTextWriter(infosetContentStream, this.encoding, this.ownsStream); 
                else
                    initializer.SetOutput(infosetContentStream, this.encoding, this.ownsStream); 
 
                this.contentID = null;
                this.initialContentTypeForRootPart = null; 
            }
        }

        static string GetBoundaryString() 
        {
            return MimeBoundaryGenerator.Next(); 
        } 

        internal static bool IsUTF8Encoding(Encoding encoding) 
        {
            return encoding.WebName == "utf-8";
        }
 
        static string GetContentTypeForMimeMessage(string boundary, string startUri, string startInfo)
        { 
            StringBuilder contentTypeBuilder = new StringBuilder( 
                String.Format(CultureInfo.InvariantCulture, "{0}/{1};{2}=\"{3}\";{4}=\"{5}\"",
                    MtomGlobals.MediaType, MtomGlobals.MediaSubtype, 
                    MtomGlobals.TypeParam, MtomGlobals.XopType,
                    MtomGlobals.BoundaryParam, boundary));

            if (startUri != null && startUri.Length > 0) 
                contentTypeBuilder.AppendFormat(CultureInfo.InvariantCulture, ";{0}=\"<{1}>\"", MtomGlobals.StartParam, startUri);
 
            if (startInfo != null && startInfo.Length > 0) 
                contentTypeBuilder.AppendFormat(CultureInfo.InvariantCulture, ";{0}=\"{1}\"", MtomGlobals.StartInfoParam, startInfo);
 
            return contentTypeBuilder.ToString();
        }

        static string GetContentTypeForRootMimePart(Encoding encoding, string startInfo) 
        {
            string contentType = String.Format(CultureInfo.InvariantCulture, "{0};{1}={2}", MtomGlobals.XopType, MtomGlobals.CharsetParam, CharSet(encoding)); 
 
            if (startInfo != null)
                contentType = String.Format(CultureInfo.InvariantCulture, "{0};{1}=\"{2}\"", contentType, MtomGlobals.TypeParam, startInfo); 

            return contentType;
        }
 
        static string CharSet(Encoding enc)
        { 
            string name = enc.WebName; 
            if (String.Compare(name, Encoding.UTF8.WebName, StringComparison.OrdinalIgnoreCase) == 0)
                return name; 
            if (String.Compare(name, Encoding.Unicode.WebName, StringComparison.OrdinalIgnoreCase) == 0)
                return "utf-16LE";
            if (String.Compare(name, Encoding.BigEndianUnicode.WebName, StringComparison.OrdinalIgnoreCase) == 0)
                return "utf-16BE"; 
            return name;
        } 
 
        public override void WriteStartElement(string prefix, string localName, string ns)
        { 
            WriteBase64InlineIfPresent();
            ThrowIfElementIsXOPInclude(prefix, localName, ns);
            Writer.WriteStartElement(prefix, localName, ns);
            depth++; 
        }
 
        public override void WriteStartElement(string prefix, XmlDictionaryString localName, XmlDictionaryString ns) 
        {
            if (localName == null) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("localName");

            WriteBase64InlineIfPresent();
            ThrowIfElementIsXOPInclude(prefix, localName.Value, ns == null ? null : ns.Value); 
            Writer.WriteStartElement(prefix, localName, ns);
            depth++; 
        } 

        void ThrowIfElementIsXOPInclude(string prefix, string localName, string ns) 
        {
            if (ns == null)
            {
                XmlBaseWriter w = this.Writer as XmlBaseWriter; 
                if (w != null)
                    ns = w.LookupNamespace(prefix); 
            } 

            if (localName == MtomGlobals.XopIncludeLocalName && ns == MtomGlobals.XopIncludeNamespace) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomDataMustNotContainXopInclude, MtomGlobals.XopIncludeLocalName, MtomGlobals.XopIncludeNamespace)));
        }

        public override void WriteEndElement() 
        {
            WriteXOPInclude(); 
            Writer.WriteEndElement(); 
            depth--;
            WriteXOPBinaryParts(); 
        }

        public override void WriteFullEndElement()
        { 
            WriteXOPInclude();
            Writer.WriteFullEndElement(); 
            depth--; 
            WriteXOPBinaryParts();
        } 

        public override void WriteValue(IStreamProvider value)
        {
            if (value == null) 
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
 
            if (Writer.WriteState == WriteState.Element) 
            {
                if (binaryDataChunks == null) 
                {
                    binaryDataChunks = new List();
                    contentID = GenerateUriForMimePart((mimeParts == null) ? 1 : mimeParts.Count + 1);
                } 
                binaryDataChunks.Add(new MtomBinaryData(value));
            } 
            else 
                Writer.WriteValue(value);
        } 

        public override void WriteBase64(byte[] buffer, int index, int count)
        {
            if (Writer.WriteState == WriteState.Element) 
            {
                if (buffer == null) 
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("buffer")); 

                // Not checking upper bound because it will be caught by "count".  This is what XmlTextWriter does. 
                if (index < 0)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("index", SR.GetString(SR.ValueMustBeNonNegative)));

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

                if (binaryDataChunks == null) 
                {
                    binaryDataChunks = new List();
                    contentID = GenerateUriForMimePart((mimeParts == null) ? 1 : mimeParts.Count + 1);
                } 

                int totalSize = ValidateSizeOfMessage(maxSizeInBytes, 0, totalSizeOfMimeParts); 
                totalSize += ValidateSizeOfMessage(maxSizeInBytes, totalSize, sizeOfBufferedBinaryData); 
                totalSize += ValidateSizeOfMessage(maxSizeInBytes, totalSize, count);
                sizeOfBufferedBinaryData += count; 
                binaryDataChunks.Add(new MtomBinaryData(buffer, index, count));
            }
            else
                Writer.WriteBase64(buffer, index, count); 
        }
 
        internal static int ValidateSizeOfMessage(int maxSize, int offset, int size) 
        {
            if (size > (maxSize - offset)) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomExceededMaxSizeInBytes, maxSize)));
            return size;
        }
 
        void WriteBase64InlineIfPresent()
        { 
            if (binaryDataChunks != null) 
            {
                WriteBase64Inline(); 
            }
        }

        void WriteBase64Inline() 
        {
            foreach (MtomBinaryData data in binaryDataChunks) 
            { 
                if (data.type == MtomBinaryDataType.Provider)
                { 
                    Writer.WriteValue(data.provider);
                }
                else
                { 
                    Writer.WriteBase64(data.chunk, 0, data.chunk.Length);
                } 
            } 
            this.sizeOfBufferedBinaryData = 0;
            binaryDataChunks = null; 
            contentType = null;
            contentID = null;
        }
 
        void WriteXOPInclude()
        { 
            if (binaryDataChunks == null) 
                return;
 
            bool inline = true;
            long size = 0;
            foreach (MtomBinaryData data in binaryDataChunks)
            { 
                long len = data.Length;
                if (len < 0 || len > (MaxInlinedBytes - size)) 
                { 
                    inline = false;
                    break; 
                }
                size += len;
            }
 
            if (inline)
                WriteBase64Inline(); 
            else 
            {
                if (mimeParts == null) 
                    mimeParts = new List();

                MimePart mimePart = new MimePart(binaryDataChunks, contentID, contentType, MimeGlobals.EncodingBinary, sizeOfBufferedBinaryData, maxSizeInBytes);
                mimeParts.Add(mimePart); 

                totalSizeOfMimeParts += ValidateSizeOfMessage(maxSizeInBytes, totalSizeOfMimeParts, mimePart.sizeInBytes); 
                totalSizeOfMimeParts += ValidateSizeOfMessage(maxSizeInBytes, totalSizeOfMimeParts, mimeWriter.GetBoundarySize()); 

                Writer.WriteStartElement(MtomGlobals.XopIncludePrefix, MtomGlobals.XopIncludeLocalName, MtomGlobals.XopIncludeNamespace); 
                Writer.WriteStartAttribute(MtomGlobals.XopIncludeHrefLocalName, MtomGlobals.XopIncludeHrefNamespace);
                Writer.WriteValue(String.Format(CultureInfo.InvariantCulture, "{0}{1}", MimeGlobals.ContentIDScheme, Uri.EscapeDataString(contentID)));
                Writer.WriteEndAttribute();
                Writer.WriteEndElement(); 
                binaryDataChunks = null;
                sizeOfBufferedBinaryData = 0; 
                contentType = null; 
                contentID = null;
            } 
        }

        public static string GenerateUriForMimePart(int index)
        { 
            return String.Format(CultureInfo.InvariantCulture, "http://tempuri.org/{0}/{1}", index, DateTime.Now.Ticks);
        } 
 
        void WriteXOPBinaryParts()
        { 
            if (depth > 0 || mimeWriter.WriteState == MimeWriterState.Closed)
                return;

            if (Writer.WriteState != WriteState.Closed) 
                Writer.Flush();
 
            if (mimeParts != null) 
            {
                foreach (MimePart part in mimeParts) 
                {
                    WriteMimeHeaders(part.contentID, part.contentType, part.contentTransferEncoding);
                    Stream s = mimeWriter.GetContentStream();
                    int blockSize = 256; 
                    int bytesRead = 0;
                    byte[] block = new byte[blockSize]; 
                    Stream stream = null; 
                    foreach (MtomBinaryData data in part.binaryData)
                    { 
                        if (data.type == MtomBinaryDataType.Provider)
                        {
                            stream = data.provider.GetStream();
                            if (stream == null) 
                                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlInvalidStream)));
                            while (true) 
                            { 
                                bytesRead = stream.Read(block, 0, blockSize);
                                if (bytesRead > 0) 
                                    s.Write(block, 0, bytesRead);
                                else
                                    break;
                                if (blockSize < 65536 && bytesRead == blockSize) 
                                {
                                    blockSize = blockSize * 16; 
                                    block = new byte[blockSize]; 
                                }
                            } 

                            data.provider.ReleaseStream(stream);
                        }
                        else 
                        {
                            s.Write(data.chunk, 0, data.chunk.Length); 
                        } 
                    }
                } 
                mimeParts.Clear();
            }
            mimeWriter.Close();
        } 

        void WriteMimeHeaders(string contentID, string contentType, string contentTransferEncoding) 
        { 
            mimeWriter.StartPart();
            if (contentID != null) 
                mimeWriter.WriteHeader(MimeGlobals.ContentIDHeader, String.Format(CultureInfo.InvariantCulture, "<{0}>", contentID));
            if (contentTransferEncoding != null)
                mimeWriter.WriteHeader(MimeGlobals.ContentTransferEncodingHeader, contentTransferEncoding);
            if (contentType != null) 
                mimeWriter.WriteHeader(MimeGlobals.ContentTypeHeader, contentType);
        } 
#if NO 
        public override bool CanSubsetElements
        { 
            get { return Writer.CanSubsetElements; }
        }
#endif
        public override void Close() 
        {
            if (!this.isClosed) 
            { 
                this.isClosed = true;
                if (IsInitialized) 
                {
                    WriteXOPInclude();
                    if (Writer.WriteState == WriteState.Element ||
                        Writer.WriteState == WriteState.Attribute || 
                        Writer.WriteState == WriteState.Content)
                    { 
                        Writer.WriteEndDocument(); 
                    }
                    Writer.Flush(); 
                    depth = 0;
                    WriteXOPBinaryParts();
                    Writer.Close();
                } 
            }
        } 
 
        void CheckIfStartContentTypeAttribute(string localName, string ns)
        { 
            if (localName != null && localName == MtomGlobals.MimeContentTypeLocalName
                && ns != null && (ns == MtomGlobals.MimeContentTypeNamespace200406 || ns == MtomGlobals.MimeContentTypeNamespace200505))
            {
                contentTypeStream = new MemoryStream(); 
                this.infosetWriter = Writer;
                this.writer = XmlDictionaryWriter.CreateBinaryWriter(contentTypeStream); 
                Writer.WriteStartElement("Wrapper"); 
                Writer.WriteStartAttribute(localName, ns);
            } 
        }

        void CheckIfEndContentTypeAttribute()
        { 
            if (contentTypeStream != null)
            { 
                Writer.WriteEndAttribute(); 
                Writer.WriteEndElement();
                Writer.Flush(); 
                contentTypeStream.Position = 0;
                XmlReader contentTypeReader = XmlDictionaryReader.CreateBinaryReader(contentTypeStream, null, XmlDictionaryReaderQuotas.Max, null, null);
                while (contentTypeReader.Read())
                { 
                    if (contentTypeReader.IsStartElement("Wrapper"))
                    { 
                        contentType = contentTypeReader.GetAttribute(MtomGlobals.MimeContentTypeLocalName, MtomGlobals.MimeContentTypeNamespace200406); 
 			if (contentType == null)
                        { 
                            contentType = contentTypeReader.GetAttribute(MtomGlobals.MimeContentTypeLocalName, MtomGlobals.MimeContentTypeNamespace200505);
                        }
                        break;
                    } 
                }
                this.writer = infosetWriter; 
                this.infosetWriter = null; 
                contentTypeStream = null;
                if (contentType != null) 
                    Writer.WriteString(contentType);
            }
        }
 
#if NO
        public override bool ElementSubsetting 
        { 
            get
            { 
                return Writer.ElementSubsetting;
            }
            set
            { 
                Writer.ElementSubsetting = value;
            } 
        } 
#endif
        public override void Flush() 
        {
            if (IsInitialized)
                Writer.Flush();
        } 

        public override string LookupPrefix(string ns) 
        { 
            return Writer.LookupPrefix(ns);
        } 

        public override XmlWriterSettings Settings
        {
            get 
            {
                return Writer.Settings; 
            } 
        }
 
        public override void WriteAttributes(XmlReader reader, bool defattr)
        {
            Writer.WriteAttributes(reader, defattr);
        } 

        public override void WriteBinHex(byte[] buffer, int index, int count) 
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteBinHex(buffer, index, count); 
        }

        public override void WriteCData(string text)
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteCData(text); 
        } 

        public override void WriteCharEntity(char ch) 
        {
            WriteBase64InlineIfPresent();
            Writer.WriteCharEntity(ch);
        } 

        public override void WriteChars(char[] buffer, int index, int count) 
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteChars(buffer, index, count); 
        }

        public override void WriteComment(string text)
        { 
            // Don't write comments after the document element
            if (depth == 0 && mimeWriter.WriteState == MimeWriterState.Closed) 
                return; 

            WriteBase64InlineIfPresent(); 
            Writer.WriteComment(text);
        }

        public override void WriteDocType(string name, string pubid, string sysid, string subset) 
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteDocType(name, pubid, sysid, subset); 
        }
#if NO 
        public override void WriteElementSubset(ArraySegment buffer)
        {
            Writer.WriteElementSubset(buffer);
        } 
#endif
        public override void WriteEndAttribute() 
        { 
            CheckIfEndContentTypeAttribute();
            Writer.WriteEndAttribute(); 
        }

        public override void WriteEndDocument()
        { 
            WriteXOPInclude();
            Writer.WriteEndDocument(); 
            depth = 0; 
            WriteXOPBinaryParts();
        } 

        public override void WriteEntityRef(string name)
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteEntityRef(name);
        } 
 
        public override void WriteName(string name)
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteName(name);
        }
 
        public override void WriteNmToken(string name)
        { 
            WriteBase64InlineIfPresent(); 
            Writer.WriteNmToken(name);
        } 

        protected override void WriteTextNode(XmlDictionaryReader reader, bool attribute)
        {
            Type type = reader.ValueType; 
            if (type == typeof(string))
            { 
                if (reader.CanReadValueChunk) 
                {
                    if (chars == null) 
                    {
                        chars = new char[256];
                    }
                    int count; 
                    while ((count = reader.ReadValueChunk(chars, 0, chars.Length)) > 0)
                    { 
                        this.WriteChars(chars, 0, count); 
                    }
                } 
                else
                {
                    WriteString(reader.Value);
                } 
                if (!attribute)
                { 
                    reader.Read(); 
                }
            } 
            else if (type == typeof(byte[]))
            {
                if (reader.CanReadBinaryContent)
                { 
                    // Its best to read in buffers that are a multiple of 3 so we don't break base64 boundaries when converting text
                    if (bytes == null) 
                    { 
                        bytes = new byte[384];
                    } 
                    int count;
                    while ((count = reader.ReadValueAsBase64(bytes, 0, bytes.Length)) > 0)
                    {
                        this.WriteBase64(bytes, 0, count); 
                    }
                } 
                else 
                {
                    WriteString(reader.Value); 
                }
                if (!attribute)
                {
                    reader.Read(); 
                }
            } 
            else 
            {
                base.WriteTextNode(reader, attribute); 
            }
        }

        public override void WriteNode(XPathNavigator navigator, bool defattr) 
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteNode(navigator, defattr); 
        }
 
        public override void WriteProcessingInstruction(string name, string text)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteProcessingInstruction(name, text); 
        }
 
        public override void WriteQualifiedName(string localName, string namespaceUri) 
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteQualifiedName(localName, namespaceUri);
        }

        public override void WriteRaw(char[] buffer, int index, int count) 
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteRaw(buffer, index, count); 
        }
 
        public override void WriteRaw(string data)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteRaw(data); 
        }
 
        public override void WriteStartAttribute(string prefix, string localName, string ns) 
        {
            Writer.WriteStartAttribute(prefix, localName, ns); 
            CheckIfStartContentTypeAttribute(localName, ns);
        }

        public override void WriteStartAttribute(string prefix, XmlDictionaryString localName, XmlDictionaryString ns) 
        {
            Writer.WriteStartAttribute(prefix, localName, ns); 
            if (localName != null && ns != null) 
                CheckIfStartContentTypeAttribute(localName.Value, ns.Value);
        } 

        public override void WriteStartDocument()
        {
            Writer.WriteStartDocument(); 
        }
 
        public override void WriteStartDocument(bool standalone) 
        {
            Writer.WriteStartDocument(standalone); 
        }

        public override WriteState WriteState
        { 
            get
            { 
                return Writer.WriteState; 
            }
        } 

        public override void WriteString(string text)
        {
            // Don't write whitespace after the document element 
            if (depth == 0 && mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(text))
                return; 
 
            WriteBase64InlineIfPresent();
            Writer.WriteString(text); 
        }

        public override void WriteString(XmlDictionaryString value)
        { 
            // Don't write whitespace after the document element
            if (depth == 0 && mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(value.Value)) 
                return; 

            WriteBase64InlineIfPresent(); 
            Writer.WriteString(value);
        }

        public override void WriteSurrogateCharEntity(char lowChar, char highChar) 
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteSurrogateCharEntity(lowChar, highChar); 
        }
 
        public override void WriteWhitespace(string whitespace)
        {
            // Don't write whitespace after the document element
            if (depth == 0 && mimeWriter.WriteState == MimeWriterState.Closed) 
                return;
 
            WriteBase64InlineIfPresent(); 
            Writer.WriteWhitespace(whitespace);
        } 

        public override void WriteValue(object value)
        {
            IStreamProvider sp = value as IStreamProvider; 
            if (sp != null)
            { 
                WriteValue(sp); 
            }
            else 
            {
                WriteBase64InlineIfPresent();
                Writer.WriteValue(value);
            } 
        }
 
        public override void WriteValue(string value) 
        {
            // Don't write whitespace after the document element 
            if (depth == 0 && mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(value))
                return;

            WriteBase64InlineIfPresent(); 
            Writer.WriteValue(value);
        } 
 
        public override void WriteValue(bool value)
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
 
        public override void WriteValue(DateTime value)
        { 
            WriteBase64InlineIfPresent(); 
            Writer.WriteValue(value);
        } 

        public override void WriteValue(double value)
        {
            WriteBase64InlineIfPresent(); 
            Writer.WriteValue(value);
        } 
 
        public override void WriteValue(int value)
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
 
        public override void WriteValue(long value)
        { 
            WriteBase64InlineIfPresent(); 
            Writer.WriteValue(value);
        } 

#if DECIMAL_FLOAT_API
        public override void WriteValue(decimal value)
        { 
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value); 
        } 

        public override void WriteValue(float value) 
        {
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        } 
#endif
        public override void WriteValue(XmlDictionaryString value) 
        { 
            // Don't write whitespace after the document element
            if (depth == 0 && mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(value.Value)) 
                return;

            WriteBase64InlineIfPresent();
            Writer.WriteValue(value); 
        }
 
        public override void WriteXmlnsAttribute(string prefix, string ns) 
        {
            Writer.WriteXmlnsAttribute(prefix, ns); 
        }

        public override void WriteXmlnsAttribute(string prefix, XmlDictionaryString ns)
        { 
            Writer.WriteXmlnsAttribute(prefix, ns);
        } 
 
        public override string XmlLang
        { 
            get
            {
                return Writer.XmlLang;
            } 
        }
 
        public override XmlSpace XmlSpace 
        {
            get 
            {
                return Writer.XmlSpace;
            }
        } 

        static class MimeBoundaryGenerator 
        { 
            static long id;
            static string prefix; 

            static MimeBoundaryGenerator()
            {
                prefix = string.Concat(Guid.NewGuid().ToString(), "+id="); 
            }
 
            internal static string Next() 
            {
                long nextId = Interlocked.Increment(ref id); 
                return String.Format(CultureInfo.InvariantCulture, "{0}{1}", prefix, nextId);
            }
        }
 
        class MimePart
        { 
            internal IList binaryData; 
            internal string contentID;
            internal string contentType; 
            internal string contentTransferEncoding;
            internal int sizeInBytes;

            internal MimePart(IList binaryData, string contentID, string contentType, string contentTransferEncoding, int sizeOfBufferedBinaryData, int maxSizeInBytes) 
            {
                this.binaryData = binaryData; 
                this.contentID = contentID; 
                this.contentType = contentType ?? MtomGlobals.DefaultContentTypeForBinary;
                this.contentTransferEncoding = contentTransferEncoding; 
                this.sizeInBytes = GetSize(contentID, contentType, contentTransferEncoding, sizeOfBufferedBinaryData, maxSizeInBytes);
            }

            static int GetSize(string contentID, string contentType, string contentTransferEncoding, int sizeOfBufferedBinaryData, int maxSizeInBytes) 
            {
                int size = XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, 0, MimeGlobals.CRLF.Length * 3); 
                if (contentTransferEncoding != null) 
                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, MimeWriter.GetHeaderSize(MimeGlobals.ContentTransferEncodingHeader, contentTransferEncoding, maxSizeInBytes));
                if (contentType != null) 
                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, MimeWriter.GetHeaderSize(MimeGlobals.ContentTypeHeader, contentType, maxSizeInBytes));
                if (contentID != null)
                {
                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, MimeWriter.GetHeaderSize(MimeGlobals.ContentIDHeader, contentID, maxSizeInBytes)); 
                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, 2); // include '<' and '>'
                } 
                size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, sizeOfBufferedBinaryData); 
                return size;
            } 
        }
    }

 
    internal static class MtomGlobals
    { 
        internal static string XopIncludeLocalName = "Include"; 
        internal static string XopIncludeNamespace = "http://www.w3.org/2004/08/xop/include";
        internal static string XopIncludePrefix = "xop"; 
        internal static string XopIncludeHrefLocalName = "href";
        internal static string XopIncludeHrefNamespace = String.Empty;
        internal static string MediaType = "multipart";
        internal static string MediaSubtype = "related"; 
        internal static string BoundaryParam = "boundary";
        internal static string TypeParam = "type"; 
        internal static string XopMediaType = "application"; 
        internal static string XopMediaSubtype = "xop+xml";
        internal static string XopType = "application/xop+xml"; 
        internal static string StartParam = "start";
        internal static string StartInfoParam = "start-info";
        internal static string ActionParam = "action";
        internal static string CharsetParam = "charset"; 
        internal static string MimeContentTypeLocalName = "contentType";
        internal static string MimeContentTypeNamespace200406 = "http://www.w3.org/2004/06/xmlmime"; 
        internal static string MimeContentTypeNamespace200505 = "http://www.w3.org/2005/05/xmlmime"; 
        internal static string DefaultContentTypeForBinary = "application/octet-stream";
    } 

    internal static class MimeGlobals
    {
        internal static string MimeVersionHeader = "MIME-Version"; 
        internal static string DefaultVersion = "1.0";
        internal static string ContentIDScheme = "cid:"; 
        internal static string ContentIDHeader = "Content-ID"; 
        internal static string ContentTypeHeader = "Content-Type";
        internal static string ContentTransferEncodingHeader = "Content-Transfer-Encoding"; 
        internal static string EncodingBinary = "binary";
        internal static string Encoding8bit = "8bit";
        internal static byte[] COLONSPACE = new byte[] { (byte)':', (byte)' ' };
        internal static byte[] DASHDASH = new byte[] { (byte)'-', (byte)'-' }; 
        internal static byte[] CRLF = new byte[] { (byte)'\r', (byte)'\n' };
        // Per RFC2045, preceding CRLF sequence is part of the boundary. MIME boundary tags begin with -- 
        internal static byte[] BoundaryPrefix = new byte[] { (byte)'\r', (byte)'\n', (byte)'-', (byte)'-' }; 
    }
 
    enum MimeWriterState
    {
        Start,
        StartPreface, 
        StartPart,
        Header, 
        Content, 
        Closed,
    } 

    internal class MimeWriter
    {
        Stream stream; 
        byte[] boundaryBytes;
        MimeWriterState state; 
        BufferedWrite bufferedWrite; 
        Stream contentStream;
 
        internal MimeWriter(Stream stream, string boundary)
        {
            if (stream == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream"); 
            if (boundary == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("boundary"); 
 
            this.stream = stream;
            this.boundaryBytes = MimeWriter.GetBoundaryBytes(boundary); 
            this.state = MimeWriterState.Start;
            this.bufferedWrite = new BufferedWrite();
        }
 
        internal static int GetHeaderSize(string name, string value, int maxSizeInBytes)
        { 
            if (name == null) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name");
            if (value == null) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");

            int size = XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, 0, MimeGlobals.COLONSPACE.Length + MimeGlobals.CRLF.Length);
            size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, name.Length); 
            size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, value.Length);
            return size; 
        } 

        internal static byte[] GetBoundaryBytes(string boundary) 
        {
            byte[] boundaryBytes = new byte[boundary.Length + MimeGlobals.BoundaryPrefix.Length];
            for (int i = 0; i < MimeGlobals.BoundaryPrefix.Length; i++)
                boundaryBytes[i] = MimeGlobals.BoundaryPrefix[i]; 
            Encoding.ASCII.GetBytes(boundary, 0, boundary.Length, boundaryBytes, MimeGlobals.BoundaryPrefix.Length);
            return boundaryBytes; 
        } 

        internal MimeWriterState WriteState 
        {
            get
            {
                return state; 
            }
        } 
 
        internal int GetBoundarySize()
        { 
            return this.boundaryBytes.Length;
        }

        internal void StartPreface() 
        {
            if (state != MimeWriterState.Start) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeWriterInvalidStateForStartPreface, state.ToString()))); 

            state = MimeWriterState.StartPreface; 
        }

        internal void StartPart()
        { 
            switch (state)
            { 
                case MimeWriterState.StartPart: 
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeWriterInvalidStateForStartPart, state.ToString()))); 
                default:
                    break;
            }
 
            state = MimeWriterState.StartPart;
 
            if (contentStream != null) 
            {
                contentStream.Flush(); 
                contentStream = null;
            }

            bufferedWrite.Write(boundaryBytes); 
            bufferedWrite.Write(MimeGlobals.CRLF);
        } 
 
        internal void Close()
        { 
            switch (state)
            {
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeWriterInvalidStateForClose, state.ToString()))); 
                default:
                    break; 
            } 

            state = MimeWriterState.Closed; 

            if (contentStream != null)
            {
                contentStream.Flush(); 
                contentStream = null;
            } 
 
            bufferedWrite.Write(boundaryBytes);
            bufferedWrite.Write(MimeGlobals.DASHDASH); 
            bufferedWrite.Write(MimeGlobals.CRLF);

            Flush();
        } 

        void Flush() 
        { 
            if (bufferedWrite.Length > 0)
            { 
                stream.Write(bufferedWrite.GetBuffer(), 0, bufferedWrite.Length);
                bufferedWrite.Reset();
            }
        } 

        internal void WriteHeader(string name, string value) 
        { 
            if (name == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); 
            if (value == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");

            switch (state) 
            {
                case MimeWriterState.Start: 
                case MimeWriterState.Content: 
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeWriterInvalidStateForHeader, state.ToString()))); 
                default:
                    break;
            }
 
            state = MimeWriterState.Header;
 
            bufferedWrite.Write(name); 
            bufferedWrite.Write(MimeGlobals.COLONSPACE);
            bufferedWrite.Write(value); 
            bufferedWrite.Write(MimeGlobals.CRLF);
        }

        internal Stream GetContentStream() 
        {
            switch (state) 
            { 
                case MimeWriterState.Start:
                case MimeWriterState.Content: 
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeWriterInvalidStateForContent, state.ToString())));
                default:
                    break; 
            }
 
            state = MimeWriterState.Content; 

            bufferedWrite.Write(MimeGlobals.CRLF); 

            Flush();

            contentStream = stream; 

            return contentStream; 
        } 
    }
 
    internal class BufferedWrite
    {
        byte[] buffer;
        int offset; 

        internal BufferedWrite() : this(256) 
        { 
        }
 
        internal BufferedWrite(int initialSize)
        {
            buffer = new byte[initialSize];
        } 

        void EnsureBuffer(int count) 
        { 
            int currSize = buffer.Length;
            if (count > currSize - offset) 
            {
                int newSize = currSize;
                do
                { 
                    if (newSize == Int32.MaxValue)
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.WriteBufferOverflow))); 
                    newSize = (newSize < Int32.MaxValue / 2) ? newSize * 2 : Int32.MaxValue; 
                }
                while (count > newSize - offset); 
                byte[] newBuffer = new byte[newSize];
                Buffer.BlockCopy(buffer, 0, newBuffer, 0, offset);
                buffer = newBuffer;
            } 
        }
 
        internal int Length 
        {
            get 
            {
                return offset;
            }
        } 

        internal byte[] GetBuffer() 
        { 
            return buffer;
        } 

        internal void Reset()
        {
            offset = 0; 
        }
 
        internal void Write(byte[] value) 
        {
            Write(value, 0, value.Length); 
        }

        internal void Write(byte[] value, int index, int count)
        { 
            EnsureBuffer(count);
            Buffer.BlockCopy(value, index, buffer, offset, count); 
            offset += count; 
        }
 
        internal void Write(string value)
        {
            Write(value, 0, value.Length);
        } 

        internal void Write(string value, int index, int count) 
        { 
            EnsureBuffer(count);
            for (int i = 0; i < count; i++) 
            {
                char c = value[index + i];
                if ((ushort)c > 0xFF)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, c, ((int)c).ToString("X", CultureInfo.InvariantCulture)))); 
                buffer[offset + i] = (byte)c;
            } 
            offset += count; 
        }
 
#if NO
        internal void Write(byte value)
        {
            EnsureBuffer(1); 
            buffer[offset++] = value;
        } 
 
        internal void Write(char value)
        { 
            EnsureBuffer(1);
            if ((ushort)value > 0xFF)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, value, ((int)value).ToString("X", CultureInfo.InvariantCulture)))));
            buffer[offset++] = (byte)value; 
        }
 
        internal void Write(int value) 
        {
            Write(value.ToString()); 
        }

        internal void Write(char[] value)
        { 
            Write(value, 0, value.Length);
        } 
 
        internal void Write(char[] value, int index, int count)
        { 
            EnsureBuffer(count);
            for (int i = 0; i < count; i++)
            {
                char c = value[index + i]; 
                if ((ushort)c > 0xFF)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, c, ((int)c).ToString("X", CultureInfo.InvariantCulture))))); 
                buffer[offset + i] = (byte)c; 
            }
            offset += count; 
        }

#endif
 
    }
 
    enum MtomBinaryDataType { Provider, Segment } 

    class MtomBinaryData 
    {
        internal MtomBinaryDataType type;

        internal IStreamProvider provider; 
        internal byte[] chunk;
 
        internal MtomBinaryData(IStreamProvider provider) 
        {
            this.type = MtomBinaryDataType.Provider; 
            this.provider = provider;
        }

        internal MtomBinaryData(byte[] buffer, int offset, int count) 
        {
            this.type = MtomBinaryDataType.Segment; 
            this.chunk = new byte[count]; 
            Buffer.BlockCopy(buffer, offset, this.chunk, 0, count);
        } 

        internal long Length
        {
            get 
            {
                if (this.type == MtomBinaryDataType.Segment) 
                    return this.chunk.Length; 

                return -1; 
            }
        }
    }
} 


// 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