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