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