]> git.ipfire.org Git - thirdparty/cups.git/blame - cgi-bin/template.c
Import CUPS v2.0.3
[thirdparty/cups.git] / cgi-bin / template.c
CommitLineData
ef416fc2 1/*
a215cf84 2 * "$Id: template.c 12701 2015-06-08 18:33:44Z msweet $"
ef416fc2 3 *
1a18c85c 4 * CGI template function.
ef416fc2 5 *
a215cf84 6 * Copyright 2007-2015 by Apple Inc.
1a18c85c 7 * Copyright 1997-2006 by Easy Software Products.
ef416fc2 8 *
1a18c85c
MS
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/".
ef416fc2 14 */
15
16#include "cgi-private.h"
bd7854cb 17#include <errno.h>
2e4ff8af 18#include <regex.h>
ef416fc2 19
20
21/*
22 * Local functions...
23 */
24
bd7854cb 25static void cgi_copy(FILE *out, FILE *in, int element, char term,
26 int indent);
ef416fc2 27static void cgi_puts(const char *s, FILE *out);
a74454a7 28static void cgi_puturi(const char *s, FILE *out);
ef416fc2 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
355e94dc 43 fprintf(stderr, "DEBUG2: cgiCopyTemplateFile(out=%p, tmpl=\"%s\")\n", out,
f301802f 44 tmpl ? tmpl : "(null)");
bd7854cb 45
91c84a35
MS
46 /*
47 * Range check input...
48 */
49
50 if (!tmpl || !out)
51 return;
52
ef416fc2 53 /*
54 * Open the template file...
55 */
56
57 if ((in = fopen(tmpl, "r")) == NULL)
bd7854cb 58 {
59 fprintf(stderr, "ERROR: Unable to open template file \"%s\" - %s\n",
f301802f 60 tmpl ? tmpl : "(null)", strerror(errno));
ef416fc2 61 return;
bd7854cb 62 }
ef416fc2 63
64 /*
65 * Parse the file to the end...
66 */
67
bd7854cb 68 cgi_copy(out, in, 0, 0, 0);
ef416fc2 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{
ef416fc2 85 char filename[1024], /* Filename */
db1f069b
MS
86 locale[16], /* Locale name */
87 *locptr; /* Pointer into locale name */
ef416fc2 88 const char *directory, /* Directory for templates */
89 *lang; /* Language */
90 FILE *in; /* Input file */
91
92
355e94dc 93 fprintf(stderr, "DEBUG2: cgiCopyTemplateLang(tmpl=\"%s\")\n",
f301802f 94 tmpl ? tmpl : "(null)");
bd7854cb 95
ef416fc2 96 /*
97 * Convert the language to a locale name...
98 */
99
db1f069b
MS
100 locale[0] = '\0';
101
ef416fc2 102 if ((lang = getenv("LANG")) != NULL)
103 {
db1f069b
MS
104 locale[0] = '/';
105 strlcpy(locale + 1, lang, sizeof(locale) - 1);
ef416fc2 106
db1f069b
MS
107 if ((locptr = strchr(locale, '.')) != NULL)
108 *locptr = '\0'; /* Strip charset */
ef416fc2 109 }
ef416fc2 110
ef55b745 111 fprintf(stderr, "DEBUG2: lang=\"%s\", locale=\"%s\"...\n",
db1f069b 112 lang ? lang : "(null)", locale);
bd7854cb 113
ef416fc2 114 /*
115 * See if we have a template file for this language...
116 */
117
118 directory = cgiGetTemplateDir();
119
db1f069b
MS
120 snprintf(filename, sizeof(filename), "%s%s/%s", directory, locale, tmpl);
121 if ((in = fopen(filename, "r")) == NULL)
ef416fc2 122 {
db1f069b 123 locale[3] = '\0';
ef416fc2 124
db1f069b
MS
125 snprintf(filename, sizeof(filename), "%s%s/%s", directory, locale, tmpl);
126 if ((in = fopen(filename, "r")) == NULL)
127 {
ef416fc2 128 snprintf(filename, sizeof(filename), "%s/%s", directory, tmpl);
db1f069b
MS
129 in = fopen(filename, "r");
130 }
ef416fc2 131 }
132
355e94dc 133 fprintf(stderr, "DEBUG2: Template file is \"%s\"...\n", filename);
bd7854cb 134
ef416fc2 135 /*
136 * Open the template file...
137 */
138
db1f069b 139 if (!in)
bd7854cb 140 {
141 fprintf(stderr, "ERROR: Unable to open template file \"%s\" - %s\n",
142 filename, strerror(errno));
ef416fc2 143 return;
bd7854cb 144 }
ef416fc2 145
146 /*
147 * Parse the file to the end...
148 */
149
bd7854cb 150 cgi_copy(stdout, in, 0, 0, 0);
ef416fc2 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
bd7854cb 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 */
ef416fc2 214{
bd7854cb 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 */
a74454a7 228 int uriencode; /* Encode as URI */
2e4ff8af 229 regex_t re; /* Regular expression to match */
bd7854cb 230
231
355e94dc 232 fprintf(stderr, "DEBUG2: %*sStarting at file position %ld...\n", indent, "",
bd7854cb 233 ftell(in));
ef416fc2 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
a74454a7 248 uriencode = 0;
249
ef416fc2 250 for (s = name; (ch = getc(in)) != EOF;)
2e4ff8af 251 if (strchr("}]<>=!~ \t\n", ch))
ef416fc2 252 break;
a74454a7 253 else if (s == name && ch == '%')
254 uriencode = 1;
ef416fc2 255 else if (s > name && ch == '?')
256 break;
257 else if (s < (name + sizeof(name) - 1))
1a18c85c 258 *s++ = (char)ch;
ef416fc2 259
260 *s = '\0';
261
262 if (s == name && isspace(ch & 255))
263 {
355e94dc 264 fprintf(stderr, "DEBUG2: %*sLone { at %ld...\n", indent, "", ftell(in));
bd7854cb 265
ef416fc2 266 if (out)
267 {
268 putc('{', out);
269 putc(ch, out);
270 }
271
272 continue;
273 }
274
bd7854cb 275 if (ch == '}')
355e94dc 276 fprintf(stderr, "DEBUG2: %*s\"{%s}\" at %ld...\n", indent, "", name,
bd7854cb 277 ftell(in));
278
ef416fc2 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
355e94dc 340 fprintf(stderr, "DEBUG2: %*sLooping on \"%s\" at %ld, count=%d...\n",
bd7854cb 341 indent, "", name + 1, pos, count);
342
ef416fc2 343 if (count > 0)
344 {
345 for (i = 0; i < count; i ++)
346 {
bd7854cb 347 if (i)
348 fseek(in, pos, SEEK_SET);
349
350 cgi_copy(out, in, i, '}', indent + 2);
ef416fc2 351 }
352 }
353 else
bd7854cb 354 cgi_copy(NULL, in, 0, '}', indent + 2);
355
355e94dc 356 fprintf(stderr, "DEBUG2: %*sFinished looping on \"%s\"...\n", indent,
bd7854cb 357 "", name + 1);
ef416fc2 358
359 continue;
360 }
f8b3a85b
MS
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 }
ef416fc2 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)
a74454a7 412 {
413 if (uriencode)
414 cgi_puturi(outptr, out);
88f9aafc 415 else if (!_cups_strcasecmp(name, "?cupsdconf_default"))
355e94dc 416 fputs(outptr, stdout);
a74454a7 417 else
418 cgi_puts(outptr, out);
419 }
ef416fc2 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
2e4ff8af 432 * {name~refex?true:false} Regex match
ef416fc2 433 */
434
1a18c85c 435 op = (char)ch;
bd7854cb 436
ef416fc2 437 if (ch == '?')
438 {
439 /*
440 * Test for existance...
441 */
442
f8b3a85b
MS
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];
bd7854cb 451 compare[0] = '\0';
ef416fc2 452 }
453 else
454 {
455 /*
456 * Compare to a string...
457 */
458
ef416fc2 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))
1a18c85c 478 *innerptr++ = (char)ch;
ef416fc2 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
1a18c85c 490 strlcpy(s, innerval, sizeof(compare) - (size_t)(s - compare));
ef416fc2 491 }
492 else if (innername[0] == '?')
493 {
494 if ((innerval = cgiGetArray(innername + 1, element)) == NULL)
495 *s = '\0';
496 else
1a18c85c 497 strlcpy(s, innerval, sizeof(compare) - (size_t)(s - compare));
ef416fc2 498 }
499 else if ((innerval = cgiGetArray(innername, element)) == NULL)
1a18c85c 500 snprintf(s, sizeof(compare) - (size_t)(s - compare), "{%s}", innername);
ef416fc2 501 else
1a18c85c 502 strlcpy(s, innerval, sizeof(compare) - (size_t)(s - compare));
ef416fc2 503
504 s += strlen(s);
505 }
506 else if (ch == '\\')
1a18c85c 507 *s++ = (char)getc(in);
ef416fc2 508 else
1a18c85c 509 *s++ = (char)ch;
ef416fc2 510
511 *s = '\0';
512
513 if (ch != '?')
bd7854cb 514 {
515 fprintf(stderr,
355e94dc 516 "DEBUG2: %*sBad terminator '%c' at file position %ld...\n",
bd7854cb 517 indent, "", ch, ftell(in));
ef416fc2 518 return;
bd7854cb 519 }
ef416fc2 520
521 /*
522 * Do the comparison...
523 */
524
525 switch (op)
526 {
527 case '<' :
88f9aafc 528 result = _cups_strcasecmp(outptr, compare) < 0;
ef416fc2 529 break;
530 case '>' :
88f9aafc 531 result = _cups_strcasecmp(outptr, compare) > 0;
ef416fc2 532 break;
533 case '=' :
88f9aafc 534 result = _cups_strcasecmp(outptr, compare) == 0;
ef416fc2 535 break;
536 case '!' :
88f9aafc 537 result = _cups_strcasecmp(outptr, compare) != 0;
ef416fc2 538 break;
2e4ff8af
MS
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,
71f63681 545 "ERROR: Unable to compile regular expression \"%s\"!\n",
2e4ff8af
MS
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;
ef416fc2 572 default :
573 result = 1;
574 break;
575 }
576 }
577
bd7854cb 578 fprintf(stderr,
355e94dc 579 "DEBUG2: %*sStarting \"{%s%c%s\" at %ld, result=%d...\n",
bd7854cb 580 indent, "", name, op, compare, ftell(in), result);
581
ef416fc2 582 if (result)
583 {
584 /*
585 * Comparison true; output first part and ignore second...
586 */
587
355e94dc 588 fprintf(stderr, "DEBUG2: %*sOutput first part...\n", indent, "");
bd7854cb 589 cgi_copy(out, in, element, ':', indent + 2);
590
355e94dc 591 fprintf(stderr, "DEBUG2: %*sSkip second part...\n", indent, "");
bd7854cb 592 cgi_copy(NULL, in, element, '}', indent + 2);
ef416fc2 593 }
594 else
595 {
596 /*
597 * Comparison false; ignore first part and output second...
598 */
599
355e94dc 600 fprintf(stderr, "DEBUG2: %*sSkip first part...\n", indent, "");
bd7854cb 601 cgi_copy(NULL, in, element, ':', indent + 2);
602
355e94dc 603 fprintf(stderr, "DEBUG2: %*sOutput second part...\n", indent, "");
bd7854cb 604 cgi_copy(out, in, element, '}', indent + 2);
ef416fc2 605 }
bd7854cb 606
355e94dc 607 fprintf(stderr, "DEBUG2: %*sFinished \"{%s%c%s\", out=%p...\n", indent, "",
bd7854cb 608 name, op, compare, out);
ef416fc2 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
bd7854cb 620 if (ch == EOF)
355e94dc 621 fprintf(stderr, "DEBUG2: %*sReturning at file position %ld on EOF...\n",
bd7854cb 622 indent, "", ftell(in));
623 else
624 fprintf(stderr,
355e94dc 625 "DEBUG2: %*sReturning at file position %ld on character '%c'...\n",
bd7854cb 626 indent, "", ftell(in), ch);
627
628 if (ch == EOF && term)
629 fprintf(stderr, "ERROR: %*sSaw EOF, expected '%c'!\n", indent, "", term);
630
ef416fc2 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 == '<')
a215cf84 651 fputs("&lt;", out);
ef416fc2 652 else if (*s == '>')
653 fputs("&gt;", out);
654 else if (*s == '\"')
655 fputs("&quot;", out);
ef55b745
MS
656 else if (*s == '\'')
657 fputs("&#39;", out);
ef416fc2 658 else if (*s == '&')
659 fputs("&amp;", out);
660 else
661 putc(*s, out);
662
663 s ++;
664 }
665}
666
667
668/*
a74454a7 669 * 'cgi_puturi()' - Put a URI string to the output file, quoting as needed...
670 */
671
672static void
673cgi_puturi(const char *s, /* I - String to output */
674 FILE *out) /* I - Output file */
675{
676 while (*s)
677 {
ef55b745 678 if (strchr("%@&+ <>#=", *s) || *s < ' ' || *s & 128)
a74454a7 679 fprintf(out, "%%%02X", *s & 255);
680 else
681 putc(*s, out);
682
683 s ++;
684 }
685}
686
687
688/*
a215cf84 689 * End of "$Id: template.c 12701 2015-06-08 18:33:44Z msweet $".
ef416fc2 690 */