FormDataSet
Bundles information stored in HTML forms.
using AngleSharp.Dom.Io;
using AngleSharp.Extensions;
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 void AsMultipart(StreamWriter stream);
public abstract void AsPlaintext(StreamWriter stream);
public abstract void AsUrlEncoded(StreamWriter stream);
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 void AsMultipart(StreamWriter stream)
{
if (base.HasName && HasValue) {
stream.WriteLine("content-disposition: form-data; name=\"" + base.Name.HtmlEncode(stream.Encoding) + "\"");
stream.WriteLine();
stream.WriteLine(_value.HtmlEncode(stream.Encoding));
}
}
public override void AsPlaintext(StreamWriter stream)
{
if (base.HasName && HasValue) {
stream.Write(base.Name);
stream.Write('=');
stream.Write(_value);
}
}
public override void AsUrlEncoded(StreamWriter stream)
{
if (base.HasName && HasValue) {
stream.Write(base.Name.UrlEncode(stream.Encoding));
stream.Write('=');
stream.Write(_value.UrlEncode(stream.Encoding));
}
}
}
private sealed class FileDataSetEntry : FormDataSetEntry
{
private readonly FileEntry _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 FileEntry Value => _value;
public FileDataSetEntry(string name, FileEntry value, string type)
: base(name, type)
{
_value = value;
}
public override bool Contains(string boundary, Encoding encoding)
{
if (_value == null || _value.Body == null)
return false;
byte[] bytes = encoding.GetBytes(boundary);
byte[] body = _value.Body;
int num = body.Length;
int i = 0;
for (int num2 = num - bytes.Length; i < num2; i++) {
if (body[i] == bytes[0]) {
bool flag = true;
for (int j = 1; j < num; j++) {
if (body[i + j] != bytes[j]) {
flag = false;
break;
}
}
if (flag)
return true;
}
}
return false;
}
public override void AsMultipart(StreamWriter stream)
{
if (base.HasName && HasValue && HasValueBody) {
stream.WriteLine("content-disposition: form-data; name=\"{0}\"; filename=\"{1}\"", base.Name.HtmlEncode(stream.Encoding), _value.Name.HtmlEncode(stream.Encoding));
stream.WriteLine("content-type: " + _value.Type);
stream.WriteLine("content-transfer-encoding: binary");
stream.WriteLine();
stream.Flush();
stream.BaseStream.Write(_value.Body, 0, _value.Body.Length);
stream.WriteLine();
}
}
public override void AsPlaintext(StreamWriter stream)
{
if (base.HasName && HasValue) {
stream.Write(base.Name);
stream.Write('=');
stream.Write(_value.Name);
}
}
public override void AsUrlEncoded(StreamWriter stream)
{
if (base.HasName && HasValue) {
stream.Write(base.Name.UrlEncode(stream.Encoding));
stream.Write('=');
stream.Write(_value.Name.UrlEncode(stream.Encoding));
}
}
}
private readonly List<FormDataSetEntry> _entries;
private string _boundary;
public string Boundary => _boundary;
public FormDataSet()
{
_boundary = Guid.NewGuid().ToString();
_entries = new List<FormDataSetEntry>();
}
public Stream AsMultipart(Encoding encoding = null)
{
encoding = (encoding ?? TextEncoding.Utf8);
MemoryStream memoryStream = new MemoryStream();
CheckBoundaries(encoding);
ReplaceCharset(encoding);
StreamWriter streamWriter = new StreamWriter(memoryStream, encoding);
streamWriter.WriteLine();
foreach (FormDataSetEntry entry in _entries) {
streamWriter.Write("--");
streamWriter.WriteLine(_boundary);
entry.AsMultipart(streamWriter);
}
streamWriter.Write("--");
streamWriter.Write(_boundary);
streamWriter.Write("--");
streamWriter.Flush();
memoryStream.Position = 0;
return memoryStream;
}
public Stream AsUrlEncoded(Encoding encoding = null)
{
encoding = (encoding ?? TextEncoding.Utf8);
string webName = encoding.WebName;
MemoryStream memoryStream = new MemoryStream();
CheckBoundaries(encoding);
ReplaceCharset(encoding);
StreamWriter streamWriter = new StreamWriter(memoryStream, encoding);
int i = 0;
bool flag = false;
if (i < _entries.Count && _entries[i].HasName && _entries[i].Name.Equals(Tags.IsIndex) && _entries[i].Type.Equals(InputTypeNames.Text, StringComparison.OrdinalIgnoreCase)) {
streamWriter.Write(((TextDataSetEntry)_entries[i]).Value);
i++;
}
for (; i < _entries.Count; i++) {
if (_entries[i].HasName) {
if (flag)
streamWriter.Write('&');
_entries[i].AsUrlEncoded(streamWriter);
flag = true;
}
}
streamWriter.Flush();
memoryStream.Position = 0;
return memoryStream;
}
public Stream AsPlaintext(Encoding encoding = null)
{
encoding = (encoding ?? TextEncoding.Utf8);
string webName = encoding.WebName;
MemoryStream memoryStream = new MemoryStream();
CheckBoundaries(encoding);
ReplaceCharset(encoding);
StreamWriter streamWriter = new StreamWriter(memoryStream, encoding);
streamWriter.WriteLine();
foreach (FormDataSetEntry entry in _entries) {
entry.AsPlaintext(streamWriter);
streamWriter.Write("\r\n");
}
streamWriter.Flush();
memoryStream.Position = 0;
return memoryStream;
}
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, FileEntry value, string type)
{
if (string.Compare(type, InputTypeNames.File, StringComparison.OrdinalIgnoreCase) == 0)
name = Normalize(name);
_entries.Add(new FileDataSetEntry(name, value, type));
}
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)
{
string[] value2 = value.Split(new string[3] {
"\r\n",
"\r",
"\n"
}, StringSplitOptions.None);
return string.Join("\r\n", value2);
}
public IEnumerator<string> GetEnumerator()
{
return (from m in _entries
select m.Name).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}