using System; using System.Collections; using System.Collections.Generic; namespace OSHttpServer { /// /// Contains some kind of input from the browser/client. /// can be QueryString, form data or any other request body content. /// public class HttpInput : IHttpInput { /// Representation of a non-initialized class instance public static readonly HttpInput Empty = new("Empty", true); private readonly Dictionary _items = new(); private string _name; /// Variable telling the class that it is non-initialized protected readonly bool _ignoreChanges; /// /// Initializes a new instance of the class. /// /// form name. public HttpInput(string name) { Name = name; } /// /// Initializes a new instance of the class. /// /// form name. /// if set to true all changes will be ignored. /// this constructor should only be used by Empty protected HttpInput(string name, bool ignoreChanges) { _name = name; _ignoreChanges = ignoreChanges; } /// Creates a deep copy of the HttpInput class /// The object to copy /// The function makes a deep copy of quite a lot which can be slow protected HttpInput(HttpInput input) { foreach (HttpInputItem item in input) _items.Add(item.Name, new HttpInputItem(item)); _name = input._name; _ignoreChanges = input._ignoreChanges; } /// /// Form name as lower case /// public string Name { get { return _name; } set { _name = value; } } /// /// Add a new element. Form array elements are parsed /// and added in a correct hierarchy. /// /// Name is converted to lower case. /// /// name is null. /// Cannot add stuff to . public void Add(string name, string value) { if (name is null) throw new ArgumentNullException("name"); if (_ignoreChanges) throw new InvalidOperationException("Cannot add stuff to HttpInput.Empty."); // Check if it's a sub item. // we can have multiple levels of sub items as in user[extension[id]] => user -> extension -> id int pos = name.IndexOf('['); if (pos != -1) { string name1 = name[..pos]; string name2 = ExtractOne(name); if (!_items.TryGetValue(name1, out HttpInputItem it)) { it = new HttpInputItem(name1, null); _items.Add(name1, it); } it.Add(name2, value); } else { if (_items.TryGetValue(name, out HttpInputItem it)) it.Add(value); else _items.Add(name, new HttpInputItem(name, value)); } } /// /// Get a form item. /// /// /// Returns if item was not found. public HttpInputItem this[string name] { get { return _items.ContainsKey(name) ? _items[name] : HttpInputItem.Empty; } } /// /// Returns true if the class contains a with the corresponding name. /// /// The field/query string name /// True if the value exists public bool Contains(string name) { return _items.TryGetValue(name, out HttpInputItem it) && it.Value is not null; } /// /// Parses an item and returns it. /// This function is primarily used to parse array items as in user[name]. /// /// /// /// public static HttpInputItem ParseItem(string name, string value) { HttpInputItem item; // Check if it's a sub item. // we can have multiple levels of sub items as in user[extension[id]]] => user -> extension -> id int pos = name.IndexOf('['); if (pos != -1) { string name1 = name[..pos]; string name2 = ExtractOne(name); item = new HttpInputItem(name1, null) { { name2, value } }; } else item = new HttpInputItem(name, value); return item; } /// Outputs the instance representing all its values joined together /// public override string ToString() { string temp = string.Empty; foreach (KeyValuePair item in _items) temp += item.Value.ToString(Name); return temp; } /// Returns all items as an unescaped query string. /// public string ToString(bool asQueryString) { if (!asQueryString) return ToString(); string temp = string.Empty; foreach (KeyValuePair item in _items) temp += item.Value.ToString(null, true) + '&'; return temp.Length > 0 ? temp[..^1] : string.Empty; } /// /// Extracts one parameter from an array /// /// Containing the string array /// All but the first value /// /// string test1 = ExtractOne("system[user][extension][id]"); /// string test2 = ExtractOne(test1); /// string test3 = ExtractOne(test2); /// // test1 = user[extension][id] /// // test2 = extension[id] /// // test3 = id /// public static string ExtractOne(string value) { int pos = value.IndexOf('['); if (pos != -1) { ++pos; int gotMore = value.IndexOf('[', pos + 1); if (gotMore != -1) value = string.Concat(value.AsSpan(pos, gotMore - pos - 1), value.AsSpan(gotMore)); else value = value.Substring(pos, value.Length - pos - 1); } return value; } /// Resets all data contained by class virtual public void Clear() { _name = string.Empty; _items.Clear(); } /// ///Returns an enumerator that iterates through the collection. /// /// /// ///A that can be used to iterate through the collection. /// ///1 IEnumerator IEnumerable.GetEnumerator() { return _items.Values.GetEnumerator(); } /// ///Returns an enumerator that iterates through a collection. /// /// /// ///An object that can be used to iterate through the collection. /// ///2 public IEnumerator GetEnumerator() { return _items.Values.GetEnumerator(); } } /// /// Base class for request data containers /// public interface IHttpInput : IEnumerable { /// /// Adds a parameter mapped to the presented name /// /// The name to map the parameter to /// The parameter value void Add(string name, string value); /// /// Returns a request parameter /// /// The name associated with the parameter /// HttpInputItem this[string name] { get; } /// /// Returns true if the container contains the requested parameter /// /// Parameter id /// True if parameter exists bool Contains(string name); } }