root/trunk/wifidog/libhttpd/protocol.c @ 253

Revision 253, 16.3 KB (checked in by alexcv, 9 years ago)

httpd URL now parsed with variable size buffers.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2** Copyright (c) 2002  Hughes Technologies Pty Ltd.  All rights
3** reserved.
4**
5** Terms under which this software may be used or copied are
6** provided in the  specific license associated with this product.
7**
8** Hughes Technologies disclaims all warranties with regard to this
9** software, including all implied warranties of merchantability and
10** fitness, in no event shall Hughes Technologies be liable for any
11** special, indirect or consequential damages or any damages whatsoever
12** resulting from loss of use, data or profits, whether in an action of
13** contract, negligence or other tortious action, arising out of or in
14** connection with the use or performance of this software.
15**
16**
17** $Id$
18**
19*/
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <ctype.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <time.h>
28
29#if defined(_WIN32)
30#else
31#include <unistd.h>
32#include <sys/file.h>
33#endif
34
35#include "config.h"
36#include "httpd.h"
37#include "httpd_priv.h"
38
39
40int _httpd_net_read(sock, buf, len)
41        int     sock;
42        char    *buf;
43        int     len;
44{
45#if defined(_WIN32)
46        return( recv(sock, buf, len, 0));
47#else
48        return( read(sock, buf, len));
49#endif
50}
51
52
53int _httpd_net_write(sock, buf, len)
54        int     sock;
55        char    *buf;
56        int     len;
57{
58#if defined(_WIN32)
59        return( send(sock, buf, len, 0));
60#else
61        return( write(sock, buf, len));
62#endif
63}
64
65int _httpd_readChar(server, cp)
66        httpd   *server;
67        char    *cp;
68{
69        if (server->readBufRemain == 0)
70        {
71                bzero(server->readBuf, HTTP_READ_BUF_LEN + 1);
72                server->readBufRemain = _httpd_net_read(server->clientSock, 
73                        server->readBuf, HTTP_READ_BUF_LEN);
74                if (server->readBufRemain < 1)
75                        return(0);
76                server->readBuf[server->readBufRemain] = 0;
77                server->readBufPtr = server->readBuf;
78        }
79        *cp = *server->readBufPtr++;
80        server->readBufRemain--;
81        return(1);
82}
83
84
85int _httpd_readLine(server, destBuf, len)
86        httpd   *server;
87        char    *destBuf;
88        int     len;
89{
90        char    curChar,
91                *dst;
92        int     count;
93       
94
95        count = 0;
96        dst = destBuf;
97        while(count < len)
98        {
99                if (_httpd_readChar(server, &curChar) < 1)
100                        return(0);
101                if (curChar == '\n')
102                {
103                        *dst = 0;
104                        return(1);
105                }
106                if (curChar == '\r')
107                {
108                        continue;
109                }
110                else
111                {
112                        *dst++ = curChar;
113                        count++;
114                }
115        }
116        *dst = 0;
117        return(1);
118}
119
120
121int _httpd_readBuf(server, destBuf, len)
122        httpd   *server;
123        char    *destBuf;
124        int     len;
125{
126        char    curChar,
127                *dst;
128        int     count;
129       
130
131        count = 0;
132        dst = destBuf;
133        while(count < len)
134        {
135                if (_httpd_readChar(server, &curChar) < 1)
136                        return(0);
137                *dst++ = curChar;
138                count++;
139        }
140        return(1);
141}
142
143void _httpd_writeAccessLog(server)
144        httpd   *server;
145{
146        char    dateBuf[30];
147        struct  tm *timePtr;
148        time_t  clock;
149        int     responseCode;
150
151
152        if (server->accessLog == NULL)
153                return;
154        clock = time(NULL);
155        timePtr = localtime(&clock);
156        strftime(dateBuf, 30, "%d/%b/%Y:%T %Z",  timePtr);
157        responseCode = atoi(server->response.response);
158        fprintf(server->accessLog, "%s - - [%s] %s \"%s\" %d %d\n", 
159                server->clientAddr, dateBuf, httpdRequestMethodName(server), 
160                httpdRequestPath(server), responseCode, 
161                server->response.responseLength);
162}
163
164void _httpd_writeErrorLog(server, level, message)
165        httpd   *server;
166        char    *level,
167                *message;
168{
169        char    dateBuf[30];
170        struct  tm *timePtr;
171        time_t  clock;
172
173
174        if (server->errorLog == NULL)
175                return;
176        clock = time(NULL);
177        timePtr = localtime(&clock);
178        strftime(dateBuf, 30, "%a %b %d %T %Y",  timePtr);
179        if (*server->clientAddr != 0)
180        {
181                fprintf(server->errorLog, "[%s] [%s] [client %s] %s\n",
182                        dateBuf, level, server->clientAddr, message);
183        }
184        else
185        {
186                fprintf(server->errorLog, "[%s] [%s] %s\n",
187                        dateBuf, level, message);
188        }
189}
190
191
192
193int _httpd_decode (bufcoded, bufplain, outbufsize)
194        char *          bufcoded;
195        char *          bufplain;
196        int             outbufsize;
197{
198        static char six2pr[64] = {
199                'A','B','C','D','E','F','G','H','I','J','K','L','M',
200                'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
201                'a','b','c','d','e','f','g','h','i','j','k','l','m',
202                'n','o','p','q','r','s','t','u','v','w','x','y','z',
203                '0','1','2','3','4','5','6','7','8','9','+','/'   
204        };
205 
206        static unsigned char pr2six[256];
207
208        /* single character decode */
209#       define DEC(c) pr2six[(int)c]
210#       define _DECODE_MAXVAL 63
211
212        static int first = 1;
213
214        int nbytesdecoded, j;
215        register char *bufin = bufcoded;
216        register unsigned char *bufout = bufplain;
217        register int nprbytes;
218
219        /*
220        ** If this is the first call, initialize the mapping table.
221        ** This code should work even on non-ASCII machines.
222        */
223        if(first) 
224        {
225                first = 0;
226                for(j=0; j<256; j++) pr2six[j] = _DECODE_MAXVAL+1;
227                for(j=0; j<64; j++) pr2six[(int)six2pr[j]] = (unsigned char)j;
228        }
229
230        /* Strip leading whitespace. */
231
232        while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++;
233
234        /*
235        ** Figure out how many characters are in the input buffer.
236        ** If this would decode into more bytes than would fit into
237        ** the output buffer, adjust the number of input bytes downwards.
238        */
239        bufin = bufcoded;
240        while(pr2six[(int)*(bufin++)] <= _DECODE_MAXVAL);
241        nprbytes = bufin - bufcoded - 1;
242        nbytesdecoded = ((nprbytes+3)/4) * 3;
243        if(nbytesdecoded > outbufsize) 
244        {
245                nprbytes = (outbufsize*4)/3;
246        }
247        bufin = bufcoded;
248   
249        while (nprbytes > 0) 
250        {
251                *(bufout++)=(unsigned char)(DEC(*bufin)<<2|DEC(bufin[1])>>4);
252                *(bufout++)=(unsigned char)(DEC(bufin[1])<<4|DEC(bufin[2])>>2);
253                *(bufout++)=(unsigned char)(DEC(bufin[2])<<6|DEC(bufin[3]));
254                bufin += 4;
255                nprbytes -= 4;
256        }
257        if(nprbytes & 03) 
258        {
259                if(pr2six[(int)bufin[-2]] > _DECODE_MAXVAL) 
260                {
261                        nbytesdecoded -= 2;
262                }
263                else 
264                {
265                        nbytesdecoded -= 1;
266                }
267        }
268        bufplain[nbytesdecoded] = 0;
269        return(nbytesdecoded);
270}
271
272
273
274char _httpd_from_hex (c)
275        char    c;
276{
277    return  c >= '0' && c <= '9' ?  c - '0'
278            : c >= 'A' && c <= 'F'? c - 'A' + 10
279            : c - 'a' + 10;     /* accept small letters just in case */
280}
281
282char * _httpd_unescape(str)
283        char    *str;
284{
285    char * p = str;
286    char * q = str;
287    static char blank[] = "";
288
289    if (!str)
290        return(blank);
291    while(*p) {
292        if (*p == '%') {
293            p++;
294            if (*p) *q = _httpd_from_hex(*p++) * 16;
295            if (*p) *q = (*q + _httpd_from_hex(*p++));
296            q++;
297        } else {
298            if (*p == '+') {
299                *q++ = ' ';
300                p++;
301            } else {
302                *q++ = *p++;
303              }
304        }
305    }
306
307    *q++ = 0;
308    return str;
309} 
310
311
312void _httpd_freeVariables(var)
313        httpVar *var;
314{
315        httpVar *curVar, *lastVar;
316
317        if (var == NULL)
318                return;
319        _httpd_freeVariables(var->nextVariable);
320        var->nextVariable = NULL;
321        curVar = var;
322        while(curVar)
323        {
324                lastVar = curVar;
325                curVar = curVar->nextValue;
326                free(lastVar->name);
327                free(lastVar->value);
328                free(lastVar);
329        }
330        return;
331}
332
333void _httpd_storeData(server, query)
334        httpd   *server;
335        char    *query;
336{
337        char    *cp,
338                *cp2,
339                *var,
340                *val,
341                *tmpVal;
342
343        if (!query)
344                return;
345
346        var = (char *)malloc(strlen(query));
347       
348        cp = query;
349        cp2 = var;
350        bzero(var, strlen(query));
351        val = NULL;
352        while(*cp)
353        {
354                if (*cp == '=')
355                {
356                        cp++;
357                        *cp2 = 0;
358                        val = cp;
359                        continue;
360                }
361                if (*cp == '&')
362                {
363                        *cp = 0;
364                        tmpVal = _httpd_unescape(val);
365                        httpdAddVariable(server, var, tmpVal);
366                        cp++;
367                        cp2 = var;
368                        val = NULL;
369                        continue;
370                }
371                if (val)
372                {
373                        cp++;
374                }
375                else
376                {
377                        *cp2 = *cp++;
378                        if (*cp2 == '.')
379                        {
380                                strcpy(cp2,"_dot_");
381                                cp2 += 5;
382                        }
383                        else
384                        {
385                                cp2++;
386                        }
387                }
388        }
389        *cp = 0;
390        tmpVal = _httpd_unescape(val);
391        httpdAddVariable(server, var, tmpVal);
392        free(var);
393}
394
395
396void _httpd_formatTimeString(server, ptr, clock)
397        httpd   *server;
398        char    *ptr;
399        int     clock;
400{
401        struct  tm *timePtr;
402
403        if (clock == 0)
404                clock = time(NULL);
405        timePtr = gmtime((time_t*)&clock);
406        strftime(ptr, HTTP_TIME_STRING_LEN,"%a, %d %b %Y %T GMT",timePtr);
407}
408
409
410void _httpd_sendHeaders(server, contentLength, modTime)
411        httpd   *server;
412        int     contentLength,
413                modTime;
414{
415        char    tmpBuf[80],
416                timeBuf[HTTP_TIME_STRING_LEN];
417
418        if(server->response.headersSent)
419                return;
420
421        server->response.headersSent = 1;
422        _httpd_net_write(server->clientSock, "HTTP/1.0 ", 9);
423        _httpd_net_write(server->clientSock, server->response.response, 
424                strlen(server->response.response));
425        _httpd_net_write(server->clientSock, server->response.headers, 
426                strlen(server->response.headers));
427
428        _httpd_formatTimeString(server, timeBuf, 0);
429        _httpd_net_write(server->clientSock,"Date: ", 6);
430        _httpd_net_write(server->clientSock, timeBuf, strlen(timeBuf));
431        _httpd_net_write(server->clientSock, "\n", 1);
432
433        _httpd_net_write(server->clientSock, "Connection: close\n", 18);
434        _httpd_net_write(server->clientSock, "Content-Type: ", 14);
435        _httpd_net_write(server->clientSock, server->response.contentType, 
436                strlen(server->response.contentType));
437        _httpd_net_write(server->clientSock, "\n", 1);
438
439        if (contentLength > 0)
440        {
441                _httpd_net_write(server->clientSock, "Content-Length: ", 16);
442                snprintf(tmpBuf, sizeof(tmpBuf), "%d", contentLength);
443                _httpd_net_write(server->clientSock, tmpBuf, strlen(tmpBuf));
444                _httpd_net_write(server->clientSock, "\n", 1);
445
446                _httpd_formatTimeString(server, timeBuf, modTime);
447                _httpd_net_write(server->clientSock, "Last-Modified: ", 15);
448                _httpd_net_write(server->clientSock, timeBuf, strlen(timeBuf));
449                _httpd_net_write(server->clientSock, "\n", 1);
450        }
451        _httpd_net_write(server->clientSock, "\n", 1);
452}
453
454httpDir *_httpd_findContentDir(server, dir, createFlag)
455        httpd   *server;
456        char    *dir;
457        int     createFlag;
458{
459        char    buffer[HTTP_MAX_URL],
460                *curDir;
461        httpDir *curItem,
462                *curChild;
463
464        strncpy(buffer, dir, HTTP_MAX_URL);
465        curItem = server->content;
466        curDir = strtok(buffer,"/");
467        while(curDir)
468        {
469                curChild = curItem->children;
470                while(curChild)
471                {
472                        if (strcmp(curChild->name, curDir) == 0)
473                                break;
474                        curChild = curChild->next;
475                }
476                if (curChild == NULL)
477                {
478                        if (createFlag == HTTP_TRUE)
479                        {
480                                curChild = malloc(sizeof(httpDir));
481                                bzero(curChild, sizeof(httpDir));
482                                curChild->name = strdup(curDir);
483                                curChild->next = curItem->children;
484                                curItem->children = curChild;
485                        }
486                        else
487                        {
488                                return(NULL);
489                        }
490                }
491                curItem = curChild;
492                curDir = strtok(NULL,"/");
493        }
494        return(curItem);
495}
496
497
498httpContent *_httpd_findContentEntry(server, dir, entryName)
499        httpd   *server;
500        httpDir *dir;
501        char    *entryName;
502{
503        httpContent *curEntry;
504
505        curEntry = dir->entries;
506        while(curEntry)
507        {
508                if (curEntry->type == HTTP_WILDCARD || 
509                    curEntry->type ==HTTP_C_WILDCARD)
510                        break;
511                if (*entryName == 0 && curEntry->indexFlag)
512                        break;
513                if (strcmp(curEntry->name, entryName) == 0)
514                        break;
515                curEntry = curEntry->next;
516        }
517        if (curEntry)
518                server->response.content = curEntry;
519        return(curEntry);
520}
521
522
523void _httpd_send304(server)
524        httpd   *server;
525{
526        httpdSetResponse(server, "304 Not Modified\n");
527        _httpd_sendHeaders(server,0,0);
528}
529
530
531void _httpd_send403(server)
532        httpd   *server;
533{
534        httpdSetResponse(server, "403 Permission Denied\n");
535        _httpd_sendHeaders(server,0,0);
536        _httpd_sendText(server,
537                "<HTML><HEAD><TITLE>403 Permission Denied</TITLE></HEAD>\n");
538        _httpd_sendText(server,
539                "<BODY><H1>Access to the request URL was denied!</H1>\n");
540}
541
542
543void _httpd_send404(server)
544        httpd   *server;
545{
546        char    msg[HTTP_MAX_URL];
547
548        snprintf(msg, HTTP_MAX_URL,
549                "File does not exist: %s", server->request.path);
550        _httpd_writeErrorLog(server,LEVEL_ERROR, msg);
551
552        if (server->handle404 && server->handle404->function) {
553                /*
554                 * There's a custom C 404 handler defined with httpdAddC404Content
555                 */
556                (server->handle404->function)(server);
557        }
558        else {
559                /*
560                 * Send stock 404
561                 */
562                httpdSetResponse(server, "404 Not Found\n");
563                _httpd_sendHeaders(server,0,0);
564                _httpd_sendText(server,
565                        "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
566                _httpd_sendText(server,
567                        "<BODY><H1>The request URL was not found!</H1>\n");
568                _httpd_sendText(server, "</BODY></HTML>\n");
569        }
570}
571
572
573void _httpd_catFile(server, path)
574        httpd   *server;
575        char    *path;
576{
577        int     fd,
578                len;
579        char    buf[HTTP_MAX_LEN];
580
581        fd = open(path,O_RDONLY);
582        if (fd < 0)
583                return;
584        len = read(fd, buf, HTTP_MAX_LEN);
585        while(len > 0)
586        {
587                server->response.responseLength += len;
588                _httpd_net_write(server->clientSock, buf, len);
589                len = read(fd, buf, HTTP_MAX_LEN);
590        }
591        close(fd);
592}
593
594
595void _httpd_sendStatic(server, data)
596        httpd   *server;
597        char    *data;
598{
599        if (_httpd_checkLastModified(server,server->startTime) == 0)
600        {
601                _httpd_send304(server);
602        }
603        _httpd_sendHeaders(server, server->startTime, strlen(data));
604        httpdOutput(server, data);
605}
606
607
608
609void _httpd_sendFile(server, path)
610        httpd   *server;
611        char    *path;
612{
613        char    *suffix;
614        struct  stat sbuf;
615
616        suffix = rindex(path, '.');
617        if (suffix != NULL)
618        {
619                if (strcasecmp(suffix,".gif") == 0) 
620                        strcpy(server->response.contentType,"image/gif");
621                if (strcasecmp(suffix,".jpg") == 0) 
622                        strcpy(server->response.contentType,"image/jpeg");
623                if (strcasecmp(suffix,".xbm") == 0) 
624                        strcpy(server->response.contentType,"image/xbm");
625                if (strcasecmp(suffix,".png") == 0) 
626                        strcpy(server->response.contentType,"image/png");
627        }
628        if (stat(path, &sbuf) < 0)
629        {
630                _httpd_send404(server);
631                return;
632        }
633        if (_httpd_checkLastModified(server,sbuf.st_mtime) == 0)
634        {
635                _httpd_send304(server);
636        }
637        else
638        {
639                _httpd_sendHeaders(server, sbuf.st_size, sbuf.st_mtime);
640                _httpd_catFile(server, path);
641        }
642}
643
644
645int _httpd_sendDirectoryEntry(server, entry, entryName)
646        httpd           *server;
647        httpContent     *entry;
648        char            *entryName;
649{
650        char            path[HTTP_MAX_URL];
651
652        snprintf(path, HTTP_MAX_URL, "%s/%s", entry->path, entryName);
653        _httpd_sendFile(server,path);
654        return(0);
655}
656
657
658void _httpd_sendText(server, msg)
659        httpd   *server;
660        char    *msg;
661{
662        server->response.responseLength += strlen(msg);
663        _httpd_net_write(server->clientSock,msg,strlen(msg));
664}
665
666
667int _httpd_checkLastModified(server, modTime)
668        httpd   *server;
669        int     modTime;
670{
671        char    timeBuf[HTTP_TIME_STRING_LEN];
672
673        _httpd_formatTimeString(server, timeBuf, modTime);
674        if (strcmp(timeBuf, server->request.ifModified) == 0)
675                return(0);
676        return(1);
677}
678
679
680static unsigned char isAcceptable[96] =
681
682/* Overencodes */
683#define URL_XALPHAS     (unsigned char) 1
684#define URL_XPALPHAS    (unsigned char) 2
685
686/*      Bit 0           xalpha          -- see HTFile.h
687**      Bit 1           xpalpha         -- as xalpha but with plus.
688**      Bit 2 ...       path            -- as xpalpha but with /
689*/
690    /*   0 1 2 3 4 5 6 7 8 9 A B C D E F */
691    {    7,0,0,0,0,0,0,0,0,0,7,0,0,7,7,7,       /* 2x   !"#$%&'()*+,-./ */
692         7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,       /* 3x  0123456789:;<=>?  */
693         7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,       /* 4x  @ABCDEFGHIJKLMNO */
694         7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7,       /* 5X  PQRSTUVWXYZ[\]^_ */
695         0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,       /* 6x  `abcdefghijklmno */
696         7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0 };     /* 7X  pqrstuvwxyz{\}~ DEL */
697 
698#define ACCEPTABLE(a)   ( a>=32 && a<128 && ((isAcceptable[a-32]) & mask))
699
700static char *hex = "0123456789ABCDEF";
701
702
703char *_httpd_escape(str)
704        char *str;
705{
706    unsigned char mask = URL_XPALPHAS;
707    char * p;
708    char * q;
709    char * result;
710    int unacceptable = 0;
711    for(p=str; *p; p++)
712        if (!ACCEPTABLE((unsigned char)*p))
713                unacceptable +=2;
714    result = (char *) malloc(p-str + unacceptable + 1);
715    bzero(result,(p-str + unacceptable + 1));
716
717    if (result == NULL)
718    {
719        return(NULL);
720    }
721    for(q=result, p=str; *p; p++) {
722        unsigned char a = *p;
723        if (!ACCEPTABLE(a)) {
724            *q++ = '%';  /* Means hex commming */
725            *q++ = hex[a >> 4];
726            *q++ = hex[a & 15];
727        }
728        else *q++ = *p;
729    }
730    *q++ = 0;                   /* Terminate */
731    return result;
732}
733
734
735
736void _httpd_sanitiseUrl(url)
737        char    *url;
738{
739        char    *from,
740                *to,
741                *last;
742
743        /*
744        ** Remove multiple slashes
745        */
746        from = to = url;
747        while(*from)
748        {
749                if (*from == '/' && *(from+1) == '/')
750                {
751                        from++;
752                        continue;
753                }
754                *to = *from;
755                to++;
756                from++;
757        }
758        *to = 0;
759
760
761        /*
762        ** Get rid of ./ sequences
763        */
764        from = to = url;
765        while(*from)
766        {
767                if (*from == '/' && *(from+1) == '.' && *(from+2)=='/')
768                {
769                        from += 2;
770                        continue;
771                }
772                *to = *from;
773                to++;
774                from++;
775        }
776        *to = 0;
777
778
779        /*
780        ** Catch use of /../ sequences and remove them.  Must track the
781        ** path structure and remove the previous path element.
782        */
783        from = to = last = url;
784        while(*from)
785        {
786                if (*from == '/' && *(from+1) == '.' && 
787                        *(from+2)=='.' && *(from+3)=='/')
788                {
789                        to = last;
790                        from += 3;
791                        continue;
792                }
793                if (*from == '/')
794                {
795                        last = to;
796                }
797                *to = *from;
798                to++;
799                from++;
800        }
801        *to = 0;
802}
Note: See TracBrowser for help on using the browser.