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