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
8  * information.
9  */
10 
11 /*
12  * Include necessary headers...
13  */
14 
15 /*#define DEBUG*/
16 #include "cgi-private.h"
17 #include <cups/http.h>
18 
19 
20 /*
21  * Session ID name
22  */
23 
24 #define CUPS_SID	"org.cups.sid"
25 
26 
27 /*
28  * Data structure to hold all the CGI form variables and arrays...
29  */
30 
31 typedef struct				/**** Form variable structure ****/
32 {
33   char		*name;			/* Name of variable */
34   int		nvalues,		/* Number of values */
35 		avalues;		/* Number of values allocated */
36   char		**values;		/* Value(s) of variable */
37 } _cgi_var_t;
38 
39 
40 /*
41  * Local globals...
42  */
43 
44 static int		num_cookies = 0;/* Number of cookies */
45 static cups_option_t	*cookies = NULL;/* Cookies */
46 static int		form_count = 0,	/* Form variable count */
47 			form_alloc = 0;	/* Number of variables allocated */
48 static _cgi_var_t	*form_vars = NULL;
49 					/* Form variables */
50 static cgi_file_t	*form_file = NULL;
51 					/* Uploaded file */
52 
53 
54 /*
55  * Local functions...
56  */
57 
58 static void		cgi_add_variable(const char *name, int element,
59 			                 const char *value);
60 static int		cgi_compare_variables(const _cgi_var_t *v1,
61 			                      const _cgi_var_t *v2);
62 static _cgi_var_t	*cgi_find_variable(const char *name);
63 static void		cgi_initialize_cookies(void);
64 static int		cgi_initialize_get(void);
65 static int		cgi_initialize_multipart(const char *boundary);
66 static int		cgi_initialize_post(void);
67 static int		cgi_initialize_string(const char *data);
68 static const char	*cgi_passwd(const char *prompt);
69 static const char	*cgi_set_sid(void);
70 static void		cgi_sort_variables(void);
71 static 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 
80 int					/* O - 1 if all variables present, 0 otherwise */
cgiCheckVariables(const char * names)81 cgiCheckVariables(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')
117     {
118       free((void *)val);
119       return (0);	/* Can't be blank, either! */
120     }
121 
122     free((void *)val);
123   }
124 
125   return (1);
126 }
127 
128 
129 /*
130  * 'cgiClearVariables()' - Clear all form variables.
131  */
132 
133 void
cgiClearVariables(void)134 cgiClearVariables(void)
135 {
136   int		i, j;			/* Looping vars */
137   _cgi_var_t	*v;			/* Current variable */
138 
139 
140   fputs("DEBUG: cgiClearVariables called.\n", stderr);
141 
142   for (v = form_vars, i = form_count; i > 0; v ++, i --)
143   {
144     free(v->name);
145     for (j = 0; j < v->nvalues; j ++)
146       if (v->values[j])
147         free(v->values[j]);
148   }
149 
150   form_count = 0;
151 
152   cgi_unlink_file();
153 }
154 
155 
156 /*
157  * 'cgiGetArray()' - Get an element from a form array.
158  */
159 
160 char *					/* O - Element value or NULL */
cgiGetArray(const char * name,int element)161 cgiGetArray(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 
170   if (element < 0 || element >= var->nvalues)
171     return (NULL);
172 
173   if (var->values[element] == NULL)
174     return (NULL);
175 
176   return (strdup(var->values[element]));
177 }
178 
179 
180 /*
181  * 'cgiGetCookie()' - Get a cookie value.
182  */
183 
184 const char *				/* O - Value or NULL */
cgiGetCookie(const char * name)185 cgiGetCookie(const char *name)		/* I - Name of cookie */
186 {
187   return (cupsGetOption(name, num_cookies, cookies));
188 }
189 
190 
191 /*
192  * 'cgiGetFile()' - Get the file (if any) that was submitted in the form.
193  */
194 
195 const cgi_file_t *			/* O - Attached file or NULL */
cgiGetFile(void)196 cgiGetFile(void)
197 {
198   return (form_file);
199 }
200 
201 
202 /*
203  * 'cgiGetSize()' - Get the size of a form array value.
204  */
205 
206 int					/* O - Number of elements */
cgiGetSize(const char * name)207 cgiGetSize(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 /*
220  * 'cgiGetVariable()' - Get a CGI variable from the database.
221  *
222  * Returns NULL if the variable doesn't exist.  If the variable is an
223  * array of values, returns the last element.
224  */
225 
226 char *					/* O - Value of variable */
cgiGetVariable(const char * name)227 cgiGetVariable(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 
234   return ((var == NULL) ? NULL : strdup(var->values[var->nvalues - 1]));
235 }
236 
237 
238 /*
239  * 'cgiInitialize()' - Initialize the CGI variable "database".
240  */
241 
242 int					/* O - Non-zero if there was form data */
cgiInitialize(void)243 cgiInitialize(void)
244 {
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 */
249 
250 
251  /*
252   * Setup a password callback for authentication...
253   */
254 
255   cupsSetPasswordCB(cgi_passwd);
256 
257  /*
258   * Set the locale so that times, etc. are formatted properly...
259   */
260 
261   setlocale(LC_ALL, "");
262 
263 #ifdef DEBUG
264  /*
265   * Disable output buffering to find bugs...
266   */
267 
268   setbuf(stdout, NULL);
269 #endif /* DEBUG */
270 
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 
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 
298   if (!_cups_strcasecmp(method, "GET"))
299     return (cgi_initialize_get());
300   else if (!_cups_strcasecmp(method, "POST") && content_type)
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))
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 
324       free((void *)cups_sid_form);
325 
326       cgiClearVariables();
327 
328       return (0);
329     }
330     else
331     {
332       free((void *)cups_sid_form);
333 
334       return (1);
335     }
336   }
337   else
338     return (0);
339 }
340 
341 
342 /*
343  * 'cgiIsPOST()' - Determine whether this page was POSTed.
344  */
345 
346 int					/* O - 1 if POST, 0 if GET */
cgiIsPOST(void)347 cgiIsPOST(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 
366 void
cgiSetArray(const char * name,int element,const char * value)367 cgiSetArray(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 
378   fprintf(stderr, "DEBUG: cgiSetArray: %s[%d]=\"%s\"\n", name, element, value);
379 
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     {
389       char **temp;			/* Temporary pointer */
390 
391       temp = (char **)realloc((void *)(var->values), sizeof(char *) * (size_t)(element + 16));
392       if (!temp)
393         return;
394 
395       var->avalues = element + 16;
396       var->values  = temp;
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])
407       free((char *)var->values[element]);
408 
409     var->values[element] = strdup(value);
410   }
411 }
412 
413 
414 /*
415  * 'cgiSetCookie()' - Set a cookie value.
416  */
417 
418 void
cgiSetCookie(const char * name,const char * value,const char * path,const char * domain,time_t expires,int secure)419 cgiSetCookie(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)
430     printf(" path=%s;", path);
431   if (domain)
432     printf(" domain=%s;", domain);
433   if (expires)
434   {
435     char	date[256];		/* Date string */
436 
437     printf(" expires=%s;", httpGetDateString2(expires, date, sizeof(date)));
438   }
439   if (secure)
440     puts(" httponly; secure;");
441   else
442     puts(" httponly;");
443 }
444 
445 
446 /*
447  * 'cgiSetSize()' - Set the array size.
448  */
449 
450 void
cgiSetSize(const char * name,int size)451 cgiSetSize(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   {
466     char **temp;			/* Temporary pointer */
467 
468     temp = (char **)realloc((void *)(var->values), sizeof(char *) * (size_t)(size + 16));
469     if (!temp)
470       return;
471 
472     var->avalues = size + 16;
473     var->values  = temp;
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])
485         free((void *)(var->values[i]));
486   }
487 
488   var->nvalues = size;
489 }
490 
491 
492 /*
493  * 'cgiSetVariable()' - Set a CGI variable in the database.
494  *
495  * If the variable is an array, this truncates the array to a single element.
496  */
497 
498 void
cgiSetVariable(const char * name,const char * value)499 cgiSetVariable(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 
509   fprintf(stderr, "cgiSetVariable: %s=\"%s\"\n", name, value);
510 
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])
520         free((char *)var->values[i]);
521 
522     var->values[0] = strdup(value);
523     var->nvalues   = 1;
524   }
525 }
526 
527 
528 /*
529  * 'cgi_add_variable()' - Add a form variable.
530  */
531 
532 static void
cgi_add_variable(const char * name,int element,const char * value)533 cgi_add_variable(const char *name,	/* I - Variable name */
534 		 int        element,	/* I - Array element number */
535                  const char *value)	/* I - Variable value */
536 {
537   _cgi_var_t	*var;			/* New variable */
538 
539 
540   if (name == NULL || value == NULL || element < 0 || element > 100000)
541     return;
542 
543   if (form_count >= form_alloc)
544   {
545     _cgi_var_t	*temp_vars;		/* Temporary form pointer */
546 
547 
548     if (form_alloc == 0)
549       temp_vars = malloc(sizeof(_cgi_var_t) * 16);
550     else
551       temp_vars = realloc(form_vars, (size_t)(form_alloc + 16) * sizeof(_cgi_var_t));
552 
553     if (!temp_vars)
554       return;
555 
556     form_vars  = temp_vars;
557     form_alloc += 16;
558   }
559 
560   var = form_vars + form_count;
561 
562   if ((var->values = calloc((size_t)element + 1, sizeof(char *))) == NULL)
563     return;
564 
565   var->name            = strdup(name);
566   var->nvalues         = element + 1;
567   var->avalues         = element + 1;
568   var->values[element] = strdup(value);
569 
570   form_count ++;
571 }
572 
573 
574 /*
575  * 'cgi_compare_variables()' - Compare two variables.
576  */
577 
578 static int				/* O - Result of comparison */
cgi_compare_variables(const _cgi_var_t * v1,const _cgi_var_t * v2)579 cgi_compare_variables(
580     const _cgi_var_t *v1,		/* I - First variable */
581     const _cgi_var_t *v2)		/* I - Second variable */
582 {
583   return (_cups_strcasecmp(v1->name, v2->name));
584 }
585 
586 
587 /*
588  * 'cgi_find_variable()' - Find a variable.
589  */
590 
591 static _cgi_var_t *			/* O - Variable pointer or NULL */
cgi_find_variable(const char * name)592 cgi_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 
600   key.name = (char *)name;
601 
602   return ((_cgi_var_t *)bsearch(&key, form_vars, (size_t)form_count, sizeof(_cgi_var_t),
603                            (int (*)(const void *, const void *))cgi_compare_variables));
604 }
605 
606 
607 /*
608  * 'cgi_initialize_cookies()' - Initialize cookies.
609  */
610 
611 static void
cgi_initialize_cookies(void)612 cgi_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   {
625     int	skip = 0;			/* Skip this cookie? */
626 
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))
642       {
643         *ptr++ = *cookie++;
644       }
645       else
646       {
647         skip = 1;
648 	cookie ++;
649       }
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))
665 	{
666 	  *ptr++ = *cookie++;
667 	}
668 	else
669 	{
670 	  skip = 1;
671 	  cookie ++;
672 	}
673 
674       if (*cookie == '\"')
675         cookie ++;
676       else
677         skip = 1;
678     }
679     else
680     {
681       for (ptr = value; *cookie && *cookie != ';';)
682         if (ptr < (value + sizeof(value) - 1))
683 	{
684 	  *ptr++ = *cookie++;
685 	}
686 	else
687 	{
688 	  skip = 1;
689 	  cookie ++;
690 	}
691     }
692 
693     if (*cookie == ';')
694       cookie ++;
695     else if (*cookie)
696       skip = 1;
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 
705     if (name[0] != '$' && !skip)
706       num_cookies = cupsAddOption(name, value, num_cookies, &cookies);
707   }
708 }
709 
710 
711 /*
712  * 'cgi_initialize_get()' - Initialize form variables using the GET method.
713  */
714 
715 static int				/* O - 1 if form data read */
cgi_initialize_get(void)716 cgi_initialize_get(void)
717 {
718   char	*data;				/* Pointer to form data string */
719 
720 
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 /*
738  * 'cgi_initialize_multipart()' - Initialize variables and file using the POST
739  *                                method.
740  *
741  * TODO: Update to support files > 2GB.
742  */
743 
744 static int				/* O - 1 if form data was read */
cgi_initialize_multipart(const char * boundary)745 cgi_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 */
756 		fd;			/* Temporary file descriptor */
757   size_t	blen;			/* Length of boundary string */
758 
759 
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 	*/
815 
816         ptr = line;
817 
818 	while ((ch = getchar()) != EOF)
819 	{
820 	  *ptr++ = (char)ch;
821 
822           if ((size_t)(ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
823 	  {
824 	    ptr -= blen;
825 	    break;
826 	  }
827 
828           if ((ptr - line - (int)blen) >= 8192)
829 	  {
830 	   /*
831 	    * Write out the first 8k of the buffer...
832 	    */
833 
834 	    write(fd, line, 8192);
835 	    memmove(line, line + 8192, (size_t)(ptr - line - 8192));
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)
845           write(fd, line, (size_t)(ptr - line));
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)
862 	    *ptr++ = (char)ch;
863 
864           if ((size_t)(ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
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 	}
887 	else if ((ptr = cgiGetVariable(name)) != NULL)
888 	{
889 	 /*
890 	  * Add another element in the array...
891 	  */
892 
893           free(ptr);
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     }
920     else if (!_cups_strncasecmp(line, "Content-Disposition:", 20))
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     }
938     else if (!_cups_strncasecmp(line, "Content-Type:", 13))
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 
962 static int				/* O - 1 if form data was read */
cgi_initialize_post(void)963 cgi_initialize_post(void)
964 {
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 */
971 
972 
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 
985   length = (size_t)strtol(content_length, NULL, 10);
986   data   = malloc(length + 1);		// lgtm [cpp/uncontrolled-allocation-size]
987 
988   if (data == NULL)
989     return (0);
990 
991  /*
992   * Read the data into the buffer...
993   */
994 
995   for (tbytes = 0; tbytes < length; tbytes += (size_t)nbytes)
996     if ((nbytes = read(0, data + tbytes, (size_t)(length - tbytes))) < 0)
997     {
998       if (errno != EAGAIN)
999       {
1000         free(data);
1001         return (0);
1002       }
1003       else
1004         nbytes = 0;
1005     }
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     }
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 
1041 static int				/* O - 1 if form data was processed */
cgi_initialize_string(const char * data)1042 cgi_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 */
1048 	value[65536],			/* Variable value */
1049 	*temp;				/* Temporary pointer */
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 
1102             if (!isxdigit(data[1] & 255) || !isxdigit(data[2] & 255))
1103 	      return (0);
1104 
1105             if (s < (value + sizeof(value) - 1))
1106 	    {
1107               data ++;
1108               ch = *data - '0';
1109               if (ch > 9)
1110         	ch -= 7;
1111               *s = (char)(ch << 4);
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 
1138     while (s >= value && isspace(*s & 255))
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     }
1151     else if ((temp = cgiGetVariable(name)) != NULL)
1152     {
1153       free(temp);
1154       cgiSetArray(name, cgiGetSize(name), value);
1155     }
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 
1171 static const char *			/* O - NULL (no return) */
cgi_passwd(const char * prompt)1172 cgi_passwd(const char *prompt)		/* I - Prompt (not used) */
1173 {
1174   (void)prompt;
1175 
1176   fprintf(stderr, "DEBUG: cgi_passwd(prompt=\"%s\") called!\n",
1177           prompt ? prompt : "(null)");
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 
1195 /*
1196  * 'cgi_set_sid()' - Set the CUPS session ID.
1197  */
1198 
1199 static const char *			/* O - New session ID */
cgi_set_sid(void)1200 cgi_set_sid(void)
1201 {
1202   char			buffer[512],	/* SID data */
1203 			sid[33];	/* SID string */
1204   unsigned char		sum[16];	/* MD5 sum */
1205   const char		*remote_addr,	/* REMOTE_ADDR */
1206 			*server_name,	/* SERVER_NAME */
1207 			*server_port;	/* SERVER_PORT */
1208   struct timeval	curtime;	/* Current time */
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 
1218   gettimeofday(&curtime, NULL);
1219   CUPS_SRAND(curtime.tv_sec + curtime.tv_usec);
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);
1226   cupsHashData("md5", (unsigned char *)buffer, strlen(buffer), sum, sizeof(sum));
1227 
1228   cgiSetCookie(CUPS_SID, cupsHashString(sum, sizeof(sum), sid, sizeof(sid)), "/", NULL, 0, 0);
1229 
1230   return (cupsGetOption(CUPS_SID, num_cookies, cookies));
1231 }
1232 
1233 
1234 /*
1235  * 'cgi_sort_variables()' - Sort all form variables for faster lookup.
1236  */
1237 
1238 static void
cgi_sort_variables(void)1239 cgi_sort_variables(void)
1240 {
1241   if (form_count < 2)
1242     return;
1243 
1244   qsort(form_vars, (size_t)form_count, sizeof(_cgi_var_t),
1245         (int (*)(const void *, const void *))cgi_compare_variables);
1246 }
1247 
1248 
1249 /*
1250  * 'cgi_unlink_file()' - Remove the uploaded form.
1251  */
1252 
1253 static void
cgi_unlink_file(void)1254 cgi_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 }
1276