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