diff options
Diffstat (limited to 'ucgi/ucgitarget.c')
-rw-r--r-- | ucgi/ucgitarget.c | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/ucgi/ucgitarget.c b/ucgi/ucgitarget.c new file mode 100644 index 0000000..178fa4e --- /dev/null +++ b/ucgi/ucgitarget.c @@ -0,0 +1,216 @@ +/* + * Usage: as CGI script, but called by userv + * environment variables are USERV_U_E_... + */ +/* + * Copyright 1996-2013,2016 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright 1998 David Damerell <damerell@chiark.greenend.org.uk> + * Copyright 1999,2003 + * Chancellor Masters and Scholars of the University of Cambridge + * Copyright 2010 Tony Finch <fanf@dotat.at> + * Copyright 2013,2016 Mark Wooding <mdw@distorted.org.uk> + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with userv-utils; if not, see http://www.gnu.org/licenses/. + */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <getopt.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> + +#include "ucgi.h" + +static const char *const default_envok[]= { + "AUTH_TYPE", + "CONTENT_LENGTH", + "CONTENT_TYPE", + "DOCUMENT_ROOT", + "GATEWAY_INTERFACE", + "HTTP_ACCEPT", + "HTTP_ACCEPT_CHARSET", + "HTTP_ACCEPT_ENCODING", + "HTTP_ACCEPT_LANGUAGE", + "HTTP_CACHE_CONTROL", + "HTTP_CONNECTION", + "HTTP_CONTENT_ENCODING", + "HTTP_COOKIE", + "HTTP_DNT", + "HTTP_HOST", + "HTTP_KEEP_ALIVE", + "HTTP_NEGOTIATE", + "HTTP_PRAGMA", + "HTTP_REFERER", + "HTTP_USER_AGENT", + "HTTP_VIA", + "HTTP_X_FORWARDED_FOR", + "HTTPS", + "PATH_INFO", + "PATH_TRANSLATED", + "QUERY_STRING", + "REDIRECT_HANDLER", + "REDIRECT_SCRIPT_URI", + "REDIRECT_SCRIPT_URL", + "REDIRECT_STATUS", + "REDIRECT_URL", + "REMOTE_ADDR", + "REMOTE_HOST", + "REMOTE_USER", + "REMOTE_IDENT", + "REQUEST_METHOD", + "REQUEST_URI", + "SCRIPT_FILENAME", + "SCRIPT_NAME", + "SCRIPT_URI", + "SCRIPT_URL", + "SERVER_ADDR", + "SERVER_ADMIN", + "SERVER_NAME", + "SERVER_PORT", + "SERVER_PROTOCOL", + "SERVER_SIGNATURE", + "SERVER_SOFTWARE", + "SSL_CIPHER", + "SSL_CLIENT_S_DN", + "SSL_CLIENT_VERIFY", + "SSL_PROTOCOL", + 0 +}; + +static void setenvar(const char *fulln, + const char *en, const char *ep, void *p) { + xsetenv(en, ep, 1); + unsetenv(fulln); +} + +int main(int argc, char **argv) { + char *scriptpath, *newvar; + const char *nextslash, *lastslash, *pathi, *ev, *ev2, *scriptdir, *av; + const char *const *envok; + const char **arguments; + size_t scriptdirlen, scriptpathlen, l; + struct stat stab; + int i, r, nargs; + const char *filters= 0; + + ev= getenv("USERV_U_DEBUG"); + if (ev && *ev) debugmode= 1; + + D( if (debugmode) printf(";;; UCGITARGET\n"); ) + if (argc > MAX_ARGS) error("too many arguments", 500); + + for (;;) { + i= getopt(argc, argv, "+e:"); if (i < 0) break; + switch (i) { + case 'e': filters= optarg; break; + default: error("bad command line", 500); break; + } + } + argc -= optind; argv += optind; + + if (!*argv) error("no script directory argument", 500); + ev= getenv("HOME"); if (!ev) error("no HOME env. var", 500); + l= strlen(*argv)+strlen(ev); + newvar= xmalloc(l+2); + sprintf(newvar,"%s/%s",ev,*argv); + scriptdir= newvar; + scriptdirlen= strlen(scriptdir); + + if (filters) + envok= load_filters(LOADF_MUST, filters, LF_END); + else { + envok= load_filters(0, + ".userv/ucgitarget.env-filter", + "/etc/userv/ucgitarget.env-filter", + LF_END); + } + + filter_environment(0, "USERV_U_E_", envok, default_envok, setenvar, 0); + + scriptpath= 0; + pathi= getenv("PATH_INFO"); + if (!pathi) error("PATH_INFO not found", 500); + lastslash= pathi; + D( if (debugmode) { + printf(";; find script name...\n" + ";; PATH_INFO = `%s'\n", + pathi); + } ) + for (;;) { + if (*lastslash != '/') error("PATH_INFO expected slash not found", 400); + if (lastslash[1]=='.' || lastslash[1]=='#' || !lastslash[1]) + error("bad char begin", 400); + nextslash= strchr(lastslash+1,'/'); + if (!nextslash) nextslash= lastslash+1+strlen(lastslash+1); + if (!nextslash) error("insufficient elements in PATH_INFO", 400); + if (nextslash==lastslash+1) error("empty component in PATH_INFO", 400); + if (nextslash-pathi > MAX_SCRIPTPATH_LEN) + error("PATH_INFO script path too long", 400); + scriptpathlen= scriptdirlen+(nextslash-pathi); + scriptpath= xrealloc(scriptpath,scriptpathlen+1); + strcpy(scriptpath,scriptdir); + memcpy(scriptpath+scriptdirlen,pathi,nextslash-pathi); + scriptpath[scriptpathlen]= 0; + if (scriptpath[scriptpathlen-1]=='~') error("bad char end", 400); + D( if (debugmode) printf(";; try `%s'\n", scriptpath); ) + r= stat(scriptpath,&stab); if (r) syserror("stat script"); + if (S_ISREG(stab.st_mode)) break; + if (!S_ISDIR(stab.st_mode)) error("script not directory or file", 500); + lastslash= nextslash; + } + D( if (debugmode) printf(";; found script: tail = `%s'\n", nextslash); ) + if (*nextslash) xsetenv("PATH_INFO",nextslash,1); + else unsetenv("PATH_INFO"); + + newvar= xmalloc(scriptpathlen+strlen(nextslash)+3); + sprintf(newvar,"%s%s",scriptpath,nextslash); + xsetenv("PATH_TRANSLATED",newvar,1); + + xsetenv("SCRIPT_FILENAME",scriptpath,1); + + ev= getenv("SCRIPT_NAME"); + if (ev) { + ev2= getenv("USER"); if (!ev2) error("no USER variable", 500); + newvar= xmalloc(strlen(ev)+2+strlen(ev2)+scriptpathlen-scriptdirlen+2); + sprintf(newvar,"%s/~%s%s",ev,ev2,scriptpath+scriptdirlen); + xsetenv("SCRIPT_NAME",newvar,1); + } + + arguments= xmalloc(sizeof(const char*)*(argc+5)); + nargs= 0; + + arguments[nargs++]= scriptpath; + while ((av= (*++argv))) arguments[nargs++]= av; + arguments[nargs++]= 0; + + D( if (debugmode) { + int i; + + printf(";; final environment...\n"); + for (i = 0; environ[i]; i++) + printf(";; %s\n", environ[i]); + + printf(";; final command line...\n"); + for (i = 0; arguments[i]; i++) + printf(";; %s\n", arguments[i]); + fflush(stdout); + } ) + + execvp(scriptpath,(char*const*)arguments); + syserror("exec script"); + return -1; +} |