Imported Upstream version 3.2.0
[platform/upstream/libwebsockets.git] / lib / roles / http / server / lws-spa.c
1 /*
2  * libwebsockets - Stateful urldecode for POST
3  *
4  * Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
5  *
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.
10  *
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.
15  *
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,
19  *  MA  02110-1301  USA
20  */
21
22 #include "core/private.h"
23
24 #define LWS_MAX_ELEM_NAME 32
25
26 enum urldecode_stateful {
27         US_NAME,
28         US_IDLE,
29         US_PC1,
30         US_PC2,
31
32         MT_LOOK_BOUND_IN,
33         MT_HNAME,
34         MT_DISP,
35         MT_TYPE,
36         MT_IGNORE1,
37         MT_IGNORE2,
38         MT_IGNORE3,
39         MT_COMPLETED,
40 };
41
42 static const char * const mp_hdr[] = {
43         "content-disposition: ",
44         "content-type: ",
45         "\x0d\x0a"
46 };
47
48 struct lws_spa;
49
50 typedef int (*lws_urldecode_stateful_cb)(struct lws_spa *spa,
51                 const char *name, char **buf, int len, int final);
52
53 struct lws_urldecode_stateful {
54         char *out;
55         struct lws_spa *data;
56         struct lws *wsi;
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];
63         int out_len;
64         int pos;
65         int hdr_idx;
66         int mp;
67         int sum;
68
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;
73
74         enum urldecode_stateful state;
75
76         lws_urldecode_stateful_cb output;
77 };
78
79 struct lws_spa {
80         struct lws_urldecode_stateful *s;
81         lws_spa_create_info_t i;
82         int *param_length;
83         char finalized;
84         char **params;
85         char *storage;
86         char *end;
87 };
88
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)
92 {
93         struct lws_urldecode_stateful *s;
94         char buf[205], *p;
95         int m = 0;
96
97         if (spa->i.ac)
98                 s = lwsac_use_zero(spa->i.ac, sizeof(*s), spa->i.ac_chunk_size);
99         else
100                 s = lws_zalloc(sizeof(*s), "stateful urldecode");
101
102         if (!s)
103                 return NULL;
104
105         s->out = out;
106         s->out_len  = out_len;
107         s->output = output;
108         s->pos = 0;
109         s->sum = 0;
110         s->mp = 0;
111         s->state = US_NAME;
112         s->name[0] = '\0';
113         s->data = spa;
114         s->wsi = wsi;
115
116         if (lws_hdr_copy(wsi, buf, sizeof(buf),
117                          WSI_TOKEN_HTTP_CONTENT_TYPE) > 0) {
118         /* multipart/form-data;
119          * boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
120
121                 if (!strncmp(buf, "multipart/form-data", 19)) {
122                         s->multipart_form_data = 1;
123                         s->state = MT_LOOK_BOUND_IN;
124                         s->mp = 2;
125                         p = strstr(buf, "boundary=");
126                         if (p) {
127                                 p += 9;
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 &&
133                                        *p && *p != ' ')
134                                         s->mime_boundary[m++] = *p++;
135
136                                 s->mime_boundary[m] = '\0';
137
138                                 lwsl_info("boundary '%s'\n", s->mime_boundary);
139                         }
140                 }
141         }
142
143         return s;
144 }
145
146 static int
147 lws_urldecode_s_process(struct lws_urldecode_stateful *s, const char *in,
148                         int len)
149 {
150         int n, m, hit = 0;
151         char c, was_end = 0;
152
153         while (len--) {
154                 if (s->pos == s->out_len - s->mp - 1) {
155                         if (s->output(s->data, s->name, &s->out, s->pos,
156                                       LWS_UFS_CONTENT))
157                                 return -1;
158
159                         was_end = s->pos;
160                         s->pos = 0;
161                 }
162                 switch (s->state) {
163
164                 /* states for url arg style */
165
166                 case US_NAME:
167                         s->inside_quote = 0;
168                         if (*in == '=') {
169                                 s->name[s->pos] = '\0';
170                                 s->pos = 0;
171                                 s->state = US_IDLE;
172                                 in++;
173                                 continue;
174                         }
175                         if (*in == '&') {
176                                 s->name[s->pos] = '\0';
177                                 if (s->output(s->data, s->name, &s->out,
178                                               s->pos, LWS_UFS_FINAL_CONTENT))
179                                         return -1;
180                                 s->pos = 0;
181                                 s->state = US_IDLE;
182                                 in++;
183                                 continue;
184                         }
185                         if (s->pos >= (int)sizeof(s->name) - 1) {
186                                 lwsl_notice("Name too long\n");
187                                 return -1;
188                         }
189                         s->name[s->pos++] = *in++;
190                         break;
191                 case US_IDLE:
192                         if (*in == '%') {
193                                 s->state++;
194                                 in++;
195                                 continue;
196                         }
197                         if (*in == '&') {
198                                 s->out[s->pos] = '\0';
199                                 if (s->output(s->data, s->name, &s->out,
200                                               s->pos, LWS_UFS_FINAL_CONTENT))
201                                         return -1;
202                                 s->pos = 0;
203                                 s->state = US_NAME;
204                                 in++;
205                                 continue;
206                         }
207                         if (*in == '+') {
208                                 in++;
209                                 s->out[s->pos++] = ' ';
210                                 continue;
211                         }
212                         s->out[s->pos++] = *in++;
213                         break;
214                 case US_PC1:
215                         n = char_to_hex(*in);
216                         if (n < 0)
217                                 return -1;
218
219                         in++;
220                         s->sum = n << 4;
221                         s->state++;
222                         break;
223
224                 case US_PC2:
225                         n = char_to_hex(*in);
226                         if (n < 0)
227                                 return -1;
228
229                         in++;
230                         s->out[s->pos++] = s->sum | n;
231                         s->state = US_IDLE;
232                         break;
233
234
235                 /* states for multipart / mime style */
236
237                 case MT_LOOK_BOUND_IN:
238 retry_as_first:
239                         if (*in == s->mime_boundary[s->mp] &&
240                             s->mime_boundary[s->mp]) {
241                                 in++;
242                                 s->mp++;
243                                 if (!s->mime_boundary[s->mp]) {
244                                         s->mp = 0;
245                                         s->state = MT_IGNORE1;
246
247                                         if (s->pos || was_end)
248                                                 if (s->output(s->data, s->name,
249                                                       &s->out, s->pos,
250                                                       LWS_UFS_FINAL_CONTENT))
251                                                         return -1;
252
253                                         s->pos = 0;
254
255                                         s->content_disp[0] = '\0';
256                                         s->name[0] = '\0';
257                                         s->content_disp_filename[0] = '\0';
258                                         s->boundary_real_crlf = 1;
259                                 }
260                                 continue;
261                         }
262                         if (s->mp) {
263                                 n = 0;
264                                 if (!s->boundary_real_crlf)
265                                         n = 2;
266                                 if (s->mp >= n) {
267                                         memcpy(s->out + s->pos,
268                                                s->mime_boundary + n, s->mp - n);
269                                         s->pos += s->mp;
270                                         s->mp = 0;
271                                         goto retry_as_first;
272                                 }
273                         }
274
275                         s->out[s->pos++] = *in;
276                         in++;
277                         s->mp = 0;
278                         break;
279
280                 case MT_HNAME:
281                         m = 0;
282                         c =*in;
283                         if (c >= 'A' && c <= 'Z')
284                                 c += 'a' - 'A';
285                         for (n = 0; n < (int)LWS_ARRAY_SIZE(mp_hdr); n++)
286                                 if (c == mp_hdr[n][s->mp]) {
287                                         m++;
288                                         hit = n;
289                                 }
290                         in++;
291                         if (!m) {
292                                 /* Unknown header - ignore it */
293                                 s->state = MT_IGNORE1;
294                                 s->mp = 0;
295                                 continue;
296                         }
297
298                         s->mp++;
299                         if (m != 1)
300                                 continue;
301
302                         if (mp_hdr[hit][s->mp])
303                                 continue;
304
305                         s->mp = 0;
306                         s->temp[0] = '\0';
307                         s->subname = 0;
308
309                         if (hit == 2)
310                                 s->state = MT_LOOK_BOUND_IN;
311                         else
312                                 s->state += hit + 1;
313                         break;
314
315                 case MT_DISP:
316                         /* form-data; name="file"; filename="t.txt" */
317
318                         if (*in == '\x0d') {
319                                 if (s->content_disp_filename[0])
320                                         if (s->output(s->data, s->name,
321                                                       &s->out, s->pos,
322                                                       LWS_UFS_OPEN))
323                                                 return -1;
324                                 s->state = MT_IGNORE2;
325                                 goto done;
326                         }
327                         if (*in == ';') {
328                                 s->subname = 1;
329                                 s->temp[0] = '\0';
330                                 s->mp = 0;
331                                 goto done;
332                         }
333
334                         if (*in == '\"') {
335                                 s->inside_quote ^= 1;
336                                 goto done;
337                         }
338
339                         if (s->subname) {
340                                 if (*in == '=') {
341                                         s->temp[s->mp] = '\0';
342                                         s->subname = 0;
343                                         s->mp = 0;
344                                         goto done;
345                                 }
346                                 if (s->mp < (int)sizeof(s->temp) - 1 &&
347                                     (*in != ' ' || s->inside_quote))
348                                         s->temp[s->mp++] = *in;
349                                 goto done;
350                         }
351
352                         if (!s->temp[0]) {
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';
357                                 goto done;
358                         }
359
360                         if (!strcmp(s->temp, "name")) {
361                                 if (s->mp < (int)sizeof(s->name) - 1)
362                                         s->name[s->mp++] = *in;
363                                 else
364                                         s->mp = (int)sizeof(s->name) - 1;
365                                 s->name[s->mp] = '\0';
366                                 goto done;
367                         }
368
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';
373                                 goto done;
374                         }
375 done:
376                         in++;
377                         break;
378
379                 case MT_TYPE:
380                         if (*in == '\x0d')
381                                 s->state = MT_IGNORE2;
382                         else {
383                                 if (s->mp < (int)sizeof(s->content_type) - 1)
384                                         s->content_type[s->mp++] = *in;
385                                 s->content_type[s->mp] = '\0';
386                         }
387                         in++;
388                         break;
389
390                 case MT_IGNORE1:
391                         if (*in == '\x0d')
392                                 s->state = MT_IGNORE2;
393                         if (*in == '-')
394                                 s->state = MT_IGNORE3;
395                         in++;
396                         break;
397
398                 case MT_IGNORE2:
399                         s->mp = 0;
400                         if (*in == '\x0a')
401                                 s->state = MT_HNAME;
402                         in++;
403                         break;
404
405                 case MT_IGNORE3:
406                         if (*in == '\x0d')
407                                 s->state = MT_IGNORE1;
408                         if (*in == '-') {
409                                 s->state = MT_COMPLETED;
410                                 s->wsi->http.rx_content_remain = 0;
411                         }
412                         in++;
413                         break;
414                 case MT_COMPLETED:
415                         break;
416                 }
417         }
418
419         return 0;
420 }
421
422 static int
423 lws_urldecode_s_destroy(struct lws_spa *spa, struct lws_urldecode_stateful *s)
424 {
425         int ret = 0;
426
427         if (s->state != US_IDLE)
428                 ret = -1;
429
430         if (!ret)
431                 if (s->output(s->data, s->name, &s->out, s->pos,
432                               LWS_UFS_FINAL_CONTENT))
433                         ret = -1;
434
435         if (s->output(s->data, s->name, NULL, 0, LWS_UFS_CLOSE))
436                 return -1;
437
438         if (!spa->i.ac)
439                 lws_free(s);
440
441         return ret;
442 }
443
444 static int
445 lws_urldecode_spa_lookup(struct lws_spa *spa, const char *name)
446 {
447         const char * const *pp = spa->i.param_names;
448         int n;
449
450         for (n = 0; n < spa->i.count_params; n++) {
451                 if (!strcmp(*pp, name))
452                         return n;
453
454                 if (spa->i.param_names_stride)
455                         pp = (const char * const *)(((char *)pp) + spa->i.param_names_stride);
456                 else
457                         pp++;
458         }
459
460         return -1;
461 }
462
463 static int
464 lws_urldecode_spa_cb(struct lws_spa *spa, const char *name, char **buf, int len,
465                      int final)
466 {
467         int n;
468
469         if (final == LWS_UFS_CLOSE || spa->s->content_disp_filename[0]) {
470                 if (spa->i.opt_cb) {
471                         n = spa->i.opt_cb(spa->i.opt_data, name,
472                                         spa->s->content_disp_filename,
473                                         buf ? *buf : NULL, len, final);
474
475                         if (n < 0)
476                                 return -1;
477                 }
478                 return 0;
479         }
480         n = lws_urldecode_spa_lookup(spa, name);
481         if (n == -1 || !len) /* unrecognized */
482                 return 0;
483
484         if (!spa->i.ac) {
485                 if (!spa->params[n])
486                         spa->params[n] = *buf;
487
488                 if ((*buf) + len >= spa->end) {
489                         lwsl_info("%s: exceeded storage\n", __func__);
490                         return -1;
491                 }
492
493                 /* move it on inside storage */
494                 (*buf) += len;
495                 *((*buf)++) = '\0';
496
497                 spa->s->out_len -= len + 1;
498         } else {
499                 spa->params[n] = lwsac_use(spa->i.ac, len + 1,
500                                            spa->i.ac_chunk_size);
501                 if (!spa->params[n])
502                         return -1;
503
504                 memcpy(spa->params[n], *buf, len);
505                 spa->params[n][len] = '\0';
506         }
507
508         spa->param_length[n] += len;
509
510         return 0;
511 }
512
513 struct lws_spa *
514 lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *i)
515 {
516         struct lws_spa *spa;
517
518         if (i->ac)
519                 spa = lwsac_use_zero(i->ac, sizeof(*spa), i->ac_chunk_size);
520         else
521                 spa = lws_zalloc(sizeof(*spa), "spa");
522
523         if (!spa)
524                 return NULL;
525
526         spa->i = *i;
527         if (!spa->i.max_storage)
528                 spa->i.max_storage = 512;
529
530         if (i->ac)
531                 spa->storage = lwsac_use(i->ac, spa->i.max_storage,
532                                          i->ac_chunk_size);
533         else
534                 spa->storage = lws_malloc(spa->i.max_storage, "spa");
535
536         if (!spa->storage)
537                 goto bail2;
538
539         spa->end = spa->storage + i->max_storage - 1;
540
541         if (i->count_params) {
542                 if (i->ac)
543                         spa->params = lwsac_use_zero(i->ac,
544                                 sizeof(char *) * i->count_params, i->ac_chunk_size);
545                 else
546                         spa->params = lws_zalloc(sizeof(char *) * i->count_params,
547                                          "spa params");
548                 if (!spa->params)
549                         goto bail3;
550         }
551
552         spa->s = lws_urldecode_s_create(spa, wsi, spa->storage, i->max_storage,
553                                         lws_urldecode_spa_cb);
554         if (!spa->s)
555                 goto bail4;
556
557         if (i->count_params) {
558                 if (i->ac)
559                         spa->param_length = lwsac_use_zero(i->ac,
560                                 sizeof(int) * i->count_params, i->ac_chunk_size);
561                 else
562                         spa->param_length = lws_zalloc(sizeof(int) * i->count_params,
563                                                 "spa param len");
564                 if (!spa->param_length)
565                         goto bail5;
566         }
567
568         lwsl_info("%s: Created SPA %p\n", __func__, spa);
569
570         return spa;
571
572 bail5:
573         lws_urldecode_s_destroy(spa, spa->s);
574 bail4:
575         if (!i->ac)
576                 lws_free(spa->params);
577 bail3:
578         if (!i->ac)
579                 lws_free(spa->storage);
580 bail2:
581         if (!i->ac)
582                 lws_free(spa);
583
584         if (i->ac)
585                 lwsac_free(i->ac);
586
587         return NULL;
588 }
589
590 struct lws_spa *
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)
594 {
595         lws_spa_create_info_t i;
596
597         memset(&i, 0, sizeof(i));
598         i.count_params = count_params;
599         i.max_storage = max_storage;
600         i.opt_cb = opt_cb;
601         i.opt_data = opt_data;
602         i.param_names = param_names;
603
604         return lws_spa_create_via_info(wsi, &i);
605 }
606
607 int
608 lws_spa_process(struct lws_spa *spa, const char *in, int len)
609 {
610         if (!spa) {
611                 lwsl_err("%s: NULL spa\n", __func__);
612                 return -1;
613         }
614         /* we reject any junk after the last part arrived and we finalized */
615         if (spa->finalized)
616                 return 0;
617
618         return lws_urldecode_s_process(spa->s, in, len);
619 }
620
621 int
622 lws_spa_get_length(struct lws_spa *spa, int n)
623 {
624         if (n >= spa->i.count_params)
625                 return 0;
626
627         return spa->param_length[n];
628 }
629
630 const char *
631 lws_spa_get_string(struct lws_spa *spa, int n)
632 {
633         if (n >= spa->i.count_params)
634                 return NULL;
635
636         return spa->params[n];
637 }
638
639 int
640 lws_spa_finalize(struct lws_spa *spa)
641 {
642         if (!spa)
643                 return 0;
644
645         if (spa->s) {
646                 lws_urldecode_s_destroy(spa, spa->s);
647                 spa->s = NULL;
648         }
649
650         spa->finalized = 1;
651
652         return 0;
653 }
654
655 int
656 lws_spa_destroy(struct lws_spa *spa)
657 {
658         int n = 0;
659
660         lwsl_info("%s: destroy spa %p\n", __func__, spa);
661
662         if (spa->s)
663                 lws_urldecode_s_destroy(spa, spa->s);
664
665         if (spa->i.ac)
666                 lwsac_free(spa->i.ac);
667         else {
668                 lws_free(spa->param_length);
669                 lws_free(spa->params);
670                 lws_free(spa->storage);
671                 lws_free(spa);
672         }
673
674         return n;
675 }