source: rtems/c/src/libnetworking/rtems_webserver/webs.c @ fd16f5b5

Last change on this file since fd16f5b5 was fd16f5b5, checked in by Joel Sherrill <joel.sherrill@…>, on 06/01/00 at 21:50:37

Patch from Keith Outwater <vac4050@…> to rename
webserver trace() routine to goahead_trace() to avoid name conflicts
with ncurses.

  • Property mode set to 100644
File size: 39.9 KB
Line 
1/*
2 * webs.c -- GoAhead Embedded HTTP webs server
3 *
4 * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
5 *
6 * See the file "license.txt" for usage and redistribution license requirements
7 */
8
9/******************************** Description *********************************/
10
11/*
12 *      This module implements an embedded HTTP/1.1 webs server. It supports
13 *      loadable URL handlers that define the nature of URL processing performed.
14 */
15
16/********************************* Includes ***********************************/
17
18#include        "wsIntrn.h"
19
20/******************************** Global Data *********************************/
21
22websStatsType    websStats;                             /* Web access stats */
23webs_t                  *webs;                                  /* Open connection list head */
24sym_fd_t                 websMime;                              /* Set of mime types */
25int                              websMax;                               /* List size */
26int                              websPort;                              /* Listen port for server */
27char_t                   websHost[64];                  /* Host name for the server */
28char_t                   websIpaddr[64];                /* IP address for the server */
29char_t                  *websHostUrl = NULL;    /* URL to access server */
30
31/*********************************** Locals ***********************************/
32/*
33 *      Standard HTTP error codes
34 */
35
36websErrorType websErrors[] = {
37        { 200, T("Data follows") },
38        { 204, T("No Content") },
39        { 301, T("Redirect") },
40        { 302, T("Redirect") },
41        { 304, T("User local copy") },
42        { 400, T("Page not found") },
43        { 401, T("Password Required") },
44        { 404, T("Site or Page Not Found") },
45        { 405, T("Access Denied") },
46        { 500, T("Web Error") },
47        { 503, T("Site Temporarily Unavailable. Try again") },
48        { 0, NULL }
49};
50
51#if WEBS_LOG_SUPPORT
52static char_t   websLogname[64] = T("log.txt"); /* Log filename */
53static int              websLogFd;                                              /* Log file handle */
54#endif
55
56static int              websListenSock;                                 /* Listen socket */
57
58/**************************** Forward Declarations ****************************/
59
60static int               websAccept(int sid, char *ipaddr, int port);
61static int               websAlloc(int sid);
62static char_t   *websErrorMsg(int code);
63static void      websFree(webs_t wp);
64static void              websFreeVar(sym_t* sp);
65static int               websGetInput(webs_t wp, char_t **ptext, int *nbytes);
66static int               websParseFirst(webs_t wp, char_t *text);
67static void      websParseRequest(webs_t wp);
68static void      websReadEvent(webs_t wp);
69static void              websSocketEvent(int sid, int mask, int data);
70static void      websTimeout(long wp);
71static void      websTimeoutCancel(webs_t wp);
72static void              websMarkTime(webs_t wp);
73static int               websGetTimeSinceMark(webs_t wp);
74
75#if WEBS_LOG_SUPPORT
76static void      websLog(webs_t wp, int code);
77#endif
78#if WEBS_IF_MODIFIED_SUPPORT
79static time_t dateParse(time_t tip, char_t *cmd);
80#endif
81
82/*********************************** Code *************************************/
83/*
84 *      Open the GoAhead WebServer
85 */
86
87int websOpenServer(int port, int retries)
88{
89        websMimeType    *mt;
90
91        a_assert(port > 0);
92        a_assert(retries >= 0);
93
94#if WEBS_PAGE_ROM
95        websRomOpen();
96#endif
97
98/*
99 *      Create a mime type lookup table for quickly determining the content type
100 */
101        websMime = symOpen(256);
102        a_assert(websMime >= 0);
103        for (mt = websMimeList; mt->type; mt++) {
104                symEnter(websMime, mt->ext, valueString(mt->type, 0), 0);
105        }
106
107/*
108 *      Open the URL handler module. The caller should create the required
109 *      URL handlers after calling this function.
110 */
111        if (websUrlHandlerOpen() < 0) {
112                return -1;
113        }
114
115#if WEBS_LOG_SUPPORT
116/*
117 *      Optional request log support
118 */
119        websLogFd = gopen(websLogname, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY,
120                0666);
121        a_assert(websLogFd >= 0);
122#endif
123       
124        return websOpenListen(port, retries);
125}
126
127/******************************************************************************/
128/*
129 *      Close the GoAhead WebServer
130 */
131
132void websCloseServer()
133{
134        webs_t  wp;
135        int             wid;
136
137/*
138 *      Close the listen handle first then all open connections.
139 */
140        websCloseListen();
141
142/*
143 *      Close each open browser connection and free all resources
144 */
145        for (wid = websMax; webs && wid >= 0; wid--) {
146                if ((wp = webs[wid]) == NULL) {
147                        continue;
148                }
149                socketCloseConnection(wp->sid);
150                websFree(wp);
151        }
152
153#if WEBS_LOG_SUPPORT
154        if (websLogFd >= 0) {
155                close(websLogFd);
156                websLogFd = -1;
157        }
158#endif
159
160#if WEBS_PAGE_ROM
161        websRomClose();
162#endif
163        symClose(websMime, NULL);
164        websFormClose();
165        websUrlHandlerClose();
166}
167
168/******************************************************************************/
169/*
170 *      Open the GoAhead WebServer listen port
171 */
172
173int websOpenListen(int port, int retries)
174{
175        int             i, orig;
176
177        a_assert(port > 0);
178        a_assert(retries >= 0);
179
180        orig = port;
181/*
182 *      Open the webs webs listen port. If we fail, try the next port.
183 */
184        for (i = 0; i <= retries; i++) {
185                websListenSock = socketOpenConnection(NULL, port, websAccept, 0);
186                if (websListenSock >= 0) {
187                        break;
188                }
189                port++;
190        }
191        if (i > retries) {
192                error(E_L, E_USER, T("Couldn't open a socket on ports %d - %d"),
193                        orig, port - 1);
194                return -1;
195        }
196        goahead_trace(0, T("webs: Listening for HTTP requests on port %d\n"), port);
197
198/*
199 *      Determine the full URL address to access the home page for this web server
200 */
201        websPort = port;
202        bfreeSafe(B_L, websHostUrl);
203        websHostUrl = NULL;
204        if (port == 80) {
205                websHostUrl = bstrdup(B_L, websHost);
206        } else {
207                gsnprintf(&websHostUrl, WEBS_MAX_URL + 80, T("%s:%d"), websHost, port);
208        }
209        return port;
210}
211
212/******************************************************************************/
213/*
214 *      Close webs listen port
215 */
216
217void websCloseListen()
218{
219        if (websListenSock >= 0) {
220                socketCloseConnection(websListenSock);
221                websListenSock = -1;
222        }
223        bfreeSafe(B_L, websHostUrl);
224        websHostUrl = NULL;
225}
226
227/******************************************************************************/
228/*
229 *      Accept a connection
230 */
231
232static int websAccept(int sid, char *ipaddr, int port)
233{
234        webs_t  wp;
235        int             wid;
236
237        a_assert(ipaddr && *ipaddr);
238        a_assert(sid >= 0);
239        a_assert(port >= 0);
240
241/*
242 *      Allocate a new handle for this accepted connection. This will allocate
243 *      a webs_t structure in the webs[] list
244 */
245        if ((wid = websAlloc(sid)) < 0) {
246                return -1;
247        }
248        wp = webs[wid];
249        a_assert(wp);
250
251        ascToUni(wp->ipaddr, ipaddr, sizeof(wp->ipaddr));
252
253/*
254 *      Check if this is a request from a browser on this system. This is useful
255 *      to know for permitting administrative operations only for local access
256 */
257        if (gstrcmp(wp->ipaddr, T("127.0.0.1")) == 0 ||
258                        gstrcmp(wp->ipaddr, websIpaddr) == 0 ||
259                        gstrcmp(wp->ipaddr, websHost) == 0) {
260                wp->flags |= WEBS_LOCAL_REQUEST;
261        }
262
263/*
264 *      Arrange for websSocketEvent to be called when read data is available
265 */
266        socketCreateHandler(sid, SOCKET_READABLE , websSocketEvent, (int) wp);
267
268/*
269 *      Arrange for a timeout to kill hung requests
270 */
271        wp->timeout = emfCreateTimer(WEBS_TIMEOUT, websTimeout, (long) wp);
272        goahead_trace(5, T("webs: accept request\n"));
273        return 0;
274}
275
276/******************************************************************************/
277/*
278 *      The webs socket handler.  Called in response to I/O. We just pass control
279 *      to the relevant read or write handler. A pointer to the webs structure
280 *      is passed as an (int) in iwp.
281 */
282
283static void websSocketEvent(int sid, int mask, int iwp)
284{
285        webs_t  wp;
286
287        wp = (webs_t) iwp;
288        a_assert(wp);
289
290        if (! websValid(wp)) {
291                return;
292        }
293
294        if (mask & SOCKET_READABLE) {
295                websReadEvent(wp);
296        }
297        if (mask & SOCKET_WRITABLE) {
298                if (wp->writeSocket) {
299                        (*wp->writeSocket)(wp);
300                }
301        }
302}
303
304/******************************************************************************/
305/*
306 *      The webs read handler. This is the primary read event loop. It uses a
307 *      state machine to track progress while parsing the HTTP request.
308 *      Note: we never block as the socket is always in non-blocking mode.
309 */
310
311static void websReadEvent(webs_t wp)
312{
313        char_t  *text;
314        int             rc, nbytes, len, done;
315
316        a_assert(wp);
317        a_assert(websValid(wp));
318
319        websMarkTime(wp);
320
321/*
322 *      Read as many lines as possible. socketGets is called to read the header
323 *      and socketRead is called to read posted data.
324 */
325        text = NULL;
326        for (done = 0; !done; ) {
327                if (text) {
328                        bfree(B_L, text);
329                        text = NULL;
330                }
331
332/*
333 *              Get more input into "text". Returns 0, if more data is needed
334 *              to continue, -1 if finished with the request, or 1 if all
335 *              required data is available for current state.
336 */
337                while ((rc = websGetInput(wp, &text, &nbytes)) == 0) {
338                        ;
339                }
340
341/*
342 *              websGetInput returns -1 if it finishes with the request
343 */
344                if (rc < 0) {
345                        break;
346                }
347
348/*
349 *              This is the state machine for the web server.
350 */
351                switch(wp->state) {
352                case WEBS_BEGIN:
353/*
354 *                      Parse the first line of the Http header
355 */
356                        if (websParseFirst(wp, text) < 0) {
357                                done++;
358                                break;
359                        }
360                        wp->state = WEBS_HEADER;
361                        break;
362               
363                case WEBS_HEADER:
364/*
365 *                      Store more of the HTTP header. As we are doing line reads, we
366 *                      need to separate the lines with '\n'
367 */
368                        if (ringqLen(&wp->header) > 0) {
369                                ringqPutstr(&wp->header, T("\n"));
370                        }
371                        ringqPutstr(&wp->header, text);
372                        break;
373
374                case WEBS_POST_CLEN:
375/*
376 *                      POST request with content specified by a content length
377 */
378                        if (wp->query) {
379                                if (wp->query[0] && !(wp->flags & WEBS_POST_DATA)) {
380/*
381 *                                      Special case where the POST request also had query data
382 *                                      specified in the URL, ie. url?query_data. In this case
383 *                                      the URL query data is separated by a '&' from the posted
384 *                                      query data.
385 */
386                                        len = gstrlen(wp->query);
387                                        wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
388                                                2) * sizeof(char_t));
389                                        wp->query[len++] = '&';
390                                        gstrcpy(&wp->query[len], text);
391
392                                } else {
393/*
394 *                                      The existing query data came from the POST request so just
395 *                                      append it.
396 */
397                                        len = gstrlen(wp->query);
398                                        wp->query = brealloc(B_L, wp->query, (len +     gstrlen(text) +
399                                                1) * sizeof(char_t));
400                                        if (wp->query) {
401                                                gstrcpy(&wp->query[len], text);
402                                        }
403                                }
404
405                        } else {
406                                wp->query = bstrdup(B_L, text);
407                        }
408/*
409 *                      Calculate how much more post data is to be read.
410 */
411                        wp->flags |= WEBS_POST_DATA;
412                        wp->clen -= nbytes;
413                        if (wp->clen > 0) {
414                                if (nbytes > 0) {
415                                        done++;
416                                        break;
417                                }
418                                done++;
419                                break;
420                        }
421/*
422 *                      No more data so process the request
423 */
424                        websUrlHandlerRequest(wp);
425                        done++;
426                        break;
427
428                case WEBS_POST:
429/*
430 *                      POST without content-length specification
431 */
432                        if (wp->query && *wp->query && !(wp->flags & WEBS_POST_DATA)) {
433                                len = gstrlen(wp->query);
434                                wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
435                                        2) * sizeof(char_t));
436                                if (wp->query) {
437                                        wp->query[len++] = '&';
438                                        gstrcpy(&wp->query[len], text);
439                                }
440
441                        } else {
442                                wp->query = bstrdup(B_L, text);
443                        }
444                        wp->flags |= WEBS_POST_DATA;
445                        done++;
446                        break;
447
448                default:
449                        websError(wp, 404, T("Bad state"));
450                        done++;
451                        break;
452                }
453        }
454        if (text) {
455                bfree(B_L, text);
456        }
457}
458
459/******************************************************************************/
460/*
461 *      Get input from the browser. Return TRUE (!0) if the request has been
462 *      handled. Return -1 on errors, 1 if input read, and 0 to instruct the
463 *      caller to call again for more input.
464 *
465 *      Note: socketRead will Return the number of bytes read if successful. This
466 *      may be less than the requested "bufsize" and may be zero. It returns -1 for
467 *      errors. It returns 0 for EOF. Otherwise it returns the number of bytes
468 *      read. Since this may be zero, callers should use socketEof() to
469 *      distinguish between this and EOF.
470 */
471
472static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes)
473{
474        char_t  *text;
475        char    buf[WEBS_SOCKET_BUFSIZ+1];
476        int             nbytes, len, clen;
477
478        a_assert(websValid(wp));
479        a_assert(ptext);
480        a_assert(pnbytes);
481
482        *ptext = text = NULL;
483        *pnbytes = 0;
484
485/*
486 *      If this request is a POST with a content length, we know the number
487 *      of bytes to read so we use socketRead().
488 */
489        if (wp->state == WEBS_POST_CLEN) {
490                len = (wp->clen > WEBS_SOCKET_BUFSIZ) ? WEBS_SOCKET_BUFSIZ : wp->clen;
491        } else {
492                len = 0;
493        }
494
495        if (len > 0) {
496                nbytes = socketRead(wp->sid, buf, len);
497
498                if (nbytes < 0) {                                               /* Error */
499                        websDone(wp, 0);
500                        return -1;
501
502                }  else if (nbytes == 0) {                              /* EOF or No data available */
503                        return -1;
504
505                } else {                                                                /* Valid data */
506/*
507 *                      Convert to UNICODE if necessary.  First be sure the string
508 *                      is NULL terminated.
509 */
510                        buf[nbytes] = '\0';
511                        if ((text = ballocAscToUni(buf)) == NULL) {
512                                websError(wp, 503, T("Insufficient memory"));
513                                return -1;
514                        }
515                }
516
517        } else {
518                nbytes = socketGets(wp->sid, &text);
519
520                if (nbytes < 0) {
521/*
522 *                      Error, EOF or incomplete
523 */
524                        if (socketEof(wp->sid)) {
525/*
526 *                              If this is a post request without content length, process
527 *                              the request as we now have all the data. Otherwise just
528 *                              close the connection.
529 */
530                                if (wp->state == WEBS_POST) {
531                                        websUrlHandlerRequest(wp);
532                                } else {
533                                        websDone(wp, 0);
534                                }
535                        }
536/*
537 *                      If state is WEBS_HEADER and the ringq is empty, then this is a
538 *                      simple request with no additional header fields to process and
539 *                      no empty line terminator.
540 */
541                        if (wp->state == WEBS_HEADER && ringqLen(&wp->header) <= 0) {
542                                websParseRequest(wp);
543                                websUrlHandlerRequest(wp);
544                        }
545                        return -1;
546
547                } else if (nbytes == 0) {
548                        if (wp->state == WEBS_HEADER) {
549/*
550 *                              Valid empty line, now finished with header
551 */
552                                websParseRequest(wp);
553                                if (wp->flags & WEBS_POST_REQUEST) {
554                                        if (wp->flags & WEBS_CLEN) {
555                                                wp->state = WEBS_POST_CLEN;
556                                                clen = wp->clen;
557                                        } else {
558                                                wp->state = WEBS_POST;
559                                                clen = 1;
560                                        }
561                                        if (clen > 0) {
562                                                return 0;                                                       /* Get more data */
563                                        }
564                                        return 1;
565
566                                }
567/*
568 *                              We've read the header so go and handle the request
569 */
570                                websUrlHandlerRequest(wp);
571                        }
572                        return -1;
573
574                }
575        }
576        a_assert(text);
577        a_assert(nbytes > 0);
578        *ptext = text;
579        *pnbytes = nbytes;
580        return 1;
581}
582
583/******************************************************************************/
584/*
585 *      Parse the first line of a HTTP request
586 */
587
588static int websParseFirst(webs_t wp, char_t *text)
589{
590        char_t  *op, *proto, *url, *host, *query, *path, *port, *ext, *buf;
591
592        a_assert(websValid(wp));
593        a_assert(text && *text);
594
595/*
596 *      Determine the request type: GET, HEAD or POST
597 */
598        op = gstrtok(text, T(" \t"));
599        if (op == NULL || *op == '\0') {
600                websError(wp, 400, T("Bad HTTP request"));
601                return -1;
602        }
603        if (gstrcmp(op, T("GET")) != 0) {
604                if (gstrcmp(op, T("POST")) == 0) {
605                        wp->flags |= WEBS_POST_REQUEST;
606                } else if (gstrcmp(op, T("HEAD")) == 0) {
607                        wp->flags |= WEBS_HEAD_REQUEST;
608                } else {
609                        websError(wp, 400, T("Bad request type"));
610                        return -1;
611                }
612        }
613
614/*
615 *      Store result in the form (CGI) variable store
616 */
617        websSetVar(wp, T("REQUEST_METHOD"), op);
618
619        url = gstrtok(NULL, T(" \t\n"));
620        if (url == NULL || *url == '\0') {
621                websError(wp, 400, T("Bad HTTP request"));
622                return -1;
623        }
624
625/*
626 *      Parse the URL and store all the various URL components. websUrlParse
627 *      returns an allocated buffer in buf which we must free. We support both
628 *      proxied and non-proxied requests. Proxied requests will have http://host/
629 *      at the start of the URL. Non-proxied will just be local path names.
630 */
631        host = path = port = proto = query = ext = NULL;
632        if (websUrlParse(url, &buf, &host, &path, &port, &query, &proto,
633                        NULL, &ext) < 0) {
634                websError(wp, 400, T("Bad URL format"));
635                return -1;
636        }
637
638        wp->url = bstrdup(B_L, url);
639        wp->query = bstrdup(B_L, query);
640        wp->host = bstrdup(B_L, host);
641        wp->path = bstrdup(B_L, path);
642        wp->port = gatoi(port);
643        if (gstrcmp(ext, T(".asp")) == 0) {
644                wp->flags |= WEBS_ASP;
645        }
646        bfree(B_L, buf);
647
648        websUrlType(url, wp->type, TSZ(wp->type));
649
650#if WEBS_PROXY_SUPPORT
651/*
652 *      Determine if this is a request for local webs data. If it is not a proxied
653 *      request from the browser, we won't see the "http://" or the system name, so
654 *      we assume it must be talking to us directly for local webs data.
655 *      Note: not fully implemented yet.
656 */
657        if (gstrstr(wp->url, T("http://")) == NULL ||
658                ((gstrcmp(wp->host, T("localhost")) == 0 ||
659                        gstrcmp(wp->host, websHost) == 0) && (wp->port == websPort))) {
660                wp->flags |= WEBS_LOCAL_PAGE;
661                if (gstrcmp(wp->path, T("/")) == 0) {
662                        wp->flags |= WEBS_HOME_PAGE;
663                }
664        }
665#endif
666
667        ringqFlush(&wp->header);
668        return 0;
669}
670
671/******************************************************************************/
672/*
673 *      Parse a full request
674 */
675
676static void websParseRequest(webs_t wp)
677{
678        char_t  *upperKey, *cp, *browser, *lp, *key, *value;
679
680        a_assert(websValid(wp));
681
682/*
683 *      Define default CGI values
684 */
685        websSetVar(wp, T("HTTP_AUTHORIZATION"), T(""));
686
687/*
688 *      Parse the header and create the Http header keyword variables
689 *      We rewrite the header as we go for non-local requests.  NOTE: this
690 *      modifies the header string directly and tokenizes each line with '\0'.
691 */
692        browser = NULL;
693        for (lp = (char_t*) wp->header.servp; lp && *lp; ) {
694                cp = lp;
695                if ((lp = gstrchr(lp, '\n')) != NULL) {
696                        lp++;
697                }
698
699                if ((key = gstrtok(cp, T(": \t\n"))) == NULL) {
700                        continue;
701                }
702
703                if ((value = gstrtok(NULL, T("\n"))) == NULL) {
704                        value = T("");
705                }
706
707                while (gisspace(*value)) {
708                        value++;
709                }
710                strlower(key);
711
712/*
713 *              Create a variable (CGI) for each line in the header
714 */
715                gsnprintf(&upperKey, (gstrlen(key) + 6), T("HTTP_%s"), key);
716                for (cp = upperKey; *cp; cp++) {
717                        if (*cp == '-')
718                                *cp = '_';
719                }
720                strupper(upperKey);
721                websSetVar(wp, upperKey, value);
722                bfree(B_L, upperKey);
723
724/*
725 *              Track the requesting agent (browser) type
726 */
727                if (gstrcmp(key, T("user-agent")) == 0) {
728                        wp->userAgent = bstrdup(B_L, value);
729
730/*
731 *              Parse the user authorization. ie. password
732 */
733                } else if (gstrcmp(key, T("authorization")) == 0) {
734                        char_t  password[FNAMESIZE];
735
736/*
737 *                      The incoming value is password:username
738 */
739                        if ((cp = gstrchr(value, ' ')) != NULL) {
740                                websDecode64(password, ++cp, sizeof(password));
741                        } else {
742                                websDecode64(password, value, sizeof(password));
743                        }
744                        if ((cp = gstrchr(password, ':')) != NULL) {
745                                *cp++ = '\0';
746                        }
747                        if (cp) {
748                                wp->password = bstrdup(B_L, cp);
749                        } else {
750                                wp->password = bstrdup(B_L, T(""));
751                        }
752
753/*
754 *              Parse the content length
755 */
756                } else if (gstrcmp(key, T("content-length")) == 0) {
757                        wp->flags |= WEBS_CLEN;
758                        wp->clen = gatoi(value);
759                        websSetVar(wp, T("CONTENT_LENGTH"), value);
760
761#if WEBS_KEEP_ALIVE_SUPPORT
762                } else if (gstrcmp(key, T("connection")) == 0) {
763                        strlower(value);
764                        if (gstrcmp(value, T("keep-alive")) == 0) {
765                                wp->flags |= WEBS_KEEP_ALIVE;
766                        }
767#endif
768
769#if WEBS_PROXY_SUPPORT
770/*
771 *              This may be useful if you wish to keep a local cache of web pages
772 *              for proxied requests.
773 */
774                } else if (gstrcmp(key, T("pragma")) == 0) {
775                        char_t  tmp[256];
776                        gstrncpy(tmp, value, TSZ(tmp));
777                        strlower(tmp);
778                        if (gstrstr(tmp, T("no-cache"))) {
779                                wp->flags |= WEBS_DONT_USE_CACHE;
780                        }
781#endif
782
783/*
784 *              Store the cookie
785 */
786                } else if (gstrcmp(key, T("cookie")) == 0) {
787                        wp->flags |= WEBS_COOKIE;
788                        wp->cookie = bstrdup(B_L, value);
789
790#if WEBS_IF_MODIFIED_SUPPORT
791/*
792 *              See if the local page has been modified since the browser last
793 *              requested this document. If not, just return a 302
794 */
795                } else if (gstrcmp(key, T("if-modified-since")) == 0) {
796                        char_t *cmd;
797                        time_t tip = 0;
798
799                        if (cp = gstrchr(value, ';')) {
800                                *cp = '\0';
801                        }
802
803                        if (cp = gstrstr(value, T(", "))) {
804                                cp += 2;
805                        }
806
807                        if (gstrstr(cp, T("GMT"))) {
808                                gsnprintf(&cmd, 64, T("clock scan %s -gmt 1"), cp);
809                        } else {
810                                gsnprintf(&cmd, 64, T("clock scan %s"), cp);
811                        }
812                        if (wp->since = dateParse(tip, cmd)) {
813                                wp->flags |= WEBS_IF_MODIFIED;
814                        }
815                        bfreeSafe(B_L, cmd);
816#endif
817                }
818        }
819}
820
821
822#if WEBS_IF_MODIFIED_SUPPORT
823/******************************************************************************/
824/*
825 *              Parse the date and time string.
826 */
827
828static time_t dateParse(time_t tip, char_t *cmd)
829{
830        return (time_t)0;
831}
832#endif
833
834
835/******************************************************************************/
836/*
837 *      Set the variable (CGI) environment for this request. Create variables
838 *      for all standard CGI variables. Also decode the query string and create
839 *      a variable for each name=value pair.
840 */
841
842void websSetEnv(webs_t wp)
843{
844        char_t  portBuf[8];
845        char_t  *keyword, *value;
846
847        a_assert(websValid(wp));
848
849        websSetVar(wp, T("QUERY_STRING"), wp->query);
850        websSetVar(wp, T("GATEWAY_INTERFACE"), T("CGI/1.1"));
851        websSetVar(wp, T("SERVER_HOST"), websHost);
852        websSetVar(wp, T("SERVER_URL"), websHostUrl);
853        websSetVar(wp, T("REMOTE_HOST"), wp->ipaddr);
854        websSetVar(wp, T("REMOTE_ADDR"), wp->ipaddr);
855        websSetVar(wp, T("PATH_INFO"), wp->path);
856        stritoa(websPort, portBuf, sizeof(portBuf));
857        websSetVar(wp, T("SERVER_PORT"), portBuf);
858       
859/*
860 *      Decode and create an environment query variable for each query keyword.
861 *      We split into pairs at each '&', then split pairs at the '='.
862 *      Note: we rely on wp->decodedQuery preserving the decoded values in the
863 *      symbol table.
864 */
865        wp->decodedQuery = bstrdup(B_L, wp->query);
866        keyword = gstrtok(wp->decodedQuery, T("&"));
867        while (keyword != NULL) {
868                if ((value = gstrchr(keyword, '=')) != NULL) {
869                        *value++ = '\0';
870                        websDecodeUrl(keyword, keyword, gstrlen(keyword));
871                        websDecodeUrl(value, value, gstrlen(value));
872
873                } else {
874                        value = T("");
875                }
876
877                if (*keyword) {
878                        websSetVar(wp, keyword, value);
879                }
880                keyword = gstrtok(NULL, T("&"));
881        }
882
883#if EMF
884/*
885 *      Add GoAhead Embedded Management Framework defines
886 */
887        websSetEmfEnvironment(wp);
888#endif
889}
890
891/******************************************************************************/
892/*
893 *      Define a webs (CGI) variable for this connection. Also create in relevant
894 *      scripting engines. Note: the incoming value may be volatile.
895 */
896
897void websSetVar(webs_t wp, char_t *var, char_t *value)
898{
899        value_t          v;
900
901        a_assert(websValid(wp));
902
903/*
904 *      value_instring will allocate the string if required.
905 */
906        if (value) {
907                v = valueString(value, VALUE_ALLOCATE);
908        } else {
909                v = valueString(T(""), VALUE_ALLOCATE);
910        }
911        symEnter(wp->cgiVars, var, v, 0);
912}
913
914/******************************************************************************/
915/*
916 *      Return TRUE if a webs variable exists for this connection.
917 */
918
919int websTestVar(webs_t wp, char_t *var)
920{
921        sym_t           *sp;
922
923        a_assert(websValid(wp));
924
925        if (var == NULL || *var == '\0') {
926                return 0;
927        }
928
929        if ((sp = symLookup(wp->cgiVars, var)) == NULL) {
930                return 0;
931        }
932        return 1;
933}
934
935/******************************************************************************/
936/*
937 *      Get a webs variable but return a default value if string not found.
938 *      Note, defaultGetValue can be NULL to permit testing existance.
939 */
940
941char_t *websGetVar(webs_t wp, char_t *var, char_t *defaultGetValue)
942{
943        sym_t   *sp;
944
945        a_assert(websValid(wp));
946        a_assert(var && *var);
947 
948        if ((sp = symLookup(wp->cgiVars, var)) != NULL) {
949                a_assert(sp->content.type == string);
950                if (sp->content.value.string) {
951                        return sp->content.value.string;
952                } else {
953                        return T("");
954                }
955        }
956        return defaultGetValue;
957}
958
959/******************************************************************************/
960/*
961 *      Cancel the request timeout. Note may be called multiple times.
962 */
963
964static void websTimeoutCancel(webs_t wp)
965{
966        a_assert(websValid(wp));
967
968        if (wp->timeout) {
969                emfDeleteTimer(wp->timeout);
970                wp->timeout = NULL;
971        }
972}
973
974/******************************************************************************/
975/*
976 *      Output a HTTP response back to the browser. If redirect is set to a
977 *      URL, the browser will be sent to this location.
978 */
979
980void websResponse(webs_t wp, int code, char_t *message, char_t *redirect)
981{
982        char_t          *date;
983
984        a_assert(websValid(wp));
985
986/*
987 *      IE3.0 needs no Keep Alive for some return codes.
988 */
989        wp->flags &= ~WEBS_KEEP_ALIVE;
990
991/*
992 *      Only output the header if a header has not already been output.
993 */
994        if ( !(wp->flags & WEBS_HEADER_DONE)) {
995                wp->flags |= WEBS_HEADER_DONE;
996                websWrite(wp, T("HTTP/1.0 %d %s\r\n"), code, websErrorMsg(code));
997
998                /* by license terms the following line of code must
999                 * not be modified.
1000                 */
1001                websWrite(wp, T("Server: GoAhead-Webs\r\n"));
1002
1003                if (wp->flags & WEBS_KEEP_ALIVE) {
1004                        websWrite(wp, T("Connection: keep-alive\r\n"));
1005                }
1006                websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n"));
1007
1008                if ((date = websGetDateString(NULL)) != NULL) {
1009                        websWrite(wp, T("Date: %s\r\n"), date);
1010                        bfree(B_L, date);
1011                }
1012                websWrite(wp, T("Content-Type: text/html\r\n"));
1013
1014/*
1015 *              We don't do a string length here as the message may be multi-line.
1016 *              Ie. <CR><LF> will count as only one and we will have a content-length
1017 *              that is too short.
1018 *
1019 *              websWrite(wp, T("Content-Length: %s\r\n"), message);
1020 */
1021                if (redirect) {
1022                        websWrite(wp, T("Location: %s\r\n"), redirect);
1023                }
1024                websWrite(wp, T("\r\n"));
1025        }
1026
1027        if (message && *message) {
1028                websWrite(wp, T("%s\r\n"), message);
1029        }
1030        websDone(wp, code);
1031}
1032
1033/******************************************************************************/
1034/*
1035 *      Redirect the user to another webs page
1036 */
1037
1038void websRedirect(webs_t wp, char_t *url)
1039{
1040        char_t  *msgbuf, *urlbuf;
1041
1042        a_assert(websValid(wp));
1043        a_assert(url);
1044
1045        websStats.redirects++;
1046        msgbuf = urlbuf = NULL;
1047
1048/*
1049 *      Some browsers require a http://host qualified URL for redirection
1050 */
1051        if (gstrstr(url, T("http://")) == NULL) {
1052                if (*url == '/') {
1053                        url++;
1054                }
1055                gsnprintf(&urlbuf, WEBS_MAX_URL + 80, T("http://%s/%s"),
1056                        websGetVar(wp, T("HTTP_HOST"),  websHostUrl), url);
1057                url = urlbuf;
1058        }
1059
1060/*
1061 *      Add human readable message for completeness. Should not be required.
1062 */
1063        gsnprintf(&msgbuf, WEBS_MAX_URL + 80,
1064                T("<html><head></head><body>\r\n\
1065                This document has moved to a new <a href=\"%s\">location</a>.\r\n\
1066                Please update your documents to reflect the new location.\r\n\
1067                </body></html>\r\n"), url);
1068
1069        websResponse(wp, 302, msgbuf, url);
1070
1071        bfreeSafe(B_L, msgbuf);
1072        bfreeSafe(B_L, urlbuf);
1073}
1074
1075/******************************************************************************/
1076/*     
1077 *      Output an error message and cleanup
1078 */
1079
1080void websError(webs_t wp, int code, char_t *fmt, ...)
1081{
1082        va_list         args;
1083        char_t          *msg, *userMsg, *buf;
1084
1085        a_assert(websValid(wp));
1086        a_assert(fmt);
1087
1088        websStats.errors++;
1089
1090        va_start(args, fmt);
1091        userMsg = NULL;
1092        gvsnprintf(&userMsg, WEBS_BUFSIZE, fmt, args);
1093        va_end(args);
1094
1095        msg = T("<html><head><title>Document Error: %s</title></head>\r\n\
1096                <body><h2>Access Error: %s</h2>\r\n\
1097                when trying to obtain <b>%s</b><br><p>%s</p></body></html>\r\n");
1098/*
1099 *      Ensure we have plenty of room
1100 */
1101        buf = NULL;
1102        gsnprintf(&buf, WEBS_BUFSIZE, msg, websErrorMsg(code),
1103                websErrorMsg(code), wp->url, userMsg);
1104
1105        websResponse(wp, code, buf, NULL);
1106        bfreeSafe(B_L, buf);
1107        bfreeSafe(B_L, userMsg);
1108}
1109
1110/******************************************************************************/
1111/*
1112 *      Return the error message for a given code
1113 */
1114
1115static char_t *websErrorMsg(int code)
1116{
1117        websErrorType*  ep;
1118
1119        for (ep = websErrors; ep->code; ep++) {
1120                if (code == ep->code) {
1121                        return ep->msg;
1122                }
1123        }
1124        a_assert(0);
1125        return T("");
1126}
1127
1128/******************************************************************************/
1129/*
1130 *      Do formatted output to the browser. This is the public ASP and form
1131 *      write procedure.
1132 */
1133
1134int websWrite(webs_t wp, char_t* fmt, ...)
1135{
1136        va_list          vargs;
1137        char_t          *buf;
1138        int                      rc;
1139       
1140        a_assert(websValid(wp));
1141
1142        va_start(vargs, fmt);
1143
1144        buf = NULL;
1145        rc = 0;
1146        if (gvsnprintf(&buf, WEBS_BUFSIZE, fmt, vargs) >= WEBS_BUFSIZE) {
1147                goahead_trace(0, T("webs: websWrite lost data, buffer overflow\n"));
1148        }
1149        va_end(vargs);
1150        a_assert(buf);
1151        if (buf) {
1152                rc = websWriteBlock(wp, buf, gstrlen(buf));
1153                bfree(B_L, buf);
1154        }
1155        return rc;
1156}
1157
1158/******************************************************************************/
1159/*
1160 *      Write a block of data of length "nChars" to the user's browser. Public
1161 *      write block procedure.  If unicode is turned on this function expects
1162 *      buf to be a unicode string and it converts it to ASCII before writing.
1163 *      See websWriteBlockData to always write binary or ASCII data with no
1164 *      unicode conversion.  This returns the number of char_t's processed.
1165 */
1166
1167int websWriteBlock(webs_t wp, char_t *buf, int nChars)
1168{
1169#if ! UNICODE
1170        return websWriteBlockData(wp, buf, nChars);
1171#else
1172        int             r;
1173        char    *charBuf;
1174
1175        a_assert(buf);
1176        a_assert(nChars >= 0);
1177
1178        if ((charBuf = ballocUniToAsc(buf, nChars)) == NULL) {
1179                return -1;
1180        }
1181        r = websWriteBlockData(wp, charBuf, nChars);
1182        bfree(B_L, charBuf);
1183        return r;
1184#endif
1185}
1186
1187/******************************************************************************/
1188/*
1189 *      Write a block of data of length "nChars" to the user's browser. Same as
1190 *      websWriteBlock except that it expects straight ASCII or binary and does no
1191 *      unicode conversion before writing the data.
1192 *      This returns the number of chars processed.
1193 */
1194
1195int websWriteBlockData(webs_t wp, char *buf, int nChars)
1196{
1197        int             len, done;
1198
1199        a_assert(wp);
1200        a_assert(websValid(wp));
1201        a_assert(buf);
1202        a_assert(nChars >= 0);
1203
1204        done = len = 0;
1205        while (nChars > 0) {
1206                if ((len = socketWrite(wp->sid, buf, nChars)) < 0) {
1207                        return -1;
1208                }
1209/*
1210 *              Block in flush if the last write could not take any more data
1211 */
1212                socketFlush(wp->sid, len == 0);
1213                nChars -= len;
1214                buf += len;
1215                done += len;
1216        }
1217        return done;
1218}
1219
1220/******************************************************************************/
1221/*
1222 *      Decode a URL (or part thereof). Allows insitu decoding.
1223 */
1224
1225void websDecodeUrl(char_t *decoded, char_t *token, int len)
1226{
1227        char_t  *ip,  *op;
1228        int             num, i, c;
1229       
1230        a_assert(decoded);
1231        a_assert(token);
1232        num = 0;
1233
1234        op = decoded;
1235        for (ip = token; *ip && len > 0; ip++, op++) {
1236                if (*ip == '+') {
1237                        *op = ' ';
1238                } else if (*ip == '%' && gisxdigit(ip[1]) && gisxdigit(ip[2])) {
1239
1240/*
1241 *                      Convert %nn to a single character
1242 */
1243                        ip++;
1244                        for (i = 0; i < 2; i++, ip++) {
1245                                c = tolower(*ip);
1246                                if (c >= 'a' && c <= 'f') {
1247                                        num = (num * 16) + 10 + c - 'a';
1248                                } else {
1249                                        num = (num * 16) + c - '0';
1250                                }
1251                        }
1252                        *op = (char_t) num;
1253                        ip--;
1254
1255                } else {
1256                        *op = *ip;
1257                }
1258                len--;
1259        }
1260        *op = '\0';
1261}
1262
1263/******************************************************************************/
1264#if WEBS_LOG_SUPPORT
1265/*
1266 *      Output a log message
1267 */
1268
1269static void websLog(webs_t wp, int code)
1270{
1271        char_t  *buf;
1272        char    *abuf;
1273        int             len;
1274
1275        a_assert(websValid(wp));
1276
1277        buf = NULL;
1278        gsnprintf(&buf, WEBS_MAX_URL + 80, T("%d %s %d %d\n"), time(0),
1279                wp->url, code, wp->written);
1280        len = gstrlen(buf);
1281        abuf = ballocUniToAsc(buf, len+1);
1282        write(websLogFd, abuf, len);
1283        bfreeSafe(B_L, buf);
1284        bfreeSafe(B_L, abuf);
1285}
1286
1287#endif /* WEBS_LOG_SUPPORT */
1288
1289/******************************************************************************/
1290/*
1291 *      Request timeout. The timeout triggers if we have not read any data from
1292 *      the users browser in the last WEBS_TIMEOUT period. If we have heard from
1293 *      the browser, simply re-issue the timeout.
1294 */
1295
1296static void websTimeout(long iwp)
1297{
1298        webs_t          wp;
1299        int                     delay, tm;
1300
1301        wp = (webs_t) iwp;
1302        a_assert(websValid(wp));
1303
1304        tm = websGetTimeSinceMark(wp) * 1000;
1305        if (tm >= WEBS_TIMEOUT) {
1306                websStats.timeouts++;
1307                wp->timeout = NULL;
1308                websDone(wp, 404);
1309
1310        } else {
1311                delay = WEBS_TIMEOUT - tm;
1312                a_assert(delay > 0);
1313                wp->timeout = emfCreateTimer(delay, websTimeout, (long) wp);
1314        }
1315}
1316
1317/******************************************************************************/
1318/*
1319 *      Called when the request is done.
1320 */
1321
1322void websDone(webs_t wp, int code)
1323{
1324        a_assert(websValid(wp));
1325
1326/*
1327 *      Disable socket handler in case keep alive set.
1328 */
1329        socketDeleteHandler(wp->sid);
1330
1331        if (code != 200) {
1332                wp->flags &= ~WEBS_KEEP_ALIVE;
1333        }
1334
1335#if WEBS_PROXY_SUPPORT
1336        if (! (wp->flags & WEBS_LOCAL_PAGE)) {
1337                websStats.activeNetRequests--;
1338        }
1339#endif
1340
1341#if WEBS_LOG_SUPPORT
1342        if (! (wp->flags & WEBS_REQUEST_DONE)) {
1343                websLog(wp, code);
1344        }
1345#endif
1346
1347/*
1348 *      Close any opened document by a handler
1349 */
1350        websPageClose(wp);
1351
1352/*
1353 *      If using Keep Alive (HTTP/1.1) we keep the socket open for a period
1354 *      while waiting for another request on the socket.
1355 */
1356        if (wp->flags & WEBS_KEEP_ALIVE) {
1357                if (socketFlush(wp->sid, 0) == 0) {
1358                        wp->state = WEBS_BEGIN;
1359                        wp->flags |= WEBS_REQUEST_DONE;
1360                        if (wp->header.buf) {
1361                                ringqFlush(&wp->header);
1362                        }
1363                        socketCreateHandler(wp->sid, SOCKET_READABLE, websSocketEvent,
1364                                (int) wp);
1365                        websTimeoutCancel(wp);
1366                        wp->timeout = emfCreateTimer(WEBS_TIMEOUT, websTimeout, (long) wp);
1367                        return;
1368                }
1369        } else {
1370                websTimeoutCancel(wp);
1371                socketCloseConnection(wp->sid);
1372        }
1373        websFree(wp);
1374}
1375
1376/******************************************************************************/
1377/*
1378 *      Allocate a new webs structure
1379 */
1380
1381static int websAlloc(int sid)
1382{
1383        webs_t          wp;
1384        int                     wid;
1385
1386/*
1387 *      Allocate a new handle for this connection
1388 */
1389        if ((wid = hAllocEntry((void***) &webs, &websMax,
1390                        sizeof(struct websRec))) < 0) {
1391                return -1;
1392        }
1393        wp = webs[wid];
1394
1395        wp->wid = wid;
1396        wp->sid = sid;
1397        wp->state = WEBS_BEGIN;
1398        wp->docfd = -1;
1399        wp->dir = NULL;
1400
1401        ringqOpen(&wp->header, WEBS_HEADER_BUFINC, WEBS_MAX_HEADER);
1402
1403/*
1404 *      Create storage for the CGI variables. We supply the symbol tables for
1405 *      both the CGI variables and for the global functions. The function table
1406 *      is common to all webs instances (ie. all browsers)
1407 */
1408        wp->cgiVars = symOpen(64);
1409
1410        return wid;
1411}
1412
1413/******************************************************************************/
1414/*
1415 *      Free a webs structure
1416 */
1417
1418static void websFree(webs_t wp)
1419{
1420        a_assert(websValid(wp));
1421
1422        if (wp->path)
1423                bfree(B_L, wp->path);
1424        if (wp->url)
1425                bfree(B_L, wp->url);
1426        if (wp->host)
1427                bfree(B_L, wp->host);
1428        if (wp->lpath)
1429                bfree(B_L, wp->lpath);
1430        if (wp->query)
1431                bfree(B_L, wp->query);
1432        if (wp->decodedQuery)
1433                bfree(B_L, wp->decodedQuery);
1434        if (wp->password)
1435                bfree(B_L, wp->password);
1436        if (wp->userName)
1437                bfree(B_L, wp->userName);
1438        if (wp->cookie)
1439                bfree(B_L, wp->cookie);
1440        if (wp->userAgent)
1441                bfree(B_L, wp->userAgent);
1442        if (wp->dir)
1443                bfree(B_L, wp->dir);
1444
1445        symClose(wp->cgiVars, websFreeVar);
1446
1447        if (wp->header.buf) {
1448                ringqClose(&wp->header);
1449        }
1450
1451        websMax = hFree((void***) &webs, wp->wid);
1452        bfree(B_L, wp);
1453        a_assert(websMax >= 0);
1454}
1455
1456/******************************************************************************/
1457/*
1458 *      Callback from symClose. Free the variable.
1459 */
1460
1461static void websFreeVar(sym_t* sp)
1462{
1463        valueFree(&sp->content);
1464}
1465
1466/******************************************************************************/
1467/*
1468 *      Return the server address
1469 */
1470
1471char_t* websGetHost()
1472{
1473        return websHost;
1474}
1475
1476/******************************************************************************/
1477/*
1478 *      Return the server address
1479 */
1480
1481char_t* websGetHostUrl()
1482{
1483        return websHostUrl;
1484}
1485
1486/******************************************************************************/
1487/*
1488 *      Return the listen port
1489 */
1490
1491int websGetPort()
1492{
1493        return websPort;
1494}
1495
1496/******************************************************************************/
1497/*
1498 *      Get the number of bytes to write
1499 */
1500
1501int websGetRequestBytes(webs_t wp)
1502{
1503        a_assert(websValid(wp));
1504
1505        return wp->numbytes;
1506}
1507
1508/******************************************************************************/
1509/*
1510 *      Get the directory for this request
1511 */
1512
1513char_t *websGetRequestDir(webs_t wp)
1514{
1515        a_assert(websValid(wp));
1516
1517        if (wp->dir == NULL) {
1518                return T("");
1519        }
1520
1521        return wp->dir;
1522}
1523
1524/******************************************************************************/
1525/*
1526 *      Get the flags for this request
1527 */
1528
1529int websGetRequestFlags(webs_t wp)
1530{
1531        a_assert(websValid(wp));
1532
1533        return wp->flags;
1534}
1535
1536/******************************************************************************/
1537/*
1538 *      Return the IP address
1539 */
1540
1541char_t *websGetRequestIpaddr(webs_t wp)
1542{
1543        a_assert(websValid(wp));
1544
1545        return wp->ipaddr;
1546}
1547
1548/******************************************************************************/
1549/*
1550 *      Set the local path for the request
1551 */
1552
1553char_t *websGetRequestLpath(webs_t wp)
1554{
1555        a_assert(websValid(wp));
1556
1557#if WEBS_PAGE_ROM
1558        return wp->path;
1559#else
1560        return wp->lpath;
1561#endif
1562}
1563
1564/******************************************************************************/
1565/*
1566 *      Get the path for this request
1567 */
1568
1569char_t *websGetRequestPath(webs_t wp)
1570{
1571        a_assert(websValid(wp));
1572
1573        if (wp->path == NULL) {
1574                return T("");
1575        }
1576
1577        return wp->path;
1578}
1579
1580/******************************************************************************/
1581/*
1582 *      Return the password
1583 */
1584
1585char_t* websGetRequestPassword(webs_t wp)
1586{
1587        a_assert(websValid(wp));
1588
1589        return wp->password;
1590}
1591
1592/******************************************************************************/
1593/*
1594 *      Return the request type
1595 */
1596
1597char_t* websGetRequestType(webs_t wp)
1598{
1599        a_assert(websValid(wp));
1600
1601        return wp->type;
1602}
1603
1604/******************************************************************************/
1605/*
1606 *      Return the username
1607 */
1608
1609char_t* websGetRequestUserName(webs_t wp)
1610{
1611        a_assert(websValid(wp));
1612
1613        return wp->userName;
1614}
1615
1616/******************************************************************************/
1617/*
1618 *      Get the number of bytes written
1619 */
1620
1621int websGetRequestWritten(webs_t wp)
1622{
1623        a_assert(websValid(wp));
1624
1625        return wp->written;
1626}
1627
1628/******************************************************************************/
1629/*
1630 *      Set the hostname
1631 */
1632
1633void websSetHost(char_t *host)
1634{
1635        gstrncpy(websHost, host, TSZ(websHost));
1636}
1637
1638/******************************************************************************/
1639/*
1640 *      Set the host URL
1641 */
1642
1643void websSetHostUrl(char_t *url)
1644{
1645        a_assert(url && *url);
1646
1647        bfreeSafe(B_L, websHostUrl);
1648        websHostUrl = gstrdup(B_L, url);
1649}
1650
1651/******************************************************************************/
1652/*
1653 *      Set the IP address
1654 */
1655
1656void websSetIpaddr(char_t *ipaddr)
1657{
1658        a_assert(ipaddr && *ipaddr);
1659
1660        gstrncpy(websIpaddr, ipaddr, TSZ(websIpaddr));
1661}
1662
1663/******************************************************************************/
1664/*
1665 *      Set the number of bytes to write
1666 */
1667
1668void websSetRequestBytes(webs_t wp, int bytes)
1669{
1670        a_assert(websValid(wp));
1671        a_assert(bytes >= 0);
1672
1673        wp->numbytes = bytes;
1674}
1675
1676/******************************************************************************/
1677/*
1678 *      Set the flags for this request
1679 */
1680
1681void websSetRequestFlags(webs_t wp, int flags)
1682{
1683        a_assert(websValid(wp));
1684
1685        wp->flags = flags;
1686}
1687
1688/******************************************************************************/
1689/*
1690 *      Set the local path for the request
1691 */
1692
1693void websSetRequestLpath(webs_t wp, char_t *lpath)
1694{
1695        a_assert(websValid(wp));
1696        a_assert(lpath && *lpath);
1697
1698        if (wp->lpath) {
1699                bfree(B_L, wp->lpath);
1700        }
1701        wp->lpath = bstrdup(B_L, lpath);
1702        websSetVar(wp, T("PATH_TRANSLATED"), wp->lpath);
1703}
1704
1705/******************************************************************************/
1706/*
1707 *      Update the URL path and the directory containing the web page
1708 */
1709
1710void websSetRequestPath(webs_t wp, char_t *dir, char_t *path)
1711{
1712        char_t  *tmp;
1713
1714        a_assert(websValid(wp));
1715
1716        if (dir) {
1717                tmp = wp->dir;
1718                wp->dir = bstrdup(B_L, dir);
1719                if (tmp) {
1720                        bfree(B_L, tmp);
1721                }
1722        }
1723        if (path) {
1724                tmp = wp->path;
1725                wp->path = bstrdup(B_L, path);
1726                websSetVar(wp, T("PATH_INFO"), wp->path);
1727                if (tmp) {
1728                        bfree(B_L, tmp);
1729                }
1730        }
1731}
1732
1733/******************************************************************************/
1734/*
1735 *      Set the Write handler for this socket
1736 */
1737
1738void websSetRequestSocketHandler(webs_t wp, int mask, void (*fn)(webs_t wp))
1739{
1740        a_assert(websValid(wp));
1741
1742        wp->writeSocket = fn;
1743        socketCreateHandler(wp->sid, SOCKET_WRITABLE, websSocketEvent, (int) wp);
1744}
1745
1746/******************************************************************************/
1747/*
1748 *      Set the number of bytes written
1749 */
1750
1751void websSetRequestWritten(webs_t wp, int written)
1752{
1753        a_assert(websValid(wp));
1754
1755        wp->written = written;
1756}
1757
1758/******************************************************************************/
1759/*
1760 *      Reurn true if the webs handle is valid
1761 */
1762
1763int websValid(webs_t wp)
1764{
1765        int             wid;
1766
1767        for (wid = 0; wid < websMax; wid++) {
1768                if (wp == webs[wid]) {
1769                        return 1;
1770                }
1771        }
1772        return 0;
1773}
1774
1775/******************************************************************************/
1776/*
1777 *      Close the document handle.
1778 */
1779
1780int websCloseFileHandle(webs_t wp)
1781{
1782        a_assert(websValid(wp));
1783
1784#ifndef WEBS_PAGE_ROM
1785        if (wp->docfd >= 0) {
1786                close(wp->docfd);
1787                wp->docfd = -1;
1788        }
1789#endif
1790
1791        return 0;
1792}
1793
1794/******************************************************************************/
1795/*
1796 *      Build an ASCII time string.  If sbuf is NULL we use the current time,
1797 *      else we use the last modified time of sbuf;
1798 */
1799
1800char_t* websGetDateString(websStatType* sbuf)
1801{
1802        char_t* cp;
1803        char_t* r;
1804        time_t  now;
1805
1806        if (sbuf == NULL) {
1807                time(&now);
1808        } else {
1809                now = sbuf->mtime;
1810        }
1811        if ((cp = gctime(&now)) != NULL) {
1812                cp[gstrlen(cp) - 1] = '\0';
1813                r = bstrdup(B_L, cp);
1814                return r;
1815        }
1816        return NULL;
1817}
1818
1819/******************************************************************************/
1820/*
1821 *      Mark time. Set a timestamp so that, later, we can return the number of
1822 *      seconds since we made the mark. Note that the mark my not be a
1823 *      "real" time, but rather a relative marker.
1824 */
1825
1826static void websMarkTime(webs_t wp)
1827{
1828        wp->timestamp = time(0);
1829}
1830
1831/******************************************************************************/
1832/*
1833 *      Get the number of seconds since the last mark.
1834 */
1835
1836static int websGetTimeSinceMark(webs_t wp)
1837{
1838        return time(0) - wp->timestamp;
1839}
1840
1841/******************************************************************************/
Note: See TracBrowser for help on using the repository browser.