Rewrite with hard-coded offsets into the PE file format to discern if a binary is PE32 or PE32+, and then to determine if it contains a "CLR Data Directory" entry that looks valid. Tested with PE32 and PE32+ compiled Mono binaries, PE32 and PE32+ native binaries, and a random assortment of garbage files. Former-commit-id: 9e7ac86ec84f653a2f79b87183efd5b0ebda001b
3158 lines
112 KiB
C#
3158 lines
112 KiB
C#
//------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------
|
|
|
|
namespace System.Xml
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Runtime;
|
|
using System.Runtime.Serialization;
|
|
using System.Text;
|
|
using System.Security;
|
|
using System.Security.Permissions;
|
|
|
|
public interface IXmlMtomReaderInitializer
|
|
{
|
|
void SetInput(byte[] buffer, int offset, int count, Encoding[] encodings, string contentType, XmlDictionaryReaderQuotas quotas, int maxBufferSize, OnXmlDictionaryReaderClose onClose);
|
|
void SetInput(Stream stream, Encoding[] encodings, string contentType, XmlDictionaryReaderQuotas quotas, int maxBufferSize, OnXmlDictionaryReaderClose onClose);
|
|
}
|
|
|
|
class XmlMtomReader : XmlDictionaryReader, IXmlLineInfo, IXmlMtomReaderInitializer
|
|
{
|
|
Encoding[] encodings;
|
|
XmlDictionaryReader xmlReader;
|
|
XmlDictionaryReader infosetReader;
|
|
MimeReader mimeReader;
|
|
Dictionary<string, MimePart> mimeParts;
|
|
OnXmlDictionaryReaderClose onClose;
|
|
bool readingBinaryElement;
|
|
int maxBufferSize;
|
|
int bufferRemaining;
|
|
MimePart part;
|
|
|
|
public XmlMtomReader()
|
|
{
|
|
}
|
|
|
|
internal static void DecrementBufferQuota(int maxBuffer, ref int remaining, int size)
|
|
{
|
|
if (remaining - size <= 0)
|
|
{
|
|
remaining = 0;
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomBufferQuotaExceeded, maxBuffer)));
|
|
}
|
|
else
|
|
{
|
|
remaining -= size;
|
|
}
|
|
}
|
|
|
|
void SetReadEncodings(Encoding[] encodings)
|
|
{
|
|
if (encodings == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encodings");
|
|
|
|
for (int i = 0; i < encodings.Length; i++)
|
|
{
|
|
if (encodings[i] == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(String.Format(CultureInfo.InvariantCulture, "encodings[{0}]", i));
|
|
}
|
|
|
|
this.encodings = new Encoding[encodings.Length];
|
|
encodings.CopyTo(this.encodings, 0);
|
|
}
|
|
|
|
void CheckContentType(string contentType)
|
|
{
|
|
if (contentType != null && contentType.Length == 0)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.MtomContentTypeInvalid), "contentType"));
|
|
}
|
|
|
|
public void SetInput(byte[] buffer, int offset, int count, Encoding[] encodings, string contentType, XmlDictionaryReaderQuotas quotas, int maxBufferSize, OnXmlDictionaryReaderClose onClose)
|
|
{
|
|
SetInput(new MemoryStream(buffer, offset, count), encodings, contentType, quotas, maxBufferSize, onClose);
|
|
}
|
|
|
|
public void SetInput(Stream stream, Encoding[] encodings, string contentType, XmlDictionaryReaderQuotas quotas, int maxBufferSize, OnXmlDictionaryReaderClose onClose)
|
|
{
|
|
SetReadEncodings(encodings);
|
|
CheckContentType(contentType);
|
|
Initialize(stream, contentType, quotas, maxBufferSize);
|
|
this.onClose = onClose;
|
|
}
|
|
|
|
void Initialize(Stream stream, string contentType, XmlDictionaryReaderQuotas quotas, int maxBufferSize)
|
|
{
|
|
if (stream == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
|
|
|
|
this.maxBufferSize = maxBufferSize;
|
|
this.bufferRemaining = maxBufferSize;
|
|
|
|
string boundary, start, startInfo;
|
|
|
|
if (contentType == null)
|
|
{
|
|
MimeMessageReader messageReader = new MimeMessageReader(stream);
|
|
MimeHeaders messageHeaders = messageReader.ReadHeaders(this.maxBufferSize, ref this.bufferRemaining);
|
|
ReadMessageMimeVersionHeader(messageHeaders.MimeVersion);
|
|
ReadMessageContentTypeHeader(messageHeaders.ContentType, out boundary, out start, out startInfo);
|
|
stream = messageReader.GetContentStream();
|
|
if (stream == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageInvalidContent)));
|
|
}
|
|
else
|
|
{
|
|
ReadMessageContentTypeHeader(new ContentTypeHeader(contentType), out boundary, out start, out startInfo);
|
|
}
|
|
|
|
this.mimeReader = new MimeReader(stream, boundary);
|
|
this.mimeParts = null;
|
|
this.readingBinaryElement = false;
|
|
|
|
MimePart infosetPart = (start == null) ? ReadRootMimePart() : ReadMimePart(GetStartUri(start));
|
|
byte[] infosetBytes = infosetPart.GetBuffer(this.maxBufferSize, ref this.bufferRemaining);
|
|
int infosetByteCount = (int)infosetPart.Length;
|
|
|
|
Encoding encoding = ReadRootContentTypeHeader(infosetPart.Headers.ContentType, this.encodings, startInfo);
|
|
CheckContentTransferEncodingOnRoot(infosetPart.Headers.ContentTransferEncoding);
|
|
|
|
IXmlTextReaderInitializer initializer = xmlReader as IXmlTextReaderInitializer;
|
|
|
|
if (initializer != null)
|
|
initializer.SetInput(infosetBytes, 0, infosetByteCount, encoding, quotas, null);
|
|
else
|
|
xmlReader = XmlDictionaryReader.CreateTextReader(infosetBytes, 0, infosetByteCount, encoding, quotas, null);
|
|
}
|
|
|
|
public override XmlDictionaryReaderQuotas Quotas
|
|
{
|
|
get
|
|
{
|
|
return this.xmlReader.Quotas;
|
|
}
|
|
}
|
|
|
|
void ReadMessageMimeVersionHeader(MimeVersionHeader header)
|
|
{
|
|
if (header != null && header.Version != MimeVersionHeader.Default.Version)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageInvalidMimeVersion, header.Version, MimeVersionHeader.Default.Version)));
|
|
}
|
|
|
|
void ReadMessageContentTypeHeader(ContentTypeHeader header, out string boundary, out string start, out string startInfo)
|
|
{
|
|
if (header == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageContentTypeNotFound)));
|
|
|
|
if (String.Compare(MtomGlobals.MediaType, header.MediaType, StringComparison.OrdinalIgnoreCase) != 0
|
|
|| String.Compare(MtomGlobals.MediaSubtype, header.MediaSubtype, StringComparison.OrdinalIgnoreCase) != 0)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageNotMultipart, MtomGlobals.MediaType, MtomGlobals.MediaSubtype)));
|
|
|
|
string type;
|
|
if (!header.Parameters.TryGetValue(MtomGlobals.TypeParam, out type) || MtomGlobals.XopType != type)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageNotApplicationXopXml, MtomGlobals.XopType)));
|
|
|
|
if (!header.Parameters.TryGetValue(MtomGlobals.BoundaryParam, out boundary))
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageRequiredParamNotSpecified, MtomGlobals.BoundaryParam)));
|
|
if (!MailBnfHelper.IsValidMimeBoundary(boundary))
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomBoundaryInvalid, boundary)));
|
|
|
|
if (!header.Parameters.TryGetValue(MtomGlobals.StartParam, out start))
|
|
start = null;
|
|
|
|
if (!header.Parameters.TryGetValue(MtomGlobals.StartInfoParam, out startInfo))
|
|
startInfo = null;
|
|
}
|
|
|
|
Encoding ReadRootContentTypeHeader(ContentTypeHeader header, Encoding[] expectedEncodings, string expectedType)
|
|
{
|
|
if (header == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootContentTypeNotFound)));
|
|
|
|
if (String.Compare(MtomGlobals.XopMediaType, header.MediaType, StringComparison.OrdinalIgnoreCase) != 0
|
|
|| String.Compare(MtomGlobals.XopMediaSubtype, header.MediaSubtype, StringComparison.OrdinalIgnoreCase) != 0)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootNotApplicationXopXml, MtomGlobals.XopMediaType, MtomGlobals.XopMediaSubtype)));
|
|
|
|
string charset;
|
|
if (!header.Parameters.TryGetValue(MtomGlobals.CharsetParam, out charset)
|
|
|| charset == null || charset.Length == 0)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootRequiredParamNotSpecified, MtomGlobals.CharsetParam)));
|
|
Encoding encoding = null;
|
|
for (int i = 0; i < encodings.Length; i++)
|
|
{
|
|
if (String.Compare(charset, expectedEncodings[i].WebName, StringComparison.OrdinalIgnoreCase) == 0)
|
|
{
|
|
encoding = expectedEncodings[i];
|
|
break;
|
|
}
|
|
}
|
|
if (encoding == null)
|
|
{
|
|
// Check for alternate names
|
|
if (String.Compare(charset, "utf-16LE", StringComparison.OrdinalIgnoreCase) == 0)
|
|
{
|
|
for (int i = 0; i < encodings.Length; i++)
|
|
{
|
|
if (String.Compare(expectedEncodings[i].WebName, Encoding.Unicode.WebName, StringComparison.OrdinalIgnoreCase) == 0)
|
|
{
|
|
encoding = expectedEncodings[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (String.Compare(charset, "utf-16BE", StringComparison.OrdinalIgnoreCase) == 0)
|
|
{
|
|
for (int i = 0; i < encodings.Length; i++)
|
|
{
|
|
if (String.Compare(expectedEncodings[i].WebName, Encoding.BigEndianUnicode.WebName, StringComparison.OrdinalIgnoreCase) == 0)
|
|
{
|
|
encoding = expectedEncodings[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (encoding == null)
|
|
{
|
|
StringBuilder expectedCharSetStr = new StringBuilder();
|
|
for (int i = 0; i < encodings.Length; i++)
|
|
{
|
|
if (expectedCharSetStr.Length != 0)
|
|
expectedCharSetStr.Append(" | ");
|
|
expectedCharSetStr.Append(encodings[i].WebName);
|
|
}
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootUnexpectedCharset, charset, expectedCharSetStr.ToString())));
|
|
}
|
|
}
|
|
|
|
if (expectedType != null)
|
|
{
|
|
string rootType;
|
|
if (!header.Parameters.TryGetValue(MtomGlobals.TypeParam, out rootType)
|
|
|| rootType == null || rootType.Length == 0)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootRequiredParamNotSpecified, MtomGlobals.TypeParam)));
|
|
if (rootType != expectedType)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootUnexpectedType, rootType, expectedType)));
|
|
}
|
|
|
|
return encoding;
|
|
}
|
|
|
|
// 7bit is default encoding in the absence of content-transfer-encoding header
|
|
void CheckContentTransferEncodingOnRoot(ContentTransferEncodingHeader header)
|
|
{
|
|
if (header != null && header.ContentTransferEncoding == ContentTransferEncoding.Other)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomContentTransferEncodingNotSupported,
|
|
header.Value,
|
|
ContentTransferEncodingHeader.SevenBit.ContentTransferEncodingValue,
|
|
ContentTransferEncodingHeader.EightBit.ContentTransferEncodingValue,
|
|
ContentTransferEncodingHeader.Binary.ContentTransferEncodingValue)));
|
|
}
|
|
|
|
void CheckContentTransferEncodingOnBinaryPart(ContentTransferEncodingHeader header)
|
|
{
|
|
if (header == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomContentTransferEncodingNotPresent,
|
|
ContentTransferEncodingHeader.Binary.ContentTransferEncodingValue)));
|
|
else if (header.ContentTransferEncoding != ContentTransferEncoding.Binary)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomInvalidTransferEncodingForMimePart,
|
|
header.Value, ContentTransferEncodingHeader.Binary.ContentTransferEncodingValue)));
|
|
}
|
|
|
|
string GetStartUri(string startUri)
|
|
{
|
|
if (startUri.StartsWith("<", StringComparison.Ordinal))
|
|
{
|
|
if (startUri.EndsWith(">", StringComparison.Ordinal))
|
|
return startUri;
|
|
else
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomInvalidStartUri, startUri)));
|
|
}
|
|
else
|
|
return String.Format(CultureInfo.InvariantCulture, "<{0}>", startUri);
|
|
}
|
|
|
|
public override bool Read()
|
|
{
|
|
bool retVal = xmlReader.Read();
|
|
|
|
if (xmlReader.NodeType == XmlNodeType.Element)
|
|
{
|
|
XopIncludeReader binaryDataReader = null;
|
|
if (xmlReader.IsStartElement(MtomGlobals.XopIncludeLocalName, MtomGlobals.XopIncludeNamespace))
|
|
{
|
|
string uri = null;
|
|
while (xmlReader.MoveToNextAttribute())
|
|
{
|
|
if (xmlReader.LocalName == MtomGlobals.XopIncludeHrefLocalName && xmlReader.NamespaceURI == MtomGlobals.XopIncludeHrefNamespace)
|
|
uri = xmlReader.Value;
|
|
else if (xmlReader.NamespaceURI == MtomGlobals.XopIncludeNamespace)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomXopIncludeInvalidXopAttributes, xmlReader.LocalName, MtomGlobals.XopIncludeNamespace)));
|
|
}
|
|
if (uri == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomXopIncludeHrefNotSpecified, MtomGlobals.XopIncludeHrefLocalName)));
|
|
|
|
MimePart mimePart = ReadMimePart(uri);
|
|
|
|
CheckContentTransferEncodingOnBinaryPart(mimePart.Headers.ContentTransferEncoding);
|
|
|
|
this.part = mimePart;
|
|
binaryDataReader = new XopIncludeReader(mimePart, xmlReader);
|
|
binaryDataReader.Read();
|
|
|
|
xmlReader.MoveToElement();
|
|
if (xmlReader.IsEmptyElement)
|
|
{
|
|
xmlReader.Read();
|
|
}
|
|
else
|
|
{
|
|
int xopDepth = xmlReader.Depth;
|
|
xmlReader.ReadStartElement();
|
|
|
|
while (xmlReader.Depth > xopDepth)
|
|
{
|
|
if (xmlReader.IsStartElement() && xmlReader.NamespaceURI == MtomGlobals.XopIncludeNamespace)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomXopIncludeInvalidXopElement, xmlReader.LocalName, MtomGlobals.XopIncludeNamespace)));
|
|
|
|
xmlReader.Skip();
|
|
}
|
|
|
|
xmlReader.ReadEndElement();
|
|
}
|
|
}
|
|
|
|
if (binaryDataReader != null)
|
|
{
|
|
this.xmlReader.MoveToContent();
|
|
this.infosetReader = this.xmlReader;
|
|
this.xmlReader = binaryDataReader;
|
|
binaryDataReader = null;
|
|
}
|
|
}
|
|
|
|
if (xmlReader.ReadState == ReadState.EndOfFile && infosetReader != null)
|
|
{
|
|
// Read past the containing EndElement if necessary
|
|
if (!retVal)
|
|
retVal = infosetReader.Read();
|
|
|
|
this.part.Release(this.maxBufferSize, ref this.bufferRemaining);
|
|
xmlReader = infosetReader;
|
|
infosetReader = null;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
MimePart ReadMimePart(string uri)
|
|
{
|
|
MimePart part = null;
|
|
|
|
if (uri == null || uri.Length == 0)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomInvalidEmptyURI)));
|
|
|
|
string contentID = null;
|
|
if (uri.StartsWith(MimeGlobals.ContentIDScheme, StringComparison.Ordinal))
|
|
contentID = String.Format(CultureInfo.InvariantCulture, "<{0}>", Uri.UnescapeDataString(uri.Substring(MimeGlobals.ContentIDScheme.Length)));
|
|
else if (uri.StartsWith("<", StringComparison.Ordinal))
|
|
contentID = uri;
|
|
|
|
if (contentID == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomInvalidCIDUri, uri)));
|
|
|
|
if (mimeParts != null && mimeParts.TryGetValue(contentID, out part))
|
|
{
|
|
if (part.ReferencedFromInfoset)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMimePartReferencedMoreThanOnce, contentID)));
|
|
}
|
|
else
|
|
{
|
|
int maxMimeParts = AppSettings.MaxMimeParts;
|
|
while (part == null && mimeReader.ReadNextPart())
|
|
{
|
|
MimeHeaders headers = mimeReader.ReadHeaders(this.maxBufferSize, ref this.bufferRemaining);
|
|
Stream contentStream = mimeReader.GetContentStream();
|
|
if (contentStream == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageInvalidContentInMimePart)));
|
|
|
|
ContentIDHeader contentIDHeader = (headers == null) ? null : headers.ContentID;
|
|
if (contentIDHeader == null || contentIDHeader.Value == null)
|
|
{
|
|
// Skip content if Content-ID header is not present
|
|
int size = 256;
|
|
byte[] bytes = new byte[size];
|
|
|
|
int read = 0;
|
|
do
|
|
{
|
|
read = contentStream.Read(bytes, 0, size);
|
|
}
|
|
while (read > 0);
|
|
continue;
|
|
}
|
|
|
|
string currentContentID = headers.ContentID.Value;
|
|
MimePart currentPart = new MimePart(contentStream, headers);
|
|
if (mimeParts == null)
|
|
mimeParts = new Dictionary<string, MimePart>();
|
|
|
|
mimeParts.Add(currentContentID, currentPart);
|
|
|
|
if (mimeParts.Count > maxMimeParts)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MaxMimePartsExceeded, maxMimeParts, AppSettings.MaxMimePartsAppSettingsString)));
|
|
|
|
if (currentContentID.Equals(contentID))
|
|
part = currentPart;
|
|
else
|
|
currentPart.GetBuffer(this.maxBufferSize, ref this.bufferRemaining);
|
|
}
|
|
|
|
if (part == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomPartNotFound, uri)));
|
|
}
|
|
|
|
part.ReferencedFromInfoset = true;
|
|
return part;
|
|
}
|
|
|
|
MimePart ReadRootMimePart()
|
|
{
|
|
MimePart part = null;
|
|
|
|
if (!mimeReader.ReadNextPart())
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomRootPartNotFound)));
|
|
|
|
MimeHeaders headers = mimeReader.ReadHeaders(this.maxBufferSize, ref this.bufferRemaining);
|
|
Stream contentStream = mimeReader.GetContentStream();
|
|
if (contentStream == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.MtomMessageInvalidContentInMimePart)));
|
|
part = new MimePart(contentStream, headers);
|
|
|
|
return part;
|
|
}
|
|
|
|
void AdvanceToContentOnElement()
|
|
{
|
|
if (NodeType != XmlNodeType.Attribute)
|
|
{
|
|
MoveToContent();
|
|
}
|
|
}
|
|
|
|
public override int AttributeCount
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.AttributeCount;
|
|
}
|
|
}
|
|
|
|
public override string BaseURI
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.BaseURI;
|
|
}
|
|
}
|
|
|
|
public override bool CanReadBinaryContent
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.CanReadBinaryContent;
|
|
}
|
|
}
|
|
|
|
public override bool CanReadValueChunk
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.CanReadValueChunk;
|
|
}
|
|
}
|
|
|
|
public override bool CanResolveEntity
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.CanResolveEntity;
|
|
}
|
|
}
|
|
|
|
public override void Close()
|
|
{
|
|
xmlReader.Close();
|
|
mimeReader.Close();
|
|
OnXmlDictionaryReaderClose onClose = this.onClose;
|
|
this.onClose = null;
|
|
if (onClose != null)
|
|
{
|
|
try
|
|
{
|
|
onClose(this);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e)) throw;
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override int Depth
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.Depth;
|
|
}
|
|
}
|
|
|
|
public override bool EOF
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.EOF;
|
|
}
|
|
}
|
|
|
|
public override string GetAttribute(int index)
|
|
{
|
|
return xmlReader.GetAttribute(index);
|
|
}
|
|
|
|
public override string GetAttribute(string name)
|
|
{
|
|
return xmlReader.GetAttribute(name);
|
|
}
|
|
|
|
public override string GetAttribute(string name, string ns)
|
|
{
|
|
return xmlReader.GetAttribute(name, ns);
|
|
}
|
|
|
|
public override string GetAttribute(XmlDictionaryString localName, XmlDictionaryString ns)
|
|
{
|
|
return xmlReader.GetAttribute(localName, ns);
|
|
}
|
|
#if NO
|
|
public override ArraySegment<byte> GetSubset(bool advance)
|
|
{
|
|
return xmlReader.GetSubset(advance);
|
|
}
|
|
#endif
|
|
public override bool HasAttributes
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.HasAttributes;
|
|
}
|
|
}
|
|
|
|
public override bool HasValue
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.HasValue;
|
|
}
|
|
}
|
|
|
|
public override bool IsDefault
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.IsDefault;
|
|
}
|
|
}
|
|
|
|
public override bool IsEmptyElement
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.IsEmptyElement;
|
|
}
|
|
}
|
|
|
|
public override bool IsLocalName(string localName)
|
|
{
|
|
return xmlReader.IsLocalName(localName);
|
|
}
|
|
|
|
public override bool IsLocalName(XmlDictionaryString localName)
|
|
{
|
|
return xmlReader.IsLocalName(localName);
|
|
}
|
|
|
|
public override bool IsNamespaceUri(string ns)
|
|
{
|
|
return xmlReader.IsNamespaceUri(ns);
|
|
}
|
|
|
|
public override bool IsNamespaceUri(XmlDictionaryString ns)
|
|
{
|
|
return xmlReader.IsNamespaceUri(ns);
|
|
}
|
|
|
|
public override bool IsStartElement()
|
|
{
|
|
return xmlReader.IsStartElement();
|
|
}
|
|
|
|
public override bool IsStartElement(string localName)
|
|
{
|
|
return xmlReader.IsStartElement(localName);
|
|
}
|
|
|
|
public override bool IsStartElement(string localName, string ns)
|
|
{
|
|
return xmlReader.IsStartElement(localName, ns);
|
|
}
|
|
|
|
public override bool IsStartElement(XmlDictionaryString localName, XmlDictionaryString ns)
|
|
{
|
|
return xmlReader.IsStartElement(localName, ns);
|
|
}
|
|
#if NO
|
|
public override bool IsStartSubsetElement()
|
|
{
|
|
return xmlReader.IsStartSubsetElement();
|
|
}
|
|
#endif
|
|
public override string LocalName
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.LocalName;
|
|
}
|
|
}
|
|
|
|
public override string LookupNamespace(string ns)
|
|
{
|
|
return xmlReader.LookupNamespace(ns);
|
|
}
|
|
|
|
public override void MoveToAttribute(int index)
|
|
{
|
|
xmlReader.MoveToAttribute(index);
|
|
}
|
|
|
|
public override bool MoveToAttribute(string name)
|
|
{
|
|
return xmlReader.MoveToAttribute(name);
|
|
}
|
|
|
|
public override bool MoveToAttribute(string name, string ns)
|
|
{
|
|
return xmlReader.MoveToAttribute(name, ns);
|
|
}
|
|
|
|
public override bool MoveToElement()
|
|
{
|
|
return xmlReader.MoveToElement();
|
|
}
|
|
|
|
public override bool MoveToFirstAttribute()
|
|
{
|
|
return xmlReader.MoveToFirstAttribute();
|
|
}
|
|
|
|
public override bool MoveToNextAttribute()
|
|
{
|
|
return xmlReader.MoveToNextAttribute();
|
|
}
|
|
|
|
public override string Name
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.Name;
|
|
}
|
|
}
|
|
|
|
public override string NamespaceURI
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.NamespaceURI;
|
|
}
|
|
}
|
|
|
|
public override XmlNameTable NameTable
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.NameTable;
|
|
}
|
|
}
|
|
|
|
public override XmlNodeType NodeType
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.NodeType;
|
|
}
|
|
}
|
|
|
|
public override string Prefix
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.Prefix;
|
|
}
|
|
}
|
|
|
|
public override char QuoteChar
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.QuoteChar;
|
|
}
|
|
}
|
|
|
|
public override bool ReadAttributeValue()
|
|
{
|
|
return xmlReader.ReadAttributeValue();
|
|
}
|
|
|
|
public override object ReadContentAs(Type returnType, IXmlNamespaceResolver namespaceResolver)
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAs(returnType, namespaceResolver);
|
|
}
|
|
|
|
public override byte[] ReadContentAsBase64()
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsBase64();
|
|
}
|
|
|
|
public override int ReadValueAsBase64(byte[] buffer, int offset, int count)
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadValueAsBase64(buffer, offset, count);
|
|
}
|
|
|
|
public override int ReadContentAsBase64(byte[] buffer, int offset, int count)
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsBase64(buffer, offset, count);
|
|
}
|
|
|
|
public override int ReadElementContentAsBase64(byte[] buffer, int offset, int count)
|
|
{
|
|
if (!readingBinaryElement)
|
|
{
|
|
if (IsEmptyElement)
|
|
{
|
|
Read();
|
|
return 0;
|
|
}
|
|
|
|
ReadStartElement();
|
|
readingBinaryElement = true;
|
|
}
|
|
|
|
int i = ReadContentAsBase64(buffer, offset, count);
|
|
|
|
if (i == 0)
|
|
{
|
|
ReadEndElement();
|
|
readingBinaryElement = false;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
public override int ReadElementContentAsBinHex(byte[] buffer, int offset, int count)
|
|
{
|
|
if (!readingBinaryElement)
|
|
{
|
|
if (IsEmptyElement)
|
|
{
|
|
Read();
|
|
return 0;
|
|
}
|
|
|
|
ReadStartElement();
|
|
readingBinaryElement = true;
|
|
}
|
|
|
|
int i = ReadContentAsBinHex(buffer, offset, count);
|
|
|
|
if (i == 0)
|
|
{
|
|
ReadEndElement();
|
|
readingBinaryElement = false;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
public override int ReadContentAsBinHex(byte[] buffer, int offset, int count)
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsBinHex(buffer, offset, count);
|
|
}
|
|
|
|
public override bool ReadContentAsBoolean()
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsBoolean();
|
|
}
|
|
|
|
public override int ReadContentAsChars(char[] chars, int index, int count)
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsChars(chars, index, count);
|
|
}
|
|
|
|
public override DateTime ReadContentAsDateTime()
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsDateTime();
|
|
}
|
|
|
|
public override decimal ReadContentAsDecimal()
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsDecimal();
|
|
}
|
|
|
|
public override double ReadContentAsDouble()
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsDouble();
|
|
}
|
|
|
|
public override int ReadContentAsInt()
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsInt();
|
|
}
|
|
|
|
public override long ReadContentAsLong()
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsLong();
|
|
}
|
|
#if NO
|
|
public override ICollection ReadContentAsList()
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsList();
|
|
}
|
|
#endif
|
|
public override object ReadContentAsObject()
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsObject();
|
|
}
|
|
|
|
public override float ReadContentAsFloat()
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsFloat();
|
|
}
|
|
|
|
public override string ReadContentAsString()
|
|
{
|
|
AdvanceToContentOnElement();
|
|
return xmlReader.ReadContentAsString();
|
|
}
|
|
|
|
public override string ReadInnerXml()
|
|
{
|
|
return xmlReader.ReadInnerXml();
|
|
}
|
|
|
|
public override string ReadOuterXml()
|
|
{
|
|
return xmlReader.ReadOuterXml();
|
|
}
|
|
|
|
public override ReadState ReadState
|
|
{
|
|
get
|
|
{
|
|
if (xmlReader.ReadState != ReadState.Interactive && infosetReader != null)
|
|
return infosetReader.ReadState;
|
|
|
|
return xmlReader.ReadState;
|
|
}
|
|
}
|
|
|
|
public override int ReadValueChunk(char[] buffer, int index, int count)
|
|
{
|
|
return xmlReader.ReadValueChunk(buffer, index, count);
|
|
}
|
|
|
|
public override void ResolveEntity()
|
|
{
|
|
xmlReader.ResolveEntity();
|
|
}
|
|
|
|
public override XmlReaderSettings Settings
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.Settings;
|
|
}
|
|
}
|
|
|
|
public override void Skip()
|
|
{
|
|
xmlReader.Skip();
|
|
}
|
|
|
|
public override string this[int index]
|
|
{
|
|
get
|
|
{
|
|
return xmlReader[index];
|
|
}
|
|
}
|
|
|
|
public override string this[string name]
|
|
{
|
|
get
|
|
{
|
|
return xmlReader[name];
|
|
}
|
|
}
|
|
|
|
public override string this[string name, string ns]
|
|
{
|
|
get
|
|
{
|
|
return xmlReader[name, ns];
|
|
}
|
|
}
|
|
|
|
public override string Value
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.Value;
|
|
}
|
|
}
|
|
|
|
public override Type ValueType
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.ValueType;
|
|
}
|
|
}
|
|
|
|
public override string XmlLang
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.XmlLang;
|
|
}
|
|
}
|
|
|
|
public override XmlSpace XmlSpace
|
|
{
|
|
get
|
|
{
|
|
return xmlReader.XmlSpace;
|
|
}
|
|
}
|
|
|
|
public bool HasLineInfo()
|
|
{
|
|
if (xmlReader.ReadState == ReadState.Closed)
|
|
return false;
|
|
|
|
IXmlLineInfo lineInfo = xmlReader as IXmlLineInfo;
|
|
if (lineInfo == null)
|
|
return false;
|
|
return lineInfo.HasLineInfo();
|
|
}
|
|
|
|
public int LineNumber
|
|
{
|
|
get
|
|
{
|
|
if (xmlReader.ReadState == ReadState.Closed)
|
|
return 0;
|
|
|
|
IXmlLineInfo lineInfo = xmlReader as IXmlLineInfo;
|
|
if (lineInfo == null)
|
|
return 0;
|
|
return lineInfo.LineNumber;
|
|
}
|
|
}
|
|
|
|
public int LinePosition
|
|
{
|
|
get
|
|
{
|
|
if (xmlReader.ReadState == ReadState.Closed)
|
|
return 0;
|
|
|
|
IXmlLineInfo lineInfo = xmlReader as IXmlLineInfo;
|
|
if (lineInfo == null)
|
|
return 0;
|
|
return lineInfo.LinePosition;
|
|
}
|
|
}
|
|
|
|
internal class MimePart
|
|
{
|
|
Stream stream;
|
|
MimeHeaders headers;
|
|
byte[] buffer;
|
|
bool isReferencedFromInfoset;
|
|
|
|
internal MimePart(Stream stream, MimeHeaders headers)
|
|
{
|
|
this.stream = stream;
|
|
this.headers = headers;
|
|
}
|
|
|
|
internal Stream Stream
|
|
{
|
|
get { return stream; }
|
|
}
|
|
|
|
internal MimeHeaders Headers
|
|
{
|
|
get { return headers; }
|
|
}
|
|
|
|
internal bool ReferencedFromInfoset
|
|
{
|
|
get { return isReferencedFromInfoset; }
|
|
set { isReferencedFromInfoset = value; }
|
|
}
|
|
|
|
internal long Length
|
|
{
|
|
get { return stream.CanSeek ? stream.Length : 0; }
|
|
}
|
|
|
|
internal byte[] GetBuffer(int maxBuffer, ref int remaining)
|
|
{
|
|
if (buffer == null)
|
|
{
|
|
MemoryStream bufferedStream = stream.CanSeek ? new MemoryStream((int)stream.Length) : new MemoryStream();
|
|
int size = 256;
|
|
byte[] bytes = new byte[size];
|
|
|
|
int read = 0;
|
|
|
|
do
|
|
{
|
|
read = stream.Read(bytes, 0, size);
|
|
XmlMtomReader.DecrementBufferQuota(maxBuffer, ref remaining, read);
|
|
if (read > 0)
|
|
bufferedStream.Write(bytes, 0, read);
|
|
}
|
|
while (read > 0);
|
|
|
|
bufferedStream.Seek(0, SeekOrigin.Begin);
|
|
buffer = bufferedStream.GetBuffer();
|
|
stream = bufferedStream;
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
internal void Release(int maxBuffer, ref int remaining)
|
|
{
|
|
remaining += (int)this.Length;
|
|
this.headers.Release(ref remaining);
|
|
}
|
|
}
|
|
|
|
internal class XopIncludeReader : XmlDictionaryReader, IXmlLineInfo
|
|
{
|
|
int chunkSize = 4096; // Just a default. Serves as a max chunk size.
|
|
int bytesRemaining;
|
|
|
|
MimePart part;
|
|
ReadState readState;
|
|
XmlDictionaryReader parentReader;
|
|
string stringValue;
|
|
int stringOffset;
|
|
XmlNodeType nodeType;
|
|
MemoryStream binHexStream;
|
|
byte[] valueBuffer;
|
|
int valueOffset;
|
|
int valueCount;
|
|
bool finishedStream;
|
|
|
|
public XopIncludeReader(MimePart part, XmlDictionaryReader reader)
|
|
{
|
|
if (part == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("part");
|
|
if (reader == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
|
|
|
|
this.part = part;
|
|
this.parentReader = reader;
|
|
this.readState = ReadState.Initial;
|
|
this.nodeType = XmlNodeType.None;
|
|
this.chunkSize = Math.Min(reader.Quotas.MaxBytesPerRead, chunkSize);
|
|
this.bytesRemaining = this.chunkSize;
|
|
this.finishedStream = false;
|
|
}
|
|
|
|
public override XmlDictionaryReaderQuotas Quotas
|
|
{
|
|
get
|
|
{
|
|
return this.parentReader.Quotas;
|
|
}
|
|
}
|
|
|
|
public override XmlNodeType NodeType
|
|
{
|
|
get
|
|
{
|
|
return (readState == ReadState.Interactive) ? nodeType : parentReader.NodeType;
|
|
}
|
|
}
|
|
|
|
public override bool Read()
|
|
{
|
|
bool retVal = true;
|
|
switch (readState)
|
|
{
|
|
case ReadState.Initial:
|
|
readState = ReadState.Interactive;
|
|
nodeType = XmlNodeType.Text;
|
|
break;
|
|
case ReadState.Interactive:
|
|
if (this.finishedStream || (this.bytesRemaining == this.chunkSize && this.stringValue == null))
|
|
{
|
|
readState = ReadState.EndOfFile;
|
|
nodeType = XmlNodeType.EndElement;
|
|
}
|
|
else
|
|
{
|
|
this.bytesRemaining = this.chunkSize;
|
|
}
|
|
break;
|
|
case ReadState.EndOfFile:
|
|
nodeType = XmlNodeType.None;
|
|
retVal = false;
|
|
break;
|
|
}
|
|
this.stringValue = null;
|
|
this.binHexStream = null;
|
|
this.valueOffset = 0;
|
|
this.valueCount = 0;
|
|
this.stringOffset = 0;
|
|
CloseStreams();
|
|
return retVal;
|
|
}
|
|
|
|
public override int ReadValueAsBase64(byte[] buffer, int offset, int count)
|
|
{
|
|
if (buffer == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
|
|
|
|
if (offset < 0)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
if (offset > buffer.Length)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, buffer.Length)));
|
|
if (count < 0)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
if (count > buffer.Length - offset)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset)));
|
|
|
|
if (this.stringValue != null)
|
|
{
|
|
count = Math.Min(count, this.valueCount);
|
|
if (count > 0)
|
|
{
|
|
Buffer.BlockCopy(this.valueBuffer, this.valueOffset, buffer, offset, count);
|
|
this.valueOffset += count;
|
|
this.valueCount -= count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
if (this.bytesRemaining < count)
|
|
count = this.bytesRemaining;
|
|
|
|
int read = 0;
|
|
if (readState == ReadState.Interactive)
|
|
{
|
|
while (read < count)
|
|
{
|
|
int actual = part.Stream.Read(buffer, offset + read, count - read);
|
|
if (actual == 0)
|
|
{
|
|
this.finishedStream = true;
|
|
break;
|
|
}
|
|
read += actual;
|
|
}
|
|
}
|
|
this.bytesRemaining -= read;
|
|
return read;
|
|
}
|
|
|
|
public override int ReadContentAsBase64(byte[] buffer, int offset, int count)
|
|
{
|
|
if (buffer == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
|
|
|
|
if (offset < 0)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
if (offset > buffer.Length)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, buffer.Length)));
|
|
if (count < 0)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
if (count > buffer.Length - offset)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset)));
|
|
|
|
if (this.valueCount > 0)
|
|
{
|
|
count = Math.Min(count, this.valueCount);
|
|
Buffer.BlockCopy(this.valueBuffer, this.valueOffset, buffer, offset, count);
|
|
this.valueOffset += count;
|
|
this.valueCount -= count;
|
|
return count;
|
|
}
|
|
|
|
if (this.chunkSize < count)
|
|
count = this.chunkSize;
|
|
|
|
int read = 0;
|
|
if (readState == ReadState.Interactive)
|
|
{
|
|
while (read < count)
|
|
{
|
|
int actual = part.Stream.Read(buffer, offset + read, count - read);
|
|
if (actual == 0)
|
|
{
|
|
this.finishedStream = true;
|
|
if (!Read())
|
|
break;
|
|
}
|
|
read += actual;
|
|
}
|
|
}
|
|
this.bytesRemaining = this.chunkSize;
|
|
return read;
|
|
}
|
|
|
|
public override int ReadContentAsBinHex(byte[] buffer, int offset, int count)
|
|
{
|
|
if (buffer == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
|
|
|
|
if (offset < 0)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
if (offset > buffer.Length)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, buffer.Length)));
|
|
if (count < 0)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
if (count > buffer.Length - offset)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset)));
|
|
|
|
if (this.chunkSize < count)
|
|
count = this.chunkSize;
|
|
|
|
int read = 0;
|
|
int consumed = 0;
|
|
while (read < count)
|
|
{
|
|
if (binHexStream == null)
|
|
{
|
|
try
|
|
{
|
|
binHexStream = new MemoryStream(new BinHexEncoding().GetBytes(this.Value));
|
|
}
|
|
catch (FormatException e) // Wrap format exceptions from decoding document contents
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(e.Message, e));
|
|
}
|
|
}
|
|
|
|
int actual = binHexStream.Read(buffer, offset + read, count - read);
|
|
if (actual == 0)
|
|
{
|
|
this.finishedStream = true;
|
|
if (!Read())
|
|
break;
|
|
|
|
consumed = 0;
|
|
}
|
|
|
|
read += actual;
|
|
consumed += actual;
|
|
}
|
|
|
|
// Trim off the consumed chars
|
|
if (this.stringValue != null && consumed > 0)
|
|
{
|
|
this.stringValue = this.stringValue.Substring(consumed * 2);
|
|
this.stringOffset = Math.Max(0, this.stringOffset - consumed * 2);
|
|
|
|
this.bytesRemaining = this.chunkSize;
|
|
}
|
|
return read;
|
|
}
|
|
|
|
public override int ReadValueChunk(char[] chars, int offset, int count)
|
|
{
|
|
if (chars == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("chars");
|
|
|
|
if (offset < 0)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
if (offset > chars.Length)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, chars.Length)));
|
|
if (count < 0)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
if (count > chars.Length - offset)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, chars.Length - offset)));
|
|
|
|
if (readState != ReadState.Interactive)
|
|
return 0;
|
|
|
|
// Copy characters from the Value property
|
|
string str = this.Value;
|
|
count = Math.Min(stringValue.Length - stringOffset, count);
|
|
if (count > 0)
|
|
{
|
|
stringValue.CopyTo(stringOffset, chars, offset, count);
|
|
stringOffset += count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public override string Value
|
|
{
|
|
get
|
|
{
|
|
if (readState != ReadState.Interactive)
|
|
return String.Empty;
|
|
|
|
if (stringValue == null)
|
|
{
|
|
// Compute the bytes to read
|
|
int byteCount = this.bytesRemaining;
|
|
byteCount -= byteCount % 3;
|
|
|
|
// Handle trailing bytes
|
|
if (this.valueCount > 0 && this.valueOffset > 0)
|
|
{
|
|
Buffer.BlockCopy(this.valueBuffer, this.valueOffset, this.valueBuffer, 0, this.valueCount);
|
|
this.valueOffset = 0;
|
|
}
|
|
byteCount -= this.valueCount;
|
|
|
|
// Resize buffer if needed
|
|
if (this.valueBuffer == null)
|
|
{
|
|
this.valueBuffer = new byte[byteCount];
|
|
}
|
|
else if (this.valueBuffer.Length < byteCount)
|
|
{
|
|
Array.Resize(ref this.valueBuffer, byteCount);
|
|
}
|
|
byte[] buffer = this.valueBuffer;
|
|
|
|
// Fill up the buffer
|
|
int offset = 0;
|
|
int read = 0;
|
|
while (byteCount > 0)
|
|
{
|
|
read = part.Stream.Read(buffer, offset, byteCount);
|
|
if (read == 0)
|
|
{
|
|
this.finishedStream = true;
|
|
break;
|
|
}
|
|
|
|
this.bytesRemaining -= read;
|
|
this.valueCount += read;
|
|
byteCount -= read;
|
|
offset += read;
|
|
}
|
|
|
|
// Convert the bytes
|
|
this.stringValue = Convert.ToBase64String(buffer, 0, this.valueCount);
|
|
}
|
|
return this.stringValue;
|
|
}
|
|
}
|
|
|
|
public override string ReadContentAsString()
|
|
{
|
|
int stringContentQuota = this.Quotas.MaxStringContentLength;
|
|
StringBuilder sb = new StringBuilder();
|
|
do
|
|
{
|
|
string val = this.Value;
|
|
if (val.Length > stringContentQuota)
|
|
XmlExceptionHelper.ThrowMaxStringContentLengthExceeded(this, this.Quotas.MaxStringContentLength);
|
|
stringContentQuota -= val.Length;
|
|
sb.Append(val);
|
|
} while (Read());
|
|
return sb.ToString();
|
|
}
|
|
|
|
public override int AttributeCount
|
|
{
|
|
get { return 0; }
|
|
}
|
|
|
|
public override string BaseURI
|
|
{
|
|
get { return parentReader.BaseURI; }
|
|
}
|
|
|
|
public override bool CanReadBinaryContent
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
public override bool CanReadValueChunk
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
public override bool CanResolveEntity
|
|
{
|
|
get { return parentReader.CanResolveEntity; }
|
|
}
|
|
|
|
public override void Close()
|
|
{
|
|
CloseStreams();
|
|
readState = ReadState.Closed;
|
|
}
|
|
|
|
void CloseStreams()
|
|
{
|
|
if (binHexStream != null)
|
|
{
|
|
binHexStream.Close();
|
|
binHexStream = null;
|
|
}
|
|
}
|
|
|
|
public override int Depth
|
|
{
|
|
get
|
|
{
|
|
return (readState == ReadState.Interactive) ? parentReader.Depth + 1 : parentReader.Depth;
|
|
}
|
|
}
|
|
|
|
public override bool EOF
|
|
{
|
|
get { return readState == ReadState.EndOfFile; }
|
|
}
|
|
|
|
public override string GetAttribute(int index)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public override string GetAttribute(string name)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public override string GetAttribute(string name, string ns)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public override string GetAttribute(XmlDictionaryString localName, XmlDictionaryString ns)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public override bool HasAttributes
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override bool HasValue
|
|
{
|
|
get { return readState == ReadState.Interactive; }
|
|
}
|
|
|
|
public override bool IsDefault
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override bool IsEmptyElement
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override bool IsLocalName(string localName)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool IsLocalName(XmlDictionaryString localName)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool IsNamespaceUri(string ns)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool IsNamespaceUri(XmlDictionaryString ns)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool IsStartElement()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool IsStartElement(string localName)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool IsStartElement(string localName, string ns)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool IsStartElement(XmlDictionaryString localName, XmlDictionaryString ns)
|
|
{
|
|
return false;
|
|
}
|
|
#if NO
|
|
public override bool IsStartSubsetElement()
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
public override string LocalName
|
|
{
|
|
get
|
|
{
|
|
return (readState == ReadState.Interactive) ? String.Empty : parentReader.LocalName;
|
|
}
|
|
}
|
|
|
|
public override string LookupNamespace(string ns)
|
|
{
|
|
return parentReader.LookupNamespace(ns);
|
|
}
|
|
|
|
public override void MoveToAttribute(int index)
|
|
{
|
|
}
|
|
|
|
public override bool MoveToAttribute(string name)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool MoveToAttribute(string name, string ns)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool MoveToElement()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool MoveToFirstAttribute()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool MoveToNextAttribute()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override string Name
|
|
{
|
|
get
|
|
{
|
|
return (readState == ReadState.Interactive) ? String.Empty : parentReader.Name;
|
|
}
|
|
}
|
|
|
|
public override string NamespaceURI
|
|
{
|
|
get
|
|
{
|
|
return (readState == ReadState.Interactive) ? String.Empty : parentReader.NamespaceURI;
|
|
}
|
|
}
|
|
|
|
public override XmlNameTable NameTable
|
|
{
|
|
get { return parentReader.NameTable; }
|
|
}
|
|
|
|
public override string Prefix
|
|
{
|
|
get
|
|
{
|
|
return (readState == ReadState.Interactive) ? String.Empty : parentReader.Prefix;
|
|
}
|
|
}
|
|
|
|
public override char QuoteChar
|
|
{
|
|
get { return parentReader.QuoteChar; }
|
|
}
|
|
|
|
public override bool ReadAttributeValue()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override string ReadInnerXml()
|
|
{
|
|
return ReadContentAsString();
|
|
}
|
|
|
|
public override string ReadOuterXml()
|
|
{
|
|
return ReadContentAsString();
|
|
}
|
|
|
|
public override ReadState ReadState
|
|
{
|
|
get { return readState; }
|
|
}
|
|
|
|
public override void ResolveEntity()
|
|
{
|
|
}
|
|
|
|
public override XmlReaderSettings Settings
|
|
{
|
|
get { return parentReader.Settings; }
|
|
}
|
|
|
|
public override void Skip()
|
|
{
|
|
Read();
|
|
}
|
|
|
|
public override string this[int index]
|
|
{
|
|
get { return null; }
|
|
}
|
|
|
|
public override string this[string name]
|
|
{
|
|
get { return null; }
|
|
}
|
|
|
|
public override string this[string name, string ns]
|
|
{
|
|
get { return null; }
|
|
}
|
|
|
|
public override string XmlLang
|
|
{
|
|
get { return parentReader.XmlLang; }
|
|
}
|
|
|
|
public override XmlSpace XmlSpace
|
|
{
|
|
get { return parentReader.XmlSpace; }
|
|
}
|
|
|
|
public override Type ValueType
|
|
{
|
|
get
|
|
{
|
|
return (readState == ReadState.Interactive) ? typeof(byte[]) : parentReader.ValueType;
|
|
}
|
|
}
|
|
|
|
bool IXmlLineInfo.HasLineInfo()
|
|
{
|
|
return ((IXmlLineInfo)parentReader).HasLineInfo();
|
|
}
|
|
|
|
int IXmlLineInfo.LineNumber
|
|
{
|
|
get
|
|
{
|
|
return ((IXmlLineInfo)parentReader).LineNumber;
|
|
}
|
|
}
|
|
|
|
int IXmlLineInfo.LinePosition
|
|
{
|
|
get
|
|
{
|
|
return ((IXmlLineInfo)parentReader).LinePosition;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class MimeMessageReader
|
|
{
|
|
static byte[] CRLFCRLF = new byte[] { (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' };
|
|
|
|
bool getContentStreamCalled;
|
|
MimeHeaderReader mimeHeaderReader;
|
|
DelimittedStreamReader reader;
|
|
|
|
public MimeMessageReader(Stream stream)
|
|
{
|
|
if (stream == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
|
|
|
|
this.reader = new DelimittedStreamReader(stream);
|
|
this.mimeHeaderReader = new MimeHeaderReader(this.reader.GetNextStream(CRLFCRLF));
|
|
}
|
|
|
|
public Stream GetContentStream()
|
|
{
|
|
if (getContentStreamCalled)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeMessageGetContentStreamCalledAlready)));
|
|
|
|
mimeHeaderReader.Close();
|
|
|
|
Stream s = reader.GetNextStream(null);
|
|
|
|
getContentStreamCalled = true;
|
|
|
|
return s;
|
|
}
|
|
|
|
public MimeHeaders ReadHeaders(int maxBuffer, ref int remaining)
|
|
{
|
|
MimeHeaders headers = new MimeHeaders();
|
|
while (mimeHeaderReader.Read(maxBuffer, ref remaining))
|
|
{
|
|
headers.Add(mimeHeaderReader.Name, mimeHeaderReader.Value, ref remaining);
|
|
}
|
|
return headers;
|
|
}
|
|
}
|
|
|
|
internal class MimeReader
|
|
{
|
|
static byte[] CRLFCRLF = new byte[] { (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' };
|
|
|
|
byte[] boundaryBytes;
|
|
string content;
|
|
Stream currentStream;
|
|
MimeHeaderReader mimeHeaderReader;
|
|
DelimittedStreamReader reader;
|
|
byte[] scratch = new byte[2];
|
|
|
|
public MimeReader(Stream stream, string boundary)
|
|
{
|
|
if (stream == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
|
|
if (boundary == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("boundary");
|
|
|
|
this.reader = new DelimittedStreamReader(stream);
|
|
this.boundaryBytes = MimeWriter.GetBoundaryBytes(boundary);
|
|
|
|
// Need to ensure that the content begins with a CRLF, in case the
|
|
// outer construct has consumed the trailing CRLF
|
|
this.reader.Push(this.boundaryBytes, 0, 2);
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
this.reader.Close();
|
|
}
|
|
|
|
/// Gets the content preceding the first part of the MIME multi-part message
|
|
public string Preface
|
|
{
|
|
get
|
|
{
|
|
if (content == null)
|
|
{
|
|
Stream s = this.reader.GetNextStream(this.boundaryBytes);
|
|
content = new StreamReader(s, System.Text.Encoding.ASCII, false, 256).ReadToEnd();
|
|
s.Close();
|
|
if (content == null)
|
|
content = string.Empty;
|
|
}
|
|
return content;
|
|
}
|
|
}
|
|
|
|
public Stream GetContentStream()
|
|
{
|
|
Fx.Assert(content != null, "");
|
|
|
|
mimeHeaderReader.Close();
|
|
|
|
return reader.GetNextStream(this.boundaryBytes);
|
|
}
|
|
|
|
public bool ReadNextPart()
|
|
{
|
|
string content = Preface;
|
|
|
|
if (currentStream != null)
|
|
{
|
|
currentStream.Close();
|
|
currentStream = null;
|
|
}
|
|
|
|
Stream stream = reader.GetNextStream(CRLFCRLF);
|
|
|
|
if (stream == null)
|
|
return false;
|
|
|
|
if (BlockRead(stream, scratch, 0, 2) == 2)
|
|
{
|
|
if (scratch[0] == '\r' && scratch[1] == '\n')
|
|
{
|
|
if (mimeHeaderReader == null)
|
|
mimeHeaderReader = new MimeHeaderReader(stream);
|
|
else
|
|
mimeHeaderReader.Reset(stream);
|
|
return true;
|
|
}
|
|
else if (scratch[0] == '-' && scratch[1] == '-')
|
|
{
|
|
int read = BlockRead(stream, scratch, 0, 2);
|
|
|
|
if (read < 2 || (scratch[0] == '\r' && scratch[1] == '\n'))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderTruncated)));
|
|
}
|
|
|
|
public MimeHeaders ReadHeaders(int maxBuffer, ref int remaining)
|
|
{
|
|
MimeHeaders headers = new MimeHeaders();
|
|
while (mimeHeaderReader.Read(maxBuffer, ref remaining))
|
|
{
|
|
headers.Add(mimeHeaderReader.Name, mimeHeaderReader.Value, ref remaining);
|
|
}
|
|
return headers;
|
|
}
|
|
|
|
int BlockRead(Stream stream, byte[] buffer, int offset, int count)
|
|
{
|
|
int read = 0;
|
|
do
|
|
{
|
|
int r = stream.Read(buffer, offset + read, count - read);
|
|
if (r == 0)
|
|
break;
|
|
read += r;
|
|
} while (read < count);
|
|
return read;
|
|
}
|
|
|
|
}
|
|
|
|
internal class DelimittedStreamReader
|
|
{
|
|
bool canGetNextStream = true;
|
|
|
|
// used for closing the reader, and validating that only one stream can be reading at a time.
|
|
DelimittedReadStream currentStream;
|
|
|
|
byte[] delimitter;
|
|
byte[] matchBuffer;
|
|
byte[] scratch;
|
|
|
|
BufferedReadStream stream;
|
|
|
|
public DelimittedStreamReader(Stream stream)
|
|
{
|
|
this.stream = new BufferedReadStream(stream);
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
this.stream.Close();
|
|
}
|
|
|
|
// Closes the current stream. If the current stream is not the same as the caller, nothing is done.
|
|
void Close(DelimittedReadStream caller)
|
|
{
|
|
if (currentStream == caller)
|
|
{
|
|
if (delimitter == null)
|
|
{
|
|
stream.Close();
|
|
}
|
|
else
|
|
{
|
|
if (scratch == null)
|
|
{
|
|
scratch = new byte[1024];
|
|
}
|
|
while (0 != Read(caller, this.scratch, 0, this.scratch.Length));
|
|
}
|
|
|
|
currentStream = null;
|
|
}
|
|
}
|
|
|
|
// Gets the next logical stream delimitted by the given sequence.
|
|
public Stream GetNextStream(byte[] delimitter)
|
|
{
|
|
if (currentStream != null)
|
|
{
|
|
currentStream.Close();
|
|
currentStream = null;
|
|
}
|
|
|
|
if (!canGetNextStream)
|
|
return null;
|
|
|
|
this.delimitter = delimitter;
|
|
|
|
canGetNextStream = delimitter != null;
|
|
|
|
currentStream = new DelimittedReadStream(this);
|
|
|
|
return currentStream;
|
|
}
|
|
|
|
enum MatchState
|
|
{
|
|
True,
|
|
False,
|
|
InsufficientData
|
|
}
|
|
|
|
MatchState MatchDelimitter(byte[] buffer, int start, int end)
|
|
{
|
|
if (this.delimitter.Length > end - start)
|
|
{
|
|
for (int i = end - start - 1; i >= 1; i--)
|
|
{
|
|
if (buffer[start + i] != delimitter[i])
|
|
return MatchState.False;
|
|
}
|
|
return MatchState.InsufficientData;
|
|
}
|
|
for (int i = delimitter.Length - 1; i >= 1; i--)
|
|
{
|
|
if (buffer[start + i] != delimitter[i])
|
|
return MatchState.False;
|
|
}
|
|
return MatchState.True;
|
|
}
|
|
|
|
int ProcessRead(byte[] buffer, int offset, int read)
|
|
{
|
|
// nothing to process if 0 bytes were read
|
|
if (read == 0)
|
|
return read;
|
|
|
|
for (int ptr = offset, end = offset + read; ptr < end; ptr++)
|
|
{
|
|
if (buffer[ptr] == delimitter[0])
|
|
{
|
|
switch (MatchDelimitter(buffer, ptr, end))
|
|
{
|
|
case MatchState.True:
|
|
{
|
|
int actual = ptr - offset;
|
|
ptr += this.delimitter.Length;
|
|
this.stream.Push(buffer, ptr, end - ptr);
|
|
this.currentStream = null;
|
|
return actual;
|
|
}
|
|
case MatchState.False:
|
|
break;
|
|
case MatchState.InsufficientData:
|
|
{
|
|
int actual = ptr - offset;
|
|
if (actual > 0)
|
|
{
|
|
this.stream.Push(buffer, ptr, end - ptr);
|
|
return actual;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return read;
|
|
}
|
|
|
|
int Read(DelimittedReadStream caller, byte[] buffer, int offset, int count)
|
|
{
|
|
if (this.currentStream != caller)
|
|
return 0;
|
|
|
|
int read = this.stream.Read(buffer, offset, count);
|
|
if (read == 0)
|
|
{
|
|
this.canGetNextStream = false;
|
|
this.currentStream = null;
|
|
return read;
|
|
}
|
|
|
|
// If delimitter is null, read until the underlying stream returns 0 bytes
|
|
if (this.delimitter == null)
|
|
return read;
|
|
|
|
// Scans the read data for the delimitter. If found, the number of bytes read are adjusted
|
|
// to account for the number of bytes up to but not including the delimitter.
|
|
int actual = ProcessRead(buffer, offset, read);
|
|
|
|
if (actual < 0)
|
|
{
|
|
if (this.matchBuffer == null || this.matchBuffer.Length < this.delimitter.Length - read)
|
|
this.matchBuffer = new byte[this.delimitter.Length - read];
|
|
|
|
int matched = this.stream.ReadBlock(this.matchBuffer, 0, this.delimitter.Length - read);
|
|
|
|
if (MatchRemainder(read, matched))
|
|
{
|
|
this.currentStream = null;
|
|
actual = 0;
|
|
}
|
|
else
|
|
{
|
|
this.stream.Push(this.matchBuffer, 0, matched);
|
|
|
|
int i = 1;
|
|
for (; i < read; i++)
|
|
{
|
|
if (buffer[i] == this.delimitter[0])
|
|
break;
|
|
}
|
|
|
|
if (i < read)
|
|
this.stream.Push(buffer, offset + i, read - i);
|
|
|
|
actual = i;
|
|
}
|
|
}
|
|
|
|
return actual;
|
|
}
|
|
|
|
bool MatchRemainder(int start, int count)
|
|
{
|
|
if (start + count != this.delimitter.Length)
|
|
return false;
|
|
|
|
for (count--; count >= 0; count--)
|
|
{
|
|
if (this.delimitter[start + count] != this.matchBuffer[count])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
internal void Push(byte[] buffer, int offset, int count)
|
|
{
|
|
this.stream.Push(buffer, offset, count);
|
|
}
|
|
|
|
class DelimittedReadStream : Stream
|
|
{
|
|
DelimittedStreamReader reader;
|
|
|
|
public DelimittedReadStream(DelimittedStreamReader reader)
|
|
{
|
|
if (reader == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
|
|
|
|
this.reader = reader;
|
|
}
|
|
|
|
public override bool CanRead
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
public override bool CanSeek
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override bool CanWrite
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override long Length
|
|
{
|
|
#pragma warning suppress 56503 // Microsoft, required by the XmlReader
|
|
get { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, this.GetType().FullName))); }
|
|
}
|
|
|
|
public override long Position
|
|
{
|
|
get
|
|
{
|
|
#pragma warning suppress 56503 // Microsoft, required by the XmlReader
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, this.GetType().FullName)));
|
|
}
|
|
set { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, this.GetType().FullName))); }
|
|
}
|
|
|
|
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, this.GetType().FullName)));
|
|
}
|
|
|
|
public override void Close()
|
|
{
|
|
reader.Close(this);
|
|
}
|
|
|
|
public override void EndWrite(IAsyncResult asyncResult)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, this.GetType().FullName)));
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, this.GetType().FullName)));
|
|
}
|
|
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
if (buffer == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
|
|
|
|
if (offset < 0)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
if (offset > buffer.Length)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, buffer.Length)));
|
|
if (count < 0)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
if (count > buffer.Length - offset)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset)));
|
|
|
|
return reader.Read(this, buffer, offset, count);
|
|
}
|
|
|
|
public override long Seek(long offset, SeekOrigin origin)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, this.GetType().FullName)));
|
|
}
|
|
|
|
public override void SetLength(long value)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, this.GetType().FullName)));
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, this.GetType().FullName)));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
internal class MimeHeaders
|
|
{
|
|
static class Constants
|
|
{
|
|
public const string ContentTransferEncoding = "content-transfer-encoding";
|
|
public const string ContentID = "content-id";
|
|
public const string ContentType = "content-type";
|
|
public const string MimeVersion = "mime-version";
|
|
}
|
|
|
|
Dictionary<string, MimeHeader> headers = new Dictionary<string, MimeHeader>();
|
|
|
|
public MimeHeaders()
|
|
{
|
|
}
|
|
|
|
public ContentTypeHeader ContentType
|
|
{
|
|
get
|
|
{
|
|
MimeHeader header;
|
|
if (headers.TryGetValue(Constants.ContentType, out header))
|
|
return header as ContentTypeHeader;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public ContentIDHeader ContentID
|
|
{
|
|
get
|
|
{
|
|
MimeHeader header;
|
|
if (headers.TryGetValue(Constants.ContentID, out header))
|
|
return header as ContentIDHeader;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public ContentTransferEncodingHeader ContentTransferEncoding
|
|
{
|
|
get
|
|
{
|
|
MimeHeader header;
|
|
if (headers.TryGetValue(Constants.ContentTransferEncoding, out header))
|
|
return header as ContentTransferEncodingHeader;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public MimeVersionHeader MimeVersion
|
|
{
|
|
get
|
|
{
|
|
MimeHeader header;
|
|
if (headers.TryGetValue(Constants.MimeVersion, out header))
|
|
return header as MimeVersionHeader;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public void Add(string name, string value, ref int remaining)
|
|
{
|
|
if (name == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name");
|
|
|
|
if (value == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
|
|
|
|
switch (name)
|
|
{
|
|
case Constants.ContentType:
|
|
Add(new ContentTypeHeader(value));
|
|
break;
|
|
case Constants.ContentID:
|
|
Add(new ContentIDHeader(name, value));
|
|
break;
|
|
case Constants.ContentTransferEncoding:
|
|
Add(new ContentTransferEncodingHeader(value));
|
|
break;
|
|
case Constants.MimeVersion:
|
|
Add(new MimeVersionHeader(value));
|
|
break;
|
|
|
|
// Skip any fields that are not recognized
|
|
// Content-description is currently not stored since it is not used
|
|
default:
|
|
remaining += value.Length * sizeof(char);
|
|
break;
|
|
}
|
|
remaining += name.Length * sizeof(char);
|
|
}
|
|
|
|
public void Add(MimeHeader header)
|
|
{
|
|
if (header == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("header");
|
|
|
|
MimeHeader existingHeader;
|
|
if (headers.TryGetValue(header.Name, out existingHeader))
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderHeaderAlreadyExists, header.Name)));
|
|
else
|
|
headers.Add(header.Name, header);
|
|
}
|
|
|
|
public void Release(ref int remaining)
|
|
{
|
|
foreach (MimeHeader header in this.headers.Values)
|
|
{
|
|
remaining += header.Value.Length * sizeof(char);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
internal class MimeHeader
|
|
{
|
|
string name;
|
|
string value;
|
|
|
|
public MimeHeader(string name, string value)
|
|
{
|
|
if (name == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name");
|
|
|
|
this.name = name;
|
|
this.value = value;
|
|
}
|
|
|
|
public string Name
|
|
{
|
|
get
|
|
{
|
|
return this.name;
|
|
}
|
|
}
|
|
|
|
public string Value
|
|
{
|
|
get
|
|
{
|
|
return this.value;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class ContentTypeHeader : MimeHeader
|
|
{
|
|
public readonly static ContentTypeHeader Default = new ContentTypeHeader("application/octet-stream");
|
|
|
|
public ContentTypeHeader(string value)
|
|
: base("content-type", value)
|
|
{
|
|
}
|
|
|
|
string mediaType;
|
|
string subType;
|
|
Dictionary<string, string> parameters;
|
|
|
|
public string MediaType
|
|
{
|
|
get
|
|
{
|
|
if (this.mediaType == null && Value != null)
|
|
ParseValue();
|
|
|
|
return this.mediaType;
|
|
}
|
|
}
|
|
|
|
public string MediaSubtype
|
|
{
|
|
get
|
|
{
|
|
if (this.subType == null && Value != null)
|
|
ParseValue();
|
|
|
|
return this.subType;
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, string> Parameters
|
|
{
|
|
get
|
|
{
|
|
if (this.parameters == null)
|
|
{
|
|
if (Value != null)
|
|
ParseValue();
|
|
else
|
|
this.parameters = new Dictionary<string, string>();
|
|
}
|
|
return this.parameters;
|
|
}
|
|
}
|
|
|
|
void ParseValue()
|
|
{
|
|
if (this.parameters == null)
|
|
{
|
|
int offset = 0;
|
|
this.parameters = new Dictionary<string, string>();
|
|
this.mediaType = MailBnfHelper.ReadToken(Value, ref offset, null);
|
|
if (offset >= Value.Length || Value[offset++] != '/')
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeContentTypeHeaderInvalid)));
|
|
this.subType = MailBnfHelper.ReadToken(Value, ref offset, null);
|
|
|
|
while (MailBnfHelper.SkipCFWS(Value, ref offset))
|
|
{
|
|
if (offset >= Value.Length || Value[offset++] != ';')
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeContentTypeHeaderInvalid)));
|
|
|
|
if (!MailBnfHelper.SkipCFWS(Value, ref offset))
|
|
break;
|
|
|
|
string paramAttribute = MailBnfHelper.ReadParameterAttribute(Value, ref offset, null);
|
|
if (paramAttribute == null || offset >= Value.Length || Value[offset++] != '=')
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeContentTypeHeaderInvalid)));
|
|
string paramValue = MailBnfHelper.ReadParameterValue(Value, ref offset, null);
|
|
|
|
this.parameters.Add(paramAttribute.ToLowerInvariant(), paramValue);
|
|
}
|
|
|
|
if (this.parameters.ContainsKey(MtomGlobals.StartInfoParam))
|
|
{
|
|
// This allows us to maintain back compat with Orcas clients while allowing clients
|
|
// following the spec (with action inside start-info) to interop with RFC 2387
|
|
string startInfo = this.parameters[MtomGlobals.StartInfoParam];
|
|
|
|
// we're only interested in finding the action here - skipping past the content type to the first ;
|
|
int startInfoOffset = startInfo.IndexOf(';');
|
|
if (startInfoOffset > -1)
|
|
{
|
|
// keep going through the start-info string until we've reached the end of the stream
|
|
while (MailBnfHelper.SkipCFWS(startInfo, ref startInfoOffset))
|
|
{
|
|
// after having read through an attribute=value pair, we always expect to be at a ;
|
|
if (startInfo[startInfoOffset] == ';')
|
|
{
|
|
startInfoOffset++;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeContentTypeHeaderInvalid)));
|
|
}
|
|
string paramAttribute = MailBnfHelper.ReadParameterAttribute(startInfo, ref startInfoOffset, null);
|
|
if (paramAttribute == null || startInfoOffset >= startInfo.Length || startInfo[startInfoOffset++] != '=')
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeContentTypeHeaderInvalid)));
|
|
string paramValue = MailBnfHelper.ReadParameterValue(startInfo, ref startInfoOffset, null);
|
|
|
|
if (paramAttribute == MtomGlobals.ActionParam)
|
|
{
|
|
this.parameters[MtomGlobals.ActionParam] = paramValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
enum ContentTransferEncoding
|
|
{
|
|
SevenBit,
|
|
EightBit,
|
|
Binary,
|
|
Other,
|
|
Unspecified
|
|
}
|
|
|
|
internal class ContentTransferEncodingHeader : MimeHeader
|
|
{
|
|
ContentTransferEncoding contentTransferEncoding;
|
|
string contentTransferEncodingValue;
|
|
|
|
public readonly static ContentTransferEncodingHeader Binary = new ContentTransferEncodingHeader(ContentTransferEncoding.Binary, "binary");
|
|
public readonly static ContentTransferEncodingHeader EightBit = new ContentTransferEncodingHeader(ContentTransferEncoding.EightBit, "8bit");
|
|
public readonly static ContentTransferEncodingHeader SevenBit = new ContentTransferEncodingHeader(ContentTransferEncoding.SevenBit, "7bit");
|
|
|
|
public ContentTransferEncodingHeader(string value)
|
|
: base("content-transfer-encoding", value.ToLowerInvariant())
|
|
{
|
|
}
|
|
|
|
public ContentTransferEncodingHeader(ContentTransferEncoding contentTransferEncoding, string value)
|
|
: base("content-transfer-encoding", null)
|
|
{
|
|
this.contentTransferEncoding = contentTransferEncoding;
|
|
this.contentTransferEncodingValue = value;
|
|
}
|
|
|
|
public ContentTransferEncoding ContentTransferEncoding
|
|
{
|
|
get
|
|
{
|
|
ParseValue();
|
|
return this.contentTransferEncoding;
|
|
}
|
|
}
|
|
|
|
public string ContentTransferEncodingValue
|
|
{
|
|
get
|
|
{
|
|
ParseValue();
|
|
return this.contentTransferEncodingValue;
|
|
}
|
|
}
|
|
|
|
void ParseValue()
|
|
{
|
|
if (this.contentTransferEncodingValue == null)
|
|
{
|
|
int offset = 0;
|
|
this.contentTransferEncodingValue = (Value.Length == 0) ? Value : ((Value[0] == '"') ? MailBnfHelper.ReadQuotedString(Value, ref offset, null) : MailBnfHelper.ReadToken(Value, ref offset, null));
|
|
switch (this.contentTransferEncodingValue)
|
|
{
|
|
case "7bit":
|
|
this.contentTransferEncoding = ContentTransferEncoding.SevenBit;
|
|
break;
|
|
case "8bit":
|
|
this.contentTransferEncoding = ContentTransferEncoding.EightBit;
|
|
break;
|
|
case "binary":
|
|
this.contentTransferEncoding = ContentTransferEncoding.Binary;
|
|
break;
|
|
default:
|
|
this.contentTransferEncoding = ContentTransferEncoding.Other;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class ContentIDHeader : MimeHeader
|
|
{
|
|
public ContentIDHeader(string name, string value)
|
|
: base(name, value)
|
|
{
|
|
}
|
|
}
|
|
|
|
internal class MimeVersionHeader : MimeHeader
|
|
{
|
|
public static readonly MimeVersionHeader Default = new MimeVersionHeader("1.0");
|
|
|
|
public MimeVersionHeader(string value)
|
|
: base("mime-version", value)
|
|
{
|
|
}
|
|
|
|
string version;
|
|
|
|
public string Version
|
|
{
|
|
get
|
|
{
|
|
if (this.version == null && Value != null)
|
|
ParseValue();
|
|
return this.version;
|
|
}
|
|
}
|
|
|
|
void ParseValue()
|
|
{
|
|
// shortcut for the most common case.
|
|
if (Value == "1.0")
|
|
{
|
|
this.version = "1.0";
|
|
}
|
|
else
|
|
{
|
|
int offset = 0;
|
|
|
|
if (!MailBnfHelper.SkipCFWS(Value, ref offset))
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeVersionHeaderInvalid)));
|
|
|
|
StringBuilder builder = new StringBuilder();
|
|
MailBnfHelper.ReadDigits(Value, ref offset, builder);
|
|
|
|
if ((!MailBnfHelper.SkipCFWS(Value, ref offset) || offset >= Value.Length || Value[offset++] != '.') || !MailBnfHelper.SkipCFWS(Value, ref offset))
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeVersionHeaderInvalid)));
|
|
|
|
builder.Append('.');
|
|
|
|
MailBnfHelper.ReadDigits(Value, ref offset, builder);
|
|
|
|
this.version = builder.ToString();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class MimeHeaderReader
|
|
{
|
|
enum ReadState
|
|
{
|
|
ReadName,
|
|
SkipWS,
|
|
ReadValue,
|
|
ReadLF,
|
|
ReadWS,
|
|
EOF
|
|
}
|
|
|
|
string value;
|
|
byte[] buffer = new byte[1024];
|
|
int maxOffset;
|
|
string name;
|
|
int offset;
|
|
ReadState readState = ReadState.ReadName;
|
|
Stream stream;
|
|
|
|
public MimeHeaderReader(Stream stream)
|
|
{
|
|
if (stream == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
|
|
|
|
this.stream = stream;
|
|
}
|
|
|
|
public string Value
|
|
{
|
|
get
|
|
{
|
|
return value;
|
|
}
|
|
}
|
|
|
|
public string Name
|
|
{
|
|
get
|
|
{
|
|
return name;
|
|
}
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
stream.Close();
|
|
readState = ReadState.EOF;
|
|
}
|
|
|
|
public bool Read(int maxBuffer, ref int remaining)
|
|
{
|
|
name = null;
|
|
value = null;
|
|
|
|
while (readState != ReadState.EOF)
|
|
{
|
|
if (offset == maxOffset)
|
|
{
|
|
maxOffset = stream.Read(this.buffer, 0, this.buffer.Length);
|
|
offset = 0;
|
|
if (BufferEnd())
|
|
break;
|
|
}
|
|
if (ProcessBuffer(maxBuffer, ref remaining))
|
|
break;
|
|
}
|
|
|
|
return value != null;
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Calls unsafe code", Safe = "Demands for FullTrust")]
|
|
[SecuritySafeCritical]
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true)]
|
|
bool ProcessBuffer(int maxBuffer, ref int remaining)
|
|
{
|
|
unsafe
|
|
{
|
|
fixed (byte* pBuffer = this.buffer)
|
|
{
|
|
byte* start = pBuffer + this.offset;
|
|
byte* end = pBuffer + this.maxOffset;
|
|
byte* ptr = start;
|
|
|
|
switch (readState)
|
|
{
|
|
case ReadState.ReadName:
|
|
for (; ptr < end; ptr++)
|
|
{
|
|
if (*ptr == ':')
|
|
{
|
|
AppendName(new string((sbyte*)start, 0, (int)(ptr - start)), maxBuffer, ref remaining);
|
|
ptr++;
|
|
goto case ReadState.SkipWS;
|
|
}
|
|
else
|
|
{
|
|
// convert to lower case up front.
|
|
if (*ptr >= 'A' && *ptr <= 'Z')
|
|
{
|
|
*ptr += 'a' - 'A';
|
|
}
|
|
else if (*ptr < 33 || *ptr > 126)
|
|
{
|
|
if (name == null && *ptr == (byte)'\r')
|
|
{
|
|
ptr++;
|
|
if (ptr >= end || *ptr != '\n')
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderMalformedHeader)));
|
|
goto case ReadState.EOF;
|
|
}
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, (char)(*ptr), ((int)(*ptr)).ToString("X", CultureInfo.InvariantCulture))));
|
|
}
|
|
}
|
|
}
|
|
AppendName(new string((sbyte*)start, 0, (int)(ptr - start)), maxBuffer, ref remaining);
|
|
readState = ReadState.ReadName;
|
|
break;
|
|
case ReadState.SkipWS:
|
|
for (; ptr < end; ptr++)
|
|
if (*ptr != (byte)'\t' && *ptr != ' ')
|
|
goto case ReadState.ReadValue;
|
|
readState = ReadState.SkipWS;
|
|
break;
|
|
case ReadState.ReadValue:
|
|
start = ptr;
|
|
for (; ptr < end; ptr++)
|
|
{
|
|
if (*ptr == (byte)'\r')
|
|
{
|
|
AppendValue(new string((sbyte*)start, 0, (int)(ptr - start)), maxBuffer, ref remaining);
|
|
ptr++;
|
|
goto case ReadState.ReadLF;
|
|
}
|
|
else if (*ptr == (byte)'\n')
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderMalformedHeader)));
|
|
}
|
|
}
|
|
AppendValue(new string((sbyte*)start, 0, (int)(ptr - start)), maxBuffer, ref remaining);
|
|
readState = ReadState.ReadValue;
|
|
break;
|
|
case ReadState.ReadLF:
|
|
if (ptr < end)
|
|
{
|
|
if (*ptr != (byte)'\n')
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderMalformedHeader)));
|
|
ptr++;
|
|
goto case ReadState.ReadWS;
|
|
}
|
|
readState = ReadState.ReadLF;
|
|
break;
|
|
case ReadState.ReadWS:
|
|
if (ptr < end)
|
|
{
|
|
if (*ptr != (byte)' ' && *ptr != (byte)'\t')
|
|
{
|
|
readState = ReadState.ReadName;
|
|
offset = (int)(ptr - pBuffer);
|
|
return true;
|
|
}
|
|
goto case ReadState.ReadValue;
|
|
}
|
|
readState = ReadState.ReadWS;
|
|
break;
|
|
case ReadState.EOF:
|
|
readState = ReadState.EOF;
|
|
offset = (int)(ptr - pBuffer);
|
|
return true;
|
|
}
|
|
offset = (int)(ptr - pBuffer);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BufferEnd()
|
|
{
|
|
if (maxOffset == 0)
|
|
{
|
|
if (readState != ReadState.ReadWS && readState != ReadState.ReadValue)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderMalformedHeader)));
|
|
|
|
readState = ReadState.EOF;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Resets the mail field reader to the new stream to reuse buffers
|
|
public void Reset(Stream stream)
|
|
{
|
|
if (stream == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
|
|
|
|
if (readState != ReadState.EOF)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MimeReaderResetCalledBeforeEOF)));
|
|
|
|
this.stream = stream;
|
|
readState = ReadState.ReadName;
|
|
maxOffset = 0;
|
|
offset = 0;
|
|
}
|
|
|
|
// helper methods
|
|
|
|
void AppendValue(string value, int maxBuffer, ref int remaining)
|
|
{
|
|
XmlMtomReader.DecrementBufferQuota(maxBuffer, ref remaining, value.Length * sizeof(char));
|
|
if (this.value == null)
|
|
this.value = value;
|
|
else
|
|
this.value += value;
|
|
}
|
|
|
|
void AppendName(string value, int maxBuffer, ref int remaining)
|
|
{
|
|
XmlMtomReader.DecrementBufferQuota(maxBuffer, ref remaining, value.Length * sizeof(char));
|
|
if (this.name == null)
|
|
this.name = value;
|
|
else
|
|
this.name += value;
|
|
}
|
|
|
|
}
|
|
|
|
internal class BufferedReadStream : Stream
|
|
{
|
|
Stream stream;
|
|
byte[] storedBuffer;
|
|
int storedLength;
|
|
int storedOffset;
|
|
bool readMore;
|
|
|
|
public BufferedReadStream(Stream stream)
|
|
: this(stream, false)
|
|
{
|
|
}
|
|
|
|
public BufferedReadStream(Stream stream, bool readMore)
|
|
{
|
|
if (stream == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
|
|
|
|
this.stream = stream;
|
|
this.readMore = readMore;
|
|
}
|
|
|
|
public override bool CanWrite
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override bool CanSeek
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override bool CanRead
|
|
{
|
|
get { return stream.CanRead; }
|
|
}
|
|
|
|
public override long Length
|
|
{
|
|
get
|
|
{
|
|
#pragma warning suppress 56503 // Microsoft, required by the Stream contract
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, stream.GetType().FullName)));
|
|
}
|
|
}
|
|
|
|
public override long Position
|
|
{
|
|
get
|
|
{
|
|
#pragma warning suppress 56503 // Microsoft, required by the Stream contract
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, stream.GetType().FullName)));
|
|
}
|
|
set
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, stream.GetType().FullName)));
|
|
}
|
|
}
|
|
|
|
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
|
{
|
|
if (!CanRead)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ReadNotSupportedOnStream, stream.GetType().FullName)));
|
|
|
|
return stream.BeginRead(buffer, offset, count, callback, state);
|
|
}
|
|
|
|
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, stream.GetType().FullName)));
|
|
}
|
|
|
|
public override void Close()
|
|
{
|
|
stream.Close();
|
|
}
|
|
|
|
public override int EndRead(IAsyncResult asyncResult)
|
|
{
|
|
if (!CanRead)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ReadNotSupportedOnStream, stream.GetType().FullName)));
|
|
|
|
return stream.EndRead(asyncResult);
|
|
}
|
|
|
|
public override void EndWrite(IAsyncResult asyncResult)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, stream.GetType().FullName)));
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
stream.Flush();
|
|
}
|
|
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
if (!CanRead)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ReadNotSupportedOnStream, stream.GetType().FullName)));
|
|
|
|
if (buffer == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
|
|
|
|
if (offset < 0)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
if (offset > buffer.Length)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, buffer.Length)));
|
|
if (count < 0)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
if (count > buffer.Length - offset)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset)));
|
|
|
|
int read = 0;
|
|
if (this.storedOffset < this.storedLength)
|
|
{
|
|
read = Math.Min(count, this.storedLength - this.storedOffset);
|
|
Buffer.BlockCopy(this.storedBuffer, this.storedOffset, buffer, offset, read);
|
|
this.storedOffset += read;
|
|
if (read == count || !this.readMore)
|
|
return read;
|
|
offset += read;
|
|
count -= read;
|
|
}
|
|
return read + stream.Read(buffer, offset, count);
|
|
}
|
|
|
|
public override int ReadByte()
|
|
{
|
|
if (this.storedOffset < this.storedLength)
|
|
return (int)this.storedBuffer[this.storedOffset++];
|
|
else
|
|
return base.ReadByte();
|
|
}
|
|
|
|
public int ReadBlock(byte[] buffer, int offset, int count)
|
|
{
|
|
int read;
|
|
int total = 0;
|
|
while (total < count && (read = Read(buffer, offset + total, count - total)) != 0)
|
|
{
|
|
total += read;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
public void Push(byte[] buffer, int offset, int count)
|
|
{
|
|
if (count == 0)
|
|
return;
|
|
|
|
if (this.storedOffset == this.storedLength)
|
|
{
|
|
if (this.storedBuffer == null || this.storedBuffer.Length < count)
|
|
this.storedBuffer = new byte[count];
|
|
this.storedOffset = 0;
|
|
this.storedLength = count;
|
|
}
|
|
else
|
|
{
|
|
// if there's room to just insert before existing data
|
|
if (count <= this.storedOffset)
|
|
this.storedOffset -= count;
|
|
// if there's room in the buffer but need to shift things over
|
|
else if (count <= this.storedBuffer.Length - this.storedLength + this.storedOffset)
|
|
{
|
|
Buffer.BlockCopy(this.storedBuffer, this.storedOffset, this.storedBuffer, count, this.storedLength - this.storedOffset);
|
|
this.storedLength += count - this.storedOffset;
|
|
this.storedOffset = 0;
|
|
}
|
|
else
|
|
{
|
|
byte[] newBuffer = new byte[count + this.storedLength - this.storedOffset];
|
|
Buffer.BlockCopy(this.storedBuffer, this.storedOffset, newBuffer, count, this.storedLength - this.storedOffset);
|
|
this.storedLength += count - this.storedOffset;
|
|
this.storedOffset = 0;
|
|
this.storedBuffer = newBuffer;
|
|
}
|
|
}
|
|
Buffer.BlockCopy(buffer, offset, this.storedBuffer, this.storedOffset, count);
|
|
}
|
|
|
|
public override long Seek(long offset, SeekOrigin origin)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, stream.GetType().FullName)));
|
|
}
|
|
|
|
public override void SetLength(long value)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupportedOnStream, stream.GetType().FullName)));
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.WriteNotSupportedOnStream, stream.GetType().FullName)));
|
|
}
|
|
}
|
|
|
|
internal static class MailBnfHelper
|
|
{
|
|
static bool[] s_fqtext = new bool[128];
|
|
static bool[] s_ttext = new bool[128];
|
|
static bool[] s_digits = new bool[128];
|
|
static bool[] s_boundary = new bool[128];
|
|
|
|
static MailBnfHelper()
|
|
{
|
|
// fqtext = %d1-9 / %d11 / %d12 / %d14-33 / %d35-91 / %d93-127
|
|
for (int i = 1; i <= 9; i++) { s_fqtext[i] = true; }
|
|
s_fqtext[11] = true;
|
|
s_fqtext[12] = true;
|
|
for (int i = 14; i <= 33; i++) { s_fqtext[i] = true; }
|
|
for (int i = 35; i <= 91; i++) { s_fqtext[i] = true; }
|
|
for (int i = 93; i <= 127; i++) { s_fqtext[i] = true; }
|
|
|
|
// ttext = %d33-126 except '()<>@,;:\"/[]?='
|
|
for (int i = 33; i <= 126; i++) { s_ttext[i] = true; }
|
|
s_ttext['('] = false;
|
|
s_ttext[')'] = false;
|
|
s_ttext['<'] = false;
|
|
s_ttext['>'] = false;
|
|
s_ttext['@'] = false;
|
|
s_ttext[','] = false;
|
|
s_ttext[';'] = false;
|
|
s_ttext[':'] = false;
|
|
s_ttext['\\'] = false;
|
|
s_ttext['"'] = false;
|
|
s_ttext['/'] = false;
|
|
s_ttext['['] = false;
|
|
s_ttext[']'] = false;
|
|
s_ttext['?'] = false;
|
|
s_ttext['='] = false;
|
|
|
|
// digits = %d48-57
|
|
for (int i = 48; i <= 57; i++)
|
|
s_digits[i] = true;
|
|
|
|
// boundary = DIGIT / ALPHA / "'" / "(" / ")" / "+" / "_" / "," / "-" / "." / "/" / ":" / "=" / "?" / " "
|
|
// cannot end with " "
|
|
for (int i = '0'; i <= '9'; i++) { s_boundary[i] = true; }
|
|
for (int i = 'A'; i <= 'Z'; i++) { s_boundary[i] = true; }
|
|
for (int i = 'a'; i <= 'z'; i++) { s_boundary[i] = true; }
|
|
s_boundary['\''] = true;
|
|
s_boundary['('] = true;
|
|
s_boundary[')'] = true;
|
|
s_boundary['+'] = true;
|
|
s_boundary['_'] = true;
|
|
s_boundary[','] = true;
|
|
s_boundary['-'] = true;
|
|
s_boundary['.'] = true;
|
|
s_boundary['/'] = true;
|
|
s_boundary[':'] = true;
|
|
s_boundary['='] = true;
|
|
s_boundary['?'] = true;
|
|
s_boundary[' '] = true;
|
|
}
|
|
|
|
public static bool SkipCFWS(string data, ref int offset)
|
|
{
|
|
int comments = 0;
|
|
for (; offset < data.Length; offset++)
|
|
{
|
|
if (data[offset] > 127)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, data[offset], ((int)data[offset]).ToString("X", CultureInfo.InvariantCulture))));
|
|
else if (data[offset] == '\\' && comments > 0)
|
|
offset += 2;
|
|
else if (data[offset] == '(')
|
|
comments++;
|
|
else if (data[offset] == ')')
|
|
comments--;
|
|
else if (data[offset] != ' ' && data[offset] != '\t' && comments == 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static string ReadQuotedString(string data, ref int offset, StringBuilder builder)
|
|
{
|
|
// assume first char is the opening quote
|
|
int start = ++offset;
|
|
StringBuilder localBuilder = (builder != null ? builder : new StringBuilder());
|
|
for (; offset < data.Length; offset++)
|
|
{
|
|
if (data[offset] == '\\')
|
|
{
|
|
localBuilder.Append(data, start, offset - start);
|
|
start = ++offset;
|
|
continue;
|
|
}
|
|
else if (data[offset] == '"')
|
|
{
|
|
localBuilder.Append(data, start, offset - start);
|
|
offset++;
|
|
return (builder != null ? null : localBuilder.ToString());
|
|
}
|
|
else if (!(data[offset] < s_fqtext.Length && s_fqtext[data[offset]]))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, data[offset], ((int)data[offset]).ToString("X", CultureInfo.InvariantCulture))));
|
|
}
|
|
}
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeReaderMalformedHeader)));
|
|
}
|
|
|
|
public static string ReadParameterAttribute(string data, ref int offset, StringBuilder builder)
|
|
{
|
|
if (!SkipCFWS(data, ref offset))
|
|
return null;
|
|
|
|
return ReadToken(data, ref offset, null);
|
|
}
|
|
|
|
public static string ReadParameterValue(string data, ref int offset, StringBuilder builder)
|
|
{
|
|
if (!SkipCFWS(data, ref offset))
|
|
return string.Empty;
|
|
|
|
if (offset < data.Length && data[offset] == '"')
|
|
return ReadQuotedString(data, ref offset, builder);
|
|
else
|
|
return ReadToken(data, ref offset, builder);
|
|
}
|
|
|
|
public static string ReadToken(string data, ref int offset, StringBuilder builder)
|
|
{
|
|
int start = offset;
|
|
for (; offset < data.Length; offset++)
|
|
{
|
|
if (data[offset] > s_ttext.Length)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.MimeHeaderInvalidCharacter, data[offset], ((int)data[offset]).ToString("X", CultureInfo.InvariantCulture))));
|
|
}
|
|
else if (!s_ttext[data[offset]])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return data.Substring(start, offset - start);
|
|
}
|
|
|
|
public static string ReadDigits(string data, ref int offset, StringBuilder builder)
|
|
{
|
|
int start = offset;
|
|
StringBuilder localBuilder = (builder != null ? builder : new StringBuilder());
|
|
for (; offset < data.Length && data[offset] < s_digits.Length && s_digits[data[offset]]; offset++);
|
|
localBuilder.Append(data, start, offset - start);
|
|
return (builder != null ? null : localBuilder.ToString());
|
|
}
|
|
|
|
public static bool IsValidMimeBoundary(string data)
|
|
{
|
|
int length = (data == null) ? 0 : data.Length;
|
|
if (length == 0 || length > 70 || data[length - 1] == ' ')
|
|
return false;
|
|
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
if (!(data[i] < s_boundary.Length && s_boundary[data[i]]))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|