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