1 /* Copyright (c) 2016 the Civetweb developers
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to deal
5 * in the Software without restriction, including without limitation the rights
6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 * copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 url_encoded_field_found(const struct mg_connection *conn,
31 struct mg_form_data_handler *fdh)
34 char filename_dec[1024];
40 mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
42 if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
43 return FORM_FIELD_STORAGE_SKIP;
47 filename_dec_len = mg_url_decode(filename,
50 (int)sizeof(filename_dec),
53 if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec))
54 || (filename_dec_len < 0)) {
55 /* Log error message and skip this field. */
56 mg_cry(conn, "%s: Cannot decode filename", __func__);
57 return FORM_FIELD_STORAGE_SKIP;
64 fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data);
66 if ((ret & 0xF) == FORM_FIELD_STORAGE_GET) {
67 if (fdh->field_get == NULL) {
68 mg_cry(conn, "%s: Function \"Get\" not available", __func__);
69 return FORM_FIELD_STORAGE_SKIP;
72 if ((ret & 0xF) == FORM_FIELD_STORAGE_STORE) {
73 if (fdh->field_store == NULL) {
74 mg_cry(conn, "%s: Function \"Store\" not available", __func__);
75 return FORM_FIELD_STORAGE_SKIP;
84 url_encoded_field_get(const struct mg_connection *conn,
89 struct mg_form_data_handler *fdh)
93 char *value_dec = mg_malloc(value_len + 1);
97 /* Log error message and stop parsing the form data. */
99 "%s: Not enough memory (required: %lu)",
101 (unsigned long)(value_len + 1));
102 return FORM_FIELD_STORAGE_ABORT;
105 mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
108 mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
110 return fdh->field_get(key_dec,
112 (size_t)value_dec_len,
118 field_stored(const struct mg_connection *conn,
121 struct mg_form_data_handler *fdh)
123 /* Equivalent to "upload" callback of "mg_upload". */
125 (void)conn; /* we do not need mg_cry here, so conn is currently unused */
127 return fdh->field_store(path, file_size, fdh->user_data);
132 search_boundary(const char *buf,
134 const char *boundary,
137 /* We must do a binary search here, not a string search, since the buffer
138 * may contain '\x00' bytes, if binary data is transferred. */
139 int clen = (int)buf_len - (int)boundary_len - 4;
142 for (i = 0; i <= clen; i++) {
143 if (!memcmp(buf + i, "\r\n--", 4)) {
144 if (!memcmp(buf + i + 4, boundary, boundary_len)) {
154 mg_handle_form_request(struct mg_connection *conn,
155 struct mg_form_data_handler *fdh)
157 const char *content_type;
164 struct file fstore = STRUCT_FILE_INITIALIZER;
165 int64_t file_size = 0; /* init here, to a avoid a false positive
166 "uninitialized variable used" warning */
169 (conn->request_info.content_length > 0) || (conn->is_chunked);
171 /* There are three ways to encode data from a HTML form:
172 * 1) method: GET (default)
173 * The form data is in the HTTP query string.
174 * 2) method: POST, enctype: "application/x-www-form-urlencoded"
175 * The form data is in the request body.
176 * The body is url encoded (the default encoding for POST).
177 * 3) method: POST, enctype: "multipart/form-data".
178 * The form data is in the request body of a multipart message.
179 * This is the typical way to handle file upload from a form.
182 if (!has_body_data) {
185 if (strcmp(conn->request_info.request_method, "GET")) {
186 /* No body data, but not a GET request.
187 * This is not a valid form request. */
191 /* GET request: form data is in the query string. */
192 /* The entire data has already been loaded, so there is no nead to
193 * call mg_read. We just need to split the query string into key-value
195 data = conn->request_info.query_string;
197 /* No query string. */
201 /* Split data in a=1&b=xy&c=3&c=4 ... */
203 const char *val = strchr(data, '=');
205 ptrdiff_t keylen, vallen;
212 /* In every "field_found" callback we ask what to do with the
213 * data ("field_storage"). This could be:
214 * FORM_FIELD_STORAGE_SKIP (0) ... ignore the value of this field
215 * FORM_FIELD_STORAGE_GET (1) ... read the data and call the get
217 * FORM_FIELD_STORAGE_STORE (2) ... store the data in a file
218 * FORM_FIELD_STORAGE_READ (3) ... let the user read the data
219 * (for parsing long data on the fly)
220 * (currently not implemented)
221 * FORM_FIELD_STORAGE_ABORT (flag) ... stop parsing
223 memset(path, 0, sizeof(path));
225 field_storage = url_encoded_field_found(conn,
235 next = strchr(val, '&');
240 vallen = (ptrdiff_t)strlen(val);
244 if (field_storage == FORM_FIELD_STORAGE_GET) {
246 url_encoded_field_get(
247 conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
249 if (field_storage == FORM_FIELD_STORAGE_STORE) {
250 /* Store the content to a file */
251 if (mg_fopen(conn, path, "wb", &fstore) == 0) {
255 if (fstore.fp != NULL) {
257 (size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
258 if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
260 "%s: Cannot write file %s",
265 remove_bad_file(conn, path);
267 file_size += (int64_t)n;
270 r = fclose(fstore.fp);
272 /* stored successfully */
273 field_stored(conn, path, file_size, fdh);
276 "%s: Error saving file %s",
279 remove_bad_file(conn, path);
285 mg_cry(conn, "%s: Cannot create file %s", __func__, path);
289 /* if (field_storage == FORM_FIELD_STORAGE_READ) { */
290 /* The idea of "field_storage=read" is to let the API user read
291 * data chunk by chunk and to some data processing on the fly.
292 * This should avoid the need to store data in the server:
293 * It should neither be stored in memory, like
294 * "field_storage=get" does, nor in a file like
295 * "field_storage=store".
296 * However, for a "GET" request this does not make any much
297 * sense, since the data is already stored in memory, as it is
298 * part of the query string.
302 if ((field_storage & FORM_FIELD_STORAGE_ABORT)
303 == FORM_FIELD_STORAGE_ABORT) {
304 /* Stop parsing the request */
308 /* Proceed to next entry */
315 content_type = mg_get_header(conn, "Content-Type");
318 || !mg_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED")
319 || !mg_strcasecmp(content_type, "APPLICATION/WWW-FORM-URLENCODED")) {
320 /* The form data is in the request body data, encoded in key/value
322 int all_data_read = 0;
324 /* Read body data and split it in keys and values.
325 * The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
326 * Here we use "POST", and read the data from the request body.
327 * The data read on the fly, so it is not required to buffer the
328 * entire request in memory before processing it. */
332 ptrdiff_t keylen, vallen;
334 int end_of_key_value_pair_found = 0;
337 if ((size_t)buf_fill < (sizeof(buf) - 1)) {
339 size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
340 r = mg_read(conn, buf + (size_t)buf_fill, to_read);
345 if (r != (int)to_read) {
346 /* TODO: Create a function to get "all_data_read" from
347 * the conn object. All data is read if the Content-Length
348 * has been reached, or if chunked encoding is used and
349 * the end marker has been read, or if the connection has
360 val = strchr(buf, '=');
369 memset(path, 0, sizeof(path));
371 field_storage = url_encoded_field_found(conn,
380 if ((field_storage & FORM_FIELD_STORAGE_ABORT)
381 == FORM_FIELD_STORAGE_ABORT) {
382 /* Stop parsing the request */
386 if (field_storage == FORM_FIELD_STORAGE_STORE) {
387 if (mg_fopen(conn, path, "wb", &fstore) == 0) {
392 mg_cry(conn, "%s: Cannot create file %s", __func__, path);
397 /* Loop to read values larger than sizeof(buf)-keylen-2 */
399 next = strchr(val, '&');
403 end_of_key_value_pair_found = 1;
405 vallen = (ptrdiff_t)strlen(val);
409 if (field_storage == FORM_FIELD_STORAGE_GET) {
411 if (!end_of_key_value_pair_found && !all_data_read) {
412 /* This callback will deliver partial contents */
415 (void)all_data_read; /* avoid warning */
419 url_encoded_field_get(conn,
420 ((get_block > 0) ? NULL : buf),
430 (size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
431 if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
433 "%s: Cannot write file %s",
438 remove_bad_file(conn, path);
440 file_size += (int64_t)n;
443 if (!end_of_key_value_pair_found) {
447 sizeof(buf) - (size_t)used);
448 buf_fill -= (int)used;
449 if ((size_t)buf_fill < (sizeof(buf) - 1)) {
451 size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
452 r = mg_read(conn, buf + (size_t)buf_fill, to_read);
457 if (r != (int)to_read) {
458 /* TODO: Create a function to get "all_data_read"
459 * from the conn object. All data is read if the
460 * Content-Length has been reached, or if chunked
461 * encoding is used and the end marker has been
462 * read, or if the connection has been closed. */
474 } while (!end_of_key_value_pair_found);
477 r = fclose(fstore.fp);
479 /* stored successfully */
480 field_stored(conn, path, file_size, fdh);
482 mg_cry(conn, "%s: Error saving file %s", __func__, path);
483 remove_bad_file(conn, path);
488 /* Proceed to next entry */
490 memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
491 buf_fill -= (int)used;
497 if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
498 /* The form data is in the request body data, encoded as multipart
499 * content (see https://www.ietf.org/rfc/rfc1867.txt,
500 * https://www.ietf.org/rfc/rfc2388.txt). */
501 const char *boundary;
504 struct mg_request_info part_header;
505 char *hbuf, *hend, *fbeg, *fend, *nbeg, *nend;
506 const char *content_disp;
509 memset(&part_header, 0, sizeof(part_header));
511 /* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
513 while (content_type[bl] == ' ') {
517 /* There has to be a BOUNDARY definition in the Content-Type header */
518 if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
519 /* Malformed request */
523 boundary = content_type + bl + 9;
524 bl = strlen(boundary);
526 if (bl + 800 > sizeof(buf)) {
527 /* Sanity check: The algorithm can not work if bl >= sizeof(buf),
528 * and it will not work effectively, if the buf is only a few byte
529 * larger than bl, or it buf can not hold the multipart header
531 * Check some reasonable number here, that should be fulfilled by
532 * any reasonable request from every browser. If it is not
533 * fulfilled, it might be a hand-made request, intended to
534 * interfere with the algorithm. */
543 buf + (size_t)buf_fill,
544 sizeof(buf) - 1 - (size_t)buf_fill);
556 if (buf[0] != '-' || buf[1] != '-') {
557 /* Malformed request */
560 if (strncmp(buf + 2, boundary, bl)) {
561 /* Malformed request */
564 if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
565 /* Every part must end with \r\n, if there is another part.
566 * The end of the request has an extra -- */
567 if (((size_t)buf_fill != (size_t)(bl + 6))
568 || (strncmp(buf + bl + 2, "--\r\n", 4))) {
569 /* Malformed request */
572 /* End of the request */
576 /* Next, we need to get the part header: Read until \r\n\r\n */
578 hend = strstr(hbuf, "\r\n\r\n");
580 /* Malformed request */
584 parse_http_headers(&hbuf, &part_header);
585 if ((hend + 2) != hbuf) {
586 /* Malformed request */
593 /* According to the RFC, every part has to have a header field like:
594 * Content-Disposition: form-data; name="..." */
595 content_disp = get_header(&part_header, "Content-Disposition");
597 /* Malformed request */
601 /* Get the mandatory name="..." part of the Content-Disposition
603 nbeg = strstr(content_disp, "name=\"");
605 /* Malformed request */
609 nend = strchr(nbeg, '\"');
611 /* Malformed request */
615 /* Get the optional filename="..." part of the Content-Disposition
617 fbeg = strstr(content_disp, "filename=\"");
620 fend = strchr(fbeg, '\"');
622 /* Malformed request (the filename field is optional, but if
623 * it exists, it needs to be terminated correctly). */
627 /* TODO: check Content-Type */
628 /* Content-Type: application/octet-stream */
634 memset(path, 0, sizeof(path));
636 field_storage = url_encoded_field_found(conn,
638 (size_t)(nend - nbeg),
640 (size_t)(fend - fbeg),
645 /* If the boundary is already in the buffer, get the address,
646 * otherwise next will be NULL. */
647 next = search_boundary(hbuf,
648 (size_t)((buf - hbuf) + buf_fill),
652 if (field_storage == FORM_FIELD_STORAGE_STORE) {
653 /* Store the content to a file */
654 if (mg_fopen(conn, path, "wb", &fstore) == 0) {
660 mg_cry(conn, "%s: Cannot create file %s", __func__, path);
666 /* Set "towrite" to the number of bytes available
668 towrite = (size_t)(buf - hend + buf_fill);
669 /* Subtract the boundary length, to deal with
670 * cases the boundary is only partially stored
674 if (field_storage == FORM_FIELD_STORAGE_GET) {
675 url_encoded_field_get(conn,
676 ((get_block > 0) ? NULL : nbeg),
679 : (size_t)(nend - nbeg)),
686 if (field_storage == FORM_FIELD_STORAGE_STORE) {
689 /* Store the content of the buffer. */
690 n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
691 if ((n != towrite) || (ferror(fstore.fp))) {
693 "%s: Cannot write file %s",
698 remove_bad_file(conn, path);
700 file_size += (int64_t)n;
704 memmove(buf, hend + towrite, bl + 4);
705 buf_fill = (int)(bl + 4);
710 buf + (size_t)buf_fill,
711 sizeof(buf) - 1 - (size_t)buf_fill);
724 next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
727 towrite = (size_t)(next - hend);
729 if (field_storage == FORM_FIELD_STORAGE_GET) {
731 url_encoded_field_get(conn,
732 ((get_block > 0) ? NULL : nbeg),
734 : (size_t)(nend - nbeg)),
740 if (field_storage == FORM_FIELD_STORAGE_STORE) {
743 n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
744 if ((n != towrite) || (ferror(fstore.fp))) {
746 "%s: Cannot write file %s",
751 remove_bad_file(conn, path);
753 file_size += (int64_t)n;
757 if (field_storage == FORM_FIELD_STORAGE_STORE) {
760 r = fclose(fstore.fp);
762 /* stored successfully */
763 field_stored(conn, path, file_size, fdh);
766 "%s: Error saving file %s",
769 remove_bad_file(conn, path);
775 if ((field_storage & FORM_FIELD_STORAGE_ABORT)
776 == FORM_FIELD_STORAGE_ABORT) {
777 /* Stop parsing the request */
781 /* Remove from the buffer */
782 used = next - buf + 2;
783 memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
784 buf_fill -= (int)used;
787 /* All parts handled */
791 /* Unknown Content-Type */