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