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