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