/* * CGI template function. * * Copyright 2007-2015 by Apple Inc. * Copyright 1997-2006 by Easy Software Products. * * Licensed under Apache License v2.0. See the file "LICENSE" for more information. */ #include "cgi-private.h" #include #include /* * Local functions... */ static void cgi_copy(FILE *out, FILE *in, int element, char term, int indent); static void cgi_puts(const char *s, FILE *out); static void cgi_puturi(const char *s, FILE *out); /* * 'cgiCopyTemplateFile()' - Copy a template file and replace all the * '{variable}' strings with the variable value. */ void cgiCopyTemplateFile(FILE *out, /* I - Output file */ const char *tmpl) /* I - Template file to read */ { FILE *in; /* Input file */ fprintf(stderr, "DEBUG2: cgiCopyTemplateFile(out=%p, tmpl=\"%s\")\n", out, tmpl ? tmpl : "(null)"); /* * Range check input... */ if (!tmpl || !out) return; /* * Open the template file... */ if ((in = fopen(tmpl, "r")) == NULL) { fprintf(stderr, "ERROR: Unable to open template file \"%s\" - %s\n", tmpl, strerror(errno)); return; } /* * Parse the file to the end... */ cgi_copy(out, in, 0, 0, 0); /* * Close the template file and return... */ fclose(in); } /* * 'cgiCopyTemplateLang()' - Copy a template file using a language... */ void cgiCopyTemplateLang(const char *tmpl) /* I - Base filename */ { char filename[1024], /* Filename */ locale[16], /* Locale name */ *locptr; /* Pointer into locale name */ const char *directory, /* Directory for templates */ *lang; /* Language */ FILE *in; /* Input file */ fprintf(stderr, "DEBUG2: cgiCopyTemplateLang(tmpl=\"%s\")\n", tmpl ? tmpl : "(null)"); /* * Convert the language to a locale name... */ if ((lang = getenv("LANG")) != NULL) { locale[0] = '/'; strlcpy(locale + 1, lang, sizeof(locale) - 1); if ((locptr = strchr(locale, '.')) != NULL) *locptr = '\0'; /* Strip charset */ } else { locale[0] = '\0'; } fprintf(stderr, "DEBUG2: lang=\"%s\", locale=\"%s\"...\n", lang ? lang : "(null)", locale); /* * See if we have a template file for this language... */ directory = cgiGetTemplateDir(); snprintf(filename, sizeof(filename), "%s%s/%s", directory, locale, tmpl); if ((in = fopen(filename, "r")) == NULL) { locale[3] = '\0'; snprintf(filename, sizeof(filename), "%s%s/%s", directory, locale, tmpl); if ((in = fopen(filename, "r")) == NULL) { snprintf(filename, sizeof(filename), "%s/%s", directory, tmpl); in = fopen(filename, "r"); } } fprintf(stderr, "DEBUG2: Template file is \"%s\"...\n", filename); /* * Open the template file... */ if (!in) { fprintf(stderr, "ERROR: Unable to open template file \"%s\" - %s\n", filename, strerror(errno)); return; } /* * Parse the file to the end... */ cgi_copy(stdout, in, 0, 0, 0); /* * Close the template file and return... */ fclose(in); } /* * 'cgiGetTemplateDir()' - Get the templates directory... */ char * /* O - Template directory */ cgiGetTemplateDir(void) { const char *datadir; /* CUPS_DATADIR env var */ static char templates[1024] = ""; /* Template directory */ if (!templates[0]) { /* * Build the template directory pathname... */ if ((datadir = getenv("CUPS_DATADIR")) == NULL) datadir = CUPS_DATADIR; snprintf(templates, sizeof(templates), "%s/templates", datadir); } return (templates); } /* * 'cgiSetServerVersion()' - Set the server name and CUPS version... */ void cgiSetServerVersion(void) { cgiSetVariable("SERVER_NAME", getenv("SERVER_NAME")); cgiSetVariable("REMOTE_USER", getenv("REMOTE_USER")); cgiSetVariable("CUPS_VERSION", CUPS_SVERSION); #ifdef LC_TIME setlocale(LC_TIME, ""); #endif /* LC_TIME */ } /* * 'cgi_copy()' - Copy the template file, substituting as needed... */ static void cgi_copy(FILE *out, /* I - Output file */ FILE *in, /* I - Input file */ int element, /* I - Element number (0 to N) */ char term, /* I - Terminating character */ int indent) /* I - Debug info indentation */ { int ch; /* Character from file */ char op; /* Operation */ char name[255], /* Name of variable */ *nameptr, /* Pointer into name */ innername[255], /* Inner comparison name */ *innerptr, /* Pointer into inner name */ *s; /* String pointer */ const char *value; /* Value of variable */ const char *innerval; /* Inner value */ const char *outptr; /* Output string pointer */ char outval[1024], /* Formatted output string */ compare[1024]; /* Comparison string */ int result; /* Result of comparison */ int uriencode; /* Encode as URI */ regex_t re; /* Regular expression to match */ fprintf(stderr, "DEBUG2: %*sStarting at file position %ld...\n", indent, "", ftell(in)); /* * Parse the file to the end... */ while ((ch = getc(in)) != EOF) if (ch == term) break; else if (ch == '{') { /* * Get a variable name... */ uriencode = 0; for (s = name; (ch = getc(in)) != EOF;) if (strchr("}]<>=!~ \t\n", ch)) break; else if (s == name && ch == '%') uriencode = 1; else if (s > name && ch == '?') break; else if (s < (name + sizeof(name) - 1)) *s++ = (char)ch; *s = '\0'; if (s == name && isspace(ch & 255)) { fprintf(stderr, "DEBUG2: %*sLone { at %ld...\n", indent, "", ftell(in)); if (out) { putc('{', out); putc(ch, out); } continue; } if (ch == '}') fprintf(stderr, "DEBUG2: %*s\"{%s}\" at %ld...\n", indent, "", name, ftell(in)); /* * See if it has a value... */ if (name[0] == '?') { /* * Insert value only if it exists... */ if ((nameptr = strrchr(name, '-')) != NULL && isdigit(nameptr[1] & 255)) { *nameptr++ = '\0'; if ((value = cgiGetArray(name + 1, atoi(nameptr) - 1)) != NULL) outptr = value; else { outval[0] = '\0'; outptr = outval; } } else if ((value = cgiGetArray(name + 1, element)) != NULL) outptr = value; else { outval[0] = '\0'; outptr = outval; } } else if (name[0] == '#') { /* * Insert count... */ if (name[1]) snprintf(outval, sizeof(outval), "%d", cgiGetSize(name + 1)); else snprintf(outval, sizeof(outval), "%d", element + 1); outptr = outval; } else if (name[0] == '[') { /* * Loop for # of elements... */ int i; /* Looping var */ long pos; /* File position */ int count; /* Number of elements */ if (isdigit(name[1] & 255)) count = atoi(name + 1); else count = cgiGetSize(name + 1); pos = ftell(in); fprintf(stderr, "DEBUG2: %*sLooping on \"%s\" at %ld, count=%d...\n", indent, "", name + 1, pos, count); if (count > 0) { for (i = 0; i < count; i ++) { if (i) fseek(in, pos, SEEK_SET); cgi_copy(out, in, i, '}', indent + 2); } } else cgi_copy(NULL, in, 0, '}', indent + 2); fprintf(stderr, "DEBUG2: %*sFinished looping on \"%s\"...\n", indent, "", name + 1); continue; } else if (name[0] == '$') { /* * Insert cookie value or nothing if not defined. */ if ((value = cgiGetCookie(name + 1)) != NULL) outptr = value; else { outval[0] = '\0'; outptr = outval; } } else { /* * Insert variable or variable name (if element is NULL)... */ if ((nameptr = strrchr(name, '-')) != NULL && isdigit(nameptr[1] & 255)) { *nameptr++ = '\0'; if ((value = cgiGetArray(name, atoi(nameptr) - 1)) == NULL) { snprintf(outval, sizeof(outval), "{%s}", name); outptr = outval; } else outptr = value; } else if ((value = cgiGetArray(name, element)) == NULL) { snprintf(outval, sizeof(outval), "{%s}", name); outptr = outval; } else outptr = value; } /* * See if the terminating character requires another test... */ if (ch == '}') { /* * End of substitution... */ if (out) { if (uriencode) cgi_puturi(outptr, out); else if (!_cups_strcasecmp(name, "?cupsdconf_default")) fputs(outptr, stdout); else cgi_puts(outptr, out); } continue; } /* * OK, process one of the following checks: * * {name?exist:not-exist} Exists? * {name=value?true:false} Equal * {namevalue?true:false} Greater than * {name!value?true:false} Not equal * {name~refex?true:false} Regex match */ op = (char)ch; if (ch == '?') { /* * Test for existance... */ if (name[0] == '?') result = cgiGetArray(name + 1, element) != NULL; else if (name[0] == '#') result = cgiGetVariable(name + 1) != NULL; else result = cgiGetArray(name, element) != NULL; result = result && outptr[0]; compare[0] = '\0'; } else { /* * Compare to a string... */ for (s = compare; (ch = getc(in)) != EOF;) if (ch == '?') break; else if (s >= (compare + sizeof(compare) - 1)) continue; else if (ch == '#') { snprintf(s, sizeof(compare) - (size_t)(s - compare), "%d", element + 1); s += strlen(s); } else if (ch == '{') { /* * Grab the value of a variable... */ innerptr = innername; while ((ch = getc(in)) != EOF && ch != '}') if (innerptr < (innername + sizeof(innername) - 1)) *innerptr++ = (char)ch; *innerptr = '\0'; if (innername[0] == '#') snprintf(s, sizeof(compare) - (size_t)(s - compare), "%d", cgiGetSize(innername + 1)); else if ((innerptr = strrchr(innername, '-')) != NULL && isdigit(innerptr[1] & 255)) { *innerptr++ = '\0'; if ((innerval = cgiGetArray(innername, atoi(innerptr) - 1)) == NULL) *s = '\0'; else strlcpy(s, innerval, sizeof(compare) - (size_t)(s - compare)); } else if (innername[0] == '?') { if ((innerval = cgiGetArray(innername + 1, element)) == NULL) *s = '\0'; else strlcpy(s, innerval, sizeof(compare) - (size_t)(s - compare)); } else if ((innerval = cgiGetArray(innername, element)) == NULL) snprintf(s, sizeof(compare) - (size_t)(s - compare), "{%s}", innername); else strlcpy(s, innerval, sizeof(compare) - (size_t)(s - compare)); s += strlen(s); } else if (ch == '\\') *s++ = (char)getc(in); else *s++ = (char)ch; *s = '\0'; if (ch != '?') { fprintf(stderr, "DEBUG2: %*sBad terminator '%c' at file position %ld...\n", indent, "", ch, ftell(in)); return; } /* * Do the comparison... */ switch (op) { case '<' : result = _cups_strcasecmp(outptr, compare) < 0; break; case '>' : result = _cups_strcasecmp(outptr, compare) > 0; break; case '=' : result = _cups_strcasecmp(outptr, compare) == 0; break; case '!' : result = _cups_strcasecmp(outptr, compare) != 0; break; case '~' : fprintf(stderr, "DEBUG: Regular expression \"%s\"\n", compare); if (regcomp(&re, compare, REG_EXTENDED | REG_ICASE)) { fprintf(stderr, "ERROR: Unable to compile regular expression \"%s\"!\n", compare); result = 0; } else { regmatch_t matches[10]; result = 0; if (!regexec(&re, outptr, 10, matches, 0)) { int i; for (i = 0; i < 10; i ++) { fprintf(stderr, "DEBUG: matches[%d].rm_so=%d\n", i, (int)matches[i].rm_so); if (matches[i].rm_so < 0) break; result ++; } } regfree(&re); } break; default : result = 1; break; } } fprintf(stderr, "DEBUG2: %*sStarting \"{%s%c%s\" at %ld, result=%d...\n", indent, "", name, op, compare, ftell(in), result); if (result) { /* * Comparison true; output first part and ignore second... */ fprintf(stderr, "DEBUG2: %*sOutput first part...\n", indent, ""); cgi_copy(out, in, element, ':', indent + 2); fprintf(stderr, "DEBUG2: %*sSkip second part...\n", indent, ""); cgi_copy(NULL, in, element, '}', indent + 2); } else { /* * Comparison false; ignore first part and output second... */ fprintf(stderr, "DEBUG2: %*sSkip first part...\n", indent, ""); cgi_copy(NULL, in, element, ':', indent + 2); fprintf(stderr, "DEBUG2: %*sOutput second part...\n", indent, ""); cgi_copy(out, in, element, '}', indent + 2); } fprintf(stderr, "DEBUG2: %*sFinished \"{%s%c%s\", out=%p...\n", indent, "", name, op, compare, out); } else if (ch == '\\') /* Quoted char */ { if (out) putc(getc(in), out); else getc(in); } else if (out) putc(ch, out); if (ch == EOF) fprintf(stderr, "DEBUG2: %*sReturning at file position %ld on EOF...\n", indent, "", ftell(in)); else fprintf(stderr, "DEBUG2: %*sReturning at file position %ld on character '%c'...\n", indent, "", ftell(in), ch); if (ch == EOF && term) fprintf(stderr, "ERROR: %*sSaw EOF, expected '%c'!\n", indent, "", term); /* * Flush any pending output... */ if (out) fflush(out); } /* * 'cgi_puts()' - Put a string to the output file, quoting as needed... */ static void cgi_puts(const char *s, /* I - String to output */ FILE *out) /* I - Output file */ { while (*s) { if (*s == '<') fputs("<", out); else if (*s == '>') fputs(">", out); else if (*s == '\"') fputs(""", out); else if (*s == '\'') fputs("'", out); else if (*s == '&') fputs("&", out); else putc(*s, out); s ++; } } /* * 'cgi_puturi()' - Put a URI string to the output file, quoting as needed... */ static void cgi_puturi(const char *s, /* I - String to output */ FILE *out) /* I - Output file */ { while (*s) { if (strchr("%@&+ <>#=", *s) || *s < ' ' || *s & 128) fprintf(out, "%%%02X", *s & 255); else putc(*s, out); s ++; } }