source: rtems/cpukit/httpd/webs.c @ 391b4dd

4.104.115
Last change on this file since 391b4dd was 391b4dd, checked in by Joel Sherrill <joel.sherrill@…>, on 03/11/10 at 19:12:30

2010-03-11 Joel Sherrill <joel.sherrill@…>

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