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