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