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