source: rtems/cpukit/httpd/webs.c @ bfb4c547

4.104.114.84.9
Last change on this file since bfb4c547 was bfb4c547, checked in by Joel Sherrill <joel.sherrill@…>, on Mar 5, 2004 at 6:14:27 PM

2004-03-05 Joel Sherrill <joel@…>

  • libnetworking/rtems_webserver/ejparse.c, libnetworking/rtems_webserver/emfdb.c, libnetworking/rtems_webserver/sock.c, libnetworking/rtems_webserver/socket.c, libnetworking/rtems_webserver/sym.c, libnetworking/rtems_webserver/webs.c, libnetworking/rtems_webserver/websuemf.c: Remove warnings.
  • Property mode set to 100644
File size: 66.2 KB
Line 
1/*
2 * webs.c -- GoAhead Embedded HTTP webs server
3 *
4 * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
5 *
6 * See the file "license.txt" for usage and redistribution license requirements
7 *
8 * $Id$
9 */
10
11/******************************** Description *********************************/
12
13/*
14 *      This module implements an embedded HTTP/1.1 web server. It supports
15 *      loadable URL handlers that define the nature of URL processing performed.
16 */
17
18/********************************* Includes ***********************************/
19
20#include        "wsIntrn.h"
21#ifdef DIGEST_ACCESS_SUPPORT
22        #include        "websda.h"
23#endif
24
25/******************************** Global Data *********************************/
26
27websStatsType   websStats;                              /* Web access stats */
28webs_t                  *webs;                                  /* Open connection list head */
29sym_fd_t                websMime;                               /* Set of mime types */
30int                             websMax;                                /* List size */
31int                             websPort;                               /* Listen port for server */
32char_t                  websHost[64];                   /* Host name for the server */
33char_t                  websIpaddr[64];                 /* IP address for the server */
34char_t                  *websHostUrl = NULL;    /* URL to access server */
35char_t                  *websIpaddrUrl = NULL;  /* URL to access server */
36
37/*********************************** Locals ***********************************/
38/*
39 *      Standard HTTP error codes
40 */
41
42websErrorType websErrors[] = {
43        { 200, T("Data follows") },
44        { 204, T("No Content") },
45        { 301, T("Redirect") },
46        { 302, T("Redirect") },
47        { 304, T("Use local copy") },
48        { 400, T("Page not found") },
49        { 401, T("Unauthorized") },
50        { 403, T("Forbidden") },
51        { 404, T("Site or Page Not Found") },
52        { 405, T("Access Denied") },
53        { 500, T("Web Error") },
54        { 501, T("Not Implemented") },
55        { 503, T("Site Temporarily Unavailable. Try again.") },
56        { 0, NULL }
57};
58
59#ifdef WEBS_LOG_SUPPORT
60static char_t   websLogname[64] = T("log.txt"); /* Log filename */
61static int              websLogFd;                                              /* Log file handle */
62#endif
63
64static int              websListenSock;                                 /* Listen socket */
65static char_t   websRealm[64] = T("GoAhead");   /* Realm name */
66
67static int              websOpenCount = 0;              /* count of apps using this module */
68
69/**************************** Forward Declarations ****************************/
70
71
72/*static char_t         *websErrorMsg(int code);*/
73static int              websGetInput(webs_t wp, char_t **ptext, int *nbytes);
74static int              websParseFirst(webs_t wp, char_t *text);
75static void     websParseRequest(webs_t wp);
76static void             websSocketEvent(int sid, int mask, int data);
77static int              websGetTimeSinceMark(webs_t wp);
78
79#ifdef WEBS_LOG_SUPPORT
80static void     websLog(webs_t wp, int code);
81#endif
82#ifdef WEBS_IF_MODIFIED_SUPPORT
83static time_t   dateParse(time_t tip, char_t *cmd);
84#endif
85
86/*********************************** Code *************************************/
87/*
88 *      Open the GoAhead WebServer
89 */
90
91int websOpenServer(int port, int retries)
92{
93        websMimeType    *mt;
94
95        if (++websOpenCount != 1) {
96                return websPort;
97        }
98
99        a_assert(port > 0);
100        a_assert(retries >= 0);
101
102#ifdef WEBS_PAGE_ROM
103        websRomOpen();
104#endif
105
106        webs = NULL;
107        websMax = 0;
108/*
109 *      Create a mime type lookup table for quickly determining the content type
110 */
111        websMime = symOpen(WEBS_SYM_INIT * 4);
112        a_assert(websMime >= 0);
113        for (mt = websMimeList; mt->type; mt++) {
114                symEnter(websMime, mt->ext, valueString(mt->type, 0), 0);
115        }
116
117/*
118 *      Open the URL handler module. The caller should create the required
119 *      URL handlers after calling this function.
120 */
121        if (websUrlHandlerOpen() < 0) {
122                return -1;
123        }
124        websFormOpen();
125
126#ifdef WEBS_LOG_SUPPORT
127/*
128 *      Optional request log support
129 */
130        websLogFd = gopen(websLogname, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY, 
131                0666);
132        a_assert(websLogFd >= 0);
133#endif
134       
135        return websOpenListen(port, retries);
136}
137
138/******************************************************************************/
139/*
140 *      Close the GoAhead WebServer
141 */
142
143void websCloseServer()
144{
145        webs_t  wp;
146        int             wid;
147
148        if (--websOpenCount > 0) {
149                return;
150        }
151
152/*
153 *      Close the listen handle first then all open connections.
154 */
155        websCloseListen();
156
157/*
158 *      Close each open browser connection and free all resources
159 */
160        for (wid = websMax; webs && wid >= 0; wid--) {
161                if ((wp = webs[wid]) == NULL) {
162                        continue;
163                }
164                socketCloseConnection(wp->sid);
165                websFree(wp);
166        }
167
168#ifdef WEBS_LOG_SUPPORT
169        if (websLogFd >= 0) {
170                close(websLogFd);
171                websLogFd = -1;
172        }
173#endif
174
175#ifdef WEBS_PAGE_ROM
176        websRomClose();
177#endif
178        symClose(websMime);
179        websFormClose();
180        websUrlHandlerClose();
181}
182
183/******************************************************************************/
184/*
185 *      Open the GoAhead WebServer listen port
186 */
187
188int websOpenListen(int port, int retries)
189{
190        int             i, orig;
191
192        a_assert(port > 0);
193        a_assert(retries >= 0);
194
195        orig = port;
196/*
197 *      Open the webs webs listen port. If we fail, try the next port.
198 */
199        for (i = 0; i <= retries; i++) {
200                websListenSock = socketOpenConnection(NULL, port, websAccept, 0);
201                if (websListenSock >= 0) {
202                        break;
203                }
204                port++;
205        }
206        if (i > retries) {
207                error(E_L, E_USER, T("Couldn't open a socket on ports %d - %d"),
208                        orig, port - 1);
209                return -1;
210        } 
211
212/*
213 *      Determine the full URL address to access the home page for this web server
214 */
215        websPort = port;
216        bfreeSafe(B_L, websHostUrl);
217        bfreeSafe(B_L, websIpaddrUrl);
218        websIpaddrUrl = websHostUrl = NULL;
219
220        if (port == 80) {
221                websHostUrl = bstrdup(B_L, websHost);
222                websIpaddrUrl = bstrdup(B_L, websIpaddr);
223        } else {
224                fmtAlloc(&websHostUrl, WEBS_MAX_URL + 80, T("%s:%d"), websHost, port);
225                fmtAlloc(&websIpaddrUrl, WEBS_MAX_URL + 80, T("%s:%d"), 
226                        websIpaddr, port);
227        }
228        trace(0, T("webs: Listening for HTTP requests at address %s\n"),
229                websIpaddrUrl);
230
231        return port;
232}
233
234/******************************************************************************/
235/*
236 *      Close webs listen port
237 */
238
239void websCloseListen()
240{
241        if (websListenSock >= 0) {
242                socketCloseConnection(websListenSock);
243                websListenSock = -1;
244        }
245        bfreeSafe(B_L, websHostUrl);
246        bfreeSafe(B_L, websIpaddrUrl);
247        websIpaddrUrl = websHostUrl = NULL;
248}
249
250/******************************************************************************/
251/*
252 *      Accept a connection
253 */
254
255int websAccept(int sid, char *ipaddr, int port, int listenSid)
256{
257        webs_t  wp;
258        int             wid;
259
260        a_assert(ipaddr && *ipaddr);
261        a_assert(sid >= 0);
262        a_assert(port >= 0);
263
264/*
265 *      Allocate a new handle for this accepted connection. This will allocate
266 *      a webs_t structure in the webs[] list
267 */
268        if ((wid = websAlloc(sid)) < 0) {
269                return -1;
270        }
271        wp = webs[wid];
272        a_assert(wp);
273        wp->listenSid = listenSid;
274
275        ascToUni(wp->ipaddr, ipaddr, min(sizeof(wp->ipaddr), strlen(ipaddr) + 1));
276
277/*
278 *      Check if this is a request from a browser on this system. This is useful
279 *      to know for permitting administrative operations only for local access
280 */
281        if (gstrcmp(wp->ipaddr, T("127.0.0.1")) == 0 || 
282                        gstrcmp(wp->ipaddr, websIpaddr) == 0 || 
283                        gstrcmp(wp->ipaddr, websHost) == 0) {
284                wp->flags |= WEBS_LOCAL_REQUEST;
285        }
286
287/*
288 *      Arrange for websSocketEvent to be called when read data is available
289 */
290        socketCreateHandler(sid, SOCKET_READABLE, websSocketEvent, (int) wp);
291
292/*
293 *      Arrange for a timeout to kill hung requests
294 */
295        wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout, (void *) wp);
296        trace(8, T("webs: accept request\n"));
297        return 0;
298}
299
300/******************************************************************************/
301/*
302 *      The webs socket handler.  Called in response to I/O. We just pass control
303 *      to the relevant read or write handler. A pointer to the webs structure
304 *      is passed as an (int) in iwp.
305 */
306
307static void websSocketEvent(int sid, int mask, int iwp)
308{
309        webs_t  wp;
310
311        wp = (webs_t) iwp;
312        a_assert(wp);
313
314        if (! websValid(wp)) {
315                return;
316        }
317
318        if (mask & SOCKET_READABLE) {
319                websReadEvent(wp);
320        } 
321        if (mask & SOCKET_WRITABLE) {
322                if (websValid(wp) && wp->writeSocket) {
323                        (*wp->writeSocket)(wp);
324                }
325        } 
326}
327
328/******************************************************************************/
329/*
330 *      The webs read handler. This is the primary read event loop. It uses a
331 *      state machine to track progress while parsing the HTTP request.
332 *      Note: we never block as the socket is always in non-blocking mode.
333 */
334
335void websReadEvent(webs_t wp)
336{
337        char_t  *text;
338        int             rc, nbytes, len, done, fd;
339
340        a_assert(wp);
341        a_assert(websValid(wp));
342
343        websSetTimeMark(wp);
344
345/*
346 *      Read as many lines as possible. socketGets is called to read the header
347 *      and socketRead is called to read posted data.
348 */
349        text = NULL;
350        fd = -1;
351        for (done = 0; !done; ) {
352                if (text) {
353                        bfree(B_L, text);
354                        text = NULL;
355                }
356
357/*
358 *              Get more input into "text". Returns 0, if more data is needed
359 *              to continue, -1 if finished with the request, or 1 if all
360 *              required data is available for current state.
361 */
362                while ((rc = websGetInput(wp, &text, &nbytes)) == 0) {
363                        ;
364                }
365
366/*
367 *              websGetInput returns -1 if it finishes with the request
368 */
369                if (rc < 0) {
370                        break;
371                }
372
373/*
374 *              This is the state machine for the web server.
375 */
376                switch(wp->state) {
377                case WEBS_BEGIN:
378/*
379 *                      Parse the first line of the Http header
380 */
381                        if (websParseFirst(wp, text) < 0) {
382                                done++;
383                                break;
384                        }
385                        wp->state = WEBS_HEADER;
386                        break;
387               
388                case WEBS_HEADER:
389/*
390 *                      Store more of the HTTP header. As we are doing line reads, we
391 *                      need to separate the lines with '\n'
392 */
393                        if (ringqLen(&wp->header) > 0) {
394                                ringqPutStr(&wp->header, T("\n"));
395                        }
396                        ringqPutStr(&wp->header, text);
397                        break;
398
399                case WEBS_POST_CLEN:
400/*
401 *                      POST request with content specified by a content length.
402 *                      If this is a CGI request, write the data to the cgi stdin.
403 *                      socketGets was used to get the data and it strips \n's so
404 *                      add them back in here.
405 */
406#ifndef __NO_CGI_BIN
407                        if (wp->flags & WEBS_CGI_REQUEST) {
408                                if (fd == -1) {
409                                        fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY,
410                                                0666);
411                                }
412                                gwrite(fd, text, gstrlen(text));
413            /*
414             * NOTE that the above comment is wrong -- if the content length
415             * is set, websGetInput() does NOT use socketGets(), it uses
416             * socketRead(), so the line below that adds an additional newline
417             * is destructive.
418             */
419                                /*gwrite(fd, T("\n"), sizeof(char_t));*/
420/*
421 *                              Line removed as per BUG02488
422 *
423                                nbytes += 1;
424 */
425                        } else 
426#endif
427                        if (wp->query) {
428                                if (wp->query[0] && !(wp->flags & WEBS_POST_DATA)) {
429/*
430 *                                      Special case where the POST request also had query data
431 *                                      specified in the URL, ie. url?query_data. In this case
432 *                                      the URL query data is separated by a '&' from the posted
433 *                                      query data.
434 */
435                                        len = gstrlen(wp->query);
436                                        wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
437                                                2) * sizeof(char_t));
438                                        wp->query[len++] = '&';
439                                        gstrcpy(&wp->query[len], text);
440
441                                } else {
442/*
443 *                                      The existing query data came from the POST request so just
444 *                                      append it.
445 */
446               if (text != NULL)
447               {
448                  len = gstrlen(wp->query);
449                  wp->query = brealloc(B_L, wp->query, (len +   gstrlen(text) +
450                     1) * sizeof(char_t));
451                  if (wp->query) {
452                     gstrcpy(&wp->query[len], text);
453                  }
454               }
455                                }
456
457                        } else {
458                                wp->query = bstrdup(B_L, text);
459                        }
460/*
461 *                      Calculate how much more post data is to be read.
462 */
463                        wp->flags |= WEBS_POST_DATA;
464                        wp->clen -= nbytes;
465                        if (wp->clen > 0) {
466                                if (nbytes > 0) {
467                                        break;
468                                }
469                                done++;
470                                break;
471                        }
472/*
473 *                      No more data so process the request, (but be sure to close
474 *                      the input file first!).
475 */
476                        if (fd != -1) {
477                                gclose (fd);
478                                fd = -1;
479                        }
480                        websUrlHandlerRequest(wp);
481                        done++;
482                        break;
483
484                case WEBS_POST:
485/*
486 *                      POST without content-length specification
487 *                      If this is a CGI request, write the data to the cgi stdin.
488 *                      socketGets was used to get the data and it strips \n's so
489 *                      add them back in here.
490 */
491
492#ifndef __NO_CGI_BIN
493                        if (wp->flags & WEBS_CGI_REQUEST) {
494                                if (fd == -1) {
495                                        fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY,
496                                                0666);
497                                }
498                                gwrite(fd, text, gstrlen(text));
499                                gwrite(fd, T("\n"), sizeof(char_t));
500                        } else
501#endif
502                        if (wp->query && *wp->query && !(wp->flags & WEBS_POST_DATA)) {
503                                len = gstrlen(wp->query);
504                                wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
505                                        2) * sizeof(char_t));
506                                if (wp->query) {
507                                        wp->query[len++] = '&';
508                                        gstrcpy(&wp->query[len], text);
509                                }
510
511                        } else {
512                                wp->query = bstrdup(B_L, text);
513                        }
514                        wp->flags |= WEBS_POST_DATA;
515                        done++;
516                        break;
517
518                default:
519                        websError(wp, 404, T("Bad state"));
520                        done++;
521                        break;
522                }
523        }
524
525        if (fd != -1) {
526                fd = gclose (fd);
527        }
528
529        if (text) {
530                bfree(B_L, text);
531        }
532}
533
534/******************************************************************************/
535/*
536 *      Get input from the browser. Return TRUE (!0) if the request has been
537 *      handled. Return -1 on errors or if the request has been processed,
538 *      1 if input read, and 0 to instruct the caller to call again for more input.
539 *
540 *      Note: socketRead will Return the number of bytes read if successful. This
541 *      may be less than the requested "bufsize" and may be zero. It returns -1 for
542 *      errors. It returns 0 for EOF. Otherwise it returns the number of bytes
543 *      read. Since this may be zero, callers should use socketEof() to
544 *      distinguish between this and EOF.
545 */
546
547static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes) 
548{
549        char_t  *text;
550        char    buf[WEBS_SOCKET_BUFSIZ+1];
551        int             nbytes, len, clen;
552
553        a_assert(websValid(wp));
554        a_assert(ptext);
555        a_assert(pnbytes);
556
557        *ptext = text = NULL;
558        *pnbytes = 0;
559
560/*
561 *      If this request is a POST with a content length, we know the number
562 *      of bytes to read so we use socketRead().
563 */
564        if (wp->state == WEBS_POST_CLEN) {
565                len = (wp->clen > WEBS_SOCKET_BUFSIZ) ? WEBS_SOCKET_BUFSIZ : wp->clen;
566        } else {
567                len = 0;
568        }
569
570        if (len > 0) {
571
572#ifdef WEBS_SSL_SUPPORT
573                if (wp->flags & WEBS_SECURE) {
574                        nbytes = websSSLRead(wp->wsp, buf, len);
575                } else {
576                        nbytes = socketRead(wp->sid, buf, len);
577                }
578#else
579                nbytes = socketRead(wp->sid, buf, len);
580#endif
581                if (nbytes < 0) {                                               /* Error */
582                        websDone(wp, 0);
583                        return -1;
584
585                }  else if (nbytes == 0) {                              /* EOF or No data available */
586                        return -1;
587
588                } else {                                                                /* Valid data */
589/*
590 *                      Convert to UNICODE if necessary.  First be sure the string
591 *                      is NULL terminated.
592 */
593                        buf[nbytes] = '\0';
594                        if ((text = ballocAscToUni(buf, nbytes)) == NULL) {
595                                websError(wp, 503, T("Insufficient memory"));
596                                return -1;
597                        }
598                }
599
600        } else {
601#ifdef WEBS_SSL_SUPPORT
602                if (wp->flags & WEBS_SECURE) {
603                        nbytes = websSSLGets(wp->wsp, &text);
604                } else {
605                        nbytes = socketGets(wp->sid, &text);
606                }
607#else
608                nbytes = socketGets(wp->sid, &text);
609#endif
610
611                if (nbytes < 0) {
612                        int eof;
613/*
614 *                      Error, EOF or incomplete
615 */
616#ifdef WEBS_SSL_SUPPORT
617                        if (wp->flags & WEBS_SECURE) {
618/*
619 *                              If state is WEBS_BEGIN and the request is secure, a -1 will
620 *                              usually indicate SSL negotiation
621 */
622                                if (wp->state == WEBS_BEGIN) {
623                                        eof = 1;
624                                } else {
625                                        eof = websSSLEof(wp->wsp);
626                                }
627                        } else {
628                                eof = socketEof(wp->sid);
629                        }
630#else
631                        eof = socketEof(wp->sid);
632#endif
633
634                        if (eof) {
635/*
636 *                              If this is a post request without content length, process
637 *                              the request as we now have all the data. Otherwise just
638 *                              close the connection.
639 */
640                                if (wp->state == WEBS_POST) {
641                                        websUrlHandlerRequest(wp);
642                                } else {
643                                        websDone(wp, 0);
644                                }
645                        } else {
646/*
647 *                              If an error occurred and it wasn't an eof, close the connection
648 */
649#ifdef HP_FIX
650                                websDone(wp, 0);
651#endif /*HP_FIX*/
652
653                        }
654/*
655 *                      If state is WEBS_HEADER and the ringq is empty, then this is a
656 *                      simple request with no additional header fields to process and
657 *                      no empty line terminator.
658 */
659/*
660 *                      NOTE: this fix for earlier versions of browsers is troublesome
661 *                      because if we don't receive the entire header in the first pass
662 *                      this code assumes we were only expecting a one line header, which
663 *                      is not necessarily the case. So we weren't processing the whole
664 *                      header and weren't fufilling requests properly.
665 */
666#ifdef UNUSED
667                        if (wp->state == WEBS_HEADER && ringqLen(&wp->header) <= 0) {
668                                websParseRequest(wp);
669                                websUrlHandlerRequest(wp);
670                        }
671#endif
672                        return -1;
673
674                } else if (nbytes == 0) {
675                        if (wp->state == WEBS_HEADER) {
676/*
677 *                              Valid empty line, now finished with header
678 */
679                                websParseRequest(wp);
680                                if (wp->flags & WEBS_POST_REQUEST) {
681                                        if (wp->flags & WEBS_CLEN) {
682                                                wp->state = WEBS_POST_CLEN;
683                                                clen = wp->clen;
684                                        } else {
685                                                wp->state = WEBS_POST;
686                                                clen = 1;
687                                        }
688                                        if (clen > 0) {
689/*
690 *                                              Return 0 to get more data.
691 */
692                                                return 0;
693                                        }
694                                        return 1;
695                                }
696/*
697 *                              We've read the header so go and handle the request
698 */
699                                websUrlHandlerRequest(wp);
700                        }
701                        return -1;
702                }
703        }
704        a_assert(text);
705        a_assert(nbytes > 0);
706        *ptext = text;
707        *pnbytes = nbytes;
708        return 1;
709}
710
711/******************************************************************************/
712/*
713 *      Parse the first line of a HTTP request
714 */
715
716static int websParseFirst(webs_t wp, char_t *text)
717{
718        char_t  *op, *proto, *protoVer, *url, *host, *query, *path, *port, *ext;
719        char_t  *buf;
720        int             testPort;
721
722        a_assert(websValid(wp));
723        a_assert(text && *text);
724
725/*
726 *      Determine the request type: GET, HEAD or POST
727 */
728        op = gstrtok(text, T(" \t"));
729        if (op == NULL || *op == '\0') {
730                websError(wp, 400, T("Bad HTTP request"));
731                return -1;
732        }
733        if (gstrcmp(op, T("GET")) != 0) {
734                if (gstrcmp(op, T("POST")) == 0) {
735                        wp->flags |= WEBS_POST_REQUEST;
736                } else if (gstrcmp(op, T("HEAD")) == 0) {
737                        wp->flags |= WEBS_HEAD_REQUEST;
738                } else {
739                        websError(wp, 400, T("Bad request type"));
740                        return -1;
741                }
742        }
743
744/*
745 *      Store result in the form (CGI) variable store
746 */
747        websSetVar(wp, T("REQUEST_METHOD"), op);
748
749        url = gstrtok(NULL, T(" \t\n"));
750        if (url == NULL || *url == '\0') {
751                websError(wp, 400, T("Bad HTTP request"));
752                return -1;
753        }
754        protoVer = gstrtok(NULL, T(" \t\n"));
755
756/*
757 *      Parse the URL and store all the various URL components. websUrlParse
758 *      returns an allocated buffer in buf which we must free. We support both
759 *      proxied and non-proxied requests. Proxied requests will have http://host/
760 *      at the start of the URL. Non-proxied will just be local path names.
761 */
762        host = path = port = proto = query = ext = NULL;
763        if (websUrlParse(url, &buf, &host, &path, &port, &query, &proto, 
764                        NULL, &ext) < 0) {
765                websError(wp, 400, T("Bad URL format"));
766                return -1;
767        }
768
769        wp->url = bstrdup(B_L, url);
770
771#ifndef __NO_CGI_BIN
772        if (gstrstr(url, CGI_BIN) != NULL) {
773                wp->flags |= WEBS_CGI_REQUEST;
774                if (wp->flags & WEBS_POST_REQUEST) {
775                        wp->cgiStdin = websGetCgiCommName();
776                }
777        }
778#endif
779
780        wp->query = bstrdup(B_L, query);
781        wp->host = bstrdup(B_L, host);
782        wp->path = bstrdup(B_L, path);
783        wp->protocol = bstrdup(B_L, proto);
784        wp->protoVersion = bstrdup(B_L, protoVer);
785       
786        if ((testPort = socketGetPort(wp->listenSid)) >= 0) {
787                wp->port = testPort;
788        } else {
789                wp->port = gatoi(port);
790        }
791
792        if (gstrcmp(ext, T(".asp")) == 0) {
793                wp->flags |= WEBS_ASP;
794        }
795        bfree(B_L, buf);
796
797        websUrlType(url, wp->type, TSZ(wp->type));
798
799#ifdef WEBS_PROXY_SUPPORT
800/*
801 *      Determine if this is a request for local webs data. If it is not a proxied
802 *      request from the browser, we won't see the "http://" or the system name, so
803 *      we assume it must be talking to us directly for local webs data.
804 *      Note: not fully implemented yet.
805 */
806        if (gstrstr(wp->url, T("http://")) == NULL || 
807                ((gstrcmp(wp->host, T("localhost")) == 0 || 
808                        gstrcmp(wp->host, websHost) == 0) && (wp->port == websPort))) {
809                wp->flags |= WEBS_LOCAL_PAGE;
810                if (gstrcmp(wp->path, T("/")) == 0) {
811                        wp->flags |= WEBS_HOME_PAGE;
812                }
813        }
814#endif
815
816        ringqFlush(&wp->header);
817        return 0;
818}
819
820/******************************************************************************/
821/*
822 *      Parse a full request
823 */
824
825#define isgoodchar(s) (gisalnum((s)) || ((s) == '/') || ((s) == '_') || \
826                                                ((s) == '.')  || ((s) == '-') )
827
828static void websParseRequest(webs_t wp)
829{
830        char_t  *authType, *upperKey, *cp, *browser, *lp, *key, *value;
831
832        a_assert(websValid(wp));
833
834/*
835 *      Define default CGI values
836 */
837        websSetVar(wp, T("HTTP_AUTHORIZATION"), T(""));
838
839/*
840 *      Parse the header and create the Http header keyword variables
841 *      We rewrite the header as we go for non-local requests.  NOTE: this
842 *      modifies the header string directly and tokenizes each line with '\0'.
843 */
844        browser = NULL;
845        for (lp = (char_t*) wp->header.servp; lp && *lp; ) {
846                cp = lp;
847                if ((lp = gstrchr(lp, '\n')) != NULL) {
848                        lp++;
849                }
850
851                if ((key = gstrtok(cp, T(": \t\n"))) == NULL) {
852                        continue;
853                }
854
855                if ((value = gstrtok(NULL, T("\n"))) == NULL) {
856                        value = T("");
857                }
858
859                while (gisspace(*value)) {
860                        value++;
861                }
862                strlower(key);
863
864/*
865 *              Create a variable (CGI) for each line in the header
866 */
867                fmtAlloc(&upperKey, (gstrlen(key) + 6), T("HTTP_%s"), key);
868                for (cp = upperKey; *cp; cp++) {
869                        if (*cp == '-')
870                                *cp = '_';
871                }
872                strupper(upperKey);
873                websSetVar(wp, upperKey, value);
874                bfree(B_L, upperKey);
875
876/*
877 *              Track the requesting agent (browser) type
878 */
879                if (gstrcmp(key, T("user-agent")) == 0) {
880                        wp->userAgent = bstrdup(B_L, value);
881
882/*
883 *              Parse the user authorization. ie. password
884 */
885                } else if (gstricmp(key, T("authorization")) == 0) {
886/*
887 *                      Determine the type of Authorization Request
888 */
889                        authType = bstrdup (B_L, value);
890                        a_assert (authType);
891/*                     
892 *                      Truncate authType at the next non-alpha character
893 */
894                        cp = authType;
895                        while (gisalpha(*cp)) {
896                                cp++;
897                        }
898                        *cp = '\0';
899
900                        wp->authType = bstrdup(B_L, authType);
901                        bfree(B_L, authType);
902
903                        if (gstricmp(wp->authType, T("basic")) == 0) {
904                                char_t  userAuth[FNAMESIZE];
905/*
906 *                              The incoming value is username:password (Basic authentication)
907 */
908                                if ((cp = gstrchr(value, ' ')) != NULL) {
909                                        *cp = '\0';
910               /*
911                * bugfix 5/24/02 -- we were leaking the memory pointed to by
912                * wp->authType that was allocated just before the if()
913                * statement that we are currently in. Thanks to Simon Byholm.
914                */
915               bfree(B_L, wp->authType);
916                                        wp->authType = bstrdup(B_L, value);
917                                        websDecode64(userAuth, ++cp, sizeof(userAuth));
918                                } else {
919                                        websDecode64(userAuth, value, sizeof(userAuth));
920                                }
921/*
922 *                              Split userAuth into userid and password
923 */
924                                if ((cp = gstrchr(userAuth, ':')) != NULL) {
925                                        *cp++ = '\0';
926                                }
927                                if (cp) {
928                                        wp->userName = bstrdup(B_L, userAuth);
929                                        wp->password = bstrdup(B_L, cp);
930                                } else {
931                                        wp->userName = bstrdup(B_L, T(""));
932                                        wp->password = bstrdup(B_L, T(""));
933                                }
934/*
935 *                              Set the flags to indicate digest authentication
936 */
937                                wp->flags |= WEBS_AUTH_BASIC;
938                        } else {
939#ifdef DIGEST_ACCESS_SUPPORT
940/*
941 *                              The incoming value is slightly more complicated (Digest)
942 */
943                                char_t *np;             /* pointer to end of tag name */
944                                char_t tp;              /* temporary character holding space */
945                                char_t *vp;             /* pointer to value */
946                                char_t *npv;    /* pointer to end of value, "next" pointer */
947                                char_t tpv;             /* temporary character holding space */
948/*
949 *                              Set the flags to indicate digest authentication
950 */
951                                wp->flags |= WEBS_AUTH_DIGEST;
952/*
953 *                              Move cp to Next word beyond "Digest",
954 *                              vp to first char after '='.
955 */
956                                cp = value;
957                                while (isgoodchar(*cp)) {
958                                        cp++;
959                                }
960                                while (!isgoodchar(*cp)) {
961                                        cp++;
962                                }
963
964/*
965 *                              Find beginning of value
966 */
967                                vp = gstrchr(cp, '=');
968                                while (vp) {
969/*
970 *                                      Zero-terminate tag name
971 */
972                                        np = cp;
973                                        while (isgoodchar(*np)) {
974                                                np++;
975                                        }
976                                        tp = *np;
977                                        *np = 0;
978/*
979 *                                      Advance value pointer to first legit character
980 */
981                                        vp++;
982                                        while (!isgoodchar(*vp)) {
983                                                vp++;
984                                        }
985/*
986 *                                      Zero-terminate value
987 */
988                                        npv = vp;
989                                        while (isgoodchar(*npv)) {
990                                                npv++;
991                                        }
992                                        tpv = *npv;
993                                        *npv = 0;
994/*
995 *                                      Extract the fields
996 */
997                                        if (gstricmp(cp, T("username")) == 0) {
998                                                wp->userName = bstrdup(B_L, vp);
999                                        } else if (gstricmp(cp, T("response")) == 0) {
1000                                                wp->digest = bstrdup(B_L, vp);
1001                                        } else if (gstricmp(cp, T("opaque")) == 0) {
1002                                                wp->opaque = bstrdup(B_L, vp);
1003                                        } else if (gstricmp(cp, T("uri")) == 0) {
1004                                                wp->uri = bstrdup(B_L, vp);
1005                                        } else if (gstricmp(cp, T("realm")) == 0) {
1006                                                wp->realm = bstrdup(B_L, vp);
1007                                        } else if (gstricmp(cp, T("nonce")) == 0) {
1008                                                wp->nonce = bstrdup(B_L, vp);
1009                                        } else if (gstricmp(cp, T("nc")) == 0) {
1010                                                wp->nc = bstrdup(B_L, vp);
1011                                        } else if (gstricmp(cp, T("cnonce")) == 0) {
1012                                                wp->cnonce = bstrdup(B_L, vp);
1013                                        } else if (gstricmp(cp, T("qop")) == 0) {
1014                                                wp->qop = bstrdup(B_L, vp);
1015                                        }
1016/*
1017 *                                      Restore tag name and value zero-terminations
1018 */
1019                                        *np = tp;
1020                                        *npv = tpv;
1021/*
1022 *                                      Advance tag name and value pointers
1023 */
1024                                        cp = npv;
1025                                        while (*cp && isgoodchar(*cp)) {
1026                                                cp++;
1027                                        }
1028                                        while (*cp && !isgoodchar(*cp)) {
1029                                                cp++;
1030                                        }
1031
1032                                        if (*cp) {
1033                                                vp = gstrchr(cp, '=');
1034                                        } else {
1035                                                vp = NULL;
1036                                        }
1037                                }
1038#endif /* DIGEST_ACCESS_SUPPORT */
1039                        } /* if (gstrcmp(wp->authType)) */
1040/*
1041 *              Parse the content length
1042 */
1043                } else if (gstrcmp(key, T("content-length")) == 0) {
1044         /*
1045          * 11 Oct 02 BgP -- The server would crash if an attacker sent a POST
1046          * message with a content-length value <= 0. We assume that anyone
1047          * sending this is malicious, and the POST is read from the socket,
1048          * but it is ignored, and the socket is closed.
1049          */
1050         wp->clen = gatoi(value);
1051         if (wp->clen > 0)
1052         {
1053                           wp->flags |= WEBS_CLEN;                     
1054                           websSetVar(wp, T("CONTENT_LENGTH"), value);
1055         }
1056         else
1057         {
1058            wp->clen = 0;
1059         }
1060
1061/*
1062 *              Parse the content type
1063 */
1064                } else if (gstrcmp(key, T("content-type")) == 0) {
1065                        websSetVar(wp, T("CONTENT_TYPE"), value);
1066
1067#ifdef WEBS_KEEP_ALIVE_SUPPORT
1068                } else if (gstrcmp(key, T("connection")) == 0) {
1069                        strlower(value);
1070                        if (gstrcmp(value, T("keep-alive")) == 0) {
1071                                wp->flags |= WEBS_KEEP_ALIVE;
1072                        }
1073#endif
1074
1075#ifdef WEBS_PROXY_SUPPORT
1076/*
1077 *              This may be useful if you wish to keep a local cache of web pages
1078 *              for proxied requests.
1079 */
1080                } else if (gstrcmp(key, T("pragma")) == 0) {
1081                        char_t  tmp[256];
1082                        gstrncpy(tmp, value, TSZ(tmp));
1083                        strlower(tmp);
1084                        if (gstrstr(tmp, T("no-cache"))) {
1085                                wp->flags |= WEBS_DONT_USE_CACHE;
1086                        }
1087#endif /* WEBS_PROXY_SUPPORT */
1088
1089/*
1090 *              Store the cookie
1091 */
1092                } else if (gstrcmp(key, T("cookie")) == 0) {
1093                        wp->flags |= WEBS_COOKIE;
1094                        wp->cookie = bstrdup(B_L, value);
1095
1096#ifdef WEBS_IF_MODIFIED_SUPPORT
1097/*
1098 *              See if the local page has been modified since the browser last
1099 *              requested this document. If not, just return a 302
1100 */
1101                } else if (gstrcmp(key, T("if-modified-since")) == 0) {
1102                        char_t *cmd;
1103                        time_t tip = 0;
1104
1105                        if ((cp = gstrchr(value, ';')) != NULL) {
1106                                *cp = '\0';
1107                        }
1108
1109                        fmtAlloc(&cmd, 64, T("%s"), value);
1110
1111                        if ((wp->since = dateParse(tip, cmd)) != 0) {
1112                                wp->flags |= WEBS_IF_MODIFIED;
1113                        }
1114
1115                        bfreeSafe(B_L, cmd);
1116#endif /* WEBS_IF_MODIFIED_SUPPORT */
1117                }
1118        }
1119}
1120
1121/******************************************************************************/
1122/*
1123 *      Set the variable (CGI) environment for this request. Create variables
1124 *      for all standard CGI variables. Also decode the query string and create
1125 *      a variable for each name=value pair.
1126 */
1127
1128void websSetEnv(webs_t wp)
1129{
1130        char_t  portBuf[8];
1131        char_t  *keyword, *value, *valCheck, *valNew;
1132
1133        a_assert(websValid(wp));
1134
1135        websSetVar(wp, T("QUERY_STRING"), wp->query);
1136        websSetVar(wp, T("GATEWAY_INTERFACE"), T("CGI/1.1"));
1137        websSetVar(wp, T("SERVER_HOST"), websHost);
1138        websSetVar(wp, T("SERVER_NAME"), websHost);
1139        websSetVar(wp, T("SERVER_URL"), websHostUrl);
1140        websSetVar(wp, T("REMOTE_HOST"), wp->ipaddr);
1141        websSetVar(wp, T("REMOTE_ADDR"), wp->ipaddr);
1142        websSetVar(wp, T("PATH_INFO"), wp->path);
1143        stritoa(websPort, portBuf, sizeof(portBuf));
1144        websSetVar(wp, T("SERVER_PORT"), portBuf);
1145        websSetVar(wp, T("SERVER_ADDR"), websIpaddr);
1146        fmtAlloc(&value, FNAMESIZE, T("%s/%s"), WEBS_NAME, WEBS_VERSION);
1147        websSetVar(wp, T("SERVER_SOFTWARE"), value);
1148        bfreeSafe(B_L, value);
1149        websSetVar(wp, T("SERVER_PROTOCOL"), wp->protoVersion);
1150
1151/*
1152 *      Decode and create an environment query variable for each query keyword.
1153 *      We split into pairs at each '&', then split pairs at the '='.
1154 *      Note: we rely on wp->decodedQuery preserving the decoded values in the
1155 *      symbol table.
1156 */
1157        wp->decodedQuery = bstrdup(B_L, wp->query);
1158        keyword = gstrtok(wp->decodedQuery, T("&"));
1159        while (keyword != NULL) {
1160                if ((value = gstrchr(keyword, '=')) != NULL) {
1161                        *value++ = '\0';
1162                        websDecodeUrl(keyword, keyword, gstrlen(keyword));
1163                        websDecodeUrl(value, value, gstrlen(value));
1164                } else {
1165                        value = T("");
1166                }
1167
1168                if (*keyword) {
1169/*
1170 *                      If keyword has already been set, append the new value to what has
1171 *                      been stored.
1172 */
1173                        if ((valCheck = websGetVar(wp, keyword, NULL)) != 0) {
1174                                fmtAlloc(&valNew, 256, T("%s %s"), valCheck, value);
1175                                websSetVar(wp, keyword, valNew);
1176                                bfreeSafe(B_L, valNew);
1177                        } else {
1178                                websSetVar(wp, keyword, value);
1179                        }
1180                }
1181                keyword = gstrtok(NULL, T("&"));
1182        }
1183
1184#ifdef EMF
1185/*
1186 *      Add GoAhead Embedded Management Framework defines
1187 */
1188        websSetEmfEnvironment(wp);
1189#endif
1190}
1191
1192/******************************************************************************/
1193/*
1194 *      Define a webs (CGI) variable for this connection. Also create in relevant
1195 *      scripting engines. Note: the incoming value may be volatile.
1196 */
1197
1198void websSetVar(webs_t wp, char_t *var, char_t *value)
1199{
1200        value_t          v;
1201
1202        a_assert(websValid(wp));
1203
1204/*
1205 *      value_instring will allocate the string if required.
1206 */
1207        if (value) {
1208                v = valueString(value, VALUE_ALLOCATE);
1209        } else {
1210                v = valueString(T(""), VALUE_ALLOCATE);
1211        }
1212        symEnter(wp->cgiVars, var, v, 0);
1213}
1214
1215/******************************************************************************/
1216/*
1217 *      Return TRUE if a webs variable exists for this connection.
1218 */
1219
1220int websTestVar(webs_t wp, char_t *var)
1221{
1222        sym_t           *sp;
1223
1224        a_assert(websValid(wp));
1225
1226        if (var == NULL || *var == '\0') {
1227                return 0;
1228        }
1229
1230        if ((sp = symLookup(wp->cgiVars, var)) == NULL) {
1231                return 0;
1232        }
1233        return 1;
1234}
1235
1236/******************************************************************************/
1237/*
1238 *      Get a webs variable but return a default value if string not found.
1239 *      Note, defaultGetValue can be NULL to permit testing existence.
1240 */
1241
1242char_t *websGetVar(webs_t wp, char_t *var, char_t *defaultGetValue)
1243{
1244        sym_t   *sp;
1245
1246        a_assert(websValid(wp));
1247        a_assert(var && *var);
1248 
1249        if ((sp = symLookup(wp->cgiVars, var)) != NULL) {
1250                a_assert(sp->content.type == string);
1251                if (sp->content.value.string) {
1252                        return sp->content.value.string;
1253                } else {
1254                        return T("");
1255                }
1256        }
1257        return defaultGetValue;
1258}
1259
1260/******************************************************************************/
1261/*
1262 *      Return TRUE if a webs variable is set to a given value
1263 */
1264
1265int websCompareVar(webs_t wp, char_t *var, char_t *value)
1266{
1267        a_assert(websValid(wp));
1268        a_assert(var && *var);
1269 
1270        if (gstrcmp(value, websGetVar(wp, var, T(" __UNDEF__ "))) == 0) {
1271                return 1;
1272        }
1273        return 0;
1274}
1275
1276/******************************************************************************/
1277/*
1278 *      Cancel the request timeout. Note may be called multiple times.
1279 */
1280
1281void websTimeoutCancel(webs_t wp)
1282{
1283        a_assert(websValid(wp));
1284
1285        if (wp->timeout >= 0) {
1286                emfUnschedCallback(wp->timeout);
1287                wp->timeout = -1;
1288        }
1289}
1290
1291/******************************************************************************/
1292/*
1293 *      Output a HTTP response back to the browser. If redirect is set to a
1294 *      URL, the browser will be sent to this location.
1295 */
1296
1297void websResponse(webs_t wp, int code, char_t *message, char_t *redirect)
1298{
1299        char_t          *date;
1300
1301        a_assert(websValid(wp));
1302
1303/*
1304 *      IE3.0 needs no Keep Alive for some return codes.
1305 */
1306        wp->flags &= ~WEBS_KEEP_ALIVE;
1307
1308/*
1309 *      Only output the header if a header has not already been output.
1310 */
1311        if ( !(wp->flags & WEBS_HEADER_DONE)) {
1312                wp->flags |= WEBS_HEADER_DONE;
1313/*
1314 *              Redirect behaves much better when sent with HTTP/1.0
1315 */
1316                if (redirect != NULL) {
1317                        websWrite(wp, T("HTTP/1.0 %d %s\r\n"), code, websErrorMsg(code));
1318                } else {
1319                        websWrite(wp, T("HTTP/1.1 %d %s\r\n"), code, websErrorMsg(code));
1320                }
1321
1322/*             
1323 *              By license terms the following line of code must not be modified.
1324 */
1325                websWrite(wp, T("Server: %s\r\n"), WEBS_NAME);
1326
1327/*             
1328 *              Timestamp/Date is usually the next to go
1329 */
1330                if ((date = websGetDateString(NULL)) != NULL) {
1331                        websWrite(wp, T("Date: %s\r\n"), date);
1332                        bfree(B_L, date);
1333                }
1334/*
1335 *              If authentication is required, send the auth header info
1336 */
1337                if (code == 401) {
1338                        if (!(wp->flags & WEBS_AUTH_DIGEST)) {
1339                                websWrite(wp, T("WWW-Authenticate: Basic realm=\"%s\"\r\n"), 
1340                                        websGetRealm());
1341#ifdef DIGEST_ACCESS_SUPPORT
1342                        } else {
1343                                char_t *nonce, *opaque;
1344
1345            /* $$$ before... (note commas instead of semicolons...)
1346                                nonce = websCalcNonce(wp),
1347                                opaque = websCalcOpaque(wp),
1348            $$$ after */
1349                                nonce = websCalcNonce(wp);
1350                                opaque = websCalcOpaque(wp); 
1351            /* ...$$$ end */
1352                                websWrite(wp, 
1353                                        T("WWW-Authenticate: Digest realm=\"%s\", domain=\"%s\",")
1354                                        T("qop=\"%s\", nonce=\"%s\", opaque=\"%s\",")
1355                                        T("algorithm=\"%s\", stale=\"%s\"\r\n"), 
1356                                        websGetRealm(),
1357                                        websGetHostUrl(),
1358                                        T("auth"),
1359                                        nonce,
1360                                        opaque, T("MD5"), T("FALSE"));
1361                                bfree(B_L, nonce);
1362                                bfree(B_L, opaque);
1363#endif
1364                        }
1365                }
1366
1367                if (wp->flags & WEBS_KEEP_ALIVE) {
1368                        websWrite(wp, T("Connection: keep-alive\r\n"));
1369                }
1370
1371                websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n"));
1372                websWrite(wp, T("Content-Type: text/html\r\n"));
1373/*
1374 *              We don't do a string length here as the message may be multi-line.
1375 *              Ie. <CR><LF> will count as only one and we will have a content-length
1376 *              that is too short.
1377 *
1378 *              websWrite(wp, T("Content-Length: %s\r\n"), message);
1379 */
1380                if (redirect) {
1381                        websWrite(wp, T("Location: %s\r\n"), redirect);
1382                }
1383                websWrite(wp, T("\r\n"));
1384        }
1385
1386/*
1387 *      If the browser didn't do a HEAD only request, send the message as well.
1388 */
1389        if ((wp->flags & WEBS_HEAD_REQUEST) == 0 && message && *message) {
1390                websWrite(wp, T("%s\r\n"), message);
1391        }
1392        websDone(wp, code);
1393}
1394
1395/******************************************************************************/
1396/*
1397 *      Redirect the user to another webs page
1398 */
1399
1400void websRedirect(webs_t wp, char_t *url)
1401{
1402        char_t  *msgbuf, *urlbuf, *redirectFmt;
1403
1404        a_assert(websValid(wp));
1405        a_assert(url);
1406
1407        websStats.redirects++;
1408        msgbuf = urlbuf = NULL;
1409
1410/*
1411 *      Some browsers require a http://host qualified URL for redirection
1412 */
1413        if (gstrstr(url, T("http://")) == NULL) {
1414                if (*url == '/') {
1415                        url++;
1416                }
1417
1418                redirectFmt = T("http://%s/%s");
1419
1420#ifdef WEBS_SSL_SUPPORT
1421                if (wp->flags & WEBS_SECURE) {
1422                        redirectFmt = T("https://%s/%s");
1423                }
1424#endif
1425
1426                fmtAlloc(&urlbuf, WEBS_MAX_URL + 80, redirectFmt,
1427                        websGetVar(wp, T("HTTP_HOST"),  websHostUrl), url);
1428                url = urlbuf;
1429        }
1430
1431/*
1432 *      Add human readable message for completeness. Should not be required.
1433 */
1434        fmtAlloc(&msgbuf, WEBS_MAX_URL + 80, 
1435                T("<html><head></head><body>\r\n\
1436                This document has moved to a new <a href=\"%s\">location</a>.\r\n\
1437                Please update your documents to reflect the new location.\r\n\
1438                </body></html>\r\n"), url);
1439
1440        websResponse(wp, 302, msgbuf, url);
1441
1442        bfreeSafe(B_L, msgbuf);
1443        bfreeSafe(B_L, urlbuf);
1444}
1445
1446/******************************************************************************/
1447/*     
1448 *      Output an error message and cleanup
1449 */
1450
1451#ifdef qRichErrorPage
1452extern int dmfRichError(webs_t wp, int code, char_t* userMsg);
1453#endif
1454void websError(webs_t wp, int code, char_t *fmt, ...)
1455{
1456        va_list         args;
1457        char_t          *msg, *userMsg, *buf;
1458#ifdef qRichErrorPage
1459   static int reEntry = 0;
1460   int errorOk;
1461#endif
1462
1463        a_assert(websValid(wp));
1464        a_assert(fmt);
1465
1466        websStats.errors++;
1467
1468        va_start(args, fmt);
1469        userMsg = NULL;
1470        fmtValloc(&userMsg, WEBS_BUFSIZE, fmt, args);
1471        va_end(args);
1472
1473#ifdef qRichErrorPage
1474   if (!reEntry)
1475   {
1476      /*
1477       * The dmfRichError function that we're about to call may very well call
1478       * websError() as part of its work. If that happens, we do NOT want to
1479       * get into a never-ending recursive call chain. When we get back here
1480       * in a call from inside dmfRichError(), we check to see if we're
1481       * already trying to call dmfRichError. If we are, we just revert to the
1482       * old non-rich behavior and display a black on white error page.
1483       */
1484
1485      reEntry = 1;
1486      errorOk = dmfRichError(wp, code, userMsg);
1487      reEntry = 0;
1488      if (errorOk)
1489      {
1490         return;
1491      }
1492      /* ...else we need to fall through and execute the simple error page. */
1493   }
1494   /* implicit else... */
1495#endif
1496
1497        msg = T("<html><head><title>Document Error: %s</title></head>\r\n\
1498                <body><h2>Access Error: %s</h2>\r\n\
1499                when trying to obtain <b>%s</b><br><p>%s</p></body></html>\r\n");
1500/*
1501 *      Ensure we have plenty of room
1502 */
1503        buf = NULL;
1504        fmtAlloc(&buf, WEBS_BUFSIZE, msg, websErrorMsg(code), 
1505                websErrorMsg(code), wp->url, userMsg);
1506
1507        websResponse(wp, code, buf, NULL);
1508        bfreeSafe(B_L, buf);
1509        bfreeSafe(B_L, userMsg);
1510}
1511
1512/******************************************************************************/
1513/*
1514 *      Return the error message for a given code
1515 */
1516
1517/*static char_t *websErrorMsg(int code)*/
1518char_t *websErrorMsg(int code)
1519{
1520        websErrorType   *ep;
1521
1522        for (ep = websErrors; ep->code; ep++) {
1523                if (code == ep->code) {
1524                        return ep->msg;
1525                }
1526        }
1527        a_assert(0);
1528        return T("");
1529}
1530
1531/******************************************************************************/
1532/*
1533 *      Do formatted output to the browser. This is the public ASP and form
1534 *      write procedure.
1535 */
1536
1537int websWrite(webs_t wp, char_t *fmt, ...)
1538{
1539        va_list          vargs;
1540        char_t          *buf;
1541        int                      rc;
1542       
1543        a_assert(websValid(wp));
1544
1545        va_start(vargs, fmt);
1546
1547        buf = NULL;
1548        rc = 0;
1549
1550        if (fmtValloc(&buf, WEBS_BUFSIZE, fmt, vargs) >= WEBS_BUFSIZE) {
1551                trace(0, T("webs: websWrite lost data, buffer overflow\n"));
1552        }
1553   
1554        va_end(vargs);
1555        a_assert(buf);
1556        if (buf) {
1557                rc = websWriteBlock(wp, buf, gstrlen(buf));
1558                bfree(B_L, buf);
1559        }
1560        return rc;
1561}
1562
1563/******************************************************************************/
1564/*
1565 *      Write a block of data of length "nChars" to the user's browser. Public
1566 *      write block procedure.  If unicode is turned on this function expects
1567 *      buf to be a unicode string and it converts it to ASCII before writing.
1568 *      See websWriteDataNonBlock to always write binary or ASCII data with no
1569 *      unicode conversion.  This returns the number of char_t's processed.
1570 *      It spins until nChars are flushed to the socket.  For non-blocking
1571 *      behavior, use websWriteDataNonBlock.
1572 */
1573
1574int websWriteBlock(webs_t wp, char_t *buf, int nChars)
1575{
1576        int             len, done;
1577        char    *asciiBuf, *pBuf;
1578
1579        a_assert(wp);
1580        a_assert(websValid(wp));
1581        a_assert(buf);
1582        a_assert(nChars >= 0);
1583
1584        done = len = 0;
1585
1586/*
1587 *      ballocUniToAsc will convert Unicode to strings to Ascii.  If Unicode is
1588 *      not turned on then ballocUniToAsc will not do the conversion.
1589 */
1590        pBuf = asciiBuf = ballocUniToAsc(buf, nChars);
1591
1592        while (nChars > 0) { 
1593#ifdef WEBS_SSL_SUPPORT
1594                if (wp->flags & WEBS_SECURE) {
1595                        if ((len = websSSLWrite(wp->wsp, pBuf, nChars)) < 0) {
1596                                bfree(B_L, asciiBuf);
1597                                return -1;
1598                        }
1599                        websSSLFlush(wp->wsp);
1600                } else {
1601                        if ((len = socketWrite(wp->sid, pBuf, nChars)) < 0) {
1602                                bfree(B_L, asciiBuf);
1603                                return -1;
1604                        }
1605                        socketFlush(wp->sid);
1606                }
1607#else /* ! WEBS_SSL_SUPPORT */
1608                if ((len = socketWrite(wp->sid, pBuf, nChars)) < 0) {
1609                        bfree(B_L, asciiBuf);
1610                        return -1;
1611                }
1612                socketFlush(wp->sid);
1613#endif /* WEBS_SSL_SUPPORT */
1614                nChars -= len;
1615                pBuf += len;
1616                done += len;
1617        }
1618
1619        bfree(B_L, asciiBuf);
1620        return done;
1621}
1622
1623/******************************************************************************/
1624/*
1625 *      Write a block of data of length "nChars" to the user's browser. Same as
1626 *      websWriteBlock except that it expects straight ASCII or binary and does no
1627 *      unicode conversion before writing the data.  If the socket cannot hold all
1628 *      the data, it will return the number of bytes flushed to the socket before
1629 *      it would have blocked.  This returns the number of chars processed or -1
1630 *      if socketWrite fails.
1631 */
1632
1633int websWriteDataNonBlock(webs_t wp, char *buf, int nChars)
1634{
1635        int r;
1636
1637        a_assert(wp);
1638        a_assert(websValid(wp));
1639        a_assert(buf);
1640        a_assert(nChars >= 0);
1641
1642#ifdef WEBS_SSL_SUPPORT
1643        if (wp->flags & WEBS_SECURE) {
1644                r = websSSLWrite(wp->wsp, buf, nChars);
1645                websSSLFlush(wp->wsp);
1646        } else {
1647                r = socketWrite(wp->sid, buf, nChars);
1648                socketFlush(wp->sid);
1649        }
1650#else
1651        r = socketWrite(wp->sid, buf, nChars);
1652        socketFlush(wp->sid);
1653#endif
1654
1655        return r;
1656}
1657
1658/******************************************************************************/
1659/*
1660 *      Decode a URL (or part thereof). Allows insitu decoding.
1661 */
1662
1663void websDecodeUrl(char_t *decoded, char_t *token, int len)
1664{
1665        char_t  *ip,  *op;
1666        int             num, i, c;
1667       
1668        a_assert(decoded);
1669        a_assert(token);
1670
1671        op = decoded;
1672        for (ip = token; *ip && len > 0; ip++, op++) {
1673                if (*ip == '+') {
1674                        *op = ' ';
1675                } else if (*ip == '%' && gisxdigit(ip[1]) && gisxdigit(ip[2])) {
1676
1677/*
1678 *                      Convert %nn to a single character
1679 */
1680                        ip++;
1681                        for (i = 0, num = 0; i < 2; i++, ip++) {
1682                                c = tolower(*ip);
1683                                if (c >= 'a' && c <= 'f') {
1684                                        num = (num * 16) + 10 + c - 'a';
1685                                } else {
1686                                        num = (num * 16) + c - '0';
1687                                }
1688                        }
1689                        *op = (char_t) num;
1690                        ip--;
1691
1692                } else {
1693                        *op = *ip;
1694                }
1695                len--;
1696        }
1697        *op = '\0';
1698}
1699
1700/******************************************************************************/
1701#ifdef WEBS_LOG_SUPPORT
1702/*
1703 *      Output a log message
1704 */
1705
1706static void websLog(webs_t wp, int code)
1707{
1708        char_t  *buf;
1709        char    *abuf;
1710        int             len;
1711#define qAnlLog 1
1712#ifdef qAnlLog
1713   time_t timer;
1714   char_t* newLine = NULL;
1715   char_t* timeStr = NULL;
1716#endif
1717        a_assert(websValid(wp));
1718
1719        buf = NULL;
1720
1721#ifdef qAnlLog
1722   time(&timer);
1723   timeStr = ctime(&timer);
1724   newLine = gstrchr(timeStr, '\n');
1725   if (newLine)
1726   {
1727      *newLine = '\0';
1728   }
1729   fmtAlloc(&buf, WEBS_MAX_URL + 80, T("%s\t%s\t%s\tcode = %d\n"), 
1730      timeStr, wp->ipaddr, wp->url, code);
1731#else
1732        fmtAlloc(&buf, WEBS_MAX_URL + 80, T("%d %s %d %d\n"), time(0), 
1733                wp->url, code, wp->written);
1734#endif
1735        len = gstrlen(buf);
1736        abuf = ballocUniToAsc(buf, len+1);
1737        write(websLogFd, abuf, len);
1738        bfreeSafe(B_L, buf);
1739        bfreeSafe(B_L, abuf);
1740}
1741
1742#endif /* WEBS_LOG_SUPPORT */
1743
1744/******************************************************************************/
1745/*
1746 *      Request timeout. The timeout triggers if we have not read any data from
1747 *      the users browser in the last WEBS_TIMEOUT period. If we have heard from
1748 *      the browser, simply re-issue the timeout.
1749 */
1750
1751void websTimeout(void *arg, int id)
1752{
1753        webs_t          wp;
1754        int                     delay, tm;
1755
1756        wp = (webs_t) arg;
1757        a_assert(websValid(wp));
1758
1759        tm = websGetTimeSinceMark(wp) * 1000;
1760        if (tm >= WEBS_TIMEOUT) {
1761                websStats.timeouts++;
1762                emfUnschedCallback(id);
1763
1764/*
1765 *              Clear the timeout id
1766 */
1767                wp->timeout = -1;
1768                websDone(wp, 404);
1769        } else {
1770                delay = WEBS_TIMEOUT - tm;
1771                a_assert(delay > 0);
1772                emfReschedCallback(id, delay);
1773        }
1774}
1775
1776/******************************************************************************/
1777/*
1778 *      Called when the request is done.
1779 */
1780
1781void websDone(webs_t wp, int code)
1782{
1783        a_assert(websValid(wp));
1784
1785/*
1786 *      Disable socket handler in case keep alive set.
1787 */
1788        socketDeleteHandler(wp->sid);
1789
1790        if (code != 200) {
1791                wp->flags &= ~WEBS_KEEP_ALIVE;
1792        }
1793
1794#ifdef WEBS_PROXY_SUPPORT
1795        if (! (wp->flags & WEBS_LOCAL_PAGE)) {
1796                websStats.activeNetRequests--;
1797        }
1798#endif
1799
1800#ifdef WEBS_LOG_SUPPORT
1801        if (! (wp->flags & WEBS_REQUEST_DONE)) {
1802                websLog(wp, code);
1803        }
1804#endif
1805
1806/*
1807 *      Close any opened document by a handler
1808 */
1809        websPageClose(wp);
1810
1811/*
1812 *      Exit if secure.
1813 */
1814#ifdef WEBS_SSL_SUPPORT
1815        if (wp->flags & WEBS_SECURE) {
1816                websTimeoutCancel(wp);
1817                websSSLFlush(wp->wsp);
1818                socketCloseConnection(wp->sid);
1819                websFree(wp);
1820                return;
1821        }
1822#endif
1823
1824/*
1825 *      If using Keep Alive (HTTP/1.1) we keep the socket open for a period
1826 *      while waiting for another request on the socket.
1827 */
1828        if (wp->flags & WEBS_KEEP_ALIVE) {
1829                if (socketFlush(wp->sid) == 0) {
1830                        wp->state = WEBS_BEGIN;
1831                        wp->flags |= WEBS_REQUEST_DONE;
1832                        if (wp->header.buf) {
1833                                ringqFlush(&wp->header);
1834                        }
1835                        socketCreateHandler(wp->sid, SOCKET_READABLE, websSocketEvent, 
1836                                (int) wp);
1837                        websTimeoutCancel(wp);
1838                        wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout,
1839                                (void *) wp);
1840                        return;
1841                }
1842        } else {
1843                websTimeoutCancel(wp);
1844                socketSetBlock(wp->sid, 1);
1845                socketFlush(wp->sid);
1846                socketCloseConnection(wp->sid);
1847        }
1848        websFree(wp);
1849}
1850
1851/******************************************************************************/
1852/*
1853 *      Allocate a new webs structure
1854 */
1855
1856int websAlloc(int sid)
1857{
1858        webs_t          wp;
1859        int                     wid;
1860
1861/*
1862 *      Allocate a new handle for this connection
1863 */
1864        if ((wid = hAllocEntry((void*) &webs, &websMax,
1865                        sizeof(struct websRec))) < 0) {
1866                return -1;
1867        }
1868        wp = webs[wid];
1869
1870        wp->wid = wid;
1871        wp->sid = sid;
1872        wp->state = WEBS_BEGIN;
1873        wp->docfd = -1;
1874        wp->timeout = -1;
1875        wp->dir = NULL;
1876        wp->authType = NULL;
1877        wp->protocol = NULL;
1878        wp->protoVersion = NULL;
1879        wp->password = NULL;
1880        wp->userName = NULL;
1881#ifdef DIGEST_ACCESS_SUPPORT
1882        wp->realm = NULL;
1883        wp->nonce = NULL;
1884        wp->digest = NULL;
1885        wp->uri = NULL;
1886        wp->opaque = NULL;
1887        wp->nc = NULL;
1888        wp->cnonce = NULL;
1889        wp->qop = NULL;
1890#endif
1891#ifdef WEBS_SSL_SUPPORT
1892        wp->wsp = NULL;
1893#endif
1894
1895        ringqOpen(&wp->header, WEBS_HEADER_BUFINC, WEBS_MAX_HEADER);
1896
1897/*
1898 *      Create storage for the CGI variables. We supply the symbol tables for
1899 *      both the CGI variables and for the global functions. The function table
1900 *      is common to all webs instances (ie. all browsers)
1901 */
1902        wp->cgiVars = symOpen(WEBS_SYM_INIT);
1903
1904        return wid;
1905}
1906
1907/******************************************************************************/
1908/*
1909 *      Free a webs structure
1910 */
1911
1912void websFree(webs_t wp)
1913{
1914        a_assert(websValid(wp));
1915
1916        if (wp->path)
1917                bfree(B_L, wp->path);
1918        if (wp->url)
1919                bfree(B_L, wp->url);
1920        if (wp->host)
1921                bfree(B_L, wp->host);
1922        if (wp->lpath)
1923                bfree(B_L, wp->lpath);
1924        if (wp->query)
1925                bfree(B_L, wp->query);
1926        if (wp->decodedQuery)
1927                bfree(B_L, wp->decodedQuery);
1928        if (wp->authType)
1929                bfree(B_L, wp->authType);
1930        if (wp->password)
1931                bfree(B_L, wp->password);
1932        if (wp->userName)
1933                bfree(B_L, wp->userName);
1934        if (wp->cookie)
1935                bfree(B_L, wp->cookie);
1936        if (wp->userAgent)
1937                bfree(B_L, wp->userAgent);
1938        if (wp->dir)
1939                bfree(B_L, wp->dir);
1940        if (wp->protocol)
1941                bfree(B_L, wp->protocol);
1942        if (wp->protoVersion)
1943                bfree(B_L, wp->protoVersion);
1944        if (wp->cgiStdin)
1945                bfree(B_L, wp->cgiStdin);
1946
1947
1948#ifdef DIGEST_ACCESS_SUPPORT
1949        if (wp->realm)
1950                bfree(B_L, wp->realm);
1951        if (wp->uri)
1952                bfree(B_L, wp->uri);
1953        if (wp->digest)
1954                bfree(B_L, wp->digest);
1955        if (wp->opaque)
1956                bfree(B_L, wp->opaque);
1957        if (wp->nonce)
1958                bfree(B_L, wp->nonce);
1959        if (wp->nc)
1960                bfree(B_L, wp->nc);
1961        if (wp->cnonce)
1962                bfree(B_L, wp->cnonce);
1963        if (wp->qop)
1964                bfree(B_L, wp->qop);
1965#endif
1966#ifdef WEBS_SSL_SUPPORT
1967        websSSLFree(wp->wsp);
1968#endif
1969        symClose(wp->cgiVars);
1970
1971        if (wp->header.buf) {
1972                ringqClose(&wp->header);
1973        }
1974
1975        websMax = hFree((void*) &webs, wp->wid);
1976        bfree(B_L, wp);
1977        a_assert(websMax >= 0);
1978}
1979
1980/******************************************************************************/
1981/*
1982 *      Return the server address
1983 */
1984
1985char_t *websGetHost()
1986{
1987        return websHost;
1988}
1989
1990/******************************************************************************/
1991/*
1992 *      Return the the url to access the server. (ip address)
1993 */
1994
1995char_t *websGetIpaddrUrl()
1996{
1997        return websIpaddrUrl;
1998}
1999
2000/******************************************************************************/
2001/*
2002 *      Return the server address
2003 */
2004
2005char_t *websGetHostUrl()
2006{
2007        return websHostUrl;
2008}
2009
2010/******************************************************************************/
2011/*
2012 *      Return the listen port
2013 */
2014
2015int websGetPort()
2016{
2017        return websPort;
2018}
2019
2020/******************************************************************************/
2021/*
2022 *      Get the number of bytes to write
2023 */
2024
2025int websGetRequestBytes(webs_t wp)
2026{
2027        a_assert(websValid(wp));
2028
2029        return wp->numbytes;
2030}
2031
2032/******************************************************************************/
2033/*
2034 *      Get the directory for this request
2035 */
2036
2037char_t *websGetRequestDir(webs_t wp)
2038{
2039        a_assert(websValid(wp));
2040
2041        if (wp->dir == NULL) {
2042                return T("");
2043        }
2044
2045        return wp->dir;
2046}
2047
2048/******************************************************************************/
2049/*
2050 *      Get the flags for this request
2051 */
2052
2053int websGetRequestFlags(webs_t wp)
2054{
2055        a_assert(websValid(wp));
2056
2057        return wp->flags;
2058}
2059
2060/******************************************************************************/
2061/*
2062 *      Return the IP address
2063 */
2064
2065char_t *websGetRequestIpaddr(webs_t wp)
2066{
2067        a_assert(websValid(wp));
2068
2069        return wp->ipaddr;
2070}
2071
2072/******************************************************************************/
2073/*
2074 *      Set the local path for the request
2075 */
2076
2077char_t *websGetRequestLpath(webs_t wp)
2078{
2079        a_assert(websValid(wp));
2080
2081#ifdef WEBS_PAGE_ROM
2082        return wp->path;
2083#else
2084        return wp->lpath;
2085#endif
2086}
2087
2088/******************************************************************************/
2089/*
2090 *      Get the path for this request
2091 */
2092
2093char_t *websGetRequestPath(webs_t wp)
2094{
2095        a_assert(websValid(wp));
2096
2097        if (wp->path == NULL) {
2098                return T("");
2099        }
2100
2101        return wp->path;
2102}
2103
2104/******************************************************************************/
2105/*
2106 *      Return the password
2107 */
2108
2109char_t *websGetRequestPassword(webs_t wp)
2110{
2111        a_assert(websValid(wp));
2112
2113        return wp->password;
2114}
2115
2116/******************************************************************************/
2117/*
2118 *      Return the request type
2119 */
2120
2121char_t *websGetRequestType(webs_t wp)
2122{
2123        a_assert(websValid(wp));
2124
2125        return wp->type;
2126}
2127
2128/******************************************************************************/
2129/*
2130 *      Return the username
2131 */
2132
2133char_t *websGetRequestUserName(webs_t wp)
2134{
2135        a_assert(websValid(wp));
2136
2137        return wp->userName;
2138}
2139
2140/******************************************************************************/
2141/*
2142 *      Get the number of bytes written
2143 */
2144
2145int websGetRequestWritten(webs_t wp)
2146{
2147        a_assert(websValid(wp));
2148
2149        return wp->written;
2150}
2151
2152/******************************************************************************/
2153/*
2154 *      Set the hostname
2155 */
2156
2157void websSetHost(char_t *host)
2158{
2159        gstrncpy(websHost, host, TSZ(websHost));
2160}
2161
2162/******************************************************************************/
2163/*
2164 *      Set the host URL
2165 */
2166
2167void websSetHostUrl(char_t *url)
2168{
2169        a_assert(url && *url);
2170
2171        bfreeSafe(B_L, websHostUrl);
2172        websHostUrl = gstrdup(B_L, url);
2173}
2174
2175/******************************************************************************/
2176/*
2177 *      Set the IP address
2178 */
2179
2180void websSetIpaddr(char_t *ipaddr)
2181{
2182        a_assert(ipaddr && *ipaddr);
2183
2184        gstrncpy(websIpaddr, ipaddr, TSZ(websIpaddr));
2185}
2186
2187/******************************************************************************/
2188/*
2189 *      Set the number of bytes to write
2190 */
2191
2192void websSetRequestBytes(webs_t wp, int bytes)
2193{
2194        a_assert(websValid(wp));
2195        a_assert(bytes >= 0);
2196
2197        wp->numbytes = bytes;
2198}
2199
2200/******************************************************************************/
2201/*
2202 *      Set the flags for this request
2203 */
2204
2205void websSetRequestFlags(webs_t wp, int flags)
2206{
2207        a_assert(websValid(wp));
2208
2209        wp->flags = flags;
2210}
2211
2212/******************************************************************************/
2213/*
2214 *      Set the local path for the request
2215 */
2216
2217void websSetRequestLpath(webs_t wp, char_t *lpath)
2218{
2219        a_assert(websValid(wp));
2220        a_assert(lpath && *lpath);
2221
2222        if (wp->lpath) {
2223                bfree(B_L, wp->lpath);
2224        }
2225        wp->lpath = bstrdup(B_L, lpath);
2226        websSetVar(wp, T("PATH_TRANSLATED"), wp->lpath);
2227}
2228
2229/******************************************************************************/
2230/*
2231 *      Update the URL path and the directory containing the web page
2232 */
2233
2234void websSetRequestPath(webs_t wp, char_t *dir, char_t *path)
2235{
2236        char_t  *tmp;
2237
2238        a_assert(websValid(wp));
2239
2240        if (dir) { 
2241                tmp = wp->dir;
2242                wp->dir = bstrdup(B_L, dir);
2243                if (tmp) {
2244                        bfree(B_L, tmp);
2245                }
2246        }
2247        if (path) {
2248                tmp = wp->path;
2249                wp->path = bstrdup(B_L, path);
2250                websSetVar(wp, T("PATH_INFO"), wp->path);
2251                if (tmp) {
2252                        bfree(B_L, tmp);
2253                }
2254        }
2255}
2256
2257/******************************************************************************/
2258/*
2259 *      Set the Write handler for this socket
2260 */
2261
2262void websSetRequestSocketHandler(webs_t wp, int mask, void (*fn)(webs_t wp))
2263{
2264        a_assert(websValid(wp));
2265
2266        wp->writeSocket = fn;
2267        socketCreateHandler(wp->sid, SOCKET_WRITABLE, websSocketEvent, (int) wp);
2268}
2269
2270/******************************************************************************/
2271/*
2272 *      Set the number of bytes written
2273 */
2274
2275void websSetRequestWritten(webs_t wp, int written)
2276{
2277        a_assert(websValid(wp));
2278
2279        wp->written = written;
2280}
2281
2282/******************************************************************************/
2283/*
2284 *      Reurn true if the webs handle is valid
2285 */
2286
2287int websValid(webs_t wp)
2288{
2289        int             wid;
2290
2291        for (wid = 0; wid < websMax; wid++) {
2292                if (wp == webs[wid]) {
2293                        return 1;
2294                }
2295        }
2296        return 0;
2297}
2298
2299/******************************************************************************/
2300/*
2301 *      Build an ASCII time string.  If sbuf is NULL we use the current time,
2302 *      else we use the last modified time of sbuf;
2303 */
2304
2305char_t *websGetDateString(websStatType *sbuf)
2306{
2307        char_t* cp, *r;
2308        time_t  now;
2309
2310        if (sbuf == NULL) {
2311                time(&now);
2312        } else {
2313                now = sbuf->mtime;
2314        }
2315        if ((cp = gctime(&now)) != NULL) {
2316                cp[gstrlen(cp) - 1] = '\0';
2317                r = bstrdup(B_L, cp);
2318                return r;
2319        }
2320        return NULL;
2321}
2322
2323/******************************************************************************/
2324/*
2325 *      Mark time. Set a timestamp so that, later, we can return the number of
2326 *      seconds since we made the mark. Note that the mark my not be a
2327 *      "real" time, but rather a relative marker.
2328 */
2329
2330void websSetTimeMark(webs_t wp)
2331{
2332        wp->timestamp = time(0);
2333}
2334
2335/******************************************************************************/
2336/*
2337 *      Get the number of seconds since the last mark.
2338 */
2339
2340static int websGetTimeSinceMark(webs_t wp)
2341{
2342        return time(0) - wp->timestamp;
2343}
2344
2345/******************************************************************************/
2346/*
2347 *      Store the new realm name
2348 */
2349
2350void websSetRealm(char_t *realmName)
2351{
2352        a_assert(realmName);
2353
2354        gstrncpy(websRealm, realmName, TSZ(websRealm));
2355}
2356
2357/******************************************************************************/
2358/*
2359 *      Return the realm name (used for authorization)
2360 */
2361
2362char_t *websGetRealm()
2363{
2364        return websRealm;
2365}
2366
2367
2368#ifdef WEBS_IF_MODIFIED_SUPPORT
2369/******************************************************************************/
2370/*     
2371 *      These functions are intended to closely mirror the syntax for HTTP-date
2372 *      from RFC 2616 (HTTP/1.1 spec).  This code was submitted by Pete Bergstrom.
2373 */
2374
2375/*     
2376 *      RFC1123Date     = wkday "," SP date1 SP time SP "GMT"
2377 *      RFC850Date      = weekday "," SP date2 SP time SP "GMT"
2378 *      ASCTimeDate     = wkday SP date3 SP time SP 4DIGIT
2379 *
2380 *      Each of these functions tries to parse the value and update the index to
2381 *      the point it leaves off parsing.
2382 */
2383
2384typedef enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC } MonthEnumeration;
2385typedef enum { SUN, MON, TUE, WED, THU, FRI, SAT } WeekdayEnumeration;
2386
2387/******************************************************************************/
2388/*     
2389 *      Parse an N-digit value
2390 */
2391
2392static int parseNDIGIT(char_t *buf, int digits, int *index) 
2393{
2394        int tmpIndex, returnValue;
2395
2396        returnValue = 0;
2397
2398        for (tmpIndex = *index; tmpIndex < *index+digits; tmpIndex++) {
2399                if (gisdigit(buf[tmpIndex])) {
2400                        returnValue = returnValue * 10 + (buf[tmpIndex] - T('0'));
2401                }
2402        }
2403        *index = tmpIndex;
2404       
2405        return returnValue;
2406}
2407
2408/******************************************************************************/
2409/*
2410 *      Return an index into the month array
2411 */
2412
2413static int parseMonth(char_t *buf, int *index) 
2414{
2415/*     
2416 *      "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" |
2417 *      "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"
2418 */
2419        int tmpIndex, returnValue;
2420
2421        returnValue = -1;
2422        tmpIndex = *index;
2423
2424        switch (buf[tmpIndex]) {
2425                case 'A':
2426                        switch (buf[tmpIndex+1]) {
2427                                case 'p':
2428                                        returnValue = APR;
2429                                        break;
2430                                case 'u':
2431                                        returnValue = AUG;
2432                                        break;
2433                        }
2434                        break;
2435                case 'D':
2436                        returnValue = DEC;
2437                        break;
2438                case 'F':
2439                        returnValue = FEB;
2440                        break;
2441                case 'J':
2442                        switch (buf[tmpIndex+1]) {
2443                                case 'a':
2444                                        returnValue = JAN;
2445                                        break;
2446                                case 'u':
2447                                        switch (buf[tmpIndex+2]) {
2448                                                case 'l':
2449                                                        returnValue = JUL;
2450                                                        break;
2451                                                case 'n':
2452                                                        returnValue = JUN;
2453                                                        break;
2454                                        }
2455                                        break;
2456                        }
2457                        break;
2458                case 'M':
2459                        switch (buf[tmpIndex+1]) {
2460                                case 'a':
2461                                        switch (buf[tmpIndex+2]) {
2462                                                case 'r':
2463                                                        returnValue = MAR;
2464                                                        break;
2465                                                case 'y':
2466                                                        returnValue = MAY;
2467                                                        break;
2468                                        }
2469                                        break;
2470                        }
2471                        break;
2472                case 'N':
2473                        returnValue = NOV;
2474                        break;
2475                case 'O':
2476                        returnValue = OCT;
2477                        break;
2478                case 'S':
2479                        returnValue = SEP;
2480                        break;
2481        }
2482
2483        if (returnValue >= 0) {
2484                *index += 3;
2485        }
2486
2487        return returnValue;
2488}
2489
2490/******************************************************************************/
2491/*
2492 *      Parse a year value (either 2 or 4 digits)
2493 */
2494
2495static int parseYear(char_t *buf, int *index) 
2496{
2497        int tmpIndex, returnValue;
2498
2499        tmpIndex = *index;
2500        returnValue = parseNDIGIT(buf, 4, &tmpIndex);
2501
2502        if (returnValue >= 0) {
2503                *index = tmpIndex;
2504        } else {
2505                returnValue = parseNDIGIT(buf, 2, &tmpIndex);
2506                if (returnValue >= 0) {
2507/*
2508 *                      Assume that any year earlier than the start of the
2509 *                      epoch for time_t (1970) specifies 20xx
2510 */
2511                        if (returnValue < 70) {
2512                                returnValue += 2000;
2513                        } else {
2514                                returnValue += 1900;
2515                        }
2516
2517                        *index = tmpIndex;
2518                }
2519        }
2520
2521        return returnValue;
2522}
2523
2524/******************************************************************************/
2525/*
2526 *      The formulas used to build these functions are from "Calendrical Calculations",
2527 *      by Nachum Dershowitz, Edward M. Reingold, Cambridge University Press, 1997.
2528 */
2529
2530#include <math.h>
2531
2532const int GregorianEpoch = 1;
2533
2534/******************************************************************************/
2535/*
2536 *  Determine if year is a leap year
2537 */
2538
2539int GregorianLeapYearP(long year) 
2540{
2541        int             result;
2542        long    tmp;
2543       
2544        tmp = year % 400;
2545
2546        if ((year % 4 == 0) &&
2547                (tmp != 100) &&
2548                (tmp != 200) &&
2549                (tmp != 300)) {
2550                result = TRUE;
2551        } else {
2552                result = FALSE;
2553        }
2554
2555        return result;
2556}
2557
2558/******************************************************************************/
2559/*
2560 *  Return the fixed date from the gregorian date
2561 */
2562
2563long FixedFromGregorian(long month, long day, long year) 
2564{
2565        long fixedDate;
2566
2567        fixedDate = (long)(GregorianEpoch - 1 + 365 * (year - 1) + 
2568                floor((year - 1) / 4.0) -
2569                floor((double)(year - 1) / 100.0) + 
2570                floor((double)(year - 1) / 400.0) + 
2571                floor((367.0 * ((double)month) - 362.0) / 12.0));
2572
2573        if (month <= 2) {
2574                fixedDate += 0;
2575        } else if (TRUE == GregorianLeapYearP(year)) {
2576                fixedDate += -1;
2577        } else {
2578                fixedDate += -2;
2579        }
2580
2581        fixedDate += day;
2582
2583        return fixedDate;
2584}
2585
2586/******************************************************************************/
2587/*
2588 *  Return the gregorian year from a fixed date
2589 */
2590
2591long GregorianYearFromFixed(long fixedDate) 
2592{
2593        long result, d0, n400, d1, n100, d2, n4, d3, n1, d4, year;
2594
2595        d0 =    fixedDate - GregorianEpoch;
2596        n400 =  (long)(floor((double)d0 / (double)146097));
2597        d1 =    d0 % 146097;
2598        n100 =  (long)(floor((double)d1 / (double)36524));
2599        d2 =    d1 % 36524;
2600        n4 =    (long)(floor((double)d2 / (double)1461));
2601        d3 =    d2 % 1461;
2602        n1 =    (long)(floor((double)d3 / (double)365));
2603        d4 =    (d3 % 365) + 1;
2604        year =  400 * n400 + 100 * n100 + 4 * n4 + n1;
2605
2606        if ((n100 == 4) || (n1 == 4)) {
2607                result = year;
2608        } else {
2609                result = year + 1;
2610        }
2611
2612        return result;
2613}
2614
2615/******************************************************************************/
2616/*
2617 *      Returns the Gregorian date from a fixed date
2618 *      (not needed for this use, but included for completeness
2619 */
2620
2621#if 0
2622GregorianFromFixed(long fixedDate, long *month, long *day, long *year)
2623{
2624        long priorDays, correction;
2625
2626        *year =                 GregorianYearFromFixed(fixedDate);
2627        priorDays =             fixedDate - FixedFromGregorian(1, 1, *year);
2628
2629        if (fixedDate < FixedFromGregorian(3,1,*year)) {
2630                correction = 0;
2631        } else if (true == GregorianLeapYearP(*year)) {
2632                correction = 1;
2633        } else {
2634                correction = 2;
2635        }
2636
2637        *month = (long)(floor((12.0 * (double)(priorDays + correction) + 373.0) / 367.0));
2638        *day = fixedDate - FixedFromGregorian(*month, 1, *year);
2639}
2640#endif
2641
2642/******************************************************************************/
2643/*
2644 *      Returns the difference between two Gregorian dates
2645 */
2646
2647long GregorianDateDifference(   long month1, long day1, long year1,
2648                                                                long month2, long day2, long year2) 
2649{
2650        return FixedFromGregorian(month2, day2, year2) - 
2651                FixedFromGregorian(month1, day1, year1);
2652}
2653
2654
2655/******************************************************************************/
2656/*
2657 *      Return the number of seconds into the current day
2658 */
2659
2660#define SECONDS_PER_DAY 24*60*60
2661
2662static int parseTime(char_t *buf, int *index) 
2663{
2664/*     
2665 *      Format of buf is - 2DIGIT ":" 2DIGIT ":" 2DIGIT
2666 */
2667        int returnValue, tmpIndex, hourValue, minuteValue, secondValue;
2668
2669        hourValue = minuteValue = secondValue = -1;
2670        returnValue = -1;
2671        tmpIndex = *index;
2672
2673        hourValue = parseNDIGIT(buf, 2, &tmpIndex);
2674
2675        if (hourValue >= 0) {
2676                tmpIndex++;
2677                minuteValue = parseNDIGIT(buf, 2, &tmpIndex);
2678                if (minuteValue >= 0) {
2679                        tmpIndex++;
2680                        secondValue = parseNDIGIT(buf, 2, &tmpIndex);
2681                }
2682        }
2683
2684        if ((hourValue >= 0) &&
2685                (minuteValue >= 0) &&
2686                (secondValue >= 0)) {
2687                returnValue = (((hourValue * 60) + minuteValue) * 60) + secondValue;
2688                *index = tmpIndex;
2689        }
2690
2691        return returnValue;
2692}
2693
2694/******************************************************************************/
2695/*
2696 *      Return the equivalent of time() given a gregorian date
2697 */
2698
2699static time_t dateToTimet(int year, int month, int day) 
2700{
2701        long dayDifference;
2702
2703     /*
2704      * Bug fix by Jeff Reeder (Jun 14, 2002): The 'month' parameter is
2705      * numbered from  0 (Jan == 0), but FixedFromGregorian() takes
2706      * months numbered from 1 (January == 1). We need to add 1
2707      * to the month
2708      */
2709        dayDifference = FixedFromGregorian(month + 1, day, year) - 
2710                FixedFromGregorian(1, 1, 1970);
2711
2712        return dayDifference * SECONDS_PER_DAY;
2713}
2714
2715/******************************************************************************/
2716/*
2717 *      Return the number of seconds between Jan 1, 1970 and the parsed date
2718 *      (corresponds to documentation for time() function)
2719 */
2720
2721static time_t parseDate1or2(char_t *buf, int *index) 
2722{
2723/*     
2724 *      Format of buf is either
2725 *      2DIGIT SP month SP 4DIGIT
2726 *      or
2727 *      2DIGIT "-" month "-" 2DIGIT
2728 */
2729        int             dayValue, monthValue, yearValue, tmpIndex;
2730        time_t  returnValue;
2731
2732        returnValue = (time_t) -1;
2733        tmpIndex = *index;
2734
2735        dayValue = monthValue = yearValue = -1;
2736
2737        if (buf[tmpIndex] == T(',')) {
2738/*
2739 *              Skip over the ", "
2740 */
2741                tmpIndex += 2; 
2742
2743                dayValue = parseNDIGIT(buf, 2, &tmpIndex);
2744                if (dayValue >= 0) {
2745/*
2746 *                      Skip over the space or hyphen
2747 */
2748                        tmpIndex++; 
2749                        monthValue = parseMonth(buf, &tmpIndex);
2750                        if (monthValue >= 0) {
2751/*
2752 *                              Skip over the space or hyphen
2753 */
2754                                tmpIndex++; 
2755                                yearValue = parseYear(buf, &tmpIndex);
2756                        }
2757                }
2758
2759                if ((dayValue >= 0) &&
2760                        (monthValue >= 0) &&
2761                        (yearValue >= 0)) {
2762                        if (yearValue < 1970) {
2763/*                             
2764 *                              Allow for Microsoft IE's year 1601 dates
2765 */
2766                                returnValue = 0; 
2767                        } else {
2768                                returnValue = dateToTimet(yearValue, monthValue, dayValue);
2769                        }
2770                        *index = tmpIndex;
2771                }
2772        }
2773       
2774        return returnValue;
2775}
2776
2777/******************************************************************************/
2778/*
2779 *      Return the number of seconds between Jan 1, 1970 and the parsed date
2780 */
2781
2782static time_t parseDate3Time(char_t *buf, int *index) 
2783{
2784/*
2785 *      Format of buf is month SP ( 2DIGIT | ( SP 1DIGIT ))
2786 */
2787        int             dayValue, monthValue, yearValue, timeValue, tmpIndex;
2788        time_t  returnValue;
2789
2790        returnValue = (time_t) -1;
2791        tmpIndex = *index;
2792
2793        dayValue = monthValue = yearValue = timeValue = -1;
2794
2795        monthValue = parseMonth(buf, &tmpIndex);
2796        if (monthValue >= 0) {
2797/*             
2798 *              Skip over the space
2799 */
2800                tmpIndex++; 
2801                if (buf[tmpIndex] == T(' ')) {
2802/*
2803 *                      Skip over this space too
2804 */
2805                        tmpIndex++; 
2806                        dayValue = parseNDIGIT(buf, 1, &tmpIndex);
2807                } else {
2808                        dayValue = parseNDIGIT(buf, 2, &tmpIndex);
2809                }
2810/*             
2811 *              Now get the time and time SP 4DIGIT
2812 */
2813                timeValue = parseTime(buf, &tmpIndex);
2814                if (timeValue >= 0) {
2815/*                     
2816 *                      Now grab the 4DIGIT year value
2817 */
2818                        yearValue = parseYear(buf, &tmpIndex);
2819                }
2820        }
2821
2822        if ((dayValue >= 0) &&
2823                (monthValue >= 0) &&
2824                (yearValue >= 0)) {
2825                returnValue = dateToTimet(yearValue, monthValue, dayValue);
2826                returnValue += timeValue;
2827                *index = tmpIndex;
2828        }
2829       
2830        return returnValue;
2831}
2832
2833
2834/******************************************************************************/
2835/*
2836 *      Although this looks like a trivial function, I found I was replicating the implementation
2837 *      seven times in the parseWeekday function. In the interests of minimizing code size
2838 *      and redundancy, it is broken out into a separate function. The cost of an extra
2839 *      function call I can live with given that it should only be called once per HTTP request.
2840 */
2841
2842static int bufferIndexIncrementGivenNTest(char_t *buf, int testIndex, char_t testChar, 
2843                                                                                  int foundIncrement, int notfoundIncrement) 
2844{
2845        if (buf[testIndex] == testChar) {
2846                return foundIncrement;
2847        }
2848
2849        return notfoundIncrement;
2850}
2851
2852/******************************************************************************/
2853/*
2854 *      Return an index into a logical weekday array
2855 */
2856
2857static int parseWeekday(char_t *buf, int *index) 
2858{
2859/*     
2860 *      Format of buf is either
2861 *      "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun"
2862 *      or
2863 *      "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday"
2864 */
2865        int tmpIndex, returnValue;
2866
2867        returnValue = -1;
2868        tmpIndex = *index;
2869
2870        switch (buf[tmpIndex]) {
2871                case 'F':
2872                        returnValue = FRI;
2873                        *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'd', sizeof("Friday"), 3);
2874                        break;
2875                case 'M':
2876                        returnValue = MON;
2877                        *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'd', sizeof("Monday"), 3);
2878                        break;
2879                case 'S':
2880                        switch (buf[tmpIndex+1]) {
2881                                case 'a':
2882                                        returnValue = SAT;
2883                                        *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'u', sizeof("Saturday"), 3);
2884                                        break;
2885                                case 'u':
2886                                        returnValue = SUN;
2887                                        *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'd', sizeof("Sunday"), 3);
2888                                        break;
2889                        }
2890                        break;
2891                case 'T':
2892                        switch (buf[tmpIndex+1]) {
2893                                case 'h':
2894                                        returnValue = THU;
2895                                        *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'r', sizeof("Thursday"), 3);
2896                                        break;
2897                                case 'u':
2898                                        returnValue = TUE;
2899                                        *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 's', sizeof("Tuesday"), 3);
2900                                        break;
2901                        }
2902                        break;
2903                case 'W':
2904                        returnValue = WED;
2905                        *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'n', sizeof("Wednesday"), 3);
2906                        break;
2907        }
2908        return returnValue;
2909}
2910
2911/******************************************************************************/
2912/*
2913 *              Parse the date and time string.
2914 */
2915
2916static time_t dateParse(time_t tip, char_t *cmd)
2917{
2918        int index, tmpIndex, weekday, timeValue;
2919        time_t parsedValue, dateValue;
2920
2921        parsedValue = (time_t) 0;
2922        index = timeValue = 0;
2923        weekday = parseWeekday(cmd, &index);
2924
2925        if (weekday >= 0) {
2926                tmpIndex = index;
2927                dateValue = parseDate1or2(cmd, &tmpIndex);
2928                if (dateValue >= 0) {
2929                        index = tmpIndex + 1;
2930/*
2931 *                      One of these two forms is being used
2932 *                      wkday "," SP date1 SP time SP "GMT"
2933 *                      weekday "," SP date2 SP time SP "GMT"
2934 */
2935                        timeValue = parseTime(cmd, &index);
2936                        if (timeValue >= 0) {
2937/*                             
2938 *                              Now match up that "GMT" string for completeness
2939 *                              Compute the final value if there were no problems in the parse
2940 */
2941                                if ((weekday >= 0) &&
2942                                        (dateValue >= 0) &&
2943                                        (timeValue >= 0)) {
2944                                        parsedValue = dateValue + timeValue;
2945                                }
2946                        }
2947                } else {
2948/*
2949 *                      Try the other form - wkday SP date3 SP time SP 4DIGIT
2950 */
2951                        tmpIndex = index;
2952                        parsedValue = parseDate3Time(cmd, &tmpIndex);
2953                }
2954        }
2955
2956        return parsedValue;
2957}
2958
2959#endif /* WEBS_IF_MODIFIED_SUPPORT */
2960
2961/******************************************************************************/
Note: See TracBrowser for help on using the repository browser.