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