chiark / gitweb /
debian/: Move ucgi into userv-utils
[userv-utils.git] / www-cgi / ucgitarget.c
1 /*
2  * Usage: as CGI script, but called by userv
3  * environment variables are USERV_U_E_...
4  */
5 /*
6  * Copyright 1996-2013,2016 Ian Jackson <ijackson@chiark.greenend.org.uk>
7  * Copyright 1998 David Damerell <damerell@chiark.greenend.org.uk>
8  * Copyright 1999,2003
9  *    Chancellor Masters and Scholars of the University of Cambridge
10  * Copyright 2010 Tony Finch <fanf@dotat.at>
11  * Copyright 2013,2016 Mark Wooding <mdw@distorted.org.uk>
12  *
13  * This is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful, but
19  * WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with userv-utils; if not, see http://www.gnu.org/licenses/.
25  */
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <getopt.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 #include <sys/stat.h>
35
36 #include "ucgi.h"
37
38 static const char *const default_envok[]= {
39   "AUTH_TYPE",
40   "CONTENT_LENGTH",
41   "CONTENT_TYPE",
42   "DOCUMENT_ROOT",
43   "GATEWAY_INTERFACE",
44   "HTTP_ACCEPT",
45   "HTTP_ACCEPT_CHARSET",
46   "HTTP_ACCEPT_ENCODING",
47   "HTTP_ACCEPT_LANGUAGE",
48   "HTTP_CACHE_CONTROL",
49   "HTTP_CONNECTION",
50   "HTTP_CONTENT_ENCODING",
51   "HTTP_COOKIE",
52   "HTTP_DNT",
53   "HTTP_HOST",
54   "HTTP_KEEP_ALIVE",
55   "HTTP_NEGOTIATE",
56   "HTTP_PRAGMA",
57   "HTTP_REFERER",
58   "HTTP_USER_AGENT",
59   "HTTP_VIA",
60   "HTTP_X_FORWARDED_FOR",
61   "HTTPS",
62   "PATH_INFO",
63   "PATH_TRANSLATED",
64   "QUERY_STRING",
65   "REDIRECT_HANDLER",
66   "REDIRECT_SCRIPT_URI",
67   "REDIRECT_SCRIPT_URL",
68   "REDIRECT_STATUS",
69   "REDIRECT_URL",
70   "REMOTE_ADDR",
71   "REMOTE_HOST",
72   "REMOTE_USER",
73   "REMOTE_IDENT",
74   "REQUEST_METHOD",
75   "REQUEST_URI",
76   "SCRIPT_FILENAME",
77   "SCRIPT_NAME",
78   "SCRIPT_URI",
79   "SCRIPT_URL",
80   "SERVER_ADDR",
81   "SERVER_ADMIN",
82   "SERVER_NAME",
83   "SERVER_PORT",
84   "SERVER_PROTOCOL",
85   "SERVER_SIGNATURE",
86   "SERVER_SOFTWARE",
87   "SSL_CIPHER",
88   "SSL_CLIENT_S_DN",
89   "SSL_CLIENT_VERIFY",
90   "SSL_PROTOCOL",
91   0
92 };
93
94 static void setenvar(const char *fulln,
95                      const char *en, const char *ep, void *p) {
96   xsetenv(en, ep, 1);
97   unsetenv(fulln);
98 }
99
100 int main(int argc, char **argv) {
101   char *scriptpath, *newvar;
102   const char *nextslash, *lastslash, *pathi, *ev, *ev2, *scriptdir, *av;
103   const char *const *envok;
104   const char **arguments;
105   size_t scriptdirlen, scriptpathlen, l;
106   struct stat stab;
107   int i, r, nargs;
108   const char *filters= 0;
109
110   ev= getenv("USERV_U_DEBUG");
111   if (ev && *ev) debugmode= 1;
112   
113   D( if (debugmode) printf(";;; UCGITARGET\n"); )
114   if (argc > MAX_ARGS) error("too many arguments", 500);
115
116   for (;;) {
117     i= getopt(argc, argv, "+e:"); if (i < 0) break;
118     switch (i) {
119     case 'e': filters= optarg; break;
120     default: error("bad command line", 500); break;
121     }
122   }
123   argc -= optind; argv += optind;
124
125   if (!*argv) error("no script directory argument", 500);
126   ev= getenv("HOME"); if (!ev) error("no HOME env. var", 500);
127   l= strlen(*argv)+strlen(ev);
128   newvar= xmalloc(l+2);
129   sprintf(newvar,"%s/%s",ev,*argv);
130   scriptdir= newvar;
131   scriptdirlen= strlen(scriptdir);
132
133   if (filters)
134     envok= load_filters(LOADF_MUST, filters, LF_END);
135   else {
136     envok= load_filters(0,
137                         ".userv/ucgitarget.env-filter",
138                         "/etc/userv/ucgitarget.env-filter",
139                         LF_END);
140   }
141
142   filter_environment(0, "USERV_U_E_", envok, default_envok, setenvar, 0);
143
144   scriptpath= 0;
145   pathi= getenv("PATH_INFO");
146   if (!pathi) error("PATH_INFO not found", 500);
147   lastslash= pathi;
148   D( if (debugmode) {
149        printf(";; find script name...\n"
150               ";;   PATH_INFO = `%s'\n",
151               pathi);
152   } )
153   for (;;) {
154     if (*lastslash != '/') error("PATH_INFO expected slash not found", 400);
155     if (lastslash[1]=='.' || lastslash[1]=='#' || !lastslash[1])
156       error("bad char begin", 400);
157     nextslash= strchr(lastslash+1,'/');
158     if (!nextslash) nextslash= lastslash+1+strlen(lastslash+1);
159     if (!nextslash) error("insufficient elements in PATH_INFO", 400);
160     if (nextslash==lastslash+1) error("empty component in PATH_INFO", 400);
161     if (nextslash-pathi > MAX_SCRIPTPATH_LEN)
162       error("PATH_INFO script path too long", 400);
163     scriptpathlen= scriptdirlen+(nextslash-pathi);
164     scriptpath= xrealloc(scriptpath,scriptpathlen+1);
165     strcpy(scriptpath,scriptdir);
166     memcpy(scriptpath+scriptdirlen,pathi,nextslash-pathi);
167     scriptpath[scriptpathlen]= 0;
168     if (scriptpath[scriptpathlen-1]=='~') error("bad char end", 400);
169     D( if (debugmode) printf(";;   try `%s'\n", scriptpath); )
170     r= stat(scriptpath,&stab); if (r) syserror("stat script");
171     if (S_ISREG(stab.st_mode)) break;
172     if (!S_ISDIR(stab.st_mode)) error("script not directory or file", 500);
173     lastslash= nextslash;
174   }
175   D( if (debugmode) printf(";;   found script: tail = `%s'\n", nextslash); )
176   if (*nextslash) xsetenv("PATH_INFO",nextslash,1);
177   else unsetenv("PATH_INFO");
178
179   newvar= xmalloc(scriptpathlen+strlen(nextslash)+3);
180   sprintf(newvar,"%s%s",scriptpath,nextslash);
181   xsetenv("PATH_TRANSLATED",newvar,1);
182
183   xsetenv("SCRIPT_FILENAME",scriptpath,1);
184
185   ev= getenv("SCRIPT_NAME");
186   if (ev) {
187     ev2= getenv("USER"); if (!ev2) error("no USER variable", 500);
188     newvar= xmalloc(strlen(ev)+2+strlen(ev2)+scriptpathlen-scriptdirlen+2);
189     sprintf(newvar,"%s/~%s%s",ev,ev2,scriptpath+scriptdirlen);
190     xsetenv("SCRIPT_NAME",newvar,1);
191   }
192
193   arguments= xmalloc(sizeof(const char*)*(argc+5));
194   nargs= 0;
195   
196   arguments[nargs++]= scriptpath;
197   while ((av= (*++argv))) arguments[nargs++]= av;
198   arguments[nargs++]= 0;
199
200   D( if (debugmode) {
201        int i;
202
203        printf(";; final environment...\n");
204        for (i = 0; environ[i]; i++)
205          printf(";;   %s\n", environ[i]);
206
207        printf(";; final command line...\n");
208        for (i = 0; arguments[i]; i++)
209          printf(";;   %s\n", arguments[i]);
210        fflush(stdout);
211   } )
212
213   execvp(scriptpath,(char*const*)arguments);
214   syserror("exec script");
215   return -1;
216 }