Logo ROOT   6.14/05
Reference Guide
handle_form.inl
Go to the documentation of this file.
1 /* Copyright (c) 2016-2017 the Civetweb developers
2  *
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:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
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
19  * THE SOFTWARE.
20  */
21 
22 
23 static int
24 url_encoded_field_found(const struct mg_connection *conn,
25  const char *key,
26  size_t key_len,
27  const char *filename,
28  size_t filename_len,
29  char *path,
30  size_t path_len,
31  struct mg_form_data_handler *fdh)
32 {
33  char key_dec[1024];
34  char filename_dec[1024];
35  int key_dec_len;
36  int filename_dec_len;
37  int ret;
38 
39  key_dec_len =
40  mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
41 
42  if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
44  }
45 
46  if (filename) {
47  filename_dec_len = mg_url_decode(filename,
48  (int)filename_len,
49  filename_dec,
50  (int)sizeof(filename_dec),
51  1);
52 
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__);
58  }
59  } else {
60  filename_dec[0] = 0;
61  }
62 
63  ret =
64  fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data);
65 
66  if ((ret & 0xF) == FORM_FIELD_STORAGE_GET) {
67  if (fdh->field_get == NULL) {
68  mg_cry(conn, "%s: Function \"Get\" not available", __func__);
70  }
71  }
72  if ((ret & 0xF) == FORM_FIELD_STORAGE_STORE) {
73  if (fdh->field_store == NULL) {
74  mg_cry(conn, "%s: Function \"Store\" not available", __func__);
76  }
77  }
78 
79  return ret;
80 }
81 
82 
83 static int
84 url_encoded_field_get(const struct mg_connection *conn,
85  const char *key,
86  size_t key_len,
87  const char *value,
88  size_t value_len,
89  struct mg_form_data_handler *fdh)
90 {
91  char key_dec[1024];
92 
93  char *value_dec = (char *)mg_malloc_ctx(value_len + 1, conn->ctx);
94  int value_dec_len, ret;
95 
96  if (!value_dec) {
97  /* Log error message and stop parsing the form data. */
98  mg_cry(conn,
99  "%s: Not enough memory (required: %lu)",
100  __func__,
101  (unsigned long)(value_len + 1));
103  }
104 
105  mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
106 
107  value_dec_len =
108  mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
109 
110  ret = fdh->field_get(key_dec,
111  value_dec,
112  (size_t)value_dec_len,
113  fdh->user_data);
114 
115  mg_free(value_dec);
116 
117  return ret;
118 }
119 
120 
121 static int
122 unencoded_field_get(const struct mg_connection *conn,
123  const char *key,
124  size_t key_len,
125  const char *value,
126  size_t value_len,
127  struct mg_form_data_handler *fdh)
128 {
129  char key_dec[1024];
130  (void)conn;
131 
132  mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
133 
134  return fdh->field_get(key_dec, value, value_len, fdh->user_data);
135 }
136 
137 
138 static int
139 field_stored(const struct mg_connection *conn,
140  const char *path,
141  long long file_size,
142  struct mg_form_data_handler *fdh)
143 {
144  /* Equivalent to "upload" callback of "mg_upload". */
145 
146  (void)conn; /* we do not need mg_cry here, so conn is currently unused */
147 
148  return fdh->field_store(path, file_size, fdh->user_data);
149 }
150 
151 
152 static const char *
153 search_boundary(const char *buf,
154  size_t buf_len,
155  const char *boundary,
156  size_t boundary_len)
157 {
158  /* We must do a binary search here, not a string search, since the buffer
159  * may contain '\x00' bytes, if binary data is transferred. */
160  int clen = (int)buf_len - (int)boundary_len - 4;
161  int i;
162 
163  for (i = 0; i <= clen; i++) {
164  if (!memcmp(buf + i, "\r\n--", 4)) {
165  if (!memcmp(buf + i + 4, boundary, boundary_len)) {
166  return buf + i;
167  }
168  }
169  }
170  return NULL;
171 }
172 
173 
174 int
175 mg_handle_form_request(struct mg_connection *conn,
176  struct mg_form_data_handler *fdh)
177 {
178  const char *content_type;
179  char path[512];
180  char buf[1024]; /* Must not be smaller than ~900 - see sanity check */
181  int field_storage;
182  int buf_fill = 0;
183  int r;
184  int field_count = 0;
185  struct mg_file fstore = STRUCT_FILE_INITIALIZER;
186  int64_t file_size = 0; /* init here, to a avoid a false positive
187  "uninitialized variable used" warning */
188 
189  int has_body_data =
190  (conn->request_info.content_length > 0) || (conn->is_chunked);
191 
192  /* There are three ways to encode data from a HTML form:
193  * 1) method: GET (default)
194  * The form data is in the HTTP query string.
195  * 2) method: POST, enctype: "application/x-www-form-urlencoded"
196  * The form data is in the request body.
197  * The body is url encoded (the default encoding for POST).
198  * 3) method: POST, enctype: "multipart/form-data".
199  * The form data is in the request body of a multipart message.
200  * This is the typical way to handle file upload from a form.
201  */
202 
203  if (!has_body_data) {
204  const char *data;
205 
206  if (strcmp(conn->request_info.request_method, "GET")) {
207  /* No body data, but not a GET request.
208  * This is not a valid form request. */
209  return -1;
210  }
211 
212  /* GET request: form data is in the query string. */
213  /* The entire data has already been loaded, so there is no nead to
214  * call mg_read. We just need to split the query string into key-value
215  * pairs. */
216  data = conn->request_info.query_string;
217  if (!data) {
218  /* No query string. */
219  return -1;
220  }
221 
222  /* Split data in a=1&b=xy&c=3&c=4 ... */
223  while (*data) {
224  const char *val = strchr(data, '=');
225  const char *next;
226  ptrdiff_t keylen, vallen;
227 
228  if (!val) {
229  break;
230  }
231  keylen = val - data;
232 
233  /* In every "field_found" callback we ask what to do with the
234  * data ("field_storage"). This could be:
235  * FORM_FIELD_STORAGE_SKIP (0) ... ignore the value of this field
236  * FORM_FIELD_STORAGE_GET (1) ... read the data and call the get
237  * callback function
238  * FORM_FIELD_STORAGE_STORE (2) ... store the data in a file
239  * FORM_FIELD_STORAGE_READ (3) ... let the user read the data
240  * (for parsing long data on the fly)
241  * (currently not implemented)
242  * FORM_FIELD_STORAGE_ABORT (flag) ... stop parsing
243  */
244  memset(path, 0, sizeof(path));
245  field_count++;
246  field_storage = url_encoded_field_found(conn,
247  data,
248  (size_t)keylen,
249  NULL,
250  0,
251  path,
252  sizeof(path) - 1,
253  fdh);
254 
255  val++;
256  next = strchr(val, '&');
257  if (next) {
258  vallen = next - val;
259  next++;
260  } else {
261  vallen = (ptrdiff_t)strlen(val);
262  next = val + vallen;
263  }
264 
265  if (field_storage == FORM_FIELD_STORAGE_GET) {
266  /* Call callback */
268  conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
269  }
270  if (field_storage == FORM_FIELD_STORAGE_STORE) {
271  /* Store the content to a file */
272  if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
273  fstore.access.fp = NULL;
274  }
275  file_size = 0;
276  if (fstore.access.fp != NULL) {
277  size_t n = (size_t)
278  fwrite(val, 1, (size_t)vallen, fstore.access.fp);
279  if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
280  mg_cry(conn,
281  "%s: Cannot write file %s",
282  __func__,
283  path);
284  (void)mg_fclose(&fstore.access);
285  remove_bad_file(conn, path);
286  }
287  file_size += (int64_t)n;
288 
289  if (fstore.access.fp) {
290  r = mg_fclose(&fstore.access);
291  if (r == 0) {
292  /* stored successfully */
293  field_stored(conn, path, file_size, fdh);
294  } else {
295  mg_cry(conn,
296  "%s: Error saving file %s",
297  __func__,
298  path);
299  remove_bad_file(conn, path);
300  }
301  fstore.access.fp = NULL;
302  }
303 
304  } else {
305  mg_cry(conn, "%s: Cannot create file %s", __func__, path);
306  }
307  }
308 
309  /* if (field_storage == FORM_FIELD_STORAGE_READ) { */
310  /* The idea of "field_storage=read" is to let the API user read
311  * data chunk by chunk and to some data processing on the fly.
312  * This should avoid the need to store data in the server:
313  * It should neither be stored in memory, like
314  * "field_storage=get" does, nor in a file like
315  * "field_storage=store".
316  * However, for a "GET" request this does not make any much
317  * sense, since the data is already stored in memory, as it is
318  * part of the query string.
319  */
320  /* } */
321 
322  if ((field_storage & FORM_FIELD_STORAGE_ABORT)
323  == FORM_FIELD_STORAGE_ABORT) {
324  /* Stop parsing the request */
325  break;
326  }
327 
328  /* Proceed to next entry */
329  data = next;
330  }
331 
332  return field_count;
333  }
334 
335  content_type = mg_get_header(conn, "Content-Type");
336 
337  if (!content_type
338  || !mg_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED")
339  || !mg_strcasecmp(content_type, "APPLICATION/WWW-FORM-URLENCODED")) {
340  /* The form data is in the request body data, encoded in key/value
341  * pairs. */
342  int all_data_read = 0;
343 
344  /* Read body data and split it in keys and values.
345  * The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
346  * Here we use "POST", and read the data from the request body.
347  * The data read on the fly, so it is not required to buffer the
348  * entire request in memory before processing it. */
349  for (;;) {
350  const char *val;
351  const char *next;
352  ptrdiff_t keylen, vallen;
353  ptrdiff_t used;
354  int end_of_key_value_pair_found = 0;
355  int get_block;
356 
357  if ((size_t)buf_fill < (sizeof(buf) - 1)) {
358 
359  size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
360  r = mg_read(conn, buf + (size_t)buf_fill, to_read);
361  if (r < 0) {
362  /* read error */
363  return -1;
364  }
365  if (r != (int)to_read) {
366  /* TODO: Create a function to get "all_data_read" from
367  * the conn object. All data is read if the Content-Length
368  * has been reached, or if chunked encoding is used and
369  * the end marker has been read, or if the connection has
370  * been closed. */
371  all_data_read = 1;
372  }
373  buf_fill += r;
374  buf[buf_fill] = 0;
375  if (buf_fill < 1) {
376  break;
377  }
378  }
379 
380  val = strchr(buf, '=');
381 
382  if (!val) {
383  break;
384  }
385  keylen = val - buf;
386  val++;
387 
388  /* Call callback */
389  memset(path, 0, sizeof(path));
390  field_count++;
391  field_storage = url_encoded_field_found(conn,
392  buf,
393  (size_t)keylen,
394  NULL,
395  0,
396  path,
397  sizeof(path) - 1,
398  fdh);
399 
400  if ((field_storage & FORM_FIELD_STORAGE_ABORT)
401  == FORM_FIELD_STORAGE_ABORT) {
402  /* Stop parsing the request */
403  break;
404  }
405 
406  if (field_storage == FORM_FIELD_STORAGE_STORE) {
407  if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
408  fstore.access.fp = NULL;
409  }
410  file_size = 0;
411  if (!fstore.access.fp) {
412  mg_cry(conn, "%s: Cannot create file %s", __func__, path);
413  }
414  }
415 
416  get_block = 0;
417  /* Loop to read values larger than sizeof(buf)-keylen-2 */
418  do {
419  next = strchr(val, '&');
420  if (next) {
421  vallen = next - val;
422  next++;
423  end_of_key_value_pair_found = 1;
424  } else {
425  vallen = (ptrdiff_t)strlen(val);
426  next = val + vallen;
427  }
428 
429  if (field_storage == FORM_FIELD_STORAGE_GET) {
430 #if 0
431  if (!end_of_key_value_pair_found && !all_data_read) {
432  /* This callback will deliver partial contents */
433  }
434 #else
435  (void)all_data_read; /* avoid warning */
436 #endif
437 
438  /* Call callback */
440  ((get_block > 0) ? NULL : buf),
441  ((get_block > 0) ? 0
442  : (size_t)keylen),
443  val,
444  (size_t)vallen,
445  fdh);
446  get_block++;
447  }
448  if (fstore.access.fp) {
449  size_t n = (size_t)
450  fwrite(val, 1, (size_t)vallen, fstore.access.fp);
451  if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
452  mg_cry(conn,
453  "%s: Cannot write file %s",
454  __func__,
455  path);
456  mg_fclose(&fstore.access);
457  remove_bad_file(conn, path);
458  }
459  file_size += (int64_t)n;
460  }
461 
462  if (!end_of_key_value_pair_found) {
463  used = next - buf;
464  memmove(buf,
465  buf + (size_t)used,
466  sizeof(buf) - (size_t)used);
467  buf_fill -= (int)used;
468  if ((size_t)buf_fill < (sizeof(buf) - 1)) {
469 
470  size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
471  r = mg_read(conn, buf + (size_t)buf_fill, to_read);
472  if (r < 0) {
473  /* read error */
474  return -1;
475  }
476  if (r != (int)to_read) {
477  /* TODO: Create a function to get "all_data_read"
478  * from the conn object. All data is read if the
479  * Content-Length has been reached, or if chunked
480  * encoding is used and the end marker has been
481  * read, or if the connection has been closed. */
482  all_data_read = 1;
483  }
484  buf_fill += r;
485  buf[buf_fill] = 0;
486  if (buf_fill < 1) {
487  break;
488  }
489  val = buf;
490  }
491  }
492 
493  } while (!end_of_key_value_pair_found);
494 
495  if (fstore.access.fp) {
496  r = mg_fclose(&fstore.access);
497  if (r == 0) {
498  /* stored successfully */
499  field_stored(conn, path, file_size, fdh);
500  } else {
501  mg_cry(conn, "%s: Error saving file %s", __func__, path);
502  remove_bad_file(conn, path);
503  }
504  fstore.access.fp = NULL;
505  }
506 
507  /* Proceed to next entry */
508  used = next - buf;
509  memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
510  buf_fill -= (int)used;
511  }
512 
513  return field_count;
514  }
515 
516  if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
517  /* The form data is in the request body data, encoded as multipart
518  * content (see https://www.ietf.org/rfc/rfc1867.txt,
519  * https://www.ietf.org/rfc/rfc2388.txt). */
520  char *boundary;
521  size_t bl;
522  ptrdiff_t used;
523  struct mg_request_info part_header;
524  char *hbuf;
525  const char *content_disp, *hend, *fbeg, *fend, *nbeg, *nend;
526  const char *next;
527  unsigned part_no;
528 
529  memset(&part_header, 0, sizeof(part_header));
530 
531  /* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
532  bl = 20;
533  while (content_type[bl] == ' ') {
534  bl++;
535  }
536 
537  /* There has to be a BOUNDARY definition in the Content-Type header */
538  if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
539  /* Malformed request */
540  return -1;
541  }
542 
543  /* Copy boundary string to variable "boundary" */
544  fbeg = content_type + bl + 9;
545  bl = strlen(fbeg);
546  boundary = (char *)mg_malloc(bl + 1);
547  if (!boundary) {
548  /* Out of memory */
549  mg_cry(conn,
550  "%s: Cannot allocate memory for boundary [%lu]",
551  __func__,
552  (unsigned long)bl);
553  return -1;
554  }
555  memcpy(boundary, fbeg, bl);
556  boundary[bl] = 0;
557 
558  /* RFC 2046 permits the boundary string to be quoted. */
559  /* If the boundary is quoted, trim the quotes */
560  if (boundary[0] == '"') {
561  hbuf = strchr(boundary + 1, '"');
562  if ((!hbuf) || (*hbuf != '"')) {
563  /* Malformed request */
564  mg_free(boundary);
565  return -1;
566  }
567  *hbuf = 0;
568  memmove(boundary, boundary + 1, bl);
569  bl = strlen(boundary);
570  }
571 
572  /* Do some sanity checks for boundary lengths */
573  if (bl > 70) {
574  /* From RFC 2046:
575  * Boundary delimiters must not appear within the
576  * encapsulated material, and must be no longer
577  * than 70 characters, not counting the two
578  * leading hyphens.
579  */
580 
581  /* The initial sanity check
582  * (bl + 800 > sizeof(buf))
583  * is no longer required, since sizeof(buf) == 1024
584  *
585  * Original comment:
586  */
587  /* Sanity check: The algorithm can not work if bl >= sizeof(buf),
588  * and it will not work effectively, if the buf is only a few byte
589  * larger than bl, or if buf can not hold the multipart header
590  * plus the boundary.
591  * Check some reasonable number here, that should be fulfilled by
592  * any reasonable request from every browser. If it is not
593  * fulfilled, it might be a hand-made request, intended to
594  * interfere with the algorithm. */
595  mg_free(boundary);
596  return -1;
597  }
598  if (bl < 4) {
599  /* Sanity check: A boundary string of less than 4 bytes makes
600  * no sense either. */
601  mg_free(boundary);
602  return -1;
603  }
604 
605  for (part_no = 0;; part_no++) {
606  size_t towrite, n;
607  int get_block;
608 
609  r = mg_read(conn,
610  buf + (size_t)buf_fill,
611  sizeof(buf) - 1 - (size_t)buf_fill);
612  if (r < 0) {
613  /* read error */
614  mg_free(boundary);
615  return -1;
616  }
617  buf_fill += r;
618  buf[buf_fill] = 0;
619  if (buf_fill < 1) {
620  /* No data */
621  mg_free(boundary);
622  return -1;
623  }
624 
625  if (part_no == 0) {
626  int d = 0;
627  while ((d < buf_fill) && (buf[d] != '-')) {
628  d++;
629  }
630  if ((d > 0) && (buf[d] == '-')) {
631  memmove(buf, buf + d, (unsigned)buf_fill - (unsigned)d);
632  buf_fill -= d;
633  buf[buf_fill] = 0;
634  }
635  }
636 
637  if (buf[0] != '-' || buf[1] != '-') {
638  /* Malformed request */
639  mg_free(boundary);
640  return -1;
641  }
642  if (strncmp(buf + 2, boundary, bl)) {
643  /* Malformed request */
644  mg_free(boundary);
645  return -1;
646  }
647  if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
648  /* Every part must end with \r\n, if there is another part.
649  * The end of the request has an extra -- */
650  if (((size_t)buf_fill != (size_t)(bl + 6))
651  || (strncmp(buf + bl + 2, "--\r\n", 4))) {
652  /* Malformed request */
653  mg_free(boundary);
654  return -1;
655  }
656  /* End of the request */
657  break;
658  }
659 
660  /* Next, we need to get the part header: Read until \r\n\r\n */
661  hbuf = buf + bl + 4;
662  hend = strstr(hbuf, "\r\n\r\n");
663  if (!hend) {
664  /* Malformed request */
665  mg_free(boundary);
666  return -1;
667  }
668 
669  part_header.num_headers =
670  parse_http_headers(&hbuf, part_header.http_headers);
671  if ((hend + 2) != hbuf) {
672  /* Malformed request */
673  mg_free(boundary);
674  return -1;
675  }
676 
677  /* Skip \r\n\r\n */
678  hend += 4;
679 
680  /* According to the RFC, every part has to have a header field like:
681  * Content-Disposition: form-data; name="..." */
682  content_disp = get_header(part_header.http_headers,
683  part_header.num_headers,
684  "Content-Disposition");
685  if (!content_disp) {
686  /* Malformed request */
687  mg_free(boundary);
688  return -1;
689  }
690 
691  /* Get the mandatory name="..." part of the Content-Disposition
692  * header. */
693  nbeg = strstr(content_disp, "name=\"");
694  while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) {
695  /* It could be somethingname= instead of name= */
696  nbeg = strstr(nbeg + 1, "name=\"");
697  }
698 
699  /* This line is not required, but otherwise some compilers
700  * generate spurious warnings. */
701  nend = nbeg;
702  /* And others complain, the result is unused. */
703  (void)nend;
704 
705  /* If name=" is found, search for the closing " */
706  if (nbeg) {
707  nbeg += 6;
708  nend = strchr(nbeg, '\"');
709  if (!nend) {
710  /* Malformed request */
711  mg_free(boundary);
712  return -1;
713  }
714  } else {
715  /* name= without quotes is also allowed */
716  nbeg = strstr(content_disp, "name=");
717  while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) {
718  /* It could be somethingname= instead of name= */
719  nbeg = strstr(nbeg + 1, "name=");
720  }
721  if (!nbeg) {
722  /* Malformed request */
723  mg_free(boundary);
724  return -1;
725  }
726  nbeg += 5;
727 
728  /* RFC 2616 Sec. 2.2 defines a list of allowed
729  * separators, but many of them make no sense
730  * here, e.g. various brackets or slashes.
731  * If they are used, probably someone is
732  * trying to attack with curious hand made
733  * requests. Only ; , space and tab seem to be
734  * reasonable here. Ignore everything else. */
735  nend = nbeg + strcspn(nbeg, ",; \t");
736  }
737 
738  /* Get the optional filename="..." part of the Content-Disposition
739  * header. */
740  fbeg = strstr(content_disp, "filename=\"");
741  while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) {
742  /* It could be somethingfilename= instead of filename= */
743  fbeg = strstr(fbeg + 1, "filename=\"");
744  }
745 
746  /* This line is not required, but otherwise some compilers
747  * generate spurious warnings. */
748  fend = fbeg;
749 
750  /* If filename=" is found, search for the closing " */
751  if (fbeg) {
752  fbeg += 10;
753  fend = strchr(fbeg, '\"');
754 
755  if (!fend) {
756  /* Malformed request (the filename field is optional, but if
757  * it exists, it needs to be terminated correctly). */
758  mg_free(boundary);
759  return -1;
760  }
761 
762  /* TODO: check Content-Type */
763  /* Content-Type: application/octet-stream */
764  }
765  if (!fbeg) {
766  /* Try the same without quotes */
767  fbeg = strstr(content_disp, "filename=");
768  while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) {
769  /* It could be somethingfilename= instead of filename= */
770  fbeg = strstr(fbeg + 1, "filename=");
771  }
772  if (fbeg) {
773  fbeg += 9;
774  fend = fbeg + strcspn(fbeg, ",; \t");
775  }
776  }
777  if (!fbeg) {
778  fend = NULL;
779  }
780 
781  /* In theory, it could be possible that someone crafts
782  * a request like name=filename=xyz. Check if name and
783  * filename do not overlap. */
784  if (!(((ptrdiff_t)fbeg > (ptrdiff_t)nend)
785  || ((ptrdiff_t)nbeg > (ptrdiff_t)fend))) {
786  mg_free(boundary);
787  return -1;
788  }
789 
790  /* Call callback for new field */
791  memset(path, 0, sizeof(path));
792  field_count++;
793  field_storage = url_encoded_field_found(conn,
794  nbeg,
795  (size_t)(nend - nbeg),
796  fbeg,
797  (size_t)(fend - fbeg),
798  path,
799  sizeof(path) - 1,
800  fdh);
801 
802  /* If the boundary is already in the buffer, get the address,
803  * otherwise next will be NULL. */
804  next = search_boundary(hbuf,
805  (size_t)((buf - hbuf) + buf_fill),
806  boundary,
807  bl);
808 
809  if (field_storage == FORM_FIELD_STORAGE_STORE) {
810  /* Store the content to a file */
811  if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
812  fstore.access.fp = NULL;
813  }
814  file_size = 0;
815 
816  if (!fstore.access.fp) {
817  mg_cry(conn, "%s: Cannot create file %s", __func__, path);
818  }
819  }
820 
821  get_block = 0;
822  while (!next) {
823  /* Set "towrite" to the number of bytes available
824  * in the buffer */
825  towrite = (size_t)(buf - hend + buf_fill);
826  /* Subtract the boundary length, to deal with
827  * cases the boundary is only partially stored
828  * in the buffer. */
829  towrite -= bl + 4;
830 
831  if (field_storage == FORM_FIELD_STORAGE_GET) {
832  unencoded_field_get(conn,
833  ((get_block > 0) ? NULL : nbeg),
834  ((get_block > 0)
835  ? 0
836  : (size_t)(nend - nbeg)),
837  hend,
838  towrite,
839  fdh);
840  get_block++;
841  }
842 
843  if (field_storage == FORM_FIELD_STORAGE_STORE) {
844  if (fstore.access.fp) {
845 
846  /* Store the content of the buffer. */
847  n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
848  if ((n != towrite) || (ferror(fstore.access.fp))) {
849  mg_cry(conn,
850  "%s: Cannot write file %s",
851  __func__,
852  path);
853  mg_fclose(&fstore.access);
854  remove_bad_file(conn, path);
855  }
856  file_size += (int64_t)n;
857  }
858  }
859 
860  memmove(buf, hend + towrite, bl + 4);
861  buf_fill = (int)(bl + 4);
862  hend = buf;
863 
864  /* Read new data */
865  r = mg_read(conn,
866  buf + (size_t)buf_fill,
867  sizeof(buf) - 1 - (size_t)buf_fill);
868  if (r < 0) {
869  /* read error */
870  mg_free(boundary);
871  return -1;
872  }
873  buf_fill += r;
874  buf[buf_fill] = 0;
875  if (buf_fill < 1) {
876  /* No data */
877  mg_free(boundary);
878  return -1;
879  }
880 
881  /* Find boundary */
882  next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
883  }
884 
885  towrite = (size_t)(next - hend);
886 
887  if (field_storage == FORM_FIELD_STORAGE_GET) {
888  /* Call callback */
889  unencoded_field_get(conn,
890  ((get_block > 0) ? NULL : nbeg),
891  ((get_block > 0) ? 0
892  : (size_t)(nend - nbeg)),
893  hend,
894  towrite,
895  fdh);
896  }
897 
898  if (field_storage == FORM_FIELD_STORAGE_STORE) {
899 
900  if (fstore.access.fp) {
901  n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
902  if ((n != towrite) || (ferror(fstore.access.fp))) {
903  mg_cry(conn,
904  "%s: Cannot write file %s",
905  __func__,
906  path);
907  mg_fclose(&fstore.access);
908  remove_bad_file(conn, path);
909  } else {
910  file_size += (int64_t)n;
911  r = mg_fclose(&fstore.access);
912  if (r == 0) {
913  /* stored successfully */
914  field_stored(conn, path, file_size, fdh);
915  } else {
916  mg_cry(conn,
917  "%s: Error saving file %s",
918  __func__,
919  path);
920  remove_bad_file(conn, path);
921  }
922  }
923  fstore.access.fp = NULL;
924  }
925  }
926 
927  if ((field_storage & FORM_FIELD_STORAGE_ABORT)
928  == FORM_FIELD_STORAGE_ABORT) {
929  /* Stop parsing the request */
930  break;
931  }
932 
933  /* Remove from the buffer */
934  used = next - buf + 2;
935  memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
936  buf_fill -= (int)used;
937  }
938 
939  /* All parts handled */
940  mg_free(boundary);
941  return field_count;
942  }
943 
944  /* Unknown Content-Type */
945  return -1;
946 }
947 
948 
949 /* End of handle_form.inl */
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded)
Definition: civetweb.c:6360
static int parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS])
Definition: civetweb.c:9232
static int mg_fopen(const struct mg_connection *conn, const char *path, int mode, struct mg_file *filep)
Definition: civetweb.c:2847
static const char * get_header(const struct mg_header *hdr, int num_hdr, const char *name)
Definition: civetweb.c:3591
int(* field_store)(const char *path, long long file_size, void *user_data)
Definition: civetweb.h:1078
int mg_read(struct mg_connection *conn, void *buf, size_t len)
Definition: civetweb.c:6038
void mg_cry(const struct mg_connection *conn, const char *fmt,...)
Definition: civetweb.c:3283
static void remove_bad_file(const struct mg_connection *conn, const char *path)
Definition: civetweb.c:9120
static int unencoded_field_get(const struct mg_connection *conn, const char *key, size_t key_len, const char *value, size_t value_len, struct mg_form_data_handler *fdh)
static const char * search_boundary(const char *buf, size_t buf_len, const char *boundary, size_t boundary_len)
int mg_handle_form_request(struct mg_connection *conn, struct mg_form_data_handler *fdh)
ROOT::R::TRInterface & r
Definition: Object.C:4
static int url_encoded_field_found(const struct mg_connection *conn, const char *key, size_t key_len, const char *filename, size_t filename_len, char *path, size_t path_len, struct mg_form_data_handler *fdh)
Definition: handle_form.inl:24
#define mg_malloc_ctx(a, c)
Definition: civetweb.c:1305
static __inline void mg_free(void *a)
Definition: civetweb.c:1300
struct mg_header http_headers[MG_MAX_HEADERS]
Definition: civetweb.h:119
int mg_strcasecmp(const char *s1, const char *s2)
Definition: civetweb.c:2977
#define d(i)
Definition: RSha256.hxx:102
const char * mg_get_header(const struct mg_connection *conn, const char *name)
Definition: civetweb.c:3628
static int mg_fclose(struct mg_file_access *fileacc)
Definition: civetweb.c:2928
static int field_stored(const struct mg_connection *conn, const char *path, long long file_size, struct mg_form_data_handler *fdh)
int(* field_get)(const char *key, const char *value, size_t valuelen, void *user_data)
Definition: civetweb.h:1057
#define MG_FOPEN_MODE_WRITE
Definition: civetweb.c:2748
int(* field_found)(const char *key, const char *filename, char *path, size_t pathlen, void *user_data)
Definition: civetweb.h:1040
typedef void((*Func_t)())
#define STRUCT_FILE_INITIALIZER
Definition: civetweb.c:2019
static __inline void * mg_malloc(size_t a)
Definition: civetweb.c:1282
const Int_t n
Definition: legend1.C:16
static int url_encoded_field_get(const struct mg_connection *conn, const char *key, size_t key_len, const char *value, size_t value_len, struct mg_form_data_handler *fdh)
Definition: handle_form.inl:84
int mg_strncasecmp(const char *s1, const char *s2, size_t len)
Definition: civetweb.c:2962