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