1/* 2 * "$Id: template.c 11742 2014-03-26 21:14:15Z msweet $" 3 * 4 * CGI template function. 5 * 6 * Copyright 2007-2014 by Apple Inc. 7 * Copyright 1997-2006 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#include "cgi-private.h" 17#include <errno.h> 18#include <regex.h> 19 20 21/* 22 * Local functions... 23 */ 24 25static void cgi_copy(FILE *out, FILE *in, int element, char term, 26 int indent); 27static void cgi_puts(const char *s, FILE *out); 28static void cgi_puturi(const char *s, FILE *out); 29 30 31/* 32 * 'cgiCopyTemplateFile()' - Copy a template file and replace all the 33 * '{variable}' strings with the variable value. 34 */ 35 36void 37cgiCopyTemplateFile(FILE *out, /* I - Output file */ 38 const char *tmpl) /* I - Template file to read */ 39{ 40 FILE *in; /* Input file */ 41 42 43 fprintf(stderr, "DEBUG2: cgiCopyTemplateFile(out=%p, tmpl=\"%s\")\n", out, 44 tmpl ? tmpl : "(null)"); 45 46 /* 47 * Range check input... 48 */ 49 50 if (!tmpl || !out) 51 return; 52 53 /* 54 * Open the template file... 55 */ 56 57 if ((in = fopen(tmpl, "r")) == NULL) 58 { 59 fprintf(stderr, "ERROR: Unable to open template file \"%s\" - %s\n", 60 tmpl ? tmpl : "(null)", strerror(errno)); 61 return; 62 } 63 64 /* 65 * Parse the file to the end... 66 */ 67 68 cgi_copy(out, in, 0, 0, 0); 69 70 /* 71 * Close the template file and return... 72 */ 73 74 fclose(in); 75} 76 77 78/* 79 * 'cgiCopyTemplateLang()' - Copy a template file using a language... 80 */ 81 82void 83cgiCopyTemplateLang(const char *tmpl) /* I - Base filename */ 84{ 85 char filename[1024], /* Filename */ 86 locale[16], /* Locale name */ 87 *locptr; /* Pointer into locale name */ 88 const char *directory, /* Directory for templates */ 89 *lang; /* Language */ 90 FILE *in; /* Input file */ 91 92 93 fprintf(stderr, "DEBUG2: cgiCopyTemplateLang(tmpl=\"%s\")\n", 94 tmpl ? tmpl : "(null)"); 95 96 /* 97 * Convert the language to a locale name... 98 */ 99 100 locale[0] = '\0'; 101 102 if ((lang = getenv("LANG")) != NULL) 103 { 104 locale[0] = '/'; 105 strlcpy(locale + 1, lang, sizeof(locale) - 1); 106 107 if ((locptr = strchr(locale, '.')) != NULL) 108 *locptr = '\0'; /* Strip charset */ 109 } 110 111 fprintf(stderr, "DEBUG2: lang=\"%s\", locale=\"%s\"...\n", 112 lang ? lang : "(null)", locale); 113 114 /* 115 * See if we have a template file for this language... 116 */ 117 118 directory = cgiGetTemplateDir(); 119 120 snprintf(filename, sizeof(filename), "%s%s/%s", directory, locale, tmpl); 121 if ((in = fopen(filename, "r")) == NULL) 122 { 123 locale[3] = '\0'; 124 125 snprintf(filename, sizeof(filename), "%s%s/%s", directory, locale, tmpl); 126 if ((in = fopen(filename, "r")) == NULL) 127 { 128 snprintf(filename, sizeof(filename), "%s/%s", directory, tmpl); 129 in = fopen(filename, "r"); 130 } 131 } 132 133 fprintf(stderr, "DEBUG2: Template file is \"%s\"...\n", filename); 134 135 /* 136 * Open the template file... 137 */ 138 139 if (!in) 140 { 141 fprintf(stderr, "ERROR: Unable to open template file \"%s\" - %s\n", 142 filename, strerror(errno)); 143 return; 144 } 145 146 /* 147 * Parse the file to the end... 148 */ 149 150 cgi_copy(stdout, in, 0, 0, 0); 151 152 /* 153 * Close the template file and return... 154 */ 155 156 fclose(in); 157} 158 159 160/* 161 * 'cgiGetTemplateDir()' - Get the templates directory... 162 */ 163 164char * /* O - Template directory */ 165cgiGetTemplateDir(void) 166{ 167 const char *datadir; /* CUPS_DATADIR env var */ 168 static char templates[1024] = ""; /* Template directory */ 169 170 171 if (!templates[0]) 172 { 173 /* 174 * Build the template directory pathname... 175 */ 176 177 if ((datadir = getenv("CUPS_DATADIR")) == NULL) 178 datadir = CUPS_DATADIR; 179 180 snprintf(templates, sizeof(templates), "%s/templates", datadir); 181 } 182 183 return (templates); 184} 185 186 187/* 188 * 'cgiSetServerVersion()' - Set the server name and CUPS version... 189 */ 190 191void 192cgiSetServerVersion(void) 193{ 194 cgiSetVariable("SERVER_NAME", getenv("SERVER_NAME")); 195 cgiSetVariable("REMOTE_USER", getenv("REMOTE_USER")); 196 cgiSetVariable("CUPS_VERSION", CUPS_SVERSION); 197 198#ifdef LC_TIME 199 setlocale(LC_TIME, ""); 200#endif /* LC_TIME */ 201} 202 203 204/* 205 * 'cgi_copy()' - Copy the template file, substituting as needed... 206 */ 207 208static void 209cgi_copy(FILE *out, /* I - Output file */ 210 FILE *in, /* I - Input file */ 211 int element, /* I - Element number (0 to N) */ 212 char term, /* I - Terminating character */ 213 int indent) /* I - Debug info indentation */ 214{ 215 int ch; /* Character from file */ 216 char op; /* Operation */ 217 char name[255], /* Name of variable */ 218 *nameptr, /* Pointer into name */ 219 innername[255], /* Inner comparison name */ 220 *innerptr, /* Pointer into inner name */ 221 *s; /* String pointer */ 222 const char *value; /* Value of variable */ 223 const char *innerval; /* Inner value */ 224 const char *outptr; /* Output string pointer */ 225 char outval[1024], /* Formatted output string */ 226 compare[1024]; /* Comparison string */ 227 int result; /* Result of comparison */ 228 int uriencode; /* Encode as URI */ 229 regex_t re; /* Regular expression to match */ 230 231 232 fprintf(stderr, "DEBUG2: %*sStarting at file position %ld...\n", indent, "", 233 ftell(in)); 234 235 /* 236 * Parse the file to the end... 237 */ 238 239 while ((ch = getc(in)) != EOF) 240 if (ch == term) 241 break; 242 else if (ch == '{') 243 { 244 /* 245 * Get a variable name... 246 */ 247 248 uriencode = 0; 249 250 for (s = name; (ch = getc(in)) != EOF;) 251 if (strchr("}]<>=!~ \t\n", ch)) 252 break; 253 else if (s == name && ch == '%') 254 uriencode = 1; 255 else if (s > name && ch == '?') 256 break; 257 else if (s < (name + sizeof(name) - 1)) 258 *s++ = (char)ch; 259 260 *s = '\0'; 261 262 if (s == name && isspace(ch & 255)) 263 { 264 fprintf(stderr, "DEBUG2: %*sLone { at %ld...\n", indent, "", ftell(in)); 265 266 if (out) 267 { 268 putc('{', out); 269 putc(ch, out); 270 } 271 272 continue; 273 } 274 275 if (ch == '}') 276 fprintf(stderr, "DEBUG2: %*s\"{%s}\" at %ld...\n", indent, "", name, 277 ftell(in)); 278 279 /* 280 * See if it has a value... 281 */ 282 283 if (name[0] == '?') 284 { 285 /* 286 * Insert value only if it exists... 287 */ 288 289 if ((nameptr = strrchr(name, '-')) != NULL && isdigit(nameptr[1] & 255)) 290 { 291 *nameptr++ = '\0'; 292 293 if ((value = cgiGetArray(name + 1, atoi(nameptr) - 1)) != NULL) 294 outptr = value; 295 else 296 { 297 outval[0] = '\0'; 298 outptr = outval; 299 } 300 } 301 else if ((value = cgiGetArray(name + 1, element)) != NULL) 302 outptr = value; 303 else 304 { 305 outval[0] = '\0'; 306 outptr = outval; 307 } 308 } 309 else if (name[0] == '#') 310 { 311 /* 312 * Insert count... 313 */ 314 315 if (name[1]) 316 sprintf(outval, "%d", cgiGetSize(name + 1)); 317 else 318 sprintf(outval, "%d", element + 1); 319 320 outptr = outval; 321 } 322 else if (name[0] == '[') 323 { 324 /* 325 * Loop for # of elements... 326 */ 327 328 int i; /* Looping var */ 329 long pos; /* File position */ 330 int count; /* Number of elements */ 331 332 333 if (isdigit(name[1] & 255)) 334 count = atoi(name + 1); 335 else 336 count = cgiGetSize(name + 1); 337 338 pos = ftell(in); 339 340 fprintf(stderr, "DEBUG2: %*sLooping on \"%s\" at %ld, count=%d...\n", 341 indent, "", name + 1, pos, count); 342 343 if (count > 0) 344 { 345 for (i = 0; i < count; i ++) 346 { 347 if (i) 348 fseek(in, pos, SEEK_SET); 349 350 cgi_copy(out, in, i, '}', indent + 2); 351 } 352 } 353 else 354 cgi_copy(NULL, in, 0, '}', indent + 2); 355 356 fprintf(stderr, "DEBUG2: %*sFinished looping on \"%s\"...\n", indent, 357 "", name + 1); 358 359 continue; 360 } 361 else if (name[0] == '$') 362 { 363 /* 364 * Insert cookie value or nothing if not defined. 365 */ 366 367 if ((value = cgiGetCookie(name + 1)) != NULL) 368 outptr = value; 369 else 370 { 371 outval[0] = '\0'; 372 outptr = outval; 373 } 374 } 375 else 376 { 377 /* 378 * Insert variable or variable name (if element is NULL)... 379 */ 380 381 if ((nameptr = strrchr(name, '-')) != NULL && isdigit(nameptr[1] & 255)) 382 { 383 *nameptr++ = '\0'; 384 if ((value = cgiGetArray(name, atoi(nameptr) - 1)) == NULL) 385 { 386 snprintf(outval, sizeof(outval), "{%s}", name); 387 outptr = outval; 388 } 389 else 390 outptr = value; 391 } 392 else if ((value = cgiGetArray(name, element)) == NULL) 393 { 394 snprintf(outval, sizeof(outval), "{%s}", name); 395 outptr = outval; 396 } 397 else 398 outptr = value; 399 } 400 401 /* 402 * See if the terminating character requires another test... 403 */ 404 405 if (ch == '}') 406 { 407 /* 408 * End of substitution... 409 */ 410 411 if (out) 412 { 413 if (uriencode) 414 cgi_puturi(outptr, out); 415 else if (!_cups_strcasecmp(name, "?cupsdconf_default")) 416 fputs(outptr, stdout); 417 else 418 cgi_puts(outptr, out); 419 } 420 421 continue; 422 } 423 424 /* 425 * OK, process one of the following checks: 426 * 427 * {name?exist:not-exist} Exists? 428 * {name=value?true:false} Equal 429 * {name<value?true:false} Less than 430 * {name>value?true:false} Greater than 431 * {name!value?true:false} Not equal 432 * {name~refex?true:false} Regex match 433 */ 434 435 op = (char)ch; 436 437 if (ch == '?') 438 { 439 /* 440 * Test for existance... 441 */ 442 443 if (name[0] == '?') 444 result = cgiGetArray(name + 1, element) != NULL; 445 else if (name[0] == '#') 446 result = cgiGetVariable(name + 1) != NULL; 447 else 448 result = cgiGetArray(name, element) != NULL; 449 450 result = result && outptr[0]; 451 compare[0] = '\0'; 452 } 453 else 454 { 455 /* 456 * Compare to a string... 457 */ 458 459 for (s = compare; (ch = getc(in)) != EOF;) 460 if (ch == '?') 461 break; 462 else if (s >= (compare + sizeof(compare) - 1)) 463 continue; 464 else if (ch == '#') 465 { 466 sprintf(s, "%d", element + 1); 467 s += strlen(s); 468 } 469 else if (ch == '{') 470 { 471 /* 472 * Grab the value of a variable... 473 */ 474 475 innerptr = innername; 476 while ((ch = getc(in)) != EOF && ch != '}') 477 if (innerptr < (innername + sizeof(innername) - 1)) 478 *innerptr++ = (char)ch; 479 *innerptr = '\0'; 480 481 if (innername[0] == '#') 482 sprintf(s, "%d", cgiGetSize(innername + 1)); 483 else if ((innerptr = strrchr(innername, '-')) != NULL && 484 isdigit(innerptr[1] & 255)) 485 { 486 *innerptr++ = '\0'; 487 if ((innerval = cgiGetArray(innername, atoi(innerptr) - 1)) == NULL) 488 *s = '\0'; 489 else 490 strlcpy(s, innerval, sizeof(compare) - (size_t)(s - compare)); 491 } 492 else if (innername[0] == '?') 493 { 494 if ((innerval = cgiGetArray(innername + 1, element)) == NULL) 495 *s = '\0'; 496 else 497 strlcpy(s, innerval, sizeof(compare) - (size_t)(s - compare)); 498 } 499 else if ((innerval = cgiGetArray(innername, element)) == NULL) 500 snprintf(s, sizeof(compare) - (size_t)(s - compare), "{%s}", innername); 501 else 502 strlcpy(s, innerval, sizeof(compare) - (size_t)(s - compare)); 503 504 s += strlen(s); 505 } 506 else if (ch == '\\') 507 *s++ = (char)getc(in); 508 else 509 *s++ = (char)ch; 510 511 *s = '\0'; 512 513 if (ch != '?') 514 { 515 fprintf(stderr, 516 "DEBUG2: %*sBad terminator '%c' at file position %ld...\n", 517 indent, "", ch, ftell(in)); 518 return; 519 } 520 521 /* 522 * Do the comparison... 523 */ 524 525 switch (op) 526 { 527 case '<' : 528 result = _cups_strcasecmp(outptr, compare) < 0; 529 break; 530 case '>' : 531 result = _cups_strcasecmp(outptr, compare) > 0; 532 break; 533 case '=' : 534 result = _cups_strcasecmp(outptr, compare) == 0; 535 break; 536 case '!' : 537 result = _cups_strcasecmp(outptr, compare) != 0; 538 break; 539 case '~' : 540 fprintf(stderr, "DEBUG: Regular expression \"%s\"\n", compare); 541 542 if (regcomp(&re, compare, REG_EXTENDED | REG_ICASE)) 543 { 544 fprintf(stderr, 545 "ERROR: Unable to compile regular expression \"%s\"!\n", 546 compare); 547 result = 0; 548 } 549 else 550 { 551 regmatch_t matches[10]; 552 553 result = 0; 554 555 if (!regexec(&re, outptr, 10, matches, 0)) 556 { 557 int i; 558 for (i = 0; i < 10; i ++) 559 { 560 fprintf(stderr, "DEBUG: matches[%d].rm_so=%d\n", i, 561 (int)matches[i].rm_so); 562 if (matches[i].rm_so < 0) 563 break; 564 565 result ++; 566 } 567 } 568 569 regfree(&re); 570 } 571 break; 572 default : 573 result = 1; 574 break; 575 } 576 } 577 578 fprintf(stderr, 579 "DEBUG2: %*sStarting \"{%s%c%s\" at %ld, result=%d...\n", 580 indent, "", name, op, compare, ftell(in), result); 581 582 if (result) 583 { 584 /* 585 * Comparison true; output first part and ignore second... 586 */ 587 588 fprintf(stderr, "DEBUG2: %*sOutput first part...\n", indent, ""); 589 cgi_copy(out, in, element, ':', indent + 2); 590 591 fprintf(stderr, "DEBUG2: %*sSkip second part...\n", indent, ""); 592 cgi_copy(NULL, in, element, '}', indent + 2); 593 } 594 else 595 { 596 /* 597 * Comparison false; ignore first part and output second... 598 */ 599 600 fprintf(stderr, "DEBUG2: %*sSkip first part...\n", indent, ""); 601 cgi_copy(NULL, in, element, ':', indent + 2); 602 603 fprintf(stderr, "DEBUG2: %*sOutput second part...\n", indent, ""); 604 cgi_copy(out, in, element, '}', indent + 2); 605 } 606 607 fprintf(stderr, "DEBUG2: %*sFinished \"{%s%c%s\", out=%p...\n", indent, "", 608 name, op, compare, out); 609 } 610 else if (ch == '\\') /* Quoted char */ 611 { 612 if (out) 613 putc(getc(in), out); 614 else 615 getc(in); 616 } 617 else if (out) 618 putc(ch, out); 619 620 if (ch == EOF) 621 fprintf(stderr, "DEBUG2: %*sReturning at file position %ld on EOF...\n", 622 indent, "", ftell(in)); 623 else 624 fprintf(stderr, 625 "DEBUG2: %*sReturning at file position %ld on character '%c'...\n", 626 indent, "", ftell(in), ch); 627 628 if (ch == EOF && term) 629 fprintf(stderr, "ERROR: %*sSaw EOF, expected '%c'!\n", indent, "", term); 630 631 /* 632 * Flush any pending output... 633 */ 634 635 if (out) 636 fflush(out); 637} 638 639 640/* 641 * 'cgi_puts()' - Put a string to the output file, quoting as needed... 642 */ 643 644static void 645cgi_puts(const char *s, /* I - String to output */ 646 FILE *out) /* I - Output file */ 647{ 648 while (*s) 649 { 650 if (*s == '<') 651 { 652 /* 653 * Pass <A HREF="url"> and </A>, otherwise quote it... 654 */ 655 656 if (!_cups_strncasecmp(s, "<A HREF=\"", 9)) 657 { 658 fputs("<A HREF=\"", out); 659 s += 9; 660 661 while (*s && *s != '\"') 662 { 663 if (*s == '&') 664 fputs("&", out); 665 else 666 putc(*s, out); 667 668 s ++; 669 } 670 671 if (*s) 672 s ++; 673 674 fputs("\">", out); 675 } 676 else if (!_cups_strncasecmp(s, "</A>", 4)) 677 { 678 fputs("</A>", out); 679 s += 3; 680 } 681 else 682 fputs("<", out); 683 } 684 else if (*s == '>') 685 fputs(">", out); 686 else if (*s == '\"') 687 fputs(""", out); 688 else if (*s == '\'') 689 fputs("'", out); 690 else if (*s == '&') 691 fputs("&", out); 692 else 693 putc(*s, out); 694 695 s ++; 696 } 697} 698 699 700/* 701 * 'cgi_puturi()' - Put a URI string to the output file, quoting as needed... 702 */ 703 704static void 705cgi_puturi(const char *s, /* I - String to output */ 706 FILE *out) /* I - Output file */ 707{ 708 while (*s) 709 { 710 if (strchr("%@&+ <>#=", *s) || *s < ' ' || *s & 128) 711 fprintf(out, "%%%02X", *s & 255); 712 else 713 putc(*s, out); 714 715 s ++; 716 } 717} 718 719 720/* 721 * End of "$Id: template.c 11742 2014-03-26 21:14:15Z msweet $". 722 */ 723