xref: /aosp_15_r20/external/libcups/cgi-bin/template.c (revision 5e7646d21f1134fb0638875d812ef646c12ab91e)
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
cgiCopyTemplateFile(FILE * out,const char * tmpl)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
cgiCopyTemplateLang(const char * tmpl)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 */
cgiGetTemplateDir(void)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
cgiSetServerVersion(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
cgi_copy(FILE * out,FILE * in,int element,char term,int indent)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
cgi_puts(const char * s,FILE * out)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
cgi_puturi(const char * s,FILE * out)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 }
681