2 * libwebsockets - Stateful urldecode for POST
4 * Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 #include "core/private.h"
24 #define LWS_MAX_ELEM_NAME 32
26 enum urldecode_stateful {
42 static const char * const mp_hdr[] = {
43 "content-disposition: ",
50 typedef int (*lws_urldecode_stateful_cb)(struct lws_spa *spa,
51 const char *name, char **buf, int len, int final);
53 struct lws_urldecode_stateful {
57 char name[LWS_MAX_ELEM_NAME];
58 char temp[LWS_MAX_ELEM_NAME];
59 char content_type[32];
60 char content_disp[32];
61 char content_disp_filename[256];
62 char mime_boundary[128];
69 unsigned int multipart_form_data:1;
70 unsigned int inside_quote:1;
71 unsigned int subname:1;
72 unsigned int boundary_real_crlf:1;
74 enum urldecode_stateful state;
76 lws_urldecode_stateful_cb output;
80 struct lws_urldecode_stateful *s;
81 lws_spa_create_info_t i;
89 static struct lws_urldecode_stateful *
90 lws_urldecode_s_create(struct lws_spa *spa, struct lws *wsi, char *out,
91 int out_len, lws_urldecode_stateful_cb output)
93 struct lws_urldecode_stateful *s;
98 s = lwsac_use_zero(spa->i.ac, sizeof(*s), spa->i.ac_chunk_size);
100 s = lws_zalloc(sizeof(*s), "stateful urldecode");
106 s->out_len = out_len;
116 if (lws_hdr_copy(wsi, buf, sizeof(buf),
117 WSI_TOKEN_HTTP_CONTENT_TYPE) > 0) {
118 /* multipart/form-data;
119 * boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
121 if (!strncmp(buf, "multipart/form-data", 19)) {
122 s->multipart_form_data = 1;
123 s->state = MT_LOOK_BOUND_IN;
125 p = strstr(buf, "boundary=");
128 s->mime_boundary[m++] = '\x0d';
129 s->mime_boundary[m++] = '\x0a';
130 s->mime_boundary[m++] = '-';
131 s->mime_boundary[m++] = '-';
132 while (m < (int)sizeof(s->mime_boundary) - 1 &&
134 s->mime_boundary[m++] = *p++;
136 s->mime_boundary[m] = '\0';
138 lwsl_info("boundary '%s'\n", s->mime_boundary);
147 lws_urldecode_s_process(struct lws_urldecode_stateful *s, const char *in,
154 if (s->pos == s->out_len - s->mp - 1) {
155 if (s->output(s->data, s->name, &s->out, s->pos,
164 /* states for url arg style */
169 s->name[s->pos] = '\0';
176 s->name[s->pos] = '\0';
177 if (s->output(s->data, s->name, &s->out,
178 s->pos, LWS_UFS_FINAL_CONTENT))
185 if (s->pos >= (int)sizeof(s->name) - 1) {
186 lwsl_notice("Name too long\n");
189 s->name[s->pos++] = *in++;
198 s->out[s->pos] = '\0';
199 if (s->output(s->data, s->name, &s->out,
200 s->pos, LWS_UFS_FINAL_CONTENT))
209 s->out[s->pos++] = ' ';
212 s->out[s->pos++] = *in++;
215 n = char_to_hex(*in);
225 n = char_to_hex(*in);
230 s->out[s->pos++] = s->sum | n;
235 /* states for multipart / mime style */
237 case MT_LOOK_BOUND_IN:
239 if (*in == s->mime_boundary[s->mp] &&
240 s->mime_boundary[s->mp]) {
243 if (!s->mime_boundary[s->mp]) {
245 s->state = MT_IGNORE1;
247 if (s->pos || was_end)
248 if (s->output(s->data, s->name,
250 LWS_UFS_FINAL_CONTENT))
255 s->content_disp[0] = '\0';
257 s->content_disp_filename[0] = '\0';
258 s->boundary_real_crlf = 1;
264 if (!s->boundary_real_crlf)
267 memcpy(s->out + s->pos,
268 s->mime_boundary + n, s->mp - n);
275 s->out[s->pos++] = *in;
283 if (c >= 'A' && c <= 'Z')
285 for (n = 0; n < (int)LWS_ARRAY_SIZE(mp_hdr); n++)
286 if (c == mp_hdr[n][s->mp]) {
292 /* Unknown header - ignore it */
293 s->state = MT_IGNORE1;
302 if (mp_hdr[hit][s->mp])
310 s->state = MT_LOOK_BOUND_IN;
316 /* form-data; name="file"; filename="t.txt" */
319 if (s->content_disp_filename[0])
320 if (s->output(s->data, s->name,
324 s->state = MT_IGNORE2;
335 s->inside_quote ^= 1;
341 s->temp[s->mp] = '\0';
346 if (s->mp < (int)sizeof(s->temp) - 1 &&
347 (*in != ' ' || s->inside_quote))
348 s->temp[s->mp++] = *in;
353 if (s->mp < (int)sizeof(s->content_disp) - 1)
354 s->content_disp[s->mp++] = *in;
355 if (s->mp < (int)sizeof(s->content_disp))
356 s->content_disp[s->mp] = '\0';
360 if (!strcmp(s->temp, "name")) {
361 if (s->mp < (int)sizeof(s->name) - 1)
362 s->name[s->mp++] = *in;
364 s->mp = (int)sizeof(s->name) - 1;
365 s->name[s->mp] = '\0';
369 if (!strcmp(s->temp, "filename")) {
370 if (s->mp < (int)sizeof(s->content_disp_filename) - 1)
371 s->content_disp_filename[s->mp++] = *in;
372 s->content_disp_filename[s->mp] = '\0';
381 s->state = MT_IGNORE2;
383 if (s->mp < (int)sizeof(s->content_type) - 1)
384 s->content_type[s->mp++] = *in;
385 s->content_type[s->mp] = '\0';
392 s->state = MT_IGNORE2;
394 s->state = MT_IGNORE3;
407 s->state = MT_IGNORE1;
409 s->state = MT_COMPLETED;
410 s->wsi->http.rx_content_remain = 0;
423 lws_urldecode_s_destroy(struct lws_spa *spa, struct lws_urldecode_stateful *s)
427 if (s->state != US_IDLE)
431 if (s->output(s->data, s->name, &s->out, s->pos,
432 LWS_UFS_FINAL_CONTENT))
435 if (s->output(s->data, s->name, NULL, 0, LWS_UFS_CLOSE))
445 lws_urldecode_spa_lookup(struct lws_spa *spa, const char *name)
447 const char * const *pp = spa->i.param_names;
450 for (n = 0; n < spa->i.count_params; n++) {
451 if (!strcmp(*pp, name))
454 if (spa->i.param_names_stride)
455 pp = (const char * const *)(((char *)pp) + spa->i.param_names_stride);
464 lws_urldecode_spa_cb(struct lws_spa *spa, const char *name, char **buf, int len,
469 if (final == LWS_UFS_CLOSE || spa->s->content_disp_filename[0]) {
471 n = spa->i.opt_cb(spa->i.opt_data, name,
472 spa->s->content_disp_filename,
473 buf ? *buf : NULL, len, final);
480 n = lws_urldecode_spa_lookup(spa, name);
481 if (n == -1 || !len) /* unrecognized */
486 spa->params[n] = *buf;
488 if ((*buf) + len >= spa->end) {
489 lwsl_info("%s: exceeded storage\n", __func__);
493 /* move it on inside storage */
497 spa->s->out_len -= len + 1;
499 spa->params[n] = lwsac_use(spa->i.ac, len + 1,
500 spa->i.ac_chunk_size);
504 memcpy(spa->params[n], *buf, len);
505 spa->params[n][len] = '\0';
508 spa->param_length[n] += len;
514 lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *i)
519 spa = lwsac_use_zero(i->ac, sizeof(*spa), i->ac_chunk_size);
521 spa = lws_zalloc(sizeof(*spa), "spa");
527 if (!spa->i.max_storage)
528 spa->i.max_storage = 512;
531 spa->storage = lwsac_use(i->ac, spa->i.max_storage,
534 spa->storage = lws_malloc(spa->i.max_storage, "spa");
539 spa->end = spa->storage + i->max_storage - 1;
541 if (i->count_params) {
543 spa->params = lwsac_use_zero(i->ac,
544 sizeof(char *) * i->count_params, i->ac_chunk_size);
546 spa->params = lws_zalloc(sizeof(char *) * i->count_params,
552 spa->s = lws_urldecode_s_create(spa, wsi, spa->storage, i->max_storage,
553 lws_urldecode_spa_cb);
557 if (i->count_params) {
559 spa->param_length = lwsac_use_zero(i->ac,
560 sizeof(int) * i->count_params, i->ac_chunk_size);
562 spa->param_length = lws_zalloc(sizeof(int) * i->count_params,
564 if (!spa->param_length)
568 lwsl_info("%s: Created SPA %p\n", __func__, spa);
573 lws_urldecode_s_destroy(spa, spa->s);
576 lws_free(spa->params);
579 lws_free(spa->storage);
591 lws_spa_create(struct lws *wsi, const char * const *param_names,
592 int count_params, int max_storage,
593 lws_spa_fileupload_cb opt_cb, void *opt_data)
595 lws_spa_create_info_t i;
597 memset(&i, 0, sizeof(i));
598 i.count_params = count_params;
599 i.max_storage = max_storage;
601 i.opt_data = opt_data;
602 i.param_names = param_names;
604 return lws_spa_create_via_info(wsi, &i);
608 lws_spa_process(struct lws_spa *spa, const char *in, int len)
611 lwsl_err("%s: NULL spa\n", __func__);
614 /* we reject any junk after the last part arrived and we finalized */
618 return lws_urldecode_s_process(spa->s, in, len);
622 lws_spa_get_length(struct lws_spa *spa, int n)
624 if (n >= spa->i.count_params)
627 return spa->param_length[n];
631 lws_spa_get_string(struct lws_spa *spa, int n)
633 if (n >= spa->i.count_params)
636 return spa->params[n];
640 lws_spa_finalize(struct lws_spa *spa)
646 lws_urldecode_s_destroy(spa, spa->s);
656 lws_spa_destroy(struct lws_spa *spa)
660 lwsl_info("%s: destroy spa %p\n", __func__, spa);
663 lws_urldecode_s_destroy(spa, spa->s);
666 lwsac_free(spa->i.ac);
668 lws_free(spa->param_length);
669 lws_free(spa->params);
670 lws_free(spa->storage);