2using System.Collections.Generic;
5using System.Threading.Tasks;
72 public async Task<object>
DecodeAsync(
string ContentType,
byte[] Data, Encoding Encoding, KeyValuePair<string, string>[] Fields, Uri BaseUri)
74 Dictionary<string, object> Form =
new Dictionary<string, object>();
76 await
Decode(Data, Fields, Form,
null, BaseUri);
89 public static async Task
Decode(
byte[] Data, KeyValuePair<string, string>[] Fields, Dictionary<string, object> Form,
90 List<EmbeddedContent> List, Uri BaseUri)
92 string Boundary =
null;
94 if (!(Fields is
null))
96 foreach (KeyValuePair<string, string> P
in Fields)
98 if (
string.Compare(P.Key,
"BOUNDARY",
true) == 0)
106 if (
string.IsNullOrEmpty(Boundary))
107 throw new Exception(
"No boundary defined.");
109 byte[] BoundaryBin = Encoding.ASCII.GetBytes(Boundary);
113 int d = BoundaryBin.Length;
119 for (j = 0; j < d; j++)
121 if (Data[i + j] != BoundaryBin[j])
127 await AddPart(Data, Start, i,
false, Form, List, BaseUri);
130 while (i < c && Data[i] <= 32)
140 await AddPart(Data, Start, c,
true, Form, List, BaseUri);
143 private static async Task AddPart(
byte[] Data,
int Start,
int i,
bool Last,
144 Dictionary<string, object> Form, List<EmbeddedContent> List, Uri BaseUri)
149 for (j = Start; j < Max; j++)
151 if (Data[j] ==
'\r' && Data[j + 1] ==
'\n' && Data[j + 2] ==
'\r' && Data[j + 3] ==
'\n')
161 if (i >= 2 && Data[i - 1] ==
'-' && Data[i - 2] ==
'-')
165 if (i >= 4 && Data[i - 1 - k] ==
'\n' && Data[i - 2 - k] ==
'\r')
169 int NrBytes = i - j - 4 - k;
170 if (NrBytes < 0 || (NrBytes == 0 && Last))
173 string Header = Encoding.ASCII.GetString(Data, Start, j - Start);
175 byte[] Data2 =
new byte[NrBytes];
176 EmbeddedContent EmbeddedContent =
new EmbeddedContent()
182 Array.Copy(Data, j + 4, Data2, 0, NrBytes);
188 for (j = 0; j < l; j++)
191 if (!
string.IsNullOrEmpty(Key))
193 if (
char.IsWhiteSpace(Key[0]) && m >= 0)
196 Rows[j] =
string.Empty;
203 foreach (
string Row
in Rows)
205 j = Row.IndexOf(
':');
209 Key = Row.Substring(0, j).Trim();
210 Value = Row.Substring(j + 1).Trim();
212 switch (Key.ToUpper())
215 EmbeddedContent.ContentType = Value;
216 j = Value.IndexOf(
';');
219 ParseContentFields(Value.Substring(j + 1).Trim(), EmbeddedContent);
220 Value = Value.Substring(0, j).Trim();
224 case "CONTENT-DISPOSITION":
225 j = Value.IndexOf(
';');
228 ParseContentFields(Value.Substring(j + 1).Trim(), EmbeddedContent);
229 Value = Value.Substring(0, j).Trim();
232 switch (Value.ToUpper())
248 case "CONTENT-TRANSFER-ENCODING":
249 EmbeddedContent.TransferEncoding = Value;
253 EmbeddedContent.ID = Value;
256 case "CONTENT-DESCRIPTION":
257 EmbeddedContent.Description = Value;
262 if (!
string.IsNullOrEmpty(EmbeddedContent.TransferEncoding))
265 EmbeddedContent.TransferDecoded = Data2;
267 throw new Exception(
"Unrecognized Content-Transfer-Encoding: " + EmbeddedContent.TransferEncoding);
276 EmbeddedContent.Decoded = Data2;
281 Form[EmbeddedContent.Name] = EmbeddedContent.Decoded;
282 Form[EmbeddedContent.Name +
"_Binary"] = Data2;
284 if (!
string.IsNullOrEmpty(EmbeddedContent.ContentType))
285 Form[EmbeddedContent.Name +
"_ContentType"] = EmbeddedContent.ContentType;
287 if (!
string.IsNullOrEmpty(EmbeddedContent.FileName))
288 Form[EmbeddedContent.Name +
"_FileName"] = EmbeddedContent.FileName;
291 List?.Add(EmbeddedContent);
295 private static void ParseContentFields(
string s, EmbeddedContent EmbeddedContent)
299 switch (Field.Key.ToUpper())
302 EmbeddedContent.Name = Field.Value;
306 EmbeddedContent.FileName = Field.Value;
310 if (
int.TryParse(Field.Value, out
int i))
311 EmbeddedContent.Size = i;
314 case "CREATION-DATE":
316 EmbeddedContent.CreationDate = DTO;
319 case "MODIFICATION-DATE":
321 EmbeddedContent.ModificationDate = DTO;
334 public static bool TryTransferDecode(
byte[] Encoded,
string TransferEncoding, out
byte[] Decoded)
336 switch (TransferEncoding.ToUpper())
348 Decoded = Convert.FromBase64String(s);
351 case "QUOTED-PRINTABLE":
352 MemoryStream ms =
new MemoryStream();
357 for (j = 0, k = Encoded.Length; j < k; j++)
361 if (b == (
byte)
'=' && j + 2 < k)
363 ch = (char)Encoded[++j];
365 if (ch >=
'0' && ch <=
'9')
366 b = (byte)(ch -
'0');
367 else if (ch >=
'a' && ch <=
'f')
368 b = (byte)(ch -
'a' + 10);
369 else if (ch >=
'A' && ch <=
'F')
370 b = (byte)(ch -
'A' + 10);
373 if (Encoded[j + 1] == (
byte)
'\n')
380 ms.WriteByte((
byte)
'=');
381 ms.WriteByte((
byte)ch);
387 ch = (char)Encoded[++j];
389 if (ch >=
'0' && ch <=
'9')
390 b |= (byte)(ch -
'0');
391 else if (ch >=
'a' && ch <=
'f')
392 b |= (byte)(ch -
'a' + 10);
393 else if (ch >=
'A' && ch <=
'F')
394 b |= (byte)(ch -
'A' + 10);
400 Decoded = ms.ToArray();
418 if (
string.Compare(FileExtension,
"formdata",
true) == 0)
441 FileExtension =
"formdata";
445 FileExtension =
string.Empty;
455 public static async Task<KeyValuePair<byte[], string>>
Encode(IEnumerable<EmbeddedContent> Content)
457 string Boundary = Guid.NewGuid().ToString();
458 string ContentType = FormDataDecoder.ContentType +
"; boundary=\"" + Boundary +
"\"";
459 return new KeyValuePair<byte[], string>(await
Encode(Content, Boundary),
ContentType);
468 public static async Task<byte[]>
Encode(IEnumerable<EmbeddedContent> Content,
string Boundary)
470 using (MemoryStream ms =
new MemoryStream())
472 StringBuilder Header =
new StringBuilder();
477 await Alternative.AssertEncoded();
480 Header.Append(
"\r\n--");
481 Header.Append(Boundary);
485 Header.Append(
"\r\nContent-Transfer-Encoding: ");
489 Header.Append(
"\r\nContent-Type: ");
494 Header.Append(
"; name=\"");
495 Header.Append(Alternative.
Name.Replace(
"\"",
"\\\""));
500 !
string.IsNullOrEmpty(Alternative.
FileName))
502 Header.Append(
"\r\nContent-Disposition: ");
507 Header.Append(
"form-data");
509 if (!
string.IsNullOrEmpty(Alternative.
Name))
511 Header.Append(
"; name=\"");
512 Header.Append(Alternative.
Name.Replace(
"\"",
"\\\""));
518 Header.Append(
"inline");
523 Header.Append(
"attachment");
527 if (!
string.IsNullOrEmpty(Alternative.
FileName))
529 Header.Append(
"; filename=\"");
530 Header.Append(Alternative.
FileName.Replace(
"\"",
"\\\""));
535 if (!
string.IsNullOrEmpty(Alternative.
ID))
537 Header.Append(
"\r\nContent-ID: ");
538 Header.Append(Alternative.
ID);
541 if (!
string.IsNullOrEmpty(Alternative.
Description))
543 Header.Append(
"\r\nContent-Description: ");
547 Header.Append(
"\r\n\r\n");
549 HeaderBin = Encoding.ASCII.GetBytes(Header.ToString());
550 ms.Write(HeaderBin, 0, HeaderBin.Length);
551 ms.Write(Alternative.
Raw, 0, Alternative.
Raw.Length);
556 Header.Append(
"\r\n--");
557 Header.Append(Boundary);
560 HeaderBin = Encoding.ASCII.GetBytes(Header.ToString());
561 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.
static string GetString(byte[] Data, Encoding DefaultEncoding)
Gets a string from its binary representation, taking any Byte Order Mark (BOM) into account.
Static class managing encoding and decoding of internet content.
static Task< object > 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, untrasnformed 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.
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 Decode(byte[] Data, KeyValuePair< string, string >[] Fields, Dictionary< string, object > Form, List< EmbeddedContent > List, Uri BaseUri)
Decodes a multipart object
bool Decodes(string ContentType, out Grade Grade)
If the decoder decodes an object with a given content type.
static async Task< byte[]> Encode(IEnumerable< EmbeddedContent > Content, string Boundary)
Encodes multi-part content
static bool TryTransferDecode(byte[] Encoded, string TransferEncoding, out byte[] Decoded)
Tries to decode transfer-encoded binary data.
const string ContentType
multipart/form-data
async Task< object > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
string[] ContentTypes
Supported content types.
static async Task< KeyValuePair< byte[], string > > Encode(IEnumerable< EmbeddedContent > Content)
Encodes multi-part form data
Plain text encoder/decoder.
const string DefaultContentType
text/plain
Basic interface for Internet Content decoders. A class implementing this interface and having a defau...
ContentDisposition
Content disposition