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