5using System.Threading.Tasks;
39 private static readonly
string[] contentTypes =
new string[] {
ContentType };
77 KeyValuePair<string, string>[] Fields, Uri BaseUri,
ICodecProgress Progress)
79 Dictionary<string, object> Form =
new Dictionary<string, object>();
81 Exception Error = await
Decode(Data, Fields, Form,
null, BaseUri, Progress);
99 public static async Task<Exception>
Decode(
byte[] Data, KeyValuePair<string, string>[] Fields, Dictionary<string, object> Form,
102 string Boundary =
null;
104 if (!(Fields is
null))
106 foreach (KeyValuePair<string, string> P
in Fields)
108 if (
string.Compare(P.Key,
"BOUNDARY",
true) == 0)
116 if (
string.IsNullOrEmpty(Boundary))
117 return new Exception(
"No boundary defined.");
120 byte[] BoundaryBin = Encoding.ASCII.GetBytes(Boundary);
124 int d = BoundaryBin.Length;
130 for (j = 0; j < d; j++)
132 if (Data[i + j] != BoundaryBin[j])
138 Error = await AddPart(Data, Start, i,
false, Form, List, BaseUri, Progress);
139 if (!(Error is
null))
143 while (i < c && Data[i] <= 32)
154 Error = await AddPart(Data, Start, c,
true, Form, List, BaseUri, Progress);
155 if (!(Error is
null))
162 private static async Task<Exception> AddPart(
byte[] Data,
int Start,
int i,
bool Last,
168 for (j = Start; j < Max; j++)
170 if (Data[j] ==
'\r' && Data[j + 1] ==
'\n' && Data[j + 2] ==
'\r' && Data[j + 3] ==
'\n')
180 if (i >= 2 && Data[i - 1] ==
'-' && Data[i - 2] ==
'-')
184 if (i >= 4 && Data[i - 1 - k] ==
'\n' && Data[i - 2 - k] ==
'\r')
188 int NrBytes = i - j - 4 - k;
189 if (NrBytes < 0 || (NrBytes == 0 && Last))
192 string Header = Encoding.ASCII.GetString(Data, Start, j - Start);
194 byte[] Data2 =
new byte[NrBytes];
195 EmbeddedContent EmbeddedContent =
new EmbeddedContent()
201 Buffer.BlockCopy(Data, j + 4, Data2, 0, NrBytes);
203 string[] Rows = Header.Split(
CommonTypes.
CRLF, StringSplitOptions.RemoveEmptyEntries);
207 for (j = 0; j < l; j++)
210 if (!
string.IsNullOrEmpty(Key))
212 if (
char.IsWhiteSpace(Key[0]) && m >= 0)
215 Rows[j] =
string.Empty;
222 foreach (
string Row
in Rows)
224 j = Row.IndexOf(
':');
228 Key = Row.Substring(0, j).Trim();
229 Value = Row.Substring(j + 1).Trim();
231 switch (Key.ToUpper())
234 EmbeddedContent.ContentType = Value;
235 j = Value.IndexOf(
';');
238 ParseContentFields(Value.Substring(j + 1).Trim(), EmbeddedContent);
239 Value = Value.Substring(0, j).Trim();
243 case "CONTENT-DISPOSITION":
244 j = Value.IndexOf(
';');
247 ParseContentFields(Value.Substring(j + 1).Trim(), EmbeddedContent);
248 Value = Value.Substring(0, j).Trim();
251 switch (Value.ToUpper())
267 case "CONTENT-TRANSFER-ENCODING":
268 EmbeddedContent.TransferEncoding = Value;
272 EmbeddedContent.ID = Value;
275 case "CONTENT-DESCRIPTION":
276 EmbeddedContent.Description = Value;
281 if (!
string.IsNullOrEmpty(EmbeddedContent.TransferEncoding))
284 EmbeddedContent.TransferDecoded = Data2;
286 return new Exception(
"Unrecognized Content-Transfer-Encoding: " + EmbeddedContent.TransferEncoding);
293 EmbeddedContent.Decoded = Data2;
295 EmbeddedContent.Decoded = Item.
Decoded;
299 EmbeddedContent.Decoded = Data2;
304 Form[EmbeddedContent.Name] = EmbeddedContent.Decoded;
305 Form[EmbeddedContent.Name +
"_Binary"] = Data2;
307 if (!
string.IsNullOrEmpty(EmbeddedContent.ContentType))
308 Form[EmbeddedContent.Name +
"_ContentType"] = EmbeddedContent.ContentType;
310 if (!
string.IsNullOrEmpty(EmbeddedContent.FileName))
311 Form[EmbeddedContent.Name +
"_FileName"] = EmbeddedContent.FileName;
314 List?.
Add(EmbeddedContent);
320 private static void ParseContentFields(
string s, EmbeddedContent EmbeddedContent)
324 switch (Field.Key.ToUpper())
327 EmbeddedContent.Name = Field.Value;
331 EmbeddedContent.FileName = Field.Value;
335 if (
int.TryParse(Field.Value, out
int i))
336 EmbeddedContent.Size = i;
339 case "CREATION-DATE":
341 EmbeddedContent.CreationDate = DTO;
344 case "MODIFICATION-DATE":
346 EmbeddedContent.ModificationDate = DTO;
359 public static bool TryTransferDecode(
byte[] Encoded,
string TransferEncoding, out
byte[] Decoded)
361 switch (TransferEncoding.ToUpper())
373 Decoded = Convert.FromBase64String(s);
376 case "QUOTED-PRINTABLE":
377 MemoryStream ms =
new MemoryStream();
382 for (j = 0, k = Encoded.Length; j < k; j++)
386 if (b == (
byte)
'=' && j + 2 < k)
388 ch = (char)Encoded[++j];
390 if (ch >=
'0' && ch <=
'9')
391 b = (byte)(ch -
'0');
392 else if (ch >=
'a' && ch <=
'f')
393 b = (byte)(ch -
'a' + 10);
394 else if (ch >=
'A' && ch <=
'F')
395 b = (byte)(ch -
'A' + 10);
398 if (Encoded[j + 1] == (
byte)
'\n')
405 ms.WriteByte((
byte)
'=');
406 ms.WriteByte((
byte)ch);
412 ch = (char)Encoded[++j];
414 if (ch >=
'0' && ch <=
'9')
415 b |= (byte)(ch -
'0');
416 else if (ch >=
'a' && ch <=
'f')
417 b |= (byte)(ch -
'a' + 10);
418 else if (ch >=
'A' && ch <=
'F')
419 b |= (byte)(ch -
'A' + 10);
425 Decoded = ms.ToArray();
443 if (
string.Compare(FileExtension,
"formdata",
true) == 0)
466 FileExtension =
"formdata";
470 FileExtension =
string.Empty;
481 public static async Task<KeyValuePair<byte[], string>>
Encode(IEnumerable<EmbeddedContent> Content,
484 string Boundary = Guid.NewGuid().ToString();
485 string ContentType = FormDataDecoder.ContentType +
"; boundary=\"" + Boundary +
"\"";
486 return new KeyValuePair<byte[], string>(await
Encode(Content, Boundary, Progress),
ContentType);
496 public static async Task<byte[]>
Encode(IEnumerable<EmbeddedContent> Content,
string Boundary,
499 using (MemoryStream ms =
new MemoryStream())
501 StringBuilder Header =
new StringBuilder();
506 await Alternative.AssertEncoded();
509 Header.Append(
"\r\n--");
510 Header.Append(Boundary);
514 Header.Append(
"\r\nContent-Transfer-Encoding: ");
518 Header.Append(
"\r\nContent-Type: ");
523 Header.Append(
"; name=\"");
524 Header.Append(Alternative.
Name.Replace(
"\"",
"\\\""));
529 !
string.IsNullOrEmpty(Alternative.
FileName))
531 Header.Append(
"\r\nContent-Disposition: ");
536 Header.Append(
"form-data");
538 if (!
string.IsNullOrEmpty(Alternative.
Name))
540 Header.Append(
"; name=\"");
541 Header.Append(Alternative.
Name.Replace(
"\"",
"\\\""));
547 Header.Append(
"inline");
552 Header.Append(
"attachment");
556 if (!
string.IsNullOrEmpty(Alternative.
FileName))
558 Header.Append(
"; filename=\"");
559 Header.Append(Alternative.
FileName.Replace(
"\"",
"\\\""));
564 if (!
string.IsNullOrEmpty(Alternative.
ID))
566 Header.Append(
"\r\nContent-ID: ");
567 Header.Append(Alternative.
ID);
570 if (!
string.IsNullOrEmpty(Alternative.
Description))
572 Header.Append(
"\r\nContent-Description: ");
576 Header.Append(
"\r\n\r\n");
578 HeaderBin = Encoding.ASCII.GetBytes(Header.ToString());
579 ms.Write(HeaderBin, 0, HeaderBin.Length);
580 ms.Write(Alternative.
Raw, 0, Alternative.
Raw.Length);
585 Header.Append(
"\r\n--");
586 Header.Append(Boundary);
589 HeaderBin = Encoding.ASCII.GetBytes(Header.ToString());
590 ms.Write(HeaderBin, 0, HeaderBin.Length);
Helps with parsing of commong data types.
static readonly char[] CRLF
Contains the CR LF character sequence.
static KeyValuePair< string, string >[] ParseFieldValues(string Value)
Parses a set of comma or semicolon-separated field values, optionaly delimited by ' or " characters.
static bool TryParseRfc822(string s, out DateTimeOffset Value)
Parses a date and time value encoded according to RFC 822, §5.
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
Represents content embedded in other content.
string FileName
Filename of embedded object.
ContentDisposition Disposition
Disposition of embedded object.
string Name
Name of embedded object.
string Description
Content-Description of embedded object, if defined.
string ContentType
Content-Type of embedded object.
byte[] Raw
Raw, untransformed body of embedded object.
string TransferEncoding
Content Transfer Encoding of embedded object, if defined. Affects how Raw is transformed into Transfe...
string ID
Content-ID of embedded object, if defined.
static async Task< KeyValuePair< byte[], string > > Encode(IEnumerable< EmbeddedContent > Content, ICodecProgress Progress)
Encodes multi-part form data
bool TryGetContentType(string FileExtension, out string ContentType)
Tries to get the content type of an item, given its file extension.
string[] FileExtensions
Supported file extensions.
bool TryGetFileExtension(string ContentType, out string FileExtension)
Tries to get the file extension of an item, given its Content-Type.
FormDataDecoder()
Decoder of form data.
static async Task< Exception > Decode(byte[] Data, KeyValuePair< string, string >[] Fields, Dictionary< string, object > Form, ChunkedList< EmbeddedContent > List, Uri BaseUri, ICodecProgress Progress)
Decodes a multipart object
static async Task< byte[]> Encode(IEnumerable< EmbeddedContent > Content, string Boundary, ICodecProgress Progress)
Encodes multi-part content
bool Decodes(string ContentType, out Grade Grade)
If the decoder decodes an object with a given content type.
async Task< ContentResponse > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri, ICodecProgress Progress)
Decodes an object.
static bool TryTransferDecode(byte[] Encoded, string TransferEncoding, out byte[] Decoded)
Tries to decode transfer-encoded binary data.
const string ContentType
multipart/form-data
string[] ContentTypes
Supported content types.
Plain text encoder/decoder.
const string DefaultContentType
text/plain
A chunked list is a linked list of chunks of objects of type T .
void Add(T Item)
Adds an item to the collection.
Static class managing binary representations of strings.
static string GetString(byte[] Data, int Offset, int Count, Encoding DefaultEncoding)
Gets a string from its binary representation, taking any Byte Order Mark (BOM) into account.
Interface for reporting progress about an encoding or decoding.
Basic interface for Internet Content decoders. A class implementing this interface and having a defau...
ContentDisposition
Content disposition