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