FormDataSet
Bundles information stored in HTML forms.
using AngleSharp.Dom.Io;
using AngleSharp.Extensions;
using AngleSharp.Network;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace AngleSharp.Html
{
internal sealed class FormDataSet : IEnumerable<string>, IEnumerable
{
private abstract class FormDataSetEntry
{
private readonly string _name;
private readonly string _type;
public bool HasName => _name != null;
public string Name => _name ?? string.Empty;
public string Type => _type ?? InputTypeNames.Text;
public FormDataSetEntry(string name, string type)
{
_name = name;
_type = type;
}
public abstract Action<StreamWriter> AsMultipart(Encoding encoding);
public abstract Tuple<string, string> AsPlaintext();
public abstract Tuple<string, string> AsUrlEncoded(Encoding encoding);
public abstract bool Contains(string boundary, Encoding encoding);
}
private sealed class TextDataSetEntry : FormDataSetEntry
{
private readonly string _value;
public bool HasValue => _value != null;
public string Value => _value;
public TextDataSetEntry(string name, string value, string type)
: base(name, type)
{
_value = value;
}
public override bool Contains(string boundary, Encoding encoding)
{
if (_value == null)
return false;
return _value.Contains(boundary);
}
public override Action<StreamWriter> AsMultipart(Encoding encoding)
{
if (base.HasName && HasValue)
return delegate(StreamWriter stream) {
stream.WriteLine("Content-Disposition: form-data; name=\"" + base.Name.HtmlEncode(encoding) + "\"");
stream.WriteLine();
stream.WriteLine(_value.HtmlEncode(encoding));
};
return null;
}
public override Tuple<string, string> AsPlaintext()
{
if (base.HasName && HasValue)
return Tuple.Create(base.Name, _value);
return null;
}
public override Tuple<string, string> AsUrlEncoded(Encoding encoding)
{
if (base.HasName && HasValue) {
byte[] bytes = encoding.GetBytes(base.Name);
byte[] bytes2 = encoding.GetBytes(_value);
return Tuple.Create(bytes.UrlEncode(), bytes2.UrlEncode());
}
return null;
}
}
private sealed class FileDataSetEntry : FormDataSetEntry
{
private readonly IFile _value;
public bool HasValue {
get {
if (_value != null)
return _value.Name != null;
return false;
}
}
public bool HasValueBody {
get {
if (_value != null && _value.Body != null)
return _value.Type != null;
return false;
}
}
public IFile Value => _value;
public string FileName {
get {
if (_value == null)
return string.Empty;
return _value.Name;
}
}
public string ContentType {
get {
if (_value == null)
return MimeTypes.Binary;
return _value.Type;
}
}
public FileDataSetEntry(string name, IFile value, string type)
: base(name, type)
{
_value = value;
}
public override bool Contains(string boundary, Encoding encoding)
{
if (_value == null || _value.Body == null)
return false;
return false;
}
public override Action<StreamWriter> AsMultipart(Encoding encoding)
{
if (base.HasName)
return delegate(StreamWriter stream) {
bool flag = HasValue && HasValueBody;
stream.WriteLine("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"", new object[2] {
base.Name.HtmlEncode(encoding),
FileName.HtmlEncode(encoding)
});
stream.WriteLine("Content-Type: " + ContentType);
stream.WriteLine();
if (flag) {
stream.Flush();
_value.Body.CopyTo(stream.BaseStream);
}
stream.WriteLine();
};
return null;
}
public override Tuple<string, string> AsPlaintext()
{
if (base.HasName && HasValue)
return Tuple.Create(base.Name, _value.Name);
return null;
}
public override Tuple<string, string> AsUrlEncoded(Encoding encoding)
{
if (base.HasName && HasValue) {
byte[] bytes = encoding.GetBytes(base.Name);
byte[] bytes2 = encoding.GetBytes(_value.Name);
return Tuple.Create(bytes.UrlEncode(), bytes2.UrlEncode());
}
return null;
}
}
private readonly List<FormDataSetEntry> _entries;
private string _boundary;
private static readonly string[] NewLines = new string[3] {
"\r\n",
"\r",
"\n"
};
public string Boundary => _boundary;
public FormDataSet()
{
_boundary = Guid.NewGuid().ToString();
_entries = new List<FormDataSetEntry>();
}
public Stream AsMultipart(Encoding encoding = null)
{
return Build(encoding, delegate(StreamWriter stream) {
Encoding enc = stream.Encoding;
IEnumerable<Action<StreamWriter>> enumerable = from m in _entries
select m.AsMultipart(enc) into m
where m != null
select m;
foreach (Action<StreamWriter> item in enumerable) {
stream.Write("--");
stream.WriteLine(_boundary);
item(stream);
}
stream.Write("--");
stream.Write(_boundary);
stream.Write("--");
});
}
public Stream AsUrlEncoded(Encoding encoding = null)
{
return Build(encoding, delegate(StreamWriter stream) {
int num = 0;
Encoding enc = stream.Encoding;
if (num < _entries.Count && _entries[num].HasName && _entries[num].Name.Equals(Tags.IsIndex) && _entries[num].Type.Equals(InputTypeNames.Text, StringComparison.OrdinalIgnoreCase)) {
stream.Write(((TextDataSetEntry)_entries[num]).Value);
num++;
}
Tuple<string, string>[] array = (from m in _entries.Skip(num)
select m.AsUrlEncoded(enc) into m
where m != null
select m).ToArray();
for (int i = 0; i < array.Length; i++) {
if (i > 0)
stream.Write('&');
stream.Write(array[i].Item1);
stream.Write('=');
stream.Write(array[i].Item2);
}
});
}
public Stream AsPlaintext(Encoding encoding = null)
{
return Build(encoding, delegate(StreamWriter stream) {
Tuple<string, string>[] array = (from m in _entries
select m.AsPlaintext() into m
where m != null
select m).ToArray();
for (int i = 0; i < array.Length; i++) {
if (i > 0)
stream.Write("\r\n");
stream.Write(array[i].Item1);
stream.Write('=');
stream.Write(array[i].Item2);
}
});
}
public void Append(string name, string value, string type)
{
if (string.Compare(type, Tags.Textarea, StringComparison.OrdinalIgnoreCase) == 0) {
name = Normalize(name);
value = Normalize(value);
}
_entries.Add(new TextDataSetEntry(name, value, type));
}
public void Append(string name, IFile value, string type)
{
if (string.Compare(type, InputTypeNames.File, StringComparison.OrdinalIgnoreCase) == 0)
name = Normalize(name);
_entries.Add(new FileDataSetEntry(name, value, type));
}
private Stream Build(Encoding encoding, Action<StreamWriter> process)
{
encoding = (encoding ?? TextEncoding.Utf8);
MemoryStream memoryStream = new MemoryStream();
CheckBoundaries(encoding);
ReplaceCharset(encoding);
StreamWriter streamWriter = new StreamWriter(memoryStream, encoding);
process(streamWriter);
streamWriter.Flush();
memoryStream.Position = 0;
return memoryStream;
}
private void ReplaceCharset(Encoding encoding)
{
for (int i = 0; i < _entries.Count; i++) {
FormDataSetEntry formDataSetEntry = _entries[i];
if (!string.IsNullOrEmpty(formDataSetEntry.Name) && formDataSetEntry.Name.Equals("_charset_") && formDataSetEntry.Type.Equals(InputTypeNames.Hidden, StringComparison.OrdinalIgnoreCase))
_entries[i] = new TextDataSetEntry(formDataSetEntry.Name, encoding.WebName, formDataSetEntry.Type);
}
}
private void CheckBoundaries(Encoding encoding)
{
bool flag = false;
do {
for (int i = 0; i < _entries.Count; i++) {
if (flag = _entries[i].Contains(_boundary, encoding)) {
_boundary = Guid.NewGuid().ToString();
break;
}
}
} while (flag);
}
private static string Normalize(string value)
{
if (!string.IsNullOrEmpty(value)) {
string[] value2 = value.Split(NewLines, StringSplitOptions.None);
return string.Join("\r\n", value2);
}
return value;
}
public IEnumerator<string> GetEnumerator()
{
return (from m in _entries
select m.Name).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}