<commit_before>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Babbacombe.Webserver {
    // Code found at http://multipartparser.codeplex.com/discussions/210676

    public abstract class PostedField {
        public string ContentType { get; set; }
        public string Name { get; set; }
    }

    public class PostedValue : PostedField {
        public string Value { get; set; }

        public PostedValue(Dictionary<string, string> properties, string contents) {
            ContentType = "text/plain";
            Name = properties["name"];
            Value = contents;
        }
    }

    public class PostedFile : PostedField {
        public string FileName { get; set; }
        public Stream FileContents { get; set; }

        public PostedFile(Dictionary<string, string> properties, byte[] contents) {
            ContentType = properties["Content-Type"];
            FileName = properties["filename"];
            Name = properties["name"];

            FileContents = new MemoryStream(contents);
        }
    }

    public class MultipartForm {
        public Dictionary<string, PostedField> Fields { get; private set; }

        private MultipartForm(IEnumerable<string> fields, byte[] data, byte[] dataStartMarker, byte[] dataEndMarker) {
            Fields = new Dictionary<string, PostedField>();

            ParseFields(fields, data, dataStartMarker, dataEndMarker);
        }

        private void ParseFields(IEnumerable<string> fields, byte[] data, byte[] dataStartMarker, byte[] dataEndMarker) {
            int dataStartIndex = (IndexOf(data, dataStartMarker, 0) > -1)
                                     ? IndexOf(data, dataStartMarker, 0) + dataStartMarker.Length
                                     : -1;
            int dataLength = (dataStartIndex > -1) ? IndexOf(data, dataEndMarker, dataStartIndex) - dataStartIndex : -1;

            foreach (var field in fields) {
                Dictionary<string, string> properties = ParseProperties(field);
                string contents = Regex.Match(field, @"(?<=\r\n\r\n)(.*?)(?=\r\n)").Value;
                byte[] rawData = null;

                if (dataLength > -1) {
                    rawData = new byte[dataLength];
                    Buffer.BlockCopy(data, dataStartIndex, rawData, 0, dataLength);
                }

                if (properties.ContainsKey("filename")) {
                    Fields.Add(properties["name"], new PostedFile(properties, rawData));
                } else {
                    Fields.Add(properties["name"], new PostedValue(properties, contents));
                }

                if (dataStartIndex > -1) {
                    dataStartIndex = IndexOf(data, dataStartMarker, dataStartIndex + dataLength);
                    if (dataStartIndex > -1) {
                        dataStartIndex += dataStartMarker.Length;
                        dataLength = IndexOf(data, dataEndMarker, dataStartIndex) - dataStartIndex;
                    }
                }
            }
        }

        private static Dictionary<string, string> ParseProperties(string field) {
            Match propertiesMatch = Regex.Match(field.Substring(0, field.IndexOf("\r\n\r\n") + "\r\n\r\n".Length),
                                                  @"(?:.*)\r\n((?<Key>[^\:=]+)(?:[\=\:])(?:[\s]*)(?<Value>[^;\s\r]+)(?:[;\s]*))+\r\n\r\n(?:.*)");

            var parsed = propertiesMatch.Groups["Key"].Captures.Cast<Capture>().Select((c, i) => new { c, i })<commit_msg>Fixed parsing problem parsing quoted strings in Multipart form data.<commit_after>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Babbacombe.Webserver {
    // Modified from code found at http://multipartparser.codeplex.com/discussions/210676

    public abstract class PostedField {
        public string ContentType { get; set; }
        public string Name { get; set; }
    }

    public class PostedValue : PostedField {
        public string Value { get; set; }

        public PostedValue(Dictionary<string, string> properties, string contents) {
            ContentType = "text/plain";
            Name = properties["name"];
            Value = contents;
        }
    }

    public class PostedFile : PostedField {
        public string FileName { get; set; }
        public Stream FileContents { get; set; }

        public PostedFile(Dictionary<string, string> properties, byte[] contents) {
            ContentType = properties["Content-Type"];
            FileName = properties["filename"];
            Name = properties["name"];

            FileContents = new MemoryStream(contents);
        }
    }

    public class MultipartForm {
        public Dictionary<string, PostedField> Fields { get; private set; }

        private MultipartForm(IEnumerable<string> fields, byte[] data, byte[] dataStartMarker, byte[] dataEndMarker) {
            Fields = new Dictionary<string, PostedField>();

            ParseFields(fields, data, dataStartMarker, dataEndMarker);
        }

        private void ParseFields(IEnumerable<string> fields, byte[] data, byte[] dataStartMarker, byte[] dataEndMarker) {
            int dataStartIndex = (IndexOf(data, dataStartMarker, 0) > -1)
                                     ? IndexOf(data, dataStartMarker, 0) + dataStartMarker.Length
                                     : -1;
            int dataLength = (dataStartIndex > -1) ? IndexOf(data, dataEndMarker, dataStartIndex) - dataStartIndex : -1;

            foreach (var field in fields) {
                Dictionary<string, string> properties = ParseProperties(field);
                string contents = Regex.Match(field, @"(?<=\r\n\r\n)(.*?)(?=\r\n)").Value;
                byte[] rawData = null;

                if (dataLength > -1) {
                    rawData = new byte[dataLength];
                    Buffer.BlockCopy(data, dataStartIndex, rawData, 0, dataLength);
                }

                if (properties.ContainsKey("filename")) {
                    Fields.Add(properties["name"], new PostedFile(properties, rawData));
                } else {
                    Fields.Add(properties["name"], new PostedValue(properties, contents));
                }

                if (dataStartIndex > -1) {
                    dataStartIndex = IndexOf(data, dataStartMarker, dataStartIndex + dataLength);
                    if (dataStartIndex > -1) {
                        dataStartIndex += dataStartMarker.Length;
                        dataLength = IndexOf(data, dataEndMarker, dataStartIndex) - dataStartIndex;
                    }
                }
            }
        }

        private static Dictionary<string, string> ParseProperties(string field) {
            Match propertiesMatch = Regex.Match(field.Substring(0, field.IndexOf("\r\n\r\n") + "\r\n\r\n".Length),
                @"(?:.*)\r\n((?<Key>[^\:=]+)(?:[\=\:])(?:[\s]*)(?<Value>([^"";\s\r]+)|(""[^""]+""))(?:[;\s]*))+\r\n\r\n(?:.*)");

            var parsed = propertiesMatch.Groups["Key"].Captures.Cast<Capture>().Select((c, i) => new { c, i })
{'fraction_non_alphanumeric': 0.12790697674418605, 'fraction_numerical': 0.004706533776301218, 'mean_word_length': 2.070548236294093, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1044045', 'n_tokens_mistral': 1953, 'n_tokens_neox': 1835, 'n_words': 439}