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