1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
#[cfg(test)]
mod tests;

use std::io;
use std::io::{BufRead, Cursor};
use crate::ext::string_ext::StringExt;
use crate::header::Header;
use crate::symbol::SYMBOL;

pub struct FormMultipartData;

pub struct Part {
    pub headers: Vec<Header>,
    pub body: Vec<u8>,
}

impl Part {
    pub fn get_header(&self, name: String) -> Option<&Header> {
        let header =  self.headers.iter().find(|x| x.name.to_lowercase() == name.to_lowercase());
        header
    }
}

impl FormMultipartData {
    pub fn parse(data: &[u8], boundary: String) -> Result<Vec<Part>, String> {

        let cursor = io::Cursor::new(data);
        let bytes_read : i128 = 0;
        let total_bytes : i128 = data.len() as i128;

        let part_list : Vec<Part> = vec![];

        let boxed_part_list = FormMultipartData::
            parse_form_part_recursively(
                cursor,
                boundary,
                bytes_read,
                total_bytes,
                part_list
            );

        if boxed_part_list.is_err() {
            let message  = boxed_part_list.err().unwrap();
            return Err(message)
        }

        Ok(boxed_part_list.unwrap())
    }

    fn parse_form_part_recursively(
                mut cursor: Cursor<&[u8]>,
                boundary: String,
                mut bytes_read: i128,
                total_bytes: i128,
                mut part_list: Vec<Part>) -> Result<Vec<Part>, String> {
        let mut buf = vec![];
        let mut part = Part { headers: vec![], body: vec![] };

        // first boundary starts parsable payload
        if bytes_read == 0 {
            let boxed_read = cursor.read_until(b'\n', &mut buf);
            if boxed_read.is_err() {
                let message = boxed_read.err().unwrap().to_string();
                return Err(message);
            }
            let bytes_offset = boxed_read.unwrap();
            let b : &[u8] = &buf;
            bytes_read = bytes_read + bytes_offset as i128;

            let boxed_line = String::from_utf8(Vec::from(b));
            if boxed_line.is_err() {
                let error_message = boxed_line.err().unwrap().to_string();
                return Err(error_message);
            }
            let string = boxed_line.unwrap();
            let string = StringExt::filter_ascii_control_characters(&string);
            let string = StringExt::truncate_new_line_carriage_return(&string);

            let _current_string_is_boundary =
                string.replace(SYMBOL.hyphen, SYMBOL.empty_string)
                    .ends_with(&boundary.replace(SYMBOL.hyphen, SYMBOL.empty_string));

            if !_current_string_is_boundary {
                let message = format!("Body in multipart/form-data request needs to start with a boundary, actual string: '{}'", string);
                return Err(message.to_string())
            }
        }

        // headers part. by spec it shall have at least Content-Disposition header or more, following
        // by empty line. Headers shall be valid utf-8 encoded strings
        let mut current_string_is_empty = false;
        while !current_string_is_empty {
            buf = vec![];
            let boxed_read = cursor.read_until(b'\n', &mut buf);
            if boxed_read.is_err() {
                let message = boxed_read.err().unwrap().to_string();
                return Err(message);
            }
            let bytes_offset = boxed_read.unwrap();
            let b : &[u8] = &buf;
            bytes_read = bytes_read + bytes_offset as i128;

            let boxed_line = String::from_utf8(Vec::from(b));
            if boxed_line.is_err() {
                let error_message = boxed_line.err().unwrap().to_string();
                return Err(error_message);
            }
            let string = boxed_line.unwrap();

            let string = StringExt::filter_ascii_control_characters(&string);
            current_string_is_empty = string.trim().len() == 0;

            let _current_string_is_boundary =
                string.replace(SYMBOL.hyphen, SYMBOL.empty_string)
                    .ends_with(&boundary.replace(SYMBOL.hyphen, SYMBOL.empty_string));

            if _current_string_is_boundary {
                let message = "There is at least one missing body part in the multipart/form-data request";
                return Err(message.to_string())
            }

            if bytes_read == total_bytes as i128 {
                return Ok(part_list)
            }


            // multipart/form-data part does not have any header specified
            if current_string_is_empty && part.headers.len() == 0 {
                let message = "One of the body parts does not have any header specified. At least Content-Disposition is required";
                return Err(message.to_string());
            }

            if !current_string_is_empty {
                let boxed_header = Header::parse_header(&string);
                if boxed_header.is_err() {
                    let message = boxed_header.err().unwrap();
                    return Err(message)
                }

                let header = boxed_header.unwrap();
                part.headers.push(header);
            }
        }


        // multipart/form-data body part. it just arbitrary bytes. ends by delimiter.
        let mut _boundary_position = 0;
        let mut current_string_is_boundary = false;
        while !current_string_is_boundary {
            buf = vec![];

            let boxed_read = cursor.read_until(b'\n', &mut buf);
            if boxed_read.is_err() {
                let message = boxed_read.err().unwrap().to_string();
                return Err(message);
            }

            let bytes_offset = boxed_read.unwrap();

            if bytes_offset == 0 { break };

            let b : &[u8] = &buf;

            bytes_read = bytes_read + bytes_offset as i128;

            let escaped_dash_boundary = boundary.replace(SYMBOL.hyphen, SYMBOL.empty_string);

            current_string_is_boundary = false;
            if b.len() >= escaped_dash_boundary.len() {
                let boxed_sequence = FormMultipartData::find_subsequence(b, escaped_dash_boundary.as_bytes());
                if boxed_sequence.is_some() {
                    current_string_is_boundary = true;
                    _boundary_position = boxed_sequence.unwrap();
                }
            }

            if !current_string_is_boundary {
                part.body.append(&mut buf.clone());
            }

        }

        if !current_string_is_boundary && bytes_read == total_bytes as i128 {
            let message = "No end boundary present in the multipart/form-data request body";
            return Err(message.to_string());
        }

        // body for specific part may end with a new line or carriage return and a new line
        // in both cases new line carriage return delimiter is not part of the body
        let body_length = part.body.len();
        if body_length > 2 { // check if body itself is present
            let is_new_line_carriage_return_ending =
                *part.body.get(body_length-2).unwrap() == b'\r'
                    && *part.body.get(body_length-1).unwrap() == b'\n';

            let is_new_line_ending =
                *part.body.get(body_length-2).unwrap() != b'\r'
                    && *part.body.get(body_length-1).unwrap() == b'\n';

            if is_new_line_carriage_return_ending {
                part.body.remove(body_length - 1); // removing \n
                part.body.remove(body_length - 2); // removing \r
            }

            if is_new_line_ending {
                part.body.remove(body_length - 1); // removing \n
            }
        }



        part_list.push(part);


        if bytes_read == total_bytes as i128 {
            return Ok(part_list)
        }

        FormMultipartData::parse_form_part_recursively(cursor, boundary, bytes_read, total_bytes, part_list)
    }

    pub fn extract_boundary(content_type: &str) -> Result<String, String> {
        let boxed_split = content_type.split_once("boundary=");
        if boxed_split.is_none() {
            let message = "No boundary found in Content-Type header";
            return Err(message.to_string())
        }


        let (_, boundary) = boxed_split.unwrap();
        Ok(boundary.to_string())
    }

    fn find_subsequence(haystack: &[u8], needle: &[u8]) -> Option<usize> {
        haystack.windows(needle.len()).position(|window| window == needle)
    }

    pub fn generate_part(part: Part) -> Result<Vec<u8>, String> {
        if part.headers.len() == 0 {
            let message = "One of the body parts does not have any header specified. At least Content-Disposition is required";
            return Err(message.to_string())
        }

        let mut formatted_header_list : String = "".to_string();
        for header in part.headers.into_iter() {
            let formatted = format!("{}{}", header.as_string(), SYMBOL.new_line_carriage_return.to_string());
            formatted_header_list = [formatted_header_list, formatted].join(SYMBOL.empty_string);
        }

        let header_body_delimiter = SYMBOL.new_line_carriage_return.to_string();

        let body = part.body;

        let part = [
            formatted_header_list.as_bytes().to_vec(),
            header_body_delimiter.as_bytes().to_vec(),
            body
        ].join(SYMBOL.empty_string.as_bytes());

        Ok(part)
    }

    pub fn generate(part_list: Vec<Part>, boundary: &str) -> Result<Vec<u8>, String> {
        if part_list.len() == 0 {
            let message = "List of the multipart/form-data request body parts is empty";
            return Err(message.to_string());
        }

        let mut bytes = vec![];
        bytes.push(boundary.as_bytes().to_vec());

        for part in part_list.into_iter() {
            let boxed_part_as_bytes = FormMultipartData::generate_part(part);
            if boxed_part_as_bytes.is_err() {
                let message = boxed_part_as_bytes.err().unwrap();
                return Err(message);
            }
            let part_as_bytes = boxed_part_as_bytes.unwrap();
            bytes.push(part_as_bytes);
            bytes.push(boundary.as_bytes().to_vec());
        }

        let result = bytes.join(SYMBOL.new_line_carriage_return.as_bytes());

        Ok(result)
    }
}