1 /*
2 * CGI form variable and array functions for CUPS.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright © 2007-2019 by Apple Inc.
6 * Copyright © 1997-2005 by Easy Software Products.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * information.
10 */
11
12 /*
13 * Include necessary headers...
14 */
15
16 /*#define DEBUG*/
17 #include "cgi-private.h"
18 #include <cups/http.h>
19
20
21 /*
22 * Session ID name
23 */
24
25 #define CUPS_SID "org.cups.sid"
26
27
28 /*
29 * Data structure to hold all the CGI form variables and arrays...
30 */
31
32 typedef struct /**** Form variable structure ****/
33 {
34 char *name; /* Name of variable */
35 int nvalues, /* Number of values */
36 avalues; /* Number of values allocated */
37 char **values; /* Value(s) of variable */
38 } _cgi_var_t;
39
40
41 /*
42 * Local globals...
43 */
44
45 static int num_cookies = 0;/* Number of cookies */
46 static cups_option_t *cookies = NULL;/* Cookies */
47 static int form_count = 0, /* Form variable count */
48 form_alloc = 0; /* Number of variables allocated */
49 static _cgi_var_t *form_vars = NULL;
50 /* Form variables */
51 static cgi_file_t *form_file = NULL;
52 /* Uploaded file */
53
54
55 /*
56 * Local functions...
57 */
58
59 static void cgi_add_variable(const char *name, int element,
60 const char *value);
61 static int cgi_compare_variables(const _cgi_var_t *v1,
62 const _cgi_var_t *v2);
63 static _cgi_var_t *cgi_find_variable(const char *name);
64 static void cgi_initialize_cookies(void);
65 static int cgi_initialize_get(void);
66 static int cgi_initialize_multipart(const char *boundary);
67 static int cgi_initialize_post(void);
68 static int cgi_initialize_string(const char *data);
69 static const char *cgi_passwd(const char *prompt);
70 static const char *cgi_set_sid(void);
71 static void cgi_sort_variables(void);
72 static void cgi_unlink_file(void);
73
74
75 /*
76 * 'cgiCheckVariables()' - Check for the presence of "required" variables.
77 *
78 * Names may be separated by spaces and/or commas.
79 */
80
81 int /* O - 1 if all variables present, 0 otherwise */
cgiCheckVariables(const char * names)82 cgiCheckVariables(const char *names) /* I - Variables to look for */
83 {
84 char name[255], /* Current variable name */
85 *s; /* Pointer in string */
86 const char *val; /* Value of variable */
87 int element; /* Array element number */
88
89
90 if (names == NULL)
91 return (1);
92
93 while (*names != '\0')
94 {
95 while (*names == ' ' || *names == ',')
96 names ++;
97
98 for (s = name; *names != '\0' && *names != ' ' && *names != ','; s ++, names ++)
99 *s = *names;
100
101 *s = 0;
102 if (name[0] == '\0')
103 break;
104
105 if ((s = strrchr(name, '-')) != NULL)
106 {
107 *s = '\0';
108 element = atoi(s + 1) - 1;
109 val = cgiGetArray(name, element);
110 }
111 else
112 val = cgiGetVariable(name);
113
114 if (val == NULL)
115 return (0);
116
117 if (*val == '\0')
118 {
119 free((void *)val);
120 return (0); /* Can't be blank, either! */
121 }
122
123 free((void *)val);
124 }
125
126 return (1);
127 }
128
129
130 /*
131 * 'cgiClearVariables()' - Clear all form variables.
132 */
133
134 void
cgiClearVariables(void)135 cgiClearVariables(void)
136 {
137 int i, j; /* Looping vars */
138 _cgi_var_t *v; /* Current variable */
139
140
141 fputs("DEBUG: cgiClearVariables called.\n", stderr);
142
143 for (v = form_vars, i = form_count; i > 0; v ++, i --)
144 {
145 free(v->name);
146 for (j = 0; j < v->nvalues; j ++)
147 if (v->values[j])
148 free(v->values[j]);
149 }
150
151 form_count = 0;
152
153 cgi_unlink_file();
154 }
155
156
157 /*
158 * 'cgiGetArray()' - Get an element from a form array.
159 */
160
161 char * /* O - Element value or NULL */
cgiGetArray(const char * name,int element)162 cgiGetArray(const char *name, /* I - Name of array variable */
163 int element) /* I - Element number (0 to N) */
164 {
165 _cgi_var_t *var; /* Pointer to variable */
166
167
168 if ((var = cgi_find_variable(name)) == NULL)
169 return (NULL);
170
171 if (element < 0 || element >= var->nvalues)
172 return (NULL);
173
174 if (var->values[element] == NULL)
175 return (NULL);
176
177 return (strdup(var->values[element]));
178 }
179
180
181 /*
182 * 'cgiGetCheckbox()' - Get a checkbox value, deleting any invalid values.
183 */
184
185 int /* O - 1 if checked, 0 otherwise */
cgiGetCheckbox(const char * name)186 cgiGetCheckbox(const char *name) /* I - Name of form field */
187 {
188 _cgi_var_t *var = cgi_find_variable(name);
189 /* Found variable */
190 const char *value = var ? var->values[var->nvalues - 1] : NULL;
191 int ret; /* Return value */
192
193
194 ret = value && !_cups_strcasecmp(value, "checked");
195
196 if (!ret && value)
197 {
198 /*
199 * Delete the invalid checkbox value...
200 */
201
202 int i = var - form_vars, j;
203
204 form_count --;
205
206 for (j = 0; j < var->nvalues; j ++)
207 free(var->values[j]);
208 free(var->name);
209 free(var->values);
210
211 if (i < form_count)
212 memmove(var, var + 1, (size_t)(form_count - i) * sizeof(_cgi_var_t));
213 }
214
215 return (ret);
216 }
217
218
219 /*
220 * 'cgiGetCookie()' - Get a cookie value.
221 */
222
223 const char * /* O - Value or NULL */
cgiGetCookie(const char * name)224 cgiGetCookie(const char *name) /* I - Name of cookie */
225 {
226 return (cupsGetOption(name, num_cookies, cookies));
227 }
228
229
230 /*
231 * 'cgiGetFile()' - Get the file (if any) that was submitted in the form.
232 */
233
234 const cgi_file_t * /* O - Attached file or NULL */
cgiGetFile(void)235 cgiGetFile(void)
236 {
237 return (form_file);
238 }
239
240
241 /*
242 * 'cgiGetSize()' - Get the size of a form array value.
243 */
244
245 int /* O - Number of elements */
cgiGetSize(const char * name)246 cgiGetSize(const char *name) /* I - Name of variable */
247 {
248 _cgi_var_t *var; /* Pointer to variable */
249
250
251 if ((var = cgi_find_variable(name)) == NULL)
252 return (0);
253
254 return (var->nvalues);
255 }
256
257
258 /*
259 * 'cgiGetTextfield()' - Get a textfield value, deleting any invalid values.
260 */
261
262 const char * /* O - Value or NULL */
cgiGetTextfield(const char * name)263 cgiGetTextfield(const char *name) /* I - Name of form field */
264 {
265 _cgi_var_t *var = cgi_find_variable(name);
266 /* Found variable */
267 const char *value = var ? var->values[var->nvalues - 1] : NULL;
268
269
270 if (value && strchr(value, '\"') != NULL)
271 {
272 /*
273 * Delete the invalid text field value...
274 */
275
276 int i = var - form_vars, j;
277
278 form_count --;
279
280 for (j = 0; j < var->nvalues; j ++)
281 free(var->values[j]);
282 free(var->name);
283 free(var->values);
284
285 if (i < form_count)
286 memmove(var, var + 1, (size_t)(form_count - i) * sizeof(_cgi_var_t));
287
288 value = NULL;
289 }
290
291 return (value ? strdup(value) : NULL);
292 }
293
294
295 /*
296 * 'cgiGetVariable()' - Get a CGI variable from the database.
297 *
298 * Returns NULL if the variable doesn't exist. If the variable is an
299 * array of values, returns the last element.
300 */
301
302 char * /* O - Value of variable */
cgiGetVariable(const char * name)303 cgiGetVariable(const char *name) /* I - Name of variable */
304 {
305 const _cgi_var_t *var; /* Returned variable */
306
307
308 var = cgi_find_variable(name);
309
310 return ((var == NULL) ? NULL : strdup(var->values[var->nvalues - 1]));
311 }
312
313
314 /*
315 * 'cgiInitialize()' - Initialize the CGI variable "database".
316 */
317
318 int /* O - Non-zero if there was form data */
cgiInitialize(void)319 cgiInitialize(void)
320 {
321 const char *method, /* Form posting method */
322 *content_type, /* Content-Type of post data */
323 *cups_sid_cookie, /* SID cookie */
324 *cups_sid_form; /* SID form variable */
325
326
327 /*
328 * Setup a password callback for authentication...
329 */
330
331 cupsSetPasswordCB(cgi_passwd);
332
333 /*
334 * Set the locale so that times, etc. are formatted properly...
335 */
336
337 setlocale(LC_ALL, "");
338
339 #ifdef DEBUG
340 /*
341 * Disable output buffering to find bugs...
342 */
343
344 setbuf(stdout, NULL);
345 #endif /* DEBUG */
346
347 /*
348 * Get cookies...
349 */
350
351 cgi_initialize_cookies();
352
353 if ((cups_sid_cookie = cgiGetCookie(CUPS_SID)) == NULL)
354 {
355 fputs("DEBUG: " CUPS_SID " cookie not found, initializing!\n", stderr);
356 cups_sid_cookie = cgi_set_sid();
357 }
358
359 fprintf(stderr, "DEBUG: " CUPS_SID " cookie is \"%s\"\n", cups_sid_cookie);
360
361 /*
362 * Get the request method (GET or POST)...
363 */
364
365 method = getenv("REQUEST_METHOD");
366 content_type = getenv("CONTENT_TYPE");
367 if (!method)
368 return (0);
369
370 /*
371 * Grab form data from the corresponding location...
372 */
373
374 if (!_cups_strcasecmp(method, "GET"))
375 return (cgi_initialize_get());
376 else if (!_cups_strcasecmp(method, "POST") && content_type)
377 {
378 const char *boundary = strstr(content_type, "boundary=");
379
380 if (boundary)
381 boundary += 9;
382
383 if (!strncmp(content_type, "multipart/form-data; ", 21))
384 {
385 if (!cgi_initialize_multipart(boundary))
386 return (0);
387 }
388 else if (!cgi_initialize_post())
389 return (0);
390
391 if ((cups_sid_form = cgiGetVariable(CUPS_SID)) == NULL ||
392 strcmp(cups_sid_cookie, cups_sid_form))
393 {
394 if (cups_sid_form)
395 fprintf(stderr, "DEBUG: " CUPS_SID " form variable is \"%s\"\n",
396 cups_sid_form);
397 else
398 fputs("DEBUG: " CUPS_SID " form variable is not present.\n", stderr);
399
400 free((void *)cups_sid_form);
401
402 cgiClearVariables();
403
404 return (0);
405 }
406 else
407 {
408 free((void *)cups_sid_form);
409
410 return (1);
411 }
412 }
413 else
414 return (0);
415 }
416
417
418 /*
419 * 'cgiIsPOST()' - Determine whether this page was POSTed.
420 */
421
422 int /* O - 1 if POST, 0 if GET */
cgiIsPOST(void)423 cgiIsPOST(void)
424 {
425 const char *method; /* REQUEST_METHOD environment variable */
426
427
428 if ((method = getenv("REQUEST_METHOD")) == NULL)
429 return (0);
430 else
431 return (!strcmp(method, "POST"));
432 }
433
434
435 /*
436 * 'cgiSetArray()' - Set array element N to the specified string.
437 *
438 * If the variable array is smaller than (element + 1), the intervening
439 * elements are set to NULL.
440 */
441
442 void
cgiSetArray(const char * name,int element,const char * value)443 cgiSetArray(const char *name, /* I - Name of variable */
444 int element, /* I - Element number (0 to N) */
445 const char *value) /* I - Value of variable */
446 {
447 int i; /* Looping var */
448 _cgi_var_t *var; /* Returned variable */
449
450
451 if (name == NULL || value == NULL || element < 0 || element > 100000)
452 return;
453
454 fprintf(stderr, "DEBUG: cgiSetArray: %s[%d]=\"%s\"\n", name, element, value);
455
456 if ((var = cgi_find_variable(name)) == NULL)
457 {
458 cgi_add_variable(name, element, value);
459 cgi_sort_variables();
460 }
461 else
462 {
463 if (element >= var->avalues)
464 {
465 char **temp; /* Temporary pointer */
466
467 temp = (char **)realloc((void *)(var->values), sizeof(char *) * (size_t)(element + 16));
468 if (!temp)
469 return;
470
471 var->avalues = element + 16;
472 var->values = temp;
473 }
474
475 if (element >= var->nvalues)
476 {
477 for (i = var->nvalues; i < element; i ++)
478 var->values[i] = NULL;
479
480 var->nvalues = element + 1;
481 }
482 else if (var->values[element])
483 free((char *)var->values[element]);
484
485 var->values[element] = strdup(value);
486 }
487 }
488
489
490 /*
491 * 'cgiSetCookie()' - Set a cookie value.
492 */
493
494 void
cgiSetCookie(const char * name,const char * value,const char * path,const char * domain,time_t expires,int secure)495 cgiSetCookie(const char *name, /* I - Name */
496 const char *value, /* I - Value */
497 const char *path, /* I - Path (typically "/") */
498 const char *domain, /* I - Domain name */
499 time_t expires, /* I - Expiration date (0 for session) */
500 int secure) /* I - Require SSL */
501 {
502 num_cookies = cupsAddOption(name, value, num_cookies, &cookies);
503
504 printf("Set-Cookie: %s=%s;", name, value);
505 if (path)
506 printf(" path=%s;", path);
507 if (domain)
508 printf(" domain=%s;", domain);
509 if (expires)
510 {
511 char date[256]; /* Date string */
512
513 printf(" expires=%s;", httpGetDateString2(expires, date, sizeof(date)));
514 }
515 if (secure)
516 puts(" httponly; secure;");
517 else
518 puts(" httponly;");
519 }
520
521
522 /*
523 * 'cgiSetSize()' - Set the array size.
524 */
525
526 void
cgiSetSize(const char * name,int size)527 cgiSetSize(const char *name, /* I - Name of variable */
528 int size) /* I - Number of elements (0 to N) */
529 {
530 int i; /* Looping var */
531 _cgi_var_t *var; /* Returned variable */
532
533
534 if (name == NULL || size < 0 || size > 100000)
535 return;
536
537 if ((var = cgi_find_variable(name)) == NULL)
538 return;
539
540 if (size >= var->avalues)
541 {
542 char **temp; /* Temporary pointer */
543
544 temp = (char **)realloc((void *)(var->values), sizeof(char *) * (size_t)(size + 16));
545 if (!temp)
546 return;
547
548 var->avalues = size + 16;
549 var->values = temp;
550 }
551
552 if (size > var->nvalues)
553 {
554 for (i = var->nvalues; i < size; i ++)
555 var->values[i] = NULL;
556 }
557 else if (size < var->nvalues)
558 {
559 for (i = size; i < var->nvalues; i ++)
560 if (var->values[i])
561 free((void *)(var->values[i]));
562 }
563
564 var->nvalues = size;
565 }
566
567
568 /*
569 * 'cgiSetVariable()' - Set a CGI variable in the database.
570 *
571 * If the variable is an array, this truncates the array to a single element.
572 */
573
574 void
cgiSetVariable(const char * name,const char * value)575 cgiSetVariable(const char *name, /* I - Name of variable */
576 const char *value) /* I - Value of variable */
577 {
578 int i; /* Looping var */
579 _cgi_var_t *var; /* Returned variable */
580
581
582 if (name == NULL || value == NULL)
583 return;
584
585 fprintf(stderr, "cgiSetVariable: %s=\"%s\"\n", name, value);
586
587 if ((var = cgi_find_variable(name)) == NULL)
588 {
589 cgi_add_variable(name, 0, value);
590 cgi_sort_variables();
591 }
592 else
593 {
594 for (i = 0; i < var->nvalues; i ++)
595 if (var->values[i])
596 free((char *)var->values[i]);
597
598 var->values[0] = strdup(value);
599 var->nvalues = 1;
600 }
601 }
602
603
604 /*
605 * 'cgi_add_variable()' - Add a form variable.
606 */
607
608 static void
cgi_add_variable(const char * name,int element,const char * value)609 cgi_add_variable(const char *name, /* I - Variable name */
610 int element, /* I - Array element number */
611 const char *value) /* I - Variable value */
612 {
613 _cgi_var_t *var; /* New variable */
614
615
616 if (name == NULL || value == NULL || element < 0 || element > 100000)
617 return;
618
619 if (form_count >= form_alloc)
620 {
621 _cgi_var_t *temp_vars; /* Temporary form pointer */
622
623
624 if (form_alloc == 0)
625 temp_vars = malloc(sizeof(_cgi_var_t) * 16);
626 else
627 temp_vars = realloc(form_vars, (size_t)(form_alloc + 16) * sizeof(_cgi_var_t));
628
629 if (!temp_vars)
630 return;
631
632 var = temp_vars + form_count;
633
634 if ((var->values = calloc((size_t)element + 1, sizeof(char *))) == NULL)
635 {
636 /*
637 * Rollback changes
638 */
639
640 if (form_alloc == 0)
641 free(temp_vars);
642 return;
643 }
644 form_vars = temp_vars;
645 form_alloc += 16;
646 }
647 else
648 {
649 var = form_vars + form_count;
650 if ((var->values = calloc((size_t)element + 1, sizeof(char *))) == NULL)
651 return;
652 }
653
654 var->name = strdup(name);
655 var->nvalues = element + 1;
656 var->avalues = element + 1;
657 var->values[element] = strdup(value);
658
659 form_count ++;
660 }
661
662
663 /*
664 * 'cgi_compare_variables()' - Compare two variables.
665 */
666
667 static int /* O - Result of comparison */
cgi_compare_variables(const _cgi_var_t * v1,const _cgi_var_t * v2)668 cgi_compare_variables(
669 const _cgi_var_t *v1, /* I - First variable */
670 const _cgi_var_t *v2) /* I - Second variable */
671 {
672 return (_cups_strcasecmp(v1->name, v2->name));
673 }
674
675
676 /*
677 * 'cgi_find_variable()' - Find a variable.
678 */
679
680 static _cgi_var_t * /* O - Variable pointer or NULL */
cgi_find_variable(const char * name)681 cgi_find_variable(const char *name) /* I - Name of variable */
682 {
683 _cgi_var_t key; /* Search key */
684
685
686 if (form_count < 1 || name == NULL)
687 return (NULL);
688
689 key.name = (char *)name;
690
691 return ((_cgi_var_t *)bsearch(&key, form_vars, (size_t)form_count, sizeof(_cgi_var_t),
692 (int (*)(const void *, const void *))cgi_compare_variables));
693 }
694
695
696 /*
697 * 'cgi_initialize_cookies()' - Initialize cookies.
698 */
699
700 static void
cgi_initialize_cookies(void)701 cgi_initialize_cookies(void)
702 {
703 const char *cookie; /* HTTP_COOKIE environment variable */
704 char name[128], /* Name string */
705 value[512], /* Value string */
706 *ptr; /* Pointer into name/value */
707
708
709 if ((cookie = getenv("HTTP_COOKIE")) == NULL)
710 return;
711
712 while (*cookie)
713 {
714 int skip = 0; /* Skip this cookie? */
715
716 /*
717 * Skip leading whitespace...
718 */
719
720 while (isspace(*cookie & 255))
721 cookie ++;
722 if (!*cookie)
723 break;
724
725 /*
726 * Copy the name...
727 */
728
729 for (ptr = name; *cookie && *cookie != '=';)
730 if (ptr < (name + sizeof(name) - 1))
731 {
732 *ptr++ = *cookie++;
733 }
734 else
735 {
736 skip = 1;
737 cookie ++;
738 }
739
740 if (*cookie != '=')
741 break;
742
743 *ptr = '\0';
744 cookie ++;
745
746 /*
747 * Then the value...
748 */
749
750 if (*cookie == '\"')
751 {
752 for (cookie ++, ptr = value; *cookie && *cookie != '\"';)
753 if (ptr < (value + sizeof(value) - 1))
754 {
755 *ptr++ = *cookie++;
756 }
757 else
758 {
759 skip = 1;
760 cookie ++;
761 }
762
763 if (*cookie == '\"')
764 cookie ++;
765 else
766 skip = 1;
767 }
768 else
769 {
770 for (ptr = value; *cookie && *cookie != ';';)
771 if (ptr < (value + sizeof(value) - 1))
772 {
773 *ptr++ = *cookie++;
774 }
775 else
776 {
777 skip = 1;
778 cookie ++;
779 }
780 }
781
782 if (*cookie == ';')
783 cookie ++;
784 else if (*cookie)
785 skip = 1;
786
787 *ptr = '\0';
788
789 /*
790 * Then add the cookie to an array as long as the name doesn't start with
791 * "$"...
792 */
793
794 if (name[0] != '$' && !skip)
795 num_cookies = cupsAddOption(name, value, num_cookies, &cookies);
796 }
797 }
798
799
800 /*
801 * 'cgi_initialize_get()' - Initialize form variables using the GET method.
802 */
803
804 static int /* O - 1 if form data read */
cgi_initialize_get(void)805 cgi_initialize_get(void)
806 {
807 char *data; /* Pointer to form data string */
808
809
810 /*
811 * Check to see if there is anything for us to read...
812 */
813
814 data = getenv("QUERY_STRING");
815 if (data == NULL || strlen(data) == 0)
816 return (0);
817
818 /*
819 * Parse it out and return...
820 */
821
822 return (cgi_initialize_string(data));
823 }
824
825
826 /*
827 * 'cgi_initialize_multipart()' - Initialize variables and file using the POST
828 * method.
829 *
830 * TODO: Update to support files > 2GB.
831 */
832
833 static int /* O - 1 if form data was read */
cgi_initialize_multipart(const char * boundary)834 cgi_initialize_multipart(
835 const char *boundary) /* I - Boundary string */
836 {
837 char line[10240], /* MIME header line */
838 name[1024], /* Form variable name */
839 filename[1024], /* Form filename */
840 mimetype[1024], /* MIME media type */
841 bstring[256], /* Boundary string to look for */
842 *ptr, /* Pointer into name/filename */
843 *end; /* End of buffer */
844 int ch, /* Character from file */
845 fd; /* Temporary file descriptor */
846 size_t blen; /* Length of boundary string */
847
848
849 /*
850 * Read multipart form data until we run out...
851 */
852
853 name[0] = '\0';
854 filename[0] = '\0';
855 mimetype[0] = '\0';
856
857 snprintf(bstring, sizeof(bstring), "\r\n--%s", boundary);
858 blen = strlen(bstring);
859
860 while (fgets(line, sizeof(line), stdin))
861 {
862 if (!strcmp(line, "\r\n"))
863 {
864 /*
865 * End of headers, grab value...
866 */
867
868 if (filename[0])
869 {
870 /*
871 * Read an embedded file...
872 */
873
874 if (form_file)
875 {
876 /*
877 * Remove previous file...
878 */
879
880 cgi_unlink_file();
881 }
882
883 /*
884 * Allocate memory for the new file...
885 */
886
887 if ((form_file = calloc(1, sizeof(cgi_file_t))) == NULL)
888 return (0);
889
890 form_file->name = strdup(name);
891 form_file->filename = strdup(filename);
892 form_file->mimetype = strdup(mimetype);
893
894 fd = cupsTempFd(form_file->tempfile, sizeof(form_file->tempfile));
895
896 if (fd < 0)
897 return (0);
898
899 atexit(cgi_unlink_file);
900
901 /*
902 * Copy file data to the temp file...
903 */
904
905 ptr = line;
906
907 while ((ch = getchar()) != EOF)
908 {
909 *ptr++ = (char)ch;
910
911 if ((size_t)(ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
912 {
913 ptr -= blen;
914 break;
915 }
916
917 if ((ptr - line - (int)blen) >= 8192)
918 {
919 /*
920 * Write out the first 8k of the buffer...
921 */
922
923 write(fd, line, 8192);
924 memmove(line, line + 8192, (size_t)(ptr - line - 8192));
925 ptr -= 8192;
926 }
927 }
928
929 /*
930 * Write the rest of the data and close the temp file...
931 */
932
933 if (ptr > line)
934 write(fd, line, (size_t)(ptr - line));
935
936 close(fd);
937 }
938 else
939 {
940 /*
941 * Just get a form variable; the current code only handles
942 * form values up to 10k in size...
943 */
944
945 ptr = line;
946 end = line + sizeof(line) - 1;
947
948 while ((ch = getchar()) != EOF)
949 {
950 if (ptr < end)
951 *ptr++ = (char)ch;
952
953 if ((size_t)(ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
954 {
955 ptr -= blen;
956 break;
957 }
958 }
959
960 *ptr = '\0';
961
962 /*
963 * Set the form variable...
964 */
965
966 if ((ptr = strrchr(name, '-')) != NULL && isdigit(ptr[1] & 255))
967 {
968 /*
969 * Set a specific index in the array...
970 */
971
972 *ptr++ = '\0';
973 if (line[0])
974 cgiSetArray(name, atoi(ptr) - 1, line);
975 }
976 else if ((ptr = cgiGetVariable(name)) != NULL)
977 {
978 /*
979 * Add another element in the array...
980 */
981
982 free(ptr);
983 cgiSetArray(name, cgiGetSize(name), line);
984 }
985 else
986 {
987 /*
988 * Just set the line...
989 */
990
991 cgiSetVariable(name, line);
992 }
993 }
994
995 /*
996 * Read the rest of the current line...
997 */
998
999 fgets(line, sizeof(line), stdin);
1000
1001 /*
1002 * Clear the state vars...
1003 */
1004
1005 name[0] = '\0';
1006 filename[0] = '\0';
1007 mimetype[0] = '\0';
1008 }
1009 else if (!_cups_strncasecmp(line, "Content-Disposition:", 20))
1010 {
1011 if ((ptr = strstr(line + 20, " name=\"")) != NULL)
1012 {
1013 strlcpy(name, ptr + 7, sizeof(name));
1014
1015 if ((ptr = strchr(name, '\"')) != NULL)
1016 *ptr = '\0';
1017 }
1018
1019 if ((ptr = strstr(line + 20, " filename=\"")) != NULL)
1020 {
1021 strlcpy(filename, ptr + 11, sizeof(filename));
1022
1023 if ((ptr = strchr(filename, '\"')) != NULL)
1024 *ptr = '\0';
1025 }
1026 }
1027 else if (!_cups_strncasecmp(line, "Content-Type:", 13))
1028 {
1029 for (ptr = line + 13; isspace(*ptr & 255); ptr ++);
1030
1031 strlcpy(mimetype, ptr, sizeof(mimetype));
1032
1033 for (ptr = mimetype + strlen(mimetype) - 1;
1034 ptr > mimetype && isspace(*ptr & 255);
1035 *ptr-- = '\0');
1036 }
1037 }
1038
1039 /*
1040 * Return 1 for "form data found"...
1041 */
1042
1043 return (1);
1044 }
1045
1046
1047 /*
1048 * 'cgi_initialize_post()' - Initialize variables using the POST method.
1049 */
1050
1051 static int /* O - 1 if form data was read */
cgi_initialize_post(void)1052 cgi_initialize_post(void)
1053 {
1054 char *content_length, /* Length of input data (string) */
1055 *data; /* Pointer to form data string */
1056 size_t length, /* Length of input data */
1057 tbytes; /* Total number of bytes read */
1058 ssize_t nbytes; /* Number of bytes read this read() */
1059 int status; /* Return status */
1060
1061
1062 /*
1063 * Check to see if there is anything for us to read...
1064 */
1065
1066 content_length = getenv("CONTENT_LENGTH");
1067 if (content_length == NULL || atoi(content_length) <= 0)
1068 return (0);
1069
1070 /*
1071 * Get the length of the input stream and allocate a buffer for it...
1072 */
1073
1074 length = (size_t)strtol(content_length, NULL, 10);
1075 data = malloc(length + 1); // lgtm [cpp/uncontrolled-allocation-size]
1076
1077 if (data == NULL)
1078 return (0);
1079
1080 /*
1081 * Read the data into the buffer...
1082 */
1083
1084 for (tbytes = 0; tbytes < length; tbytes += (size_t)nbytes)
1085 if ((nbytes = read(0, data + tbytes, (size_t)(length - tbytes))) < 0)
1086 {
1087 if (errno != EAGAIN)
1088 {
1089 free(data);
1090 return (0);
1091 }
1092 else
1093 nbytes = 0;
1094 }
1095 else if (nbytes == 0)
1096 {
1097 /*
1098 * CUPS STR #3176: OpenBSD: Early end-of-file on POST data causes 100% CPU
1099 *
1100 * This should never happen, but does on OpenBSD. If we see early end-of-
1101 * file, treat this as an error and process no data.
1102 */
1103
1104 free(data);
1105 return (0);
1106 }
1107
1108 data[length] = '\0';
1109
1110 /*
1111 * Parse it out...
1112 */
1113
1114 status = cgi_initialize_string(data);
1115
1116 /*
1117 * Free the data and return...
1118 */
1119
1120 free(data);
1121
1122 return (status);
1123 }
1124
1125
1126 /*
1127 * 'cgi_initialize_string()' - Initialize form variables from a string.
1128 */
1129
1130 static int /* O - 1 if form data was processed */
cgi_initialize_string(const char * data)1131 cgi_initialize_string(const char *data) /* I - Form data string */
1132 {
1133 int done; /* True if we're done reading a form variable */
1134 char *s, /* Pointer to current form string */
1135 ch, /* Temporary character */
1136 name[255], /* Name of form variable */
1137 value[65536], /* Variable value */
1138 *temp; /* Temporary pointer */
1139
1140
1141 /*
1142 * Check input...
1143 */
1144
1145 if (data == NULL)
1146 return (0);
1147
1148 /*
1149 * Loop until we've read all the form data...
1150 */
1151
1152 while (*data != '\0')
1153 {
1154 /*
1155 * Get the variable name...
1156 */
1157
1158 for (s = name; *data != '\0'; data ++)
1159 if (*data == '=')
1160 break;
1161 else if (*data >= ' ' && s < (name + sizeof(name) - 1))
1162 *s++ = *data;
1163
1164 *s = '\0';
1165 if (*data == '=')
1166 data ++;
1167 else
1168 return (0);
1169
1170 /*
1171 * Read the variable value...
1172 */
1173
1174 for (s = value, done = 0; !done && *data != '\0'; data ++)
1175 switch (*data)
1176 {
1177 case '&' : /* End of data... */
1178 done = 1;
1179 break;
1180
1181 case '+' : /* Escaped space character */
1182 if (s < (value + sizeof(value) - 1))
1183 *s++ = ' ';
1184 break;
1185
1186 case '%' : /* Escaped control character */
1187 /*
1188 * Read the hex code...
1189 */
1190
1191 if (!isxdigit(data[1] & 255) || !isxdigit(data[2] & 255))
1192 return (0);
1193
1194 if (s < (value + sizeof(value) - 1))
1195 {
1196 data ++;
1197 ch = *data - '0';
1198 if (ch > 9)
1199 ch -= 7;
1200 *s = (char)(ch << 4);
1201
1202 data ++;
1203 ch = *data - '0';
1204 if (ch > 9)
1205 ch -= 7;
1206 *s++ |= ch;
1207 }
1208 else
1209 data += 2;
1210 break;
1211
1212 default : /* Other characters come straight through */
1213 if (*data >= ' ' && s < (value + sizeof(value) - 1))
1214 *s++ = *data;
1215 break;
1216 }
1217
1218 *s = '\0'; /* nul terminate the string */
1219
1220 /*
1221 * Remove trailing whitespace...
1222 */
1223
1224 if (s > value)
1225 s --;
1226
1227 while (s >= value && isspace(*s & 255))
1228 *s-- = '\0';
1229
1230 /*
1231 * Add the string to the variable "database"...
1232 */
1233
1234 if ((s = strrchr(name, '-')) != NULL && isdigit(s[1] & 255))
1235 {
1236 *s++ = '\0';
1237 if (value[0])
1238 cgiSetArray(name, atoi(s) - 1, value);
1239 }
1240 else if ((temp = cgiGetVariable(name)) != NULL)
1241 {
1242 free(temp);
1243 cgiSetArray(name, cgiGetSize(name), value);
1244 }
1245 else
1246 cgiSetVariable(name, value);
1247 }
1248
1249 return (1);
1250 }
1251
1252
1253 /*
1254 * 'cgi_passwd()' - Catch authentication requests and notify the server.
1255 *
1256 * This function sends a Status header and exits, forcing authentication
1257 * for this request.
1258 */
1259
1260 static const char * /* O - NULL (no return) */
cgi_passwd(const char * prompt)1261 cgi_passwd(const char *prompt) /* I - Prompt (not used) */
1262 {
1263 (void)prompt;
1264
1265 fprintf(stderr, "DEBUG: cgi_passwd(prompt=\"%s\") called!\n",
1266 prompt ? prompt : "(null)");
1267
1268 /*
1269 * Send a 401 (unauthorized) status to the server, so it can notify
1270 * the client that authentication is required.
1271 */
1272
1273 puts("Status: 401\n");
1274 exit(0);
1275
1276 /*
1277 * This code is never executed, but is present to satisfy the compiler.
1278 */
1279
1280 return (NULL);
1281 }
1282
1283
1284 /*
1285 * 'cgi_set_sid()' - Set the CUPS session ID.
1286 */
1287
1288 static const char * /* O - New session ID */
cgi_set_sid(void)1289 cgi_set_sid(void)
1290 {
1291 char buffer[512], /* SID data */
1292 sid[33]; /* SID string */
1293 unsigned char sum[16]; /* MD5 sum */
1294 const char *remote_addr, /* REMOTE_ADDR */
1295 *server_name, /* SERVER_NAME */
1296 *server_port; /* SERVER_PORT */
1297 struct timeval curtime; /* Current time */
1298
1299
1300 if ((remote_addr = getenv("REMOTE_ADDR")) == NULL)
1301 remote_addr = "REMOTE_ADDR";
1302 if ((server_name = getenv("SERVER_NAME")) == NULL)
1303 server_name = "SERVER_NAME";
1304 if ((server_port = getenv("SERVER_PORT")) == NULL)
1305 server_port = "SERVER_PORT";
1306
1307 gettimeofday(&curtime, NULL);
1308 CUPS_SRAND(curtime.tv_sec + curtime.tv_usec);
1309 snprintf(buffer, sizeof(buffer), "%s:%s:%s:%02X%02X%02X%02X%02X%02X%02X%02X",
1310 remote_addr, server_name, server_port,
1311 (unsigned)CUPS_RAND() & 255, (unsigned)CUPS_RAND() & 255,
1312 (unsigned)CUPS_RAND() & 255, (unsigned)CUPS_RAND() & 255,
1313 (unsigned)CUPS_RAND() & 255, (unsigned)CUPS_RAND() & 255,
1314 (unsigned)CUPS_RAND() & 255, (unsigned)CUPS_RAND() & 255);
1315 cupsHashData("md5", (unsigned char *)buffer, strlen(buffer), sum, sizeof(sum));
1316
1317 cgiSetCookie(CUPS_SID, cupsHashString(sum, sizeof(sum), sid, sizeof(sid)), "/", NULL, 0, 0);
1318
1319 return (cupsGetOption(CUPS_SID, num_cookies, cookies));
1320 }
1321
1322
1323 /*
1324 * 'cgi_sort_variables()' - Sort all form variables for faster lookup.
1325 */
1326
1327 static void
cgi_sort_variables(void)1328 cgi_sort_variables(void)
1329 {
1330 if (form_count < 2)
1331 return;
1332
1333 qsort(form_vars, (size_t)form_count, sizeof(_cgi_var_t),
1334 (int (*)(const void *, const void *))cgi_compare_variables);
1335 }
1336
1337
1338 /*
1339 * 'cgi_unlink_file()' - Remove the uploaded form.
1340 */
1341
1342 static void
cgi_unlink_file(void)1343 cgi_unlink_file(void)
1344 {
1345 if (form_file)
1346 {
1347 /*
1348 * Remove the temporary file...
1349 */
1350
1351 unlink(form_file->tempfile);
1352
1353 /*
1354 * Free memory used...
1355 */
1356
1357 free(form_file->name);
1358 free(form_file->filename);
1359 free(form_file->mimetype);
1360 free(form_file);
1361
1362 form_file = NULL;
1363 }
1364 }
1365