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