Add axTLS sourcecode
[project/luci.git] / libs / nixio / axTLS / httpd / proc.c
1 /*
2  * Copyright (c) 2007-2008, Cameron Rich
3  * 
4  * All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * * Redistributions of source code must retain the above copyright notice, 
10  *   this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright notice, 
12  *   this list of conditions and the following disclaimer in the documentation 
13  *   and/or other materials provided with the distribution.
14  * * Neither the name of the axTLS project nor the names of its contributors 
15  *   may be used to endorse or promote products derived from this software 
16  *   without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <time.h>
38 #include <string.h>
39 #include "axhttp.h"
40
41 #define HTTP_VERSION        "HTTP/1.1"
42
43 static const char * index_file = "index.html";
44
45 static int special_read(struct connstruct *cn, void *buf, size_t count);
46 static int special_write(struct connstruct *cn, 
47                                         const char *buf, size_t count);
48 static void send_error(struct connstruct *cn, int err);
49 static int hexit(char c);
50 static void urldecode(char *buf);
51 static void buildactualfile(struct connstruct *cn);
52 static int sanitizefile(const char *buf);
53 static int sanitizehost(char *buf);
54 static int htaccess_check(struct connstruct *cn);
55 static const char *getmimetype(const char *name);
56
57 #if defined(CONFIG_HTTP_DIRECTORIES)
58 static void urlencode(const uint8_t *s, char *t);
59 static void procdirlisting(struct connstruct *cn);
60 #endif
61 #if defined(CONFIG_HTTP_HAS_CGI)
62 static void proccgi(struct connstruct *cn);
63 static void decode_path_info(struct connstruct *cn, char *path_info);
64 static int init_read_post_data(char *buf, char *data, struct connstruct *cn, int old_rv);
65 #endif
66 #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
67 static int auth_check(struct connstruct *cn);
68 #endif
69
70 #if AXDEBUG
71 #define AXDEBUGSTART \
72         { \
73                 FILE *axdout; \
74                 axdout = fopen("/var/log/axdebug", "a"); \
75         
76 #define AXDEBUGEND \
77                 fclose(axdout); \
78         }
79 #else /* AXDEBUG */
80 #define AXDEBUGSTART
81 #define AXDEBUGEND
82 #endif /* AXDEBUG */
83
84 /* Returns 1 if elems should continue being read, 0 otherwise */
85 static int procheadelem(struct connstruct *cn, char *buf) 
86 {
87     char *delim, *value;
88
89     if ((delim = strchr(buf, ' ')) == NULL)
90         return 0;
91
92     *delim = 0;
93     value = delim+1;
94
95     if (strcmp(buf, "GET") == 0 || strcmp(buf, "HEAD") == 0 ||
96                                             strcmp(buf, "POST") == 0) 
97     {
98         if (buf[0] == 'H') 
99             cn->reqtype = TYPE_HEAD;
100         else if (buf[0] == 'P') 
101             cn->reqtype = TYPE_POST;
102
103         if ((delim = strchr(value, ' ')) == NULL)       /* expect HTTP type */
104             return 0;
105
106         *delim = 0;
107         urldecode(value);
108
109         if (sanitizefile(value) == 0) 
110         {
111             send_error(cn, 403);
112             return 0;
113         }
114
115 #if defined(CONFIG_HTTP_HAS_CGI)
116         decode_path_info(cn, value);
117 #else
118         my_strncpy(cn->filereq, value, MAXREQUESTLENGTH);
119 #endif
120         cn->if_modified_since = -1;
121     } 
122     else if (strcmp(buf, "Host:") == 0) 
123     {
124         if (sanitizehost(value) == 0) 
125         {
126             removeconnection(cn);
127             return 0;
128         }
129
130         my_strncpy(cn->server_name, value, MAXREQUESTLENGTH);
131     } 
132     else if (strcmp(buf, "Connection:") == 0 && strcmp(value, "close") == 0) 
133     {
134         cn->close_when_done = 1;
135     } 
136     else if (strcmp(buf, "If-Modified-Since:") == 0) 
137     {
138         cn->if_modified_since = tdate_parse(value);
139     }
140     else if (strcmp(buf, "Expect:") == 0)
141     {
142         send_error(cn, 417); /* expectation failed */
143         return 0;
144     }
145 #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
146     else if (strcmp(buf, "Authorization:") == 0 &&
147                                     strncmp(value, "Basic ", 6) == 0)
148     {
149         int size;
150         if (base64_decode(&value[6], strlen(&value[6]), 
151                                         (uint8_t *)cn->authorization, &size))
152             cn->authorization[0] = 0;   /* error */
153         else
154             cn->authorization[size] = 0;
155     }
156 #endif
157 #if defined(CONFIG_HTTP_HAS_CGI)
158     else if (strcmp(buf, "Content-Length:") == 0)
159     {
160         sscanf(value, "%d", &cn->content_length);
161     }
162     else if (strcmp(buf, "Cookie:") == 0)
163     {
164         my_strncpy(cn->cookie, value, MAXREQUESTLENGTH);
165     }
166 #endif
167
168     return 1;
169 }
170
171 #if defined(CONFIG_HTTP_DIRECTORIES)
172 static void procdirlisting(struct connstruct *cn)
173 {
174     char buf[MAXREQUESTLENGTH];
175     char actualfile[1024];
176
177     if (cn->reqtype == TYPE_HEAD) 
178     {
179         snprintf(buf, sizeof(buf), HTTP_VERSION
180                 " 200 OK\nContent-Type: text/html\n\n");
181         write(cn->networkdesc, buf, strlen(buf));
182         removeconnection(cn);
183         return;
184     }
185
186     strcpy(actualfile, cn->actualfile);
187
188 #ifdef WIN32
189     strcat(actualfile, "*");
190     cn->dirp = FindFirstFile(actualfile, &cn->file_data);
191
192     if (cn->dirp == INVALID_HANDLE_VALUE) 
193     {
194         send_error(cn, 404);
195         return;
196     }
197 #else
198     if ((cn->dirp = opendir(actualfile)) == NULL) 
199     {
200         send_error(cn, 404);
201         return;
202     }
203 #endif
204
205     snprintf(buf, sizeof(buf), HTTP_VERSION
206             " 200 OK\nContent-Type: text/html\n\n"
207             "<html><body>\n<title>Directory Listing</title>\n"
208             "<h3>Directory listing of %s://%s%s</h3><br />\n", 
209             cn->is_ssl ? "https" : "http", cn->server_name, cn->filereq);
210     special_write(cn, buf, strlen(buf));
211     cn->state = STATE_DOING_DIR;
212 }
213
214 void procdodir(struct connstruct *cn) 
215 {
216 #ifndef WIN32
217     struct dirent *dp;
218 #endif
219     char buf[MAXREQUESTLENGTH];
220     char encbuf[1024];
221     char *file;
222
223     do 
224     {
225        buf[0] = 0;
226
227 #ifdef WIN32
228         if (!FindNextFile(cn->dirp, &cn->file_data)) 
229 #else
230         if ((dp = readdir(cn->dirp)) == NULL)  
231 #endif
232         {
233             snprintf(buf, sizeof(buf), "</body></html>\n");
234             special_write(cn, buf, strlen(buf));
235             removeconnection(cn);
236 #ifndef WIN32
237             closedir(cn->dirp);
238 #endif
239             return;
240         }
241
242 #ifdef WIN32
243         file = cn->file_data.cFileName;
244 #else
245         file = dp->d_name;
246 #endif
247
248         /* if no index file, don't display the ".." directory */
249         if (cn->filereq[0] == '/' && cn->filereq[1] == '\0' &&
250                 strcmp(file, "..") == 0) 
251             continue;
252
253         /* don't display files beginning with "." */
254         if (file[0] == '.' && file[1] != '.')
255             continue;
256
257         /* make sure a '/' is at the end of a directory */
258         if (cn->filereq[strlen(cn->filereq)-1] != '/')
259             strcat(cn->filereq, "/");
260
261         /* see if the dir + file is another directory */
262         snprintf(buf, sizeof(buf), "%s%s", cn->actualfile, file);
263         if (isdir(buf))
264             strcat(file, "/");
265
266         urlencode((uint8_t *)file, encbuf);
267         snprintf(buf, sizeof(buf), "<a href=\"%s%s\">%s</a><br />\n",
268                 cn->filereq, encbuf, file);
269     } while (special_write(cn, buf, strlen(buf)));
270 }
271
272 /* Encode funny chars -> %xx in newly allocated storage */
273 /* (preserves '/' !) */
274 static void urlencode(const uint8_t *s, char *t) 
275 {
276     const uint8_t *p = s;
277     char *tp = t;
278
279     for (; *p; p++) 
280     {
281         if ((*p > 0x00 && *p < ',') ||
282                 (*p > '9' && *p < 'A') ||
283                 (*p > 'Z' && *p < '_') ||
284                 (*p > '_' && *p < 'a') ||
285                 (*p > 'z' && *p < 0xA1)) 
286         {
287             sprintf((char *)tp, "%%%02X", *p);
288             tp += 3; 
289         } 
290         else 
291         {
292             *tp = *p;
293             tp++;
294         }
295     }
296
297     *tp='\0';
298 }
299
300 #endif
301
302 void procreadhead(struct connstruct *cn) 
303 {
304     char buf[MAXREQUESTLENGTH*4], *tp, *next;
305     int rv;
306
307     memset(buf, 0, MAXREQUESTLENGTH*4);
308     rv = special_read(cn, buf, sizeof(buf)-1);
309     if (rv <= 0) 
310     {
311         if (rv < 0) /* really dead? */
312             removeconnection(cn);
313         return;
314     }
315
316     buf[rv] = '\0';
317     next = tp = buf;
318
319 #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
320     cn->authorization[0] = 0;
321 #endif
322
323     /* Split up lines and send to procheadelem() */
324     while (*next != '\0') 
325     {
326         /* If we have a blank line, advance to next stage */
327         if (*next == '\r' || *next == '\n') 
328         {
329 #if defined(CONFIG_HTTP_HAS_CGI)
330             if (cn->reqtype == TYPE_POST && cn->content_length > 0)
331             {
332                 if (init_read_post_data(buf,next,cn,rv) == 0)
333                     return;
334             }
335 #endif
336
337             buildactualfile(cn);
338             cn->state = STATE_WANT_TO_SEND_HEAD;
339             return;
340         }
341
342         while (*next != '\r' && *next != '\n' && *next != '\0') 
343             next++;
344
345         if (*next == '\r') 
346         {
347             *next = '\0';
348             next += 2;
349         }
350         else if (*next == '\n') 
351             *next++ = '\0';
352
353         if (procheadelem(cn, tp) == 0) 
354             return;
355
356         tp = next;
357     }
358 }
359
360 /* In this function we assume that the file has been checked for
361  * maliciousness (".."s, etc) and has been decoded
362  */
363 void procsendhead(struct connstruct *cn) 
364 {
365     char buf[MAXREQUESTLENGTH];
366     struct stat stbuf;
367     time_t now = cn->timeout - CONFIG_HTTP_TIMEOUT;
368     char date[32];
369     int file_exists;
370
371     /* are we trying to access a file over the HTTP connection instead of a
372      * HTTPS connection? Or is this directory disabled? */
373     if (htaccess_check(cn))      
374     {
375         send_error(cn, 403);
376         return;
377     }
378
379 #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
380     if (auth_check(cn))     /* see if there is a '.htpasswd' file */
381     {
382 #ifdef CONFIG_HTTP_VERBOSE
383         printf("axhttpd: access to %s denied\n", cn->filereq); TTY_FLUSH();
384 #endif
385         removeconnection(cn);
386         return;
387     }
388 #endif
389
390     file_exists = stat(cn->actualfile, &stbuf);
391
392 #if defined(CONFIG_HTTP_HAS_CGI)
393
394     if (file_exists != -1 && cn->is_cgi)
395     {
396         if ((stbuf.st_mode & S_IEXEC) == 0 || isdir(cn->actualfile))
397         {
398             /* A non-executable file, or directory? */
399             send_error(cn, 403);
400         }
401         else
402             proccgi(cn);
403
404         return;
405     }
406 #endif
407
408     /* look for "index.html"? */
409     if (isdir(cn->actualfile))
410     {
411         char tbuf[MAXREQUESTLENGTH];
412         snprintf(tbuf, MAXREQUESTLENGTH, "%s%s", cn->actualfile, index_file);
413
414         if ((file_exists = stat(tbuf, &stbuf)) != -1) 
415             my_strncpy(cn->actualfile, tbuf, MAXREQUESTLENGTH);
416         else
417         {
418 #if defined(CONFIG_HTTP_DIRECTORIES)
419             /* If not, we do a directory listing of it */
420             procdirlisting(cn);
421 #else
422             send_error(cn, 404);
423 #endif
424             return;
425         }
426     }
427
428     if (file_exists == -1)
429     {
430         send_error(cn, 404);
431         return;
432     }
433
434     strcpy(date, ctime(&now));
435
436     /* has the file been read before? */
437     if (cn->if_modified_since != -1 && (cn->if_modified_since == 0 || 
438                                        cn->if_modified_since >= stbuf.st_mtime))
439     {
440         snprintf(buf, sizeof(buf), HTTP_VERSION" 304 Not Modified\nServer: "
441                 "%s\nDate: %s\n", server_version, date);
442         special_write(cn, buf, strlen(buf));
443         cn->state = STATE_WANT_TO_READ_HEAD;
444         return;
445     }
446
447     if (cn->reqtype == TYPE_HEAD) 
448     {
449         removeconnection(cn);
450         return;
451     } 
452     else 
453     {
454         int flags = O_RDONLY;
455 #if defined(WIN32) || defined(CONFIG_PLATFORM_CYGWIN)
456         flags |= O_BINARY;
457 #endif
458         cn->filedesc = open(cn->actualfile, flags);
459
460         if (cn->filedesc < 0) 
461         {
462             send_error(cn, 404);
463             return;
464         }
465
466         snprintf(buf, sizeof(buf), HTTP_VERSION" 200 OK\nServer: %s\n"
467             "Content-Type: %s\nContent-Length: %ld\n"
468             "Date: %sLast-Modified: %s\n", server_version,
469             getmimetype(cn->actualfile), (long) stbuf.st_size,
470             date, ctime(&stbuf.st_mtime)); /* ctime() has a \n on the end */
471
472         special_write(cn, buf, strlen(buf));
473
474 #ifdef CONFIG_HTTP_VERBOSE
475         printf("axhttpd: %s:/%s\n", cn->is_ssl ? "https" : "http", cn->filereq);
476         TTY_FLUSH();
477 #endif
478
479 #ifdef WIN32
480         for (;;)
481         {
482             procreadfile(cn);
483             if (cn->filedesc == -1)
484                 break;
485
486             do 
487             {
488                 procsendfile(cn);
489             } while (cn->state != STATE_WANT_TO_READ_FILE);
490         }
491 #else
492         cn->state = STATE_WANT_TO_READ_FILE;
493 #endif
494     }
495 }
496
497 void procreadfile(struct connstruct *cn) 
498 {
499     int rv = read(cn->filedesc, cn->databuf, BLOCKSIZE);
500
501     if (rv <= 0) 
502     {
503         close(cn->filedesc);
504         cn->filedesc = -1;
505
506         if (cn->close_when_done)        /* close immediately */
507             removeconnection(cn);
508         else 
509         {                               /* keep socket open - HTTP 1.1 */
510             cn->state = STATE_WANT_TO_READ_HEAD;
511             cn->numbytes = 0;
512         }
513
514         return;
515     }
516
517     cn->numbytes = rv;
518     cn->state = STATE_WANT_TO_SEND_FILE;
519 }
520
521 void procsendfile(struct connstruct *cn) 
522 {
523     int rv = special_write(cn, cn->databuf, cn->numbytes);
524
525     if (rv < 0)
526         removeconnection(cn);
527     else if (rv == cn->numbytes)
528     {
529         cn->state = STATE_WANT_TO_READ_FILE;
530     }
531     else if (rv == 0)
532     { 
533         /* Do nothing */ 
534     }
535     else 
536     {
537         memmove(cn->databuf, cn->databuf + rv, cn->numbytes - rv);
538         cn->numbytes -= rv;
539     }
540 }
541
542 #if defined(CONFIG_HTTP_HAS_CGI)
543 /* Should this be a bit more dynamic? It would mean more calls to malloc etc */
544 #define CGI_ARG_SIZE        17
545
546 static void proccgi(struct connstruct *cn) 
547 {
548     int tpipe[2], spipe[2];
549     char *myargs[2];
550     char cgienv[CGI_ARG_SIZE][MAXREQUESTLENGTH];
551     char * cgiptr[CGI_ARG_SIZE+4];
552     const char *type = "HEAD";
553     int cgi_index = 0, i;
554     pid_t pid;
555 #ifdef WIN32
556     int tmp_stdout;
557 #endif
558
559     snprintf(cgienv[0], MAXREQUESTLENGTH, 
560             HTTP_VERSION" 200 OK\nServer: %s\n%s",
561             server_version, (cn->reqtype == TYPE_HEAD) ? "\n" : "");
562     special_write(cn, cgienv[0], strlen(cgienv[0]));
563
564     if (cn->reqtype == TYPE_HEAD) 
565     {
566         removeconnection(cn);
567         return;
568     }
569
570 #ifdef CONFIG_HTTP_VERBOSE
571         printf("[CGI]: %s:/%s\n", cn->is_ssl ? "https" : "http", cn->filereq);
572         TTY_FLUSH();
573 #endif
574
575     /* win32 cgi is a bit too painful */
576 #ifndef WIN32
577         /* set up pipe that is used for sending POST query data to CGI script*/
578     if (cn->reqtype == TYPE_POST) 
579     {
580         if (pipe(spipe) == -1)
581         {
582             printf("[CGI]: could not create pipe");
583             TTY_FLUSH();
584             return;
585         }
586     }
587
588         if (pipe(tpipe) == -1)
589     {
590         printf("[CGI]: could not create pipe");
591         TTY_FLUSH();
592         return;
593     }
594
595     /*
596      * use vfork() instead of fork() for performance 
597      */
598     if ((pid = vfork()) > 0)  /* parent */
599     {
600         /* Send POST query data to CGI script */
601         if ((cn->reqtype == TYPE_POST) && (cn->content_length > 0)) 
602         {
603             write(spipe[1], cn->post_data, cn->content_length);
604             close(spipe[0]);     
605             close(spipe[1]);
606
607             /* free the memory that is allocated in read_post_data() */
608             free(cn->post_data); 
609             cn->post_data = NULL;
610         }
611
612         /* Close the write descriptor */
613         close(tpipe[1]);
614         cn->filedesc = tpipe[0];
615         cn->state = STATE_WANT_TO_READ_FILE;
616         cn->close_when_done = 1;
617         return;
618     }
619
620     if (pid < 0) /* vfork failed */
621         exit(1);
622
623     /* The problem child... */
624
625     /* Our stdout/stderr goes to the socket */
626     dup2(tpipe[1], 1);
627     dup2(tpipe[1], 2);
628
629     /* If it was a POST request, send the socket data to our stdin */
630     if (cn->reqtype == TYPE_POST) 
631         dup2(spipe[0], 0);  
632     else    /* Otherwise we can shutdown the read side of the sock */
633         shutdown(cn->networkdesc, 0);
634
635     myargs[0] = cn->actualfile;
636     myargs[1] = NULL;
637
638     /* 
639      * set the cgi args. A url is defined by:
640      * http://$SERVER_NAME:$SERVER_PORT$SCRIPT_NAME$PATH_INFO?$QUERY_STRING
641      * TODO: other CGI parameters?
642      */
643     sprintf(cgienv[cgi_index++], "SERVER_SOFTWARE=%s", server_version);
644     strcpy(cgienv[cgi_index++], "DOCUMENT_ROOT=" CONFIG_HTTP_WEBROOT);
645     snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
646             "SERVER_NAME=%s", cn->server_name);
647     sprintf(cgienv[cgi_index++], "SERVER_PORT=%d", 
648             cn->is_ssl ? CONFIG_HTTP_HTTPS_PORT : CONFIG_HTTP_PORT);
649     snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
650             "REQUEST_URI=%s", cn->uri_request);
651     snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
652             "SCRIPT_NAME=%s", cn->filereq);
653     snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
654             "PATH_INFO=%s", cn->uri_path_info);
655     snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
656             "QUERY_STRING=%s", cn->uri_query);
657     snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
658             "REMOTE_ADDR=%s", cn->remote_addr);
659     snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
660             "HTTP_COOKIE=%s", cn->cookie);  /* note: small size */
661 #if defined(CONFIG_HTTP_HAS_AUTHORIZATION)
662     snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
663             "REMOTE_USER=%s", cn->authorization);
664 #endif
665
666     switch (cn->reqtype)
667     {
668         case TYPE_GET: 
669             type = "GET";
670             break;
671
672         case TYPE_POST:
673             type = "POST";
674             sprintf(cgienv[cgi_index++], 
675                         "CONTENT_LENGTH=%d", cn->content_length);
676             strcpy(cgienv[cgi_index++],     /* hard-code? */
677                         "CONTENT_TYPE=application/x-www-form-urlencoded");
678             break;
679     }
680
681     sprintf(cgienv[cgi_index++], "REQUEST_METHOD=%s", type);
682
683     if (cn->is_ssl)
684         strcpy(cgienv[cgi_index++], "HTTPS=on");
685
686 #ifdef CONFIG_PLATFORM_CYGWIN
687     /* TODO: find out why Lua needs this */
688     strcpy(cgienv[cgi_index++], "PATH=/usr/bin");
689 #endif
690
691     if (cgi_index >= CGI_ARG_SIZE)
692     {
693         printf("Content-type: text/plain\n\nToo many CGI args (%d, %d)\n",
694                 cgi_index, CGI_ARG_SIZE);
695         _exit(1);
696     }
697
698     /* copy across the pointer indexes */
699     for (i = 0; i < cgi_index; i++)
700         cgiptr[i] = cgienv[i];
701
702     cgiptr[i++] = "AUTH_TYPE=Basic";
703     cgiptr[i++] = "GATEWAY_INTERFACE=CGI/1.1";
704     cgiptr[i++] = "SERVER_PROTOCOL="HTTP_VERSION;
705     cgiptr[i] = NULL;
706
707     execve(myargs[0], myargs, cgiptr);
708     printf("Content-type: text/plain\n\nshouldn't get here\n");
709     _exit(1);
710 #endif
711 }
712
713 static char * cgi_filetype_match(struct connstruct *cn, const char *fn)
714 {
715     struct cgiextstruct *tp = cgiexts;
716
717     while (tp != NULL) 
718     {
719         char *t;
720
721         if ((t = strstr(fn, tp->ext)) != NULL)
722         {
723             t += strlen(tp->ext);
724
725             if (*t == '/' || *t == '\0')
726             {
727 #ifdef CONFIG_HTTP_ENABLE_LUA
728                 if (strcmp(tp->ext, ".lua") == 0 || strcmp(tp->ext, ".lp") == 0)
729                     cn->is_lua = 1;
730 #endif
731
732                 return t;
733             }
734             else
735                 return NULL;
736
737         }
738
739         tp = tp->next;
740     }
741
742     return NULL;
743 }
744
745 static void decode_path_info(struct connstruct *cn, char *path_info)
746 {
747     char *cgi_delim;
748
749     cn->is_cgi = 0;
750 #ifdef CONFIG_HTTP_ENABLE_LUA
751     cn->is_lua = 0;
752 #endif
753     *cn->uri_request = '\0';
754     *cn->uri_path_info = '\0';
755     *cn->uri_query = '\0';
756
757     my_strncpy(cn->uri_request, path_info, MAXREQUESTLENGTH);
758
759     /* query info? */
760     if ((cgi_delim = strchr(path_info, '?')))
761     {
762         *cgi_delim = '\0';
763         my_strncpy(cn->uri_query, cgi_delim+1, MAXREQUESTLENGTH);
764     }
765
766     if ((cgi_delim = cgi_filetype_match(cn, path_info)) != NULL)
767     {
768         cn->is_cgi = 1;     /* definitely a CGI script */
769
770         /* path info? */
771         if (*cgi_delim != '\0')
772         {
773             my_strncpy(cn->uri_path_info, cgi_delim, MAXREQUESTLENGTH);
774             *cgi_delim = '\0';
775         }
776     }
777
778     /* the bit at the start must be the script name */
779     my_strncpy(cn->filereq, path_info, MAXREQUESTLENGTH);
780 }
781
782 static int init_read_post_data(char *buf, char *data, 
783                                 struct connstruct *cn, int old_rv)
784 {
785    char *next = data;
786    int rv = old_rv;
787    char *post_data;
788
789     /* Too much Post data to send. MAXPOSTDATASIZE should be 
790        configured (now it can be chaged in the header file) */
791    if (cn->content_length > MAXPOSTDATASIZE) 
792    {
793        send_error(cn, 418);
794        return 0;
795    }
796    
797    /* remove CRLF */
798    while ((*next == '\r' || *next == '\n') && (next < &buf[rv])) 
799        next++;
800    
801    if (cn->post_data == NULL)
802    {
803        cn->post_data = (char *) calloc(1, (cn->content_length + 1)); 
804        /* Allocate buffer for the POST data that will be used by proccgi 
805           to send POST data to the CGI script */
806
807        if (cn->post_data == NULL)
808        {
809            printf("axhttpd: could not allocate memory for POST data\n"); 
810            TTY_FLUSH();
811            send_error(cn, 599);
812            return 0;
813        }
814    }
815
816    cn->post_state = 0;
817    cn->post_read = 0;
818    post_data = cn->post_data;
819
820    while (next < &buf[rv])
821    { 
822        /*copy POST data to buffer*/
823        *post_data = *next;
824        post_data++;
825        next++;
826        cn->post_read++;
827        if (cn->post_read == cn->content_length)
828        { 
829            /* No more POST data to be copied */
830            *post_data = '\0';
831            return 1;
832        }
833    }
834
835    /* More POST data has to be read. read_post_data will continue with that */
836    cn->post_state = 1;
837    return 0;
838 }
839
840 void read_post_data(struct connstruct *cn)
841 {
842     char buf[MAXREQUESTLENGTH*4], *next;
843     char *post_data;
844     int rv;
845
846     bzero(buf,MAXREQUESTLENGTH*4);
847     rv = special_read(cn, buf, sizeof(buf)-1);
848     if (rv <= 0) 
849     {
850         if (rv < 0) /* really dead? */
851             removeconnection(cn);
852         return;
853     }
854
855     buf[rv] = '\0';
856     next = buf;
857
858     post_data = &cn->post_data[cn->post_read];
859
860     while (next < &buf[rv])
861     {
862         *post_data = *next;
863         post_data++;
864         next++;
865         cn->post_read++;
866         if (cn->post_read == cn->content_length)
867         {  
868             /* No more POST data to be copied */
869             *post_data='\0';
870             cn->post_state = 0;
871             buildactualfile(cn);
872             cn->state = STATE_WANT_TO_SEND_HEAD;
873             return;
874         }
875     }
876
877     /* More POST data to read */
878 }
879
880 #endif  /* CONFIG_HTTP_HAS_CGI */
881
882 /* Decode string %xx -> char (in place) */
883 static void urldecode(char *buf) 
884 {
885     int v;
886     char *p, *s, *w;
887
888     w = p = buf;
889
890     while (*p) 
891     {
892         v = 0;
893
894         if (*p == '%') 
895         {
896             s = p;
897             s++;
898
899             if (isxdigit((int) s[0]) && isxdigit((int) s[1]))
900             {
901                 v = hexit(s[0])*16 + hexit(s[1]);
902
903                 if (v) 
904                 { 
905                     /* do not decode %00 to null char */
906                     *w = (char)v;
907                     p = &s[1];
908                 }
909             }
910
911         }
912
913         if (!v) *w=*p;
914         p++; 
915         w++;
916     }
917
918     *w='\0';
919 }
920
921 static int hexit(char c) 
922 {
923     if (c >= '0' && c <= '9')
924         return c - '0';
925     else if (c >= 'a' && c <= 'f')
926         return c - 'a' + 10;
927     else if (c >= 'A' && c <= 'F')
928         return c - 'A' + 10;
929     else
930         return 0;
931 }
932
933 static void buildactualfile(struct connstruct *cn)
934 {
935     char *cp;
936     snprintf(cn->actualfile, MAXREQUESTLENGTH, ".%s", cn->filereq);
937
938 #ifndef WIN32
939     /* Add directory slash if not there */
940     if (isdir(cn->actualfile) && 
941             cn->actualfile[strlen(cn->actualfile)-1] != '/')
942         strcat(cn->actualfile, "/");
943
944     /* work out the directory name */
945     strncpy(cn->dirname, cn->actualfile, MAXREQUESTLENGTH);
946     if ((cp = strrchr(cn->dirname, '/')) == NULL)
947         cn->dirname[0] = 0;
948     else
949         *cp = 0;
950 #else
951     {
952         char curr_dir[MAXREQUESTLENGTH];
953         char path[MAXREQUESTLENGTH];
954         char *t = cn->actualfile;
955
956         GetCurrentDirectory(MAXREQUESTLENGTH, curr_dir);
957
958         /* convert all the forward slashes to back slashes */
959         while ((t = strchr(t, '/')))
960             *t++ = '\\';
961
962         snprintf(path, MAXREQUESTLENGTH, "%s%s", curr_dir, cn->actualfile);
963         memcpy(cn->actualfile, path, MAXREQUESTLENGTH);
964
965         /* Add directory slash if not there */
966         if (isdir(cn->actualfile) && 
967                     cn->actualfile[strlen(cn->actualfile)-1] != '\\')
968             strcat(cn->actualfile, "\\");
969
970         /* work out the directory name */
971         strncpy(cn->dirname, cn->actualfile, MAXREQUESTLENGTH);
972         if ((cp = strrchr(cn->dirname, '\\')) == NULL)
973             cn->dirname[0] = 0;
974         else
975             *cp = 0;
976     }
977 #endif
978
979 #if defined(CONFIG_HTTP_ENABLE_LUA)
980     /* 
981      * Use the lua launcher if this file has a lua extension. Put this at the
982      * end as we need the directory name.
983      */
984     if (cn->is_lua)
985         sprintf(cn->actualfile, "%s%s", CONFIG_HTTP_LUA_PREFIX, 
986                 CONFIG_HTTP_LUA_CGI_LAUNCHER);
987 #endif
988 }
989
990 static int sanitizefile(const char *buf) 
991 {
992     int len, i;
993
994     /* Don't accept anything not starting with a / */
995     if (*buf != '/') 
996         return 0;
997
998     len = strlen(buf);
999     for (i = 0; i < len; i++) 
1000     {
1001         /* Check for "/." i.e. don't send files starting with a . */
1002         if (buf[i] == '/' && buf[i+1] == '.') 
1003             return 0;
1004     }
1005
1006     return 1;
1007 }
1008
1009 static int sanitizehost(char *buf)
1010 {
1011     while (*buf != '\0') 
1012     {
1013         /* Handle the port */
1014         if (*buf == ':') 
1015         {
1016             *buf = '\0';
1017             return 1;
1018         }
1019
1020         /* Enforce some basic URL rules... */
1021         if ((isalnum(*buf) == 0 && *buf != '-' && *buf != '.') ||
1022                 (*buf == '.' && *(buf+1) == '.') ||
1023                 (*buf == '.' && *(buf+1) == '-') ||
1024                 (*buf == '-' && *(buf+1) == '.'))
1025             return 0;
1026
1027         buf++;
1028     }
1029
1030     return 1;
1031 }
1032
1033 static FILE * exist_check(struct connstruct *cn, const char *check_file)
1034 {
1035     char pathname[MAXREQUESTLENGTH];
1036     snprintf(pathname, MAXREQUESTLENGTH, "%s/%s", cn->dirname, check_file);
1037     return fopen(pathname, "r");
1038 }
1039
1040 #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
1041 static void send_authenticate(struct connstruct *cn, const char *realm)
1042 {
1043     char buf[1024];
1044
1045     snprintf(buf, sizeof(buf), HTTP_VERSION" 401 Unauthorized\n"
1046          "WWW-Authenticate: Basic\n"
1047                  "realm=\"%s\"\n", realm);
1048     special_write(cn, buf, strlen(buf));
1049 }
1050
1051 static int check_digest(char *salt, const char *msg_passwd)
1052 {
1053     uint8_t b256_salt[MAXREQUESTLENGTH];
1054     uint8_t real_passwd[MD5_SIZE];
1055     int salt_size;
1056     char *b64_passwd;
1057     uint8_t md5_result[MD5_SIZE];
1058     MD5_CTX ctx;
1059
1060     /* retrieve the salt */
1061     if ((b64_passwd = strchr(salt, '$')) == NULL)
1062         return -1;
1063
1064     *b64_passwd++ = 0;
1065     if (base64_decode(salt, strlen(salt), b256_salt, &salt_size))
1066         return -1;
1067
1068     if (base64_decode(b64_passwd, strlen(b64_passwd), real_passwd, NULL))
1069         return -1;
1070
1071     /* very simple MD5 crypt algorithm, but then the salt we use is large */
1072     MD5_Init(&ctx);
1073     MD5_Update(&ctx, b256_salt, salt_size);           /* process the salt */
1074     MD5_Update(&ctx, (uint8_t *)msg_passwd, strlen(msg_passwd)); 
1075     MD5_Final(md5_result, &ctx);
1076     return memcmp(md5_result, real_passwd, MD5_SIZE);/* 0 = ok */
1077 }
1078
1079 static int auth_check(struct connstruct *cn)
1080 {
1081     char line[MAXREQUESTLENGTH];
1082     FILE *fp;
1083     char *cp;
1084
1085     if ((fp = exist_check(cn, ".htpasswd")) == NULL)
1086         return 0;               /* no .htpasswd file, so let though */
1087
1088     if (cn->authorization[0] == 0)
1089         goto error;
1090
1091     /* cn->authorization is in form "username:password" */
1092     if ((cp = strchr(cn->authorization, ':')) == NULL)
1093         goto error;
1094     else
1095         *cp++ = 0;  /* cp becomes the password */
1096
1097     while (fgets(line, sizeof(line), fp) != NULL)
1098     {
1099         char *b64_file_passwd;
1100         int l = strlen(line);
1101
1102         /* nuke newline */
1103         if (line[l-1] == '\n')
1104             line[l-1] = 0;
1105
1106         /* line is form "username:salt(b64)$password(b64)" */
1107         if ((b64_file_passwd = strchr(line, ':')) == NULL)
1108             continue;
1109
1110         *b64_file_passwd++ = 0;
1111
1112         if (strcmp(line, cn->authorization)) /* our user? */
1113             continue;
1114
1115         if (check_digest(b64_file_passwd, cp) == 0)
1116         {
1117             fclose(fp);
1118             return 0;
1119         }
1120     }
1121
1122 error:
1123     fclose(fp);
1124     send_authenticate(cn, cn->server_name);
1125     return -1;
1126 }
1127 #endif
1128
1129 static int htaccess_check(struct connstruct *cn)
1130 {
1131     char line[MAXREQUESTLENGTH];
1132     FILE *fp;
1133     int ret = 0;
1134
1135     if ((fp = exist_check(cn, ".htaccess")) == NULL)
1136         return 0;               /* no .htaccess file, so let though */
1137
1138     while (fgets(line, sizeof(line), fp) != NULL)
1139     {
1140         if (strstr(line, "Deny all") || /* access to this dir denied */
1141                     /* Access will be denied unless SSL is active */
1142                     (!cn->is_ssl && strstr(line, "SSLRequireSSL")) ||
1143                     /* Access will be denied if SSL is active */
1144                     (cn->is_ssl && strstr(line, "SSLDenySSL")))
1145         {
1146             ret = -1;
1147             break;
1148         }
1149     }
1150
1151     fclose(fp);
1152     return ret;
1153 }
1154
1155 static void send_error(struct connstruct *cn, int err)
1156 {
1157     char buf[MAXREQUESTLENGTH];
1158     char *title;
1159     char *text;
1160
1161     switch (err)
1162     {
1163         case 403:
1164             title = "Forbidden";
1165             text = "File is protected";
1166 #ifdef CONFIG_HTTP_VERBOSE
1167             printf("axhttpd: access to %s denied\n", cn->filereq); TTY_FLUSH();
1168 #endif
1169             break;
1170
1171         case 404:
1172             title = "Not Found";
1173             text = title;
1174             break;
1175
1176         case 418:
1177             title = "POST data size is to large";
1178             text = title;
1179             break;
1180
1181         default:
1182             title = "Unknown";
1183             text = "Unknown";
1184             break;
1185     }
1186
1187     snprintf(buf, MAXREQUESTLENGTH, "HTTP/1.1 %d %s\n"
1188             "Content-Type: text/html\n"
1189             "Cache-Control: no-cache,no-store\n"
1190             "Connection: close\n\n"
1191             "<html>\n<head>\n<title>%d %s</title></head>\n"
1192             "<body><h1>%d %s</h1>\n</body></html>\n", 
1193             err, title, err, title, err, text);
1194     special_write(cn, buf, strlen(buf));
1195     removeconnection(cn);
1196 }
1197
1198 static const char *getmimetype(const char *name)
1199 {
1200     /* only bother with a few mime types - let the browser figure the rest out */
1201     if (strstr(name, ".htm"))
1202         return "text/html";
1203     else if (strstr(name, ".css"))
1204         return "text/css"; 
1205     else
1206         return "application/octet-stream";
1207 }
1208
1209 static int special_write(struct connstruct *cn, 
1210                                         const char *buf, size_t count)
1211 {
1212     if (cn->is_ssl)
1213     {
1214         SSL *ssl = cn->ssl;
1215         return ssl ? ssl_write(ssl, (uint8_t *)buf, count) : -1;
1216     }
1217     else
1218         return SOCKET_WRITE(cn->networkdesc, buf, count);
1219 }
1220
1221 static int special_read(struct connstruct *cn, void *buf, size_t count)
1222 {
1223     int res;
1224
1225     if (cn->is_ssl)
1226     {
1227         uint8_t *read_buf;
1228         if ((res = ssl_read(cn->ssl, &read_buf)) > SSL_OK)
1229         {
1230             memcpy(buf, read_buf, res > (int)count ? count : res);
1231         }
1232     }
1233     else
1234         res = SOCKET_READ(cn->networkdesc, buf, count);
1235
1236     return res;
1237 }
1238