< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Xml.MimeMessageReader
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Xml/XmlMtomReader.cs
Line coverage
88%
Covered lines: 15
Uncovered lines: 2
Coverable lines: 17
Total lines: 2629
Line coverage: 88.2%
Branch coverage
66%
Covered branches: 4
Total branches: 6
Branch coverage: 66.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
.ctor(...)50%2283.33%
GetContentStream()50%2283.33%
ReadHeaders(...)100%22100%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Xml/XmlMtomReader.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Globalization;
 7using System.IO;
 8using System.Security;
 9using System.Security.Permissions;
 10using System.Text;
 11using System.Xml;
 12
 13using CoreWCF.Runtime;
 14using CoreWCF.Text;
 15
 16namespace CoreWCF.Xml
 17{
 18    internal interface IXmlMtomReaderInitializer
 19    {
 20        void SetInput(byte[] buffer, int offset, int count, Encoding[] encodings, string contentType, XmlDictionaryReade
 21        void SetInput(Stream stream, Encoding[] encodings, string contentType, XmlDictionaryReaderQuotas quotas, int max
 22    }
 23
 24    internal class XmlMtomReader : XmlDictionaryReader, IXmlLineInfo, IXmlMtomReaderInitializer
 25    {
 26        public static XmlDictionaryReader Create(Stream stream, Encoding encoding, XmlDictionaryReaderQuotas quotas)
 27        {
 28            if (encoding == null)
 29                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(encoding));
 30
 31            return Create(stream, new Encoding[1] { encoding }, quotas);
 32        }
 33
 34        public static XmlDictionaryReader Create(Stream stream, Encoding[] encodings, XmlDictionaryReaderQuotas quotas) 
 35
 36        public static XmlDictionaryReader Create(Stream stream, Encoding[] encodings, string contentType, XmlDictionaryR
 37
 38        public static XmlDictionaryReader Create(Stream stream, Encoding[] encodings, string contentType,
 39            XmlDictionaryReaderQuotas quotas, int maxBufferSize, OnXmlDictionaryReaderClose onClose)
 40        {
 41            XmlMtomReader reader = new XmlMtomReader();
 42            reader.SetInput(stream, encodings, contentType, quotas, maxBufferSize, onClose);
 43            return reader;
 44        }
 45
 46        public static XmlDictionaryReader Create(byte[] buffer, int offset, int count, Encoding encoding, XmlDictionaryR
 47        {
 48            if (encoding == null)
 49                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(encoding));
 50
 51            return Create(buffer, offset, count, new Encoding[1] { encoding }, quotas);
 52        }
 53
 54        public static XmlDictionaryReader Create(byte[] buffer, int offset, int count, Encoding[] encodings, XmlDictiona
 55
 56        public static XmlDictionaryReader Create(byte[] buffer, int offset, int count, Encoding[] encodings, string cont
 57
 58        public static XmlDictionaryReader Create(byte[] buffer, int offset, int count, Encoding[] encodings, string cont
 59            XmlDictionaryReaderQuotas quotas, int maxBufferSize, OnXmlDictionaryReaderClose onClose)
 60        {
 61            XmlMtomReader reader = new XmlMtomReader();
 62            reader.SetInput(buffer, offset, count, encodings, contentType, quotas, maxBufferSize, onClose);
 63            return reader;
 64        }
 65
 66        private Encoding[] _encodings;
 67        private XmlDictionaryReader _xmlReader;
 68        private XmlDictionaryReader _infosetReader;
 69        private MimeReader _mimeReader;
 70        private Dictionary<string, MimePart> _mimeParts;
 71        private OnXmlDictionaryReaderClose _onClose;
 72        private bool _readingBinaryElement;
 73        private int _maxBufferSize;
 74        private int _bufferRemaining;
 75        private MimePart _part;
 76
 77        public XmlMtomReader()
 78        {
 79        }
 80
 81        internal static void DecrementBufferQuota(int maxBuffer, ref int remaining, int size)
 82        {
 83            if (remaining - size <= 0)
 84            {
 85                remaining = 0;
 86                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomBufferQuotaE
 87            }
 88            else
 89            {
 90                remaining -= size;
 91            }
 92        }
 93
 94        private void SetReadEncodings(Encoding[] encodings)
 95        {
 96            if (encodings == null)
 97                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(encodings));
 98
 99            for (int i = 0; i < encodings.Length; i++)
 100            {
 101                if (encodings[i] == null)
 102                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(string.Format(CultureInfo.Invariant
 103            }
 104
 105            _encodings = new Encoding[encodings.Length];
 106            encodings.CopyTo(_encodings, 0);
 107        }
 108
 109        private void CheckContentType(string contentType)
 110        {
 111            if (contentType != null && contentType.Length == 0)
 112                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.MtomContentTypeInvali
 113        }
 114
 115        public void SetInput(byte[] buffer, int offset, int count, Encoding[] encodings, string contentType, XmlDictiona
 116
 117        public void SetInput(Stream stream, Encoding[] encodings, string contentType, XmlDictionaryReaderQuotas quotas, 
 118        {
 119            SetReadEncodings(encodings);
 120            CheckContentType(contentType);
 121            Initialize(stream, contentType, quotas, maxBufferSize);
 122            _onClose = onClose;
 123        }
 124
 125        private void Initialize(Stream stream, string contentType, XmlDictionaryReaderQuotas quotas, int maxBufferSize)
 126        {
 127            if (stream == null)
 128                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(stream));
 129
 130            _maxBufferSize = maxBufferSize;
 131            _bufferRemaining = maxBufferSize;
 132
 133            string boundary, start, startInfo;
 134
 135            if (contentType == null)
 136            {
 137                MimeMessageReader messageReader = new MimeMessageReader(stream);
 138                MimeHeaders messageHeaders = messageReader.ReadHeaders(_maxBufferSize, ref _bufferRemaining);
 139                ReadMessageMimeVersionHeader(messageHeaders.MimeVersion);
 140                ReadMessageContentTypeHeader(messageHeaders.ContentType, out boundary, out start, out startInfo);
 141                stream = messageReader.GetContentStream();
 142                if (stream == null)
 143                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.MtomMessageInvalidCont
 144            }
 145            else
 146            {
 147                ReadMessageContentTypeHeader(new ContentTypeHeader(contentType), out boundary, out start, out startInfo)
 148            }
 149
 150            _mimeReader = new MimeReader(stream, boundary);
 151            _mimeParts = null;
 152            _readingBinaryElement = false;
 153
 154            MimePart infosetPart = (start == null) ? ReadRootMimePart() : ReadMimePart(GetStartUri(start));
 155            byte[] infosetBytes = infosetPart.GetBuffer(_maxBufferSize, ref _bufferRemaining);
 156            int infosetByteCount = (int)infosetPart.Length;
 157
 158            Encoding encoding = ReadRootContentTypeHeader(infosetPart.Headers.ContentType, _encodings, startInfo);
 159            CheckContentTransferEncodingOnRoot(infosetPart.Headers.ContentTransferEncoding);
 160
 161            IXmlTextReaderInitializer initializer = _xmlReader as IXmlTextReaderInitializer;
 162
 163            if (initializer != null)
 164                initializer.SetInput(infosetBytes, 0, infosetByteCount, encoding, quotas, null);
 165            else
 166                _xmlReader = XmlDictionaryReader.CreateTextReader(infosetBytes, 0, infosetByteCount, encoding, quotas, n
 167        }
 168
 169        public override XmlDictionaryReaderQuotas Quotas => _xmlReader.Quotas;
 170
 171        private void ReadMessageMimeVersionHeader(MimeVersionHeader header)
 172        {
 173            if (header != null && header.Version != MimeVersionHeader.Default.Version)
 174                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomMessageInval
 175        }
 176
 177        private void ReadMessageContentTypeHeader(ContentTypeHeader header, out string boundary, out string start, out s
 178        {
 179            if (header == null)
 180                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.MtomMessageContentTypeNotF
 181
 182            if (string.Compare(MtomGlobals.MediaType, header.MediaType, StringComparison.OrdinalIgnoreCase) != 0
 183                || string.Compare(MtomGlobals.MediaSubtype, header.MediaSubtype, StringComparison.OrdinalIgnoreCase) != 
 184                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomMessageNotMu
 185
 186            string type;
 187            if (!header.Parameters.TryGetValue(MtomGlobals.TypeParam, out type) || MtomGlobals.XopType != type)
 188                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomMessageNotAp
 189
 190            if (!header.Parameters.TryGetValue(MtomGlobals.BoundaryParam, out boundary))
 191                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomMessageRequi
 192            if (!MailBnfHelper.IsValidMimeBoundary(boundary))
 193                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomBoundaryInva
 194
 195            if (!header.Parameters.TryGetValue(MtomGlobals.StartParam, out start))
 196                start = null;
 197
 198            if (!header.Parameters.TryGetValue(MtomGlobals.StartInfoParam, out startInfo))
 199                startInfo = null;
 200        }
 201
 202        private Encoding ReadRootContentTypeHeader(ContentTypeHeader header, Encoding[] expectedEncodings, string expect
 203        {
 204            if (header == null)
 205                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.MtomRootContentTypeNotFoun
 206
 207            if (string.Compare(MtomGlobals.XopMediaType, header.MediaType, StringComparison.OrdinalIgnoreCase) != 0
 208                || string.Compare(MtomGlobals.XopMediaSubtype, header.MediaSubtype, StringComparison.OrdinalIgnoreCase) 
 209                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomRootNotAppli
 210
 211            string charset;
 212            if (!header.Parameters.TryGetValue(MtomGlobals.CharsetParam, out charset)
 213                || charset == null || charset.Length == 0)
 214                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomRootRequired
 215            Encoding encoding = null;
 216            for (int i = 0; i < _encodings.Length; i++)
 217            {
 218                if (string.Compare(charset, expectedEncodings[i].WebName, StringComparison.OrdinalIgnoreCase) == 0)
 219                {
 220                    encoding = expectedEncodings[i];
 221                    break;
 222                }
 223            }
 224            if (encoding == null)
 225            {
 226                // Check for alternate names
 227                if (string.Compare(charset, "utf-16LE", StringComparison.OrdinalIgnoreCase) == 0)
 228                {
 229                    for (int i = 0; i < _encodings.Length; i++)
 230                    {
 231                        if (string.Compare(expectedEncodings[i].WebName, Encoding.Unicode.WebName, StringComparison.Ordi
 232                        {
 233                            encoding = expectedEncodings[i];
 234                            break;
 235                        }
 236                    }
 237                }
 238                else if (string.Compare(charset, "utf-16BE", StringComparison.OrdinalIgnoreCase) == 0)
 239                {
 240                    for (int i = 0; i < _encodings.Length; i++)
 241                    {
 242                        if (string.Compare(expectedEncodings[i].WebName, Encoding.BigEndianUnicode.WebName, StringCompar
 243                        {
 244                            encoding = expectedEncodings[i];
 245                            break;
 246                        }
 247                    }
 248                }
 249
 250                if (encoding == null)
 251                {
 252                    StringBuilder expectedCharSetStr = new StringBuilder();
 253                    for (int i = 0; i < _encodings.Length; i++)
 254                    {
 255                        if (expectedCharSetStr.Length != 0)
 256                            expectedCharSetStr.Append(" | ");
 257                        expectedCharSetStr.Append(_encodings[i].WebName);
 258                    }
 259                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomRootUnex
 260                }
 261            }
 262
 263            if (expectedType != null)
 264            {
 265                string rootType;
 266                if (!header.Parameters.TryGetValue(MtomGlobals.TypeParam, out rootType)
 267                    || rootType == null || rootType.Length == 0)
 268                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomRootRequ
 269                if (rootType != expectedType)
 270                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomRootUnex
 271            }
 272
 273            return encoding;
 274        }
 275
 276        // 7bit is default encoding in the absence of content-transfer-encoding header
 277        private void CheckContentTransferEncodingOnRoot(ContentTransferEncodingHeader header)
 278        {
 279            if (header != null && header.ContentTransferEncoding == ContentTransferEncoding.Other)
 280                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomContentTrans
 281                                                                                      header.Value,
 282                                                                                      ContentTransferEncodingHeader.Seve
 283                                                                                      ContentTransferEncodingHeader.Eigh
 284                                                                                      ContentTransferEncodingHeader.Bina
 285        }
 286
 287        private void CheckContentTransferEncodingOnBinaryPart(ContentTransferEncodingHeader header)
 288        {
 289            if (header == null)
 290                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomContentTrans
 291                    ContentTransferEncodingHeader.Binary.ContentTransferEncodingValue)));
 292            else if (header.ContentTransferEncoding != ContentTransferEncoding.Binary)
 293                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomInvalidTrans
 294                    header.Value, ContentTransferEncodingHeader.Binary.ContentTransferEncodingValue)));
 295        }
 296
 297        private string GetStartUri(string startUri)
 298        {
 299            if (startUri.StartsWith("<", StringComparison.Ordinal))
 300            {
 301                if (startUri.EndsWith(">", StringComparison.Ordinal))
 302                    return startUri;
 303                else
 304                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomInvalidS
 305            }
 306            else
 307                return string.Format(CultureInfo.InvariantCulture, "<{0}>", startUri);
 308        }
 309
 310        public override bool Read()
 311        {
 312            bool retVal = _xmlReader.Read();
 313
 314            if (_xmlReader.NodeType == XmlNodeType.Element)
 315            {
 316                XopIncludeReader binaryDataReader = null;
 317                if (_xmlReader.IsStartElement(MtomGlobals.XopIncludeLocalName, MtomGlobals.XopIncludeNamespace))
 318                {
 319                    string uri = null;
 320                    while (_xmlReader.MoveToNextAttribute())
 321                    {
 322                        if (_xmlReader.LocalName == MtomGlobals.XopIncludeHrefLocalName && _xmlReader.NamespaceURI == Mt
 323                            uri = _xmlReader.Value;
 324                        else if (_xmlReader.NamespaceURI == MtomGlobals.XopIncludeNamespace)
 325                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.Mtom
 326                    }
 327                    if (uri == null)
 328                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomXopI
 329
 330                    MimePart mimePart = ReadMimePart(uri);
 331
 332                    CheckContentTransferEncodingOnBinaryPart(mimePart.Headers.ContentTransferEncoding);
 333
 334                    _part = mimePart;
 335                    binaryDataReader = new XopIncludeReader(mimePart, _xmlReader);
 336                    binaryDataReader.Read();
 337
 338                    _xmlReader.MoveToElement();
 339                    if (_xmlReader.IsEmptyElement)
 340                    {
 341                        _xmlReader.Read();
 342                    }
 343                    else
 344                    {
 345                        int xopDepth = _xmlReader.Depth;
 346                        _xmlReader.ReadStartElement();
 347
 348                        while (_xmlReader.Depth > xopDepth)
 349                        {
 350                            if (_xmlReader.IsStartElement() && _xmlReader.NamespaceURI == MtomGlobals.XopIncludeNamespac
 351                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.
 352
 353                            _xmlReader.Skip();
 354                        }
 355
 356                        _xmlReader.ReadEndElement();
 357                    }
 358                }
 359
 360                if (binaryDataReader != null)
 361                {
 362                    _xmlReader.MoveToContent();
 363                    _infosetReader = _xmlReader;
 364                    _xmlReader = binaryDataReader;
 365                    binaryDataReader = null;
 366                }
 367            }
 368
 369            if (_xmlReader.ReadState == ReadState.EndOfFile && _infosetReader != null)
 370            {
 371                // Read past the containing EndElement if necessary
 372                if (!retVal)
 373                    retVal = _infosetReader.Read();
 374
 375                _part.Release(_maxBufferSize, ref _bufferRemaining);
 376                _xmlReader = _infosetReader;
 377                _infosetReader = null;
 378            }
 379
 380            return retVal;
 381        }
 382
 383        private MimePart ReadMimePart(string uri)
 384        {
 385            MimePart part = null;
 386
 387            if (uri == null || uri.Length == 0)
 388                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.MtomInvalidEmptyURI));
 389
 390            string contentID = null;
 391            if (uri.StartsWith(MimeGlobals.ContentIDScheme, StringComparison.Ordinal))
 392                contentID = string.Format(CultureInfo.InvariantCulture, "<{0}>", Uri.UnescapeDataString(uri.Substring(Mi
 393            else if (uri.StartsWith("<", StringComparison.Ordinal))
 394                contentID = uri;
 395
 396            if (contentID == null)
 397                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomInvalidCIDUr
 398
 399            if (_mimeParts != null && _mimeParts.TryGetValue(contentID, out part))
 400            {
 401                if (part.ReferencedFromInfoset)
 402                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomMimePart
 403            }
 404            else
 405            {
 406                // TODO: Fix this to be a configurable setting on MtomMessageEncodingBindingElement
 407                int maxMimeParts = 1000; // AppSettings.MaxMimeParts;
 408                while (part == null && _mimeReader.ReadNextPart())
 409                {
 410                    MimeHeaders headers = _mimeReader.ReadHeaders(_maxBufferSize, ref _bufferRemaining);
 411                    Stream contentStream = _mimeReader.GetContentStream();
 412                    if (contentStream == null)
 413                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.MtomMessageInvalid
 414
 415                    ContentIDHeader contentIDHeader = (headers == null) ? null : headers.ContentID;
 416                    if (contentIDHeader == null || contentIDHeader.Value == null)
 417                    {
 418                        // Skip content if Content-ID header is not present
 419                        int size = 256;
 420                        byte[] bytes = new byte[size];
 421
 422                        int read = 0;
 423                        do
 424                        {
 425                            read = contentStream.Read(bytes, 0, size);
 426                        }
 427                        while (read > 0);
 428                        continue;
 429                    }
 430
 431                    string currentContentID = headers.ContentID.Value;
 432                    MimePart currentPart = new MimePart(contentStream, headers);
 433                    if (_mimeParts == null)
 434                        _mimeParts = new Dictionary<string, MimePart>();
 435
 436                    _mimeParts.Add(currentContentID, currentPart);
 437
 438                    if (_mimeParts.Count > maxMimeParts)
 439                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MaxMimeP
 440
 441                    if (currentContentID.Equals(contentID))
 442                        part = currentPart;
 443                    else
 444                        currentPart.GetBuffer(_maxBufferSize, ref _bufferRemaining);
 445                }
 446
 447                if (part == null)
 448                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomPartNotF
 449            }
 450
 451            part.ReferencedFromInfoset = true;
 452            return part;
 453        }
 454
 455        private MimePart ReadRootMimePart()
 456        {
 457            MimePart part = null;
 458
 459            if (!_mimeReader.ReadNextPart())
 460                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.MtomRootPartNotFound));
 461
 462            MimeHeaders headers = _mimeReader.ReadHeaders(_maxBufferSize, ref _bufferRemaining);
 463            Stream contentStream = _mimeReader.GetContentStream();
 464            if (contentStream == null)
 465                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.MtomMessageInvalidContentI
 466            part = new MimePart(contentStream, headers);
 467
 468            return part;
 469        }
 470
 471        private void AdvanceToContentOnElement()
 472        {
 473            if (NodeType != XmlNodeType.Attribute)
 474            {
 475                MoveToContent();
 476            }
 477        }
 478
 479        public override int AttributeCount => _xmlReader.AttributeCount;
 480
 481        public override string BaseURI => _xmlReader.BaseURI;
 482
 483        public override bool CanReadBinaryContent => _xmlReader.CanReadBinaryContent;
 484
 485        public override bool CanReadValueChunk => _xmlReader.CanReadValueChunk;
 486
 487        public override bool CanResolveEntity => _xmlReader.CanResolveEntity;
 488
 489        public override void Close()
 490        {
 491            _xmlReader.Close();
 492            _mimeReader.Close();
 493            OnXmlDictionaryReaderClose onClose = _onClose;
 494            _onClose = null;
 495            if (onClose != null)
 496            {
 497                try
 498                {
 499                    onClose(this);
 500                }
 501                catch (Exception e)
 502                {
 503                    if (Fx.IsFatal(e))
 504                        throw;
 505
 506                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
 507                }
 508            }
 509        }
 510
 511        public override int Depth => _xmlReader.Depth;
 512
 513        public override bool EOF => _xmlReader.EOF;
 514
 515        public override string GetAttribute(int index) => _xmlReader.GetAttribute(index);
 516
 517        public override string GetAttribute(string name) => _xmlReader.GetAttribute(name);
 518
 519        public override string GetAttribute(string name, string ns) => _xmlReader.GetAttribute(name, ns);
 520
 521        public override string GetAttribute(XmlDictionaryString localName, XmlDictionaryString ns) => _xmlReader.GetAttr
 522
 523        public override bool HasAttributes => _xmlReader.HasAttributes;
 524
 525        public override bool HasValue => _xmlReader.HasValue;
 526
 527        public override bool IsDefault => _xmlReader.IsDefault;
 528
 529        public override bool IsEmptyElement => _xmlReader.IsEmptyElement;
 530
 531        public override bool IsLocalName(string localName) => _xmlReader.IsLocalName(localName);
 532
 533        public override bool IsLocalName(XmlDictionaryString localName) => _xmlReader.IsLocalName(localName);
 534
 535        public override bool IsNamespaceUri(string ns) => _xmlReader.IsNamespaceUri(ns);
 536
 537        public override bool IsNamespaceUri(XmlDictionaryString ns) => _xmlReader.IsNamespaceUri(ns);
 538
 539        public override bool IsStartElement() => _xmlReader.IsStartElement();
 540
 541        public override bool IsStartElement(string localName) => _xmlReader.IsStartElement(localName);
 542
 543        public override bool IsStartElement(string localName, string ns) => _xmlReader.IsStartElement(localName, ns);
 544
 545        public override bool IsStartElement(XmlDictionaryString localName, XmlDictionaryString ns) => _xmlReader.IsStart
 546
 547        public override string LocalName => _xmlReader.LocalName;
 548
 549        public override string LookupNamespace(string ns) => _xmlReader.LookupNamespace(ns);
 550
 551        public override void MoveToAttribute(int index) => _xmlReader.MoveToAttribute(index);
 552
 553        public override bool MoveToAttribute(string name) => _xmlReader.MoveToAttribute(name);
 554
 555        public override bool MoveToAttribute(string name, string ns) => _xmlReader.MoveToAttribute(name, ns);
 556
 557        public override bool MoveToElement() => _xmlReader.MoveToElement();
 558
 559        public override bool MoveToFirstAttribute() => _xmlReader.MoveToFirstAttribute();
 560
 561        public override bool MoveToNextAttribute() => _xmlReader.MoveToNextAttribute();
 562
 563        public override string Name => _xmlReader.Name;
 564
 565        public override string NamespaceURI => _xmlReader.NamespaceURI;
 566
 567        public override XmlNameTable NameTable => _xmlReader.NameTable;
 568
 569        public override XmlNodeType NodeType => _xmlReader.NodeType;
 570
 571        public override string Prefix => _xmlReader.Prefix;
 572
 573        public override char QuoteChar => _xmlReader.QuoteChar;
 574
 575        public override bool ReadAttributeValue() => _xmlReader.ReadAttributeValue();
 576
 577        public override object ReadContentAs(Type returnType, IXmlNamespaceResolver namespaceResolver)
 578        {
 579            AdvanceToContentOnElement();
 580            return _xmlReader.ReadContentAs(returnType, namespaceResolver);
 581        }
 582
 583        public override byte[] ReadContentAsBase64()
 584        {
 585            AdvanceToContentOnElement();
 586            return _xmlReader.ReadContentAsBase64();
 587        }
 588
 589        public override int ReadValueAsBase64(byte[] buffer, int offset, int count)
 590        {
 591            AdvanceToContentOnElement();
 592            return _xmlReader.ReadValueAsBase64(buffer, offset, count);
 593        }
 594
 595        public override int ReadContentAsBase64(byte[] buffer, int offset, int count)
 596        {
 597            AdvanceToContentOnElement();
 598            return _xmlReader.ReadContentAsBase64(buffer, offset, count);
 599        }
 600
 601        public override int ReadElementContentAsBase64(byte[] buffer, int offset, int count)
 602        {
 603            if (!_readingBinaryElement)
 604            {
 605                if (IsEmptyElement)
 606                {
 607                    Read();
 608                    return 0;
 609                }
 610
 611                ReadStartElement();
 612                _readingBinaryElement = true;
 613            }
 614
 615            int i = ReadContentAsBase64(buffer, offset, count);
 616
 617            if (i == 0)
 618            {
 619                ReadEndElement();
 620                _readingBinaryElement = false;
 621            }
 622
 623            return i;
 624        }
 625
 626        public override int ReadElementContentAsBinHex(byte[] buffer, int offset, int count)
 627        {
 628            if (!_readingBinaryElement)
 629            {
 630                if (IsEmptyElement)
 631                {
 632                    Read();
 633                    return 0;
 634                }
 635
 636                ReadStartElement();
 637                _readingBinaryElement = true;
 638            }
 639
 640            int i = ReadContentAsBinHex(buffer, offset, count);
 641
 642            if (i == 0)
 643            {
 644                ReadEndElement();
 645                _readingBinaryElement = false;
 646            }
 647
 648            return i;
 649        }
 650
 651        public override int ReadContentAsBinHex(byte[] buffer, int offset, int count)
 652        {
 653            AdvanceToContentOnElement();
 654            return _xmlReader.ReadContentAsBinHex(buffer, offset, count);
 655        }
 656
 657        public override bool ReadContentAsBoolean()
 658        {
 659            AdvanceToContentOnElement();
 660            return _xmlReader.ReadContentAsBoolean();
 661        }
 662
 663        public override int ReadContentAsChars(char[] chars, int index, int count)
 664        {
 665            AdvanceToContentOnElement();
 666            return _xmlReader.ReadContentAsChars(chars, index, count);
 667        }
 668
 669        public override DateTime ReadContentAsDateTime()
 670        {
 671            AdvanceToContentOnElement();
 672            return _xmlReader.ReadContentAsDateTime();
 673        }
 674
 675        public override decimal ReadContentAsDecimal()
 676        {
 677            AdvanceToContentOnElement();
 678            return _xmlReader.ReadContentAsDecimal();
 679        }
 680
 681        public override double ReadContentAsDouble()
 682        {
 683            AdvanceToContentOnElement();
 684            return _xmlReader.ReadContentAsDouble();
 685        }
 686
 687        public override int ReadContentAsInt()
 688        {
 689            AdvanceToContentOnElement();
 690            return _xmlReader.ReadContentAsInt();
 691        }
 692
 693        public override long ReadContentAsLong()
 694        {
 695            AdvanceToContentOnElement();
 696            return _xmlReader.ReadContentAsLong();
 697        }
 698
 699        public override object ReadContentAsObject()
 700        {
 701            AdvanceToContentOnElement();
 702            return _xmlReader.ReadContentAsObject();
 703        }
 704
 705        public override float ReadContentAsFloat()
 706        {
 707            AdvanceToContentOnElement();
 708            return _xmlReader.ReadContentAsFloat();
 709        }
 710
 711        public override string ReadContentAsString()
 712        {
 713            AdvanceToContentOnElement();
 714            return _xmlReader.ReadContentAsString();
 715        }
 716
 717        public override string ReadInnerXml() => _xmlReader.ReadInnerXml();
 718
 719        public override string ReadOuterXml() => _xmlReader.ReadOuterXml();
 720
 721        public override ReadState ReadState
 722        {
 723            get
 724            {
 725                if (_xmlReader.ReadState != ReadState.Interactive && _infosetReader != null)
 726                    return _infosetReader.ReadState;
 727
 728                return _xmlReader.ReadState;
 729            }
 730        }
 731
 732        public override int ReadValueChunk(char[] buffer, int index, int count) => _xmlReader.ReadValueChunk(buffer, ind
 733
 734        public override void ResolveEntity() => _xmlReader.ResolveEntity();
 735
 736        public override XmlReaderSettings Settings => _xmlReader.Settings;
 737
 738        public override void Skip() => _xmlReader.Skip();
 739
 740        public override string this[int index] => _xmlReader[index];
 741
 742        public override string this[string name] => _xmlReader[name];
 743
 744        public override string this[string name, string ns] => _xmlReader[name, ns];
 745
 746        public override string Value => _xmlReader.Value;
 747
 748        public override Type ValueType => _xmlReader.ValueType;
 749
 750        public override string XmlLang => _xmlReader.XmlLang;
 751
 752        public override XmlSpace XmlSpace => _xmlReader.XmlSpace;
 753
 754        public bool HasLineInfo()
 755        {
 756            if (_xmlReader.ReadState == ReadState.Closed)
 757                return false;
 758
 759            IXmlLineInfo lineInfo = _xmlReader as IXmlLineInfo;
 760            if (lineInfo == null)
 761                return false;
 762            return lineInfo.HasLineInfo();
 763        }
 764
 765        public int LineNumber
 766        {
 767            get
 768            {
 769                if (_xmlReader.ReadState == ReadState.Closed)
 770                    return 0;
 771
 772                IXmlLineInfo lineInfo = _xmlReader as IXmlLineInfo;
 773                if (lineInfo == null)
 774                    return 0;
 775                return lineInfo.LineNumber;
 776            }
 777        }
 778
 779        public int LinePosition
 780        {
 781            get
 782            {
 783                if (_xmlReader.ReadState == ReadState.Closed)
 784                    return 0;
 785
 786                IXmlLineInfo lineInfo = _xmlReader as IXmlLineInfo;
 787                if (lineInfo == null)
 788                    return 0;
 789                return lineInfo.LinePosition;
 790            }
 791        }
 792
 793        internal class MimePart
 794        {
 795            private Stream stream;
 796            private MimeHeaders headers;
 797            private byte[] buffer;
 798            private bool isReferencedFromInfoset;
 799
 800            internal MimePart(Stream stream, MimeHeaders headers)
 801            {
 802                this.stream = stream;
 803                this.headers = headers;
 804            }
 805
 806            internal Stream Stream => stream;
 807
 808            internal MimeHeaders Headers => headers;
 809
 810            internal bool ReferencedFromInfoset
 811            {
 812                get => isReferencedFromInfoset;
 813                set => isReferencedFromInfoset = value;
 814            }
 815
 816            internal long Length => stream.CanSeek ? stream.Length : 0;
 817
 818            internal byte[] GetBuffer(int maxBuffer, ref int remaining)
 819            {
 820                if (buffer == null)
 821                {
 822                    MemoryStream bufferedStream = stream.CanSeek ? new MemoryStream((int)stream.Length) : new MemoryStre
 823                    int size = 256;
 824                    byte[] bytes = new byte[size];
 825
 826                    int read = 0;
 827
 828                    do
 829                    {
 830                        read = stream.Read(bytes, 0, size);
 831                        XmlMtomReader.DecrementBufferQuota(maxBuffer, ref remaining, read);
 832                        if (read > 0)
 833                            bufferedStream.Write(bytes, 0, read);
 834                    }
 835                    while (read > 0);
 836
 837                    bufferedStream.Seek(0, SeekOrigin.Begin);
 838                    buffer = bufferedStream.GetBuffer();
 839                    stream = bufferedStream;
 840                }
 841                return buffer;
 842            }
 843
 844            internal void Release(int maxBuffer, ref int remaining)
 845            {
 846                remaining += (int)Length;
 847                headers.Release(ref remaining);
 848            }
 849        }
 850
 851        internal class XopIncludeReader : XmlDictionaryReader, IXmlLineInfo
 852        {
 853            private int _chunkSize = 4096;  // Just a default.  Serves as a max chunk size.
 854            private int _bytesRemaining;
 855            private MimePart _part;
 856            private ReadState _readState;
 857            private XmlDictionaryReader _parentReader;
 858            private string _stringValue;
 859            private int _stringOffset;
 860            private XmlNodeType _nodeType;
 861            private MemoryStream _binHexStream;
 862            private byte[] _valueBuffer;
 863            private int _valueOffset;
 864            private int _valueCount;
 865            private bool _finishedStream;
 866
 867            public XopIncludeReader(MimePart part, XmlDictionaryReader reader)
 868            {
 869                if (part == null)
 870                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(part));
 871                if (reader == null)
 872                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader));
 873
 874                _part = part;
 875                _parentReader = reader;
 876                _readState = ReadState.Initial;
 877                _nodeType = XmlNodeType.None;
 878                _chunkSize = Math.Min(reader.Quotas.MaxBytesPerRead, _chunkSize);
 879                _bytesRemaining = _chunkSize;
 880                _finishedStream = false;
 881            }
 882
 883            public override XmlDictionaryReaderQuotas Quotas => _parentReader.Quotas;
 884
 885            public override XmlNodeType NodeType => (_readState == ReadState.Interactive) ? _nodeType : _parentReader.No
 886
 887            public override bool Read()
 888            {
 889                bool retVal = true;
 890                switch (_readState)
 891                {
 892                    case ReadState.Initial:
 893                        _readState = ReadState.Interactive;
 894                        _nodeType = XmlNodeType.Text;
 895                        break;
 896                    case ReadState.Interactive:
 897                        if (_finishedStream || (_bytesRemaining == _chunkSize && _stringValue == null))
 898                        {
 899                            _readState = ReadState.EndOfFile;
 900                            _nodeType = XmlNodeType.EndElement;
 901                        }
 902                        else
 903                        {
 904                            _bytesRemaining = _chunkSize;
 905                        }
 906                        break;
 907                    case ReadState.EndOfFile:
 908                        _nodeType = XmlNodeType.None;
 909                        retVal = false;
 910                        break;
 911                }
 912                _stringValue = null;
 913                _binHexStream = null;
 914                _valueOffset = 0;
 915                _valueCount = 0;
 916                _stringOffset = 0;
 917                CloseStreams();
 918                return retVal;
 919            }
 920
 921            public override int ReadValueAsBase64(byte[] buffer, int offset, int count)
 922            {
 923                if (buffer == null)
 924                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(buffer));
 925
 926                if (offset < 0)
 927                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(off
 928                if (offset > buffer.Length)
 929                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(off
 930                if (count < 0)
 931                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(cou
 932                if (count > buffer.Length - offset)
 933                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(cou
 934
 935                if (_stringValue != null)
 936                {
 937                    count = Math.Min(count, _valueCount);
 938                    if (count > 0)
 939                    {
 940                        Buffer.BlockCopy(_valueBuffer, _valueOffset, buffer, offset, count);
 941                        _valueOffset += count;
 942                        _valueCount -= count;
 943                    }
 944                    return count;
 945                }
 946
 947                if (_bytesRemaining < count)
 948                    count = _bytesRemaining;
 949
 950                int read = 0;
 951                if (_readState == ReadState.Interactive)
 952                {
 953                    while (read < count)
 954                    {
 955                        int actual = _part.Stream.Read(buffer, offset + read, count - read);
 956                        if (actual == 0)
 957                        {
 958                            _finishedStream = true;
 959                            break;
 960                        }
 961                        read += actual;
 962                    }
 963                }
 964                _bytesRemaining -= read;
 965                return read;
 966            }
 967
 968            public override int ReadContentAsBase64(byte[] buffer, int offset, int count)
 969            {
 970                if (buffer == null)
 971                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(buffer));
 972
 973                if (offset < 0)
 974                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(off
 975                if (offset > buffer.Length)
 976                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(off
 977                if (count < 0)
 978                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(cou
 979                if (count > buffer.Length - offset)
 980                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(cou
 981
 982                if (_valueCount > 0)
 983                {
 984                    count = Math.Min(count, _valueCount);
 985                    Buffer.BlockCopy(_valueBuffer, _valueOffset, buffer, offset, count);
 986                    _valueOffset += count;
 987                    _valueCount -= count;
 988                    return count;
 989                }
 990
 991                if (_chunkSize < count)
 992                    count = _chunkSize;
 993
 994                int read = 0;
 995                if (_readState == ReadState.Interactive)
 996                {
 997                    while (read < count)
 998                    {
 999                        int actual = _part.Stream.Read(buffer, offset + read, count - read);
 1000                        if (actual == 0)
 1001                        {
 1002                            _finishedStream = true;
 1003                            if (!Read())
 1004                                break;
 1005                        }
 1006                        read += actual;
 1007                    }
 1008                }
 1009                _bytesRemaining = _chunkSize;
 1010                return read;
 1011            }
 1012
 1013            public override int ReadContentAsBinHex(byte[] buffer, int offset, int count)
 1014            {
 1015                if (buffer == null)
 1016                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(buffer));
 1017
 1018                if (offset < 0)
 1019                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(off
 1020                if (offset > buffer.Length)
 1021                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(off
 1022                if (count < 0)
 1023                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(cou
 1024                if (count > buffer.Length - offset)
 1025                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(cou
 1026
 1027                if (_chunkSize < count)
 1028                    count = _chunkSize;
 1029
 1030                int read = 0;
 1031                int consumed = 0;
 1032                while (read < count)
 1033                {
 1034                    if (_binHexStream == null)
 1035                    {
 1036                        try
 1037                        {
 1038                            _binHexStream = new MemoryStream(new BinHexEncoding().GetBytes(Value));
 1039                        }
 1040                        catch (FormatException e) // Wrap format exceptions from decoding document contents
 1041                        {
 1042                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(e.Message, e));
 1043                        }
 1044                    }
 1045
 1046                    int actual = _binHexStream.Read(buffer, offset + read, count - read);
 1047                    if (actual == 0)
 1048                    {
 1049                        _finishedStream = true;
 1050                        if (!Read())
 1051                            break;
 1052
 1053                        consumed = 0;
 1054                    }
 1055
 1056                    read += actual;
 1057                    consumed += actual;
 1058                }
 1059
 1060                // Trim off the consumed chars
 1061                if (_stringValue != null && consumed > 0)
 1062                {
 1063                    _stringValue = _stringValue.Substring(consumed * 2);
 1064                    _stringOffset = Math.Max(0, _stringOffset - consumed * 2);
 1065
 1066                    _bytesRemaining = _chunkSize;
 1067                }
 1068                return read;
 1069            }
 1070
 1071            public override int ReadValueChunk(char[] chars, int offset, int count)
 1072            {
 1073                if (chars == null)
 1074                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(chars));
 1075
 1076                if (offset < 0)
 1077                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(off
 1078                if (offset > chars.Length)
 1079                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(off
 1080                if (count < 0)
 1081                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(cou
 1082                if (count > chars.Length - offset)
 1083                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(cou
 1084
 1085                if (_readState != ReadState.Interactive)
 1086                    return 0;
 1087
 1088                // Copy characters from the Value property
 1089                string str = Value;
 1090                count = Math.Min(_stringValue.Length - _stringOffset, count);
 1091                if (count > 0)
 1092                {
 1093                    _stringValue.CopyTo(_stringOffset, chars, offset, count);
 1094                    _stringOffset += count;
 1095                }
 1096                return count;
 1097            }
 1098
 1099            public override string Value
 1100            {
 1101                get
 1102                {
 1103                    if (_readState != ReadState.Interactive)
 1104                        return string.Empty;
 1105
 1106                    if (_stringValue == null)
 1107                    {
 1108                        // Compute the bytes to read
 1109                        int byteCount = _bytesRemaining;
 1110                        byteCount -= byteCount % 3;
 1111
 1112                        // Handle trailing bytes
 1113                        if (_valueCount > 0 && _valueOffset > 0)
 1114                        {
 1115                            Buffer.BlockCopy(_valueBuffer, _valueOffset, _valueBuffer, 0, _valueCount);
 1116                            _valueOffset = 0;
 1117                        }
 1118                        byteCount -= _valueCount;
 1119
 1120                        // Resize buffer if needed
 1121                        if (_valueBuffer == null)
 1122                        {
 1123                            _valueBuffer = new byte[byteCount];
 1124                        }
 1125                        else if (_valueBuffer.Length < byteCount)
 1126                        {
 1127                            Array.Resize(ref _valueBuffer, byteCount);
 1128                        }
 1129                        byte[] buffer = _valueBuffer;
 1130
 1131                        // Fill up the buffer
 1132                        int offset = 0;
 1133                        int read = 0;
 1134                        while (byteCount > 0)
 1135                        {
 1136                            read = _part.Stream.Read(buffer, offset, byteCount);
 1137                            if (read == 0)
 1138                            {
 1139                                _finishedStream = true;
 1140                                break;
 1141                            }
 1142
 1143                            _bytesRemaining -= read;
 1144                            _valueCount += read;
 1145                            byteCount -= read;
 1146                            offset += read;
 1147                        }
 1148
 1149                        // Convert the bytes
 1150                        _stringValue = Convert.ToBase64String(buffer, 0, _valueCount);
 1151                    }
 1152                    return _stringValue;
 1153                }
 1154            }
 1155
 1156            public override string ReadContentAsString()
 1157            {
 1158                int stringContentQuota = Quotas.MaxStringContentLength;
 1159                StringBuilder sb = new StringBuilder();
 1160                do
 1161                {
 1162                    string val = Value;
 1163                    if (val.Length > stringContentQuota)
 1164                        XmlExceptionHelper.ThrowMaxStringContentLengthExceeded(this, Quotas.MaxStringContentLength);
 1165                    stringContentQuota -= val.Length;
 1166                    sb.Append(val);
 1167                } while (Read());
 1168                return sb.ToString();
 1169            }
 1170
 1171            public override int AttributeCount => 0;
 1172
 1173            public override string BaseURI => _parentReader.BaseURI;
 1174
 1175            public override bool CanReadBinaryContent => true;
 1176
 1177            public override bool CanReadValueChunk => true;
 1178
 1179            public override bool CanResolveEntity => _parentReader.CanResolveEntity;
 1180
 1181            public override void Close()
 1182            {
 1183                CloseStreams();
 1184                _readState = ReadState.Closed;
 1185            }
 1186
 1187            private void CloseStreams()
 1188            {
 1189                if (_binHexStream != null)
 1190                {
 1191                    _binHexStream.Close();
 1192                    _binHexStream = null;
 1193                }
 1194            }
 1195
 1196            public override int Depth => (_readState == ReadState.Interactive) ? _parentReader.Depth + 1 : _parentReader
 1197
 1198            public override bool EOF => _readState == ReadState.EndOfFile;
 1199
 1200            public override string GetAttribute(int index) => null;
 1201
 1202            public override string GetAttribute(string name) => null;
 1203
 1204            public override string GetAttribute(string name, string ns) => null;
 1205
 1206            public override string GetAttribute(XmlDictionaryString localName, XmlDictionaryString ns) => null;
 1207
 1208            public override bool HasAttributes => false;
 1209
 1210            public override bool HasValue => _readState == ReadState.Interactive;
 1211
 1212            public override bool IsDefault => false;
 1213
 1214            public override bool IsEmptyElement => false;
 1215
 1216            public override bool IsLocalName(string localName) => false;
 1217
 1218            public override bool IsLocalName(XmlDictionaryString localName) => false;
 1219
 1220            public override bool IsNamespaceUri(string ns) => false;
 1221
 1222            public override bool IsNamespaceUri(XmlDictionaryString ns) => false;
 1223
 1224            public override bool IsStartElement() => false;
 1225
 1226            public override bool IsStartElement(string localName) => false;
 1227
 1228            public override bool IsStartElement(string localName, string ns) => false;
 1229
 1230            public override bool IsStartElement(XmlDictionaryString localName, XmlDictionaryString ns) => false;
 1231
 1232            public override string LocalName => (_readState == ReadState.Interactive) ? string.Empty : _parentReader.Loc
 1233
 1234            public override string LookupNamespace(string ns) => _parentReader.LookupNamespace(ns);
 1235
 1236            public override void MoveToAttribute(int index)
 1237            {
 1238            }
 1239
 1240            public override bool MoveToAttribute(string name) => false;
 1241
 1242            public override bool MoveToAttribute(string name, string ns) => false;
 1243
 1244            public override bool MoveToElement() => false;
 1245
 1246            public override bool MoveToFirstAttribute() => false;
 1247
 1248            public override bool MoveToNextAttribute() => false;
 1249
 1250            public override string Name => (_readState == ReadState.Interactive) ? string.Empty : _parentReader.Name;
 1251
 1252            public override string NamespaceURI => (_readState == ReadState.Interactive) ? string.Empty : _parentReader.
 1253
 1254            public override XmlNameTable NameTable => _parentReader.NameTable;
 1255
 1256            public override string Prefix => (_readState == ReadState.Interactive) ? string.Empty : _parentReader.Prefix
 1257
 1258            public override char QuoteChar => _parentReader.QuoteChar;
 1259
 1260            public override bool ReadAttributeValue() => false;
 1261
 1262            public override string ReadInnerXml() => ReadContentAsString();
 1263
 1264            public override string ReadOuterXml() => ReadContentAsString();
 1265
 1266            public override ReadState ReadState => _readState;
 1267
 1268            public override void ResolveEntity()
 1269            {
 1270            }
 1271
 1272            public override XmlReaderSettings Settings => _parentReader.Settings;
 1273
 1274            public override void Skip() => Read();
 1275
 1276            public override string this[int index] => null;
 1277
 1278            public override string this[string name] => null;
 1279
 1280            public override string this[string name, string ns] => null;
 1281
 1282            public override string XmlLang => _parentReader.XmlLang;
 1283
 1284            public override XmlSpace XmlSpace => _parentReader.XmlSpace;
 1285
 1286            public override Type ValueType => (_readState == ReadState.Interactive) ? typeof(byte[]) : _parentReader.Val
 1287
 1288            bool IXmlLineInfo.HasLineInfo() => ((IXmlLineInfo)_parentReader).HasLineInfo();
 1289
 1290            int IXmlLineInfo.LineNumber => ((IXmlLineInfo)_parentReader).LineNumber;
 1291
 1292            int IXmlLineInfo.LinePosition => ((IXmlLineInfo)_parentReader).LinePosition;
 1293        }
 1294    }
 1295
 1296    internal class MimeMessageReader
 1297    {
 11298        private static byte[] CRLFCRLF = new byte[] { (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' };
 1299        private bool getContentStreamCalled;
 1300        private MimeHeaderReader mimeHeaderReader;
 1301        private DelimittedStreamReader reader;
 1302
 21303        public MimeMessageReader(Stream stream)
 1304        {
 21305            if (stream == null)
 01306                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(stream));
 1307
 21308            reader = new DelimittedStreamReader(stream);
 21309            mimeHeaderReader = new MimeHeaderReader(reader.GetNextStream(CRLFCRLF));
 21310        }
 1311
 1312        public Stream GetContentStream()
 1313        {
 21314            if (getContentStreamCalled)
 01315                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.MimeMessageGe
 1316
 21317            mimeHeaderReader.Close();
 1318
 21319            Stream s = reader.GetNextStream(null);
 1320
 21321            getContentStreamCalled = true;
 1322
 21323            return s;
 1324        }
 1325
 1326        public MimeHeaders ReadHeaders(int maxBuffer, ref int remaining)
 1327        {
 21328            MimeHeaders headers = new MimeHeaders();
 61329            while (mimeHeaderReader.Read(maxBuffer, ref remaining))
 1330            {
 41331                headers.Add(mimeHeaderReader.Name, mimeHeaderReader.Value, ref remaining);
 1332            }
 21333            return headers;
 1334        }
 1335    }
 1336
 1337    internal class MimeReader
 1338    {
 1339        private static byte[] CRLFCRLF = new byte[] { (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' };
 1340        private byte[] boundaryBytes;
 1341        private string content;
 1342        private Stream currentStream;
 1343        private MimeHeaderReader mimeHeaderReader;
 1344        private DelimittedStreamReader reader;
 1345        private byte[] scratch = new byte[2];
 1346
 1347        public MimeReader(Stream stream, string boundary)
 1348        {
 1349            if (stream == null)
 1350                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(stream));
 1351            if (boundary == null)
 1352                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(boundary));
 1353
 1354            reader = new DelimittedStreamReader(stream);
 1355            boundaryBytes = MimeWriter.GetBoundaryBytes(boundary);
 1356
 1357            // Need to ensure that the content begins with a CRLF, in case the
 1358            // outer construct has consumed the trailing CRLF
 1359            reader.Push(boundaryBytes, 0, 2);
 1360        }
 1361
 1362        public void Close() => reader.Close();
 1363
 1364        /// Gets the content preceding the first part of the MIME multi-part message
 1365        public string Preface
 1366        {
 1367            get
 1368            {
 1369                if (content == null)
 1370                {
 1371                    Stream s = reader.GetNextStream(boundaryBytes);
 1372                    content = new StreamReader(s, System.Text.Encoding.ASCII, false, 256).ReadToEnd();
 1373                    s.Close();
 1374                    if (content == null)
 1375                        content = string.Empty;
 1376                }
 1377                return content;
 1378            }
 1379        }
 1380
 1381        public Stream GetContentStream()
 1382        {
 1383            Fx.Assert(content != null, "");
 1384
 1385            mimeHeaderReader.Close();
 1386
 1387            return reader.GetNextStream(boundaryBytes);
 1388        }
 1389
 1390        public bool ReadNextPart()
 1391        {
 1392            string content = Preface;
 1393
 1394            if (currentStream != null)
 1395            {
 1396                currentStream.Close();
 1397                currentStream = null;
 1398            }
 1399
 1400            Stream stream = reader.GetNextStream(CRLFCRLF);
 1401
 1402            if (stream == null)
 1403                return false;
 1404
 1405            if (BlockRead(stream, scratch, 0, 2) == 2)
 1406            {
 1407                if (scratch[0] == '\r' && scratch[1] == '\n')
 1408                {
 1409                    if (mimeHeaderReader == null)
 1410                        mimeHeaderReader = new MimeHeaderReader(stream);
 1411                    else
 1412                        mimeHeaderReader.Reset(stream);
 1413                    return true;
 1414                }
 1415                else if (scratch[0] == '-' && scratch[1] == '-')
 1416                {
 1417                    int read = BlockRead(stream, scratch, 0, 2);
 1418
 1419                    if (read < 2 || (scratch[0] == '\r' && scratch[1] == '\n'))
 1420                        return false;
 1421                }
 1422            }
 1423
 1424            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.MimeReaderTruncated));
 1425        }
 1426
 1427        public MimeHeaders ReadHeaders(int maxBuffer, ref int remaining)
 1428        {
 1429            MimeHeaders headers = new MimeHeaders();
 1430            while (mimeHeaderReader.Read(maxBuffer, ref remaining))
 1431            {
 1432                headers.Add(mimeHeaderReader.Name, mimeHeaderReader.Value, ref remaining);
 1433            }
 1434            return headers;
 1435        }
 1436
 1437        private int BlockRead(Stream stream, byte[] buffer, int offset, int count)
 1438        {
 1439            int read = 0;
 1440            do
 1441            {
 1442                int r = stream.Read(buffer, offset + read, count - read);
 1443                if (r == 0)
 1444                    break;
 1445                read += r;
 1446            } while (read < count);
 1447            return read;
 1448        }
 1449
 1450    }
 1451
 1452    internal class DelimittedStreamReader
 1453    {
 1454        private bool canGetNextStream = true;
 1455
 1456        // used for closing the reader, and validating that only one stream can be reading at a time.
 1457        private DelimittedReadStream currentStream;
 1458        private byte[] delimitter;
 1459        private byte[] matchBuffer;
 1460        private byte[] scratch;
 1461        private BufferedReadStream stream;
 1462
 1463        public DelimittedStreamReader(Stream stream)
 1464        {
 1465            this.stream = new BufferedReadStream(stream);
 1466        }
 1467
 1468        public void Close() => stream.Close();
 1469
 1470        // Closes the current stream.  If the current stream is not the same as the caller, nothing is done.
 1471        private void Close(DelimittedReadStream caller)
 1472        {
 1473            if (currentStream == caller)
 1474            {
 1475                if (delimitter == null)
 1476                {
 1477                    stream.Close();
 1478                }
 1479                else
 1480                {
 1481                    if (scratch == null)
 1482                    {
 1483                        scratch = new byte[1024];
 1484                    }
 1485                    while (0 != Read(caller, scratch, 0, scratch.Length))
 1486                        ;
 1487                }
 1488
 1489                currentStream = null;
 1490            }
 1491        }
 1492
 1493        // Gets the next logical stream delimitted by the given sequence.
 1494        public Stream GetNextStream(byte[] delimitter)
 1495        {
 1496            if (currentStream != null)
 1497            {
 1498                currentStream.Close();
 1499                currentStream = null;
 1500            }
 1501
 1502            if (!canGetNextStream)
 1503                return null;
 1504
 1505            this.delimitter = delimitter;
 1506
 1507            canGetNextStream = delimitter != null;
 1508
 1509            currentStream = new DelimittedReadStream(this);
 1510
 1511            return currentStream;
 1512        }
 1513
 1514        private enum MatchState
 1515        {
 1516            True,
 1517            False,
 1518            InsufficientData
 1519        }
 1520
 1521        private MatchState MatchDelimitter(byte[] buffer, int start, int end)
 1522        {
 1523            if (delimitter.Length > end - start)
 1524            {
 1525                for (int i = end - start - 1; i >= 1; i--)
 1526                {
 1527                    if (buffer[start + i] != delimitter[i])
 1528                        return MatchState.False;
 1529                }
 1530                return MatchState.InsufficientData;
 1531            }
 1532            for (int i = delimitter.Length - 1; i >= 1; i--)
 1533            {
 1534                if (buffer[start + i] != delimitter[i])
 1535                    return MatchState.False;
 1536            }
 1537            return MatchState.True;
 1538        }
 1539
 1540        private int ProcessRead(byte[] buffer, int offset, int read)
 1541        {
 1542            // nothing to process if 0 bytes were read
 1543            if (read == 0)
 1544                return read;
 1545
 1546            for (int ptr = offset, end = offset + read; ptr < end; ptr++)
 1547            {
 1548                if (buffer[ptr] == delimitter[0])
 1549                {
 1550                    switch (MatchDelimitter(buffer, ptr, end))
 1551                    {
 1552                        case MatchState.True:
 1553                            {
 1554                                int actual = ptr - offset;
 1555                                ptr += delimitter.Length;
 1556                                stream.Push(buffer, ptr, end - ptr);
 1557                                currentStream = null;
 1558                                return actual;
 1559                            }
 1560                        case MatchState.False:
 1561                            break;
 1562                        case MatchState.InsufficientData:
 1563                            {
 1564                                int actual = ptr - offset;
 1565                                if (actual > 0)
 1566                                {
 1567                                    stream.Push(buffer, ptr, end - ptr);
 1568                                    return actual;
 1569                                }
 1570                                else
 1571                                {
 1572                                    return -1;
 1573                                }
 1574                            }
 1575                    }
 1576                }
 1577            }
 1578            return read;
 1579        }
 1580
 1581        private int Read(DelimittedReadStream caller, byte[] buffer, int offset, int count)
 1582        {
 1583            if (currentStream != caller)
 1584                return 0;
 1585
 1586            int read = stream.Read(buffer, offset, count);
 1587            if (read == 0)
 1588            {
 1589                canGetNextStream = false;
 1590                currentStream = null;
 1591                return read;
 1592            }
 1593
 1594            // If delimitter is null, read until the underlying stream returns 0 bytes
 1595            if (delimitter == null)
 1596                return read;
 1597
 1598            // Scans the read data for the delimitter. If found, the number of bytes read are adjusted
 1599            // to account for the number of bytes up to but not including the delimitter.
 1600            int actual = ProcessRead(buffer, offset, read);
 1601
 1602            if (actual < 0)
 1603            {
 1604                if (matchBuffer == null || matchBuffer.Length < delimitter.Length - read)
 1605                    matchBuffer = new byte[delimitter.Length - read];
 1606
 1607                int matched = stream.ReadBlock(matchBuffer, 0, delimitter.Length - read);
 1608
 1609                if (MatchRemainder(read, matched))
 1610                {
 1611                    currentStream = null;
 1612                    actual = 0;
 1613                }
 1614                else
 1615                {
 1616                    stream.Push(matchBuffer, 0, matched);
 1617
 1618                    int i = 1;
 1619                    for (; i < read; i++)
 1620                    {
 1621                        if (buffer[i] == delimitter[0])
 1622                            break;
 1623                    }
 1624
 1625                    if (i < read)
 1626                        stream.Push(buffer, offset + i, read - i);
 1627
 1628                    actual = i;
 1629                }
 1630            }
 1631
 1632            return actual;
 1633        }
 1634
 1635        private bool MatchRemainder(int start, int count)
 1636        {
 1637            if (start + count != delimitter.Length)
 1638                return false;
 1639
 1640            for (count--; count >= 0; count--)
 1641            {
 1642                if (delimitter[start + count] != matchBuffer[count])
 1643                    return false;
 1644            }
 1645            return true;
 1646        }
 1647
 1648        internal void Push(byte[] buffer, int offset, int count) => stream.Push(buffer, offset, count);
 1649
 1650        private class DelimittedReadStream : Stream
 1651        {
 1652            private DelimittedStreamReader reader;
 1653
 1654            public DelimittedReadStream(DelimittedStreamReader reader)
 1655            {
 1656                if (reader == null)
 1657                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader));
 1658
 1659                this.reader = reader;
 1660            }
 1661
 1662            public override bool CanRead => true;
 1663
 1664            public override bool CanSeek => false;
 1665
 1666            public override bool CanWrite => false;
 1667
 1668            public override long Length => throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedExc
 1669
 1670            public override long Position
 1671            {
 1672                get => throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.
 1673                set => throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.
 1674            }
 1675
 1676            public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object
 1677
 1678            public override void Close() => reader.Close(this);
 1679
 1680            public override void EndWrite(IAsyncResult asyncResult) => throw DiagnosticUtility.ExceptionUtility.ThrowHel
 1681
 1682            public override void Flush() => throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedEx
 1683
 1684            public override int Read(byte[] buffer, int offset, int count)
 1685            {
 1686                if (buffer == null)
 1687                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(buffer));
 1688
 1689                if (offset < 0)
 1690                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(off
 1691                if (offset > buffer.Length)
 1692                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(off
 1693                if (count < 0)
 1694                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(cou
 1695                if (count > buffer.Length - offset)
 1696                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(cou
 1697
 1698                return reader.Read(this, buffer, offset, count);
 1699            }
 1700
 1701            public override long Seek(long offset, SeekOrigin origin) => throw DiagnosticUtility.ExceptionUtility.ThrowH
 1702
 1703            public override void SetLength(long value) => throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new 
 1704
 1705            public override void Write(byte[] buffer, int offset, int count) => throw DiagnosticUtility.ExceptionUtility
 1706        }
 1707
 1708    }
 1709
 1710    internal class MimeHeaders
 1711    {
 1712        private static class Constants
 1713        {
 1714            public const string ContentTransferEncoding = "content-transfer-encoding";
 1715            public const string ContentID = "content-id";
 1716            public const string ContentType = "content-type";
 1717            public const string MimeVersion = "mime-version";
 1718        }
 1719
 1720        private Dictionary<string, MimeHeader> headers = new Dictionary<string, MimeHeader>();
 1721
 1722        public MimeHeaders()
 1723        {
 1724        }
 1725
 1726        public ContentTypeHeader ContentType
 1727        {
 1728            get
 1729            {
 1730                MimeHeader header;
 1731                if (headers.TryGetValue(Constants.ContentType, out header))
 1732                    return header as ContentTypeHeader;
 1733                return null;
 1734            }
 1735        }
 1736
 1737        public ContentIDHeader ContentID
 1738        {
 1739            get
 1740            {
 1741                MimeHeader header;
 1742                if (headers.TryGetValue(Constants.ContentID, out header))
 1743                    return header as ContentIDHeader;
 1744                return null;
 1745            }
 1746        }
 1747
 1748        public ContentTransferEncodingHeader ContentTransferEncoding
 1749        {
 1750            get
 1751            {
 1752                MimeHeader header;
 1753                if (headers.TryGetValue(Constants.ContentTransferEncoding, out header))
 1754                    return header as ContentTransferEncodingHeader;
 1755                return null;
 1756            }
 1757        }
 1758
 1759        public MimeVersionHeader MimeVersion
 1760        {
 1761            get
 1762            {
 1763                MimeHeader header;
 1764                if (headers.TryGetValue(Constants.MimeVersion, out header))
 1765                    return header as MimeVersionHeader;
 1766                return null;
 1767            }
 1768        }
 1769
 1770        public void Add(string name, string value, ref int remaining)
 1771        {
 1772            if (name == null)
 1773                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(name));
 1774
 1775            if (value == null)
 1776                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value));
 1777
 1778            switch (name)
 1779            {
 1780                case Constants.ContentType:
 1781                    Add(new ContentTypeHeader(value));
 1782                    break;
 1783                case Constants.ContentID:
 1784                    Add(new ContentIDHeader(name, value));
 1785                    break;
 1786                case Constants.ContentTransferEncoding:
 1787                    Add(new ContentTransferEncodingHeader(value));
 1788                    break;
 1789                case Constants.MimeVersion:
 1790                    Add(new MimeVersionHeader(value));
 1791                    break;
 1792
 1793                // Skip any fields that are not recognized
 1794                // Content-description is currently not stored since it is not used
 1795                default:
 1796                    remaining += value.Length * sizeof(char);
 1797                    break;
 1798            }
 1799            remaining += name.Length * sizeof(char);
 1800        }
 1801
 1802        public void Add(MimeHeader header)
 1803        {
 1804            if (header == null)
 1805                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(header));
 1806
 1807            MimeHeader existingHeader;
 1808            if (headers.TryGetValue(header.Name, out existingHeader))
 1809                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.Format(SR.MimeReaderHea
 1810            else
 1811                headers.Add(header.Name, header);
 1812        }
 1813
 1814        public void Release(ref int remaining)
 1815        {
 1816            foreach (MimeHeader header in headers.Values)
 1817            {
 1818                remaining += header.Value.Length * sizeof(char);
 1819            }
 1820        }
 1821
 1822    }
 1823
 1824    internal class MimeHeader
 1825    {
 1826        private string name;
 1827        private string value;
 1828
 1829        public MimeHeader(string name, string value)
 1830        {
 1831            if (name == null)
 1832                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(name));
 1833
 1834            this.name = name;
 1835            this.value = value;
 1836        }
 1837
 1838        public string Name => name;
 1839
 1840        public string Value => value;
 1841    }
 1842
 1843    internal class ContentTypeHeader : MimeHeader
 1844    {
 1845        public static readonly ContentTypeHeader Default = new ContentTypeHeader("application/octet-stream");
 1846
 1847        public ContentTypeHeader(string value)
 1848            : base("content-type", value)
 1849        {
 1850        }
 1851
 1852        private string mediaType;
 1853        private string subType;
 1854        private Dictionary<string, string> parameters;
 1855
 1856        public string MediaType
 1857        {
 1858            get
 1859            {
 1860                if (mediaType == null && Value != null)
 1861                    ParseValue();
 1862
 1863                return mediaType;
 1864            }
 1865        }
 1866
 1867        public string MediaSubtype
 1868        {
 1869            get
 1870            {
 1871                if (subType == null && Value != null)
 1872                    ParseValue();
 1873
 1874                return subType;
 1875            }
 1876        }
 1877
 1878        public Dictionary<string, string> Parameters
 1879        {
 1880            get
 1881            {
 1882                if (parameters == null)
 1883                {
 1884                    if (Value != null)
 1885                        ParseValue();
 1886                    else
 1887                        parameters = new Dictionary<string, string>();
 1888                }
 1889                return parameters;
 1890            }
 1891        }
 1892
 1893        private void ParseValue()
 1894        {
 1895            if (parameters == null)
 1896            {
 1897                int offset = 0;
 1898                parameters = new Dictionary<string, string>();
 1899                mediaType = MailBnfHelper.ReadToken(Value, ref offset, null);
 1900                if (offset >= Value.Length || Value[offset++] != '/')
 1901                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.MimeContentTypeHead
 1902                subType = MailBnfHelper.ReadToken(Value, ref offset, null);
 1903
 1904                while (MailBnfHelper.SkipCFWS(Value, ref offset))
 1905                {
 1906                    if (offset >= Value.Length || Value[offset++] != ';')
 1907                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.MimeContentType
 1908
 1909                    if (!MailBnfHelper.SkipCFWS(Value, ref offset))
 1910                        break;
 1911
 1912                    string paramAttribute = MailBnfHelper.ReadParameterAttribute(Value, ref offset, null);
 1913                    if (paramAttribute == null || offset >= Value.Length || Value[offset++] != '=')
 1914                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.MimeContentType
 1915                    string paramValue = MailBnfHelper.ReadParameterValue(Value, ref offset, null);
 1916
 1917                    parameters.Add(paramAttribute.ToLowerInvariant(), paramValue);
 1918                }
 1919
 1920                if (parameters.ContainsKey(MtomGlobals.StartInfoParam))
 1921                {
 1922                    // This allows us to maintain back compat with Orcas clients while allowing clients
 1923                    // following the spec (with action inside start-info) to interop with RFC 2387
 1924                    string startInfo = parameters[MtomGlobals.StartInfoParam];
 1925
 1926                    // we're only interested in finding the action here - skipping past the content type to the first ;
 1927                    int startInfoOffset = startInfo.IndexOf(';');
 1928                    if (startInfoOffset > -1)
 1929                    {
 1930                        // keep going through the start-info string until we've reached the end of the stream
 1931                        while (MailBnfHelper.SkipCFWS(startInfo, ref startInfoOffset))
 1932                        {
 1933                            // after having read through an attribute=value pair, we always expect to be at a ;
 1934                            if (startInfo[startInfoOffset] == ';')
 1935                            {
 1936                                startInfoOffset++;
 1937                            }
 1938                            else
 1939                            {
 1940                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.MimeCon
 1941                            }
 1942                            string paramAttribute = MailBnfHelper.ReadParameterAttribute(startInfo, ref startInfoOffset,
 1943                            if (paramAttribute == null || startInfoOffset >= startInfo.Length || startInfo[startInfoOffs
 1944                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.MimeCon
 1945                            string paramValue = MailBnfHelper.ReadParameterValue(startInfo, ref startInfoOffset, null);
 1946
 1947                            if (paramAttribute == MtomGlobals.ActionParam)
 1948                            {
 1949                                parameters[MtomGlobals.ActionParam] = paramValue;
 1950                            }
 1951                        }
 1952                    }
 1953                }
 1954
 1955            }
 1956        }
 1957    }
 1958
 1959    internal enum ContentTransferEncoding
 1960    {
 1961        SevenBit,
 1962        EightBit,
 1963        Binary,
 1964        Other,
 1965        Unspecified
 1966    }
 1967
 1968    internal class ContentTransferEncodingHeader : MimeHeader
 1969    {
 1970        private ContentTransferEncoding contentTransferEncoding;
 1971        private string contentTransferEncodingValue;
 1972
 1973        public static readonly ContentTransferEncodingHeader Binary = new ContentTransferEncodingHeader(ContentTransferE
 1974        public static readonly ContentTransferEncodingHeader EightBit = new ContentTransferEncodingHeader(ContentTransfe
 1975        public static readonly ContentTransferEncodingHeader SevenBit = new ContentTransferEncodingHeader(ContentTransfe
 1976
 1977        public ContentTransferEncodingHeader(string value)
 1978            : base("content-transfer-encoding", value.ToLowerInvariant())
 1979        {
 1980        }
 1981
 1982        public ContentTransferEncodingHeader(ContentTransferEncoding contentTransferEncoding, string value)
 1983            : base("content-transfer-encoding", null)
 1984        {
 1985            this.contentTransferEncoding = contentTransferEncoding;
 1986            contentTransferEncodingValue = value;
 1987        }
 1988
 1989        public ContentTransferEncoding ContentTransferEncoding
 1990        {
 1991            get
 1992            {
 1993                ParseValue();
 1994                return contentTransferEncoding;
 1995            }
 1996        }
 1997
 1998        public string ContentTransferEncodingValue
 1999        {
 2000            get
 2001            {
 2002                ParseValue();
 2003                return contentTransferEncodingValue;
 2004            }
 2005        }
 2006
 2007        private void ParseValue()
 2008        {
 2009            if (contentTransferEncodingValue == null)
 2010            {
 2011                int offset = 0;
 2012                contentTransferEncodingValue = (Value.Length == 0) ? Value : ((Value[0] == '"') ? MailBnfHelper.ReadQuot
 2013                switch (contentTransferEncodingValue)
 2014                {
 2015                    case "7bit":
 2016                        contentTransferEncoding = ContentTransferEncoding.SevenBit;
 2017                        break;
 2018                    case "8bit":
 2019                        contentTransferEncoding = ContentTransferEncoding.EightBit;
 2020                        break;
 2021                    case "binary":
 2022                        contentTransferEncoding = ContentTransferEncoding.Binary;
 2023                        break;
 2024                    default:
 2025                        contentTransferEncoding = ContentTransferEncoding.Other;
 2026                        break;
 2027                }
 2028            }
 2029        }
 2030    }
 2031
 2032    internal class ContentIDHeader : MimeHeader
 2033    {
 2034        public ContentIDHeader(string name, string value)
 2035            : base(name, value)
 2036        {
 2037        }
 2038    }
 2039
 2040    internal class MimeVersionHeader : MimeHeader
 2041    {
 2042        public static readonly MimeVersionHeader Default = new MimeVersionHeader("1.0");
 2043
 2044        public MimeVersionHeader(string value)
 2045            : base("mime-version", value)
 2046        {
 2047        }
 2048
 2049        private string version;
 2050
 2051        public string Version
 2052        {
 2053            get
 2054            {
 2055                if (version == null && Value != null)
 2056                    ParseValue();
 2057                return version;
 2058            }
 2059        }
 2060
 2061        private void ParseValue()
 2062        {
 2063            // shortcut for the most common case.
 2064            if (Value == "1.0")
 2065            {
 2066                version = "1.0";
 2067            }
 2068            else
 2069            {
 2070                int offset = 0;
 2071
 2072                if (!MailBnfHelper.SkipCFWS(Value, ref offset))
 2073                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.MimeVersionHeaderIn
 2074
 2075                StringBuilder builder = new StringBuilder();
 2076                MailBnfHelper.ReadDigits(Value, ref offset, builder);
 2077
 2078                if ((!MailBnfHelper.SkipCFWS(Value, ref offset) || offset >= Value.Length || Value[offset++] != '.') || 
 2079                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.MimeVersionHeaderIn
 2080
 2081                builder.Append('.');
 2082
 2083                MailBnfHelper.ReadDigits(Value, ref offset, builder);
 2084
 2085                version = builder.ToString();
 2086            }
 2087        }
 2088    }
 2089
 2090    internal class MimeHeaderReader
 2091    {
 2092        private enum ReadState
 2093        {
 2094            ReadName,
 2095            SkipWS,
 2096            ReadValue,
 2097            ReadLF,
 2098            ReadWS,
 2099            EOF
 2100        }
 2101
 2102        private string value;
 2103        private byte[] buffer = new byte[1024];
 2104        private int maxOffset;
 2105        private string name;
 2106        private int offset;
 2107        private ReadState readState = ReadState.ReadName;
 2108        private Stream stream;
 2109
 2110        public MimeHeaderReader(Stream stream)
 2111        {
 2112            if (stream == null)
 2113                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(stream));
 2114
 2115            this.stream = stream;
 2116        }
 2117
 2118        public string Value => value;
 2119
 2120        public string Name => name;
 2121
 2122        public void Close()
 2123        {
 2124            stream.Close();
 2125            readState = ReadState.EOF;
 2126        }
 2127
 2128        public bool Read(int maxBuffer, ref int remaining)
 2129        {
 2130            name = null;
 2131            value = null;
 2132
 2133            while (readState != ReadState.EOF)
 2134            {
 2135                if (offset == maxOffset)
 2136                {
 2137                    maxOffset = stream.Read(buffer, 0, buffer.Length);
 2138                    offset = 0;
 2139                    if (BufferEnd())
 2140                        break;
 2141                }
 2142                if (ProcessBuffer(maxBuffer, ref remaining))
 2143                    break;
 2144            }
 2145
 2146            return value != null;
 2147        }
 2148
 2149        private bool ProcessBuffer(int maxBuffer, ref int remaining)
 2150        {
 2151            unsafe
 2152            {
 2153                fixed (byte* pBuffer = buffer)
 2154                {
 2155                    byte* start = pBuffer + offset;
 2156                    byte* end = pBuffer + maxOffset;
 2157                    byte* ptr = start;
 2158
 2159                    switch (readState)
 2160                    {
 2161                        case ReadState.ReadName:
 2162                            for (; ptr < end; ptr++)
 2163                            {
 2164                                if (*ptr == ':')
 2165                                {
 2166                                    AppendName(new string((sbyte*)start, 0, (int)(ptr - start)), maxBuffer, ref remainin
 2167                                    ptr++;
 2168                                    goto case ReadState.SkipWS;
 2169                                }
 2170                                else
 2171                                {
 2172                                    // convert to lower case up front.
 2173                                    if (*ptr >= 'A' && *ptr <= 'Z')
 2174                                    {
 2175                                        *ptr += 'a' - 'A';
 2176                                    }
 2177                                    else if (*ptr < 33 || *ptr > 126)
 2178                                    {
 2179                                        if (name == null && *ptr == (byte)'\r')
 2180                                        {
 2181                                            ptr++;
 2182                                            if (ptr >= end || *ptr != '\n')
 2183                                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatExce
 2184                                            goto case ReadState.EOF;
 2185                                        }
 2186
 2187                                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR
 2188                                    }
 2189                                }
 2190                            }
 2191                            AppendName(new string((sbyte*)start, 0, (int)(ptr - start)), maxBuffer, ref remaining);
 2192                            readState = ReadState.ReadName;
 2193                            break;
 2194                        case ReadState.SkipWS:
 2195                            for (; ptr < end; ptr++)
 2196                                if (*ptr != (byte)'\t' && *ptr != ' ')
 2197                                    goto case ReadState.ReadValue;
 2198                            readState = ReadState.SkipWS;
 2199                            break;
 2200                        case ReadState.ReadValue:
 2201                            start = ptr;
 2202                            for (; ptr < end; ptr++)
 2203                            {
 2204                                if (*ptr == (byte)'\r')
 2205                                {
 2206                                    AppendValue(new string((sbyte*)start, 0, (int)(ptr - start)), maxBuffer, ref remaini
 2207                                    ptr++;
 2208                                    goto case ReadState.ReadLF;
 2209                                }
 2210                                else if (*ptr == (byte)'\n')
 2211                                {
 2212                                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.Mim
 2213                                }
 2214                            }
 2215                            AppendValue(new string((sbyte*)start, 0, (int)(ptr - start)), maxBuffer, ref remaining);
 2216                            readState = ReadState.ReadValue;
 2217                            break;
 2218                        case ReadState.ReadLF:
 2219                            if (ptr < end)
 2220                            {
 2221                                if (*ptr != (byte)'\n')
 2222                                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.Mim
 2223                                ptr++;
 2224                                goto case ReadState.ReadWS;
 2225                            }
 2226                            readState = ReadState.ReadLF;
 2227                            break;
 2228                        case ReadState.ReadWS:
 2229                            if (ptr < end)
 2230                            {
 2231                                if (*ptr != (byte)' ' && *ptr != (byte)'\t')
 2232                                {
 2233                                    readState = ReadState.ReadName;
 2234                                    offset = (int)(ptr - pBuffer);
 2235                                    return true;
 2236                                }
 2237                                goto case ReadState.ReadValue;
 2238                            }
 2239                            readState = ReadState.ReadWS;
 2240                            break;
 2241                        case ReadState.EOF:
 2242                            readState = ReadState.EOF;
 2243                            offset = (int)(ptr - pBuffer);
 2244                            return true;
 2245                    }
 2246                    offset = (int)(ptr - pBuffer);
 2247                }
 2248            }
 2249            return false;
 2250        }
 2251
 2252        private bool BufferEnd()
 2253        {
 2254            if (maxOffset == 0)
 2255            {
 2256                if (readState != ReadState.ReadWS && readState != ReadState.ReadValue)
 2257                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.MimeReaderMalformed
 2258
 2259                readState = ReadState.EOF;
 2260                return true;
 2261            }
 2262            return false;
 2263        }
 2264
 2265        // Resets the mail field reader to the new stream to reuse buffers
 2266        public void Reset(Stream stream)
 2267        {
 2268            if (stream == null)
 2269                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(stream));
 2270
 2271            if (readState != ReadState.EOF)
 2272                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.MimeReaderRes
 2273
 2274            this.stream = stream;
 2275            readState = ReadState.ReadName;
 2276            maxOffset = 0;
 2277            offset = 0;
 2278        }
 2279
 2280        // helper methods
 2281
 2282        private void AppendValue(string value, int maxBuffer, ref int remaining)
 2283        {
 2284            XmlMtomReader.DecrementBufferQuota(maxBuffer, ref remaining, value.Length * sizeof(char));
 2285            if (this.value == null)
 2286                this.value = value;
 2287            else
 2288                this.value += value;
 2289        }
 2290
 2291        private void AppendName(string value, int maxBuffer, ref int remaining)
 2292        {
 2293            XmlMtomReader.DecrementBufferQuota(maxBuffer, ref remaining, value.Length * sizeof(char));
 2294            if (name == null)
 2295                name = value;
 2296            else
 2297                name += value;
 2298        }
 2299
 2300    }
 2301
 2302    internal class BufferedReadStream : Stream
 2303    {
 2304        private Stream stream;
 2305        private byte[] storedBuffer;
 2306        private int storedLength;
 2307        private int storedOffset;
 2308        private bool readMore;
 2309
 2310        public BufferedReadStream(Stream stream)
 2311            : this(stream, false)
 2312        {
 2313        }
 2314
 2315        public BufferedReadStream(Stream stream, bool readMore)
 2316        {
 2317            if (stream == null)
 2318                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(stream));
 2319
 2320            this.stream = stream;
 2321            this.readMore = readMore;
 2322        }
 2323
 2324        public override bool CanWrite => false;
 2325
 2326        public override bool CanSeek => false;
 2327
 2328        public override bool CanRead => stream.CanRead;
 2329
 2330        public override long Length => throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedExcepti
 2331
 2332        public override long Position
 2333        {
 2334            get => throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.Seek
 2335            set => throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.Seek
 2336        }
 2337
 2338        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object stat
 2339        {
 2340            if (!CanRead)
 2341                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.ReadNot
 2342
 2343            return stream.BeginRead(buffer, offset, count, callback, state);
 2344        }
 2345
 2346        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object sta
 2347
 2348        public override void Close() => stream.Close();
 2349
 2350        public override int EndRead(IAsyncResult asyncResult)
 2351        {
 2352            if (!CanRead)
 2353                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.ReadNot
 2354
 2355            return stream.EndRead(asyncResult);
 2356        }
 2357
 2358        public override void EndWrite(IAsyncResult asyncResult) => throw DiagnosticUtility.ExceptionUtility.ThrowHelperE
 2359
 2360        public override void Flush() => stream.Flush();
 2361
 2362        public override int Read(byte[] buffer, int offset, int count)
 2363        {
 2364            if (!CanRead)
 2365                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.ReadNot
 2366
 2367            if (buffer == null)
 2368                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(buffer));
 2369
 2370            if (offset < 0)
 2371                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(offset)
 2372            if (offset > buffer.Length)
 2373                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(offset)
 2374            if (count < 0)
 2375                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count),
 2376            if (count > buffer.Length - offset)
 2377                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count),
 2378
 2379            int read = 0;
 2380            if (storedOffset < storedLength)
 2381            {
 2382                read = Math.Min(count, storedLength - storedOffset);
 2383                Buffer.BlockCopy(storedBuffer, storedOffset, buffer, offset, read);
 2384                storedOffset += read;
 2385                if (read == count || !readMore)
 2386                    return read;
 2387                offset += read;
 2388                count -= read;
 2389            }
 2390            return read + stream.Read(buffer, offset, count);
 2391        }
 2392
 2393        public override int ReadByte()
 2394        {
 2395            if (storedOffset < storedLength)
 2396                return (int)storedBuffer[storedOffset++];
 2397            else
 2398                return base.ReadByte();
 2399        }
 2400
 2401        public int ReadBlock(byte[] buffer, int offset, int count)
 2402        {
 2403            int read;
 2404            int total = 0;
 2405            while (total < count && (read = Read(buffer, offset + total, count - total)) != 0)
 2406            {
 2407                total += read;
 2408            }
 2409            return total;
 2410        }
 2411
 2412        public void Push(byte[] buffer, int offset, int count)
 2413        {
 2414            if (count == 0)
 2415                return;
 2416
 2417            if (storedOffset == storedLength)
 2418            {
 2419                if (storedBuffer == null || storedBuffer.Length < count)
 2420                    storedBuffer = new byte[count];
 2421                storedOffset = 0;
 2422                storedLength = count;
 2423            }
 2424            else
 2425            {
 2426                // if there's room to just insert before existing data
 2427                if (count <= storedOffset)
 2428                    storedOffset -= count;
 2429                // if there's room in the buffer but need to shift things over
 2430                else if (count <= storedBuffer.Length - storedLength + storedOffset)
 2431                {
 2432                    Buffer.BlockCopy(storedBuffer, storedOffset, storedBuffer, count, storedLength - storedOffset);
 2433                    storedLength += count - storedOffset;
 2434                    storedOffset = 0;
 2435                }
 2436                else
 2437                {
 2438                    byte[] newBuffer = new byte[count + storedLength - storedOffset];
 2439                    Buffer.BlockCopy(storedBuffer, storedOffset, newBuffer, count, storedLength - storedOffset);
 2440                    storedLength += count - storedOffset;
 2441                    storedOffset = 0;
 2442                    storedBuffer = newBuffer;
 2443                }
 2444            }
 2445            Buffer.BlockCopy(buffer, offset, storedBuffer, storedOffset, count);
 2446        }
 2447
 2448        public override long Seek(long offset, SeekOrigin origin) => throw DiagnosticUtility.ExceptionUtility.ThrowHelpe
 2449
 2450        public override void SetLength(long value) => throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotS
 2451
 2452        public override void Write(byte[] buffer, int offset, int count) => throw DiagnosticUtility.ExceptionUtility.Thr
 2453    }
 2454
 2455    internal static class MailBnfHelper
 2456    {
 2457        private static bool[] s_fqtext = new bool[128];
 2458        private static bool[] s_ttext = new bool[128];
 2459        private static bool[] s_digits = new bool[128];
 2460        private static bool[] s_boundary = new bool[128];
 2461
 2462        static MailBnfHelper()
 2463        {
 2464            // fqtext = %d1-9 / %d11 / %d12 / %d14-33 / %d35-91 / %d93-127
 2465            for (int i = 1; i <= 9; i++)
 2466            { s_fqtext[i] = true; }
 2467            s_fqtext[11] = true;
 2468            s_fqtext[12] = true;
 2469            for (int i = 14; i <= 33; i++)
 2470            { s_fqtext[i] = true; }
 2471            for (int i = 35; i <= 91; i++)
 2472            { s_fqtext[i] = true; }
 2473            for (int i = 93; i <= 127; i++)
 2474            { s_fqtext[i] = true; }
 2475
 2476            // ttext = %d33-126 except '()<>@,;:\"/[]?='
 2477            for (int i = 33; i <= 126; i++)
 2478            { s_ttext[i] = true; }
 2479            s_ttext['('] = false;
 2480            s_ttext[')'] = false;
 2481            s_ttext['<'] = false;
 2482            s_ttext['>'] = false;
 2483            s_ttext['@'] = false;
 2484            s_ttext[','] = false;
 2485            s_ttext[';'] = false;
 2486            s_ttext[':'] = false;
 2487            s_ttext['\\'] = false;
 2488            s_ttext['"'] = false;
 2489            s_ttext['/'] = false;
 2490            s_ttext['['] = false;
 2491            s_ttext[']'] = false;
 2492            s_ttext['?'] = false;
 2493            s_ttext['='] = false;
 2494
 2495            // digits = %d48-57
 2496            for (int i = 48; i <= 57; i++)
 2497                s_digits[i] = true;
 2498
 2499            // boundary = DIGIT / ALPHA / "'" / "(" / ")" / "+" / "_" / "," / "-" / "." / "/" / ":" / "=" / "?" / " "
 2500            // cannot end with " "
 2501            for (int i = '0'; i <= '9'; i++)
 2502            { s_boundary[i] = true; }
 2503            for (int i = 'A'; i <= 'Z'; i++)
 2504            { s_boundary[i] = true; }
 2505            for (int i = 'a'; i <= 'z'; i++)
 2506            { s_boundary[i] = true; }
 2507            s_boundary['\''] = true;
 2508            s_boundary['('] = true;
 2509            s_boundary[')'] = true;
 2510            s_boundary['+'] = true;
 2511            s_boundary['_'] = true;
 2512            s_boundary[','] = true;
 2513            s_boundary['-'] = true;
 2514            s_boundary['.'] = true;
 2515            s_boundary['/'] = true;
 2516            s_boundary[':'] = true;
 2517            s_boundary['='] = true;
 2518            s_boundary['?'] = true;
 2519            s_boundary[' '] = true;
 2520        }
 2521
 2522        public static bool SkipCFWS(string data, ref int offset)
 2523        {
 2524            int comments = 0;
 2525            for (; offset < data.Length; offset++)
 2526            {
 2527                if (data[offset] > 127)
 2528                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.Format(SR.MimeHeade
 2529                else if (data[offset] == '\\' && comments > 0)
 2530                    offset += 2;
 2531                else if (data[offset] == '(')
 2532                    comments++;
 2533                else if (data[offset] == ')')
 2534                    comments--;
 2535                else if (data[offset] != ' ' && data[offset] != '\t' && comments == 0)
 2536                    return true;
 2537            }
 2538            return false;
 2539        }
 2540
 2541        public static string ReadQuotedString(string data, ref int offset, StringBuilder builder)
 2542        {
 2543            // assume first char is the opening quote
 2544            int start = ++offset;
 2545            StringBuilder localBuilder = (builder != null ? builder : new StringBuilder());
 2546            for (; offset < data.Length; offset++)
 2547            {
 2548                if (data[offset] == '\\')
 2549                {
 2550                    localBuilder.Append(data, start, offset - start);
 2551                    start = ++offset;
 2552                    continue;
 2553                }
 2554                else if (data[offset] == '"')
 2555                {
 2556                    localBuilder.Append(data, start, offset - start);
 2557                    offset++;
 2558                    return (builder != null ? null : localBuilder.ToString());
 2559                }
 2560                else if (!(data[offset] < s_fqtext.Length && s_fqtext[data[offset]]))
 2561                {
 2562                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.Format(SR.MimeHeade
 2563                }
 2564            }
 2565            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.MimeReaderMalformedHeader))
 2566        }
 2567
 2568        public static string ReadParameterAttribute(string data, ref int offset, StringBuilder builder)
 2569        {
 2570            if (!SkipCFWS(data, ref offset))
 2571                return null;
 2572
 2573            return ReadToken(data, ref offset, null);
 2574        }
 2575
 2576        public static string ReadParameterValue(string data, ref int offset, StringBuilder builder)
 2577        {
 2578            if (!SkipCFWS(data, ref offset))
 2579                return string.Empty;
 2580
 2581            if (offset < data.Length && data[offset] == '"')
 2582                return ReadQuotedString(data, ref offset, builder);
 2583            else
 2584                return ReadToken(data, ref offset, builder);
 2585        }
 2586
 2587        public static string ReadToken(string data, ref int offset, StringBuilder builder)
 2588        {
 2589            int start = offset;
 2590            for (; offset < data.Length; offset++)
 2591            {
 2592                if (data[offset] > s_ttext.Length)
 2593                {
 2594                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.Format(SR.MimeHeade
 2595                }
 2596                else if (!s_ttext[data[offset]])
 2597                {
 2598                    break;
 2599                }
 2600            }
 2601            return data.Substring(start, offset - start);
 2602        }
 2603
 2604        public static string ReadDigits(string data, ref int offset, StringBuilder builder)
 2605        {
 2606            int start = offset;
 2607            StringBuilder localBuilder = (builder != null ? builder : new StringBuilder());
 2608            for (; offset < data.Length && data[offset] < s_digits.Length && s_digits[data[offset]]; offset++)
 2609                ;
 2610            localBuilder.Append(data, start, offset - start);
 2611            return (builder != null ? null : localBuilder.ToString());
 2612        }
 2613
 2614        public static bool IsValidMimeBoundary(string data)
 2615        {
 2616            int length = (data == null) ? 0 : data.Length;
 2617            if (length == 0 || length > 70 || data[length - 1] == ' ')
 2618                return false;
 2619
 2620            for (int i = 0; i < length; i++)
 2621            {
 2622                if (!(data[i] < s_boundary.Length && s_boundary[data[i]]))
 2623                    return false;
 2624            }
 2625
 2626            return true;
 2627        }
 2628    }
 2629}