Intial commit
[profile/ivi/w3m.git] / local.c
1 /* $Id: local.c,v 1.35 2007/05/23 15:06:05 inu Exp $ */
2 #include "fm.h"
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <signal.h>
9 #include <errno.h>
10 #ifdef HAVE_READLINK
11 #include <unistd.h>
12 #endif                          /* HAVE_READLINK */
13 #ifdef __EMX__
14 #include <limits.h>             /* _MAX_PATH ? */
15 #endif                          /* __EMX__ */
16 #include "local.h"
17 #include "hash.h"
18
19 #ifdef __MINGW32_VERSION
20 #include <winsock.h>
21 #endif
22
23 #define CGIFN_NORMAL     0
24 #define CGIFN_LIBDIR     1
25 #define CGIFN_CGIBIN     2
26
27 static Str Local_cookie = NULL;
28 static char *Local_cookie_file = NULL;
29
30 static void
31 writeLocalCookie()
32 {
33     FILE *f;
34
35     if (no_rc_dir)
36         return;
37     if (Local_cookie_file)
38         return;
39     Local_cookie_file = tmpfname(TMPF_COOKIE, NULL)->ptr;
40     set_environ("LOCAL_COOKIE_FILE", Local_cookie_file);
41     f = fopen(Local_cookie_file, "wb");
42     if (!f)
43         return;
44     localCookie();
45     fwrite(Local_cookie->ptr, sizeof(char), Local_cookie->length, f);
46     fclose(f);
47     chmod(Local_cookie_file, S_IRUSR | S_IWUSR);
48 }
49
50 /* setup cookie for local CGI */
51 Str
52 localCookie()
53 {
54     char hostname[256];
55
56     if (Local_cookie)
57         return Local_cookie;
58     gethostname(hostname, 256);
59     srand48((long)New(char) + (long)time(NULL));
60     Local_cookie = Sprintf("%ld@%s", lrand48(), hostname);
61     return Local_cookie;
62 }
63
64 Str
65 loadLocalDir(char *dname)
66 {
67     Str tmp;
68     DIR *d;
69     Directory *dir;
70     struct stat st;
71     char **flist;
72     char *p, *qdir;
73     Str fbuf = Strnew();
74 #ifdef HAVE_LSTAT
75     struct stat lst;
76 #ifdef HAVE_READLINK
77     char lbuf[1024];
78 #endif                          /* HAVE_READLINK */
79 #endif                          /* HAVE_LSTAT */
80     int i, l, nrow = 0, n = 0, maxlen = 0;
81     int nfile, nfile_max = 100;
82     Str dirname;
83
84     d = opendir(dname);
85     if (d == NULL)
86         return NULL;
87     dirname = Strnew_charp(dname);
88     if (Strlastchar(dirname) != '/')
89         Strcat_char(dirname, '/');
90     qdir = html_quote(Str_conv_from_system(dirname)->ptr);
91     /* FIXME: gettextize? */
92     tmp = Strnew_m_charp("<HTML>\n<HEAD>\n<BASE HREF=\"file://",
93                         html_quote(file_quote(dirname->ptr)),
94                          "\">\n<TITLE>Directory list of ", qdir,
95                          "</TITLE>\n</HEAD>\n<BODY>\n<H1>Directory list of ",
96                          qdir, "</H1>\n", NULL);
97     flist = New_N(char *, nfile_max);
98     nfile = 0;
99     while ((dir = readdir(d)) != NULL) {
100         flist[nfile++] = allocStr(dir->d_name, -1);
101         if (nfile == nfile_max) {
102             nfile_max *= 2;
103             flist = New_Reuse(char *, flist, nfile_max);
104         }
105         if (multicolList) {
106             l = strlen(dir->d_name);
107             if (l > maxlen)
108                 maxlen = l;
109             n++;
110         }
111     }
112
113     if (multicolList) {
114         l = COLS / (maxlen + 2);
115         if (!l)
116             l = 1;
117         nrow = (n + l - 1) / l;
118         n = 1;
119         Strcat_charp(tmp, "<TABLE CELLPADDING=0>\n<TR VALIGN=TOP>\n");
120     }
121     qsort((void *)flist, nfile, sizeof(char *), strCmp);
122     for (i = 0; i < nfile; i++) {
123         p = flist[i];
124         if (strcmp(p, ".") == 0)
125             continue;
126         Strcopy(fbuf, dirname);
127         if (Strlastchar(fbuf) != '/')
128             Strcat_char(fbuf, '/');
129         Strcat_charp(fbuf, p);
130 #ifdef HAVE_LSTAT
131         if (lstat(fbuf->ptr, &lst) < 0)
132             continue;
133 #endif                          /* HAVE_LSTAT */
134         if (stat(fbuf->ptr, &st) < 0)
135             continue;
136         if (multicolList) {
137             if (n == 1)
138                 Strcat_charp(tmp, "<TD><NOBR>");
139         }
140         else {
141 #ifdef HAVE_LSTAT
142             if (S_ISLNK(lst.st_mode))
143                 Strcat_charp(tmp, "[LINK] ");
144             else
145 #endif                          /* HAVE_LSTAT */
146             if (S_ISDIR(st.st_mode))
147                 Strcat_charp(tmp, "[DIR]&nbsp; ");
148             else
149                 Strcat_charp(tmp, "[FILE] ");
150         }
151         Strcat_m_charp(tmp, "<A HREF=\"", html_quote(file_quote(p)), NULL);
152         if (S_ISDIR(st.st_mode))
153             Strcat_char(tmp, '/');
154         Strcat_m_charp(tmp, "\">", html_quote(conv_from_system(p)), NULL);
155         if (S_ISDIR(st.st_mode))
156             Strcat_char(tmp, '/');
157         Strcat_charp(tmp, "</A>");
158         if (multicolList) {
159             if (n++ == nrow) {
160                 Strcat_charp(tmp, "</NOBR></TD>\n");
161                 n = 1;
162             }
163             else {
164                 Strcat_charp(tmp, "<BR>\n");
165             }
166         }
167         else {
168 #if defined(HAVE_LSTAT) && defined(HAVE_READLINK)
169             if (S_ISLNK(lst.st_mode)) {
170                 if ((l = readlink(fbuf->ptr, lbuf, sizeof(lbuf))) > 0) {
171                     lbuf[l] = '\0';
172                     Strcat_m_charp(tmp, " -> ",
173                                    html_quote(conv_from_system(lbuf)), NULL);
174                     if (S_ISDIR(st.st_mode))
175                         Strcat_char(tmp, '/');
176                 }
177             }
178 #endif                          /* HAVE_LSTAT && HAVE_READLINK */
179             Strcat_charp(tmp, "<br>\n");
180         }
181     }
182     if (multicolList) {
183         Strcat_charp(tmp, "</TR>\n</TABLE>\n");
184     }
185     Strcat_charp(tmp, "</BODY>\n</HTML>\n");
186
187     return tmp;
188 }
189
190 static int
191 check_local_cgi(char *file, int status)
192 {
193     struct stat st;
194
195     if (status != CGIFN_LIBDIR && status != CGIFN_CGIBIN)
196         return -1;
197     if (stat(file, &st) < 0)
198         return -1;
199     if (S_ISDIR(st.st_mode))
200         return -1;
201 #ifndef __MINGW32_VERSION
202     if ((st.st_uid == geteuid() && (st.st_mode & S_IXUSR)) || (st.st_gid == getegid() && (st.st_mode & S_IXGRP)) || (st.st_mode & S_IXOTH))     /* executable */
203         return 0;
204 #endif
205     return -1;
206 }
207
208 void
209 set_environ(char *var, char *value)
210 {
211 #ifdef HAVE_SETENV
212     if (var != NULL && value != NULL)
213         setenv(var, value, 1);
214 #else                           /* not HAVE_SETENV */
215 #ifdef HAVE_PUTENV
216     static Hash_sv *env_hash = NULL;
217     Str tmp = Strnew_m_charp(var, "=", value, NULL);
218
219     if (env_hash == NULL)
220         env_hash = newHash_sv(20);
221     putHash_sv(env_hash, var, (void *)tmp->ptr);
222     putenv(tmp->ptr);
223 #else                           /* not HAVE_PUTENV */
224     extern char **environ;
225     char **ne;
226     char *p;
227     int i, l, el;
228     char **e, **newenv;
229
230     /* I have no setenv() nor putenv() */
231     /* This part is taken from terms.c of skkfep */
232     l = strlen(var);
233     for (e = environ, i = 0; *e != NULL; e++, i++) {
234         if (strncmp(e, var, l) == 0 && (*e)[l] == '=') {
235             el = strlen(*e) - l - 1;
236             if (el >= strlen(value)) {
237                 strcpy(*e + l + 1, value);
238                 return 0;
239             }
240             else {
241                 for (; *e != NULL; e++, i++) {
242                     *e = *(e + 1);
243                 }
244                 i--;
245                 break;
246             }
247         }
248     }
249     newenv = (char **)GC_malloc((i + 2) * sizeof(char *));
250     if (newenv == NULL)
251         return;
252     for (e = environ, ne = newenv; *e != NULL; *(ne++) = *(e++)) ;
253     *(ne++) = p;
254     *ne = NULL;
255     environ = newenv;
256 #endif                          /* not HAVE_PUTENV */
257 #endif                          /* not HAVE_SETENV */
258 }
259
260 static void
261 set_cgi_environ(char *name, char *fn, char *req_uri)
262 {
263     set_environ("SERVER_SOFTWARE", w3m_version);
264     set_environ("SERVER_PROTOCOL", "HTTP/1.0");
265     set_environ("SERVER_NAME", "localhost");
266     set_environ("SERVER_PORT", "80");   /* dummy */
267     set_environ("REMOTE_HOST", "localhost");
268     set_environ("REMOTE_ADDR", "127.0.0.1");
269     set_environ("GATEWAY_INTERFACE", "CGI/1.1");
270
271     set_environ("SCRIPT_NAME", name);
272     set_environ("SCRIPT_FILENAME", fn);
273     set_environ("REQUEST_URI", req_uri);
274 }
275
276 static Str
277 checkPath(char *fn, char *path)
278 {
279     char *p;
280     Str tmp;
281     struct stat st;
282     while (*path) {
283         p = strchr(path, ':');
284         tmp = Strnew_charp(expandPath(p ? allocStr(path, p - path) : path));
285         if (Strlastchar(tmp) != '/')
286             Strcat_char(tmp, '/');
287         Strcat_charp(tmp, fn);
288         if (stat(tmp->ptr, &st) == 0)
289             return tmp;
290         if (!p)
291             break;
292         path = p + 1;
293         while (*path == ':')
294             path++;
295     }
296     return NULL;
297 }
298
299 static int
300 cgi_filename(char *uri, char **fn, char **name, char **path_info)
301 {
302     Str tmp;
303     int offset;
304
305     *fn = uri;
306     *name = uri;
307     *path_info = NULL;
308
309     if (cgi_bin != NULL && strncmp(uri, "/cgi-bin/", 9) == 0) {
310         offset = 9;
311         if ((*path_info = strchr(uri + offset, '/')))
312             *name = allocStr(uri, *path_info - uri);
313         tmp = checkPath(*name + offset, cgi_bin);
314         if (tmp == NULL)
315             return CGIFN_NORMAL;
316         *fn = tmp->ptr;
317         return CGIFN_CGIBIN;
318     }
319
320 #ifdef __EMX__
321     {
322         char lib[_MAX_PATH];
323         _abspath(lib, w3m_lib_dir(), _MAX_PATH);        /* Translate '\\' to '/' */
324         tmp = Strnew_charp(lib);
325     }
326 #else
327     tmp = Strnew_charp(w3m_lib_dir());
328 #endif
329     if (Strlastchar(tmp) != '/')
330         Strcat_char(tmp, '/');
331     if (strncmp(uri, "/$LIB/", 6) == 0)
332         offset = 6;
333     else if (strncmp(uri, tmp->ptr, tmp->length) == 0)
334         offset = tmp->length;
335     else if (*uri == '/' && document_root != NULL) {
336         Str tmp2 = Strnew_charp(document_root);
337         if (Strlastchar(tmp2) != '/')
338             Strcat_char(tmp2, '/');
339         Strcat_charp(tmp2, uri + 1);
340         if (strncmp(tmp2->ptr, tmp->ptr, tmp->length) != 0)
341             return CGIFN_NORMAL;
342         uri = tmp2->ptr;
343         *name = uri;
344         offset = tmp->length;
345     }
346     else
347         return CGIFN_NORMAL;
348     if ((*path_info = strchr(uri + offset, '/')))
349         *name = allocStr(uri, *path_info - uri);
350     Strcat_charp(tmp, *name + offset);
351     *fn = tmp->ptr;
352     return CGIFN_LIBDIR;
353 }
354
355 FILE *
356 localcgi_post(char *uri, char *qstr, FormList *request, char *referer)
357 {
358     FILE *fr = NULL, *fw = NULL;
359     int status;
360     pid_t pid;
361     char *file = uri, *name = uri, *path_info = NULL, *tmpf = NULL;
362
363 #ifdef __MINGW32_VERSION
364     return NULL;
365 #else
366     status = cgi_filename(uri, &file, &name, &path_info);
367     if (check_local_cgi(file, status) < 0)
368         return NULL;
369     writeLocalCookie();
370     if (request && request->enctype != FORM_ENCTYPE_MULTIPART) {
371         tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
372         fw = fopen(tmpf, "w");
373         if (!fw)
374             return NULL;
375     }
376     pid = open_pipe_rw(&fr, NULL);
377     if (pid < 0)
378         return NULL;
379     else if (pid) {
380         if (fw)
381             fclose(fw);
382         return fr;
383     }
384     setup_child(TRUE, 2, fw ? fileno(fw) : -1);
385
386     if (qstr)
387         uri = Strnew_m_charp(uri, "?", qstr, NULL)->ptr;
388     set_cgi_environ(name, file, uri);
389     if (path_info)
390         set_environ("PATH_INFO", path_info);
391     if (referer && referer != NO_REFERER)
392         set_environ("HTTP_REFERER", referer);
393     if (request) {
394         set_environ("REQUEST_METHOD", "POST");
395         if (qstr)
396             set_environ("QUERY_STRING", qstr);
397         set_environ("CONTENT_LENGTH", Sprintf("%d", request->length)->ptr);
398         if (request->enctype == FORM_ENCTYPE_MULTIPART) {
399             set_environ("CONTENT_TYPE",
400                         Sprintf("multipart/form-data; boundary=%s",
401                                 request->boundary)->ptr);
402             freopen(request->body, "r", stdin);
403         }
404         else {
405             set_environ("CONTENT_TYPE", "application/x-www-form-urlencoded");
406             fwrite(request->body, sizeof(char), request->length, fw);
407             fclose(fw);
408             freopen(tmpf, "r", stdin);
409         }
410     }
411     else {
412         set_environ("REQUEST_METHOD", "GET");
413         set_environ("QUERY_STRING", qstr ? qstr : "");
414         freopen(DEV_NULL_PATH, "r", stdin);
415     }
416
417 #ifdef HAVE_CHDIR               /* ifndef __EMX__ ? */
418     chdir(mydirname(file));
419 #endif
420     execl(file, mybasename(file), NULL);
421     fprintf(stderr, "execl(\"%s\", \"%s\", NULL): %s\n",
422             file, mybasename(file), strerror(errno));
423     exit(1);
424     return NULL;
425 #endif
426 }