]> git.ipfire.org Git - thirdparty/cups.git/blob - cgi-bin/template.c
c7d2144f8cfb6603e2a5d8705d9578f5d3766ce8
[thirdparty/cups.git] / cgi-bin / template.c
1 /*
2 * CGI template function.
3 *
4 * Copyright 2007-2015 by Apple Inc.
5 * Copyright 1997-2006 by Easy Software Products.
6 *
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
11 * file is missing or damaged, see the license at "http://www.cups.org/".
12 */
13
14 #include "cgi-private.h"
15 #include <errno.h>
16 #include <regex.h>
17
18
19 /*
20 * Local functions...
21 */
22
23 static void cgi_copy(FILE *out, FILE *in, int element, char term,
24 int indent);
25 static void cgi_puts(const char *s, FILE *out);
26 static void cgi_puturi(const char *s, FILE *out);
27
28
29 /*
30 * 'cgiCopyTemplateFile()' - Copy a template file and replace all the
31 * '{variable}' strings with the variable value.
32 */
33
34 void
35 cgiCopyTemplateFile(FILE *out, /* I - Output file */
36 const char *tmpl) /* I - Template file to read */
37 {
38 FILE *in; /* Input file */
39
40
41 fprintf(stderr, "DEBUG2: cgiCopyTemplateFile(out=%p, tmpl=\"%s\")\n", out,
42 tmpl ? tmpl : "(null)");
43
44 /*
45 * Range check input...
46 */
47
48 if (!tmpl || !out)
49 return;
50
51 /*
52 * Open the template file...
53 */
54
55 if ((in = fopen(tmpl, "r")) == NULL)
56 {
57 fprintf(stderr, "ERROR: Unable to open template file \"%s\" - %s\n",
58 tmpl ? tmpl : "(null)", strerror(errno));
59 return;
60 }
61
62 /*
63 * Parse the file to the end...
64 */
65
66 cgi_copy(out, in, 0, 0, 0);
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
80 void
81 cgiCopyTemplateLang(const char *tmpl) /* I - Base filename */
82 {
83 char filename[1024], /* Filename */
84 locale[16], /* Locale name */
85 *locptr; /* Pointer into locale name */
86 const char *directory, /* Directory for templates */
87 *lang; /* Language */
88 FILE *in; /* Input file */
89
90
91 fprintf(stderr, "DEBUG2: cgiCopyTemplateLang(tmpl=\"%s\")\n",
92 tmpl ? tmpl : "(null)");
93
94 /*
95 * Convert the language to a locale name...
96 */
97
98 locale[0] = '\0';
99
100 if ((lang = getenv("LANG")) != NULL)
101 {
102 locale[0] = '/';
103 strlcpy(locale + 1, lang, sizeof(locale) - 1);
104
105 if ((locptr = strchr(locale, '.')) != NULL)
106 *locptr = '\0'; /* Strip charset */
107 }
108
109 fprintf(stderr, "DEBUG2: lang=\"%s\", locale=\"%s\"...\n",
110 lang ? lang : "(null)", locale);
111
112 /*
113 * See if we have a template file for this language...
114 */
115
116 directory = cgiGetTemplateDir();
117
118 snprintf(filename, sizeof(filename), "%s%s/%s", directory, locale, tmpl);
119 if ((in = fopen(filename, "r")) == NULL)
120 {
121 locale[3] = '\0';
122
123 snprintf(filename, sizeof(filename), "%s%s/%s", directory, locale, tmpl);
124 if ((in = fopen(filename, "r")) == NULL)
125 {
126 snprintf(filename, sizeof(filename), "%s/%s", directory, tmpl);
127 in = fopen(filename, "r");
128 }
129 }
130
131 fprintf(stderr, "DEBUG2: Template file is \"%s\"...\n", filename);
132
133 /*
134 * Open the template file...
135 */
136
137 if (!in)
138 {
139 fprintf(stderr, "ERROR: Unable to open template file \"%s\" - %s\n",
140 filename, strerror(errno));
141 return;
142 }
143
144 /*
145 * Parse the file to the end...
146 */
147
148 cgi_copy(stdout, in, 0, 0, 0);
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
162 char * /* O - Template directory */
163 cgiGetTemplateDir(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
189 void
190 cgiSetServerVersion(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
206 static void
207 cgi_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 */
212 {
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 */
226 int uriencode; /* Encode as URI */
227 regex_t re; /* Regular expression to match */
228
229
230 fprintf(stderr, "DEBUG2: %*sStarting at file position %ld...\n", indent, "",
231 ftell(in));
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
246 uriencode = 0;
247
248 for (s = name; (ch = getc(in)) != EOF;)
249 if (strchr("}]<>=!~ \t\n", ch))
250 break;
251 else if (s == name && ch == '%')
252 uriencode = 1;
253 else if (s > name && ch == '?')
254 break;
255 else if (s < (name + sizeof(name) - 1))
256 *s++ = (char)ch;
257
258 *s = '\0';
259
260 if (s == name && isspace(ch & 255))
261 {
262 fprintf(stderr, "DEBUG2: %*sLone { at %ld...\n", indent, "", ftell(in));
263
264 if (out)
265 {
266 putc('{', out);
267 putc(ch, out);
268 }
269
270 continue;
271 }
272
273 if (ch == '}')
274 fprintf(stderr, "DEBUG2: %*s\"{%s}\" at %ld...\n", indent, "", name,
275 ftell(in));
276
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
338 fprintf(stderr, "DEBUG2: %*sLooping on \"%s\" at %ld, count=%d...\n",
339 indent, "", name + 1, pos, count);
340
341 if (count > 0)
342 {
343 for (i = 0; i < count; i ++)
344 {
345 if (i)
346 fseek(in, pos, SEEK_SET);
347
348 cgi_copy(out, in, i, '}', indent + 2);
349 }
350 }
351 else
352 cgi_copy(NULL, in, 0, '}', indent + 2);
353
354 fprintf(stderr, "DEBUG2: %*sFinished looping on \"%s\"...\n", indent,
355 "", name + 1);
356
357 continue;
358 }
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 }
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)
410 {
411 if (uriencode)
412 cgi_puturi(outptr, out);
413 else if (!_cups_strcasecmp(name, "?cupsdconf_default"))
414 fputs(outptr, stdout);
415 else
416 cgi_puts(outptr, out);
417 }
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
430 * {name~refex?true:false} Regex match
431 */
432
433 op = (char)ch;
434
435 if (ch == '?')
436 {
437 /*
438 * Test for existance...
439 */
440
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];
449 compare[0] = '\0';
450 }
451 else
452 {
453 /*
454 * Compare to a string...
455 */
456
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))
476 *innerptr++ = (char)ch;
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
488 strlcpy(s, innerval, sizeof(compare) - (size_t)(s - compare));
489 }
490 else if (innername[0] == '?')
491 {
492 if ((innerval = cgiGetArray(innername + 1, element)) == NULL)
493 *s = '\0';
494 else
495 strlcpy(s, innerval, sizeof(compare) - (size_t)(s - compare));
496 }
497 else if ((innerval = cgiGetArray(innername, element)) == NULL)
498 snprintf(s, sizeof(compare) - (size_t)(s - compare), "{%s}", innername);
499 else
500 strlcpy(s, innerval, sizeof(compare) - (size_t)(s - compare));
501
502 s += strlen(s);
503 }
504 else if (ch == '\\')
505 *s++ = (char)getc(in);
506 else
507 *s++ = (char)ch;
508
509 *s = '\0';
510
511 if (ch != '?')
512 {
513 fprintf(stderr,
514 "DEBUG2: %*sBad terminator '%c' at file position %ld...\n",
515 indent, "", ch, ftell(in));
516 return;
517 }
518
519 /*
520 * Do the comparison...
521 */
522
523 switch (op)
524 {
525 case '<' :
526 result = _cups_strcasecmp(outptr, compare) < 0;
527 break;
528 case '>' :
529 result = _cups_strcasecmp(outptr, compare) > 0;
530 break;
531 case '=' :
532 result = _cups_strcasecmp(outptr, compare) == 0;
533 break;
534 case '!' :
535 result = _cups_strcasecmp(outptr, compare) != 0;
536 break;
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,
543 "ERROR: Unable to compile regular expression \"%s\"!\n",
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;
570 default :
571 result = 1;
572 break;
573 }
574 }
575
576 fprintf(stderr,
577 "DEBUG2: %*sStarting \"{%s%c%s\" at %ld, result=%d...\n",
578 indent, "", name, op, compare, ftell(in), result);
579
580 if (result)
581 {
582 /*
583 * Comparison true; output first part and ignore second...
584 */
585
586 fprintf(stderr, "DEBUG2: %*sOutput first part...\n", indent, "");
587 cgi_copy(out, in, element, ':', indent + 2);
588
589 fprintf(stderr, "DEBUG2: %*sSkip second part...\n", indent, "");
590 cgi_copy(NULL, in, element, '}', indent + 2);
591 }
592 else
593 {
594 /*
595 * Comparison false; ignore first part and output second...
596 */
597
598 fprintf(stderr, "DEBUG2: %*sSkip first part...\n", indent, "");
599 cgi_copy(NULL, in, element, ':', indent + 2);
600
601 fprintf(stderr, "DEBUG2: %*sOutput second part...\n", indent, "");
602 cgi_copy(out, in, element, '}', indent + 2);
603 }
604
605 fprintf(stderr, "DEBUG2: %*sFinished \"{%s%c%s\", out=%p...\n", indent, "",
606 name, op, compare, out);
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
618 if (ch == EOF)
619 fprintf(stderr, "DEBUG2: %*sReturning at file position %ld on EOF...\n",
620 indent, "", ftell(in));
621 else
622 fprintf(stderr,
623 "DEBUG2: %*sReturning at file position %ld on character '%c'...\n",
624 indent, "", ftell(in), ch);
625
626 if (ch == EOF && term)
627 fprintf(stderr, "ERROR: %*sSaw EOF, expected '%c'!\n", indent, "", term);
628
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
642 static void
643 cgi_puts(const char *s, /* I - String to output */
644 FILE *out) /* I - Output file */
645 {
646 while (*s)
647 {
648 if (*s == '<')
649 fputs("&lt;", out);
650 else if (*s == '>')
651 fputs("&gt;", out);
652 else if (*s == '\"')
653 fputs("&quot;", out);
654 else if (*s == '\'')
655 fputs("&#39;", out);
656 else if (*s == '&')
657 fputs("&amp;", out);
658 else
659 putc(*s, out);
660
661 s ++;
662 }
663 }
664
665
666 /*
667 * 'cgi_puturi()' - Put a URI string to the output file, quoting as needed...
668 */
669
670 static void
671 cgi_puturi(const char *s, /* I - String to output */
672 FILE *out) /* I - Output file */
673 {
674 while (*s)
675 {
676 if (strchr("%@&+ <>#=", *s) || *s < ' ' || *s & 128)
677 fprintf(out, "%%%02X", *s & 255);
678 else
679 putc(*s, out);
680
681 s ++;
682 }
683 }