/* * Copyright (c) 2003-2016 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Processing of text/html documents. */ #include "cgi.h" #include #include #include #include #include #include #include #include /* Set the HTML document directory. */ void HTML_SetDirectory(CGI_Query *q, const char *path) { if (Strlcpy(q->html_dir, path, sizeof(q->html_dir)) >= sizeof(q->html_dir)) CGI_Exit(CGI_EX_SOFTWARE, "HTML_SetDirectory: Path overflow"); } /* Make a message HTML-safe */ static __inline__ void MessageToHTML(char *htmlMsg, size_t htmlMsgLen, const char *msg) { const char *c; char *d; for (c = &msg[0], d = &htmlMsg[0]; *c != '\0' && d < &htmlMsg[htmlMsgLen - 5]; c++) { if (*c == '<') { strncpy(d, "<", 4); d+=4; } else if (*c == '>') { strncpy(d, ">", 4); d+=4; } else { *d = *c; d++; } } *d = '\0'; } /* Find the named HTML document and copy its absolute path into dst. */ static int FindDoc(CGI_Query *q, const char *name, char *dst, size_t dst_len, off_t *docLen) { char path[FILENAME_MAX]; struct stat sb; Strlcpy(path, q->html_dir, sizeof(path)); Strlcat(path, "/", sizeof(path)); Strlcat(path, name, sizeof(path)); if (stat(path, &sb) == 0 && Strlcpy(dst, path, dst_len) < dst_len) { *docLen = sb.st_size; return (0); } CGI_SetError(_("HTML document not found: %s"), name); return (-1); } /* * Write an HTML document to the query output, appling the chain of * input filters which are registered for the `text/html' content-type. * * TODO: Implement a small memory cache */ int HTML_Output(CGI_Query *q, const char *name) { char file[FILENAME_MAX]; char path[MAXPATHLEN]; struct cgi_filter *fil; char charbase[16]; void *data; off_t len; int fd, e; if (strcmp(q->cset, "utf-8") == 0) { /* For */ Strlcpy(charbase, "utf8/", sizeof(charbase)); } else { Strlcpy(charbase, q->cset, sizeof(charbase)); Strlcat(charbase, "/", sizeof(charbase)); } Strlcpy(file, charbase, sizeof(file)); Strlcat(file, name, sizeof(file)); Strlcat(file, ".html.", sizeof(file)); Strlcat(file, q->lang, sizeof(file)); if (FindDoc(q, file, path,sizeof(path), &len) == -1) { Strlcpy(file, charbase, sizeof(file)); Strlcat(file, name, sizeof(file)); Strlcat(file, ".html.en", sizeof(file)); if (FindDoc(q, file, path,sizeof(path), &len) == -1) { Strlcpy(file, name, sizeof(file)); Strlcat(file, ".html.en", sizeof(file)); if (FindDoc(q, file, path,sizeof(path), &len) == -1) goto fail_open; } } if ((fd = open(path, O_RDONLY)) == -1) { CGI_SetError("%s: %s", path, strerror(errno)); goto fail_open; } if ((data = TryMalloc(len)) == NULL) { goto fail; } if (SYS_Read(fd, data, len) == -1) { goto fail; } TAILQ_FOREACH(fil, &cgi->filters, filters) { if (fil->type == CGI_INPUT_FILTER && strcmp(fil->content_type, "text/html") == 0) len = fil->func(q, &data, len); } CGI_WriteFiltered(q, data, len); free(data); close(fd); return (0); fail: free(data); close(fd); fail_open: CGI_Log(CGI_LOG_CRIT, "HTML_Output: %s", CGI_GetError()); CGI_Printf(q, _("Document not found: \"%s\""), name); return (-1); } /* * Write an HTML fragment to query output in [json] mode. */ int CGI_PutJSON_HTML(CGI_Query *q, const char *key, const char *name) { char file[FILENAME_MAX]; char path[MAXPATHLEN]; struct cgi_filter *fil; char charbase[16]; char *data, *pBody; int inBody = 0; off_t len; int fd, e; CGI_PutC(q, '"'); CGI_PutS(q, key); CGI_PutS(q, "\": \""); if (strcmp(q->cset, "utf-8") == 0) { /* For */ Strlcpy(charbase, "utf8/", sizeof(charbase)); } else { Strlcpy(charbase, q->cset, sizeof(charbase)); Strlcat(charbase, "/", sizeof(charbase)); } Strlcpy(file, charbase, sizeof(file)); Strlcat(file, name, sizeof(file)); Strlcat(file, ".html.", sizeof(file)); Strlcat(file, q->lang, sizeof(file)); if (FindDoc(q, file, path,sizeof(path), &len) == -1) { Strlcpy(file, charbase, sizeof(file)); Strlcat(file, name, sizeof(file)); Strlcat(file, ".html.en", sizeof(file)); if (FindDoc(q, file, path,sizeof(path), &len) == -1) { Strlcpy(file, name, sizeof(file)); Strlcat(file, ".html.en", sizeof(file)); if (FindDoc(q, file, path,sizeof(path), &len) == -1) goto fail_open; } } if ((fd = open(path, O_RDONLY)) == -1) { CGI_SetError("%s: %s", path, strerror(errno)); goto fail_open; } if ((data = TryMalloc(len)) == NULL) { goto fail; } if (SYS_Read(fd, data, len) == -1) { goto fail; } TAILQ_FOREACH(fil, &cgi->filters, filters) { if (fil->type == CGI_INPUT_FILTER && strcmp(fil->content_type, "text/html") == 0) len = fil->func(q, (void *)&data, len); } if ((pBody = strstr(data, "")) == NULL) { /* Locate fragment */ CGI_SetError("%s: No body tag", path); goto fail; } for (pBody += strlen(""); *pBody != '\0'; pBody++) { if (*pBody == '<' && strncmp(&pBody[1],"/body>",6)==0) { break; } if (*pBody == '\\') { CGI_PutS(q, "\\\\"); } else if (*pBody == '"') { CGI_PutS(q, "\\\""); } else if (*pBody == '\n') { CGI_PutS(q, "\\n"); } else if (*pBody == '\t') { CGI_PutS(q, "\\t"); } else { CGI_PutC(q, *pBody); } } free(data); close(fd); CGI_PutS(q, "\","); return (0); fail: free(data); close(fd); fail_open: CGI_Log(CGI_LOG_CRIT, "HTML_Output: %s", CGI_GetError()); CGI_PutS(q, CGI_GetError()); CGI_PutS(q, "\","); return (-1); } /* Write a generic HTML error document to the standard output. */ void HTML_OutputError(CGI_Query *q, const char *fmt, ...) { char errMsg[CGI_ERROR_MAX], htmlMsg[CGI_ERROR_MAX]; va_list ap; va_start(ap, fmt); vsnprintf(errMsg, sizeof(errMsg), fmt, ap); va_end(ap); MessageToHTML(htmlMsg, sizeof(htmlMsg), errMsg); Set("_error", "" "%s: %s", _("Error"), htmlMsg); HTML_Output(q, "error"); } /* Set a dismissible error message. */ void HTML_SetError(const char *fmt, ...) { char errMsg[CGI_ERROR_MAX], htmlMsg[CGI_ERROR_MAX]; va_list ap; char *c, *d; va_start(ap, fmt); vsnprintf(errMsg, sizeof(errMsg), fmt, ap); va_end(ap); MessageToHTML(htmlMsg, sizeof(htmlMsg), errMsg); Set("_error", "" "%s: %s" "×" "", _("Error"), htmlMsg); } /* Set a dismissible success message. */ void HTML_SetSuccess(const char *fmt, ...) { char msg[CGI_ERROR_MAX], htmlMsg[CGI_ERROR_MAX]; va_list ap; va_start(ap, fmt); vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); MessageToHTML(htmlMsg, sizeof(htmlMsg), msg); Set("_error", "" "%s: %s" "×" "", _("Success: "), htmlMsg); } void HTML_SelectBeginS(VAR *v, const char *tagArgs) { CatS(v, "'); } void HTML_SelectBegin(VAR *v, const char *argsFmt, ...) { char args[1024]; va_list ap; va_start(ap, argsFmt); vsnprintf(args, sizeof(args), argsFmt, ap); va_end(ap); CatS(v, "'); } void HTML_OptionS(VAR *v, const char *cur, const char *name, const char *descr) { Cat(v, "%s", name, (strcmp(name, cur) == 0) ? " selected" : "", descr); } void HTML_Option(VAR *v, const char *cur, const char *name, const char *descrFmt, ...) { char descr[1024]; va_list ap; va_start(ap, descrFmt); vsnprintf(descr, sizeof(descr), descrFmt, ap); va_end(ap); HTML_OptionS(v, cur, name, descr); } void HTML_SelectEnd(VAR *v) { CatS(v, "\n"); } void HTML_NotebookInit(HTML_Notebook *nb, CGI_Query *q, const char *name, const char *def) { nb->name = name; if ((nb->cur_tab = CGI_Get(q, name, 64)) == NULL) { nb->cur_tab = def; } nb->ntabs = 0; TAILQ_INIT(&nb->tabs); SetS(name, nb->cur_tab); } HTML_NotebookTab * HTML_NotebookAdd(HTML_Notebook *nb, const char *name, const char *descr) { HTML_NotebookTab *tab; tab = Malloc(sizeof(HTML_NotebookTab)); tab->name = name; tab->descr = descr; TAILQ_INSERT_TAIL(&nb->tabs, tab, tabs); nb->ntabs++; return (tab); } void HTML_NotebookOutput(HTML_Notebook *nb, const char *url) { char vname[64]; HTML_NotebookTab *tab; VAR *v; int pct = 100 / nb->ntabs; Strlcpy(vname, nb->name, sizeof(vname)); Strlcat(vname, "__tabs", sizeof(vname)); v = SetS(vname, ""); TAILQ_FOREACH(tab, &nb->tabs, tabs) { Cat(v, "", pct); if (strcmp(tab->name, nb->cur_tab) == 0) { CatS(v, tab->descr); } else { CatS(v, "name); CatS(v, "'>"); CatS(v, tab->descr); CatS(v, ""); } CatS(v, ""); } CatS(v, ""); } void HTML_NotebookFree(HTML_Notebook *nb) { HTML_NotebookTab *ent, *nent; for (ent = TAILQ_FIRST(&nb->tabs); ent != TAILQ_END(&nb->tabs); ent = nent) { nent = TAILQ_NEXT(ent, tabs); Free(ent); } TAILQ_INIT(&nb->tabs); }