Logo ROOT   6.10/09
Reference Guide
handle_form.inl
Go to the documentation of this file.
1 /* Copyright (c) 2016 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 = mg_malloc(value_len + 1);
94  int value_dec_len;
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  return fdh->field_get(key_dec,
111  value_dec,
112  (size_t)value_dec_len,
113  fdh->user_data);
114 }
115 
116 
117 static int
118 field_stored(const struct mg_connection *conn,
119  const char *path,
120  long long file_size,
121  struct mg_form_data_handler *fdh)
122 {
123  /* Equivalent to "upload" callback of "mg_upload". */
124 
125  (void)conn; /* we do not need mg_cry here, so conn is currently unused */
126 
127  return fdh->field_store(path, file_size, fdh->user_data);
128 }
129 
130 
131 static const char *
132 search_boundary(const char *buf,
133  size_t buf_len,
134  const char *boundary,
135  size_t boundary_len)
136 {
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;
140  int i;
141 
142  for (i = 0; i <= clen; i++) {
143  if (!memcmp(buf + i, "\r\n--", 4)) {
144  if (!memcmp(buf + i + 4, boundary, boundary_len)) {
145  return buf + i;
146  }
147  }
148  }
149  return NULL;
150 }
151 
152 
153 int
154 mg_handle_form_request(struct mg_connection *conn,
155  struct mg_form_data_handler *fdh)
156 {
157  const char *content_type;
158  char path[512];
159  char buf[1024];
160  int field_storage;
161  int buf_fill = 0;
162  int r;
163  int field_count = 0;
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 */
167 
168  int has_body_data =
169  (conn->request_info.content_length > 0) || (conn->is_chunked);
170 
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.
180  */
181 
182  if (!has_body_data) {
183  const char *data;
184 
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. */
188  return -1;
189  }
190 
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
194  * pairs. */
195  data = conn->request_info.query_string;
196  if (!data) {
197  /* No query string. */
198  return -1;
199  }
200 
201  /* Split data in a=1&b=xy&c=3&c=4 ... */
202  while (*data) {
203  const char *val = strchr(data, '=');
204  const char *next;
205  ptrdiff_t keylen, vallen;
206 
207  if (!val) {
208  break;
209  }
210  keylen = val - data;
211 
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
216  * callback function
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
222  */
223  memset(path, 0, sizeof(path));
224  field_count++;
225  field_storage = url_encoded_field_found(conn,
226  data,
227  (size_t)keylen,
228  NULL,
229  0,
230  path,
231  sizeof(path) - 1,
232  fdh);
233 
234  val++;
235  next = strchr(val, '&');
236  if (next) {
237  vallen = next - val;
238  next++;
239  } else {
240  vallen = (ptrdiff_t)strlen(val);
241  next = val + vallen;
242  }
243 
244  if (field_storage == FORM_FIELD_STORAGE_GET) {
245  /* Call callback */
247  conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
248  }
249  if (field_storage == FORM_FIELD_STORAGE_STORE) {
250  /* Store the content to a file */
251  if (mg_fopen(conn, path, "wb", &fstore) == 0) {
252  fstore.fp = NULL;
253  }
254  file_size = 0;
255  if (fstore.fp != NULL) {
256  size_t n =
257  (size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
258  if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
259  mg_cry(conn,
260  "%s: Cannot write file %s",
261  __func__,
262  path);
263  fclose(fstore.fp);
264  fstore.fp = NULL;
265  remove_bad_file(conn, path);
266  }
267  file_size += (int64_t)n;
268 
269  if (fstore.fp) {
270  r = fclose(fstore.fp);
271  if (r == 0) {
272  /* stored successfully */
273  field_stored(conn, path, file_size, fdh);
274  } else {
275  mg_cry(conn,
276  "%s: Error saving file %s",
277  __func__,
278  path);
279  remove_bad_file(conn, path);
280  }
281  fstore.fp = NULL;
282  }
283 
284  } else {
285  mg_cry(conn, "%s: Cannot create file %s", __func__, path);
286  }
287  }
288 
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.
299  */
300  /* } */
301 
302  if ((field_storage & FORM_FIELD_STORAGE_ABORT)
303  == FORM_FIELD_STORAGE_ABORT) {
304  /* Stop parsing the request */
305  break;
306  }
307 
308  /* Proceed to next entry */
309  data = next;
310  }
311 
312  return field_count;
313  }
314 
315  content_type = mg_get_header(conn, "Content-Type");
316 
317  if (!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
321  * pairs. */
322  int all_data_read = 0;
323 
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. */
329  for (;;) {
330  const char *val;
331  const char *next;
332  ptrdiff_t keylen, vallen;
333  ptrdiff_t used;
334  int end_of_key_value_pair_found = 0;
335  int get_block;
336 
337  if ((size_t)buf_fill < (sizeof(buf) - 1)) {
338 
339  size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
340  r = mg_read(conn, buf + (size_t)buf_fill, to_read);
341  if (r < 0) {
342  /* read error */
343  return -1;
344  }
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
350  * been closed. */
351  all_data_read = 1;
352  }
353  buf_fill += r;
354  buf[buf_fill] = 0;
355  if (buf_fill < 1) {
356  break;
357  }
358  }
359 
360  val = strchr(buf, '=');
361 
362  if (!val) {
363  break;
364  }
365  keylen = val - buf;
366  val++;
367 
368  /* Call callback */
369  memset(path, 0, sizeof(path));
370  field_count++;
371  field_storage = url_encoded_field_found(conn,
372  buf,
373  (size_t)keylen,
374  NULL,
375  0,
376  path,
377  sizeof(path) - 1,
378  fdh);
379 
380  if ((field_storage & FORM_FIELD_STORAGE_ABORT)
381  == FORM_FIELD_STORAGE_ABORT) {
382  /* Stop parsing the request */
383  break;
384  }
385 
386  if (field_storage == FORM_FIELD_STORAGE_STORE) {
387  if (mg_fopen(conn, path, "wb", &fstore) == 0) {
388  fstore.fp = NULL;
389  }
390  file_size = 0;
391  if (!fstore.fp) {
392  mg_cry(conn, "%s: Cannot create file %s", __func__, path);
393  }
394  }
395 
396  get_block = 0;
397  /* Loop to read values larger than sizeof(buf)-keylen-2 */
398  do {
399  next = strchr(val, '&');
400  if (next) {
401  vallen = next - val;
402  next++;
403  end_of_key_value_pair_found = 1;
404  } else {
405  vallen = (ptrdiff_t)strlen(val);
406  next = val + vallen;
407  }
408 
409  if (field_storage == FORM_FIELD_STORAGE_GET) {
410 #if 0
411  if (!end_of_key_value_pair_found && !all_data_read) {
412  /* This callback will deliver partial contents */
413  }
414 #else
415  (void)all_data_read; /* avoid warning */
416 #endif
417 
418  /* Call callback */
420  ((get_block > 0) ? NULL : buf),
421  ((get_block > 0) ? 0
422  : (size_t)keylen),
423  val,
424  (size_t)vallen,
425  fdh);
426  get_block++;
427  }
428  if (fstore.fp) {
429  size_t n =
430  (size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
431  if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
432  mg_cry(conn,
433  "%s: Cannot write file %s",
434  __func__,
435  path);
436  fclose(fstore.fp);
437  fstore.fp = NULL;
438  remove_bad_file(conn, path);
439  }
440  file_size += (int64_t)n;
441  }
442 
443  if (!end_of_key_value_pair_found) {
444  used = next - buf;
445  memmove(buf,
446  buf + (size_t)used,
447  sizeof(buf) - (size_t)used);
448  buf_fill -= (int)used;
449  if ((size_t)buf_fill < (sizeof(buf) - 1)) {
450 
451  size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
452  r = mg_read(conn, buf + (size_t)buf_fill, to_read);
453  if (r < 0) {
454  /* read error */
455  return -1;
456  }
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. */
463  all_data_read = 1;
464  }
465  buf_fill += r;
466  buf[buf_fill] = 0;
467  if (buf_fill < 1) {
468  break;
469  }
470  val = buf;
471  }
472  }
473 
474  } while (!end_of_key_value_pair_found);
475 
476  if (fstore.fp) {
477  r = fclose(fstore.fp);
478  if (r == 0) {
479  /* stored successfully */
480  field_stored(conn, path, file_size, fdh);
481  } else {
482  mg_cry(conn, "%s: Error saving file %s", __func__, path);
483  remove_bad_file(conn, path);
484  }
485  fstore.fp = NULL;
486  }
487 
488  /* Proceed to next entry */
489  used = next - buf;
490  memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
491  buf_fill -= (int)used;
492  }
493 
494  return field_count;
495  }
496 
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;
502  size_t bl;
503  ptrdiff_t used;
504  struct mg_request_info part_header;
505  char *hbuf, *hend, *fbeg, *fend, *nbeg, *nend;
506  const char *content_disp;
507  const char *next;
508 
509  memset(&part_header, 0, sizeof(part_header));
510 
511  /* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
512  bl = 20;
513  while (content_type[bl] == ' ') {
514  bl++;
515  }
516 
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 */
520  return -1;
521  }
522 
523  boundary = content_type + bl + 9;
524  bl = strlen(boundary);
525 
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
530  * plus the boundary.
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. */
535  return -1;
536  }
537 
538  for (;;) {
539  size_t towrite, n;
540  int get_block;
541 
542  r = mg_read(conn,
543  buf + (size_t)buf_fill,
544  sizeof(buf) - 1 - (size_t)buf_fill);
545  if (r < 0) {
546  /* read error */
547  return -1;
548  }
549  buf_fill += r;
550  buf[buf_fill] = 0;
551  if (buf_fill < 1) {
552  /* No data */
553  return -1;
554  }
555 
556  if (buf[0] != '-' || buf[1] != '-') {
557  /* Malformed request */
558  return -1;
559  }
560  if (strncmp(buf + 2, boundary, bl)) {
561  /* Malformed request */
562  return -1;
563  }
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 */
570  return -1;
571  }
572  /* End of the request */
573  break;
574  }
575 
576  /* Next, we need to get the part header: Read until \r\n\r\n */
577  hbuf = buf + bl + 4;
578  hend = strstr(hbuf, "\r\n\r\n");
579  if (!hend) {
580  /* Malformed request */
581  return -1;
582  }
583 
584  parse_http_headers(&hbuf, &part_header);
585  if ((hend + 2) != hbuf) {
586  /* Malformed request */
587  return -1;
588  }
589 
590  /* Skip \r\n\r\n */
591  hend += 4;
592 
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");
596  if (!content_disp) {
597  /* Malformed request */
598  return -1;
599  }
600 
601  /* Get the mandatory name="..." part of the Content-Disposition
602  * header. */
603  nbeg = strstr(content_disp, "name=\"");
604  if (!nbeg) {
605  /* Malformed request */
606  return -1;
607  }
608  nbeg += 6;
609  nend = strchr(nbeg, '\"');
610  if (!nend) {
611  /* Malformed request */
612  return -1;
613  }
614 
615  /* Get the optional filename="..." part of the Content-Disposition
616  * header. */
617  fbeg = strstr(content_disp, "filename=\"");
618  if (fbeg) {
619  fbeg += 10;
620  fend = strchr(fbeg, '\"');
621  if (!fend) {
622  /* Malformed request (the filename field is optional, but if
623  * it exists, it needs to be terminated correctly). */
624  return -1;
625  }
626 
627  /* TODO: check Content-Type */
628  /* Content-Type: application/octet-stream */
629 
630  } else {
631  fend = fbeg;
632  }
633 
634  memset(path, 0, sizeof(path));
635  field_count++;
636  field_storage = url_encoded_field_found(conn,
637  nbeg,
638  (size_t)(nend - nbeg),
639  fbeg,
640  (size_t)(fend - fbeg),
641  path,
642  sizeof(path) - 1,
643  fdh);
644 
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),
649  boundary,
650  bl);
651 
652  if (field_storage == FORM_FIELD_STORAGE_STORE) {
653  /* Store the content to a file */
654  if (mg_fopen(conn, path, "wb", &fstore) == 0) {
655  fstore.fp = NULL;
656  }
657  file_size = 0;
658 
659  if (!fstore.fp) {
660  mg_cry(conn, "%s: Cannot create file %s", __func__, path);
661  }
662  }
663 
664  get_block = 0;
665  while (!next) {
666  /* Set "towrite" to the number of bytes available
667  * in the buffer */
668  towrite = (size_t)(buf - hend + buf_fill);
669  /* Subtract the boundary length, to deal with
670  * cases the boundary is only partially stored
671  * in the buffer. */
672  towrite -= bl + 4;
673 
674  if (field_storage == FORM_FIELD_STORAGE_GET) {
676  ((get_block > 0) ? NULL : nbeg),
677  ((get_block > 0)
678  ? 0
679  : (size_t)(nend - nbeg)),
680  hend,
681  towrite,
682  fdh);
683  get_block++;
684  }
685 
686  if (field_storage == FORM_FIELD_STORAGE_STORE) {
687  if (fstore.fp) {
688 
689  /* Store the content of the buffer. */
690  n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
691  if ((n != towrite) || (ferror(fstore.fp))) {
692  mg_cry(conn,
693  "%s: Cannot write file %s",
694  __func__,
695  path);
696  fclose(fstore.fp);
697  fstore.fp = NULL;
698  remove_bad_file(conn, path);
699  }
700  file_size += (int64_t)n;
701  }
702  }
703 
704  memmove(buf, hend + towrite, bl + 4);
705  buf_fill = (int)(bl + 4);
706  hend = buf;
707 
708  /* Read new data */
709  r = mg_read(conn,
710  buf + (size_t)buf_fill,
711  sizeof(buf) - 1 - (size_t)buf_fill);
712  if (r < 0) {
713  /* read error */
714  return -1;
715  }
716  buf_fill += r;
717  buf[buf_fill] = 0;
718  if (buf_fill < 1) {
719  /* No data */
720  return -1;
721  }
722 
723  /* Find boundary */
724  next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
725  }
726 
727  towrite = (size_t)(next - hend);
728 
729  if (field_storage == FORM_FIELD_STORAGE_GET) {
730  /* Call callback */
732  ((get_block > 0) ? NULL : nbeg),
733  ((get_block > 0) ? 0
734  : (size_t)(nend - nbeg)),
735  hend,
736  towrite,
737  fdh);
738  }
739 
740  if (field_storage == FORM_FIELD_STORAGE_STORE) {
741 
742  if (fstore.fp) {
743  n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
744  if ((n != towrite) || (ferror(fstore.fp))) {
745  mg_cry(conn,
746  "%s: Cannot write file %s",
747  __func__,
748  path);
749  fclose(fstore.fp);
750  fstore.fp = NULL;
751  remove_bad_file(conn, path);
752  }
753  file_size += (int64_t)n;
754  }
755  }
756 
757  if (field_storage == FORM_FIELD_STORAGE_STORE) {
758 
759  if (fstore.fp) {
760  r = fclose(fstore.fp);
761  if (r == 0) {
762  /* stored successfully */
763  field_stored(conn, path, file_size, fdh);
764  } else {
765  mg_cry(conn,
766  "%s: Error saving file %s",
767  __func__,
768  path);
769  remove_bad_file(conn, path);
770  }
771  fstore.fp = NULL;
772  }
773  }
774 
775  if ((field_storage & FORM_FIELD_STORAGE_ABORT)
776  == FORM_FIELD_STORAGE_ABORT) {
777  /* Stop parsing the request */
778  break;
779  }
780 
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;
785  }
786 
787  /* All parts handled */
788  return field_count;
789  }
790 
791  /* Unknown Content-Type */
792  return -1;
793 }
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded)
Definition: civetweb.c:4414
int(* field_store)(const char *path, long long file_size, void *user_data)
Definition: civetweb.h:817
int mg_read(struct mg_connection *conn, void *buf, size_t len)
Definition: civetweb.c:4146
void mg_cry(const struct mg_connection *conn, const char *fmt,...)
Definition: civetweb.c:1894
static const char * get_header(const struct mg_request_info *ri, const char *name)
Definition: civetweb.c:2047
#define NULL
Definition: RtypesCore.h:88
static void remove_bad_file(const struct mg_connection *conn, const char *path)
Definition: civetweb.c:6753
static void parse_http_headers(char **buf, struct mg_request_info *ri)
Definition: civetweb.c:6815
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)
TRandom2 r(17)
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
static int mg_fopen(const struct mg_connection *conn, const char *path, const char *mode, struct file *filep)
Definition: civetweb.c:1528
int mg_strcasecmp(const char *s1, const char *s2)
Definition: civetweb.c:1606
const char * mg_get_header(const struct mg_connection *conn, const char *name)
Definition: civetweb.c:2063
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:796
int(* field_found)(const char *key, const char *filename, char *path, size_t pathlen, void *user_data)
Definition: civetweb.h:779
typedef void((*Func_t)())
#define STRUCT_FILE_INITIALIZER
Definition: civetweb.c:1053
Definition: file.py:1
static __inline void * mg_malloc(size_t a)
Definition: civetweb.c:758
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:1591