]> git.ipfire.org Git - thirdparty/cups.git/blame - cgi-bin/var.c
Support the latest HTTP Digest authentication specification (Issue #4862)
[thirdparty/cups.git] / cgi-bin / var.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * CGI form variable and array functions for CUPS.
ef416fc2 3 *
fab4b71e 4 * Copyright 2007-2015 by Apple Inc.
7e86f2f6 5 * Copyright 1997-2005 by Easy Software Products.
ef416fc2 6 *
7e86f2f6
MS
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
57b7b66b 11 * missing or damaged, see the license at "http://www.cups.org/".
7e86f2f6
MS
12 */
13
14/*
15 * Include necessary headers...
ef416fc2 16 */
17
18/*#define DEBUG*/
19#include "cgi-private.h"
f8b3a85b 20#include <cups/http.h>
f8b3a85b
MS
21
22
23/*
24 * Session ID name
25 */
26
27#define CUPS_SID "org.cups.sid"
ef416fc2 28
29
30/*
31 * Data structure to hold all the CGI form variables and arrays...
32 */
33
34typedef 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
f8b3a85b
MS
47static int num_cookies = 0;/* Number of cookies */
48static cups_option_t *cookies = NULL;/* Cookies */
ef416fc2 49static int form_count = 0, /* Form variable count */
50 form_alloc = 0; /* Number of variables allocated */
51static _cgi_var_t *form_vars = NULL;
52 /* Form variables */
53static cgi_file_t *form_file = NULL;
54 /* Uploaded file */
55
56
57/*
58 * Local functions...
59 */
60
61static void cgi_add_variable(const char *name, int element,
62 const char *value);
63static int cgi_compare_variables(const _cgi_var_t *v1,
64 const _cgi_var_t *v2);
65static _cgi_var_t *cgi_find_variable(const char *name);
f8b3a85b 66static void cgi_initialize_cookies(void);
ef416fc2 67static int cgi_initialize_get(void);
68static int cgi_initialize_multipart(const char *boundary);
69static int cgi_initialize_post(void);
70static int cgi_initialize_string(const char *data);
71static const char *cgi_passwd(const char *prompt);
f8b3a85b 72static const char *cgi_set_sid(void);
ef416fc2 73static void cgi_sort_variables(void);
74static 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
83int /* O - 1 if all variables present, 0 otherwise */
84cgiCheckVariables(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
ef55b745
MS
127/*
128 * 'cgiClearVariables()' - Clear all form variables.
129 */
130
131void
132cgiClearVariables(void)
133{
134 int i, j; /* Looping vars */
135 _cgi_var_t *v; /* Current variable */
136
137
82cc1f9a
MS
138 fputs("DEBUG: cgiClearVariables called.\n", stderr);
139
ef55b745
MS
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
ef416fc2 154/*
f8b3a85b 155 * 'cgiGetArray()' - Get an element from a form array.
ef416fc2 156 */
157
158const char * /* O - Element value or NULL */
159cgiGetArray(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
ef416fc2 168 if (element < 0 || element >= var->nvalues)
169 return (NULL);
170
ef55b745 171 return (_cupsStrRetain(var->values[element]));
ef416fc2 172}
173
174
f8b3a85b
MS
175/*
176 * 'cgiGetCookie()' - Get a cookie value.
177 */
178
179const char * /* O - Value or NULL */
180cgiGetCookie(const char *name) /* I - Name of cookie */
181{
182 return (cupsGetOption(name, num_cookies, cookies));
183}
184
185
ef416fc2 186/*
187 * 'cgiGetFile()' - Get the file (if any) that was submitted in the form.
188 */
189
190const cgi_file_t * /* O - Attached file or NULL */
191cgiGetFile(void)
192{
193 return (form_file);
194}
195
196
197/*
198 * 'cgiGetSize()' - Get the size of a form array value.
199 */
200
201int /* O - Number of elements */
202cgiGetSize(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/*
f8b3a85b 215 * 'cgiGetVariable()' - Get a CGI variable from the database.
ef416fc2 216 *
217 * Returns NULL if the variable doesn't exist. If the variable is an
f8b3a85b 218 * array of values, returns the last element.
ef416fc2 219 */
220
221const char * /* O - Value of variable */
222cgiGetVariable(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)
ae71f5de 231 DEBUG_printf(("cgiGetVariable(\"%s\") is returning NULL...\n", name));
ef416fc2 232 else
ae71f5de
MS
233 DEBUG_printf(("cgiGetVariable(\"%s\") is returning \"%s\"...\n", name,
234 var->values[var->nvalues - 1]));
ef416fc2 235#endif /* DEBUG */
236
ef55b745 237 return ((var == NULL) ? NULL : _cupsStrRetain(var->values[var->nvalues - 1]));
ef416fc2 238}
239
240
241/*
f8b3a85b 242 * 'cgiInitialize()' - Initialize the CGI variable "database".
ef416fc2 243 */
244
245int /* O - Non-zero if there was form data */
246cgiInitialize(void)
247{
f8b3a85b
MS
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 */
ef416fc2 252
253
254 /*
255 * Setup a password callback for authentication...
256 */
257
258 cupsSetPasswordCB(cgi_passwd);
259
80ca4592 260 /*
261 * Set the locale so that times, etc. are formatted properly...
262 */
263
264 setlocale(LC_ALL, "");
265
ef416fc2 266#ifdef DEBUG
267 /*
268 * Disable output buffering to find bugs...
269 */
270
271 setbuf(stdout, NULL);
ef416fc2 272#endif /* DEBUG */
273
f8b3a85b
MS
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
ef416fc2 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
88f9aafc 301 if (!_cups_strcasecmp(method, "GET"))
ef416fc2 302 return (cgi_initialize_get());
88f9aafc 303 else if (!_cups_strcasecmp(method, "POST") && content_type)
ef416fc2 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))
f8b3a85b
MS
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 }
ef416fc2 330 else
f8b3a85b 331 return (1);
ef416fc2 332 }
333 else
334 return (0);
335}
336
337
338/*
339 * 'cgiIsPOST()' - Determine whether this page was POSTed.
340 */
341
342int /* O - 1 if POST, 0 if GET */
343cgiIsPOST(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
362void
363cgiSetArray(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
82cc1f9a
MS
374 fprintf(stderr, "DEBUG: cgiSetArray: %s[%d]=\"%s\"\n", name, element, value);
375
ef416fc2 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 {
91c84a35
MS
385 const char **temp; /* Temporary pointer */
386
387 temp = (const char **)realloc((void *)(var->values),
7e86f2f6 388 sizeof(char *) * (size_t)(element + 16));
91c84a35
MS
389 if (!temp)
390 return;
391
ef416fc2 392 var->avalues = element + 16;
91c84a35 393 var->values = temp;
ef416fc2 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])
ef55b745 404 _cupsStrFree((char *)var->values[element]);
ef416fc2 405
ef55b745 406 var->values[element] = _cupsStrAlloc(value);
ef416fc2 407 }
408}
409
410
f8b3a85b
MS
411/*
412 * 'cgiSetCookie()' - Set a cookie value.
413 */
414
415void
416cgiSetCookie(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)
85dda01c 427 printf(" path=%s;", path);
f8b3a85b 428 if (domain)
85dda01c 429 printf(" domain=%s;", domain);
f8b3a85b
MS
430 if (expires)
431 {
432 char date[256]; /* Date string */
433
85dda01c 434 printf(" expires=%s;", httpGetDateString2(expires, date, sizeof(date)));
f8b3a85b
MS
435 }
436 if (secure)
6c2b2b19 437 puts(" httponly; secure;");
f8b3a85b 438 else
6c2b2b19 439 puts(" httponly;");
f8b3a85b
MS
440}
441
442
ef416fc2 443/*
444 * 'cgiSetSize()' - Set the array size.
445 */
446
447void
448cgiSetSize(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 {
91c84a35
MS
463 const char **temp; /* Temporary pointer */
464
465 temp = (const char **)realloc((void *)(var->values),
7e86f2f6 466 sizeof(char *) * (size_t)(size + 16));
91c84a35
MS
467 if (!temp)
468 return;
469
ef416fc2 470 var->avalues = size + 16;
91c84a35 471 var->values = temp;
ef416fc2 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])
ef55b745 483 _cupsStrFree((void *)(var->values[i]));
ef416fc2 484 }
485
486 var->nvalues = size;
487}
488
489
490/*
f8b3a85b 491 * 'cgiSetVariable()' - Set a CGI variable in the database.
ef416fc2 492 *
493 * If the variable is an array, this truncates the array to a single element.
494 */
495
496void
497cgiSetVariable(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
82cc1f9a
MS
507 fprintf(stderr, "cgiSetVariable: %s=\"%s\"\n", name, value);
508
ef416fc2 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])
ef55b745 518 _cupsStrFree((char *)var->values[i]);
ef416fc2 519
ef55b745 520 var->values[0] = _cupsStrAlloc(value);
ef416fc2 521 var->nvalues = 1;
522 }
523}
524
525
526/*
527 * 'cgi_add_variable()' - Add a form variable.
528 */
529
530static void
531cgi_add_variable(const char *name, /* I - Variable name */
532 int element, /* I - Array element number */
533 const char *value) /* I - Variable value */
534{
91c84a35 535 _cgi_var_t *var; /* New variable */
ef416fc2 536
537
538 if (name == NULL || value == NULL || element < 0 || element > 100000)
539 return;
540
ae71f5de
MS
541 DEBUG_printf(("cgi_add_variable: Adding variable \'%s\' with value "
542 "\'%s\'...\n", name, value));
ef416fc2 543
544 if (form_count >= form_alloc)
545 {
91c84a35
MS
546 _cgi_var_t *temp_vars; /* Temporary form pointer */
547
548
ef416fc2 549 if (form_alloc == 0)
91c84a35 550 temp_vars = malloc(sizeof(_cgi_var_t) * 16);
ef416fc2 551 else
7e86f2f6 552 temp_vars = realloc(form_vars, (size_t)(form_alloc + 16) * sizeof(_cgi_var_t));
91c84a35
MS
553
554 if (!temp_vars)
555 return;
ef416fc2 556
91c84a35 557 form_vars = temp_vars;
ef416fc2 558 form_alloc += 16;
559 }
560
91c84a35
MS
561 var = form_vars + form_count;
562
7e86f2f6 563 if ((var->values = calloc((size_t)element + 1, sizeof(char *))) == NULL)
91c84a35
MS
564 return;
565
ef55b745 566 var->name = _cupsStrAlloc(name);
ef416fc2 567 var->nvalues = element + 1;
568 var->avalues = element + 1;
ef55b745 569 var->values[element] = _cupsStrAlloc(value);
ef416fc2 570
571 form_count ++;
572}
573
574
575/*
576 * 'cgi_compare_variables()' - Compare two variables.
577 */
578
579static int /* O - Result of comparison */
580cgi_compare_variables(
581 const _cgi_var_t *v1, /* I - First variable */
582 const _cgi_var_t *v2) /* I - Second variable */
583{
88f9aafc 584 return (_cups_strcasecmp(v1->name, v2->name));
ef416fc2 585}
586
587
588/*
f8b3a85b 589 * 'cgi_find_variable()' - Find a variable.
ef416fc2 590 */
591
592static _cgi_var_t * /* O - Variable pointer or NULL */
593cgi_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
7e86f2f6 603 return ((_cgi_var_t *)bsearch(&key, form_vars, (size_t)form_count, sizeof(_cgi_var_t),
ef416fc2 604 (int (*)(const void *, const void *))cgi_compare_variables));
605}
606
607
f8b3a85b
MS
608/*
609 * 'cgi_initialize_cookies()' - Initialize cookies.
610 */
611
612static void
613cgi_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 {
fab4b71e
MS
626 int skip = 0; /* Skip this cookie? */
627
f8b3a85b
MS
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))
fab4b71e 643 {
f8b3a85b 644 *ptr++ = *cookie++;
fab4b71e 645 }
f8b3a85b 646 else
fab4b71e
MS
647 {
648 skip = 1;
649 cookie ++;
650 }
f8b3a85b
MS
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))
fab4b71e 666 {
f8b3a85b 667 *ptr++ = *cookie++;
fab4b71e 668 }
f8b3a85b 669 else
fab4b71e
MS
670 {
671 skip = 1;
672 cookie ++;
673 }
f8b3a85b
MS
674
675 if (*cookie == '\"')
676 cookie ++;
fab4b71e
MS
677 else
678 skip = 1;
f8b3a85b
MS
679 }
680 else
681 {
682 for (ptr = value; *cookie && *cookie != ';';)
683 if (ptr < (value + sizeof(value) - 1))
fab4b71e 684 {
f8b3a85b 685 *ptr++ = *cookie++;
fab4b71e 686 }
f8b3a85b 687 else
fab4b71e
MS
688 {
689 skip = 1;
690 cookie ++;
691 }
f8b3a85b
MS
692 }
693
694 if (*cookie == ';')
695 cookie ++;
696 else if (*cookie)
fab4b71e 697 skip = 1;
f8b3a85b
MS
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
fab4b71e 706 if (name[0] != '$' && !skip)
f8b3a85b
MS
707 num_cookies = cupsAddOption(name, value, num_cookies, &cookies);
708 }
709}
710
711
ef416fc2 712/*
713 * 'cgi_initialize_get()' - Initialize form variables using the GET method.
714 */
715
716static int /* O - 1 if form data read */
717cgi_initialize_get(void)
718{
719 char *data; /* Pointer to form data string */
720
721
ae71f5de 722 DEBUG_puts("cgi_initialize_get: Initializing variables using GET method...");
ef416fc2 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/*
f8b3a85b
MS
741 * 'cgi_initialize_multipart()' - Initialize variables and file using the POST
742 * method.
ef416fc2 743 *
744 * TODO: Update to support files > 2GB.
745 */
746
747static int /* O - 1 if form data was read */
748cgi_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 */
7e86f2f6
MS
759 fd; /* Temporary file descriptor */
760 size_t blen; /* Length of boundary string */
ef416fc2 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 */
88f9aafc 820
ef416fc2 821 ptr = line;
822
823 while ((ch = getchar()) != EOF)
824 {
7e86f2f6 825 *ptr++ = (char)ch;
ef416fc2 826
7e86f2f6 827 if ((size_t)(ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
ef416fc2 828 {
829 ptr -= blen;
830 break;
831 }
832
7e86f2f6 833 if ((ptr - line - (int)blen) >= 8192)
ef416fc2 834 {
835 /*
836 * Write out the first 8k of the buffer...
837 */
838
839 write(fd, line, 8192);
07623986 840 memmove(line, line + 8192, (size_t)(ptr - line - 8192));
ef416fc2 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)
7e86f2f6 850 write(fd, line, (size_t)(ptr - line));
ef416fc2 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)
7e86f2f6 867 *ptr++ = (char)ch;
ef416fc2 868
7e86f2f6 869 if ((size_t)(ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
ef416fc2 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 }
88f9aafc 924 else if (!_cups_strncasecmp(line, "Content-Disposition:", 20))
ef416fc2 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 }
88f9aafc 942 else if (!_cups_strncasecmp(line, "Content-Type:", 13))
ef416fc2 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
966static int /* O - 1 if form data was read */
967cgi_initialize_post(void)
968{
7e86f2f6
MS
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 */
ef416fc2 975
976
ae71f5de 977 DEBUG_puts("cgi_initialize_post: Initializing variables using POST method...");
ef416fc2 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
7e86f2f6 991 length = (size_t)strtol(content_length, NULL, 10);
ef416fc2 992 data = malloc(length + 1);
993
994 if (data == NULL)
995 return (0);
996
997 /*
998 * Read the data into the buffer...
999 */
1000
7e86f2f6
MS
1001 for (tbytes = 0; tbytes < length; tbytes += (size_t)nbytes)
1002 if ((nbytes = read(0, data + tbytes, (size_t)(length - tbytes))) < 0)
080811b1 1003 {
ef416fc2 1004 if (errno != EAGAIN)
1005 {
1006 free(data);
1007 return (0);
1008 }
080811b1
MS
1009 else
1010 nbytes = 0;
1011 }
f11a948a
MS
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 }
ef416fc2 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
1047static int /* O - 1 if form data was processed */
1048cgi_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 */
f8b3a85b 1054 value[65536]; /* Variable value */
ef416fc2 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
c7017ecc
MS
1107 if (!isxdigit(data[1] & 255) || !isxdigit(data[2] & 255))
1108 return (0);
1109
ef416fc2 1110 if (s < (value + sizeof(value) - 1))
1111 {
1112 data ++;
1113 ch = *data - '0';
1114 if (ch > 9)
1115 ch -= 7;
7e86f2f6 1116 *s = (char)(ch << 4);
ef416fc2 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
58dc1933 1143 while (s >= value && isspace(*s & 255))
ef416fc2 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
1173static const char * /* O - NULL (no return) */
1174cgi_passwd(const char *prompt) /* I - Prompt (not used) */
1175{
1176 (void)prompt;
1177
f301802f 1178 fprintf(stderr, "DEBUG: cgi_passwd(prompt=\"%s\") called!\n",
1179 prompt ? prompt : "(null)");
ef416fc2 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
f8b3a85b
MS
1197/*
1198 * 'cgi_set_sid()' - Set the CUPS session ID.
1199 */
1200
1201static const char * /* O - New session ID */
1202cgi_set_sid(void)
1203{
1204 char buffer[512], /* SID data */
1205 sid[33]; /* SID string */
f8b3a85b
MS
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);
7ec11630 1226 cupsHashData("md5", (unsigned char *)buffer, strlen(buffer), sum, sizeof(sum));
88f9aafc 1227
7ec11630 1228 cgiSetCookie(CUPS_SID, cupsHashString(sum, sizeof(sum), sid, sizeof(sid)), "/", NULL, 0, 0);
f8b3a85b
MS
1229
1230 return (cupsGetOption(CUPS_SID, num_cookies, cookies));
1231}
1232
1233
ef416fc2 1234/*
1235 * 'cgi_sort_variables()' - Sort all form variables for faster lookup.
1236 */
1237
1238static void
1239cgi_sort_variables(void)
1240{
1241#ifdef DEBUG
1242 int i;
1243
1244
ae71f5de 1245 DEBUG_puts("cgi_sort_variables: Sorting variables...");
ef416fc2 1246#endif /* DEBUG */
1247
1248 if (form_count < 2)
1249 return;
1250
7e86f2f6 1251 qsort(form_vars, (size_t)form_count, sizeof(_cgi_var_t),
ef416fc2 1252 (int (*)(const void *, const void *))cgi_compare_variables);
1253
1254#ifdef DEBUG
ae71f5de 1255 DEBUG_puts("cgi_sort_variables: Sorted variable list is:");
ef416fc2 1256 for (i = 0; i < form_count; i ++)
ae71f5de
MS
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]));
ef416fc2 1260#endif /* DEBUG */
1261}
1262
1263
1264/*
1265 * 'cgi_unlink_file()' - Remove the uploaded form.
1266 */
1267
1268static void
1269cgi_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}