OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rtems-20020807/] [c/] [src/] [libnetworking/] [rtems_webserver/] [webs.c] - Blame information for rev 1779

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.