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