From a6b4c0df5f74d1238337f41d1d13f4f168ad01f1 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Fri, 1 Sep 2000 10:57:21 +0000 Subject: 2000-08-30 Joel Sherrill * Merged version 2.1 of GoAhead webserver. This update was submitted by Antti P Miettinen . * NOTES, base64.c, ejIntrn.h, emfdb.c, emfdb.h, md5.h, md5c.c, um.c, um.h: New files. * wbase64.c: Removed. * Makefile.am, asp.c, balloc.c, default.c, ej.h, ejlex.c, ejparse.c, form.c, h.c, handler.c, mime.c, misc.c, ringq.c, rom.c, security.c, socket.c, sym.c, uemf.c, uemf.h, url.c, value.c, webcomp.c, webmain.c, webpage.c, webrom.c, webs.c, webs.h, websuemf.c, wsIntrn.h: Modified. --- c/src/exec/libnetworking/ChangeLog | 12 + c/src/libnetworking/ChangeLog | 12 + c/src/libnetworking/rtems_webserver/Makefile.am | 13 +- c/src/libnetworking/rtems_webserver/NOTES | 29 + c/src/libnetworking/rtems_webserver/asp.c | 38 +- c/src/libnetworking/rtems_webserver/balloc.c | 382 ++++-- c/src/libnetworking/rtems_webserver/base64.c | 147 +++ c/src/libnetworking/rtems_webserver/default.c | 79 +- c/src/libnetworking/rtems_webserver/ej.h | 200 +--- c/src/libnetworking/rtems_webserver/ejIntrn.h | 228 ++++ c/src/libnetworking/rtems_webserver/ejlex.c | 160 ++- c/src/libnetworking/rtems_webserver/ejparse.c | 379 ++++-- c/src/libnetworking/rtems_webserver/emfdb.c | 1050 +++++++++++++++++ c/src/libnetworking/rtems_webserver/emfdb.h | 102 ++ c/src/libnetworking/rtems_webserver/form.c | 27 +- c/src/libnetworking/rtems_webserver/h.c | 34 +- c/src/libnetworking/rtems_webserver/handler.c | 109 +- c/src/libnetworking/rtems_webserver/md5.h | 48 + c/src/libnetworking/rtems_webserver/md5c.c | 335 ++++++ c/src/libnetworking/rtems_webserver/mime.c | 2 +- c/src/libnetworking/rtems_webserver/misc.c | 163 ++- c/src/libnetworking/rtems_webserver/ringq.c | 93 +- c/src/libnetworking/rtems_webserver/rom.c | 17 +- c/src/libnetworking/rtems_webserver/security.c | 165 ++- c/src/libnetworking/rtems_webserver/socket.c | 1130 +++++++++--------- c/src/libnetworking/rtems_webserver/sym.c | 133 ++- c/src/libnetworking/rtems_webserver/uemf.c | 129 ++- c/src/libnetworking/rtems_webserver/uemf.h | 831 +++++++++---- c/src/libnetworking/rtems_webserver/um.c | 1415 +++++++++++++++++++++++ c/src/libnetworking/rtems_webserver/um.h | 184 +++ c/src/libnetworking/rtems_webserver/url.c | 23 +- c/src/libnetworking/rtems_webserver/value.c | 1167 ++++++++++++++++++- c/src/libnetworking/rtems_webserver/wbase64.c | 149 --- c/src/libnetworking/rtems_webserver/webcomp.c | 25 +- c/src/libnetworking/rtems_webserver/webmain.c | 116 +- c/src/libnetworking/rtems_webserver/webpage.c | 4 +- c/src/libnetworking/rtems_webserver/webrom.c | 3 +- c/src/libnetworking/rtems_webserver/webs.c | 1397 ++++++++++++++++++---- c/src/libnetworking/rtems_webserver/webs.h | 85 +- c/src/libnetworking/rtems_webserver/websuemf.c | 175 ++- c/src/libnetworking/rtems_webserver/wsIntrn.h | 71 +- cpukit/httpd/Makefile.am | 13 +- cpukit/httpd/NOTES | 29 + cpukit/httpd/asp.c | 38 +- cpukit/httpd/balloc.c | 382 ++++-- cpukit/httpd/base64.c | 147 +++ cpukit/httpd/default.c | 79 +- cpukit/httpd/ej.h | 200 +--- cpukit/httpd/ejIntrn.h | 228 ++++ cpukit/httpd/ejlex.c | 160 ++- cpukit/httpd/ejparse.c | 379 ++++-- cpukit/httpd/emfdb.c | 1050 +++++++++++++++++ cpukit/httpd/emfdb.h | 102 ++ cpukit/httpd/form.c | 27 +- cpukit/httpd/h.c | 34 +- cpukit/httpd/handler.c | 109 +- cpukit/httpd/md5.h | 48 + cpukit/httpd/md5c.c | 335 ++++++ cpukit/httpd/mime.c | 2 +- cpukit/httpd/misc.c | 163 ++- cpukit/httpd/ringq.c | 93 +- cpukit/httpd/rom.c | 17 +- cpukit/httpd/security.c | 165 ++- cpukit/httpd/socket.c | 1130 +++++++++--------- cpukit/httpd/sym.c | 133 ++- cpukit/httpd/uemf.c | 129 ++- cpukit/httpd/uemf.h | 831 +++++++++---- cpukit/httpd/um.c | 1415 +++++++++++++++++++++++ cpukit/httpd/um.h | 184 +++ cpukit/httpd/url.c | 23 +- cpukit/httpd/value.c | 1167 ++++++++++++++++++- cpukit/httpd/wbase64.c | 149 --- cpukit/httpd/webcomp.c | 25 +- cpukit/httpd/webmain.c | 116 +- cpukit/httpd/webpage.c | 4 +- cpukit/httpd/webrom.c | 3 +- cpukit/httpd/webs.c | 1397 ++++++++++++++++++---- cpukit/httpd/webs.h | 85 +- cpukit/httpd/websuemf.c | 175 ++- cpukit/httpd/wsIntrn.h | 71 +- cpukit/libnetworking/ChangeLog | 12 + 81 files changed, 17732 insertions(+), 3978 deletions(-) create mode 100644 c/src/libnetworking/rtems_webserver/NOTES create mode 100644 c/src/libnetworking/rtems_webserver/base64.c create mode 100644 c/src/libnetworking/rtems_webserver/ejIntrn.h create mode 100644 c/src/libnetworking/rtems_webserver/emfdb.c create mode 100644 c/src/libnetworking/rtems_webserver/emfdb.h create mode 100644 c/src/libnetworking/rtems_webserver/md5.h create mode 100644 c/src/libnetworking/rtems_webserver/md5c.c create mode 100644 c/src/libnetworking/rtems_webserver/um.c create mode 100644 c/src/libnetworking/rtems_webserver/um.h delete mode 100644 c/src/libnetworking/rtems_webserver/wbase64.c create mode 100644 cpukit/httpd/NOTES create mode 100644 cpukit/httpd/base64.c create mode 100644 cpukit/httpd/ejIntrn.h create mode 100644 cpukit/httpd/emfdb.c create mode 100644 cpukit/httpd/emfdb.h create mode 100644 cpukit/httpd/md5.h create mode 100644 cpukit/httpd/md5c.c create mode 100644 cpukit/httpd/um.c create mode 100644 cpukit/httpd/um.h delete mode 100644 cpukit/httpd/wbase64.c diff --git a/c/src/exec/libnetworking/ChangeLog b/c/src/exec/libnetworking/ChangeLog index fba8f2452f..61dfc66622 100644 --- a/c/src/exec/libnetworking/ChangeLog +++ b/c/src/exec/libnetworking/ChangeLog @@ -1,3 +1,15 @@ +2000-08-31 Joel Sherrill + + * Merged version 2.1 of GoAhead webserver. This update + was submitted by Antti P Miettinen . + * NOTES, base64.c, ejIntrn.h, emfdb.c, emfdb.h, md5.h, md5c.c, + um.c, um.h: New files. + * wbase64.c: Removed. + * Makefile.am, asp.c, balloc.c, default.c, ej.h, ejlex.c, ejparse.c, + form.c, h.c, handler.c, mime.c, misc.c, ringq.c, rom.c, security.c, + socket.c, sym.c, uemf.c, uemf.h, url.c, value.c, webcomp.c, webmain.c, + webpage.c, webrom.c, webs.c, webs.h, websuemf.c, wsIntrn.h: Modified. + 2000-08-31 Ralf Corsepius * netinet/tcp_input.c: Spelling corrections. diff --git a/c/src/libnetworking/ChangeLog b/c/src/libnetworking/ChangeLog index fba8f2452f..61dfc66622 100644 --- a/c/src/libnetworking/ChangeLog +++ b/c/src/libnetworking/ChangeLog @@ -1,3 +1,15 @@ +2000-08-31 Joel Sherrill + + * Merged version 2.1 of GoAhead webserver. This update + was submitted by Antti P Miettinen . + * NOTES, base64.c, ejIntrn.h, emfdb.c, emfdb.h, md5.h, md5c.c, + um.c, um.h: New files. + * wbase64.c: Removed. + * Makefile.am, asp.c, balloc.c, default.c, ej.h, ejlex.c, ejparse.c, + form.c, h.c, handler.c, mime.c, misc.c, ringq.c, rom.c, security.c, + socket.c, sym.c, uemf.c, uemf.h, url.c, value.c, webcomp.c, webmain.c, + webpage.c, webrom.c, webs.c, webs.h, websuemf.c, wsIntrn.h: Modified. + 2000-08-31 Ralf Corsepius * netinet/tcp_input.c: Spelling corrections. diff --git a/c/src/libnetworking/rtems_webserver/Makefile.am b/c/src/libnetworking/rtems_webserver/Makefile.am index f14ceaaf67..c85580a0c2 100644 --- a/c/src/libnetworking/rtems_webserver/Makefile.am +++ b/c/src/libnetworking/rtems_webserver/Makefile.am @@ -7,14 +7,15 @@ AUTOMAKE_OPTIONS = foreign 1.4 LIBNAME = lib.a LIB = $(ARCH)/$(LIBNAME) -C_FILES = asp.c balloc.c wbase64.c default.c ejlex.c ejparse.c form.c h.c \ - handler.c mime.c misc.c webpage.c ringq.c rom.c security.c socket.c \ - sym.c uemf.c url.c value.c webrom.c webs.c websuemf.c webmain.c +C_FILES = asp.c balloc.c base64.c default.c ejlex.c ejparse.c emfdb.c \ + form.c h.c handler.c md5c.c mime.c misc.c webpage.c ringq.c rom.c \ + security.c socket.c sym.c uemf.c um.c url.c value.c webrom.c webs.c \ + websuemf.c webmain.c C_O_FILES = $(C_FILES:%.c=$(ARCH)/%.o) OBJS = $(C_O_FILES) -H_FILES = ej.h uemf.h webs.h wsIntrn.h +H_FILES = ej.h ejIntrn.h emfdb.h md5.h uemf.h um.h webs.h wsIntrn.h include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg include $(top_srcdir)/../../../automake/lib.am @@ -52,7 +53,7 @@ endif EXTRA_DIST = asp.c balloc.c default.c ej.h ejlex.c ejparse.c form.c h.c \ handler.c mime.c misc.c ringq.c rom.c rtems_webserver.h security.c \ - socket.c sym.c uemf.c uemf.h url.c value.c wbase64.c webcomp.c webmain.c \ - webpage.c webrom.c webs.c webs.h websuemf.c wsIntrn.h + socket.c sym.c uemf.c uemf.h um.h url.c value.c base64.c webcomp.c \ + webmain.c webpage.c webrom.c webs.c webs.h websuemf.c wsIntrn.h include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/libnetworking/rtems_webserver/NOTES b/c/src/libnetworking/rtems_webserver/NOTES new file mode 100644 index 0000000000..6b0e4c55b0 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/NOTES @@ -0,0 +1,29 @@ +# +# $Id$ +# + +Notes on merging GoAhead Webs 2.1. Eventually RTEMS should be supported +in their distributions and this directory removed. + +Applied patch from Antti P Miettinen . + +Obtain the original distribution from http://www.goahead.com for +documentation. + +Tailoring +========= +socket.c is RTEMS specific + +Renames +======= + +Distributed as This Directory +============== ================ +base64.c wbase64.c +page.c webpage.c + +RTEMS Specific Additions +======================== +webmain.c +rtems_webserver.h + diff --git a/c/src/libnetworking/rtems_webserver/asp.c b/c/src/libnetworking/rtems_webserver/asp.c index 4611a8f422..9b37523e02 100644 --- a/c/src/libnetworking/rtems_webserver/asp.c +++ b/c/src/libnetworking/rtems_webserver/asp.c @@ -1,7 +1,7 @@ /* * asp.c -- Active Server Page Support * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -20,11 +20,12 @@ /********************************** Locals ************************************/ -static sym_fd_t websAspFunctions = -1; /* Symbol table of functions */ +static sym_fd_t websAspFunctions = -1; /* Symbol table of functions */ +static int aspOpenCount = 0; /* count of apps using this module */ /***************************** Forward Declarations ***************************/ -static char_t *strtokcmp(char_t* s1, char_t* s2); +static char_t *strtokcmp(char_t *s1, char_t *s2); static char_t *skipWhite(char_t *s); /************************************* Code ***********************************/ @@ -34,15 +35,17 @@ static char_t *skipWhite(char_t *s); int websAspOpen() { + if (++aspOpenCount == 1) { /* * Create the table for ASP functions */ - websAspFunctions = symOpen(128); + websAspFunctions = symOpen(WEBS_SYM_INIT * 2); /* * Create standard ASP commands */ - websAspDefine(T("write"), websAspWrite); + websAspDefine(T("write"), websAspWrite); + } return 0; } @@ -53,8 +56,11 @@ int websAspOpen() void websAspClose() { - if (websAspFunctions != -1) { - symClose(websAspFunctions, NULL); + if (--aspOpenCount <= 0) { + if (websAspFunctions != -1) { + symClose(websAspFunctions); + websAspFunctions = -1; + } } } @@ -65,7 +71,7 @@ void websAspClose() * documents, it is better to make them plain HTML files rather than ASPs. */ -int websAspRequest(webs_t wp, char_t* lpath) +int websAspRequest(webs_t wp, char_t *lpath) { websStatType sbuf; char *rbuf; @@ -84,7 +90,7 @@ int websAspRequest(webs_t wp, char_t* lpath) path = websGetRequestPath(wp); /* - * Create Ejscript instance incase it is needed + * Create Ejscript instance in case it is needed */ ejid = ejOpenEngine(wp->cgiVars, websAspFunctions); if (ejid < 0) { @@ -112,12 +118,12 @@ int websAspRequest(webs_t wp, char_t* lpath) websError(wp, 200, T("Cant read %s"), lpath); goto done; } - websCloseFileHandle(wp); + websPageClose(wp); /* * Convert to UNICODE if necessary. */ - if ((buf = ballocAscToUni(rbuf)) == NULL) { + if ((buf = ballocAscToUni(rbuf, len)) == NULL) { websError(wp, 200, T("Can't get memory")); goto done; } @@ -220,7 +226,7 @@ int websAspRequest(webs_t wp, char_t* lpath) */ done: if (websValid(wp)) { - websCloseFileHandle(wp); + websPageClose(wp); if (ejid >= 0) { ejCloseEngine(ejid); } @@ -252,9 +258,9 @@ int websAspWrite(int ejid, webs_t wp, int argc, char_t **argv) int i; a_assert(websValid(wp)); - a_assert(argv); - + for (i = 0; i < argc; ) { + a_assert(argv); if (websWriteBlock(wp, argv[i], gstrlen(argv[i])) < 0) { return -1; } @@ -273,7 +279,7 @@ int websAspWrite(int ejid, webs_t wp, int argc, char_t **argv) * Return a pointer to the location in s1 after s2 ends. */ -static char_t* strtokcmp(char_t* s1, char_t* s2) +static char_t *strtokcmp(char_t *s1, char_t *s2) { int len; @@ -310,4 +316,4 @@ static char_t *skipWhite(char_t *s) return s; } -/******************************************************************************/ \ No newline at end of file +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/balloc.c b/c/src/libnetworking/rtems_webserver/balloc.c index f2dae27867..d535a6605c 100644 --- a/c/src/libnetworking/rtems_webserver/balloc.c +++ b/c/src/libnetworking/rtems_webserver/balloc.c @@ -1,7 +1,7 @@ /* * balloc.c -- Block allocation module * - * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -11,7 +11,7 @@ /* * This module implements a very fast block allocation scheme suitable for * ROMed environments. It maintains block class queues for rapid allocation - * and minimal fragmentation. This modules does not coalesce blocks. The + * and minimal fragmentation. This module does not coalesce blocks. The * storage space may be populated statically or via the traditional malloc * mechanisms. Large blocks greater than the maximum class size may be * allocated from the O/S or run-time system via malloc. To permit the use @@ -38,14 +38,6 @@ #if !NO_BALLOC /********************************* Defines ************************************/ -typedef struct { - union { - void *next; /* Pointer to next in q */ - int size; /* Actual requested size */ - } u; - int flags; /* Per block allocation flags */ -} bType; - /* * Define B_STATS if you wish to track memory block and stack usage */ @@ -63,7 +55,9 @@ typedef struct { char_t file[FNAMESIZE]; long allocated; /* Bytes currently allocated */ long count; /* Current block count */ - long allocs; /* Count of alloc attempts */ + long times; /* Count of alloc attempts */ + long largest; /* largest allocated here */ + int q; } bStatsFileType; /* @@ -77,15 +71,26 @@ typedef struct { static bStatsType bStats[B_MAX_CLASS]; /* Per class stats */ static bStatsFileType bStatsFiles[B_MAX_FILES];/* Per file stats */ static bStatsBlkType bStatsBlks[B_MAX_BLOCKS];/* Per block stats */ -static int bStatsBlksMax; /* Max block entry */ -static int bStatsFilesMax; /* Max file entry */ -static int bStatsMemInUse; /* Memory currently in use */ -static int bStatsMemMax; /* Max memory ever used */ +static int bStatsBlksMax = 0; /* Max block entry */ +static int bStatsFilesMax = 0; /* Max file entry */ +static int bStatsMemInUse = 0; /* Memory currently in use */ +static int bStatsBallocInUse = 0; /* Memory currently balloced */ +static int bStatsMemMax = 0; /* Max memory ever used */ +static int bStatsBallocMax = 0; /* Max memory ever balloced */ static void *bStackMin = (void*) -1;/* Miniumum stack position */ static void *bStackStart; /* Starting stack position */ -static int bStatsMemMalloc; /* Malloced memory */ +static int bStatsMemMalloc = 0; /* Malloced memory */ #endif /* B_STATS */ +/* + * ROUNDUP4(size) returns the next higher integer value of size that is + * divisible by 4, or the value of size if size is divisible by 4. + * ROUNDUP4() is used in aligning memory allocations on 4-byte boundaries. + * + * Note: ROUNDUP4() is only required on some operating systems (IRIX). + */ + +#define ROUNDUP4(size) ((size) % 4) ? (size) + (4 - ((size) % 4)) : (size) /********************************** Locals ************************************/ /* @@ -98,6 +103,7 @@ static char *bFreeNext; /* Pointer to next free mem */ static int bFreeSize; /* Size of free memory */ static int bFreeLeft; /* Size of free left for use */ static int bFlags = B_USE_MALLOC; /* Default to auto-malloc */ +static int bopenCount = 0; /* Num tasks using balloc */ /*************************** Forward Declarations *****************************/ @@ -115,9 +121,11 @@ static void bFillBlock(void *buf, int bufsize); #if B_VERIFY_CAUSES_SEVERE_OVERHEAD static void verifyUsedBlock(bType *bp, int q); static void verifyFreeBlock(bType *bp, int q); -static void verifyBallocSpace(); +void verifyBallocSpace(); #endif +static int ballocGetSize(int size, int *q); + /********************************** Code **************************************/ /* * Initialize the balloc module. bopen should be called the very first thing @@ -133,10 +141,25 @@ int bopen(void *buf, int bufsize, int flags) { bFlags = flags; +#if BASTARD_TESTING + srand(time(0L)); +#endif /* BASTARD_TESTING */ + +/* + * If bopen already called by a shared process, just increment the count + * and return; + */ + if (++bopenCount > 1) { + return 0; + } + if (buf == NULL) { if (bufsize == 0) { bufsize = B_DEFAULT_MEM; } +#ifdef IRIX + bufsize = ROUNDUP4(bufsize); +#endif if ((buf = malloc(bufsize)) == NULL) { return -1; } @@ -149,6 +172,7 @@ int bopen(void *buf, int bufsize, int flags) bFreeSize = bFreeLeft = bufsize; bFreeBuf = bFreeNext = buf; + memset(bQhead, 0, sizeof(bQhead)); #if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD bFillBlock(buf, bufsize); #endif @@ -171,8 +195,9 @@ void bclose() #if B_VERIFY_CAUSES_SEVERE_OVERHEAD verifyBallocSpace(); #endif - if (! (bFlags & B_USER_BUF)) { + if (--bopenCount <= 0 && !(bFlags & B_USER_BUF)) { free(bFreeBuf); + bopenCount = 0; } } @@ -185,13 +210,13 @@ void bclose() void *balloc(B_ARGS_DEC, int size) { bType *bp; - int q, memSize, mask; + int q, memSize; /* * Call bopen with default values if the application has not yet done so */ if (bFreeBuf == NULL) { - if (bopen(NULL, B_DEFAULT_MEM , 0) < 0) { + if (bopen(NULL, B_DEFAULT_MEM, 0) < 0) { return NULL; } } @@ -202,17 +227,14 @@ void *balloc(B_ARGS_DEC, int size) return NULL; } -/* - * Determine the relevant block queue with a block big enough -- - * include room for the block header. - */ - mask = (size + sizeof(bType)) >> B_SHIFT; - for (q = 0; mask; mask >>= 1) { - q++; +#if BASTARD_TESTING + if (rand() == 0x7fff) { + return NULL; } +#endif /* BASTARD_TESTING */ + - a_assert(0 <= q && q <= B_MAX_CLASS); - memSize = (1 << (B_SHIFT + q)); + memSize = ballocGetSize(size, &q); if (q >= B_MAX_CLASS) { /* @@ -221,11 +243,13 @@ void *balloc(B_ARGS_DEC, int size) if (bFlags & B_USE_MALLOC) { #if B_STATS bstats(0, NULL); +#endif +#ifdef IRIX + memSize = ROUNDUP4(memSize); #endif bp = (bType*) malloc(memSize); if (bp == NULL) { - goahead_trace(0, T("B: malloc failed for %s:%d, size %d\n"), - B_ARGS, memSize); + traceRaw(T("B: malloc failed\n")); return NULL; } #if B_STATS @@ -236,11 +260,14 @@ void *balloc(B_ARGS_DEC, int size) #endif } else { - goahead_trace(0, T("B: balloc failed for %s:%d, size %d\n"), - B_ARGS, memSize); + traceRaw(T("B: malloc failed\n")); return NULL; } - bp->u.size = size; + +/* + * the u.size is the actual size allocated for data + */ + bp->u.size = memSize - sizeof(bType); bp->flags = B_MALLOCED; } else if ((bp = bQhead[q]) != NULL) { @@ -254,7 +281,7 @@ void *balloc(B_ARGS_DEC, int size) #if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD bFillBlock(bp, memSize); #endif - bp->u.size = size; + bp->u.size = memSize - sizeof(bType); bp->flags = 0; } else { @@ -272,22 +299,24 @@ void *balloc(B_ARGS_DEC, int size) #if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD bFillBlock(bp, memSize); #endif - bp->u.size = size; + bp->u.size = memSize - sizeof(bType); bp->flags = 0; } else if (bFlags & B_USE_MALLOC) { - static int once = 0; - if (once++ < 20) { #if B_STATS + static int once = 0; + if (once++ == 0) { bstats(0, NULL); -#endif } +#endif /* * Nothing left on the primary free list, so malloc a new block */ +#ifdef IRIX + memSize = ROUNDUP4(memSize); +#endif if ((bp = (bType*) malloc(memSize)) == NULL) { - goahead_trace(0, T("B: malloc failed for %s:%d size %d\n"), - B_ARGS, memSize); + traceRaw(T("B: malloc failed\n")); return NULL; } #if B_STATS @@ -296,20 +325,31 @@ void *balloc(B_ARGS_DEC, int size) #if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD bFillBlock(bp, memSize); #endif - bp->u.size = size; + bp->u.size = memSize - sizeof(bType); bp->flags = B_MALLOCED; } else { - goahead_trace(0, T("B: alloc failed for %s:%d size %d\n"), B_ARGS, size); + traceRaw(T("B: malloc failed\n")); return NULL; } } #if B_STATS - bStatsAlloc(B_ARGS, bp, q, size); + bStatsAlloc(B_ARGS, bp, q, memSize); #endif bp->flags |= B_INTEGRITY; +/* + * The following is a good place to put a breakpoint when trying to reduce + * determine and reduce maximum memory use. + */ +#if 0 +#if B_STATS + if (bStatsBallocInUse == bStatsBallocMax) { + bstats(0, NULL); + } +#endif +#endif return (void*) ((char*) bp + sizeof(bType)); } @@ -323,41 +363,34 @@ void *balloc(B_ARGS_DEC, int size) void bfree(B_ARGS_DEC, void *mp) { bType *bp; - int mask, q; + int q, memSize; #if B_VERIFY_CAUSES_SEVERE_OVERHEAD verifyBallocSpace(); #endif - a_assert(mp); - bp = (bType*) ((char*) mp - sizeof(bType)); + a_assert((bp->flags & B_INTEGRITY_MASK) == B_INTEGRITY); + if ((bp->flags & B_INTEGRITY_MASK) != B_INTEGRITY) { return; } -/* - * Determine the relevant block queue - */ - mask = (bp->u.size + sizeof(bType)) >> B_SHIFT; - for (q = 0; mask; mask >>= 1) { - q++; - } - a_assert(0 <= q && q <= B_MAX_CLASS); + memSize = ballocGetSize(bp->u.size, &q); #if B_VERIFY_CAUSES_SEVERE_OVERHEAD verifyUsedBlock(bp,q); +#endif +#if B_STATS + bStatsFree(B_ARGS, bp, q, bp->u.size+sizeof(bType)); #endif if (bp->flags & B_MALLOCED) { free(bp); return; } -#if B_STATS - bStatsFree(B_ARGS, bp, q, bp->u.size); -#endif #if B_VERIFY_CAUSES_SEVERE_OVERHEAD - bFillBlock(bp, 1 << (B_SHIFT + q)); + bFillBlock(bp, memSize); #endif /* @@ -365,6 +398,8 @@ void bfree(B_ARGS_DEC, void *mp) */ bp->u.next = bQhead[q]; bQhead[q] = bp; + + bp->flags = B_FILL_WORD; } /******************************************************************************/ @@ -432,7 +467,7 @@ char_t *bstrdup(B_ARGS_DEC, char_t *s) void *brealloc(B_ARGS_DEC, void *mp, int newsize) { - bType* bp; + bType *bp; void *newbuf; if (mp == NULL) { @@ -440,6 +475,14 @@ void *brealloc(B_ARGS_DEC, void *mp, int newsize) } bp = (bType*) ((char*) mp - sizeof(bType)); a_assert((bp->flags & B_INTEGRITY_MASK) == B_INTEGRITY); + +/* + * If the allocated memory already has enough room just return the previously + * allocated address. + */ + if (bp->u.size >= newsize) { + return mp; + } if ((newbuf = balloc(B_ARGS, newsize)) != NULL) { memcpy(newbuf, mp, bp->u.size); bfree(B_ARGS, mp); @@ -447,6 +490,24 @@ void *brealloc(B_ARGS_DEC, void *mp, int newsize) return newbuf; } +/******************************************************************************/ +/* + * Find the size of the block to be balloc'ed. It takes in a size, finds the + * smallest binary block it fits into, adds an overhead amount and returns. + * q is the binary size used to keep track of block sizes in use. Called + * from both balloc and bfree. + */ + +static int ballocGetSize(int size, int *q) +{ + int mask; + + mask = (size == 0) ? 0 : (size-1) >> B_SHIFT; + for (*q = 0; mask; mask >>= 1) { + *q = *q + 1; + } + return ((1 << (B_SHIFT + *q)) + sizeof(bType)); +} /******************************************************************************/ #if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD @@ -464,17 +525,20 @@ static void bFillBlock(void *buf, int bufsize) #if B_STATS /* * Statistics. Do output via calling the writefn callback function with - * "handle" as the output file handle. + * "handle" as the output file handle. */ void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) { - bStatsFileType *fp; + bStatsFileType *fp, *files; + bStatsBlkType *blkp; bType *bp; - int q, count, mem, total; + char_t *cp; + int q, count, mem, total, len; static int recurseProtect = 0; if (recurseProtect++ > 0) { + recurseProtect--; return; } @@ -492,19 +556,18 @@ void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) * Q Size Free Bytes Inuse Bytes Allocs * dd ddddd ddd ddddd dddd ddddd dddd */ - (*writefn)(handle, " Q Size Free Bytes Inuse Bytes Allocs\n"); + (*writefn)(handle, " Q Size Free Bytes Inuse Bytes Allocs\n"); total = 0; for (q = 0; q < B_MAX_CLASS; q++) { count = 0; for (bp = bQhead[q]; bp; bp = bp->u.next) { - a_assert((bp->flags & B_INTEGRITY_MASK) == B_INTEGRITY); count++; } mem = count * (1 << (q + B_SHIFT)); total += mem; (*writefn)(handle, - T("%2d %5d %3d %5d %4d %5d %4d\n"), + T("%2d %5d %4d %6d %4d %5d %4d\n"), q, 1 << (q + B_SHIFT), count, mem, bStats[q].inuse, bStats[q].inuse * (1 << (q + B_SHIFT)), bStats[q].alloc); } @@ -513,11 +576,26 @@ void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) /* * Print summary stats + * + * bFreeSize Initial memory reserved with bopen call + * bStatsMemMalloc memory from calls to system MALLOC + * bStatsMemMax + * bStatsBallocMax largest amount of memory from balloc calls + * bStatsMemInUse + * bStatsBallocInUse present balloced memory being used + * bStatsBlksMax); + * bStackStart + * bStackMin); + * total); + * bFreeLeft); + * */ (*writefn)(handle, T("Initial free list size %7d\n"), bFreeSize); (*writefn)(handle, T("Max memory malloced %7d\n"), bStatsMemMalloc); (*writefn)(handle, T("Max memory ever used %7d\n"), bStatsMemMax); + (*writefn)(handle, T("Max memory ever balloced %7d\n"), bStatsBallocMax); (*writefn)(handle, T("Memory currently in use %7d\n"), bStatsMemInUse); + (*writefn)(handle, T("Memory currently balloced %7d\n"), bStatsBallocInUse); (*writefn)(handle, T("Max blocks allocated %7d\n"), bStatsBlksMax); (*writefn)(handle, T("Maximum stack used %7d\n"), (int) bStackStart - (int) bStackMin); @@ -527,20 +605,49 @@ void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) (*writefn)(handle, T("Total free memory %7d\n"), bFreeLeft + total); /* - * Print per file allocation stats + * Print per file allocation stats. Sort the copied table. */ - qsort(bStatsFiles, bStatsFilesMax, sizeof(bStatsFileType), bStatsFileSort); - (*writefn)(handle, T("\nPer File Memory Stats\n")); + len = sizeof(bStatsFileType) * B_MAX_FILES; + files = malloc(len); + if (files == NULL) { + (*writefn)(handle, T("Can't allocate stats memory\n")); + recurseProtect--; + return; + } + memcpy(files, bStatsFiles, len); + qsort(files, bStatsFilesMax, sizeof(bStatsFileType), bStatsFileSort); + + (*writefn)(handle, T("\nMemory Currently Allocated\n")); total = 0; - for (fp = bStatsFiles; fp < &bStatsFiles[bStatsFilesMax]; fp++) { + (*writefn)(handle, + T(" bytes, blocks in use, total times,") + T("largest, q\n")); + + for (fp = files; fp < &files[bStatsFilesMax]; fp++) { if (fp->file[0]) { - (*writefn)(handle, - T("%18s, bytes %7d, blocks in use %5d, total allocs %6d\n"), - fp->file, fp->allocated, fp->count, fp->allocs); + (*writefn)(handle, T("%18s, %7d, %5d, %6d, %7d,%4d\n"), + fp->file, fp->allocated, fp->count, fp->times, fp->largest, + fp->q); total += fp->allocated; } } - (*writefn)(handle, T("\nTotal allocated %7d\n"), total); + (*writefn)(handle, T("\nTotal allocated %7d\n\n"), total); + +/* + * Dump the actual strings + */ + (*writefn)(handle, T("\nStrings\n")); + for (blkp = &bStatsBlks[bStatsBlksMax - 1]; blkp >= bStatsBlks; blkp--) { + if (blkp->ptr) { + cp = (char_t*) ((char*) blkp->ptr + sizeof(bType)); + fp = blkp->who; + if (gisalnum(*cp)) { + (*writefn)(handle, T("%-50s allocated by %s\n"), cp, + fp->file); + } + } + } + free(files); recurseProtect--; } @@ -563,26 +670,6 @@ static int bStatsFileSort(const void *cp1, const void *cp2) return 1; } -/******************************************************************************/ -/* - * Default output function. Just send to trace channel. - */ - -static void bstatsWrite(int handle, char_t *fmt, ...) -{ - va_list args; - char_t *buf; - - va_start(args, fmt); - buf = NULL; - gvsnprintf(&buf, VALUE_MAX_STRING, fmt, args); - va_end(args); - goahead_trace(0, buf); - if (buf) { - bfree(B_L, buf); - } -} - /******************************************************************************/ /* * Accumulate allocation statistics @@ -590,23 +677,24 @@ static void bstatsWrite(int handle, char_t *fmt, ...) static void bStatsAlloc(B_ARGS_DEC, void *ptr, int q, int size) { + int memSize; bStatsFileType *fp; bStatsBlkType *bp; char_t name[FNAMESIZE + 10]; - a_assert(file && *file); - a_assert(0 <= q && q <= B_MAX_CLASS); - a_assert(size > 0); - gsprintf(name, T("%s:%d"), B_ARGS); bStats[q].alloc++; bStats[q].inuse++; bStatsMemInUse += size; - if (bStatsMemInUse > bStatsMemMax) { bStatsMemMax = bStatsMemInUse; } + memSize = (1 << (B_SHIFT + q)) + sizeof(bType); + bStatsBallocInUse += memSize; + if (bStatsBallocInUse > bStatsBallocMax) { + bStatsBallocMax = bStatsBallocInUse; + } /* * Track maximum stack usage. Assumes a stack growth down. Approximate as @@ -623,13 +711,17 @@ static void bStatsAlloc(B_ARGS_DEC, void *ptr, int q, int size) if (fp->file[0] == file[0] && gstrcmp(fp->file, name) == 0) { fp->allocated += size; fp->count++; - fp->allocs++; + fp->times++; + if (fp->largest < size) { + fp->largest = size; + fp->q = q; + } break; } } /* - * Find the first free slot for this file and add current block size. + * New entry: find the first free slot and create a new entry */ if (fp >= &bStatsFiles[bStatsFilesMax]) { for (fp = bStatsFiles; fp < &bStatsFiles[B_MAX_FILES]; fp++) { @@ -637,7 +729,9 @@ static void bStatsAlloc(B_ARGS_DEC, void *ptr, int q, int size) gstrncpy(fp->file, name, TSZ(fp->file)); fp->allocated += size; fp->count++; - fp->allocs++; + fp->times++; + fp->largest = size; + fp->q = q; if ((fp - bStatsFiles) >= bStatsFilesMax) { bStatsFilesMax = (fp - bStatsFiles) + 1; } @@ -668,35 +762,48 @@ static void bStatsAlloc(B_ARGS_DEC, void *ptr, int q, int size) static void bStatsFree(B_ARGS_DEC, void *ptr, int q, int size) { + int memSize; bStatsFileType *fp; bStatsBlkType *bp; - char_t name[FNAMESIZE + 10]; - - a_assert(file && *file); - a_assert(0 <= q && q <= B_MAX_CLASS); - a_assert(size > 0); + memSize = (1 << (B_SHIFT + q)) + sizeof(bType); bStatsMemInUse -= size; + bStatsBallocInUse -= memSize; bStats[q].inuse--; - gsprintf(name, T("%s:%d"), B_ARGS); - /* - * Update the per block stats + * Update the per block stats. Try from the end first */ - for (bp = bStatsBlks; bp < &bStatsBlks[bStatsBlksMax]; bp++) { + for (bp = &bStatsBlks[bStatsBlksMax - 1]; bp >= bStatsBlks; bp--) { if (bp->ptr == ptr) { bp->ptr = NULL; fp = bp->who; + bp->who = NULL; fp->allocated -= size; fp->count--; return; } } - a_assert(0); +} + +/******************************************************************************/ +/* + * Default output function. Just send to trace channel. + */ + +#undef sprintf +static void bstatsWrite(int handle, char_t *fmt, ...) +{ + va_list args; + char_t buf[BUF_MAX]; + va_start(args, fmt); + vsprintf(buf, fmt, args); + va_end(args); + traceRaw(buf); } + #else /* not B_STATS */ /******************************************************************************/ /* @@ -712,10 +819,10 @@ void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) #if B_VERIFY_CAUSES_SEVERE_OVERHEAD /* * The following routines verify the integrity of the balloc memory space. - * These functions depend use the B_FILL feature. Corruption is defined + * These functions use the B_FILL feature. Corruption is defined * as bad integrity flags in allocated blocks or data other than B_FILL_CHAR * being found anywhere in the space which is unallocated and that is not a - * next pointer in the free queues. a_assert is called if any corruption is + * next pointer in the free queues. a_assert is called if any corruption is * found. CAUTION: These functions add severe processing overhead and should * only be used when searching for a tough corruption problem. */ @@ -731,8 +838,8 @@ static void verifyUsedBlock(bType *bp, int q) int memSize, size; char *p; - memSize = (1 << (B_SHIFT + q)); - a_assert((bp->flags & ~B_MALLOCED) == B_INTEGRITY ); + memSize = (1 << (B_SHIFT + q)) + sizeof(bType); + a_assert((bp->flags & ~B_MALLOCED) == B_INTEGRITY); size = bp->u.size; for (p = ((char *)bp)+sizeof(bType)+size; p < ((char*)bp)+memSize; p++) { a_assert(*p == B_FILL_CHAR); @@ -750,28 +857,41 @@ static void verifyFreeBlock(bType *bp, int q) int memSize; char *p; - memSize = (1 << (B_SHIFT + q)); + memSize = (1 << (B_SHIFT + q)) + sizeof(bType); for (p = ((char *)bp)+sizeof(void*); p < ((char*)bp)+memSize; p++) { a_assert(*p == B_FILL_CHAR); } bp = (bType *)p; a_assert((bp->flags & ~B_MALLOCED) == B_INTEGRITY || - bp->flags == B_FILL_WORD); + bp->flags == B_FILL_WORD); } /******************************************************************************/ /* * verifyBallocSpace reads through the entire balloc memory space and - * verifies that all allocated blocks are uncorrupted and that with the - * exception of free list next pointers all other unallocated space is + * verifies that all allocated blocks are uncorrupted and that, with the + * exception of free list next pointers, all other unallocated space is * filled with B_FILL_CHAR. */ -static void verifyBallocSpace() +void verifyBallocSpace() { + int q; char *p; bType *bp; +/* + * First verify all the free blocks. + */ + for (q = 0; q < B_MAX_CLASS; q++) { + for (bp = bQhead[q]; bp != NULL; bp = bp->u.next) { + verifyFreeBlock(bp, q); + } + } + +/* + * Now verify other space + */ p = bFreeBuf; while (p < (bFreeBuf + bFreeSize)) { bp = (bType *)p; @@ -782,7 +902,7 @@ static void verifyBallocSpace() } } else { a_assert(((bp->flags & ~B_MALLOCED) == B_INTEGRITY) || - bp->flags == B_FILL_WORD); + bp->flags == B_FILL_WORD); p += (sizeof(bType) + bp->u.size); while (p < (bFreeBuf + bFreeSize) && *p == B_FILL_CHAR) { p++; @@ -807,19 +927,29 @@ void bclose() } /******************************************************************************/ -#if UNICODE -char_t* bstrdupNoBalloc(char_t* s) + +void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) +{ +} + +/******************************************************************************/ + +char_t *bstrdupNoBalloc(char_t *s) { +#if UNICODE if (s) { return wcsdup(s); } else { return wcsdup(T("")); } +#else + return bstrdupANoBalloc(s); +#endif } -#endif /* UNICODE */ /******************************************************************************/ -char* bstrdupANoBalloc(char* s) + +char *bstrdupANoBalloc(char *s) { char* buf; diff --git a/c/src/libnetworking/rtems_webserver/base64.c b/c/src/libnetworking/rtems_webserver/base64.c new file mode 100644 index 0000000000..b1eb6b01c5 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/base64.c @@ -0,0 +1,147 @@ +/* + * base64.c -- Base64 Mime encoding + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + */ + +/******************************** Description *********************************/ + +/* + * The base64 command encodes and decodes a string in mime base64 format + */ + +/********************************* Includes ***********************************/ + +#include "wsIntrn.h" + +/******************************** Local Data **********************************/ +/* + * Mapping of ANSI chars to base64 Mime encoding alphabet (see below) + */ + +static char_t map64[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char_t alphabet64[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', +}; + +/*********************************** Code *************************************/ +/* + * Decode a buffer from "string" and into "outbuf" + */ + +int websDecode64(char_t *outbuf, char_t *string, int outlen) +{ + unsigned long shiftbuf; + char_t *cp, *op; + int c, i, j, shift; + + op = outbuf; + *op = '\0'; + cp = string; + while (*cp && *cp != '=') { +/* + * Map 4 (6bit) input bytes and store in a single long (shiftbuf) + */ + shiftbuf = 0; + shift = 18; + for (i = 0; i < 4 && *cp && *cp != '='; i++, cp++) { + c = map64[*cp & 0xff]; + if (c == -1) { + error(E_L, E_LOG, T("Bad string: %s at %c index %d"), string, + c, i); + return -1; + } + shiftbuf = shiftbuf | (c << shift); + shift -= 6; + } +/* + * Interpret as 3 normal 8 bit bytes (fill in reverse order). + * Check for potential buffer overflow before filling. + */ + --i; + if ((op + i) >= &outbuf[outlen]) { + gstrcpy(outbuf, T("String too big")); + return -1; + } + for (j = 0; j < i; j++) { + *op++ = (char_t) ((shiftbuf >> (8 * (2 - j))) & 0xff); + } + *op = '\0'; + } + return 0; +} + + +/******************************************************************************/ +/* + * Encode a buffer from "string" into "outbuf" + */ + +void websEncode64(char_t *outbuf, char_t *string, int outlen) +{ + unsigned long shiftbuf; + char_t *cp, *op; + int x, i, j, shift; + + op = outbuf; + *op = '\0'; + cp = string; + while (*cp) { +/* + * Take three characters and create a 24 bit number in shiftbuf + */ + shiftbuf = 0; + for (j = 2; j >= 0 && *cp; j--, cp++) { + shiftbuf |= ((*cp & 0xff) << (j * 8)); + } +/* + * Now convert shiftbuf to 4 base64 letters. The i,j magic calculates + * how many letters need to be output. + */ + shift = 18; + for (i = ++j; i < 4 && op < &outbuf[outlen] ; i++) { + x = (shiftbuf >> shift) & 0x3f; + *op++ = alphabet64[(shiftbuf >> shift) & 0x3f]; + shift -= 6; + } +/* + * Pad at the end with '=' + */ + while (j-- > 0) { + *op++ = '='; + } + *op = '\0'; + } +} +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/default.c b/c/src/libnetworking/rtems_webserver/default.c index c485c22285..6851995872 100644 --- a/c/src/libnetworking/rtems_webserver/default.c +++ b/c/src/libnetworking/rtems_webserver/default.c @@ -1,9 +1,11 @@ /* * default.c -- Default URL handler. Includes support for ASP. * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ */ /******************************** Description *********************************/ @@ -38,16 +40,15 @@ static void websDefaultWriteEvent(webs_t wp); */ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, - char_t *url, char_t *path, char_t* query) + char_t *url, char_t *path, char_t *query) { websStatType sbuf; - char_t *lpath, *tmp; - char_t *date; + char_t *lpath, *tmp, *date; int bytes, flags, nchars; a_assert(websValid(wp)); a_assert(url && *url); - a_assert(path && *path); + a_assert(path); a_assert(query); /* @@ -74,8 +75,7 @@ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, path[--nchars] = '\0'; } nchars += gstrlen(websDefaultPage) + 2; - tmp = NULL; - gsnprintf(&tmp, nchars, T("%s/%s"), path, websDefaultPage); + fmtAlloc(&tmp, nchars, T("%s/%s"), path, websDefaultPage); websRedirect(wp, tmp); bfreeSafe(B_L, tmp); return 1; @@ -87,13 +87,13 @@ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, if (websPageOpen(wp, lpath, path, SOCKET_RDONLY | SOCKET_BINARY, 0666) < 0) { websError(wp, 400, - T("Can't open document %s
for URL %s"), - lpath, url); + T("Cannot open URL %s"), url); return 1; } if (websPageStat(wp, lpath, path, &sbuf) < 0) { - websError(wp, 400, T("Can't stat page %s
for URL %s"), - lpath, url); + websError(wp, 400, T("Cannot stat page for URL %s"), + url); + return 1; } /* @@ -107,12 +107,13 @@ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, if (sbuf.mtime <= wp->since) { websWrite(wp, T("HTTP/1.0 304 Use local copy\r\n")); - /* by license terms the following line of code must - * not be modified. - */ - websWrite(wp, T("Server: GoAhead-Webs\r\n")); +/* + * by license terms the following line of code must + * not be modified. + */ + websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); - if (flags && WEBS_KEEP_ALIVE) { + if (flags & WEBS_KEEP_ALIVE) { websWrite(wp, T("Connection: keep-alive\r\n")); } websWrite(wp, T("\r\n")); @@ -131,8 +132,8 @@ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, /* * By license terms the following line of code must not be modified. -*/ - websWrite(wp, T("Server: GoAhead-Webs\r\n")); + */ + websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); bfree(B_L, date); } flags |= WEBS_HEADER_DONE; @@ -164,6 +165,14 @@ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, } websWrite(wp, T("\r\n")); +/* + * All done if the browser did a HEAD request + */ + if (flags & WEBS_HEAD_REQUEST) { + websDone(wp, 200); + return 1; + } + /* * Evaluate ASP requests */ @@ -175,17 +184,18 @@ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, return 1; } -/* - * All done if the browser did a HEAD request - */ - if (flags & WEBS_HEAD_REQUEST) { - websDone(wp, 200); - return 1; +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + websDefaultWriteEvent(wp); + } else { + websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent); } +#else /* * For normal web documents, return the data via background write */ websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent); +#endif return 1; } @@ -240,7 +250,7 @@ int websValidateUrl(webs_t wp, char_t *path) /* * Create local path for document. Need extra space all "/" and null. */ - if (npart) { + if (npart || (gstrcmp(path, T("/")) == 0) || (path[0] == '\0')) { lpath = balloc(B_L, (gstrlen(dir) + 1 + len + 1) * sizeof(char_t)); gstrcpy(lpath, dir); @@ -267,15 +277,16 @@ int websValidateUrl(webs_t wp, char_t *path) static void websDefaultWriteEvent(webs_t wp) { - int len, wrote, flags, bytes, written; - char * buf; + int len, wrote, flags, bytes, written; + char *buf; a_assert(websValid(wp)); flags = websGetRequestFlags(wp); - wrote = 0; - bytes = 0; + websMarkTime(wp); + + wrote = bytes = 0; written = websGetRequestWritten(wp); /* @@ -284,20 +295,20 @@ static void websDefaultWriteEvent(webs_t wp) if ( !(flags & WEBS_ASP)) { bytes = websGetRequestBytes(wp); /* - * Note: websWriteBlock may return less than we wanted. It will return - * -1 on a socket error + * Note: websWriteDataNonBlock may return less than we wanted. It will + * return -1 on a socket error */ if ((buf = balloc(B_L, PAGE_READ_BUFSIZE)) == NULL) { websError(wp, 200, T("Can't get memory")); } else { while ((len = websPageReadData(wp, buf, PAGE_READ_BUFSIZE)) > 0) { - if ((wrote = websWriteBlockData(wp, buf, len)) < 0) { + if ((wrote = websWriteDataNonBlock(wp, buf, len)) < 0) { break; } written += wrote; if (wrote != len) { - websPageSeek(wp, - (wrote - len)); + websPageSeek(wp, - (len - wrote)); break; } } @@ -330,9 +341,11 @@ void websDefaultClose() { if (websDefaultPage) { bfree(B_L, websDefaultPage); + websDefaultPage = NULL; } if (websDefaultDir) { bfree(B_L, websDefaultDir); + websDefaultDir = NULL; } } diff --git a/c/src/libnetworking/rtems_webserver/ej.h b/c/src/libnetworking/rtems_webserver/ej.h index 91eb72a4d2..fa7a51a771 100644 --- a/c/src/libnetworking/rtems_webserver/ej.h +++ b/c/src/libnetworking/rtems_webserver/ej.h @@ -1,7 +1,7 @@ /* * ej.h -- Ejscript(TM) header * - * Copyright (c) Go Ahead Software, Inc., 1992-1999 + * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved. * * See the file "license.txt" for information on usage and redistribution */ @@ -12,214 +12,32 @@ /******************************** Description *********************************/ /* - * Go Ahead Ejscript(TM) header. This defines the Ejscript API and internal + * GoAhead Ejscript(TM) header. This defines the Ejscript API and internal * structures. */ /********************************* Includes ***********************************/ -#include -#include -#include -#ifndef CE - #include -#endif - -#if LYNX - #include -#endif - -#ifdef QNX4 - #include -#endif - -#if UEMF - #include "uemf.h" -#else - #include - #include - #include "basic/basicInternal.h" +#if ! UEMF + #include "basic/basic.h" #include "emf/emf.h" - #include "webs/webs.h" +#else + #include "uemf.h" #endif /********************************** Defines ***********************************/ -/* - * Constants - */ -#define EJ_INC 110 /* Growth for tags/tokens */ -#define EJ_OFFSET 1 /* hAlloc doesn't like 0 entries */ -#define EJ_MAX_RECURSE 100 /* Sanity for maximum recursion */ - -/* - * Ejscript Lexical analyser tokens - */ -#define TOK_ERR -1 /* Any error */ -#define TOK_LPAREN 1 /* ( */ -#define TOK_RPAREN 2 /* ) */ -#define TOK_IF 3 /* if */ -#define TOK_ELSE 4 /* else */ -#define TOK_LBRACE 5 /* { */ -#define TOK_RBRACE 6 /* } */ -#define TOK_LOGICAL 7 /* ||, &&, ! */ -#define TOK_EXPR 8 /* +, -, /, % */ -#define TOK_SEMI 9 /* ; */ -#define TOK_LITERAL 10 /* literal string */ -#define TOK_FUNCTION 11 /* function name */ -#define TOK_NEWLINE 12 /* newline white space */ -#define TOK_ID 13 /* function name */ -#define TOK_EOF 14 /* End of script */ -#define TOK_COMMA 15 /* Comma */ -#define TOK_VAR 16 /* var */ -#define TOK_ASSIGNMENT 17 /* = */ -#define TOK_FOR 18 /* for */ -#define TOK_INC_DEC 19 /* ++, -- */ -#define TOK_RETURN 20 /* return */ - -/* - * Expression operators - */ -#define EXPR_LESS 1 /* < */ -#define EXPR_LESSEQ 2 /* <= */ -#define EXPR_GREATER 3 /* > */ -#define EXPR_GREATEREQ 4 /* >= */ -#define EXPR_EQ 5 /* == */ -#define EXPR_NOTEQ 6 /* != */ -#define EXPR_PLUS 7 /* + */ -#define EXPR_MINUS 8 /* - */ -#define EXPR_DIV 9 /* / */ -#define EXPR_MOD 10 /* % */ -#define EXPR_LSHIFT 11 /* << */ -#define EXPR_RSHIFT 12 /* >> */ -#define EXPR_MUL 13 /* * */ -#define EXPR_ASSIGNMENT 14 /* = */ -#define EXPR_INC 15 /* ++ */ -#define EXPR_DEC 16 /* -- */ - -/* - * Conditional operators - */ -#define COND_AND 1 /* && */ -#define COND_OR 2 /* || */ -#define COND_NOT 3 /* ! */ - -/* - * States - */ -#define STATE_ERR -1 /* Error state */ -#define STATE_EOF 1 /* End of file */ -#define STATE_COND 2 /* Parsing a "(conditional)" stmt */ -#define STATE_COND_DONE 3 -#define STATE_RELEXP 4 /* Parsing a relational expr */ -#define STATE_RELEXP_DONE 5 -#define STATE_EXPR 6 /* Parsing an expression */ -#define STATE_EXPR_DONE 7 -#define STATE_STMT 8 /* Parsing General statement */ -#define STATE_STMT_DONE 9 -#define STATE_STMT_BLOCK_DONE 10 /* End of block "}" */ -#define STATE_ARG_LIST 11 /* Function arg list */ -#define STATE_ARG_LIST_DONE 12 -#define STATE_DEC_LIST 16 /* Declaration list */ -#define STATE_DEC_LIST_DONE 17 -#define STATE_DEC 18 -#define STATE_DEC_DONE 19 -#define STATE_BEGIN STATE_STMT - -/* - * Flags. Used in ej_t and as parameter to parse() - */ -#define FLAGS_EXE 0x1 /* Execute statements */ -#define FLAGS_VARIABLES 0x2 /* Allocated variables store */ -#define FLAGS_FUNCTIONS 0x4 /* Allocated function store */ - -/* - * Function call structure - */ -typedef struct { - char_t *fname; /* Function name */ - char_t **args; /* Args for function (halloc) */ - int nArgs; /* Number of args */ -} ejfunc_t; - -/* - * EJ evaluation block structure - */ -typedef struct ejEval { - ringq_t tokbuf; /* Current token */ - ringq_t script; /* Input script for parsing */ - char_t *putBackToken; /* Putback token string */ - int putBackTokenId; /* Putback token ID */ - char_t *line; /* Current line */ - int lineLength; /* Current line length */ - int lineNumber; /* Parse line number */ - int lineColumn; /* Column in line */ -} ejinput_t; - -/* - * Per Ejscript session structure - */ -typedef struct ej { - ejinput_t *input; /* Input evaluation block */ - sym_fd_t functions; /* Symbol table for functions */ - sym_fd_t *variables; /* hAlloc list of variables */ - int variableMax; /* Number of entries */ - ejfunc_t *func; /* Current function */ - char_t *result; /* Current expression result */ - char_t *error; /* Error message */ - char_t *token; /* Pointer to token string */ - int tid; /* Current token id */ - int eid; /* Halloc handle */ - int flags; /* Flags */ - int userHandle; /* User defined handle */ -} ej_t; /******************************** Prototypes **********************************/ +extern int ejArgs(int argc, char_t **argv, char_t *fmt, ...); +extern void ejSetResult(int eid, char_t *s); extern int ejOpenEngine(sym_fd_t variables, sym_fd_t functions); extern void ejCloseEngine(int eid); -extern int ejOpenBlock(int eid); -extern int ejCloseBlock(int eid, int vid); -extern char_t *ejEval(int eid, char_t *script, char_t **emsg); -extern char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg); -extern char_t *ejEvalFile(int eid, char_t *path, char_t **emsg); extern int ejSetGlobalFunction(int eid, char_t *name, int (*fn)(int eid, void *handle, int argc, char_t **argv)); -extern void *ejGetGlobalFunction(int eid, char_t *name); -extern int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, - int (*fn)(int eid, void *handle, int argc, char_t **argv)); -extern int ejArgs(int argc, char_t **argv, char_t *fmt, ...); -extern void ejError(ej_t* ep, char_t* fmt, ...); -extern void ejSetUserHandle(int eid, int handle); -extern int ejGetUserHandle(int eid); -extern int ejGetLineNumber(int eid); -extern void ejSetResult(int eid, char_t *s); -extern char_t *ejGetResult(int eid); extern void ejSetVar(int eid, char_t *var, char_t *value); -extern void ejSetLocalVar(int eid, char_t *var, char_t *value); extern int ejGetVar(int eid, char_t *var, char_t **value); -extern void ejSetGlobalVar(int eid, char_t *var, char_t *value); - -extern int ejLexOpen(ej_t* ep); -extern void ejLexClose(ej_t* ep); -extern int ejLexOpenScript(ej_t* ep, char_t *script); -extern void ejLexCloseScript(ej_t* ep); -extern void ejLexSaveInputState(ej_t* ep, ejinput_t* state); -extern void ejLexFreeInputState(ej_t* ep, ejinput_t* state); -extern void ejLexRestoreInputState(ej_t* ep, ejinput_t* state); -extern int ejLexGetToken(ej_t* ep, int state); -extern void ejLexPutbackToken(ej_t* ep, int tid, char_t *string); - -extern sym_fd_t ejGetVariableTable(int eid); -extern sym_fd_t ejGetFunctionTable(int eid); - -extern int ejEmfOpen(int eid); -extern void ejEmfClose(int eid); - -extern int ejEmfDbRead(int eid, void *handle, int argc, char_t **argv); -extern int ejEmfDbReadKeyed(int eid, void *handle, int argc, char_t **argv); -extern int ejEmfDbTableGetNrow(int eid, void *handle, int argc, char_t **argv); -extern int ejEmfTrace(int eid, void *handle, int argc, char_t **argv); -extern int ejEmfDbWrite(int eid, void *handle, int argc, char_t **argv); +extern char_t *ejEval(int eid, char_t *script, char_t **emsg); #endif /* _h_EJ */ diff --git a/c/src/libnetworking/rtems_webserver/ejIntrn.h b/c/src/libnetworking/rtems_webserver/ejIntrn.h new file mode 100644 index 0000000000..0ab7b9b5e5 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/ejIntrn.h @@ -0,0 +1,228 @@ +/* + * ejIntrn.h -- Ejscript(TM) header + * + * Copyright (c) GoAhead Software, Inc., 1992-2000 + * + * See the file "license.txt" for information on usage and redistribution + */ + +#ifndef _h_EJINTERNAL +#define _h_EJINTERNAL 1 + +/******************************** Description *********************************/ + +/* + * GoAhead Ejscript(TM) header. This defines the Ejscript API and internal + * structures. + */ + +/********************************* Includes ***********************************/ + +#include +#include +#include + +#if CE +#if ! UEMF + #include +#endif +#endif + +#if LYNX + #include +#endif + +#ifdef QNX4 + #include +#endif + +#if UEMF + #include "uemf.h" +#else + #include + #include + #include "basic/basicInternal.h" + #include "emf/emfInternal.h" +#endif + +#include "ej.h" + +/********************************** Defines ***********************************/ +/* + * Constants + */ +#define EJ_INC 110 /* Growth for tags/tokens */ +#define EJ_SCRIPT_INC 1023 /* Growth for ej scripts */ +#define EJ_OFFSET 1 /* hAlloc doesn't like 0 entries */ +#define EJ_MAX_RECURSE 100 /* Sanity for maximum recursion */ + +/* + * Ejscript Lexical analyser tokens + */ +#define TOK_ERR -1 /* Any error */ +#define TOK_LPAREN 1 /* ( */ +#define TOK_RPAREN 2 /* ) */ +#define TOK_IF 3 /* if */ +#define TOK_ELSE 4 /* else */ +#define TOK_LBRACE 5 /* { */ +#define TOK_RBRACE 6 /* } */ +#define TOK_LOGICAL 7 /* ||, &&, ! */ +#define TOK_EXPR 8 /* +, -, /, % */ +#define TOK_SEMI 9 /* ; */ +#define TOK_LITERAL 10 /* literal string */ +#define TOK_FUNCTION 11 /* function name */ +#define TOK_NEWLINE 12 /* newline white space */ +#define TOK_ID 13 /* function name */ +#define TOK_EOF 14 /* End of script */ +#define TOK_COMMA 15 /* Comma */ +#define TOK_VAR 16 /* var */ +#define TOK_ASSIGNMENT 17 /* = */ +#define TOK_FOR 18 /* for */ +#define TOK_INC_DEC 19 /* ++, -- */ +#define TOK_RETURN 20 /* return */ + +/* + * Expression operators + */ +#define EXPR_LESS 1 /* < */ +#define EXPR_LESSEQ 2 /* <= */ +#define EXPR_GREATER 3 /* > */ +#define EXPR_GREATEREQ 4 /* >= */ +#define EXPR_EQ 5 /* == */ +#define EXPR_NOTEQ 6 /* != */ +#define EXPR_PLUS 7 /* + */ +#define EXPR_MINUS 8 /* - */ +#define EXPR_DIV 9 /* / */ +#define EXPR_MOD 10 /* % */ +#define EXPR_LSHIFT 11 /* << */ +#define EXPR_RSHIFT 12 /* >> */ +#define EXPR_MUL 13 /* * */ +#define EXPR_ASSIGNMENT 14 /* = */ +#define EXPR_INC 15 /* ++ */ +#define EXPR_DEC 16 /* -- */ +#define EXPR_BOOL_COMP 17 /* ! */ +/* + * Conditional operators + */ +#define COND_AND 1 /* && */ +#define COND_OR 2 /* || */ +#define COND_NOT 3 /* ! */ + +/* + * States + */ +#define STATE_ERR -1 /* Error state */ +#define STATE_EOF 1 /* End of file */ +#define STATE_COND 2 /* Parsing a "(conditional)" stmt */ +#define STATE_COND_DONE 3 +#define STATE_RELEXP 4 /* Parsing a relational expr */ +#define STATE_RELEXP_DONE 5 +#define STATE_EXPR 6 /* Parsing an expression */ +#define STATE_EXPR_DONE 7 +#define STATE_STMT 8 /* Parsing General statement */ +#define STATE_STMT_DONE 9 +#define STATE_STMT_BLOCK_DONE 10 /* End of block "}" */ +#define STATE_ARG_LIST 11 /* Function arg list */ +#define STATE_ARG_LIST_DONE 12 +#define STATE_DEC_LIST 16 /* Declaration list */ +#define STATE_DEC_LIST_DONE 17 +#define STATE_DEC 18 +#define STATE_DEC_DONE 19 + +#define STATE_RET 20 /* Return statement */ + +#define STATE_BEGIN STATE_STMT + +/* + * Flags. Used in ej_t and as parameter to parse() + */ +#define FLAGS_EXE 0x1 /* Execute statements */ +#define FLAGS_VARIABLES 0x2 /* Allocated variables store */ +#define FLAGS_FUNCTIONS 0x4 /* Allocated function store */ + +/* + * Function call structure + */ +typedef struct { + char_t *fname; /* Function name */ + char_t **args; /* Args for function (halloc) */ + int nArgs; /* Number of args */ +} ejfunc_t; + +/* + * EJ evaluation block structure + */ +typedef struct ejEval { + ringq_t tokbuf; /* Current token */ + ringq_t script; /* Input script for parsing */ + char_t *putBackToken; /* Putback token string */ + int putBackTokenId; /* Putback token ID */ + char_t *line; /* Current line */ + int lineLength; /* Current line length */ + int lineNumber; /* Parse line number */ + int lineColumn; /* Column in line */ +} ejinput_t; + +/* + * Per Ejscript session structure + */ +typedef struct ej { + ejinput_t *input; /* Input evaluation block */ + sym_fd_t functions; /* Symbol table for functions */ + sym_fd_t *variables; /* hAlloc list of variables */ + int variableMax; /* Number of entries */ + ejfunc_t *func; /* Current function */ + char_t *result; /* Current expression result */ + char_t *error; /* Error message */ + char_t *token; /* Pointer to token string */ + int tid; /* Current token id */ + int eid; /* Halloc handle */ + int flags; /* Flags */ + int userHandle; /* User defined handle */ +} ej_t; + +/******************************** Prototypes **********************************/ + +extern int ejOpenBlock(int eid); +extern int ejCloseBlock(int eid, int vid); +extern char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg); +#ifndef __NO_EJ_FILE +extern char_t *ejEvalFile(int eid, char_t *path, char_t **emsg); +#endif +extern int ejRemoveGlobalFunction(int eid, char_t *name); +extern void *ejGetGlobalFunction(int eid, char_t *name); +extern int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, + int (*fn)(int eid, void *handle, int argc, char_t **argv)); +extern void ejError(ej_t* ep, char_t* fmt, ...); +extern void ejSetUserHandle(int eid, int handle); +extern int ejGetUserHandle(int eid); +extern int ejGetLineNumber(int eid); +extern char_t *ejGetResult(int eid); +extern void ejSetLocalVar(int eid, char_t *var, char_t *value); +extern void ejSetGlobalVar(int eid, char_t *var, char_t *value); + +extern int ejLexOpen(ej_t* ep); +extern void ejLexClose(ej_t* ep); +extern int ejLexOpenScript(ej_t* ep, char_t *script); +extern void ejLexCloseScript(ej_t* ep); +extern void ejLexSaveInputState(ej_t* ep, ejinput_t* state); +extern void ejLexFreeInputState(ej_t* ep, ejinput_t* state); +extern void ejLexRestoreInputState(ej_t* ep, ejinput_t* state); +extern int ejLexGetToken(ej_t* ep, int state); +extern void ejLexPutbackToken(ej_t* ep, int tid, char_t *string); + +extern sym_fd_t ejGetVariableTable(int eid); +extern sym_fd_t ejGetFunctionTable(int eid); + +extern int ejEmfOpen(int eid); +extern void ejEmfClose(int eid); + +extern int ejEmfDbRead(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbReadKeyed(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbTableGetNrow(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbDeleteRow(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfTrace(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbWrite(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbCollectTable(int eid, void *handle, int argc, char_t **argv); + +#endif /* _h_EJINTERNAL */ diff --git a/c/src/libnetworking/rtems_webserver/ejlex.c b/c/src/libnetworking/rtems_webserver/ejlex.c index 091d17411b..ea5cead255 100644 --- a/c/src/libnetworking/rtems_webserver/ejlex.c +++ b/c/src/libnetworking/rtems_webserver/ejlex.c @@ -1,7 +1,7 @@ /* * ejlex.c -- Ejscript(TM) Lexical Analyser * - * Copyright (c) Go Ahead Software, Inc., 1995-1999 + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -15,7 +15,7 @@ /********************************** Includes **********************************/ -#include "ej.h" +#include "ejIntrn.h" #if UEMF #include "uemf.h" @@ -23,12 +23,16 @@ #include "basic/basicInternal.h" #endif +/********************************** Defines ***********************************/ +#define OCTAL 8 +#define HEX 16 /****************************** Forward Declarations **************************/ static int getLexicalToken(ej_t* ep, int state); static int tokenAddChar(ej_t *ep, int c); static int inputGetc(ej_t* ep); static void inputPutback(ej_t* ep, int c); +static int charConvert(ej_t* ep, int base, int maxDig); /************************************* Code ***********************************/ /* @@ -77,13 +81,13 @@ int ejLexOpenScript(ej_t* ep, char_t *script) if (ringqOpen(&ip->tokbuf, EJ_INC, -1) < 0) { return -1; } - if (ringqOpen(&ip->script, EJ_INC, -1) < 0) { + if (ringqOpen(&ip->script, EJ_SCRIPT_INC, -1) < 0) { return -1; } /* * Put the Ejscript into a ring queue for easy parsing */ - ringqPutstr(&ip->script, script); + ringqPutStr(&ip->script, script); ip->lineNumber = 1; ip->lineLength = 0; @@ -178,6 +182,7 @@ void ejLexFreeInputState(ej_t* ep, ejinput_t* state) { if (state->putBackToken) { bfree(B_L, state->putBackToken); + state->putBackToken = NULL; } } @@ -189,7 +194,7 @@ void ejLexFreeInputState(ej_t* ep, ejinput_t* state) int ejLexGetToken(ej_t* ep, int state) { ep->tid = getLexicalToken(ep, state); - goahead_trace(7, T("ejGetToken: %d, \"%s\"\n"), ep->tid, ep->token); + trace(9, T("ejGetToken: %d, \"%s\"\n"), ep->tid, ep->token); return ep->tid; } @@ -202,7 +207,7 @@ static int getLexicalToken(ej_t* ep, int state) { ringq_t *inq, *tokq; ejinput_t* ip; - int done, tid, c, quote, style, back_quoted, lval, i; + int done, tid, c, quote, style; a_assert(ep); ip = ep->input; @@ -218,7 +223,7 @@ static int getLexicalToken(ej_t* ep, int state) ringqFlush(tokq); if (ip->putBackTokenId > 0) { - ringqPutstr(tokq, ip->putBackToken); + ringqPutStr(tokq, ip->putBackToken); tid = ip->putBackTokenId; ip->putBackTokenId = 0; ep->token = (char_t*) tokq->servp; @@ -385,7 +390,7 @@ static int getLexicalToken(ej_t* ep, int state) inputPutback(ep, c); return TOK_ASSIGNMENT; - case '!': /* "!=" */ + case '!': /* "!=" or "!"*/ if ((c = inputGetc(ep)) < 0) { ejError(ep, T("Syntax Error")); return TOK_ERR; @@ -394,8 +399,9 @@ static int getLexicalToken(ej_t* ep, int state) tokenAddChar(ep, EXPR_NOTEQ); return TOK_EXPR; } - tokenAddChar(ep, COND_NOT); - return TOK_LOGICAL; + inputPutback(ep, c); + tokenAddChar(ep, EXPR_BOOL_COMP); + return TOK_EXPR; case ';': tokenAddChar(ep, c); @@ -428,25 +434,23 @@ static int getLexicalToken(ej_t* ep, int state) ejError(ep, T("Syntax Error")); return TOK_ERR; } - back_quoted = 0; + while (c != quote) { - if (c == '\\' && !back_quoted) { - back_quoted++; - } else if (back_quoted) { - if (gisdigit((char_t) c)) { - lval = 0; - for (i = 0; i < 3; i++) { - if ('0' <= c && c <= '7') { - break; - } - lval = lval * 8 + c; - if ((c = inputGetc(ep)) < 0) { - break; - } - } - c = (int) lval; +/* + * check for escape sequence characters + */ + if (c == '\\') { + c = inputGetc(ep); + + if (gisdigit(c)) { +/* + * octal support, \101 maps to 65 = 'A'. put first char + * back so converter will work properly. + */ + inputPutback(ep, c); + c = charConvert(ep, OCTAL, 3); - } else if (back_quoted) { + } else { switch (c) { case 'n': c = '\n'; break; @@ -459,37 +463,28 @@ static int getLexicalToken(ej_t* ep, int state) case 't': c = '\t'; break; case 'x': - lval = 0; - for (i = 0; i < 2; i++) { - if (! gisxdigit((char_t) c)) { - break; - } - lval = lval * 16 + c; - if ((c = inputGetc(ep)) < 0) { - break; - } - } - c = (int) lval; +/* + * hex support, \x41 maps to 65 = 'A' + */ + c = charConvert(ep, HEX, 2); break; case 'u': - lval = 0; - for (i = 0; i < 4; i++) { - if (! gisxdigit((char_t) c)) { - break; - } - lval = lval * 16 + c; - if ((c = inputGetc(ep)) < 0) { - break; - } - } - c = (int) lval; +/* + * unicode support, \x0401 maps to 65 = 'A' + */ + c = charConvert(ep, HEX, 2); + c = c*16 + charConvert(ep, HEX, 2); + break; case '\'': case '\"': + case '\\': break; + default: + ejError(ep, T("Invalid Escape Sequence")); + return TOK_ERR; } } - back_quoted = 0; if (tokenAddChar(ep, c) < 0) { return TOK_ERR; } @@ -513,7 +508,7 @@ static int getLexicalToken(ej_t* ep, int state) } if ((c = inputGetc(ep)) < 0) break; - } while (gisdigit((char_t) c)); + } while (gisdigit(c)); inputPutback(ep, c); return TOK_LITERAL; @@ -521,21 +516,19 @@ static int getLexicalToken(ej_t* ep, int state) /* * Identifiers or a function names */ - back_quoted = 0; while (1) { - if (c == '\\' && !back_quoted) { - back_quoted++; - } else { - back_quoted = 0; - if (tokenAddChar(ep, c) < 0) { + if (c == '\\') { +/* + * just ignore any \ characters. + */ + } else if (tokenAddChar(ep, c) < 0) { break; - } } if ((c = inputGetc(ep)) < 0) { break; } - if (!back_quoted && (!gisalnum((char_t) c) && c != '$' && - c != '_')) { + if (!gisalnum(c) && c != '$' && c != '_' && + c != '\\') { break; } } @@ -558,13 +551,16 @@ static int getLexicalToken(ej_t* ep, int state) } else if (gstrcmp(ep->token, T("for")) == 0) { return TOK_FOR; } else if (gstrcmp(ep->token, T("return")) == 0) { + if ((c == ';') || (c == '(')) { + inputPutback(ep, c); + } return TOK_RETURN; } } -/* - * skip white space after token to find out whether this is - * a function or not. +/* + * Skip white space after token to find out whether this is + * a function or not. */ while (c == ' ' || c == '\t' || c == '\r' || c == '\n') { if ((c = inputGetc(ep)) < 0) @@ -677,3 +673,41 @@ static void inputPutback(ej_t* ep, int c) } /******************************************************************************/ +/* + * Convert a hex or octal character back to binary, return original char if + * not a hex digit + */ + +static int charConvert(ej_t* ep, int base, int maxDig) +{ + int i, c, lval, convChar; + + lval = 0; + for (i = 0; i < maxDig; i++) { + if ((c = inputGetc(ep)) < 0) { + break; + } +/* + * Initialize to out of range value + */ + convChar = base; + if (gisdigit(c)) { + convChar = c - '0'; + } else if (c >= 'a' && c <= 'f') { + convChar = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + convChar = c - 'A' + 10; + } +/* + * if unexpected character then return it to buffer. + */ + if (convChar >= base) { + inputPutback(ep, c); + break; + } + lval = (lval * base) + convChar; + } + return lval; +} + +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/ejparse.c b/c/src/libnetworking/rtems_webserver/ejparse.c index 1734f6f719..8863c069ea 100644 --- a/c/src/libnetworking/rtems_webserver/ejparse.c +++ b/c/src/libnetworking/rtems_webserver/ejparse.c @@ -1,7 +1,7 @@ /* * ejparse.c -- Ejscript(TM) Parser * - * Copyright (c) Go Ahead Software, Inc., 1995-1999 + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -15,7 +15,11 @@ /********************************** Includes **********************************/ -#include "ej.h" +#include "ejIntrn.h" + +#if CE + #include "CE/wincompat.h" +#endif /********************************** Local Data ********************************/ @@ -24,11 +28,14 @@ int ejMax = -1; /* Maximum size of */ /****************************** Forward Declarations **************************/ +#ifndef B_STATS +#define setString(a,b,c) setstring(b,c) +#endif + static ej_t *ejPtr(int eid); static void clearString(char_t **ptr); -static void setString(char_t **ptr, char_t *s); +static void setString(B_ARGS_DEC, char_t **ptr, char_t *s); static void appendString(char_t **ptr, char_t *s); -static void freeVar(sym_t* sp); static int parse(ej_t *ep, int state, int flags); static int parseStmt(ej_t *ep, int state, int flags); static int parseDeclaration(ej_t *ep, int state, int flags); @@ -39,6 +46,7 @@ static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs); static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs); static int evalFunction(ej_t *ep); static void freeFunc(ejfunc_t *func); +static void ejRemoveNewlines(ej_t *ep, int state); /************************************* Code ***********************************/ /* @@ -57,8 +65,8 @@ int ejOpenEngine(sym_fd_t variables, sym_fd_t functions) ep->eid = eid; /* - * Create a top level symbol table if one is not provided for variables and - * functions. Variables may create other symbol tables for block level + * Create a top level symbol table if one is not provided for variables and + * functions. Variables may create other symbol tables for block level * declarations so we use hAlloc to manage a list of variable tables. */ if ((vid = hAlloc((void***) &ep->variables)) < 0) { @@ -72,7 +80,6 @@ int ejOpenEngine(sym_fd_t variables, sym_fd_t functions) if (variables == -1) { ep->variables[vid] = symOpen(64) + EJ_OFFSET; ep->flags |= FLAGS_VARIABLES; - } else { ep->variables[vid] = variables + EJ_OFFSET; } @@ -122,36 +129,27 @@ void ejCloseEngine(int eid) ejLexClose(ep); - if (ep->flags & FLAGS_VARIABLES) { - for (i = ep->variableMax - 1; i >= 0; i--) { - symClose(ep->variables[i] - EJ_OFFSET, freeVar); - ep->variableMax = hFree((void***) &ep->variables, i); + for (i = ep->variableMax - 1; i >= 0; i--) { + if (ep->flags & FLAGS_VARIABLES) { + symClose(ep->variables[i] - EJ_OFFSET); } + ep->variableMax = hFree((void***) &ep->variables, i); } + if (ep->flags & FLAGS_FUNCTIONS) { - symClose(ep->functions, freeVar); + symClose(ep->functions); } ejMax = hFree((void***) &ejHandles, ep->eid); bfree(B_L, ep); } -/******************************************************************************/ -/* - * Callback from symClose. Free the variable. - */ - -static void freeVar(sym_t* sp) -{ - valueFree(&sp->content); -} - +#ifndef __NO_EJ_FILE /******************************************************************************/ /* * Evaluate a Ejscript file */ -#if DEV char_t *ejEvalFile(int eid, char_t *path, char_t **emsg) { gstat_t sbuf; @@ -165,6 +163,7 @@ char_t *ejEvalFile(int eid, char_t *path, char_t **emsg) if (emsg) { *emsg = NULL; } + if ((ep = ejPtr(eid)) == NULL) { return NULL; } @@ -173,26 +172,30 @@ char_t *ejEvalFile(int eid, char_t *path, char_t **emsg) ejError(ep, T("Bad handle %d"), eid); return NULL; } + if (gstat(path, &sbuf) < 0) { - close(fd); + gclose(fd); ejError(ep, T("Cant stat %s"), path); return NULL; } + if ((fileBuf = balloc(B_L, sbuf.st_size + 1)) == NULL) { - close(fd); + gclose(fd); ejError(ep, T("Cant malloc %d"), sbuf.st_size); return NULL; } - if (read(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) { - close(fd); + + if (gread(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) { + gclose(fd); bfree(B_L, fileBuf); ejError(ep, T("Error reading %s"), path); return NULL; } + fileBuf[sbuf.st_size] = '\0'; - close(fd); + gclose(fd); - if ((script = ballocAscToUni(fileBuf)) == NULL) { + if ((script = ballocAscToUni(fileBuf, sbuf.st_size)) == NULL) { bfree(B_L, fileBuf); ejError(ep, T("Cant malloc %d"), sbuf.st_size + 1); return NULL; @@ -204,29 +207,31 @@ char_t *ejEvalFile(int eid, char_t *path, char_t **emsg) bfree(B_L, script); return rs; } -#endif +#endif /* __NO_EJ_FILE */ /******************************************************************************/ /* * Create a new variable scope block so that consecutive ejEval calls may * be made with the same varible scope. This space MUST be closed with - * ejCloseBlock when the evaluations are complete. + * ejCloseBlock when the evaluations are complete. */ int ejOpenBlock(int eid) { ej_t *ep; - int vid; + int vid; if((ep = ejPtr(eid)) == NULL) { return -1; - } + } + if ((vid = hAlloc((void***) &ep->variables)) < 0) { return -1; } + if (vid >= ep->variableMax) { ep->variableMax = vid + 1; - } + } ep->variables[vid] = symOpen(64) + EJ_OFFSET; return vid; @@ -241,15 +246,16 @@ int ejOpenBlock(int eid) int ejCloseBlock(int eid, int vid) { ej_t *ep; - + if((ep = ejPtr(eid)) == NULL) { return -1; } - symClose(ep->variables[vid] - EJ_OFFSET, freeVar); + symClose(ep->variables[vid] - EJ_OFFSET); ep->variableMax = hFree((void***) &ep->variables, vid); - return 0; + return 0; } + /******************************************************************************/ /* * Create a new variable scope block and evaluate a script. All variables @@ -263,7 +269,7 @@ char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg) a_assert(script); - vid = ejOpenBlock(eid); + vid = ejOpenBlock(eid); returnVal = ejEval(eid, script, emsg); ejCloseBlock(eid, vid); @@ -272,7 +278,7 @@ char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg) /******************************************************************************/ /* - * Parse and evaluate a Ejscript. The caller may provide a symbol table to + * Parse and evaluate a Ejscript. The caller may provide a symbol table to * use for variables and function definitions. Return char_t pointer on * success otherwise NULL pointer is returned. */ @@ -282,17 +288,21 @@ char_t *ejEval(int eid, char_t *script, char_t **emsg) ej_t *ep; ejinput_t *oldBlock; int state; - + void *endlessLoopTest; + int loopCounter; + + a_assert(script); if (emsg) { *emsg = NULL; - } + } + if ((ep = ejPtr(eid)) == NULL) { return NULL; } - setString(&ep->result, T("")); + setString(B_L, &ep->result, T("")); /* * Allocate a new evaluation block, and save the old one @@ -303,8 +313,29 @@ char_t *ejEval(int eid, char_t *script, char_t **emsg) /* * Do the actual parsing and evaluation */ + loopCounter = 0; + endlessLoopTest = NULL; + do { state = parse(ep, STATE_BEGIN, FLAGS_EXE); + + if (state == STATE_RET) { + state = STATE_EOF; + } +/* + * prevent parser from going into infinite loop. If parsing the same + * line 10 times then fail and report Syntax error. Most normal error + * are caught in the parser itself. + */ + if (endlessLoopTest == ep->input->script.servp) { + if (loopCounter++ > 10) { + state = STATE_ERR; + ejError(ep, T("Syntax error")); + } + } else { + endlessLoopTest = ep->input->script.servp; + loopCounter = 0; + } } while (state != STATE_EOF && state != STATE_ERR); ejLexCloseScript(ep); @@ -324,9 +355,11 @@ char_t *ejEval(int eid, char_t *script, char_t **emsg) if (state == STATE_EOF) { return ep->result; } + if (state == STATE_ERR) { return NULL; } + return ep->result; } @@ -344,12 +377,25 @@ static int parse(ej_t *ep, int state, int flags) * Any statement, function arguments or conditional expressions */ case STATE_STMT: + if ((state = parseStmt(ep, state, flags)) != STATE_STMT_DONE && + state != STATE_EOF && state != STATE_STMT_BLOCK_DONE && + state != STATE_RET) { + state = STATE_ERR; + } + break; + case STATE_DEC: - state = parseStmt(ep, state, flags); + if ((state = parseStmt(ep, state, flags)) != STATE_DEC_DONE && + state != STATE_EOF) { + state = STATE_ERR; + } break; case STATE_EXPR: - state = parseStmt(ep, state, flags); + if ((state = parseStmt(ep, state, flags)) != STATE_EXPR_DONE && + state != STATE_EOF) { + state = STATE_ERR; + } break; /* @@ -397,9 +443,9 @@ static int parseStmt(ej_t *ep, int state, int flags) ejfunc_t func; ejfunc_t *saveFunc; ejinput_t condScript, endScript, bodyScript, incrScript; - char_t *value; - char_t *identifier; + char_t *value, *identifier; int done, expectSemi, thenFlags, elseFlags, tid, cond, forFlags; + int ejVarType; a_assert(ep); @@ -451,21 +497,22 @@ static int parseStmt(ej_t *ep, int state, int flags) * This could either be a reference to a variable or an assignment */ identifier = NULL; - setString(&identifier, ep->token); + setString(B_L, &identifier, ep->token); /* * Peek ahead to see if this is an assignment */ tid = ejLexGetToken(ep, state); if (tid == TOK_ASSIGNMENT) { if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) { + clearString(&identifier); goto error; } if (flags & FLAGS_EXE) { if ( state == STATE_DEC ) { ejSetLocalVar(ep->eid, identifier, ep->result); - } - else { - if (ejGetVar(ep->eid, identifier, &value) > 0) { + } else { + ejVarType = ejGetVar(ep->eid, identifier, &value); + if (ejVarType > 0) { ejSetLocalVar(ep->eid, identifier, ep->result); } else { ejSetGlobalVar(ep->eid, identifier, ep->result); @@ -475,17 +522,23 @@ static int parseStmt(ej_t *ep, int state, int flags) } else if (tid == TOK_INC_DEC ) { value = NULL; - if ( flags & FLAGS_EXE ) { - if (ejGetVar(ep->eid, identifier, &value) < 0) { + if (flags & FLAGS_EXE) { + ejVarType = ejGetVar(ep->eid, identifier, &value); + if (ejVarType < 0) { ejError(ep, T("Undefined variable %s\n"), identifier); goto error; } - setString(&ep->result, value); + setString(B_L, &ep->result, value); if (evalExpr(ep, value, (int) *ep->token, T("1")) < 0) { state = STATE_ERR; break; } - ejSetGlobalVar(ep->eid, identifier, ep->result); + + if (ejVarType > 0) { + ejSetLocalVar(ep->eid, identifier, ep->result); + } else { + ejSetGlobalVar(ep->eid, identifier, ep->result); + } } } else { @@ -495,7 +548,7 @@ static int parseStmt(ej_t *ep, int state, int flags) value = NULL; if (state == STATE_DEC) { if (ejGetVar(ep->eid, identifier, &value) > 0) { - ejError(ep, T("Variable already declared"), + ejError(ep, T("Variable already declared"), identifier); clearString(&identifier); goto error; @@ -504,14 +557,14 @@ static int parseStmt(ej_t *ep, int state, int flags) } else { if ( flags & FLAGS_EXE ) { if (ejGetVar(ep->eid, identifier, &value) < 0) { - ejError(ep, T("Undefined variable %s\n"), + ejError(ep, T("Undefined variable %s\n"), identifier); clearString(&identifier); goto error; } } } - setString(&ep->result, value); + setString(B_L, &ep->result, value); ejLexPutbackToken(ep, tid, ep->token); } clearString(&identifier); @@ -526,7 +579,7 @@ static int parseStmt(ej_t *ep, int state, int flags) /* * Set the result to the literal (number or string constant) */ - setString(&ep->result, ep->token); + setString(B_L, &ep->result, ep->token); if (state == STATE_STMT) { expectSemi++; } @@ -541,10 +594,10 @@ static int parseStmt(ej_t *ep, int state, int flags) saveFunc = ep->func; } memset(&func, 0, sizeof(ejfunc_t)); - setString(&func.fname, ep->token); + setString(B_L, &func.fname, ep->token); ep->func = &func; - setString(&ep->result, T("")); + setString(B_L, &ep->result, T("")); if (ejLexGetToken(ep, state) != TOK_LPAREN) { freeFunc(&func); goto error; @@ -604,11 +657,20 @@ static int parseStmt(ej_t *ep, int state, int flags) elseFlags = flags; } /* - * Process the "then" case + * Process the "then" case. Allow for RETURN statement */ - if (parse(ep, STATE_STMT, thenFlags) != STATE_STMT_DONE) { + switch (parse(ep, STATE_STMT, thenFlags)) { + case STATE_RET: + return STATE_RET; + case STATE_STMT_DONE: + break; + default: goto error; } +/* + * check to see if there is an "else" case + */ + ejRemoveNewlines(ep, state); tid = ejLexGetToken(ep, state); if (tid != TOK_ELSE) { ejLexPutbackToken(ep, tid, ep->token); @@ -616,9 +678,14 @@ static int parseStmt(ej_t *ep, int state, int flags) break; } /* - * Process the "else" case + * Process the "else" case. Allow for return. */ - if (parse(ep, STATE_STMT, elseFlags) != STATE_STMT_DONE) { + switch (parse(ep, STATE_STMT, elseFlags)) { + case STATE_RET: + return STATE_RET; + case STATE_STMT_DONE: + break; + default: goto error; } done++; @@ -650,7 +717,7 @@ static int parseStmt(ej_t *ep, int state, int flags) } /* - * The first time through, we save the current input context just + * The first time through, we save the current input context just * to each step: prior to the conditional, the loop increment and the * loop body. */ @@ -663,7 +730,7 @@ static int parseStmt(ej_t *ep, int state, int flags) if (ejLexGetToken(ep, state) != TOK_SEMI) { goto error; } - + /* * Don't execute the loop increment statement or the body first time */ @@ -677,7 +744,7 @@ static int parseStmt(ej_t *ep, int state, int flags) } /* - * Parse the body and remember the end of the body script + * Parse the body and remember the end of the body script */ ejLexSaveInputState(ep, &bodyScript); if (parse(ep, STATE_STMT, forFlags) != STATE_STMT_DONE) { @@ -693,7 +760,13 @@ static int parseStmt(ej_t *ep, int state, int flags) * Evaluate the body */ ejLexRestoreInputState(ep, &bodyScript); - if (parse(ep, STATE_STMT, flags) != STATE_STMT_DONE) { + + switch (parse(ep, STATE_STMT, flags)) { + case STATE_RET: + return STATE_RET; + case STATE_STMT_DONE: + break; + default: goto error; } /* @@ -760,6 +833,13 @@ static int parseStmt(ej_t *ep, int state, int flags) state = parse(ep, STATE_STMT, flags); } while (state == STATE_STMT_DONE); +/* + * Allow return statement. + */ + if (state == STATE_RET) { + return state; + } + if (ejLexGetToken(ep, state) != TOK_RBRACE) { goto error; } @@ -779,7 +859,7 @@ static int parseStmt(ej_t *ep, int state, int flags) if (flags & FLAGS_EXE) { while ( ejLexGetToken(ep, state) != TOK_EOF ); done++; - return STATE_EOF; + return STATE_RET; } break; } @@ -794,10 +874,7 @@ static int parseStmt(ej_t *ep, int state, int flags) /* * Skip newline after semi-colon */ - tid = ejLexGetToken(ep, state); - if (tid != TOK_NEWLINE) { - ejLexPutbackToken(ep, tid, ep->token); - } + ejRemoveNewlines(ep, state); } /* @@ -810,6 +887,7 @@ doneParse: ejLexFreeInputState(ep, &endScript); ejLexFreeInputState(ep, &bodyScript); } + if (state == STATE_STMT) { return STATE_STMT_DONE; } else if (state == STATE_DEC) { @@ -928,7 +1006,7 @@ static int parseCond(ej_t *ep, int state, int flags) a_assert(ep); - setString(&ep->result, T("")); + setString(B_L, &ep->result, T("")); rhs = lhs = NULL; operator = 0; @@ -942,14 +1020,15 @@ static int parseCond(ej_t *ep, int state, int flags) state = STATE_ERR; break; } + if (operator > 0) { - setString(&rhs, ep->result); + setString(B_L, &rhs, ep->result); if (evalCond(ep, lhs, operator, rhs) < 0) { state = STATE_ERR; break; } } - setString(&lhs, ep->result); + setString(B_L, &lhs, ep->result); tid = ejLexGetToken(ep, state); if (tid == TOK_LOGICAL) { @@ -969,6 +1048,7 @@ static int parseCond(ej_t *ep, int state, int flags) if (lhs) { bfree(B_L, lhs); } + if (rhs) { bfree(B_L, rhs); } @@ -987,33 +1067,46 @@ static int parseExpr(ej_t *ep, int state, int flags) a_assert(ep); - setString(&ep->result, T("")); + setString(B_L, &ep->result, T("")); rhs = lhs = NULL; rel = 0; + tid = 0; do { /* * This loop will handle an entire expression list. We call parse * to evalutate each term which returns the result in ep->result. */ - state = parse(ep, STATE_EXPR, flags); - if (state != STATE_EXPR_DONE) { - state = STATE_ERR; - break; - } - if (rel > 0) { - setString(&rhs, ep->result); - if (evalExpr(ep, lhs, rel, rhs) < 0) { + if (tid == TOK_LOGICAL) { + if ((state = parse(ep, STATE_RELEXP, flags)) != STATE_RELEXP_DONE) { + state = STATE_ERR; + break; + } + } else { + if ((state = parse(ep, STATE_EXPR, flags)) != STATE_EXPR_DONE) { state = STATE_ERR; break; } } - setString(&lhs, ep->result); - if ((tid = ejLexGetToken(ep, state)) == TOK_EXPR) { - rel = (int) *ep->token; + if (rel > 0) { + setString(B_L, &rhs, ep->result); + if (tid == TOK_LOGICAL) { + if (evalCond(ep, lhs, rel, rhs) < 0) { + state = STATE_ERR; + break; + } + } else { + if (evalExpr(ep, lhs, rel, rhs) < 0) { + state = STATE_ERR; + break; + } + } + } + setString(B_L, &lhs, ep->result); - } else if (tid == TOK_INC_DEC) { + if ((tid = ejLexGetToken(ep, state)) == TOK_EXPR || + tid == TOK_INC_DEC || tid == TOK_LOGICAL) { rel = (int) *ep->token; } else { @@ -1023,10 +1116,14 @@ static int parseExpr(ej_t *ep, int state, int flags) } while (state == STATE_EXPR_DONE); - if (rhs) + if (rhs) { bfree(B_L, rhs); - if (lhs) + } + + if (lhs) { bfree(B_L, lhs); + } + return state; } @@ -1045,7 +1142,7 @@ static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs) a_assert(rel > 0); lval = 0; - if (gisdigit(*lhs) && gisdigit(*rhs)) { + if (gisdigit((int)*lhs) && gisdigit((int)*rhs)) { l = gatoi(lhs); r = gatoi(rhs); switch (rel) { @@ -1060,15 +1157,15 @@ static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs) return -1; } } else { - if (!gisdigit(*lhs)) { + if (!gisdigit((int)*lhs)) { ejError(ep, T("Conditional must be numeric"), lhs); } else { ejError(ep, T("Conditional must be numeric"), rhs); } } - + stritoa(lval, buf, sizeof(buf)); - setString(&ep->result, buf); + setString(B_L, &ep->result, buf); return 0; } @@ -1091,19 +1188,21 @@ static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs) */ numeric = 1; for (cp = lhs; *cp; cp++) { - if (!gisdigit(*cp)) { + if (!gisdigit((int)*cp)) { numeric = 0; break; } } + if (numeric) { for (cp = rhs; *cp; cp++) { - if (!gisdigit(*cp)) { + if (!gisdigit((int)*cp)) { numeric = 0; break; } } } + if (numeric) { l = gatoi(lhs); r = gatoi(rhs); @@ -1161,6 +1260,9 @@ static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs) case EXPR_GREATEREQ: lval = (l >= r) ? 1 : 0; break; + case EXPR_BOOL_COMP: + lval = (r == 0) ? 1 : 0; + break; default: ejError(ep, T("Bad operator %d"), rel); return -1; @@ -1205,7 +1307,7 @@ static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs) } stritoa(lval, buf, sizeof(buf)); - setString(&ep->result, buf); + setString(B_L, &ep->result, buf); return 0; } @@ -1223,13 +1325,14 @@ static int evalFunction(ej_t *ep) ejError(ep, T("Undefined procedure %s"), ep->func->fname); return -1; } + fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer; if (fn == NULL) { ejError(ep, T("Undefined procedure %s"), ep->func->fname); return -1; } - return (*fn)(ep->eid, (void*) ep->userHandle, ep->func->nArgs, + return (*fn)(ep->eid, (void*) ep->userHandle, ep->func->nArgs, ep->func->args); } @@ -1250,12 +1353,11 @@ void ejError(ej_t* ep, char_t* fmt, ...) va_start(args, fmt); msgbuf = NULL; - gvsnprintf(&msgbuf, E_MAX_ERROR, fmt, args); + fmtValloc(&msgbuf, E_MAX_ERROR, fmt, args); va_end(args); if (ep && ip) { - errbuf = NULL; - gsnprintf(&errbuf, E_MAX_ERROR, T("%s\n At line %d, line => \n\n%s\n"), + fmtAlloc(&errbuf, E_MAX_ERROR, T("%s\n At line %d, line => \n\n%s\n"), msgbuf, ip->lineNumber, ip->line); bfreeSafe(B_L, ep->error); ep->error = errbuf; @@ -1283,14 +1385,14 @@ static void clearString(char_t **ptr) * Set a string value */ -static void setString(char_t **ptr, char_t *s) +static void setString(B_ARGS_DEC, char_t **ptr, char_t *s) { a_assert(ptr); if (*ptr) { - bfree(B_L, *ptr); + bfree(B_ARGS, *ptr); } - *ptr = bstrdup(B_L, s); + *ptr = bstrdup(B_ARGS, s); } /******************************************************************************/ @@ -1319,7 +1421,7 @@ static void appendString(char_t **ptr, char_t *s) * Define a function */ -int ejSetGlobalFunction(int eid, char_t *name, +int ejSetGlobalFunction(int eid, char_t *name, int (*fn)(int eid, void *handle, int argc, char_t **argv)) { ej_t *ep; @@ -1335,7 +1437,7 @@ int ejSetGlobalFunction(int eid, char_t *name, * Define a function directly into the function symbol table. */ -int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, +int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, int (*fn)(int eid, void *handle, int argc, char_t **argv)) { if (symEnter(functions, name, valueInteger((long) fn), 0) == NULL) { @@ -1344,6 +1446,21 @@ int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, return 0; } +/******************************************************************************/ +/* + * Remove ("undefine") a function + */ + +int ejRemoveGlobalFunction(int eid, char_t *name) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return symDelete(ep->functions, name); +} + /******************************************************************************/ /* * Get a function definition @@ -1358,6 +1475,7 @@ void *ejGetGlobalFunction(int eid, char_t *name) if ((ep = ejPtr(eid)) == NULL) { return NULL; } + if ((sp = symLookup(ep->functions, name)) != NULL) { fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer; return (void*) fn; @@ -1367,7 +1485,7 @@ void *ejGetGlobalFunction(int eid, char_t *name) /******************************************************************************/ /* - * Utility routine to crack Ejscript arguments. Return the number of args + * Utility routine to crack Ejscript arguments. Return the number of args * seen. This routine only supports %s and %d type args. * * Typical usage: @@ -1477,7 +1595,7 @@ void ejSetResult(int eid, char_t *s) if ((ep = ejPtr(eid)) == NULL) { return; } - setString(&ep->result, s); + setString(B_L, &ep->result, s); } /******************************************************************************/ @@ -1511,6 +1629,7 @@ void ejSetVar(int eid, char_t *var, char_t *value) if ((ep = ejPtr(eid)) == NULL) { return; } + if (value == NULL) { v = valueString(value, 0); } else { @@ -1535,6 +1654,7 @@ void ejSetLocalVar(int eid, char_t *var, char_t *value) if ((ep = ejPtr(eid)) == NULL) { return; } + if (value == NULL) { v = valueString(value, 0); } else { @@ -1545,7 +1665,7 @@ void ejSetLocalVar(int eid, char_t *var, char_t *value) /******************************************************************************/ /* - * Set a global variable. Note: a variable with a value of NULL means + * Set a global variable. Note: a variable with a value of NULL means * declared but undefined. The value is defined in the global variable frame. */ @@ -1559,6 +1679,7 @@ void ejSetGlobalVar(int eid, char_t *var, char_t *value) if ((ep = ejPtr(eid)) == NULL) { return; } + if (value == NULL) { v = valueString(value, 0); } else { @@ -1585,19 +1706,19 @@ int ejGetVar(int eid, char_t *var, char_t **value) return -1; } - for (i = ep->variableMax - 1; i >= 0; i--) { - if ((sp = symLookup(ep->variables[i] - EJ_OFFSET, var)) == NULL) { - continue; + i = ep->variableMax - 1; + if ((sp = symLookup(ep->variables[i] - EJ_OFFSET, var)) == NULL) { + i = 0; + if ((sp = symLookup(ep->variables[0] - EJ_OFFSET, var)) == NULL) { + return -1; } - a_assert(sp->content.type == string); - *value = sp->content.value.string; - return i; } - return -1; + a_assert(sp->content.type == string); + *value = sp->content.value.string; + return i; } /******************************************************************************/ -#if UNUSED /* * Get the variable symbol table */ @@ -1609,9 +1730,9 @@ sym_fd_t ejGetVariableTable(int eid) if ((ep = ejPtr(eid)) == NULL) { return -1; } - return ep->variables; + return *ep->variables; } -#endif + /******************************************************************************/ /* * Get the functions symbol table @@ -1640,6 +1761,7 @@ static void freeFunc(ejfunc_t *func) bfree(B_L, func->args[i]); func->nArgs = hFree((void***) &func->args, i); } + if (func->fname) { bfree(B_L, func->fname); func->fname = NULL; @@ -1647,7 +1769,7 @@ static void freeFunc(ejfunc_t *func) } /******************************************************************************/ -/* +/* * Get Ejscript pointer */ @@ -1663,3 +1785,18 @@ static ej_t *ejPtr(int eid) } /******************************************************************************/ +/* + * This function removes any new lines. Used for else cases, etc. + */ +static void ejRemoveNewlines(ej_t *ep, int state) +{ + int tid; + + do { + tid = ejLexGetToken(ep, state); + } while (tid == TOK_NEWLINE); + + ejLexPutbackToken(ep, tid, ep->token); +} + +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/emfdb.c b/c/src/libnetworking/rtems_webserver/emfdb.c new file mode 100644 index 0000000000..a316c1a454 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/emfdb.c @@ -0,0 +1,1050 @@ +/* + * emfdb.c -- EMF database compatability functions for GoAhead WebServer. + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ + */ + +/******************************** Description *********************************/ +/* + * Textfile-based database support for WebServer 2.1. + */ + +/********************************* Includes ***********************************/ + +#include "emfdb.h" +#include "wsIntrn.h" + +/********************************* Defines ************************************/ + +#define KEYWORD_TABLE T("TABLE") +#define KEYWORD_ROW T("ROW") + +/*********************************** Locals ***********************************/ + +/* + * Variable to support the basicSet and basicGet functions. + */ + +static char_t *basicProdDir = NULL; +static char_t *basicDefaultDir = T("."); /* Default set to current */ + +/* + * hAlloc chain list of table schemas to be closed + */ + +static int dbMaxTables = 0; +static dbTable_t **dbListTables = NULL; + +/****************************** Forward Declarations **************************/ + +static int crack(char_t *buf, char_t **key, char_t **val); +static char_t *trim(char_t *str); +static int GetColumnIndex(int tid, char_t *colName); + +/******************************************************************************/ +/* + * Add a schema to the module-internal schema database + */ + +int dbRegisterDBSchema(dbTable_t *pTableRegister) +{ + dbTable_t *pTable; + int tid; + + a_assert(pTableRegister); + + trace(4, T("DB: Registering database table <%s>\n"), + pTableRegister->name); + +/* + * Bump up the size of the table array + */ + tid = hAllocEntry((void***) &dbListTables, + &dbMaxTables, sizeof(dbTable_t)); + +/* + * Copy the table schema to the last spot in schema array + */ + a_assert(dbListTables); + pTable = dbListTables[tid]; + a_assert(pTable); + +/* + * Copy the name of the table + */ + pTable->name = bstrdup(B_L, pTableRegister->name); + +/* + * Copy the number of columns + */ + pTable->nColumns = pTableRegister->nColumns; + +/* + * Copy the column definitions + */ + if (pTable->nColumns > 0) { + int i; + pTable->columnNames = balloc(B_L, sizeof(char_t *) * pTable->nColumns); + pTable->columnTypes = balloc(B_L, sizeof(int *) * pTable->nColumns); + + for (i = 0; (i < pTableRegister->nColumns); i++) { + pTable->columnNames[i] = + bstrdup(B_L, pTableRegister->columnNames[i]); + pTable->columnTypes[i] = pTableRegister->columnTypes[i]; + } + + } else { + pTable->columnNames = NULL; + pTable->columnTypes = NULL; + } + +/* + * Zero out the table's data (very important!) + */ + pTable->nRows = 0; + pTable->rows = NULL; + + return 0; +} + +/******************************************************************************/ +/* + * This is provided for compatibility with EMF. Tables are "registered" + * with staticly defined schemas. There is only one did in this package: 0. + */ + +int dbOpen(char_t *tablename, char_t *filename, + int (*gettime)(int did), int flags) +{ + basicProdDir = NULL; + basicDefaultDir = T("."); + dbMaxTables = 0; + dbListTables = NULL; + return 0; +} + +/******************************************************************************/ +/* + * Delete all the rows of the tables, and all of the tables + */ + +void dbClose(int did) +{ + int table, column; + dbTable_t *pTable; + +/* + * Before doing anything, delete all the contents of the database + */ + dbZero(did); + +/* + * Now delete the tables + */ + for (table = 0; table < dbMaxTables; table++) { + pTable = dbListTables[table]; + + if (pTable != NULL) { +/* + * Delete the table schema + */ + if (pTable->nColumns) { + for (column = 0; column < pTable->nColumns; column++) { + bfreeSafe(B_L, pTable->columnNames[column]); + } + bfreeSafe(B_L, pTable->columnNames); + bfreeSafe(B_L, pTable->columnTypes); + } +/* + * Delete the table name + */ + bfreeSafe(B_L, pTable->name); +/* + * Free the table + */ + bfreeSafe(B_L, pTable); + hFree((void ***) &dbListTables, table); + } + } + + if (dbListTables) { + bfree(B_L, dbListTables); + } + +/* + * Set the global table list to a safe value + */ + dbListTables = NULL; + dbMaxTables = 0; +} + + +/******************************************************************************/ +/* + * Delete all the data records in all tables + */ + +void dbZero(int did) +{ + int table, row, column, nRows, nColumns; + int *pRow; + dbTable_t *pTable; + +/* + * Delete all data from all tables + */ + for (table = 0; table < dbMaxTables; table++) { + pTable = dbListTables[table]; +/* + * Delete the row data contained within the schema + */ + if (pTable) { + nColumns = pTable->nColumns; + nRows = pTable->nRows; + for (row = 0; row < nRows; row++) { + pRow = pTable->rows[row]; + if (pRow) { +/* + * Only delete the contents of rows not previously deleted! + */ + for (column = 0; column < nColumns; column++) { + if (pTable->columnTypes[column] == T_STRING) { + bfreeSafe(B_L, (char_t *)(pRow[column])); + pRow[column] = (int)NULL; + } + } + + bfreeSafe(B_L, pRow); + hFree((void ***) &pTable->rows, row); + } + } + + pTable->rows = NULL; + pTable->nRows = 0; + } + } +} + +/******************************************************************************/ +/* + * Find the a row in the table with the given string in the given column + */ + +int dbSearchStr(int did, char_t *tablename, + char_t *colName, char_t *value, int flags) +{ + int tid, nRows, nColumns, column; + dbTable_t *pTable; + + a_assert(tablename); + a_assert(colName); + a_assert(value); + + tid = dbGetTableId(0, tablename); + a_assert(tid >= 0); + + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + pTable = dbListTables[tid]; + } else { + return DB_ERR_TABLE_NOT_FOUND; + } + + nColumns = pTable->nColumns; + nRows = pTable->nRows; + column = GetColumnIndex(tid, colName); + a_assert (column >= 0); + + if (column >= 0) { + char_t *compareVal; + int row, *pRow; +/* + * Scan through rows until we find a match. + * Note that some of these rows may be deleted! + */ + row = 0; + while (row < nRows) { + pRow = pTable->rows[row]; + if (pRow) { + compareVal = (char_t *)(pRow[column]); + if (compareVal && (gstrcmp(compareVal, value) == 0)) { + return row; + } + } + row++; + } + } else { +/* + * Return -2 if search column was not found + */ + trace(3, T("DB: Unable to find column <%s> in table <%s>\n"), + colName, tablename); + return DB_ERR_COL_NOT_FOUND; + } + + return -1; +} + +/******************************************************************************/ +/* + * Add a new row to the given table. Return the new row ID. + */ + +int dbAddRow(int did, char_t *tablename) +{ + int tid, size; + dbTable_t *pTable; + + a_assert(tablename); + + tid = dbGetTableId(0, tablename); + a_assert(tid >= 0); + + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + pTable = dbListTables[tid]; + } else { + return DB_ERR_TABLE_NOT_FOUND; + } + + a_assert(pTable); + + if (pTable) { + trace(5, T("DB: Adding a row to table <%s>\n"), tablename); + + size = pTable->nColumns * max(sizeof(int), sizeof(char_t *)); + return hAllocEntry((void***) &(pTable->rows), &(pTable->nRows), size); + } + + return -1; +} + +/******************************************************************************/ +/* + * Delete a row in the table. + */ + +int dbDeleteRow(int did, char_t *tablename, int row) +{ + int tid, nColumns, nRows; + dbTable_t *pTable; + + a_assert(tablename); + tid = dbGetTableId(0, tablename); + a_assert(tid >= 0); + + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + pTable = dbListTables[tid]; + } else { + return DB_ERR_TABLE_NOT_FOUND; + } + + nColumns = pTable->nColumns; + nRows = pTable->nRows; + + if ((row >= 0) && (row < nRows)) { + int *pRow = pTable->rows[row]; + + if (pRow) { + int column = 0; +/* + * Free up any allocated strings + */ + while (column < nColumns) { + if (pRow[column] && + (pTable->columnTypes[column] == T_STRING)) { + bfree(B_L, (char_t *)pRow[column]); + } + + column++; + } +/* + * Zero out the row for safety + */ + memset(pRow, 0, nColumns * max(sizeof(int), sizeof(char_t *))); + + bfreeSafe(B_L, pRow); + pTable->nRows = hFree((void ***)&pTable->rows, row); + trace(5, T("DB: Deleted row <%d> from table <%s>\n"), + row, tablename); + } + return 0; + } else { + trace(3, T("DB: Unable to delete row <%d> from table <%s>\n"), + row, tablename); + } + + return -1; +} + +/*****************************************************************************/ +/* + * Grow the rows in the table to the nominated size. + */ + +int dbSetTableNrow(int did, char_t *tablename, int nNewRows) +{ + int nRet, tid, nRows, nColumns; + dbTable_t *pTable; + + a_assert(tablename); + tid = dbGetTableId(0, tablename); + a_assert(tid >= 0) ; + + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + pTable = dbListTables[tid]; + } else { + return DB_ERR_TABLE_NOT_FOUND; + } + + nRet = -1; + + a_assert(pTable); + if (pTable) { + nColumns = pTable->nColumns; + nRows = pTable->nRows; + nRet = 0; + + if (nRows >= nNewRows) { +/* + * If number of rows already allocated exceeds requested number, do nothing + */ + trace(4, T("DB: Ignoring row set to <%d> in table <%s>\n"), + nNewRows, tablename); + } else { + trace(4, T("DB: Setting rows to <%d> in table <%s>\n"), + nNewRows, tablename); + while (pTable->nRows < nNewRows) { + if (dbAddRow(did, tablename) < 0) { + return -1; + } + } + } + } + + return nRet; +} + +/******************************************************************************/ +/* + * Return the number of rows in the given table + */ + +int dbGetTableNrow(int did, char_t *tablename) +{ + int tid; + + a_assert(tablename); + tid = dbGetTableId(did, tablename); + + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + return (dbListTables[tid])->nRows; + } else { + return -1; + } +} + +/******************************************************************************/ +/* + * Do table driven read of the database + */ + +int dbReadInt(int did, char_t *table, char_t *column, int row, int *returnValue) +{ + int colIndex, *pRow, tid; + dbTable_t *pTable; + + a_assert(table); + a_assert(column); + a_assert(returnValue); + + tid = dbGetTableId(0, table); + a_assert(tid >= 0); + +/* + * Return -6 if table is not found + */ + if (tid < 0) { + return DB_ERR_TABLE_NOT_FOUND; + } + +/* + * Return -7 if table id has been deleted + */ + pTable = dbListTables[tid]; + if (pTable == NULL) { + return DB_ERR_TABLE_DELETED; + } + + a_assert(row >= 0); + + if ((row >= 0) && (row < pTable->nRows)) { + colIndex = GetColumnIndex(tid, column); + a_assert(colIndex >= 0); + + if (colIndex >= 0) { + pRow = pTable->rows[row]; + if (pRow) { + *returnValue = pRow[colIndex]; + return 0; + } + return DB_ERR_ROW_DELETED; + } + return DB_ERR_COL_NOT_FOUND; + } + + return DB_ERR_ROW_NOT_FOUND; +} + +/******************************************************************************/ +/* + * dbReadStr calls dbReadInt to do table driven read of database + */ + +int dbReadStr(int did, char_t *table, char_t *column, int row, + char_t **returnValue) +{ + return dbReadInt(did, table, column, row, (int *)returnValue); +} + +/******************************************************************************/ +/* + * The dbWriteInt function writes a value into a table at a given row and + * column. The existence of the row and column is verified before the + * write. 0 is returned on succes, -1 is returned on error. + */ + +int dbWriteInt(int did, char_t *table, char_t *column, int row, int iData) +{ + int tid, colIndex, *pRow; + dbTable_t *pTable; + + a_assert(table); + a_assert(column); + +/* + * Make sure that this table exists + */ + tid = dbGetTableId(0, table); + a_assert(tid >= 0); + + if (tid < 0) { + return DB_ERR_TABLE_NOT_FOUND; + } + + pTable = dbListTables[tid]; + + if (pTable) { +/* + * Make sure that the column exists + */ + colIndex = GetColumnIndex(tid, column); + a_assert(colIndex >= 0); + if (colIndex >= 0) { +/* + * Make sure that the row exists + */ + a_assert((row >= 0) && (row < pTable->nRows)); + if ((row >= 0) && (row < pTable->nRows)) { + pRow = pTable->rows[row]; + if (pRow) { + pRow[colIndex] = iData; + return 0; + } + return DB_ERR_ROW_DELETED; + } + return DB_ERR_ROW_NOT_FOUND; + } + return DB_ERR_COL_NOT_FOUND; + } + + return DB_ERR_TABLE_DELETED; +} + +/******************************************************************************/ +/* + * The dbWriteStr function writes a string value into a table at a given row + * and column. The existence of the row and column is verified before the + * write. The column is also checked to confirm it is a string field. + * 0 is returned on succes, -1 is returned on error. + */ + +int dbWriteStr(int did, char_t *table, char_t *column, int row, char_t *s) +{ + int tid, colIndex; + int *pRow; + char_t *ptr; + dbTable_t *pTable; + + a_assert(table); + a_assert(column); + + tid = dbGetTableId(0, table); + a_assert(tid >= 0); + + if (tid < 0) { + return DB_ERR_TABLE_NOT_FOUND; + } + +/* + * Make sure that this table exists + */ + pTable = dbListTables[tid]; + a_assert(pTable); + if (!pTable) { + return DB_ERR_TABLE_DELETED; + } + +/* + * Make sure that this column exists + */ + colIndex = GetColumnIndex(tid, column); + if (colIndex < 0) { + return DB_ERR_COL_NOT_FOUND; + } + +/* + * Make sure that this column is a string column + */ + if (pTable->columnTypes[colIndex] != T_STRING) { + return DB_ERR_BAD_FORMAT; + } + +/* + * Make sure that the row exists + */ + a_assert((row >= 0) && (row < pTable->nRows)); + if ((row >= 0) && (row < pTable->nRows)) { + pRow = pTable->rows[row]; + } else { + return DB_ERR_ROW_NOT_FOUND; + } + + if (!pRow) { + return DB_ERR_ROW_DELETED; + } + +/* + * If the column already has a value, be sure to delete it to prevent + * memory leaks. + */ + if (pRow[colIndex]) { + bfree(B_L, (char_t *) pRow[colIndex]); + } + +/* + * Make sure we make a copy of the string to write into the column. + * This allocated string will be deleted when the row is deleted. + */ + ptr = bstrdup(B_L, s); + pRow[colIndex] = (int)ptr; + + return 0; +} + +/******************************************************************************/ +/* + * Print a key-value pair to a file + */ + +static int dbWriteKeyValue(int fd, char_t *key, char_t *value) +{ + int rc; + int len; + char_t *pLineOut; + + a_assert(key && *key); + a_assert(value); + + fmtAlloc(&pLineOut, BUF_MAX, T("%s=%s\n"), key, value); + + if (pLineOut) { + len = gstrlen(pLineOut); +#if CE + rc = writeUniToAsc(fd, pLineOut, len); +#else + rc = gwrite(fd, pLineOut, len); +#endif + bfree(B_L, pLineOut); + } else { + rc = -1; + } + + return rc; +} + +/******************************************************************************/ +/* + * Persist a database to a file + */ + +int dbSave(int did, char_t *filename, int flags) +{ + int row, column, nColumns, nRows, fd, rc; + int *colTypes, *pRow, nRet, tid; + char_t *path, *tmpFile, *tmpNum; + char_t **colNames; + dbTable_t *pTable; + + trace(5, T("DB: About to save database to file\n")); + + a_assert(dbMaxTables > 0); + +/* + * First write to a temporary file, then switch around later. + */ + fmtAlloc(&tmpFile, FNAMESIZE, T("%s/data.tmp"), basicGetProductDir()); + if ((fd = gopen(tmpFile, + O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666)) < 0) { + trace(1, T("WARNING: Failed to open file %s\n"), tmpFile); + bfree(B_L, tmpFile); + return -1; + } + + nRet = 0; + + for (tid = 0; (tid < dbMaxTables) && (nRet != -1); tid++) { + pTable = dbListTables[tid]; + + if (pTable) { +/* + * Print the TABLE=tableName directive to the file + */ + rc = dbWriteKeyValue(fd, KEYWORD_TABLE, pTable->name); + + nColumns = pTable->nColumns; + nRows = pTable->nRows; + + for (row = 0; (row < nRows) && (nRet == 0); row++) { + pRow = pTable->rows[row]; +/* + * if row is NULL, the row has been deleted, so don't + * write it out. + */ + if ((pRow == NULL) || (pRow[0] == '\0') || + (*(char_t *)(pRow[0]) == '\0')) { + continue; + } +/* + * Print the ROW=rowNumber directive to the file + */ + fmtAlloc(&tmpNum, 20, T("%d"), row); + rc = dbWriteKeyValue(fd, KEYWORD_ROW, tmpNum); + bfreeSafe(B_L, tmpNum); + + colNames = pTable->columnNames; + colTypes = pTable->columnTypes; +/* + * Print the key-value pairs (COLUMN=value) for data cells + */ + for (column = 0; (column < nColumns) && (rc >= 0); + column++, colNames++, colTypes++) { + if (*colTypes == T_STRING) { + rc = dbWriteKeyValue(fd, *colNames, + (char_t *)(pRow[column])); + } else { + fmtAlloc(&tmpNum, 20, T("%d"), pRow[column]); + rc = dbWriteKeyValue(fd, *colNames, tmpNum); + bfreeSafe(B_L, tmpNum); + } + } + + if (rc < 0) { + trace(1, T("WARNING: Failed to write to file %s\n"), + tmpFile); + nRet = -1; + } + } + } + } + + gclose(fd); + +/* + * Replace the existing file with the temporary file, if no errors + */ + if (nRet == 0) { + fmtAlloc(&path, FNAMESIZE, T("%s/%s"), basicGetProductDir(), filename); + + gunlink(path); + if (grename(tmpFile, path) != 0) { + trace(1, T("WARNING: Failed to rename %s to %s\n"), tmpFile, path); + nRet = -1; + } + + bfree(B_L, path); + } + + bfree(B_L, tmpFile); + + return nRet; +} + +/******************************************************************************/ +/* + * Crack a keyword=value string into keyword and value. We can change buf. + */ + +static int crack(char_t *buf, char_t **key, char_t **val) +{ + char_t *ptr; + + if ((ptr = gstrrchr(buf, '\n')) != NULL || + (ptr = gstrrchr(buf, '\r')) != NULL) { + *ptr = '\0'; + } + +/* + * Find the = sign. It must exist. + */ + if ((ptr = gstrstr(buf, T("="))) == NULL) { + return -1; + } + + *ptr++ = '\0'; + *key = trim(buf); + *val = trim(ptr); + + return 0; +} + +/******************************************************************************/ +/* + * Parse the file. These files consist of key-value pairs, separated by the + * "=" sign. Parsing of tables starts with the "TABLE=value" pair, and rows + * are parsed starting with the "ROW=value" pair. + */ + +int dbLoad(int did, char_t *filename, int flags) +{ + gstat_t sbuf; + char_t *buf, *keyword, *value, *path, *ptr; + char_t *tablename; + int fd, tid, row; + dbTable_t *pTable; + + a_assert(did >= 0); + + fmtAlloc(&path, FNAMESIZE, T("%s/%s"), basicGetProductDir(), filename); + trace(4, T("DB: About to read data file <%s>\n"), path); + + if (gstat(path, &sbuf) < 0) { + trace(3, T("DB: Failed to stat persistent data file.\n")); + bfree(B_L, path); + return -1; + } + + fd = gopen(path, O_RDONLY | O_BINARY, 0666); + bfree(B_L, path); + + if (fd < 0) { + trace(3, T("DB: No persistent data file present.\n")); + return -1; + } + + if (sbuf.st_size <= 0) { + trace(3, T("DB: Persistent data file is empty.\n")); + gclose(fd); + return -1; + } +/* + * Read entire file into temporary buffer + */ + buf = balloc(B_L, sbuf.st_size + 1); +#if CE + if (readAscToUni(fd, &buf, sbuf.st_size) != (int)sbuf.st_size) { +#else + if (gread(fd, buf, sbuf.st_size) != (int)sbuf.st_size) { +#endif + trace(3, T("DB: Persistent data read failed.\n")); + bfree(B_L, buf); + gclose(fd); + return -1; + } + + gclose(fd); + *(buf + sbuf.st_size) = '\0'; + + row = -1; + tid = -1; + pTable = NULL; + ptr = gstrtok(buf, T("\n")); + tablename = NULL; + + do { + if (crack(ptr, &keyword, &value) < 0) { + trace(5, T("DB: Failed to crack line %s\n"), ptr); + continue; + } + + a_assert(keyword && *keyword); + + if (gstrcmp(keyword, KEYWORD_TABLE) == 0) { +/* + * Table name found, check to see if it's registered + */ + if (tablename) { + bfree(B_L, tablename); + } + + tablename = bstrdup(B_L, value); + tid = dbGetTableId(did, tablename); + + if (tid >= 0) { + pTable = dbListTables[tid]; + } else { + pTable = NULL; + } + + } else if (gstrcmp(keyword, KEYWORD_ROW) == 0) { +/* + * Row/Record indicator found, add a new row to table + */ + if (tid >= 0) { + int nRows = dbGetTableNrow(did, tablename); + + if (dbSetTableNrow(did, tablename, nRows + 1) == 0) { + row = nRows; + } + } + + } else if (row != -1) { +/* + * some other data found, assume it's a COLUMN=value + */ + int nColumn = GetColumnIndex(tid, keyword); + + if ((nColumn >= 0) && (pTable != NULL)) { + int nColumnType = pTable->columnTypes[nColumn]; + if (nColumnType == T_STRING) { + dbWriteStr(did, tablename, keyword, row, value); + } else { + dbWriteInt(did, tablename, keyword, row, gstrtoi(value)); + } + } + } + } while ((ptr = gstrtok(NULL, T("\n"))) != NULL); + + if (tablename) { + bfree(B_L, tablename); + } + + bfree(B_L, buf); + + return 0; +} + +/******************************************************************************/ +/* + * Return a table id given the table name + */ + +int dbGetTableId(int did, char_t *tablename) +{ + int tid; + dbTable_t *pTable; + + a_assert(tablename); + + for (tid = 0; (tid < dbMaxTables); tid++) { + if ((pTable = dbListTables[tid]) != NULL) { + if (gstrcmp(tablename, pTable->name) == 0) { + return tid; + } + } + } + + return -1; +} + +/******************************************************************************/ +/* + * Return a pointer to the table name, given its ID + */ + +char_t *dbGetTableName(int did, int tid) +{ + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + return (dbListTables[tid])->name; + } + + return NULL; +} + +/******************************************************************************/ +/* + * Trim leading white space. + */ + +static char_t *trim(char_t *str) +{ + while (isspace((int)*str)) { + str++; + } + return str; +} + +/******************************************************************************/ +/* + * Return a column index given the column name + */ + +static int GetColumnIndex(int tid, char_t *colName) +{ + int column; + dbTable_t *pTable; + + a_assert(colName); + + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + pTable = dbListTables[tid]; + + for (column = 0; (column < pTable->nColumns); column++) { + if (gstrcmp(colName, pTable->columnNames[column]) == 0) + return column; + } + } + + return -1; +} + +/******************************************************************************/ +/* + * Set the prefix-directory + */ + +void basicSetProductDir(char_t *proddir) +{ + int len; + + if (basicProdDir != NULL); { + bfree(B_L, basicProdDir); + } + + basicProdDir = bstrdup(B_L, proddir); +/* + * Make sure that prefix-directory doesn't end with a '/' + */ + len = gstrlen(basicProdDir); + if ((len > 0) && *(basicProdDir + len - 1) == '/') { + *(basicProdDir+len-1) = '\0'; + } +} + +/******************************************************************************/ +/* + * Return the prefix-directory + */ + +char_t *basicGetProductDir() +{ + if (basicProdDir) { + return basicProdDir; + } else { + return basicDefaultDir; + } +} + +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/emfdb.h b/c/src/libnetworking/rtems_webserver/emfdb.h new file mode 100644 index 0000000000..0ba7e67401 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/emfdb.h @@ -0,0 +1,102 @@ +/* + * emfdb.h -- EMF database compatability functions for GoAhead WebServer. + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ + */ + +/******************************** Description *********************************/ +/* + * Emf-like textfile database support for WebServer 2.1. + */ + +/********************************* Includes ***********************************/ + +#ifndef _h_EMFDB +#define _h_EMFDB 1 + +#if ! UEMF + #include "basic/basic.h" + #include "emf/emf.h" +#else + #include "uemf.h" +#endif + + +/********************************* Defines ************************************/ + +#define T_INT 0 +#define T_STRING 1 + +#define DB_OK 0 +#define DB_ERR_GENERAL -1 +#define DB_ERR_COL_NOT_FOUND -2 +#define DB_ERR_COL_DELETED -3 +#define DB_ERR_ROW_NOT_FOUND -4 +#define DB_ERR_ROW_DELETED -5 +#define DB_ERR_TABLE_NOT_FOUND -6 +#define DB_ERR_TABLE_DELETED -7 +#define DB_ERR_BAD_FORMAT -8 + +typedef struct dbTable_s { + char_t *name; + int nColumns; + char_t **columnNames; + int *columnTypes; + int nRows; + int **rows; +} dbTable_t; + +/********************************** Prototypes ********************************/ + +/* + * Add a schema to the module-internal schema database + */ +extern int dbRegisterDBSchema(dbTable_t *sTable); + +extern int dbOpen(char_t *databasename, char_t *filename, + int (*gettime)(int did), int flags); +extern void dbClose(int did); +extern int dbGetTableId(int did, char_t *tname); +extern char_t *dbGetTableName(int did, int tid); +extern int dbReadInt(int did, char_t *table, char_t *column, int row, + int *returnValue); +extern int dbReadStr(int did, char_t *table, char_t *column, int row, + char_t **returnValue); +extern int dbWriteInt(int did, char_t *table, char_t *column, int row, + int idata); +extern int dbWriteStr(int did, char_t *table, char_t *column, int row, + char_t *s); +extern int dbAddRow(int did, char_t *table); +extern int dbDeleteRow(int did, char_t *table, int rid); +extern int dbSetTableNrow(int did, char_t *table, int nNewRows); +extern int dbGetTableNrow(int did, char_t *table); + +/* + * Dump the contents of a database to file + */ +extern int dbSave(int did, char_t *filename, int flags); + +/* + * Load the contents of a database to file + */ +extern int dbLoad(int did, char_t *filename, int flags); + +/* + * Search for a data in a given column + */ +extern int dbSearchStr(int did, char_t *table, char_t *column, + char_t *value, int flags); + +extern void dbZero(int did); + +extern char_t *basicGetProductDir(); +extern void basicSetProductDir(char_t *proddir); + +#endif /* _h_EMFDB */ + +/******************************************************************************/ + diff --git a/c/src/libnetworking/rtems_webserver/form.c b/c/src/libnetworking/rtems_webserver/form.c index 05d5600ada..dbad2d2e78 100644 --- a/c/src/libnetworking/rtems_webserver/form.c +++ b/c/src/libnetworking/rtems_webserver/form.c @@ -1,7 +1,7 @@ /* * form.c -- Form processing (in-memory CGI) for the GoAhead Web server * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -29,12 +29,12 @@ static sym_fd_t formSymtab = -1; /* Symbol table for form handlers */ */ int websFormHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, - char_t *url, char_t *path, char_t* query) + char_t *url, char_t *path, char_t *query) { sym_t *sp; char_t formBuf[FNAMESIZE]; char_t *cp, *formName; - int (*fn)(void *sock, char_t* path, char_t *args); + int (*fn)(void *sock, char_t *path, char_t *args); a_assert(websValid(wp)); a_assert(url && *url); @@ -63,16 +63,23 @@ int websFormHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, if (sp == NULL) { websError(wp, 200, T("Form %s is not defined"), formName); } else { - fn = (int (*)(void*, char_t*, char_t*)) sp->content.value.integer; + fn = (int (*)(void *, char_t *, char_t *)) sp->content.value.integer; a_assert(fn); if (fn) { /* * For good practice, forms must call websDone() */ (*fn)((void*) wp, formName, query); + +/* + * Remove the test to force websDone, since this prevents + * the server "push" from a form> + */ +#if 0 /* push */ if (websValid(wp)) { websError(wp, 200, T("Form didn't call websDone")); } +#endif /* push */ } } return 1; @@ -86,8 +93,6 @@ int websFormHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, int websFormDefine(char_t *name, void (*fn)(webs_t wp, char_t *path, char_t *query)) { - static int once = 0; - a_assert(name && *name); a_assert(fn); @@ -95,9 +100,6 @@ int websFormDefine(char_t *name, void (*fn)(webs_t wp, char_t *path, return -1; } - if (once++ == 0) { - websFormOpen(); - } symEnter(formSymtab, name, valueInteger((int) fn), (int) NULL); return 0; } @@ -109,7 +111,7 @@ int websFormDefine(char_t *name, void (*fn)(webs_t wp, char_t *path, void websFormOpen() { - formSymtab = symOpen(64); + formSymtab = symOpen(WEBS_SYM_INIT); } /******************************************************************************/ @@ -120,7 +122,8 @@ void websFormOpen() void websFormClose() { if (formSymtab != -1) { - symClose(formSymtab, NULL); + symClose(formSymtab); + formSymtab = -1; } } @@ -139,7 +142,7 @@ void websHeader(webs_t wp) /* * By license terms the following line of code must not be modified */ - websWrite(wp, T("Server: GoAhead-Webs\r\n")); + websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); websWrite(wp, T("Pragma: no-cache\n")); websWrite(wp, T("Cache-control: no-cache\n")); diff --git a/c/src/libnetworking/rtems_webserver/h.c b/c/src/libnetworking/rtems_webserver/h.c index ed7a4553eb..23bc1ddd9c 100644 --- a/c/src/libnetworking/rtems_webserver/h.c +++ b/c/src/libnetworking/rtems_webserver/h.c @@ -1,15 +1,15 @@ /* * h.c -- Handle allocation module * - * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * See the file "license.txt" for usage and redistribution license requirements */ /******************************** Description *********************************/ /* - * This module provides a simple API to allocate and free handles - * It maintains a dynamic array of pointers. These usually point to + * This module provides a simple API to allocate and free handles + * It maintains a dynamic array of pointers. These usually point to * per-handle structures. */ @@ -36,12 +36,16 @@ /*********************************** Code *************************************/ /* - * Allocate a new file handle. On the first call, the caller must set the + * Allocate a new file handle. On the first call, the caller must set the * handle map to be a pointer to a null pointer. *map points to the second * element in the handle array. */ +#if B_STATS +int HALLOC(B_ARGS_DEC, void ***map) +#else int hAlloc(void ***map) +#endif { int *mp; int handle, len, memsize, incr; @@ -51,7 +55,11 @@ int hAlloc(void ***map) if (*map == NULL) { incr = H_INCR; memsize = (incr + H_OFFSET) * sizeof(void**); +#if B_STATS + if ((mp = (int*) balloc(B_ARGS, memsize)) == NULL) { +#else if ((mp = (int*) balloc(B_L, memsize)) == NULL) { +#endif return -1; } memset(mp, 0, memsize); @@ -68,11 +76,12 @@ int hAlloc(void ***map) * Find the first null handle */ if (mp[H_USED] < mp[H_LEN]) { - for (handle = 0; handle < len; handle++) + for (handle = 0; handle < len; handle++) { if (mp[handle+H_OFFSET] == 0) { mp[H_USED]++; return handle; } + } } else { handle = len; } @@ -139,7 +148,11 @@ int hFree(void ***map, int handle) * Allocate an entry in the halloc array. */ +#if B_STATS +int HALLOCENTRY(B_ARGS_DEC, void ***list, int *max, int size) +#else int hAllocEntry(void ***list, int *max, int size) +#endif { char_t *cp; int id; @@ -147,12 +160,20 @@ int hAllocEntry(void ***list, int *max, int size) a_assert(list); a_assert(max); +#if B_STATS + if ((id = HALLOC(B_ARGS, (void***) list)) < 0) { +#else if ((id = hAlloc((void***) list)) < 0) { +#endif return -1; } - + if (size > 0) { +#if B_STATS + if ((cp = balloc(B_ARGS, size)) == NULL) { +#else if ((cp = balloc(B_L, size)) == NULL) { +#endif hFree(list, id); return -1; } @@ -169,3 +190,4 @@ int hAllocEntry(void ***list, int *max, int size) } /******************************************************************************/ + diff --git a/c/src/libnetworking/rtems_webserver/handler.c b/c/src/libnetworking/rtems_webserver/handler.c index fb42d2d097..88693bf9a6 100644 --- a/c/src/libnetworking/rtems_webserver/handler.c +++ b/c/src/libnetworking/rtems_webserver/handler.c @@ -1,7 +1,7 @@ /* * handler.c -- URL handler support * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -19,14 +19,16 @@ /*********************************** Locals ***********************************/ -static websUrlHandlerType* websUrlHandler; /* URL handler list */ +static websUrlHandlerType *websUrlHandler; /* URL handler list */ static int websUrlHandlerMax; /* Number of entries */ +static int urlHandlerOpenCount = 0; /* count of apps */ /**************************** Forward Declarations ****************************/ -static int websUrlHandlerSort(const void* p1, const void* p2); +static int websUrlHandlerSort(const void *p1, const void *p2); static int websPublishHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int sid, char_t *url, char_t *path, char_t *query); +static int websTidyUrl(webs_t wp); /*********************************** Code *************************************/ /* @@ -35,7 +37,11 @@ static int websPublishHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int websUrlHandlerOpen() { - websAspOpen(); + if (++urlHandlerOpenCount == 1) { + websAspOpen(); + websUrlHandler = NULL; + websUrlHandlerMax = 0; + } return 0; } @@ -46,17 +52,20 @@ int websUrlHandlerOpen() void websUrlHandlerClose() { - websUrlHandlerType* sp; - - websAspClose(); - for (sp = websUrlHandler; sp < &websUrlHandler[websUrlHandlerMax]; sp++) { - bfree(B_L, sp->urlPrefix); - if (sp->webDir) { - bfree(B_L, sp->webDir); + websUrlHandlerType *sp; + + if (--urlHandlerOpenCount <= 0) { + websAspClose(); + for (sp = websUrlHandler; sp < &websUrlHandler[websUrlHandlerMax]; + sp++) { + bfree(B_L, sp->urlPrefix); + if (sp->webDir) { + bfree(B_L, sp->webDir); + } } + bfree(B_L, websUrlHandler); + websUrlHandlerMax = 0; } - bfree(B_L, websUrlHandler); - websUrlHandlerMax = 0; } /******************************************************************************/ @@ -135,7 +144,7 @@ int websUrlHandlerDelete(int (*handler)(webs_t wp, char_t *urlPrefix, * Sort in decreasing URL length order observing the flags for first and last */ -static int websUrlHandlerSort(const void* p1, const void* p2) +static int websUrlHandlerSort(const void *p1, const void *p2) { websUrlHandlerType *s1, *s2; int rc; @@ -247,6 +256,8 @@ int websUrlHandlerRequest(webs_t wp) websSetRequestPath(wp, websGetDefaultDir(), NULL); + websTidyUrl(wp); + /* * We loop over each handler in order till one accepts the request. * The security handler will handle the request if access is NOT allowed. @@ -264,7 +275,7 @@ int websUrlHandlerRequest(webs_t wp) return 1; } if (!websValid(wp)) { - goahead_trace(0, + trace(0, T("webs: handler %s called websDone, but didn't return 1\n"), sp->urlPrefix); return 1; @@ -272,7 +283,7 @@ int websUrlHandlerRequest(webs_t wp) } } /* - * If no handler processed the request, then return an error. Note: It was + * If no handler processed the request, then return an error. Note: It is * the handlers responsibility to call websDone */ if (i >= websUrlHandlerMax) { @@ -281,4 +292,70 @@ int websUrlHandlerRequest(webs_t wp) return 0; } + +/******************************************************************************/ +/* + * Tidy up the URL path. Return -1 if the URL is bad. + * Used to eliminate repeated directory delimiters ('/'). + */ + +static int websTidyUrl(webs_t wp) +{ + char_t *parts[64]; /* Array of ptr's to URL parts */ + char_t *token, *url, *tidyurl; + int i, len, npart; + + a_assert(websValid(wp)); + +/* + * Copy the string so we don't destroy the original (yet) + */ + url = bstrdup(B_L, wp->url); + websDecodeUrl(url, url, gstrlen(url)); + + len = npart = 0; + parts[0] = NULL; + token = gstrtok(url, T("/")); + +/* + * Look at each directory segment and process "." and ".." segments + * Don't allow the browser to pop outside the root web. + */ + while (token != NULL) { + if (gstrcmp(token, T("..")) == 0) { + if (npart > 0) { + npart--; + } + + } else if (gstrcmp(token, T(".")) != 0) { + parts[npart] = token; + len += gstrlen(token) + 1; + npart++; + } + token = gstrtok(NULL, T("/")); + } + +/* + * Re-construct URL. Need extra space all "/" and null. + */ + if (npart || (gstrcmp(url, T("/")) == 0) || (url[0] == '\0')) { + tidyurl = balloc(B_L, (len + 2) * sizeof(char_t)); + *tidyurl = '\0'; + + for (i = 0; i < npart; i++) { + gstrcat(tidyurl, T("/")); + gstrcat(tidyurl, parts[i]); + } + + bfree(B_L, url); + + bfree(B_L, wp->url); + wp->url = tidyurl; + return 0; + } else { + bfree(B_L, url); + return -1; + } +} + /******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/md5.h b/c/src/libnetworking/rtems_webserver/md5.h new file mode 100644 index 0000000000..d27031b44d --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/md5.h @@ -0,0 +1,48 @@ +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#ifndef _h_MD5 +#define _h_MD5 1 + +#ifndef UINT4 +#define UINT4 unsigned long +#endif + +#ifndef POINTER +#define POINTER unsigned char * +#endif + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CONTEXT; + +extern void MD5Init (MD5_CONTEXT *); +extern void MD5Update (MD5_CONTEXT *, unsigned char *, unsigned int); +extern void MD5Final (unsigned char [16], MD5_CONTEXT *); + +#endif /* _h_MD5 */ \ No newline at end of file diff --git a/c/src/libnetworking/rtems_webserver/md5c.c b/c/src/libnetworking/rtems_webserver/md5c.c new file mode 100644 index 0000000000..90142cf785 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/md5c.c @@ -0,0 +1,335 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#include "md5.h" + +/* Constants for MD5Transform routine. + */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform (UINT4 [4], unsigned char [64]); +static void Encode (unsigned char *, UINT4 *, unsigned int); +static void Decode (UINT4 *, unsigned char *, unsigned int); +static void MD5_memcpy (POINTER, POINTER, unsigned int); +static void MD5_memset (POINTER, int, unsigned int); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * Note: The following MD5 macros can be implemented as functions + * for code compactness, (at the expense of execution speed). + */ + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void MD5Init (context) +MD5_CONTEXT *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void MD5Update (context, input, inputLen) +MD5_CONTEXT *context; /* context */ +unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. +*/ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CONTEXT *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. +*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. +*/ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform (state, block) +UINT4 state[4]; +unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. +*/ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (output, input, len) +UINT4 *output; +unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. + */ + +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} diff --git a/c/src/libnetworking/rtems_webserver/mime.c b/c/src/libnetworking/rtems_webserver/mime.c index 2aa2422ca9..daa467f9cc 100644 --- a/c/src/libnetworking/rtems_webserver/mime.c +++ b/c/src/libnetworking/rtems_webserver/mime.c @@ -1,7 +1,7 @@ /* * mime.c -- Web server mime types * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ diff --git a/c/src/libnetworking/rtems_webserver/misc.c b/c/src/libnetworking/rtems_webserver/misc.c index 88b65a2008..0b3a9049cb 100644 --- a/c/src/libnetworking/rtems_webserver/misc.c +++ b/c/src/libnetworking/rtems_webserver/misc.c @@ -1,7 +1,7 @@ /* * misc.c -- Miscellaneous routines. * - * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -16,12 +16,12 @@ /********************************* Defines ************************************/ /* - * Sprintf buffer structure. Make the increment 8 less than 64 so that + * Sprintf buffer structure. Make the increment 64 so that * a balloc can use a 64 byte block. */ #define STR_REALLOC 0x1 /* Reallocate the buffer as required */ -#define STR_INC 58 /* Growth increment */ +#define STR_INC 64 /* Growth increment */ typedef struct { char_t *s; /* Pointer to buffer */ @@ -59,11 +59,11 @@ static void put_ulong(strbuf_t *buf, unsigned long int value, int base, /************************************ Code ************************************/ /* * "basename" returns a pointer to the last component of a pathname - * LINUX, RTEMS, and LynxOS have their own basename function + * LINUX and LynxOS have their own basename function */ -#if ! LINUX & ! __rtems__ & ! LYNX -char_t *basename(char_t* name) +#if ! LINUX && ! LYNX && ! __rtems__ +char_t *basename(char_t *name) { char_t *cp; @@ -83,7 +83,7 @@ char_t *basename(char_t* name) return ++cp; } } -#endif /* ! LINUX && ! __rtems__ && ! LYNX */ +#endif /* ! LINUX & ! LYNX */ /******************************************************************************/ /* @@ -91,9 +91,9 @@ char_t *basename(char_t* name) * the size of the buffer in BYTES! */ -char_t *dirname(char_t* buf, char_t* name, int bufsize) +char_t *dirname(char_t *buf, char_t *name, int bufsize) { - char_t* cp; + char_t *cp; int len; a_assert(name); @@ -130,14 +130,15 @@ char_t *dirname(char_t* buf, char_t* name, int bufsize) return buf; } + /******************************************************************************/ /* * sprintf and vsprintf are bad, ok. You can easily clobber memory. Use - * gsnprintf and gvsnprintf instead! These functions do _not_ support floating + * fmtAlloc and fmtValloc instead! These functions do _not_ support floating * point, like %e, %f, %g... */ -int gsnprintf(char_t **s, int n, char_t *fmt, ...) +int fmtAlloc(char_t **s, int n, char_t *fmt, ...) { va_list ap; int result; @@ -147,7 +148,30 @@ int gsnprintf(char_t **s, int n, char_t *fmt, ...) *s = NULL; va_start(ap, fmt); - result = gvsnprintf(s, n, fmt, ap); + result = dsnprintf(s, n, fmt, ap, 0); + va_end(ap); + return result; +} + +/******************************************************************************/ +/* + * Support a static buffer version for small buffers only! + */ + +int fmtStatic(char_t *s, int n, char_t *fmt, ...) +{ + va_list ap; + int result; + + a_assert(s); + a_assert(fmt); + a_assert(n <= 256); + + if (n <= 0) { + return -1; + } + va_start(ap, fmt); + result = dsnprintf(&s, n, fmt, ap, 0); va_end(ap); return result; } @@ -158,7 +182,7 @@ int gsnprintf(char_t **s, int n, char_t *fmt, ...) * reallocing if required. */ -int gsprintfRealloc(char_t **s, int n, int msize, char_t *fmt, ...) +int fmtRealloc(char_t **s, int n, int msize, char_t *fmt, ...) { va_list ap; int result; @@ -180,17 +204,18 @@ int gsprintfRealloc(char_t **s, int n, int msize, char_t *fmt, ...) * A vsprintf replacement. */ -int gvsnprintf(char_t **s, int n, char_t *fmt, va_list arg) +int fmtValloc(char_t **s, int n, char_t *fmt, va_list arg) { a_assert(s); a_assert(fmt); + *s = NULL; return dsnprintf(s, n, fmt, arg, 0); } /******************************************************************************/ /* - * Dynamic sprintf implementation. Supports dynamic buffer allocation also. + * Dynamic sprintf implementation. Supports dynamic buffer allocation. * This function can be called multiple times to grow an existing allocated * buffer. In this case, msize is set to the size of the previously allocated * buffer. The buffer will be realloced, as required. If msize is set, we @@ -252,7 +277,7 @@ static int dsnprintf(char_t **s, int size, char_t *fmt, va_list arg, int msize) } c = *fmt++; } else { - for ( ; gisdigit(c); c = *fmt++) { + for ( ; gisdigit((int)c); c = *fmt++) { width = width * 10 + (c - '0'); } } @@ -263,7 +288,7 @@ static int dsnprintf(char_t **s, int size, char_t *fmt, va_list arg, int msize) prec = va_arg(arg, int); c = *fmt++; } else { - for (prec = 0; gisdigit(c); c = *fmt++) { + for (prec = 0; gisdigit((int)c); c = *fmt++) { prec = prec * 10 + (c - '0'); } } @@ -402,24 +427,28 @@ static int strnlen(char_t *s, unsigned int n) static void put_char(strbuf_t *buf, char_t c) { - if (buf->count >= buf->size) { + if (buf->count >= (buf->size - 1)) { if (! (buf->flags & STR_REALLOC)) { return; } buf->size += STR_INC; if (buf->size > buf->max && buf->size > STR_INC) { - a_assert(buf->size <= buf->max); +/* + * Caller should increase the size of the calling buffer + */ buf->size -= STR_INC; return; } if (buf->s == NULL) { - buf->s = balloc(B_L, buf->size * sizeof(char_t*)); + buf->s = balloc(B_L, buf->size * sizeof(char_t)); } else { - buf->s = brealloc(B_L, buf->s, buf->size * sizeof(char_t*)); + buf->s = brealloc(B_L, buf->s, buf->size * sizeof(char_t)); } } buf->s[buf->count] = c; - ++buf->count; + if (c != '\0') { + ++buf->count; + } } /******************************************************************************/ @@ -428,7 +457,7 @@ static void put_char(strbuf_t *buf, char_t c) */ static void put_string(strbuf_t *buf, char_t *s, int len, int width, - int prec, enum flag f) + int prec, enum flag f) { int i; @@ -458,7 +487,7 @@ static void put_string(strbuf_t *buf, char_t *s, int len, int width, */ static void put_ulong(strbuf_t *buf, unsigned long int value, int base, - int upper, char_t *prefix, int width, int prec, enum flag f) + int upper, char_t *prefix, int width, int prec, enum flag f) { unsigned long x, x2; int len, zeros, i; @@ -475,8 +504,14 @@ static void put_ulong(strbuf_t *buf, unsigned long int value, int base, width -= strnlen(prefix, ULONG_MAX); } if (!(f & flag_minus)) { - for (i = 0; i < width; ++i) { - put_char(buf, ' '); + if (f & flag_zero) { + for (i = 0; i < width; ++i) { + put_char(buf, '0'); + } + } else { + for (i = 0; i < width; ++i) { + put_char(buf, ' '); + } } } if (prefix != NULL) { @@ -524,7 +559,7 @@ char_t *ascToUni(char_t *ubuf, char *str, int nBytes) * N.B. nBytes is the number of _bytes_ in the destination buffer, buf. */ -char *uniToAsc(char *buf, char_t* ustr, int nBytes) +char *uniToAsc(char *buf, char_t *ustr, int nBytes) { #if UNICODE if (WideCharToMultiByte(CP_ACP, 0, ustr, nBytes, buf, nBytes, NULL, @@ -537,24 +572,26 @@ char *uniToAsc(char *buf, char_t* ustr, int nBytes) return (char*) buf; } - /******************************************************************************/ /* * allocate (balloc) a buffer and do ascii to unicode conversion into it. - * cp points to the ascii string which must be NULL terminated. - * Return a pointer to the unicode buffer which must be bfree'd later. - * Return NULL on failure to get buffer. + * cp points to the ascii buffer. alen is the length of the buffer to be + * converted not including a terminating NULL. Return a pointer to the + * unicode buffer which must be bfree'd later. Return NULL on failure to + * get buffer. The buffer returned is NULL terminated. */ -char_t *ballocAscToUni(char * cp) + +char_t *ballocAscToUni(char *cp, int alen) { - char_t * unip; + char_t *unip; int ulen; - ulen = (strlen(cp) + 1) * sizeof(char_t); + ulen = (alen + 1) * sizeof(char_t); if ((unip = balloc(B_L, ulen)) == NULL) { return NULL; } ascToUni(unip, cp, ulen); + unip[alen] = 0; return unip; } @@ -562,20 +599,68 @@ char_t *ballocAscToUni(char * cp) /* * allocate (balloc) a buffer and do unicode to ascii conversion into it. * unip points to the unicoded string. ulen is the number of characters - * in the unicode string including teminating null, if there is one. - * Return a pointer to the ascii buffer which must be bfree'd later. - * Return NULL on failure to get buffer. + * in the unicode string not including a teminating null. Return a pointer + * to the ascii buffer which must be bfree'd later. Return NULL on failure + * to get buffer. The buffer returned is NULL terminated. */ -char *ballocUniToAsc(char_t * unip, int ulen) + +char *ballocUniToAsc(char_t *unip, int ulen) { char * cp; - if ((cp = balloc(B_L, ulen)) == NULL) { + if ((cp = balloc(B_L, ulen+1)) == NULL) { return NULL; } uniToAsc(cp, unip, ulen); + cp[ulen] = '\0'; return cp; } /******************************************************************************/ +/* + * convert a hex string to an integer. The end of the string or a non-hex + * character will indicate the end of the hex specification. + */ + +unsigned int hextoi(char_t *hexstring) +{ + register char_t *h; + register unsigned int c, v; + + v = 0; + h = hexstring; + if (*h == '0' && (*(h+1) == 'x' || *(h+1) == 'X')) { + h += 2; + } + while ((c = (unsigned int)*h++) != 0) { + if (c >= '0' && c <= '9') { + c -= '0'; + } else if (c >= 'a' && c <= 'f') { + c = (c - 'a') + 10; + } else if (c >= 'A' && c <= 'F') { + c = (c - 'A') + 10; + } else { + break; + } + v = (v * 0x10) + c; + } + return v; +} + +/******************************************************************************/ +/* + * convert a string to an integer. If the string starts with "0x" or "0X" + * a hexidecimal conversion is done. + */ + +unsigned int gstrtoi(char_t *s) +{ + if (*s == '0' && (*(s+1) == 'x' || *(s+1) == 'X')) { + s += 2; + return hextoi(s); + } + return gatoi(s); +} + +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/ringq.c b/c/src/libnetworking/rtems_webserver/ringq.c index 62c4634eef..cd483ae007 100644 --- a/c/src/libnetworking/rtems_webserver/ringq.c +++ b/c/src/libnetworking/rtems_webserver/ringq.c @@ -1,7 +1,7 @@ /* * ringq.c -- Ring queue buffering module * - * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -10,7 +10,7 @@ /* * A ring queue allows maximum utilization of memory for data storage and is - * ideal for input/output buffering. This module provides a highly effecient + * ideal for input/output buffering. This module provides a highly efficient * implementation and a vehicle for dynamic strings. * * WARNING: This is a public implementation and callers have full access to @@ -36,10 +36,10 @@ * rq->buf rq->servp rq->endp rq->enduf * * The queue is empty when servp == endp. This means that the queue will hold - * at most rq->buflen -1 bytes. It is the fillers responsibility to ensure + * at most rq->buflen -1 bytes. It is the filler's responsibility to ensure * the ringq is never filled such that servp == endp. * - * It is the fillers responsibility to "wrap" the endp back to point to + * It is the filler's responsibility to "wrap" the endp back to point to * rq->buf when the pointer steps past the end. Correspondingly it is the * consumers responsibility to "wrap" the servp when it steps to rq->endbuf. * The ringqPutc and ringqGetc routines will do this automatically. @@ -65,7 +65,10 @@ /***************************** Forward Declarations ***************************/ -static int ringq_grow(ringq_t *rq); +static int ringqGrow(ringq_t *rq); +static int getBinBlockSize(int size); + +int ringqGrowCalls = 0; /*********************************** Code *************************************/ /* @@ -76,12 +79,15 @@ static int ringq_grow(ringq_t *rq); * dynamically allocated. Set maxsize */ -int ringqOpen(ringq_t *rq, int increment, int maxsize) +int ringqOpen(ringq_t *rq, int initSize, int maxsize) { + int increment; + a_assert(rq); - a_assert(increment >= 0); + a_assert(initSize >= 0); - if ((rq->buf = balloc(B_L, increment)) == NULL) { + increment = getBinBlockSize(initSize); + if ((rq->buf = balloc(B_L, (increment))) == NULL) { return -1; } rq->maxsize = maxsize; @@ -115,8 +121,8 @@ void ringqClose(ringq_t *rq) /******************************************************************************/ /* - * Return the length of the ringq. Users must fill the queue to a high - * water mark of at most one less than the queue size. + * Return the length of the data in the ringq. Users must fill the queue to + * a high water mark of at most one less than the queue size. */ int ringqLen(ringq_t *rq) @@ -126,8 +132,7 @@ int ringqLen(ringq_t *rq) if (rq->servp > rq->endp) { return rq->buflen + rq->endp - rq->servp; - } - else { + } else { return rq->endp - rq->servp; } } @@ -166,12 +171,12 @@ int ringqGetc(ringq_t *rq) int ringqPutc(ringq_t *rq, char_t c) { - char_t* cp; + char_t *cp; a_assert(rq); a_assert(rq->buflen == (rq->endbuf - rq->buf)); - if (ringqPutBlkMax(rq) < (int) sizeof(char_t) && !ringq_grow(rq)) { + if ((ringqPutBlkMax(rq) < (int) sizeof(char_t)) && !ringqGrow(rq)) { return -1; } @@ -191,12 +196,12 @@ int ringqPutc(ringq_t *rq, char_t c) int ringqInsertc(ringq_t *rq, char_t c) { - char_t* cp; + char_t *cp; a_assert(rq); a_assert(rq->buflen == (rq->endbuf - rq->buf)); - if (ringqPutBlkMax(rq) < (int) sizeof(char_t) && !ringq_grow(rq)) { + if (ringqPutBlkMax(rq) < (int) sizeof(char_t) && !ringqGrow(rq)) { return -1; } if (rq->servp <= rq->buf) { @@ -210,10 +215,10 @@ int ringqInsertc(ringq_t *rq, char_t c) /******************************************************************************/ /* - * Add a string to the queue. Add a trailing wide null (two nulls) + * Add a string to the queue. Add a trailing null (maybe two nulls) */ -int ringqPutstr(ringq_t *rq, char_t *str) +int ringqPutStr(ringq_t *rq, char_t *str) { int rc; @@ -226,6 +231,19 @@ int ringqPutstr(ringq_t *rq, char_t *str) return rc; } +/******************************************************************************/ +/* + * Add a null terminator. This does NOT increase the size of the queue + */ + +void ringqAddNull(ringq_t *rq) +{ + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + *((char_t*) rq->endp) = (char_t) '\0'; +} + /******************************************************************************/ #if UNICODE /* @@ -261,7 +279,7 @@ int ringqPutcA(ringq_t *rq, char c) a_assert(rq); a_assert(rq->buflen == (rq->endbuf - rq->buf)); - if (ringqPutBlkMax(rq) == 0 && !ringq_grow(rq)) { + if (ringqPutBlkMax(rq) == 0 && !ringqGrow(rq)) { return -1; } @@ -282,7 +300,7 @@ int ringqInsertcA(ringq_t *rq, char c) a_assert(rq); a_assert(rq->buflen == (rq->endbuf - rq->buf)); - if (ringqPutBlkMax(rq) == 0 && !ringq_grow(rq)) { + if (ringqPutBlkMax(rq) == 0 && !ringqGrow(rq)) { return -1; } if (rq->servp <= rq->buf) { @@ -298,7 +316,7 @@ int ringqInsertcA(ringq_t *rq, char c) * ie. beyond the last valid byte. */ -int ringqPutstrA(ringq_t *rq, char *str) +int ringqPutStrA(ringq_t *rq, char *str) { int rc; @@ -334,7 +352,7 @@ int ringqPutBlk(ringq_t *rq, unsigned char *buf, int size) while (size > 0) { this = min(ringqPutBlkMax(rq), size); if (this <= 0) { - if (! ringq_grow(rq)) { + if (! ringqGrow(rq)) { break; } this = min(ringqPutBlkMax(rq), size); @@ -485,10 +503,13 @@ void ringqGetBlkAdj(ringq_t *rq, int size) void ringqFlush(ringq_t *rq) { a_assert(rq); + a_assert(rq->servp); rq->servp = rq->buf; rq->endp = rq->buf; - *rq->servp = '\0'; + if (rq->servp) { + *rq->servp = '\0'; + } } /******************************************************************************/ @@ -498,7 +519,7 @@ void ringqFlush(ringq_t *rq) * the maximum possible size. */ -static int ringq_grow(ringq_t *rq) +static int ringqGrow(ringq_t *rq) { unsigned char *newbuf; int len; @@ -531,7 +552,31 @@ static int ringq_grow(ringq_t *rq) rq->endbuf = &rq->buf[rq->buflen]; ringqPutBlk(rq, newbuf, len); + +/* + * Double the increment so the next grow will line up with balloc'ed memory + */ + rq->increment = getBinBlockSize(2 * rq->increment); + return 1; } /******************************************************************************/ +/* + * Find the smallest binary memory size that "size" will fit into. This + * makes the ringq and ringqGrow routines much more efficient. The balloc + * routine likes powers of 2 minus 1. + */ + +static int getBinBlockSize(int size) +{ + int q; + + size = size >> B_SHIFT; + for (q = 0; size; size >>= 1) { + q++; + } + return (1 << (B_SHIFT + q)); +} + +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/rom.c b/c/src/libnetworking/rtems_webserver/rom.c index 4e6c8f1e93..6bb8a8da99 100644 --- a/c/src/libnetworking/rtems_webserver/rom.c +++ b/c/src/libnetworking/rtems_webserver/rom.c @@ -1,7 +1,7 @@ /* * rom.c -- Support for ROMed page retrieval. * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -20,13 +20,6 @@ #include -#if CE -#define EINVAL 22 -#define EBADF 9 -#else -#include -#endif - #include "wsIntrn.h" /******************************** Local Data **********************************/ @@ -46,7 +39,7 @@ int websRomOpen() int nchars; char_t name[SYM_MAX]; - romTab = symOpen(64); + romTab = symOpen(WEBS_SYM_INIT); for (wip = websRomPageIndex; wip->path; wip++) { gstrncpy(name, wip->path, SYM_MAX); @@ -67,7 +60,7 @@ int websRomOpen() void websRomClose() { - symClose(romTab, NULL); + symClose(romTab); } /******************************************************************************/ @@ -105,7 +98,7 @@ void websRomPageClose(int fd) * Stat a web page */ -int websRomPageStat(char_t *path, websStatType* sbuf) +int websRomPageStat(char_t *path, websStatType *sbuf) { websRomPageIndexType *wip; sym_t *sp; @@ -193,6 +186,6 @@ long websRomPageSeek(webs_t wp, long offset, int origin) return (wip->pos = pos); } -#endif +#endif /* WEBS_PAGE_ROM */ /******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/security.c b/c/src/libnetworking/rtems_webserver/security.c index 01d4000f40..f4579ad41e 100644 --- a/c/src/libnetworking/rtems_webserver/security.c +++ b/c/src/libnetworking/rtems_webserver/security.c @@ -1,7 +1,7 @@ /* * security.c -- Security handler * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -9,18 +9,42 @@ /******************************** Description *********************************/ /* - * This module provides a basic security policy. It supports a single global - * password and ignores the username. Encoding/decoding of the password is - * -not- done. + * This module provides a basic security policy. */ /********************************* Includes ***********************************/ #include "wsIntrn.h" +#include "um.h" +#ifdef DIGEST_ACCESS_SUPPORT +#include "websda.h" +#endif + +/********************************** Defines ***********************************/ +/* + * The following #defines change the behaviour of security in the absence + * of User Management. + * Note that use of User management functions require prior calling of + * umInit() to behave correctly + */ + +#ifndef USER_MANAGEMENT_SUPPORT +#define umGetAccessMethodForURL(url) AM_FULL +#define umUserExists(userid) 0 +#define umUserCanAccessURL(userid, url) 1 +#define umGetUserPassword(userid) websGetPassword() +#define umGetAccessLimitSecure(accessLimit) 0 +#define umGetAccessLimit(url) NULL +#endif /******************************** Local Data **********************************/ static char_t websPassword[WEBS_MAX_PASS]; /* Access password (decoded) */ +#ifdef _DEBUG +static int debugSecurity = 1; +#else +static int debugSecurity = 0; +#endif /*********************************** Code *************************************/ /* @@ -30,46 +54,143 @@ static char_t websPassword[WEBS_MAX_PASS]; /* Access password (decoded) */ int websSecurityHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, char_t *query) { - char_t *type, *password; - int flags; + char_t *type, *userid, *password, *accessLimit; + int flags, nRet; + accessMeth_t am; a_assert(websValid(wp)); a_assert(url && *url); a_assert(path && *path); - /* * Get the critical request details */ type = websGetRequestType(wp); password = websGetRequestPassword(wp); + userid = websGetRequestUserName(wp); flags = websGetRequestFlags(wp); +/* + * Get the access limit for the URL. Exit if none found. + */ + accessLimit = umGetAccessLimit(path); + if (accessLimit == NULL) { + return 0; + } + +/* + * Check to see if URL must be encrypted + */ +#ifdef WEBS_SSL_SUPPORT + nRet = umGetAccessLimitSecure(accessLimit); + if (nRet && ((flags | WEBS_SECURE) == 0)) { + websStats.access++; + websError(wp, 200, T("Access Denied\nSecure access is required.")); + trace(3, T("SEC: Non-secure access attempted on <%s>\n"), path); + return 1; + } +#endif + +/* + * Get the access limit for the URL + */ + am = umGetAccessMethodForURL(accessLimit); + + nRet = 0; + if ((flags & WEBS_LOCAL_REQUEST) && (debugSecurity == 0)) { +/* + * Local access is always allowed (defeat when debugging) + */ + } else if (am == AM_NONE) { +/* + * URL is supposed to be hidden! Make like it wasn't found. + */ + websStats.access++; + websError(wp, 400, T("Page Not Found")); + nRet = 1; + } else if (userid && *userid) { + if (!umUserExists(userid)) { + websStats.access++; + websError(wp, 200, T("Access Denied\nUnknown User")); + trace(3, T("SEC: Unknown user <%s> attempted to access <%s>\n"), + userid, path); + nRet = 1; + } else if (!umUserCanAccessURL(userid, accessLimit)) { + websStats.access++; + websError(wp, 403, T("Access Denied\nProhibited User")); + nRet = 1; + } else if (password && * password) { + char_t * userpass = umGetUserPassword(userid); + if (userpass) { + if (gstrcmp(password, userpass) != 0) { + websStats.access++; + websError(wp, 200, T("Access Denied\nWrong Password")); + trace(3, T("SEC: Password fail for user <%s>") + T("attempt to access <%s>\n"), userid, path); + nRet = 1; + } else { +/* + * User and password check out. + */ + } + + bfree (B_L, userpass); + } +#ifdef DIGEST_ACCESS_SUPPORT + } else if (flags & WEBS_AUTH_DIGEST) { + + char_t *digestCalc; /* - * Validate the users password if required (local access is always allowed) - * We compare the decoded form of the password. + * Check digest for equivalence */ - if (*websPassword && !(flags & WEBS_LOCAL_REQUEST)) { + wp->password = umGetUserPassword(userid); - if (password && *password) { - if (gstrcmp(password, websPassword) != 0) { + a_assert(wp->digest); + a_assert(wp->nonce); + a_assert(wp->password); + + digestCalc = websCalcDigest(wp); + a_assert(digestCalc); + + if (gstrcmp(wp->digest, digestCalc) != 0) { websStats.access++; websError(wp, 200, T("Access Denied\nWrong Password")); - websSetPassword(T("")); - return 1; + nRet = 1; } + + bfree (B_L, digestCalc); +#endif } else { /* - * This will cause the browser to display a password / username - * dialog + * No password has been specified */ +#ifdef DIGEST_ACCESS_SUPPORT + if (am == AM_DIGEST) { + wp->flags |= WEBS_AUTH_DIGEST; + } +#endif websStats.errors++; - websError(wp, 401, T("Access Denied\r\n\ - Access to this document requires a password.\ - \r\n")); - return 1; + websError(wp, 401, + T("Access to this document requires a password")); + nRet = 1; + } + } else if (am != AM_FULL) { +/* + * This will cause the browser to display a password / username + * dialog + */ +#ifdef DIGEST_ACCESS_SUPPORT + if (am == AM_DIGEST) { + wp->flags |= WEBS_AUTH_DIGEST; } +#endif + websStats.errors++; + websError(wp, 401, T("Access to this document requires a User ID")); + nRet = 1; } - return 0; + + bfree(B_L, accessLimit); + + return nRet; } /******************************************************************************/ @@ -102,7 +223,7 @@ void websSetPassword(char_t *password) char_t *websGetPassword() { - return websPassword; + return bstrdup(B_L, websPassword); } /******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/socket.c b/c/src/libnetworking/rtems_webserver/socket.c index 81d81eb6fd..200cf232b5 100644 --- a/c/src/libnetworking/rtems_webserver/socket.c +++ b/c/src/libnetworking/rtems_webserver/socket.c @@ -1,66 +1,53 @@ + /* - * socket.c -- Socket support module for UNIX + * sockGen.c -- Posix Socket support module for general posix use * - * Copyright (c) Go Ahead, 1995-1999 + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. */ /******************************** Description *********************************/ /* - * SCO Unix Socket Module. This supports non-blocking buffered socket I/O. + * Posix Socket Module. This supports blocking and non-blocking buffered + * socket I/O. */ +#if (!WIN) || LITTLEFOOT || WEBS + /********************************** Includes **********************************/ -#include -#include #include #include -#include -#if __rtems__ -#include +#if UEMF + #include "uemf.h" +#else + #include + #include + #include + #include "emfInternal.h" #endif -#include "uemf.h" - -/*********************************** Defines **********************************/ - -typedef struct { - char host[64]; /* Host name */ - ringq_t inBuf; /* Input ring queue */ - ringq_t outBuf; /* Output ring queue */ - ringq_t lineBuf; /* Line ring queue */ - socketAccept_t accept; /* Accept handler */ - socketHandler_t handler; /* User I/O handler */ - int handler_data; /* User handler data */ - int sid; /* Index into socket[] */ - int port; /* Port to listen on */ - int flags; /* Current state flags */ - int readyMask; /* Events now ready */ - int interestMask; /* Events interest */ - int error; /* Last error */ - int sock; /* Actual socket handle */ -} socket_t; +#if VXWORKS + #include +#endif + +#if __rtems__ + #include +#endif /************************************ Locals **********************************/ -static socket_t** socketList; /* List of open sockets */ -static int socketMax; /* Maximum size of socket */ -static int socketHighestFd = -1; /* Highest socket fd opened */ +extern socket_t **socketList; /* List of open sockets */ +extern int socketMax; /* Maximum size of socket */ +extern int socketHighestFd; /* Highest socket fd opened */ +static int socketOpenCount = 0; /* Number of task using sockets */ /***************************** Forward Declarations ***************************/ -static int socketAlloc(char* host, int port, socketAccept_t accept, int flags); -static void socketFree(int sid); -static void socketAccept(socket_t* sp); -static int socketGetInput(int sid, char* buf, int toRead, int* errCode); -static int socketDoOutput(socket_t* sp, char* buf, int toWrite, int* errCode); +static void socketAccept(socket_t *sp); static int socketDoEvent(socket_t *sp); -static int socketGetError(); -static int socketWaitForEvent(socket_t* sp, int events, int* errCode); -static int socketNonBlock(socket_t *sp); -static socket_t* socketPtr(int sid); +static int tryAlternateConnect(int sock, struct sockaddr *sockaddr); /*********************************** Code *************************************/ /* @@ -69,6 +56,27 @@ static socket_t* socketPtr(int sid); int socketOpen() { +#if CE || WIN + WSADATA wsaData; +#endif + + if (++socketOpenCount > 1) { + return 0; + } + +#if CE || WIN + if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) { + return -1; + } + if (wsaData.wVersion != MAKEWORD(1,1)) { + WSACleanup(); + return -1; + } +#endif + socketList = NULL; + socketMax = 0; + socketHighestFd = -1; + return 0; } @@ -81,10 +89,13 @@ void socketClose() { int i; - for (i = socketMax; i >= 0; i--) { - if (socketList && socketList[i]) { - socketCloseConnection(i); + if (--socketOpenCount <= 0) { + for (i = socketMax; i >= 0; i--) { + if (socketList && socketList[i]) { + socketCloseConnection(i); + } } + socketOpenCount = 0; } } @@ -93,13 +104,18 @@ void socketClose() * Open a client or server socket. Host is NULL if we want server capability. */ -int socketOpenConnection(char* host, int port, socketAccept_t accept, int flags) +int socketOpenConnection(char *host, int port, socketAccept_t accept, int flags) { +#if ! (NO_GETHOSTBYNAME || VXWORKS) + struct hostent *hostent; /* Host database entry */ +#endif /* ! (NO_GETHOSTBYNAME || VXWORKS) */ socket_t *sp; - struct sockaddr_in sockaddr; - struct hostent *hostent; /* Host database entry */ - int sid, rc; + struct sockaddr_in sockaddr; + int sid, bcast, dgram, rc; + if (port > SOCKET_PORT_MAX) { + return -1; + } /* * Allocate a socket structure */ @@ -121,68 +137,195 @@ int socketOpenConnection(char* host, int port, socketAccept_t accept, int flags) } else { sockaddr.sin_addr.s_addr = inet_addr(host); if (sockaddr.sin_addr.s_addr == INADDR_NONE) { +/* + * If the OS does not support gethostbyname functionality, the macro: + * NO_GETHOSTBYNAME should be defined to skip the use of gethostbyname. + * Unfortunatly there is no easy way to recover, the following code + * simply uses the basicGetHost IP for the sockaddr. + */ + +#if NO_GETHOSTBYNAME + if (strcmp(host, basicGetHost()) == 0) { + sockaddr.sin_addr.s_addr = inet_addr(basicGetAddress()); + } + if (sockaddr.sin_addr.s_addr == INADDR_NONE) { + socketFree(sid); + return -1; + } +#elif VXWORKS + sockaddr.sin_addr.s_addr = (unsigned long) hostGetByName(host); + if (sockaddr.sin_addr.s_addr == NULL) { + errno = ENXIO; + socketFree(sid); + return -1; + } +#else hostent = gethostbyname(host); if (hostent != NULL) { memcpy((char *) &sockaddr.sin_addr, (char *) hostent->h_addr_list[0], (size_t) hostent->h_length); } else { - errno = ENXIO; - socketFree(sid); - return -1; + char *asciiAddress; + char_t *address; + + address = basicGetAddress(); + asciiAddress = ballocUniToAsc(address, gstrlen(address)); + sockaddr.sin_addr.s_addr = inet_addr(asciiAddress); + bfree(B_L, asciiAddress); + if (sockaddr.sin_addr.s_addr == INADDR_NONE) { + errno = ENXIO; + socketFree(sid); + return -1; + } } +#endif /* (NO_GETHOSTBYNAME || VXWORKS) */ } } + bcast = sp->flags & SOCKET_BROADCAST; + if (bcast) { + sp->flags |= SOCKET_DATAGRAM; + } + dgram = sp->flags & SOCKET_DATAGRAM; + /* - * Create the socket. Set the close on exec flag so children don't - * inherit the socket. + * Create the socket. Support for datagram sockets. Set the close on + * exec flag so children don't inherit the socket. */ - sp->sock = socket(AF_INET, SOCK_STREAM, 0); + sp->sock = socket(AF_INET, dgram ? SOCK_DGRAM: SOCK_STREAM, 0); if (sp->sock < 0) { socketFree(sid); return -1; } +#ifndef __NO_FCNTL fcntl(sp->sock, F_SETFD, FD_CLOEXEC); +#endif socketHighestFd = max(socketHighestFd, sp->sock); +/* + * If broadcast, we need to turn on broadcast capability. + */ + if (bcast) { + int broadcastFlag = 1; + if (setsockopt(sp->sock, SOL_SOCKET, SO_BROADCAST, + (char *) &broadcastFlag, sizeof(broadcastFlag)) < 0) { + socketFree(sid); + return -1; + } + } + /* * Host is set if we are the client */ if (host) { /* - * Connect to the remote server + * Connect to the remote server in blocking mode, then go into + * non-blocking mode if desired. */ - if (connect(sp->sock, (struct sockaddr *) &sockaddr, - sizeof(sockaddr)) < 0) { - socketFree(sid); - return -1; - } - socketNonBlock(sp); + if (!dgram) { + if (! (sp->flags & SOCKET_BLOCK)) { +/* + * sockGen.c is only used for Windows products when blocking + * connects are expected. This applies to FieldUpgrader + * agents and open source webserver connectws. Therefore the + * asynchronous connect code here is not compiled. + */ +#if (WIN || CE) && !(LITTLEFOOT || WEBS) + int flag; + sp->flags |= SOCKET_ASYNC; +/* + * Set to non-blocking for an async connect + */ + flag = 1; + if (ioctlsocket(sp->sock, FIONBIO, &flag) == SOCKET_ERROR) { + socketFree(sid); + return -1; + } +#else + socketSetBlock(sid, 1); +#endif /* #if (WIN || CE) && !(LITTLEFOOT || WEBS) */ + + } + if ((rc = connect(sp->sock, (struct sockaddr *) &sockaddr, + sizeof(sockaddr))) < 0 && + (rc = tryAlternateConnect(sp->sock, + (struct sockaddr *) &sockaddr)) < 0) { +#if WIN || CE + if (socketGetError() != EWOULDBLOCK) { + socketFree(sid); + return -1; + } +#else + socketFree(sid); + return -1; + +#endif /* WIN || CE */ + + } + } } else { /* - * Bind to the socket endpoint with resule and the call listen() - ** to start listening + * Bind to the socket endpoint and the call listen() to start listening */ rc = 1; setsockopt(sp->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc)); - if (bind(sp->sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) - < 0) { + if (bind(sp->sock, (struct sockaddr *) &sockaddr, + sizeof(sockaddr)) < 0) { socketFree(sid); return -1; } - sp->flags |= SOCKET_LISTENING; - if (listen(sp->sock, SOMAXCONN) < 0) { - socketFree(sid); - return -1; + if (! dgram) { + if (listen(sp->sock, SOMAXCONN) < 0) { + socketFree(sid); + return -1; + } +#if !UEMF + sp->fileHandle = emfCreateFileHandler(sp->sock, SOCKET_READABLE, + (emfFileProc *) socketAccept, (void *) sp); +#else + sp->flags |= SOCKET_LISTENING; +#endif } - sp->interestMask = SOCKET_READABLE; + sp->handlerMask |= SOCKET_READABLE; + } + +/* + * Set the blocking mode + */ + + if (flags & SOCKET_BLOCK) { + socketSetBlock(sid, 1); + } else { + socketSetBlock(sid, 0); } return sid; } +/******************************************************************************/ +/* + * If the connection failed, swap the first two bytes in the + * sockaddr structure. This is a kludge due to a change in + * VxWorks between versions 5.3 and 5.4, but we want the + * product to run on either. + */ + +static int tryAlternateConnect(int sock, struct sockaddr *sockaddr) +{ +#if VXWORKS + char *ptr; + + ptr = (char *)sockaddr; + *ptr = *(ptr+1); + *(ptr+1) = 0; + return connect(sock, sockaddr, sizeof(struct sockaddr)); +#else + return -1; +#endif /* VXWORKS */ +} + /******************************************************************************/ /* * Close a socket @@ -190,30 +333,25 @@ int socketOpenConnection(char* host, int port, socketAccept_t accept, int flags) void socketCloseConnection(int sid) { - socket_t* sp; + socket_t *sp; if ((sp = socketPtr(sid)) == NULL) { return; } - -/* - * We always flush all output before closing. Unlink from the emf event - * mechanism and then free (and close) the connection - */ - socketFlush(sid, 1); socketFree(sid); } /******************************************************************************/ /* - * Accept a connection. Called by socketDoEvent + * Accept a connection. Called as a callback on incoming connection. */ -static void socketAccept(socket_t* sp) +static void socketAccept(socket_t *sp) { struct sockaddr_in addr; socket_t *nsp; - int len; + size_t len; + char *pString; int newSock, nid; a_assert(sp); @@ -225,462 +363,354 @@ static void socketAccept(socket_t* sp) if ((newSock = accept(sp->sock, (struct sockaddr *) &addr, &len)) < 0) { return; } +#ifndef __NO_FCNTL fcntl(newSock, F_SETFD, FD_CLOEXEC); +#endif socketHighestFd = max(socketHighestFd, newSock); /* * Create a socket structure and insert into the socket list */ - nid = socketAlloc(sp->host, sp->port, sp->accept, 0); + nid = socketAlloc(sp->host, sp->port, sp->accept, sp->flags); nsp = socketList[nid]; a_assert(nsp); nsp->sock = newSock; + nsp->flags &= ~SOCKET_LISTENING; if (nsp == NULL) { return; } /* - * Call the user accept callback, the user must call socketCreateHandler + * Set the blocking mode before calling the accept callback. + */ + + socketSetBlock(nid, (nsp->flags & SOCKET_BLOCK) ? 1: 0); +/* + * Call the user accept callback. The user must call socketCreateHandler * to register for further events of interest. */ if (sp->accept != NULL) { - if ((sp->accept)(nid, inet_ntoa(addr.sin_addr), - ntohs(addr.sin_port)) < 0) { + pString = inet_ntoa(addr.sin_addr); + if ((sp->accept)(nid, pString, ntohs(addr.sin_port), sp->sid) < 0) { socketFree(nid); - return; } +#if VXWORKS + free(pString); +#endif } - socketNonBlock(nsp); } /******************************************************************************/ /* - * Write to a socket. This may block if the underlying socket cannot - * absorb the data. Returns -1 on error, otherwise the number of bytes - * written. + * Get more input from the socket and return in buf. + * Returns 0 for EOF, -1 for errors and otherwise the number of bytes read. */ -int socketWrite(int sid, char* buf, int bufsize) +int socketGetInput(int sid, char *buf, int toRead, int *errCode) { - socket_t* sp; - ringq_t* rq; - int len, bytesWritten, room; + struct sockaddr_in server; + socket_t *sp; + int len, bytesRead; a_assert(buf); - a_assert(bufsize >= 0); + a_assert(errCode); + + *errCode = 0; if ((sp = socketPtr(sid)) == NULL) { return -1; } - -/* - * Loop adding as much data to the output ringq as we can absorb - * Flush when the ringq is too full and continue. - */ - rq = &sp->outBuf; - for (bytesWritten = 0; bufsize > 0; ) { - if ((room = ringqPutBlkMax(rq)) == 0) { - if (socketFlush(sid, 0) < 0) { - return -1; - } - if ((room = ringqPutBlkMax(rq)) == 0) { - break; - } - continue; - } - len = min(room, bufsize); - ringqPutBlk(rq, (unsigned char*) buf, len); - bytesWritten += len; - bufsize -= len; - buf += len; - } - return bytesWritten; -} -/******************************************************************************/ /* - * Read from a socket. Return the number of bytes read if successful. This - * may be less than the requested "bufsize" and may be zero. Return -1 for - * errors. Return 0 for EOF. Otherwise return the number of bytes read. Since - * this may be zero, callers should use socketEof() to distinguish between - * this and EOF. Note: this ignores the line buffer, so a previous socketGets - * which read a partial line may cause a subsequent socketRead to miss - * some data. + * If we have previously seen an EOF condition, then just return */ - -int socketRead(int sid, char* buf, int bufsize) -{ - socket_t* sp; - ringq_t* rq; - int len, room, errCode, bytesRead; - - a_assert(buf); - a_assert(bufsize > 0); - - if ((sp = socketPtr(sid)) == NULL) { - return -1; - } - if (sp->flags & SOCKET_EOF) { return 0; } +#if (WIN || CE) && !(LITTLEFOOT || WEBS) + if ( !(sp->flags & SOCKET_BLOCK) + && ! socketWaitForEvent(sp, FD_CONNECT, errCode)) { + return -1; + } +#endif - rq = &sp->inBuf; - for (bytesRead = 0; bufsize > 0; ) { - len = min(ringqLen(rq), bufsize); - if (len <= 0) { - room = ringqPutBlkMax(rq); - len = socketGetInput(sid, (char*) rq->endp, room, &errCode); - if (len < 0) { - if (errCode == EWOULDBLOCK) { - if (bytesRead >= 0) { - return bytesRead; - } - } - return -1; - - } else if (len == 0) { /* - * This is EOF, but we may have already read some data so pass that - * back first before notifying EOF. The next read will return 0 - * to indicate EOF. + * Read the data */ - return bytesRead; - } - ringqPutBlkAdj(rq, len); - len = min(len, bufsize); + if (sp->flags & SOCKET_DATAGRAM) { + len = sizeof(server); + bytesRead = recvfrom(sp->sock, buf, toRead, 0, + (struct sockaddr *) &server, &len); + } else { + bytesRead = recv(sp->sock, buf, toRead, 0); + } + if (bytesRead < 0) { + if (errno == ECONNRESET) { + return 0; } - memcpy(&buf[bytesRead], rq->servp, len); - ringqGetBlkAdj(rq, len); - bufsize -= len; - bytesRead += len; + *errCode = socketGetError(); + return -1; } return bytesRead; } /******************************************************************************/ /* - * Get a string from a socket. This returns data in *buf in a malloced string - * after trimming the '\n'. If there is zero bytes returned, *buf will be set - * to NULL. It returns -1 for error, EOF or when no complete line yet read. - * Otherwise the length of the line is returned. If a partial line is read - * socketInputBuffered or socketEof can be used to distinguish between EOF - * and partial line still buffered. This routine eats and ignores carriage - * returns. + * Process an event on the event queue */ -int socketGets(int sid, char** buf) +#ifndef UEMF + +static int socketEventProc(void *data, int mask) { - socket_t* sp; - ringq_t* lq; - char c; - int rc, len; + socket_t *sp; + ringq_t *rq; + int sid; - a_assert(buf); + sid = (int) data; + + a_assert(sid >= 0 && sid < socketMax); + a_assert(socketList[sid]); if ((sp = socketPtr(sid)) == NULL) { - return -1; + return 1; } - lq = &sp->lineBuf; - - while (1) { - if ((rc = socketRead(sid, &c, 1)) < 0) { - return rc; - - } else if (rc == 0) { -/* - * If there is a partial line and we are at EOF, pretend we saw a '\n' - */ - if (ringqLen(lq) > 0 && (sp->flags & SOCKET_EOF)) { - c = '\n'; - } else { - return -1; - } - } /* - * If a newline is seen, return the data excluding the new line to the - * caller. If carriage return is seen, just eat it. + * If now writable and flushing in the background, continue flushing */ - if (c == '\n') { - len = ringqLen(lq); - if (len > 0) { - if ((*buf = balloc(B_L, len + 1)) == NULL) { - return -1; - } - memset(*buf, 0, len + 1); - ringqGetBlk(lq, (unsigned char*) *buf, len); + if (mask & SOCKET_WRITABLE) { + if (sp->flags & SOCKET_FLUSHING) { + rq = &sp->outBuf; + if (ringqLen(rq) > 0) { + socketFlush(sp->sid); } else { - *buf = NULL; + sp->flags &= ~SOCKET_FLUSHING; } - return len; - - } else if (c == '\r') { - continue; } - ringqPutc(lq, c); } -} -/******************************************************************************/ /* - * Flush a socket. Do not wait, just initiate output and return. - * This will return -1 on errors and 0 if successful. - */ - -int socketFlush(int sid, int block) -{ - socket_t* sp; - ringq_t* rq; - int len, bytesWritten, errCode; - - a_assert(block == 0 || block == 1); - - if ((sp = socketPtr(sid)) == NULL) { - return -1; - } - rq = &sp->outBuf; - -/* - * Set the background flushing flag which socketDoEvent will check to - * continue the flush. + * Now invoke the users socket handler. NOTE: the handler may delete the + * socket, so we must be very careful after calling the handler. */ - if (!block) { - sp->flags |= SOCKET_FLUSHING; + if (sp->handler && (sp->handlerMask & mask)) { + (sp->handler)(sid, mask & sp->handlerMask, sp->handler_data); } - -/* - * Break from loop if not blocking after initiating output. If we are blocking - * we wait for a write event. - */ - while (ringqLen(rq) > 0) { - len = ringqGetBlkMax(&sp->outBuf); - bytesWritten = socketDoOutput(sp, (char*) rq->servp, len, &errCode); - if (bytesWritten < 0) { - if (errCode == EINTR) { - continue; - } else if (errCode == EWOULDBLOCK || errCode == EAGAIN) { - if (! block) { - return 0; - } - if (socketWaitForEvent(sp, SOCKET_WRITABLE | SOCKET_EXCEPTION, - &errCode)) { - continue; - } - } - return -1; - } - ringqGetBlkAdj(rq, bytesWritten); - if (! block) { - break; - } + if (socketList && sid < socketMax && socketList[sid] == sp) { + socketRegisterInterest(sp, sp->handlerMask); } - return 0; + return 1; } +#endif /* ! UEMF */ /******************************************************************************/ /* - * Return the count of input characters buffered. We look at both the line - * buffer and the input (raw) buffer. Return -1 on error or EOF. + * Define the events of interest */ -int socketInputBuffered(int sid) +void socketRegisterInterest(socket_t *sp, int handlerMask) { - socket_t* sp; + a_assert(sp); - if ((sp = socketPtr(sid)) == NULL) { - return -1; - } - if (socketEof(sid)) { - return -1; + sp->handlerMask = handlerMask; +#if !UEMF + if (handlerMask) { + sp->fileHandle = emfCreateFileHandler(sp->sock, handlerMask, + (emfFileProc *) socketEventProc, (void *) sp->sid); + } else { + emfDeleteFileHandler(sp->fileHandle); } - return ringqLen(&sp->lineBuf) + ringqLen(&sp->inBuf); +#endif /* ! UEMF */ } /******************************************************************************/ /* - * Return true if EOF + * Wait until an event occurs on a socket. Return 1 on success, 0 on failure. + * or -1 on exception (UEMF only) */ -int socketEof(int sid) +int socketWaitForEvent(socket_t *sp, int handlerMask, int *errCode) { - socket_t* sp; + int mask; - if ((sp = socketPtr(sid)) == NULL) { + a_assert(sp); + + mask = sp->handlerMask; + sp->handlerMask |= handlerMask; + while (socketSelect(sp->sid, 1000)) { + if (sp->currentEvents & (handlerMask | SOCKET_EXCEPTION)) { + break; + } + } + sp->handlerMask = mask; + if (sp->currentEvents & SOCKET_EXCEPTION) { return -1; + } else if (sp->currentEvents & handlerMask) { + return 1; } - return sp->flags & SOCKET_EOF; + if (errCode) { + *errCode = errno = EWOULDBLOCK; + } + return 0; } /******************************************************************************/ /* - * Create a user handler for this socket. The handler called whenever there - * is an event of interest as defined by interestMask (SOCKET_READABLE, ...) + * Return TRUE if there is a socket with an event ready to process, */ -void socketCreateHandler(int sid, int interestMask, socketHandler_t handler, - int data) +int socketReady(int sid) { - socket_t* sp; + socket_t *sp; + int all; - if ((sp = socketPtr(sid)) == NULL) { - return; + all = 0; + if (sid < 0) { + sid = 0; + all = 1; } - sp->handler = handler; - sp->handler_data = data; - sp->interestMask = interestMask; -} -/******************************************************************************/ + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (! all) { + break; + } else { + continue; + } + } + if (sp->currentEvents & sp->handlerMask) { + return 1; + } /* - * Delete a handler + * If there is input data, also call select to test for new events */ - -void socketDeleteHandler(int sid) -{ - socket_t* sp; - - if ((sp = socketPtr(sid)) == NULL) { - return; + if (sp->handlerMask & SOCKET_READABLE && socketInputBuffered(sid)) { + socketSelect(sid, 0); + return 1; + } + if (! all) { + break; + } } - sp->handler = NULL; - sp->interestMask = 0; + return 0; } /******************************************************************************/ /* - * Get more input from the socket and return in buf. - * Returns 0 for EOF, -1 for errors and otherwise the number of bytes read. + * Wait for a handle to become readable or writable and return a number of + * noticed events. Timeout is in milliseconds. */ -static int socketGetInput(int sid, char* buf, int toRead, int* errCode) -{ - struct sockaddr_in server; - socket_t* sp; - int len, bytesRead; - - a_assert(buf); - a_assert(errCode); +#if WIN || CE - *errCode = 0; +int socketSelect(int sid, int timeout) +{ + struct timeval tv; + socket_t *sp; + fd_set readFds, writeFds, exceptFds; + int nEvents; + int all, socketHighestFd; /* Highest socket fd opened */ + + FD_ZERO(&readFds); + FD_ZERO(&writeFds); + FD_ZERO(&exceptFds); + socketHighestFd = -1; - if ((sp = socketPtr(sid)) == NULL) { - return -1; - } + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; /* - * If we have previously seen an EOF condition, then just return + * Set the select event masks for events to watch */ - if (sp->flags & SOCKET_EOF) { - return 0; - } + all = nEvents = 0; -/* - * Read the data - */ - if (sp->flags & SOCKET_BROADCAST) { - server.sin_family = AF_INET; - server.sin_addr.s_addr = INADDR_BROADCAST; - server.sin_port = htons((short)(sp->port & 0xFFFF)); - len = sizeof(server); - bytesRead = recvfrom(sp->sock, buf, toRead, 0, - (struct sockaddr*) &server, &len); - } else { - bytesRead = recv(sp->sock, buf, toRead, 0); + if (sid < 0) { + all++; + sid = 0; } - if (bytesRead < 0) { - if (errno == ECONNRESET) { - return 0; + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + continue; } - *errCode = socketGetError(); - return -1; - - } else if (bytesRead == 0) { - sp->flags |= SOCKET_EOF; - } - return bytesRead; -} - -/******************************************************************************/ + a_assert(sp); /* - * Socket output procedure. Return -1 on errors otherwise return the number - * of bytes written. + * Set the appropriate bit in the ready masks for the sp->sock. */ - -static int socketDoOutput(socket_t* sp, char* buf, int toWrite, int* errCode) -{ - struct sockaddr_in server; - int bytes; - - a_assert(sp); - a_assert(buf); - a_assert(toWrite > 0); - a_assert(errCode); - - *errCode = 0; + if (sp->handlerMask & SOCKET_READABLE) { + FD_SET(sp->sock, &readFds); + nEvents++; + if (socketInputBuffered(sid) > 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + if (sp->handlerMask & SOCKET_WRITABLE) { + FD_SET(sp->sock, &writeFds); + nEvents++; + } + if (sp->handlerMask & SOCKET_EXCEPTION) { + FD_SET(sp->sock, &exceptFds); + nEvents++; + } + if (! all) { + break; + } + } /* - * Write the data + * Windows select() fails if no descriptors are set, instead of just sleeping + * like other, nice select() calls. So, if WIN, sleep. */ - if (sp->flags & SOCKET_BROADCAST) { - server.sin_family = AF_INET; - server.sin_addr.s_addr = INADDR_BROADCAST; - server.sin_port = htons((short)(sp->port & 0xFFFF)); - bytes = sendto(sp->sock, buf, toWrite, 0, - (struct sockaddr*) &server, sizeof(server)); - } else { - bytes = send(sp->sock, buf, toWrite, 0); - } - - if (bytes == 0 && bytes != toWrite) { - *errCode = EWOULDBLOCK; - return -1; - } - - if (bytes < 0) { - *errCode = socketGetError(); + if (nEvents == 0) { + Sleep(timeout); + return 0; } - return bytes; -} -/******************************************************************************/ /* - * Return TRUE if there is a socket with an event ready to process, + * Wait for the event or a timeout. */ + nEvents = select(socketHighestFd+1, &readFds, &writeFds, &exceptFds, &tv); -int socketReady() -{ - socket_t *sp; - int i; - - for (i = 0; i < socketMax; i++) { - if ((sp = socketList[i]) == NULL) { + if (all) { + sid = 0; + } + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { continue; - } - if (sp->readyMask & sp->interestMask) { - return 1; + } + + if (FD_ISSET(sp->sock, &readFds) || socketInputBuffered(sid) > 0) { + sp->currentEvents |= SOCKET_READABLE; + } + if (FD_ISSET(sp->sock, &writeFds)) { + sp->currentEvents |= SOCKET_WRITABLE; + } + if (FD_ISSET(sp->sock, &exceptFds)) { + sp->currentEvents |= SOCKET_EXCEPTION; + } + if (! all) { + break; } } - return 0; + + return nEvents; } -/******************************************************************************/ -/* - * Wait for a handle to become readable or writable and return a number of - * noticed events. - */ +#else /* not WIN || CE */ -int socketSelect() +int socketSelect(int sid, int timeout) { - socket_t *sp; - fd_mask *readFds, *writeFds, *exceptFds; - int sid, len, nwords, index, bit, nEvents; + socket_t *sp; + struct timeval tv; + fd_mask *readFds, *writeFds, *exceptFds; + int all, len, nwords, index, bit, nEvents; /* * Allocate and zero the select masks */ - nwords = (socketHighestFd + NFDBITS - 1) / NFDBITS; + nwords = (socketHighestFd + NFDBITS) / NFDBITS; len = nwords * sizeof(int); readFds = balloc(B_L, len); @@ -690,12 +720,26 @@ int socketSelect() exceptFds = balloc(B_L, len); memset(exceptFds, 0, len); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + /* * Set the select event masks for events to watch */ - for (sid = 0; sid < socketMax; sid++) { + all = nEvents = 0; + + if (sid < 0) { + all++; + sid = 0; + } + + for (; sid < socketMax; sid++) { if ((sp = socketList[sid]) == NULL) { - continue; + if (all == 0) { + break; + } else { + continue; + } } a_assert(sp); @@ -708,39 +752,61 @@ int socketSelect() /* * Set the appropriate bit in the ready masks for the sp->sock. */ - if (sp->interestMask & SOCKET_READABLE) { + if (sp->handlerMask & SOCKET_READABLE) { readFds[index] |= bit; + nEvents++; + if (socketInputBuffered(sid) > 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } } - if (sp->interestMask & SOCKET_WRITABLE) { + if (sp->handlerMask & SOCKET_WRITABLE) { writeFds[index] |= bit; + nEvents++; } - if (sp->interestMask & SOCKET_EXCEPTION) { + if (sp->handlerMask & SOCKET_EXCEPTION) { exceptFds[index] |= bit; + nEvents++; + } + if (! all) { + break; } } /* - * Wait for the event or a timeout. + * Wait for the event or a timeout. Reset nEvents to be the number of actual + * events now. */ nEvents = select(socketHighestFd + 1, (fd_set *) readFds, - (fd_set *) writeFds, (fd_set *) exceptFds, NULL); + (fd_set *) writeFds, (fd_set *) exceptFds, &tv); + if (nEvents > 0) { - for (sid = 0; sid < socketMax; sid++) { + if (all) { + sid = 0; + } + for (; sid < socketMax; sid++) { if ((sp = socketList[sid]) == NULL) { - continue; + if (all == 0) { + break; + } else { + continue; + } } index = sp->sock / (NBBY * sizeof(fd_mask)); bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask))); - - if (readFds[index] & bit) { - sp->readyMask |= SOCKET_READABLE; + + if (readFds[index] & bit || socketInputBuffered(sid) > 0) { + sp->currentEvents |= SOCKET_READABLE; } if (writeFds[index] & bit) { - sp->readyMask |= SOCKET_WRITABLE; + sp->currentEvents |= SOCKET_WRITABLE; } if (exceptFds[index] & bit) { - sp->readyMask |= SOCKET_EXCEPTION; + sp->currentEvents |= SOCKET_EXCEPTION; + } + if (! all) { + break; } } } @@ -751,58 +817,70 @@ int socketSelect() return nEvents; } +#endif /* WIN || CE */ /******************************************************************************/ /* * Process socket events */ -void socketProcess() +void socketProcess(int sid) { socket_t *sp; - int sid; + int all; + all = 0; + if (sid < 0) { + all = 1; + sid = 0; + } /* * Process each socket */ - for (sid = 0; sid < socketMax; sid++) { + for (; sid < socketMax; sid++) { if ((sp = socketList[sid]) == NULL) { - continue; + if (! all) { + break; + } else { + continue; + } } - if ((sp->readyMask & sp->interestMask) || - ((sp->interestMask & SOCKET_READABLE) && - socketInputBuffered(sid))) { + if (socketReady(sid)) { socketDoEvent(sp); } + if (! all) { + break; + } } } /******************************************************************************/ /* - * Process and event on the event queue + * Process an event on the event queue */ static int socketDoEvent(socket_t *sp) { - ringq_t* rq; - int sid; + ringq_t *rq; + int sid; a_assert(sp); sid = sp->sid; - if (sp->readyMask & SOCKET_READABLE) { - if (sp->flags & SOCKET_LISTENING) { + if (sp->currentEvents & SOCKET_READABLE) { + if (sp->flags & SOCKET_LISTENING) { socketAccept(sp); - sp->readyMask = 0; + sp->currentEvents = 0; return 1; - } + } + } else { /* * If there is still read data in the buffers, trigger the read handler * NOTE: this may busy spin if the read handler doesn't read the data */ - if (sp->interestMask & SOCKET_READABLE && socketInputBuffered(sid)) { - sp->readyMask |= SOCKET_READABLE; + if (sp->handlerMask & SOCKET_READABLE && socketInputBuffered(sid)) { + sp->currentEvents |= SOCKET_READABLE; } } @@ -810,11 +888,11 @@ static int socketDoEvent(socket_t *sp) /* * If now writable and flushing in the background, continue flushing */ - if (sp->readyMask & SOCKET_WRITABLE) { + if (sp->currentEvents & SOCKET_WRITABLE) { if (sp->flags & SOCKET_FLUSHING) { rq = &sp->outBuf; if (ringqLen(rq) > 0) { - socketFlush(sp->sid, 0); + socketFlush(sp->sid); } else { sp->flags &= ~SOCKET_FLUSHING; } @@ -825,15 +903,14 @@ static int socketDoEvent(socket_t *sp) * Now invoke the users socket handler. NOTE: the handler may delete the * socket, so we must be very careful after calling the handler. */ - if (sp->handler && (sp->interestMask & sp->readyMask)) { - (sp->handler)(sid, sp->interestMask & sp->readyMask, + if (sp->handler && (sp->handlerMask & sp->currentEvents)) { + (sp->handler)(sid, sp->handlerMask & sp->currentEvents, sp->handler_data); /* - * Make sure socket pointer is still valid, then set the readyMask - * to 0. + * Make sure socket pointer is still valid, then reset the currentEvents. */ - if (socketPtr(sid)) { - sp->readyMask = 0; + if (socketList && sid < socketMax && socketList[sid] == sp) { + sp->currentEvents = 0; } } return 1; @@ -841,154 +918,101 @@ static int socketDoEvent(socket_t *sp) /******************************************************************************/ /* - * Allocate a new socket structure + * Set the socket blocking mode */ -static int socketAlloc(char* host, int port, socketAccept_t accept, int flags) +int socketSetBlock(int sid, int on) { - socket_t *sp; - int sid; - - if ((sid = hAlloc((void***) &socketList)) < 0) { - return -1; - } - if ((sp = (socket_t*) balloc(B_L, sizeof(socket_t))) == NULL) { - hFree((void***) &socket, sid); - return -1; - } - memset(sp, 0, sizeof(socket_t)); - socketList[sid] = sp; - if (sid >= socketMax) - socketMax = sid + 1; - - sp->sid = sid; - sp->accept = accept; - sp->port = port; - sp->flags = flags; - - if (host) { - strncpy(sp->host, host, sizeof(sp->host)); - } + socket_t *sp; + unsigned long flag; + int iflag; + int oldBlock; - ringqOpen(&sp->inBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ); - ringqOpen(&sp->outBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ); - ringqOpen(&sp->lineBuf, SOCKET_BUFSIZ, -1); - - return sid; -} - -/******************************************************************************/ -/* - * Free a socket structure - */ - -static void socketFree(int sid) -{ - socket_t* sp; - int i; + flag = iflag = !on; if ((sp = socketPtr(sid)) == NULL) { - return; - } - if (sp->sock >= 0) { - close(sp->sock); + a_assert(0); + return 0; } - - ringqClose(&sp->inBuf); - ringqClose(&sp->outBuf); - ringqClose(&sp->lineBuf); - - bfree(B_L, sp); - socketMax = hFree((void***) &socketList, sid); - -/* - * Calculate the new highest socket number - */ - socketHighestFd = -1; - for (i = 0; i < socketMax; i++) { - if ((sp = socketList[i]) == NULL) { - continue; - } - socketHighestFd = max(socketHighestFd, sp->sock); + oldBlock = (sp->flags & SOCKET_BLOCK); + sp->flags &= ~(SOCKET_BLOCK); + if (on) { + sp->flags |= SOCKET_BLOCK; } -} -/******************************************************************************/ /* - * Validate a socket handle + * Put the socket into block / non-blocking mode */ + if (sp->flags & SOCKET_BLOCK) { +#if CE || WIN + ioctlsocket(sp->sock, FIONBIO, &flag); +#elif ECOS + int off; + off = 0; + ioctl(sp->sock, FIONBIO, &off); +#elif VXWORKS + ioctl(sp->sock, FIONBIO, (int)&iflag); +#else + fcntl(sp->sock, F_SETFL, fcntl(sp->sock, F_GETFL) & ~O_NONBLOCK); +#endif -static socket_t* socketPtr(int sid) -{ - if (sid < 0 || sid >= socketMax || socketList[sid] == NULL) { - a_assert(NULL); - return NULL; + } else { +#if CE || WIN + ioctlsocket(sp->sock, FIONBIO, &flag); +#elif ECOS + int on; + on = 1; + ioctl(sp->sock, FIONBIO, &on); +#elif VXWORKS + ioctl(sp->sock, FIONBIO, (int)&iflag); +#else + fcntl(sp->sock, F_SETFL, fcntl(sp->sock, F_GETFL) | O_NONBLOCK); +#endif } - - a_assert(socketList[sid]); - return socketList[sid]; + return oldBlock; } /******************************************************************************/ /* - * Get the operating system error code + * Return true if a readable socket has buffered data. - not public */ -static int socketGetError() +int socketDontBlock() { - return errno; -} - -/******************************************************************************/ -/* - * Wait until an event occurs on a socket. Return 1 on success, 0 on failure. - */ - -static int socketWaitForEvent(socket_t* sp, int interestMask, int* errCode) -{ - a_assert(sp); + socket_t *sp; + int i; - while (socketSelect()) { - if (sp->readyMask & interestMask) { - break; + for (i = 0; i < socketMax; i++) { + if ((sp = socketList[i]) == NULL || + (sp->handlerMask & SOCKET_READABLE) == 0) { + continue; + } + if (socketInputBuffered(i) > 0) { + return 1; } } - if (sp->readyMask & SOCKET_EXCEPTION) { - return -1; - } else if (sp->readyMask & SOCKET_WRITABLE) { - return 0; - } else { - *errCode = errno = EWOULDBLOCK; - return -1; - } + return 0; } /******************************************************************************/ /* - * Put the socket into non-blocking mode + * Return true if a particular socket buffered data. - not public */ -static int socketNonBlock(socket_t *sp) +int socketSockBuffered(int sock) { - int flags; + socket_t *sp; + int i; - flags = fcntl(sp->sock, F_GETFL) | O_NONBLOCK; - if (fcntl(sp->sock, F_SETFL, flags) < 0) { - return -1; - } + for (i = 0; i < socketMax; i++) { + if ((sp = socketList[i]) == NULL || sp->sock != sock) { + continue; + } + return socketInputBuffered(i); + } return 0; } -/******************************************************************************/ -/* - * Duplicate stdin and stdout - */ - -int DuplicateStdFile (int sid) -{ - if (0 != dup2(socketList[sid]->sock, 0) || 1 != dup2(socketList[sid]->sock, 1)) - return -1; - - return 0; +#endif /* (!WIN) | LITTLEFOOT | WEBS */ -} +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/sym.c b/c/src/libnetworking/rtems_webserver/sym.c index c7b47b5e00..cc06cffe2a 100644 --- a/c/src/libnetworking/rtems_webserver/sym.c +++ b/c/src/libnetworking/rtems_webserver/sym.c @@ -1,15 +1,15 @@ /* * sym.c -- Symbol Table module * - * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ /******************************** Description *********************************/ /* - * This module implements a highly efficient generic symbol table with - * update and access routines. Symbols are simple character strings and + * This module implements a highly efficient generic symbol table with + * update and access routines. Symbols are simple character strings and * the values they take can be flexible types as defined by value_t. * This modules allows multiple symbol tables to be created. */ @@ -32,24 +32,51 @@ typedef struct { /* Symbol table descriptor */ /********************************* Globals ************************************/ -static sym_tabent_t **sym; /* List of symbol tables */ -static int sym_max; /* One past the max symbol table */ +static sym_tabent_t **sym; /* List of symbol tables */ +static int symMax; /* One past the max symbol table */ +static int symOpenCount = 0; /* Count of apps using sym */ -static int htIndex; /* Current location in table */ -static sym_t* next; /* Next symbol in iteration */ +static int htIndex; /* Current location in table */ +static sym_t* next; /* Next symbol in iteration */ /**************************** Forward Declarations ****************************/ -static int hashIndex(sym_tabent_t *tp, char_t *name); -static sym_t *hash(sym_tabent_t *tp, char_t *name); -static int calc_prime(int size); +static int hashIndex(sym_tabent_t *tp, char_t *name); +static sym_t *hash(sym_tabent_t *tp, char_t *name); +static int calcPrime(int size); /*********************************** Code *************************************/ +/* + * Open the symbol table subSystem. + */ + +int symSubOpen() +{ + if (++symOpenCount == 1) { + symMax = 0; + sym = NULL; + } + return 0; +} + +/******************************************************************************/ +/* + * Close the symbol table subSystem. + */ + +void symSubClose() +{ + if (--symOpenCount <= 0) { + symOpenCount = 0; + } +} + +/******************************************************************************/ /* * Create a symbol table. */ -sym_fd_t symOpen(int hash_size) +sym_fd_t symOpen(int hash_size) { sym_fd_t sd; sym_tabent_t *tp; @@ -67,20 +94,20 @@ sym_fd_t symOpen(int hash_size) * Create a new symbol table structure and zero */ if ((tp = (sym_tabent_t*) balloc(B_L, sizeof(sym_tabent_t))) == NULL) { - sym_max = hFree((void***) &sym, sd); + symMax = hFree((void***) &sym, sd); return -1; } memset(tp, 0, sizeof(sym_tabent_t)); - if (sd >= sym_max) { - sym_max = sd + 1; + if (sd >= symMax) { + symMax = sd + 1; } - a_assert(0 <= sd && sd < sym_max); + a_assert(0 <= sd && sd < symMax); sym[sd] = tp; /* * Now create the hash table for fast indexing. */ - tp->hash_size = calc_prime(hash_size); + tp->hash_size = calcPrime(hash_size); tp->hash_table = (sym_t**) balloc(B_L, tp->hash_size * sizeof(sym_t*)); a_assert(tp->hash_table); memset(tp->hash_table, 0, tp->hash_size * sizeof(sym_t*)); @@ -94,13 +121,13 @@ sym_fd_t symOpen(int hash_size) * to free resources associated with each symbol table entry. */ -void symClose(sym_fd_t sd, void (*cleanup)(sym_t *symp)) +void symClose(sym_fd_t sd) { sym_tabent_t *tp; sym_t *sp, *forw; - int i; + int i; - a_assert(0 <= sd && sd < sym_max); + a_assert(0 <= sd && sd < symMax); tp = sym[sd]; a_assert(tp); @@ -110,30 +137,18 @@ void symClose(sym_fd_t sd, void (*cleanup)(sym_t *symp)) for (i = 0; i < tp->hash_size; i++) { for (sp = tp->hash_table[i]; sp; sp = forw) { forw = sp->forw; - if (cleanup) { - (*cleanup)(sp); - } valueFree(&sp->name); + valueFree(&sp->content); bfree(B_L, (void*) sp); sp = forw; } } bfree(B_L, (void*) tp->hash_table); - sym_max = hFree((void***) &sym, sd); + symMax = hFree((void***) &sym, sd); bfree(B_L, (void*) tp); } -/******************************************************************************/ -/* - * Default callback for freeing the value. - */ - -void symFreeVar(sym_t* sp) -{ - valueFree(&sp->content); -} - /******************************************************************************/ /* * Return the first symbol in the hashtable if there is one. This call is used @@ -145,9 +160,9 @@ sym_t* symFirst(sym_fd_t sd) { sym_tabent_t *tp; sym_t *sp, *forw; - int i; + int i; - a_assert(0 <= sd && sd < sym_max); + a_assert(0 <= sd && sd < symMax); tp = sym[sd]; a_assert(tp); @@ -180,9 +195,9 @@ sym_t* symNext(sym_fd_t sd) { sym_tabent_t *tp; sym_t *sp, *forw; - int i; + int i; - a_assert(0 <= sd && sd < sym_max); + a_assert(0 <= sd && sd < symMax); tp = sym[sd]; a_assert(tp); @@ -219,9 +234,10 @@ sym_t *symLookup(sym_fd_t sd, char_t *name) sym_t *sp; char_t *cp; - a_assert(0 <= sd && sd < sym_max); - tp = sym[sd]; - a_assert(tp); + a_assert(0 <= sd && sd < symMax); + if ((tp = sym[sd]) == NULL) { + return NULL; + } if (name == NULL || *name == '\0') { return NULL; @@ -241,8 +257,10 @@ sym_t *symLookup(sym_fd_t sd, char_t *name) /******************************************************************************/ /* - * Enter a symbol into the table. If already there, update its value. - * Always succeeds if memory available. + * Enter a symbol into the table. If already there, update its value. + * Always succeeds if memory available. We allocate a copy of "name" here + * so it can be a volatile variable. The value "v" is just a copy of the + * passed in value, so it MUST be persistent. */ sym_t *symEnter(sym_fd_t sd, char_t *name, value_t v, int arg) @@ -252,13 +270,13 @@ sym_t *symEnter(sym_fd_t sd, char_t *name, value_t v, int arg) char_t *cp; int hindex; - a_assert(name && *name); - a_assert(0 <= sd && sd < sym_max); + a_assert(name); + a_assert(0 <= sd && sd < symMax); tp = sym[sd]; a_assert(tp); /* - * Calculate the first daisy-chain from the hash table. If non-zero, then + * Calculate the first daisy-chain from the hash table. If non-zero, then * we have daisy-chain, so scan it and look for the symbol. */ last = NULL; @@ -333,12 +351,12 @@ int symDelete(sym_fd_t sd, char_t *name) int hindex; a_assert(name && *name); - a_assert(0 <= sd && sd < sym_max); + a_assert(0 <= sd && sd < symMax); tp = sym[sd]; a_assert(tp); /* - * Calculate the first daisy-chain from the hash table. If non-zero, then + * Calculate the first daisy-chain from the hash table. If non-zero, then * we have daisy-chain, so scan it and look for the symbol. */ last = NULL; @@ -355,7 +373,7 @@ int symDelete(sym_fd_t sd, char_t *name) if (sp == (sym_t*) NULL) { /* Not Found */ return -1; } - + /* * Unlink and free the symbol. Last will be set if the element to be deleted * is not first in the chain. @@ -366,6 +384,7 @@ int symDelete(sym_fd_t sd, char_t *name) tp->hash_table[hindex] = sp->forw; } valueFree(&sp->name); + valueFree(&sp->content); bfree(B_L, (void*) sp); return 0; @@ -393,13 +412,13 @@ static sym_t *hash(sym_tabent_t *tp, char_t *name) static int hashIndex(sym_tabent_t *tp, char_t *name) { - unsigned int sum; - int i; + unsigned int sum; + int i; a_assert(tp); /* * Add in each character shifted up progressively by 7 bits. The shift - * amount is rounded so as to not shift too far. It thus cycles with each + * amount is rounded so as to not shift too far. It thus cycles with each * new cycle placing character shifted up by one bit. */ i = 0; @@ -416,13 +435,14 @@ static int hashIndex(sym_tabent_t *tp, char_t *name) * Check if this number is a prime */ -static int is_prime(int n) +static int isPrime(int n) { - int i; + int i, max; a_assert(n > 0); - for (i = 2; i < n; i++) { + max = n / 2; + for (i = 2; i <= max; i++) { if (n % i == 0) { return 0; } @@ -435,14 +455,14 @@ static int is_prime(int n) * Calculate the largest prime smaller than size. */ -static int calc_prime(int size) +static int calcPrime(int size) { int count; a_assert(size > 0); for (count = size; count > 0; count--) { - if (is_prime(count)) { + if (isPrime(count)) { return count; } } @@ -450,3 +470,4 @@ static int calc_prime(int size) } /******************************************************************************/ + diff --git a/c/src/libnetworking/rtems_webserver/uemf.c b/c/src/libnetworking/rtems_webserver/uemf.c index ef7c8f3135..aa177849a2 100644 --- a/c/src/libnetworking/rtems_webserver/uemf.c +++ b/c/src/libnetworking/rtems_webserver/uemf.c @@ -1,7 +1,7 @@ /* * uemf.c -- GoAhead Micro Embedded Management Framework * - * Copyright (c) Go Ahead Software, Inc., 1995-1999 + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -22,31 +22,62 @@ int emfInst; /* Application instance handle */ +/****************************** Forward Declarations **************************/ + +extern void defaultErrorHandler(int etype, char_t *buf); +static void (*errorHandler)(int etype, char_t *msg) = defaultErrorHandler; + +extern void defaultTraceHandler(int level, char_t *buf); +static void (*traceHandler)(int level, char_t *buf) = defaultTraceHandler; + /************************************* Code ***********************************/ /* * Error message that doesn't need user attention. Customize this code - * to direct error messages to whereever the developer wishes + * to direct error messages to wherever the developer wishes */ -void error(E_ARGS_DEC, int flags, char_t *fmt, ...) +void error(E_ARGS_DEC, int etype, char_t *fmt, ...) { - va_list arglist; - - va_start(arglist, fmt); - - if (flags & E_LOG) { - /* Log error message */ + va_list args; + char_t *fmtBuf, *buf; - } else if (flags & E_ASSERT) { - /* Assert message */ + va_start(args, fmt); + fmtValloc(&fmtBuf, E_MAX_ERROR, fmt, args); - } else if (flags & E_USER) { - /* Display message to the user */ + if (etype == E_LOG) { + fmtAlloc(&buf, E_MAX_ERROR, T("%s\n"), fmtBuf); +#if DEV + } else if (etype == E_ASSERT) { + fmtAlloc(&buf, E_MAX_ERROR, + T("Assertion %s, failed at %s %d\n"), fmtBuf, E_ARGS); +#endif + } else if (etype == E_USER) { + fmtAlloc(&buf, E_MAX_ERROR, T("%s\n"), fmtBuf); + } + va_end(args); + bfree(B_L, fmtBuf); + + if (errorHandler) { + errorHandler(etype, buf); } - vprintf (fmt, arglist); - va_end(arglist); + bfreeSafe(B_L, buf); +} + +/******************************************************************************/ +/* + * Replace the default error handler. Return pointer to old error handler. + */ + +void (*errorSetHandler(void (*function)(int etype, char_t *msg))) \ + (int etype, char_t *msg) +{ + void (*oldHandler)(int etype, char_t *buf); + + oldHandler = errorHandler; + errorHandler = function; + return oldHandler; } /******************************************************************************/ @@ -54,21 +85,48 @@ void error(E_ARGS_DEC, int flags, char_t *fmt, ...) * Trace log. Customize this function to log trace output */ -void goahead_trace(int level, char_t *afmt, ...) +void trace(int level, char_t *fmt, ...) { -#if DEBUG va_list args; char_t *buf; - va_start(args, afmt); - buf = NULL; - gvsnprintf(&buf, VALUE_MAX_STRING, afmt, args); - if (buf) { - gprintf(buf); - bfree(B_L, buf); + va_start(args, fmt); + fmtValloc(&buf, VALUE_MAX_STRING, fmt, args); + + if (traceHandler) { + traceHandler(level, buf); } + bfreeSafe(B_L, buf); va_end(args); -#endif +} + +/******************************************************************************/ +/* + * Trace log. Customize this function to log trace output + */ + +void traceRaw(char_t *buf) +{ + if (traceHandler) { + traceHandler(0, buf); + } +} + +/******************************************************************************/ +/* + * Replace the default trace handler. Return a pointer to the old handler. + */ + +void (*traceSetHandler(void (*function)(int level, char_t *buf))) + (int level, char *buf) +{ + void (*oldHandler)(int level, char_t *buf); + + oldHandler = traceHandler; + if (function) { + traceHandler = function; + } + return oldHandler; } /******************************************************************************/ @@ -197,4 +255,27 @@ char_t *stritoa(int n, char_t *string, int width) } /******************************************************************************/ +/* + * Stubs + */ +char_t *basicGetProduct() +{ + return T("uemf"); +} + +char_t *basicGetAddress() +{ + return T("localhost"); +} + +int errorOpen(char_t *pname) +{ + return 0; +} + +void errorClose() +{ +} + +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/uemf.h b/c/src/libnetworking/rtems_webserver/uemf.h index 8945907dfb..59533aac00 100644 --- a/c/src/libnetworking/rtems_webserver/uemf.h +++ b/c/src/libnetworking/rtems_webserver/uemf.h @@ -1,7 +1,7 @@ /* - * uemf.h -- Go Ahead Micro Embedded Management Framework Header + * uemf.h -- GoAhead Micro Embedded Management Framework Header * - * Copyright (c) Go Ahead Software, Inc., 1995-1999 + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -12,7 +12,7 @@ /******************************** Description *********************************/ /* - * Go Ahead Web Server header. This defines the Web public APIs + * GoAhead Web Server header. This defines the Web public APIs */ /******************************* Per O/S Includes *****************************/ @@ -29,7 +29,9 @@ #include #include #include -#endif + #include + #include +#endif /* WIN */ #if CE #include @@ -37,15 +39,26 @@ #include #include #include "CE/wincompat.h" -#endif + #include +#endif /* CE */ #if NW #include -#endif +#endif /* NW */ + +#if SCOV5 + #include + #include + #include "sys/socket.h" + #include "sys/select.h" + #include "netinet/in.h" + #include "arpa/inet.h" + #include "netdb.h" +#endif /* SCOV5 */ #if UNIX #include -#endif +#endif /* UNIX */ #if LINUX || __rtems__ #include @@ -61,7 +74,9 @@ #include #include #include -#endif + #include + #include +#endif /* LINUX */ #if LYNX #include @@ -74,13 +89,15 @@ #include #include #include -#endif + #include + #include +#endif /* LYNX */ #if UW #include -#endif +#endif /* UW */ -#if VXW486 +#if VXWORKS #include #include #include @@ -90,7 +107,25 @@ #include #include #include -#endif + #include + #include +#endif /* VXWORKS */ + +#if SOLARIS + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif /* SOLARIS */ #if QNX4 #include @@ -100,7 +135,20 @@ #include #include #include -#endif + #include + #include + #include + #include +#endif /* QNX4 */ + +#if ECOS + #include + #include + #include + #include + #include + #include +#endif /* ECOS */ /********************************** Includes **********************************/ @@ -108,6 +156,69 @@ #include #include +#if ! WEBS +#include "messages.h" +#endif /* ! WEBS */ + +/******************************* Per O/S Defines *****************************/ + +#if UW + #define __NO_PACK 1 +#endif /* UW */ + +#if SCOV5 || VXWORKS || LINUX || LYNX || __rtems__ +#ifndef O_BINARY +#define O_BINARY 0 +#endif /* O_BINARY */ +#define SOCKET_ERROR -1 +#endif /* SCOV5 || VXWORKS || LINUX || LYNX */ + +#if WIN || CE +/* + * __NO_FCNTL means can't access fcntl function. Fcntl.h is still available. + */ +#define __NO_FCNTL 1 + +#undef R_OK +#define R_OK 4 +#undef W_OK +#define W_OK 2 +#undef X_OK +#define X_OK 1 +#undef F_OK +#define F_OK 0 +#endif /* WIN || CE */ + +#if LINUX && !__rtems__ && ! _STRUCT_TIMEVAL +struct timeval +{ + time_t tv_sec; /* Seconds. */ + time_t tv_usec; /* Microseconds. */ +}; +#define _STRUCT_TIMEVAL 1 +#endif /* LINUX && ! _STRUCT_TIMEVAL */ + +#if ECOS + #define O_RDONLY 1 + #define O_BINARY 2 + + #define __NO_PACK 1 + #define __NO_EJ_FILE 1 + #define __NO_CGI_BIN 1 + #define __NO_FCNTL 1 + +/* + * #define LIBKERN_INLINE to avoid kernel inline functions + */ + #define LIBKERN_INLINE + +#endif /* ECOS */ + +#if QNX4 + typedef long fd_mask; + #define NFDBITS (sizeof (fd_mask) * NBBY) /* bits per mask */ +#endif /* QNX4 */ + /********************************** Unicode ***********************************/ /* * Constants and limits. Also FNAMESIZE and PATHSIZE are currently defined @@ -119,34 +230,85 @@ #define XML_MAX 4096 /* Maximum size for tags/tokens */ #define BUF_MAX 4096 /* General sanity check for bufs */ -/* - * Type for unicode systems - */ -#if UNICODE +#if LITTLEFOOT || WEBS +#define LF_BUF_MAX (510) +#define LF_PATHSIZE LF_BUF_MAX +#else +#define LF_BUF_MAX BUF_MAX +#define LF_PATHSIZE PATHSIZE +#define UPPATHSIZE PATHSIZE +#endif /* LITTLEFOOT || WEBS */ +#ifndef CHAR_T_DEFINED +#define CHAR_T_DEFINED 1 +#if UNICODE /* * To convert strings to UNICODE. We have a level of indirection so things * like T(__FILE__) will expand properly. */ -#define T(x) __TXT(x) -#define __TXT(s) L ## s -typedef unsigned short char_t; -typedef unsigned short uchar_t; +#define T(x) __TXT(x) +#define __TXT(s) L ## s +typedef unsigned short char_t; +typedef unsigned short uchar_t; /* * Text size of buffer macro. A buffer bytes will hold (size / char size) * characters. */ -#define TSZ(x) (sizeof(x) / sizeof(char_t)) +#define TSZ(x) (sizeof(x) / sizeof(char_t)) /* * How many ASCII bytes are required to represent this UNICODE string? */ -#define TASTRL(x) ((wcslen(x) + 1) * sizeof(char_t)) +#define TASTRL(x) ((wcslen(x) + 1) * sizeof(char_t)) + +#else +#define T(s) s +typedef char char_t; +#define TSZ(x) (sizeof(x)) +#define TASTRL(x) (strlen(x) + 1) +#if WIN +typedef unsigned char uchar_t; +#endif /* WIN */ +#endif /* UNICODE */ + +#endif /* ! CHAR_T_DEFINED */ + +/* + * "Boolean" constants + */ + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* + * GoAhead Copyright. + */ +#define GOAHEAD_COPYRIGHT \ + T("Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.") + +/* + * The following include has to be after the unicode defines. By putting it + * here, many modules in various parts of the tree are cleaner. + */ +#if LITTLEFOOT && INMEM + #include "lf/inmem.h" +#endif /* LITTLEFOOT && INMEM */ + +/* + * Type for unicode systems + */ +#if UNICODE #define gmain wmain +#define gasctime _wasctime #define gsprintf swprintf #define gprintf wprintf #define gfprintf fwprintf @@ -165,15 +327,23 @@ typedef unsigned short uchar_t; #define gstrrchr wcsrchr #define gstrtok wcstok #define gstrnset wcsnset +#define gstrrchr wcsrchr #define gstrstr wcsstr +#define gstrtol wcstol #define gfopen _wfopen #define gopen _wopen +#define gclose close #define gcreat _wcreat #define gfgets fgetws #define gfputs fputws +#define gfscanf fwscanf +#define ggets _getws +#define glseek lseek #define gunlink _wunlink +#define gread read #define grename _wrename +#define gwrite write #define gtmpnam _wtmpnam #define gtempnam _wtempnam #define gfindfirst _wfindfirst @@ -182,24 +352,34 @@ typedef unsigned short uchar_t; #define gfindclose _findclose #define gstat _wstat #define gaccess _waccess +#define gchmod _wchmod typedef struct _stat gstat_t; #define gmkdir _wmkdir #define gchdir _wchdir #define grmdir _wrmdir -#define gremove _wremove #define ggetcwd _wgetcwd #define gtolower towlower #define gtoupper towupper +#if CE +#define gisspace isspace +#define gisdigit isdigit +#define gisxdigit isxdigit +#define gisupper isupper +#define gislower islower +#define gisprint isprint +#else +#define gremove _wremove #define gisspace iswspace #define gisdigit iswdigit #define gisxdigit iswxdigit -#define gisalnum iswalnum -#define gisalpha iswalpha #define gisupper iswupper #define gislower iswlower +#endif /* if CE */ +#define gisalnum iswalnum +#define gisalpha iswalpha #define gatoi(s) wcstol(s, NULL, 10) #define gctime _wctime @@ -208,14 +388,57 @@ typedef struct _stat gstat_t; #else /* ! UNICODE */ -#define T(s) s -#define TSZ(x) (sizeof(x)) -#define TASTRL(x) (strlen(x) + 1) -typedef char char_t; -#if WIN -typedef unsigned char uchar_t; -#endif +#ifndef gopen +#if INMEM +#define gclose imClose +#define gclosedir imClosedir +#define gchdir imChdir +#define gchmod imChmod +#define ggetcwd imGetcwd +#define glseek imLseek +#define gloadModule imLoadModule +#define gmkdir imMkdir +#define gopen imOpen +#define gopendir imOpendir +#define gread imRead +#define greaddir imReaddir +#define grename imRename +#define grmdir imRmdir +#define gstat imStat +#define gunlink imUnlink +#define gwrite imWrite +#else +#define gclose close +#define gclosedir closedir +#if VXWORKS +#define gchdir vxchdir +#define gmkdir vxmkdir +#define grmdir vxrmdir +#else +#if LYNX || LINUX || SOLARIS +#define gmkdir(s) mkdir(s,0755) +#else +#define gmkdir mkdir +#endif /* LYNX || LINUX || SOLARIS */ +#define grmdir rmdir +#define gchdir chdir +#endif /* VXWORKS */ +#define gchmod chmod +#define ggetcwd getcwd +#define glseek lseek +#define gloadModule loadModule +#define gopen open +#define gopendir opendir +#define gread read +#define greaddir readdir +#define grename rename +#define gstat stat +#define gunlink unlink +#define gwrite write +#endif /* INMEM */ +#endif /* ! gopen */ +#define gasctime asctime #define gsprintf sprintf #define gprintf printf #define gfprintf fprintf @@ -229,36 +452,32 @@ typedef unsigned char uchar_t; #define gstrcat strcat #define gstrcmp strcmp #define gstrncmp strncmp -#define gstricmp stricmp +#define gstricmp strcmpci #define gstrchr strchr #define gstrrchr strrchr #define gstrtok strtok #define gstrnset strnset +#define gstrrchr strrchr #define gstrstr strstr +#define gstrtol strtol #define gfopen fopen -#define gopen open #define gcreat creat #define gfgets fgets #define gfputs fputs -#define gunlink unlink -#define grename rename +#define gfscanf fscanf +#define ggets gets #define gtmpnam tmpnam #define gtempnam tempnam #define gfindfirst _findfirst #define gfinddata_t _finddata_t #define gfindnext _findnext #define gfindclose _findclose -#define gstat stat #define gaccess access typedef struct stat gstat_t; -#define gmkdir mkdir -#define gchdir chdir -#define grmdir rmdir #define gremove remove -#define ggetcwd getcwd #define gtolower tolower #define gtoupper toupper @@ -274,16 +493,23 @@ typedef struct stat gstat_t; #define gctime ctime #define ggetenv getenv #define gexecvp execvp -#ifndef VXW486 +#ifndef VXWORKS #define gmain main -#endif -#endif +#endif /* ! VXWORKS */ +#if VXWORKS +#define fcntl(a, b, c) +#endif /* VXWORKS */ +#endif /* ! UNICODE */ /********************************** Defines ***********************************/ -#define FNAMESIZE 256 /* Max length of file names */ +#ifndef FNAMESIZE +#define FNAMESIZE 254 /* Max length of file names */ +#endif /* FNAMESIZE */ #define E_MAX_ERROR 4096 +#define URL_MAX 4096 + /* * Error types */ @@ -291,79 +517,84 @@ typedef struct stat gstat_t; #define E_LOG 0x2 /* Log error to log file */ #define E_USER 0x3 /* Error that must be displayed */ -#define E_L T(__FILE__), __LINE__ -#define E_ARGS_DEC char_t *file, int line -#define E_ARGS file, line +#define E_L T(__FILE__), __LINE__ +#define E_ARGS_DEC char_t *file, int line +#define E_ARGS file, line -#if ASSERT - #define a_assert(C) if (C) ; else error(E_L, E_ASSERT, T("%s"), #C) +#if ASSERT || ASSERT_CE + #define a_assert(C) if (C) ; else error(E_L, E_ASSERT, T("%s"), T(#C)) #else #define a_assert(C) if (1) ; else -#endif - -#define VALUE_VALID { {0}, integer, 1 } -#define VALUE_INVALID { {0}, undefined, 0 } - -/* - * Allocation flags - */ -#define VALUE_ALLOCATE 0x1 - -#define value_numeric(t) (t == integer) -#define value_str(t) (t >= string || t <= bytes) -#define value_ok(t) (t > undefined && t <= symbol) +#endif /* ASSERT || ASSERT_CE */ +/******************************************************************************/ +/* VALUE */ +/******************************************************************************/ /* * These values are not prefixed so as to aid code readability */ -#if !defined(UW) && !defined(__rtems__) -#pragma pack(2) -#endif typedef enum { undefined = 0, - integer = 1, - string = 2, - bytes = 3, - errmsg = 4 -} value_type_t; + byteint = 1, + shortint = 2, + integer = 3, + hex = 4, + percent = 5, + octal = 6, + big = 7, + flag = 8, + floating = 9, + string = 10, + bytes = 11, + symbol = 12, + errmsg = 13 +} vtype_t; + +#ifndef __NO_PACK +#pragma pack(2) +#endif /* _NO_PACK */ -/* - * In UW, bit fields default to unsigned unless explicitly defined as signed. - * Unfortunately, enum become ints, but not explicitly signed. Thus, if using - * an enum type in a bit field, it becomes unsigned, but we need signed. So - * for UW we declare value_type_t to be a signed int to make this all work. - */ typedef struct { union { + char flag; + char byteint; + short shortint; + char percent; long integer; + long hex; + long octal; + long big[2]; +#if FLOATING_POINT_SUPPORT + double floating; +#endif /* FLOATING_POINT_SUPPORT */ char_t *string; - char_t *bytes; + char *bytes; char_t *errmsg; void *symbol; } value; -#if UW - signed int type : 8; -#else - value_type_t type : 8; -#endif - - unsigned int valid : 1; - unsigned int user_def_1 : 1; - unsigned int allocated : 1; /* String was balloced */ + vtype_t type : 16; + unsigned int valid : 8; + unsigned int allocated : 8; /* String was balloced */ } value_t; +#ifndef __NO_PACK +#pragma pack() +#endif /* __NO_PACK */ + /* - * Extract a string from the value depending whether inline or via pointer + * Allocation flags */ -#define value_strget(v) \ - (((v)->type == bytes) ? (v)->value.bytes : (v)->value.string) +#define VALUE_ALLOCATE 0x1 -#if !defined(UW) && !defined(__rtems__) -#pragma pack() -#endif +#define value_numeric(t) (t >= byteint && t <= big) +#define value_str(t) (t >= string && t <= bytes) +#define value_ok(t) (t > undefined && t <= symbol) + +#define VALUE_VALID { {0}, integer, 1 } +#define VALUE_INVALID { {0}, undefined, 0 } /******************************************************************************/ /* @@ -417,16 +648,28 @@ typedef struct { } ringq_t; /* - * Block allocation definitions + * Block allocation (balloc) definitions */ -#define B_L __FILE__, __LINE__ -#define B_ARGS_DEC char *file, int line +#ifdef B_STATS +#ifndef B_L +#define B_L T(__FILE__), __LINE__ +#define B_ARGS_DEC char_t *file, int line #define B_ARGS file, line +#endif /* B_L */ +#endif /* B_STATS */ /* * Block classes are: 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, * 16384, 32768, 65536 */ +typedef struct { + union { + void *next; /* Pointer to next in q */ + int size; /* Actual requested size */ + } u; + int flags; /* Per block allocation flags */ +} bType; + #define B_SHIFT 4 /* Convert size to class */ #define B_ROUND ((1 << (B_SHIFT)) - 1) #define B_MAX_CLASS 13 /* Maximum class number + 1 */ @@ -438,13 +681,12 @@ typedef struct { #define B_MAX_BLOCKS (64 * 1024) /* Maximum allocated blocks */ /* - * Flags. The integer value is used as an arbitrary value to fill the flags. + * Flags. The integrity value is used as an arbitrary value to fill the flags. */ - -#define B_USE_MALLOC 0x1 /* Okay to use malloc if required */ -#define B_USER_BUF 0x2 /* User supplied buffer for mem */ #define B_INTEGRITY 0x8124000 /* Integrity value */ #define B_INTEGRITY_MASK 0xFFFF000 /* Integrity mask */ +#define B_USE_MALLOC 0x1 /* Okay to use malloc if required */ +#define B_USER_BUF 0x2 /* User supplied buffer for mem */ /* * The symbol table record for each symbol entry @@ -459,15 +701,68 @@ typedef struct sym_t { typedef int sym_fd_t; /* Returned by symOpen */ +/* + * Script engines + */ +#define EMF_SCRIPT_JSCRIPT 0 /* javascript */ +#define EMF_SCRIPT_TCL 1 /* tcl */ +#define EMF_SCRIPT_EJSCRIPT 2 /* Ejscript */ +#define EMF_SCRIPT_MAX 3 + +#define MAXINT INT_MAX +#define BITSPERBYTE 8 +#define BITS(type) (BITSPERBYTE * (int) sizeof(type)) +#define STRSPACE T("\t \n\r\t") + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif /* max */ + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif /* min */ + +/******************************************************************************/ +/* CRON */ +/******************************************************************************/ + +typedef struct { + char_t *minute; + char_t *hour; + char_t *day; + char_t *month; + char_t *dayofweek; +} cron_t; + +extern long cronUntil(cron_t *cp, int period, time_t testTime); +extern int cronAlloc(cron_t *cp, char_t *str); +extern int cronFree(cron_t *cp); + +/******************************************************************************/ +/* SOCKET */ +/******************************************************************************/ /* * Socket flags */ + +#if (WIN || CE) && WEBS +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ENETDOWN WSAENETDOWN +#define ECONNRESET WSAECONNRESET +#endif /* (WIN || CE) && WEBS) */ + #define SOCKET_EOF 0x1 /* Seen end of file */ #define SOCKET_CONNECTING 0x2 /* Connect in progress */ #define SOCKET_BROADCAST 0x4 /* Broadcast mode */ #define SOCKET_PENDING 0x8 /* Message pending on this socket */ #define SOCKET_FLUSHING 0x10 /* Background flushing */ -#define SOCKET_LISTENING 0x20 /* Server socket listening */ +#define SOCKET_DATAGRAM 0x20 /* Use datagrams */ +#define SOCKET_ASYNC 0x40 /* Use async connect */ +#define SOCKET_BLOCK 0x80 /* Use blocking I/O */ +#define SOCKET_LISTENING 0x100 /* Socket is server listener */ +#define SOCKET_CLOSING 0x200 /* Socket is closing */ + +#define SOCKET_PORT_MAX 0xffff /* Max Port size */ /* * Socket error values @@ -485,181 +780,261 @@ typedef int sym_fd_t; /* Returned by symOpen */ #define SOCKET_READABLE 0x2 /* Make socket readable */ #define SOCKET_WRITABLE 0x4 /* Make socket writable */ #define SOCKET_EXCEPTION 0x8 /* Interested in exceptions */ +#define EMF_SOCKET_MESSAGE (WM_USER+13) -#define SOCKET_BUFSIZ 512 /* Underlying buffer size */ +#if LITTLEFOOT +#define SOCKET_BUFSIZ 510 /* Underlying buffer size */ +#else +#define SOCKET_BUFSIZ 1024 /* Underlying buffer size */ +#endif /* LITTLEFOOT */ typedef void (*socketHandler_t)(int sid, int mask, int data); -typedef int (*socketAccept_t)(int sid, char *ipaddr, int port); +typedef int (*socketAccept_t)(int sid, char *ipaddr, int port, + int listenSid); +typedef struct { + char host[64]; /* Host name */ + ringq_t inBuf; /* Input ring queue */ + ringq_t outBuf; /* Output ring queue */ + ringq_t lineBuf; /* Line ring queue */ + socketAccept_t accept; /* Accept handler */ + socketHandler_t handler; /* User I/O handler */ + int handler_data; /* User handler data */ + int handlerMask; /* Handler events of interest */ + int sid; /* Index into socket[] */ + int port; /* Port to listen on */ + int flags; /* Current state flags */ + int sock; /* Actual socket handle */ + int fileHandle; /* ID of the file handler */ + int interestEvents; /* Mask of events to watch for */ + int currentEvents; /* Mask of ready events (FD_xx) */ + int selectEvents; /* Events being selected */ + int saveMask; /* saved Mask for socketFlush */ + int error; /* Last error */ +} socket_t; +/********************************* Prototypes *********************************/ /* - * Script engines + * Balloc module + * */ -#define EMF_SCRIPT_JSCRIPT 0 /* javascript */ -#define EMF_SCRIPT_TCL 1 /* tcl */ -#define EMF_SCRIPT_EJSCRIPT 2 /* Ejscript */ -#define EMF_SCRIPT_MAX 3 -#define MAXINT INT_MAX -#define BITSPERBYTE 8 -#define BITS(type) (BITSPERBYTE * (int) sizeof(type)) - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif +extern void bclose(); +extern int bopen(void *buf, int bufsize, int flags); -/******************************* Per O/S Defines ******************************/ +/* + * Define NO_BALLOC to turn off our balloc module altogether + * #define NO_BALLOC 1 + */ -#if VXW486 || LINUX || __rtems__ || LYNX -#ifndef O_BINARY -#define O_BINARY 0 -#endif -#define SOCKET_ERROR -1 -#endif +#if NO_BALLOC +#define balloc(B_ARGS, num) malloc(num) +#define bfree(B_ARGS, p) free(p) +#define bfreeSafe(B_ARGS, p) \ + if (p) { free(p); } else +#define brealloc(B_ARGS, p, num) realloc(p, num) +extern char_t *bstrdupNoBalloc(char_t *s); +extern char *bstrdupANoBalloc(char *s); +#define bstrdup(B_ARGS, s) bstrdupNoBalloc(s) +#define bstrdupA(B_ARGS, s) bstrdupANoBalloc(s) +#define gstrdup(B_ARGS, s) bstrdupNoBalloc(s) -#if WIN -#undef R_OK -#define R_OK 4 -#undef W_OK -#define W_OK 2 -#undef X_OK -#define X_OK 1 -#undef F_OK -#define F_OK 0 -#endif +#else /* BALLOC */ -/********************************* Prototypes *********************************/ +#ifndef B_STATS +#define balloc(B_ARGS, num) balloc(num) +#define bfree(B_ARGS, p) bfree(p) +#define bfreeSafe(B_ARGS, p) bfreeSafe(p) +#define brealloc(B_ARGS, p, size) brealloc(p, size) +#define bstrdup(B_ARGS, p) bstrdup(p) -extern void bclose(); -extern int bopen(void *buf, int bufsize, int flags); -#if NO_BALLOC -#undef B_L -#define B_L x -#define balloc(x, num) malloc(num) -#define bfree(x, p) free(p) -#define bfreeSafe(x, p) \ - if (p) { bfree(x, p); } else -#define brealloc(x, p, num) realloc(p, num) -extern char *bstrdupANoBalloc(char *s); -#define bstrdupA(x, s) bstrdupANoBalloc(s) #if UNICODE -extern char_t *bstrdupNoBalloc(char_t *s); -#define bstrdup(x, s) bstrdupNoBalloc(s) -#define gstrdup(x, s) bstrdupNoBalloc(s) -#else /* Not UNICODE */ -#define bstrdup(x, s) bstrdupANoBalloc(s) -#define gstrdup(x, s) bstrdupANoBalloc(s) +#define bstrdupA(B_ARGS, p) bstrdupA(p) +#else /* UNICODE */ +#define bstrdupA bstrdup #endif /* UNICODE */ -#else /* BALLOC */ +#endif /* B_STATS */ + +#define gstrdup bstrdup extern void *balloc(B_ARGS_DEC, int size); -extern void bfree(B_ARGS_DEC, void *mp); -extern void *brealloc(B_ARGS_DEC, void *buf, int newsize); -extern void bstats(int handle, void (*writefn)(int fd, char_t *fmt, ...)); -extern char_t *bstrdup(B_ARGS_DEC, char_t *s); -extern void bfreeSafe(B_ARGS_DEC, void* mp); -#define gstrdup(B_ARGS, s) bstrdup(B_ARGS, s) +extern void bfree(B_ARGS_DEC, void *mp); +extern void bfreeSafe(B_ARGS_DEC, void *mp); +extern void *brealloc(B_ARGS_DEC, void *buf, int newsize); +extern char_t *bstrdup(B_ARGS_DEC, char_t *s); + #if UNICODE extern char *bstrdupA(B_ARGS_DEC, char *s); -#else +#else /* UNICODE */ #define bstrdupA bstrdup #endif /* UNICODE */ #endif /* BALLOC */ +extern void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)); + +/* + * Flags. The integrity value is used as an arbitrary value to fill the flags. + */ +#define B_USE_MALLOC 0x1 /* Okay to use malloc if required */ +#define B_USER_BUF 0x2 /* User supplied buffer for mem */ + + #if !LINUX && !__rtems__ -extern char_t* basename(char_t* name); -#endif +extern char_t *basename(char_t *name); +#endif /* !LINUX */ -extern void *emfCreateTimer(int delay, void (*routine)(long arg), long arg); -extern void emfDeleteTimer(void *id); -extern int emfInstGet(); -extern void emfInstSet(int inst); -extern void error(E_ARGS_DEC, int flags, char_t *fmt, ...); +#if UEMF && WEBS +/* + * The open source webserver uses a different callback/timer mechanism + * than other emf derivative products such as FieldUpgrader agents + * so redefine those API for webserver so that they can coexist in the + * same address space as the others. + */ +#define emfSchedCallback websSchedCallBack +#define emfUnschedCallback websUnschedCallBack +#define emfReschedCallback websReschedCallBack +#endif /* UEMF && WEBS */ + +typedef void (emfSchedProc)(void *data, int id); +extern int emfSchedCallback(int delay, emfSchedProc *proc, void *arg); +extern void emfUnschedCallback(int id); +extern void emfReschedCallback(int id, int delay); +extern void emfSchedProcess(); +extern int emfInstGet(); +extern void emfInstSet(int inst); +extern void error(E_ARGS_DEC, int flags, char_t *fmt, ...); +extern void (*errorSetHandler(void (*function)(int etype, char_t *msg))) \ + (int etype, char_t *msg); + +#if B_STATS +#define hAlloc(x) HALLOC(B_L, x) +#define hAllocEntry(x, y, z) HALLOCENTRY(B_L, x, y, z) +extern int HALLOC(B_ARGS_DEC, void ***map); +extern int HALLOCENTRY(B_ARGS_DEC, void ***list, int *max, int size); +#else +extern int hAlloc(void ***map); +extern int hAllocEntry(void ***list, int *max, int size); +#endif /* B_STATS */ -extern int hAlloc(void*** map); -extern int hFree(void*** map, int handle); -extern int hAllocEntry(void ***list, int *max, int size); +extern int hFree(void ***map, int handle); -extern int ringqOpen(ringq_t *rq, int increment, int maxsize); -extern void ringqClose(ringq_t *rq); -extern int ringqLen(ringq_t *rq); +extern int ringqOpen(ringq_t *rq, int increment, int maxsize); +extern void ringqClose(ringq_t *rq); +extern int ringqLen(ringq_t *rq); -extern int ringqPutc(ringq_t *rq, char_t c); -extern int ringqInsertc(ringq_t *rq, char_t c); -extern int ringqPutstr(ringq_t *rq, char_t *str); -extern int ringqGetc(ringq_t *rq); +extern int ringqPutc(ringq_t *rq, char_t c); +extern int ringqInsertc(ringq_t *rq, char_t c); +extern int ringqPutStr(ringq_t *rq, char_t *str); +extern int ringqGetc(ringq_t *rq); -extern int gvsnprintf(char_t **s, int n, char_t *fmt, va_list arg); -extern int gsnprintf(char_t **s, int n, char_t *fmt, ...); +extern int fmtValloc(char_t **s, int n, char_t *fmt, va_list arg); +extern int fmtAlloc(char_t **s, int n, char_t *fmt, ...); +extern int fmtStatic(char_t *s, int n, char_t *fmt, ...); #if UNICODE -extern int ringqPutcA(ringq_t* rq, char c); -extern int ringqInsertcA(ringq_t* rq, char c); -extern int ringqPutstrA(ringq_t* rq, char* str); -extern int ringqGetcA(ringq_t* rq); +extern int ringqPutcA(ringq_t *rq, char c); +extern int ringqInsertcA(ringq_t *rq, char c); +extern int ringqPutStrA(ringq_t *rq, char *str); +extern int ringqGetcA(ringq_t *rq); #else #define ringqPutcA ringqPutc #define ringqInsertcA ringqInsertc -#define ringqPutstrA ringqPutstr +#define ringqPutStrA ringqPutStr #define ringqGetcA ringqGetc -#endif - -extern int ringqPutBlk(ringq_t *rq, unsigned char *buf, int len); -extern int ringqPutBlkMax(ringq_t *rq); -extern void ringqPutBlkAdj(ringq_t *rq, int size); -extern int ringqGetBlk(ringq_t *rq, unsigned char *buf, int len); -extern int ringqGetBlkMax(ringq_t *rq); -extern void ringqGetBlkAdj(ringq_t *rq, int size); -extern void ringqFlush(ringq_t *rq); - -extern int scriptSetVar(int engine, char_t *var, char_t *value); -extern int scriptEval(int engine, char_t *cmd, char_t **rslt, int chan); +#endif /* UNICODE */ -extern void socketClose(); -extern void socketCloseConnection(int sid); -extern void socketCreateHandler(int sid, int mask, socketHandler_t +extern int ringqPutBlk(ringq_t *rq, unsigned char *buf, int len); +extern int ringqPutBlkMax(ringq_t *rq); +extern void ringqPutBlkAdj(ringq_t *rq, int size); +extern int ringqGetBlk(ringq_t *rq, unsigned char *buf, int len); +extern int ringqGetBlkMax(ringq_t *rq); +extern void ringqGetBlkAdj(ringq_t *rq, int size); +extern void ringqFlush(ringq_t *rq); +extern void ringqAddNull(ringq_t *rq); + +extern int scriptSetVar(int engine, char_t *var, char_t *value); +extern int scriptEval(int engine, char_t *cmd, char_t **rslt, int chan); + +extern void socketClose(); +extern void socketCloseConnection(int sid); +extern void socketCreateHandler(int sid, int mask, socketHandler_t handler, int arg); -extern void socketDeleteHandler(int sid); -extern int socketEof(int sid); -extern int socketFlush(int sid, int block); -extern int socketGets(int sid, char_t** buf); -extern int socketInputBuffered(int sid); -extern int socketOpen(); -extern int socketOpenConnection(char *host, int port, +extern void socketDeleteHandler(int sid); +extern int socketEof(int sid); +extern int socketCanWrite(int sid); +extern void socketSetBufferSize(int sid, int in, int line, int out); +extern int socketFlush(int sid); +extern int socketGets(int sid, char_t **buf); +extern int socketGetPort(int sid); +extern int socketInputBuffered(int sid); +extern int socketOpen(); +extern int socketOpenConnection(char *host, int port, socketAccept_t accept, int flags); -extern void socketProcess(); -extern int socketRead(int sid, char *buf, int len); -extern int socketReady(); -extern int socketWrite(int sid, char *buf, int len); -extern int socketWriteString(int sid, char_t *buf); -extern int socketSelect(); +extern void socketProcess(int hid); +extern int socketRead(int sid, char *buf, int len); +extern int socketReady(int hid); +extern int socketWrite(int sid, char *buf, int len); +extern int socketWriteString(int sid, char_t *buf); +extern int socketSelect(int hid, int timeout); +extern int socketGetHandle(int sid); +extern int socketSetBlock(int sid, int flags); +extern int socketGetBlock(int sid); +extern int socketAlloc(char *host, int port, socketAccept_t accept, + int flags); +extern void socketFree(int sid); +extern int socketGetError(); +extern socket_t *socketPtr(int sid); +extern int socketWaitForEvent(socket_t *sp, int events, int *errCode); +extern void socketRegisterInterest(socket_t *sp, int handlerMask); +extern int socketGetInput(int sid, char *buf, int toRead, int *errCode); extern char_t *strlower(char_t *string); extern char_t *strupper(char_t *string); extern char_t *stritoa(int n, char_t *string, int width); -extern sym_fd_t symOpen(int hash_size); -extern void symClose(sym_fd_t sd, void (*cleanup)(sym_t *sp)); +extern sym_fd_t symOpen(int hash_size); +extern void symClose(sym_fd_t sd); extern sym_t *symLookup(sym_fd_t sd, char_t *name); extern sym_t *symEnter(sym_fd_t sd, char_t *name, value_t v, int arg); -extern int symDelete(sym_fd_t sd, char_t *name); -extern void symWalk(sym_fd_t sd, void (*fn)(sym_t *symp)); - -extern void goahead_trace(int lev, char_t *fmt, ...); +extern int symDelete(sym_fd_t sd, char_t *name); +extern void symWalk(sym_fd_t sd, void (*fn)(sym_t *symp)); +extern sym_t *symFirst(sym_fd_t sd); +extern sym_t *symNext(sym_fd_t sd); +extern int symSubOpen(); +extern void symSubClose(); + +extern void trace(int lev, char_t *fmt, ...); +extern void traceRaw(char_t *buf); +extern void (*traceSetHandler(void (*function)(int level, char_t *buf))) + (int level, char_t *buf); -extern value_t valueInteger(long value); -extern value_t valueString(char_t *value, int flags); -extern value_t valueErrmsg(char_t *value); -extern void valueFree(value_t *v); +extern value_t valueInteger(long value); +extern value_t valueString(char_t *value, int flags); +extern value_t valueErrmsg(char_t *value); +extern void valueFree(value_t *v); +extern int vxchdir(char *dirname); + +extern unsigned int hextoi(char_t *hexstring); +extern unsigned int gstrtoi(char_t *s); +extern time_t timeMsec(); extern char_t *ascToUni(char_t *ubuf, char *str, int nBytes); extern char *uniToAsc(char *buf, char_t *ustr, int nBytes); -extern char_t *ballocAscToUni(char * cp); -extern char *ballocUniToAsc(char_t * unip, int ulen); +extern char_t *ballocAscToUni(char *cp, int alen); +extern char *ballocUniToAsc(char_t *unip, int ulen); + +extern char_t *basicGetHost(); +extern char_t *basicGetAddress(); +extern char_t *basicGetProduct(); +extern void basicSetHost(char_t *host); +extern void basicSetAddress(char_t *addr); + +extern int harnessOpen(char_t **argv); +extern void harnessClose(int status); +extern void harnessTesting(char_t *msg, ...); +extern void harnessPassed(); +extern void harnessFailed(int line); +extern int harnessLevel(); #endif /* _h_UEMF */ diff --git a/c/src/libnetworking/rtems_webserver/um.c b/c/src/libnetworking/rtems_webserver/um.c new file mode 100644 index 0000000000..ce9e171c01 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/um.c @@ -0,0 +1,1415 @@ +/* + * um.c -- User Management + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ + */ + +/******************************** Description *********************************/ +/* + * User Management routines for adding/deleting/changing users and groups + * Also, routines for determining user access + */ + +/********************************* Includes ***********************************/ + +#include "um.h" +#include "emfdb.h" +#include "webs.h" + +/********************************** Defines ***********************************/ + +#define UM_DB_FILENAME T("um.xml") +#define UM_TXT_FILENAME T("umconfig.txt") + +/* + * Table names + */ +#define UM_USER_TABLENAME T("users") +#define UM_GROUP_TABLENAME T("groups") +#define UM_ACCESS_TABLENAME T("access") + +/* + * Column names + */ +#define UM_NAME T("name") +#define UM_PASS T("password") +#define UM_GROUP T("group") +#define UM_PROT T("prot") +#define UM_DISABLE T("disable") +#define UM_METHOD T("method") +#define UM_PRIVILEGE T("priv") +#define UM_SECURE T("secure") + +/* + * XOR encryption mask + * Note: This string should be modified for individual sites + * in order to enhance user password security. + */ +#define UM_XOR_ENCRYPT T("*j7a(L#yZ98sSd5HfSgGjMj8;Ss;d)(*&^#@$a2s0i3g") + +/******************************** Local Data **********************************/ + +#ifdef UEMF +/* + * User table definition + */ +#define NUMBER_OF_USER_COLUMNS 5 + +char_t *userColumnNames[NUMBER_OF_USER_COLUMNS] = { + UM_NAME, UM_PASS, UM_GROUP, UM_PROT, UM_DISABLE +}; + +int userColumnTypes[NUMBER_OF_USER_COLUMNS] = { + T_STRING, T_STRING, T_STRING, T_INT, T_INT +}; + +dbTable_t userTable = { + UM_USER_TABLENAME, + NUMBER_OF_USER_COLUMNS, + userColumnNames, + userColumnTypes, + 0, + NULL +}; + +/* + * Group table definition + */ +#define NUMBER_OF_GROUP_COLUMNS 5 + +char_t *groupColumnNames[NUMBER_OF_GROUP_COLUMNS] = { + UM_NAME, UM_PRIVILEGE, UM_METHOD, UM_PROT, UM_DISABLE +}; + +int groupColumnTypes[NUMBER_OF_GROUP_COLUMNS] = { + T_STRING, T_INT, T_INT, T_INT, T_INT +}; + +dbTable_t groupTable = { + UM_GROUP_TABLENAME, + NUMBER_OF_GROUP_COLUMNS, + groupColumnNames, + groupColumnTypes, + 0, + NULL +}; + +/* + * Access Limit table definition + */ +#define NUMBER_OF_ACCESS_COLUMNS 4 + +char_t *accessColumnNames[NUMBER_OF_ACCESS_COLUMNS] = { + UM_NAME, UM_METHOD, UM_SECURE, UM_GROUP +}; + +int accessColumnTypes[NUMBER_OF_ACCESS_COLUMNS] = { + T_STRING, T_INT, T_INT, T_STRING +}; + +dbTable_t accessTable = { + UM_ACCESS_TABLENAME, + NUMBER_OF_ACCESS_COLUMNS, + accessColumnNames, + accessColumnTypes, + 0, + NULL +}; +#endif /* #ifdef UEMF */ + +/* + * Database Identifier returned from dbOpen() + */ +static int didUM = -1; + +/* + * Configuration database persist filename + */ +static char_t *saveFilename = NULL; + +static int umOpenCount = 0; /* count of apps using this module */ + +/*************************** Forward Declarations *****************************/ + +static bool_t umCheckName(char_t *name); + +/*********************************** Code *************************************/ +/* + * umOpen() registers the UM tables in the fake emf-database + */ + +int umOpen() +{ + if (++umOpenCount != 1) { + return didUM; + } +/* + * Do not initialize if intialization has already taken place + */ + if (didUM == -1) { + didUM = dbOpen(UM_USER_TABLENAME, UM_DB_FILENAME, NULL, 0); +#ifdef UEMF + dbRegisterDBSchema(&userTable); + dbRegisterDBSchema(&groupTable); + dbRegisterDBSchema(&accessTable); +#endif + } + + if (saveFilename == NULL) { + saveFilename = bstrdup(B_L, UM_TXT_FILENAME); + } + + return didUM; +} + +/******************************************************************************/ +/* + * umClose() frees up the UM tables in the fake emf-database + */ + +void umClose() +{ + if (--umOpenCount > 0) { + return; + } +/* + * Do not close if intialization has not taken place + */ + if (didUM != -1) { + dbClose(didUM); + didUM = -1; + } + + if (saveFilename != NULL) { + bfree(B_L, saveFilename); + saveFilename = NULL; + } +} + +/******************************************************************************/ +/* + * umCommit() persists all of the UM tables + */ + +int umCommit(char_t *filename) +{ + if (filename && *filename) { + if (saveFilename != NULL) { + bfree(B_L, saveFilename); + } + + saveFilename = bstrdup(B_L, filename); + } + + a_assert (saveFilename && *saveFilename); + trace(3, T("UM: Writing User Configuration to file <%s>\n"), + saveFilename); + + return dbSave(didUM, saveFilename, 0); +} + +/******************************************************************************/ +/* + * umRestore() loads up the UM tables with persisted data + */ + +int umRestore(char_t *filename) +{ + if (filename && *filename) { + if (saveFilename != NULL) { + bfree(B_L, saveFilename); + } + + saveFilename = bstrdup(B_L, filename); + } + + a_assert(saveFilename && *saveFilename); + + trace(3, T("UM: Loading User Configuration from file <%s>\n"), + saveFilename); + +/* + * First empty the database, otherwise we wind up with duplicates! + */ + dbZero(didUM); + return dbLoad(didUM, saveFilename, 0); +} + +/******************************************************************************/ +/* + * Encrypt/Decrypt a text string. + * Returns the number of characters encrypted. + */ + +static int umEncryptString(char_t *textString) +{ + char_t *enMask; + char_t enChar; + int numChars; + + a_assert(textString); + + enMask = UM_XOR_ENCRYPT; + numChars = 0; + + while (*textString) { + enChar = *textString ^ *enMask; +/* + * Do not produce encrypted text with embedded linefeeds or tabs. + * Simply use existing character. + */ + if (enChar && !gisspace(enChar)) + *textString = enChar; +/* + * Increment all pointers. + */ + enMask++; + textString++; + numChars++; +/* + * Wrap encryption mask pointer if at end of length. + */ + if (*enMask == '\0') { + enMask = UM_XOR_ENCRYPT; + } + } + + return numChars; +} + +/******************************************************************************/ +/* + * umGetFirstRowData() - return a pointer to the first non-blank key value + * in the given column for the given table. + */ + +static char_t *umGetFirstRowData(char_t *tableName, char_t *columnName) +{ + char_t *columnData; + int row; + int check; + + a_assert(tableName && *tableName); + a_assert(columnName && *columnName); + + row = 0; +/* + * Move through table until we retrieve the first row with non-null + * column data. + */ + columnData = NULL; + while ((check = dbReadStr(didUM, tableName, columnName, row++, + &columnData)) == 0 || (check == DB_ERR_ROW_DELETED)) { + if (columnData && *columnData) { + return columnData; + } + } + + return NULL; +} + +/******************************************************************************/ +/* + * umGetNextRowData() - return a pointer to the first non-blank + * key value following the given one. + */ + +static char_t *umGetNextRowData(char_t *tableName, char_t *columnName, + char_t *keyLast) +{ + char_t *key; + int row; + int check; + + a_assert(tableName && *tableName); + a_assert(columnName && *columnName); + a_assert(keyLast && *keyLast); +/* + * Position row counter to row where the given key value was found + */ + row = 0; + key = NULL; + + while ((((check = dbReadStr(didUM, tableName, columnName, row++, + &key)) == 0) || (check == DB_ERR_ROW_DELETED)) && + ((key == NULL) || (gstrcmp(key, keyLast) != 0))) { + } +/* + * If the last key value was not found, return NULL + */ + if (!key || gstrcmp(key, keyLast) != 0) { + return NULL; + } +/* + * Move through table until we retrieve the next row with a non-null key + */ + while (((check = dbReadStr(didUM, tableName, columnName, row++, &key)) + == 0) || (check == DB_ERR_ROW_DELETED)) { + if (key && *key && (gstrcmp(key, keyLast) != 0)) { + return key; + } + } + + return NULL; +} + +/******************************************************************************/ +/* + * umAddUser() - Adds a user to the "users" table. + */ + +int umAddUser(char_t *user, char_t *pass, char_t *group, + bool_t prot, bool_t disabled) +{ + int row; + char_t *password; + + a_assert(user && *user); + a_assert(pass && *pass); + a_assert(group && *group); + + trace(3, T("UM: Adding User <%s>\n"), user); + +/* + * Do not allow duplicates + */ + if (umUserExists(user)) { + return UM_ERR_DUPLICATE; + } + +/* + * Make sure user name and password contain valid characters + */ + if (!umCheckName(user)) { + return UM_ERR_BAD_NAME; + } + + if (!umCheckName(pass)) { + return UM_ERR_BAD_PASSWORD; + } + +/* + * Make sure group exists + */ + if (!umGroupExists(group)) { + return UM_ERR_NOT_FOUND; + } + +/* + * Now create the user record + */ + row = dbAddRow(didUM, UM_USER_TABLENAME); + + if (row < 0) { + return UM_ERR_GENERAL; + } + + if (dbWriteStr(didUM, UM_USER_TABLENAME, UM_NAME, row, user) != 0) { + return UM_ERR_GENERAL; + } + + password = bstrdup(B_L, pass); + umEncryptString(password); + dbWriteStr(didUM, UM_USER_TABLENAME, UM_PASS, row, password); + bfree(B_L, password); + dbWriteStr(didUM, UM_USER_TABLENAME, UM_GROUP, row, group); + dbWriteInt(didUM, UM_USER_TABLENAME, UM_PROT, row, prot); + dbWriteInt(didUM, UM_USER_TABLENAME, UM_DISABLE, row, disabled); + + return 0; +} + +/******************************************************************************/ +/* + * umDeleteUser() - remove a user from the "users" table + */ + +int umDeleteUser(char_t *user) +{ + int row; + + a_assert(user && *user); + trace(3, T("UM: Deleting User <%s>\n"), user); +/* + * Check to see if user is delete-protected + */ + if (umGetUserProtected(user)) { + return UM_ERR_PROTECTED; + } + +/* + * If found, delete the user from the database + */ + if ((row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0)) >= 0) { + return dbDeleteRow(didUM, UM_USER_TABLENAME, row); + } + + return UM_ERR_NOT_FOUND; +} + +/******************************************************************************/ +/* + * umGetFirstUser() - Returns the user ID of the first user found in the + * "users" table. + */ + +char_t *umGetFirstUser() +{ + return umGetFirstRowData(UM_USER_TABLENAME, UM_NAME); +} + +/******************************************************************************/ +/* + * umGetNextUser() Returns the next user found in the "users" table after + * the given user. + */ + +char_t *umGetNextUser(char_t *userLast) +{ + return umGetNextRowData(UM_USER_TABLENAME, UM_NAME, userLast); +} + +/******************************************************************************/ +/* + * umUserExists() Returns TRUE if userid exists. + */ + +bool_t umUserExists(char_t *user) +{ + a_assert(user && *user); + + if (dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0) >= 0) { + return TRUE; + } else { + return FALSE; + } +} + +/******************************************************************************/ +/* + * umGetUserPassword() returns a de-crypted copy of the user password + */ + +char_t *umGetUserPassword(char_t *user) +{ + char_t *password; + int row; + + a_assert(user && *user); + + password = NULL; + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + + if (row >= 0) { + char_t *pass = NULL; + dbReadStr(didUM, UM_USER_TABLENAME, UM_PASS, row, &pass); +/* + * Decrypt password + * Note, this function returns a copy of the password, which must + * be deleted at some time in the future. + */ + password = bstrdup(B_L, pass); + umEncryptString(password); + } + + return password; +} + +/******************************************************************************/ +/* + * umSetUserPassword() updates the user password in the user "table" after + * encrypting the given password + */ + +int umSetUserPassword(char_t *user, char_t *pass) +{ + int row, nRet; + char_t *password; + + a_assert(user && *user); + a_assert(pass && *pass); + trace(3, T("UM: Attempting to change the password for user <%s>\n"), user); +/* + * Find the row of the user + */ + if ((row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0)) < 0) { + return UM_ERR_NOT_FOUND; + } + + password = bstrdup(B_L, pass); + umEncryptString(password); + nRet = dbWriteStr(didUM, UM_USER_TABLENAME, UM_PASS, row, password); + bfree(B_L, password); + + return nRet; +} + +/******************************************************************************/ +/* + * umGetUserGroup() returns the name of the user group + */ + +char_t *umGetUserGroup(char_t *user) +{ + char_t *group; + int row; + + a_assert(user && *user); + group = NULL; +/* + * Find the row of the user + */ + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + + if (row >= 0) { + dbReadStr(didUM, UM_USER_TABLENAME, UM_GROUP, row, &group); + } + + return group; +} + +/******************************************************************************/ +/* + * umSetUserGroup() Sets the name of the user group for the user + */ + +int umSetUserGroup(char_t *user, char_t *group) +{ + int row; + + a_assert(user && *user); + a_assert(group && *group); +/* + * Find the row of the user + */ + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + + if (row >= 0) { + return dbWriteStr(didUM, UM_USER_TABLENAME, UM_GROUP, row, group); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * umGetUserEnabled() - returns if the user is enabled + * Returns FALSE if the user is not found. + */ + +bool_t umGetUserEnabled(char_t *user) +{ + int disabled, row; + + a_assert(user && *user); + + disabled = 1; +/* + * Find the row of the user + */ + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + + if (row >= 0) { + dbReadInt(didUM, UM_USER_TABLENAME, UM_DISABLE, row, &disabled); + } + + return (bool_t)!disabled; +} + +/******************************************************************************/ +/* + * umSetUserEnabled() Enables/disables the user + */ +int umSetUserEnabled(char_t *user, bool_t enabled) +{ + int row; + + a_assert(user && *user); +/* + * Find the row of the user + */ + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + if (row >= 0) { + return dbWriteInt(didUM, UM_USER_TABLENAME, UM_DISABLE, row, !enabled); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * umGetUserProtected() - determine deletability of user + */ + +bool_t umGetUserProtected(char_t *user) +{ + int protect, row; + + a_assert(user && *user); +/* + * Find the row of the user + */ + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + protect = FALSE; + + if (row >= 0) { + dbReadInt(didUM, UM_USER_TABLENAME, UM_PROT, row, &protect); + } + + return (bool_t)protect; +} + +/******************************************************************************/ +/* + * umSetUserProtected() sets the delete protection for the user + */ +int umSetUserProtected(char_t *user, bool_t protect) +{ + int row; + + a_assert(user && *user); +/* + * Find the row of the user + */ + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_USER_TABLENAME, UM_PROT, row, protect); + } else { + return UM_ERR_NOT_FOUND; + } +} + + +/******************************************************************************/ +/* + * umAddGroup() adds a group to the "Group" table + */ + +int umAddGroup(char_t *group, short priv, accessMeth_t am, + bool_t prot, bool_t disabled) +{ + int row; + + a_assert(group && *group); + trace(3, T("UM: Adding group <%s>\n"), group); + +/* + * Do not allow duplicates + */ + if (umGroupExists(group)) { + return UM_ERR_DUPLICATE; + } + +/* + * Only allow valid characters in key field + */ + if (!umCheckName(group)) { + return UM_ERR_BAD_NAME; + } + +/* + * Add a new row to the table + */ + if ((row = dbAddRow(didUM, UM_GROUP_TABLENAME)) < 0) { + return UM_ERR_GENERAL; + } + +/* + * Write the key field + */ + if (dbWriteStr(didUM, UM_GROUP_TABLENAME, UM_NAME, row, group) != 0) { + return UM_ERR_GENERAL; + } + +/* + * Write the remaining fields + */ + dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_PRIVILEGE, row, priv); + dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_METHOD, row, (int) am); + dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_PROT, row, prot); + dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_DISABLE, row, disabled); + + return 0; +} + +/******************************************************************************/ +/* + * umDeleteGroup() - Delete a user group, if not protected + */ + +int umDeleteGroup(char_t *group) +{ + int row; + + a_assert(group && *group); + trace(3, T("UM: Deleting Group <%s>\n"), group); + +/* + * Check to see if the group is in use + */ + if (umGetGroupInUse(group)) { + return UM_ERR_IN_USE; + } + +/* + * Check to see if the group is delete-protected + */ + if (umGetGroupProtected(group)) { + return UM_ERR_PROTECTED; + } + +/* + * Find the row of the group to delete + */ + if ((row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0)) < 0) { + return UM_ERR_NOT_FOUND; + } + + return dbDeleteRow(didUM, UM_GROUP_TABLENAME, row); +} + +/******************************************************************************/ +/* + * umGroupExists() returns TRUE if group exists, FALSE otherwise + */ + +bool_t umGroupExists(char_t *group) +{ + a_assert(group && *group); + + if (dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0) >= 0) { + return TRUE; + } else { + return FALSE; + } +} + + +/******************************************************************************/ +/* + * umGetGroupInUse() returns TRUE if the group is referenced by a user or by + * an access limit. + */ + +bool_t umGetGroupInUse(char_t *group) +{ + a_assert(group && *group); + +/* + * First, check the user table + */ + if (dbSearchStr(didUM, UM_USER_TABLENAME, UM_GROUP, group, 0) >= 0) { + return TRUE; + } + +/* + * Second, check the access limit table + */ + if (dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_GROUP, group, 0) >= 0) { + return TRUE; + } + + return FALSE; +} + + +/******************************************************************************/ +/* + * umGetFirstGroup() - return a pointer to the first non-blank group name + */ + +char_t *umGetFirstGroup() +{ + return umGetFirstRowData(UM_GROUP_TABLENAME, UM_NAME); +} + +/******************************************************************************/ +/* + * umGetNextGroup() - return a pointer to the first non-blank group name + * following the given group name + */ + +char_t *umGetNextGroup(char_t *groupLast) +{ + return umGetNextRowData(UM_GROUP_TABLENAME, UM_NAME, groupLast); +} + +/******************************************************************************/ +/* + * Returns the default access method to use for a given group + */ + +accessMeth_t umGetGroupAccessMethod(char_t *group) +{ + int am, row; + + a_assert(group && *group); + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + + if (row >= 0) { + dbReadInt(didUM, UM_GROUP_TABLENAME, UM_METHOD, row, (int *)&am); + } else { + am = AM_INVALID; + } + + return (accessMeth_t) am; +} + +/******************************************************************************/ +/* + * Set the default access method to use for a given group + */ + +int umSetGroupAccessMethod(char_t *group, accessMeth_t am) +{ + int row; + + a_assert(group && *group); + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_METHOD, row, (int) am); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * Returns the privilege mask for a given group + */ + +short umGetGroupPrivilege(char_t *group) +{ + int privilege, row; + + a_assert(group && *group); + privilege = -1; + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + + if (row >= 0) { + dbReadInt(didUM, UM_GROUP_TABLENAME, UM_PRIVILEGE, row, &privilege); + } + + return (short) privilege; +} + +/******************************************************************************/ +/* + * Set the privilege mask for a given group + */ + +int umSetGroupPrivilege(char_t *group, short privilege) +{ + int row; + + a_assert(group && *group); + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_PRIVILEGE, row, + (int)privilege); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * Returns the enabled setting for a given group. + * Returns FALSE if group is not found. + */ + +bool_t umGetGroupEnabled(char_t *group) +{ + int disabled, row; + + a_assert(group && *group); + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + disabled = 1; + + if (row >= 0) { + dbReadInt(didUM, UM_GROUP_TABLENAME, UM_DISABLE, row, &disabled); + } + + return (bool_t) !disabled; +} + +/******************************************************************************/ +/* + * Sets the enabled setting for a given group. + */ + +int umSetGroupEnabled(char_t *group, bool_t enabled) +{ + int row; + + a_assert(group && *group); + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_DISABLE, row, + (int) !enabled); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * Returns the protected setting for a given group + * Returns FALSE if user is not found + */ + +bool_t umGetGroupProtected(char_t *group) +{ + int protect, row; + + a_assert(group && *group); + + protect = 0; + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + if (row >= 0) { + dbReadInt(didUM, UM_GROUP_TABLENAME, UM_PROT, row, &protect); + } + + return (bool_t) protect; +} + +/******************************************************************************/ +/* + * Sets the protected setting for a given group + */ + +int umSetGroupProtected(char_t *group, bool_t protect) +{ + int row; + + a_assert(group && *group); + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_PROT, row, + (int) protect); + } else { + return UM_ERR_NOT_FOUND; + } +} + + +/******************************************************************************/ +/* + * umAddAccessLimit() adds an access limit to the "access" table + */ + +int umAddAccessLimit(char_t *url, accessMeth_t am, short secure, char_t *group) +{ + int row; + + a_assert(url && *url); + trace(3, T("UM: Adding Access Limit for <%s>\n"), url); + +/* + * Do not allow duplicates + */ + if (umAccessLimitExists(url)) { + return UM_ERR_DUPLICATE; + } + +/* + * Add a new row to the table + */ + if ((row = dbAddRow(didUM, UM_ACCESS_TABLENAME)) < 0) { + return UM_ERR_GENERAL; + } + +/* + * Write the key field + */ + if(dbWriteStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, row, url) < 0) { + return UM_ERR_GENERAL; + } + +/* + * Write the remaining fields + */ + dbWriteInt(didUM, UM_ACCESS_TABLENAME, UM_METHOD, row, (int)am); + dbWriteInt(didUM, UM_ACCESS_TABLENAME, UM_SECURE, row, (int)secure); + dbWriteStr(didUM, UM_ACCESS_TABLENAME, UM_GROUP, row, group); + + return 0; +} + +/******************************************************************************/ +/* + * umDeleteAccessLimit() + */ + +int umDeleteAccessLimit(char_t *url) +{ + int row; + + a_assert(url && *url); + trace(3, T("UM: Deleting Access Limit for <%s>\n"), url); +/* + * Find the row of the access limit to delete + */ + if ((row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0)) < 0) { + return UM_ERR_NOT_FOUND; + } + + return dbDeleteRow(didUM, UM_ACCESS_TABLENAME, row); +} + +/******************************************************************************/ +/* + * umGetFirstGroup() - return a pointer to the first non-blank access limit + */ + +char_t *umGetFirstAccessLimit() +{ + return umGetFirstRowData(UM_ACCESS_TABLENAME, UM_NAME); +} + +/******************************************************************************/ +/* + * umGetNextAccessLimit() - return a pointer to the first non-blank + * access limit following the given one + */ + +char_t *umGetNextAccessLimit(char_t *urlLast) +{ + return umGetNextRowData(UM_ACCESS_TABLENAME, UM_NAME, urlLast); +} + +/******************************************************************************/ +/* + * umAccessLimitExists() returns TRUE if this access limit exists + */ + +bool_t umAccessLimitExists(char_t *url) +{ + a_assert(url && *url); + + if (dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0) < 0) { + return FALSE; + } else { + return TRUE; + } +} + +/******************************************************************************/ +/* + * umGetAccessLimit() returns the Access Method for the URL + */ + +accessMeth_t umGetAccessLimitMethod(char_t *url) +{ + int am, row; + + am = (int) AM_INVALID; + row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0); + + if (row >= 0) { + dbReadInt(didUM, UM_ACCESS_TABLENAME, UM_METHOD, row, &am); + } + + return (accessMeth_t) am; +} + +/******************************************************************************/ +/* + * umSetAccessLimitMethod() - set Access Method for Access Limit + */ + +int umSetAccessLimitMethod(char_t *url, accessMeth_t am) +{ + int row; + + a_assert(url && *url); + row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_ACCESS_TABLENAME, UM_METHOD, row, (int) am); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * umGetAccessLimitSecure() - returns secure switch for access limit + */ + +short umGetAccessLimitSecure(char_t *url) +{ + int secure, row; + + a_assert(url && *url); + secure = -1; + row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0); + + if (row >= 0) { + dbReadInt(didUM, UM_ACCESS_TABLENAME, UM_SECURE, row, &secure); + } + + return (short)secure; +} + +/******************************************************************************/ +/* + * umSetAccessLimitSecure() - sets the secure flag for the URL + */ + +int umSetAccessLimitSecure(char_t *url, short secure) +{ + int row; + + a_assert(url && *url); + row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_ACCESS_TABLENAME, UM_SECURE, row, + (int)secure); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * umGetAccessLimitGroup() - returns the user group of the access limit + */ + +char_t *umGetAccessLimitGroup(char_t *url) +{ + char_t *group; + int row; + + a_assert(url && *url); + group = NULL; + row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0); + + if (row >= 0) { + dbReadStr(didUM, UM_ACCESS_TABLENAME, UM_GROUP, row, &group); + } + + return group; +} + +/******************************************************************************/ +/* + * umSetAccessLimitGroup() - sets the user group for the access limit. + */ + +int umSetAccessLimitGroup(char_t *url, char_t *group) +{ + int row; + + a_assert(url && *url); + row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0); + + if (row >= 0) { + return dbWriteStr(didUM, UM_ACCESS_TABLENAME, UM_GROUP, row, group); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * Returns the access limit to use for a given URL, by checking for URLs up + * the directory tree. Creates a new string that must be deleted. + */ + +char_t *umGetAccessLimit(char_t *url) +{ + char_t *urlRet, *urlCheck, *lastChar; + int len; + + a_assert(url && *url); + urlRet = NULL; + urlCheck = bstrdup(B_L, url); + a_assert(urlCheck); + len = gstrlen(urlCheck); +/* + * Scan back through URL to see if there is a "parent" access limit + */ + while (len && !urlRet) { + if (umAccessLimitExists(urlCheck)) { + urlRet = bstrdup(B_L, urlCheck); + } else { +/* + * Trim the end portion of the URL to the previous directory marker + */ + lastChar = urlCheck + len; + lastChar--; + + while ((lastChar >= urlCheck) && ((*lastChar == '/') || + (*lastChar == '\\'))) { + *lastChar = 0; + lastChar--; + } + + while ((lastChar >= urlCheck) && (*lastChar != '/') && + (*lastChar != '\\')) { + *lastChar = 0; + lastChar--; + } + + len = gstrlen(urlCheck); + } + } + bfree (B_L, urlCheck); + + return urlRet; +} + +/******************************************************************************/ +/* + * Returns the access method to use for a given URL + */ + +accessMeth_t umGetAccessMethodForURL(char_t *url) +{ + accessMeth_t amRet; + char_t *urlHavingLimit, *group; + + urlHavingLimit = umGetAccessLimit(url); + if (urlHavingLimit) { + group = umGetAccessLimitGroup(urlHavingLimit); + + if (group && *group) { + amRet = umGetGroupAccessMethod(group); + } else { + amRet = umGetAccessLimitMethod(urlHavingLimit); + } + + bfree(B_L, urlHavingLimit); + } else { + amRet = AM_FULL; + } + + return amRet; +} + +/******************************************************************************/ +/* + * Returns TRUE if user can access URL + */ + +bool_t umUserCanAccessURL(char_t *user, char_t *url) +{ + accessMeth_t amURL; + char_t *group, *usergroup, *urlHavingLimit; + short priv; + + a_assert(user && *user); + a_assert(url && *url); + +/* + * Make sure user exists + */ + if (!umUserExists(user)) { + return FALSE; + } + +/* + * Make sure user is enabled + */ + if (!umGetUserEnabled(user)) { + return FALSE; + } + +/* + * Make sure user has sufficient privileges (any will do) + */ + usergroup = umGetUserGroup(user); + priv = umGetGroupPrivilege(usergroup); + if (priv == 0) { + return FALSE; + } + +/* + * Make sure user's group is enabled + */ + if (!umGetGroupEnabled(usergroup)) { + return FALSE; + } + +/* + * The access method of the user group must not be AM_NONE + */ + if (umGetGroupAccessMethod(usergroup) == AM_NONE) { + return FALSE; + } + +/* + * Check to see if there is an Access Limit for this URL + */ + urlHavingLimit = umGetAccessLimit(url); + if (urlHavingLimit) { + amURL = umGetAccessLimitMethod(urlHavingLimit); + group = umGetAccessLimitGroup(urlHavingLimit); + bfree(B_L, urlHavingLimit); + } else { +/* + * If there isn't an access limit for the URL, user has full access + */ + return TRUE; + } + +/* + * If the access method for the URL is AM_NONE then + * the file "doesn't exist". + */ + if (amURL == AM_NONE) { + return FALSE; + } + +/* + * If Access Limit has a group specified, then the user must be a + * member of that group + */ + if (group && *group) { + if (usergroup && (gstrcmp(group, usergroup) != 0)) { + return FALSE; + } + } + +/* + * Otherwise, user can access the URL + */ + return TRUE; +} + +/******************************************************************************/ +/* + * Returns TRUE if given name has only valid chars + */ + +static bool_t umCheckName(char_t *name) +{ + a_assert(name && *name); + + if (name && *name) { + while (*name) { + if (gisspace(*name)) { + return FALSE; + } + + name++; + } + + return TRUE; + } + + return FALSE; +} + +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/um.h b/c/src/libnetworking/rtems_webserver/um.h new file mode 100644 index 0000000000..d44fa28e91 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/um.h @@ -0,0 +1,184 @@ +/* + * um.h -- GoAhead User Management public header + * + * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved. + * + * See the file "license.txt" for information on usage and redistribution + * + * $Id$ + */ + +#ifndef _h_UM +#define _h_UM 1 + +/******************************** Description *********************************/ + +/* + * GoAhead User Management header. This defines the User Management + * public APIs. Include this header for files that contain access to + * user inquiry or management. + */ + +/********************************* Includes ***********************************/ + +#if ! UEMF + #include "basic/basic.h" + #include "emf/emf.h" +#else + #include "uemf.h" +#endif + +/********************************** Defines ***********************************/ + +/* + * Error Return Flags + */ +#define UM_OK 0 +#define UM_ERR_GENERAL -1 +#define UM_ERR_NOT_FOUND -2 +#define UM_ERR_PROTECTED -3 +#define UM_ERR_DUPLICATE -4 +#define UM_ERR_IN_USE -5 +#define UM_ERR_BAD_NAME -6 +#define UM_ERR_BAD_PASSWORD -7 + +/* + * Privilege Masks + */ +#define PRIV_NONE 0x00 +#define PRIV_READ 0x01 +#define PRIV_WRITE 0x02 +#define PRIV_ADMIN 0x04 + +/* + * User classes + */ +typedef short bool_t; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +typedef enum { + AM_NONE = 0, + AM_FULL, + AM_BASIC, + AM_DIGEST, + AM_INVALID +} accessMeth_t; + +/********************************** Prototypes ********************************/ + +/* + * umOpen() must be called before accessing User Management functions + */ +extern int umOpen(); + +/* + * umClose() should be called before shutdown to free memory + */ +extern void umClose(); + +/* + * umCommit() persists the user management database + */ +extern int umCommit(char_t *filename); + +/* + * umRestore() loads the user management database + */ +extern int umRestore(char_t *filename); + +/* + * umUser functions use a user ID for a key + */ +extern int umAddUser(char_t *user, char_t *password, + char_t *group, bool_t protect, bool_t disabled); + +extern int umDeleteUser(char_t *user); + +extern char_t *umGetFirstUser(); +extern char_t *umGetNextUser(char_t *lastUser); + +extern bool_t umUserExists(char_t *user); + +extern char_t *umGetUserPassword(char_t *user); +extern int umSetUserPassword(char_t *user, char_t *password); + +extern char_t *umGetUserGroup(char_t *user); +extern int umSetUserGroup(char_t *user, char_t *password); + +extern bool_t umGetUserEnabled(char_t *user); +extern int umSetUserEnabled(char_t *user, bool_t enabled); + +extern bool_t umGetUserProtected(char_t *user); +extern int umSetUserProtected(char_t *user, bool_t protect); + +/* + * umGroup functions use a group name for a key + */ +extern int umAddGroup(char_t *group, short privilege, + accessMeth_t am, bool_t protect, bool_t disabled); + +extern int umDeleteGroup(char_t *group); + +extern char_t *umGetFirstGroup(); +extern char_t *umGetNextGroup(char_t *lastUser); + +extern bool_t umGroupExists(char_t *group); +extern bool_t umGetGroupInUse(char_t *group); + +extern accessMeth_t umGetGroupAccessMethod(char_t *group); +extern int umSetGroupAccessMethod(char_t *group, accessMeth_t am); + +extern bool_t umGetGroupEnabled(char_t *group); +extern int umSetGroupEnabled(char_t *group, bool_t enabled); + +extern short umGetGroupPrivilege(char_t *group); +extern int umSetGroupPrivilege(char_t *group, short privileges); + +extern bool_t umGetGroupProtected(char_t *group); +extern int umSetGroupProtected(char_t *group, bool_t protect); + +/* + * umAccessLimit functions use a URL as a key + */ +extern int umAddAccessLimit(char_t *url, accessMeth_t am, + short secure, char_t *group); + +extern int umDeleteAccessLimit(char_t *url); + +extern char_t *umGetFirstAccessLimit(); +extern char_t *umGetNextAccessLimit(char_t *lastUser); + +/* + * Returns the name of an ancestor access limit if + */ +extern char_t *umGetAccessLimit(char_t *url); + +extern bool_t umAccessLimitExists(char_t *url); + +extern accessMeth_t umGetAccessLimitMethod(char_t *url); +extern int umSetAccessLimitMethod(char_t *url, accessMeth_t am); + +extern short umGetAccessLimitSecure(char_t *url); +extern int umSetAccessLimitSecure(char_t *url, short secure); + +extern char_t *umGetAccessLimitGroup(char_t *url); +extern int umSetAccessLimitGroup(char_t *url, char_t *group); + +/* + * Convenience Functions + */ + +extern accessMeth_t umGetAccessMethodForURL(char_t *url); +extern bool_t umUserCanAccessURL(char_t *user, char_t *url); + +#endif /* _h_UM */ + +/******************************************************************************/ + diff --git a/c/src/libnetworking/rtems_webserver/url.c b/c/src/libnetworking/rtems_webserver/url.c index 75746fd490..0258387c59 100644 --- a/c/src/libnetworking/rtems_webserver/url.c +++ b/c/src/libnetworking/rtems_webserver/url.c @@ -1,7 +1,7 @@ /* * url.c -- Parse URLs * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -83,7 +83,7 @@ int websUrlParse(char_t *url, char_t **pbuf, char_t **phost, char_t **ppath, ulen = gstrlen(url); /* - * We allocate enough to store a separate hostname and port number fields. + * We allocate enough to store separate hostname and port number fields. * As there are 3 strings in the one buffer, we need room for 3 null chars. * We allocate MAX_PORT_LEN char_t's for the port number. */ @@ -146,16 +146,23 @@ int websUrlParse(char_t *url, char_t **pbuf, char_t **phost, char_t **ppath, } /* - * Parse the tag and the query + * Parse the query string */ - if ((cp = gstrchr(tok, '#')) != NULL) { - *cp++ = '\0'; - path = tok; - tok = cp; - } if ((cp = gstrchr(tok, '?')) != NULL) { *cp++ = '\0'; query = cp; + path = tok; + tok = query; + } + +/* + * Parse the fragment identifier + */ + if ((cp = gstrchr(tok, '#')) != NULL) { + *cp++ = '\0'; + if (*query == 0) { + path = tok; + } } /* diff --git a/c/src/libnetworking/rtems_webserver/value.c b/c/src/libnetworking/rtems_webserver/value.c index 5872382556..97dba81633 100644 --- a/c/src/libnetworking/rtems_webserver/value.c +++ b/c/src/libnetworking/rtems_webserver/value.c @@ -1,47 +1,60 @@ /* * value.c -- Generic type (holds all types) * - * Copyright (c) Go Ahead Software, Inc., 1995-1999 - * - * See the file "license.txt" for usage and redistribution license requirements + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. */ /******************************** Description *********************************/ /* * This module provides a generic type that can hold all possible types. - * It is designed to provide maximum effeciency. + * It is designed to provide maximum effeciency. */ /********************************* Includes ***********************************/ -#include "uemf.h" +#if UEMF + #include "uemf.h" +#else + #include "basic/basicInternal.h" +#endif /*********************************** Locals ***********************************/ +#if !UEMF +static value_t value_null; /* All zeros */ +/***************************** Forward Declarations ***************************/ +static void coerce_types(value_t* v1, value_t* v2); +static int value_to_integer(value_t* vp); +#endif /*!UEMF*/ /*********************************** Code *************************************/ /* - * Initialize a integer value. + * Initialize a integer value. */ value_t valueInteger(long value) { - value_t v = VALUE_VALID; + value_t v; + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = integer; v.value.integer = value; return v; } /******************************************************************************/ /* - * Initialize a string value. Note: not allocation + * Initialize a string value. */ -value_t valueString(char_t *value, int flags) +value_t valueString(char_t* value, int flags) { - value_t v = VALUE_VALID; + value_t v; + memset(&v, 0x0, sizeof(v)); + v.valid = 1; v.type = string; if (flags & VALUE_ALLOCATE) { v.allocated = 1; @@ -55,20 +68,1144 @@ value_t valueString(char_t *value, int flags) /******************************************************************************/ /* - * Free any storage allocated for a value. + * Free any storage allocated for a value. */ -void valueFree(value_t *v) +void valueFree(value_t* v) { - a_assert(v); - - if (v->valid && v->allocated && v->type == string && + if (v->valid && v->allocated && v->type == string && v->value.string != NULL) { bfree(B_L, v->value.string); } +#if !UEMF + if (v->valid && v->type == symbol && v->value.symbol.data != NULL && + v->value.symbol.freeCb !=NULL) { + v->value.symbol.freeCb(v->value.symbol.data); + } +#endif v->type = undefined; v->valid = 0; v->allocated = 0; } +#if !UEMF + +/******************************************************************************/ +/* + * Initialize an invalid value. + */ + +value_t valueInvalid() +{ + value_t v; + v.valid = 0; + v.type = undefined; + return v; +} + +/******************************************************************************/ +/* + * Initialize a flag value. + */ + +value_t valueBool(int value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.type = flag; + v.valid = 1; + v.value.flag = (char) value; + return v; +} + +/******************************************************************************/ +/* + * Initialize a byteint value. + */ + +value_t valueByteint(char value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = byteint; + v.value.byteint = value; + return v; +} + +/******************************************************************************/ +/* + * Initialize a shortint value. + */ + +value_t valueShortint(short value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = shortint; + v.value.shortint = value; + return v; +} + +#if FLOATING_POINT_SUPPORT +/******************************************************************************/ +/* + * Initialize a floating value. + */ + +value_t valueFloating(double value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = floating; + v.value.floating = value; + return v; +} +#endif /* FLOATING_POINT_SUPPORT */ + +/******************************************************************************/ +/* + * Initialize a big value. + */ + +value_t valueBig(long high_word, long low_word) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = big; + v.value.big[BLOW] = low_word; + v.value.big[BHIGH] = high_word; + return v; +} + +/******************************************************************************/ +/* + * Initialize a hex value. + */ + +value_t valueHex(int value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = hex; + v.value.integer = value; + return v; +} + +/******************************************************************************/ +/* + * Initialize a octal value. + */ + +value_t valueOctal(int value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = octal; + v.value.integer = value; + return v; +} + +/******************************************************************************/ +/* + * Initialize a percent value. + */ + +value_t valuePercent(int value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = percent; + v.value.percent = (char) value; + return v; +} + +/******************************************************************************/ +/* + * Initialize an byte array. Note: no allocation, just store the ptr + */ + +value_t valueBytes(char* value, int flags) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = bytes; + if (flags & VALUE_ALLOCATE) { + v.allocated = 1; + v.value.bytes = bstrdupA(B_L, value); + } else { + v.allocated = 0; + v.value.bytes = value; + } + return v; +} + +/******************************************************************************/ +/* + * Initialize a symbol value. + * Value parameter can hold a pointer to any type of value + * Free parameter can be NULL, or a function pointer to a function that will + * free the value + */ + +value_t valueSymbol(void *value, freeCallback freeCb) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = symbol; + v.value.symbol.data = value; + v.value.symbol.freeCb = freeCb; + return v; +} + +/******************************************************************************/ +/* + * Initialize an error message value. + */ + +value_t valueErrmsg(char_t* value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = errmsg; + v.value.errmsg = value; + return v; +} + +/******************************************************************************/ +/* + * Copy a value. If the type is 'string' then allocate another string. + * Note: we allow the copy of a null value. + */ + +value_t valueCopy(value_t v2) +{ + value_t v1; + + v1 = v2; + if (v2.valid && v2.type == string && v2.value.string != NULL) { + v1.value.string = gstrdup(B_L, v2.value.string); + v1.allocated = 1; + } + return v1; +} + + +/******************************************************************************/ +/* + * Add a value. + */ + +value_t valueAdd(value_t v1, value_t v2) +{ + value_t v; + + a_assert(v1.valid); + a_assert(v2.valid); + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + + if (v1.type != v2.type) + coerce_types(&v1, &v2); + + switch (v1.type) { + default: + case string: + case bytes: + a_assert(0); + break; + +#if FLOATING_POINT_SUPPORT + case floating: + v1.value.floating += v2.value.floating; + return v1; +#endif + + case flag: + v1.value.bool |= v2.value.flag; + return v1; + + case byteint: + case percent: + v1.value.byteint += v2.value.byteint; + return v1; + + case shortint: + v1.value.shortint += v2.value.shortint; + return v1; + + case hex: + case integer: + case octal: + v1.value.integer += v2.value.integer; + return v1; + + case big: + v.type = big; + badd(v.value.big, v1.value.big, v2.value.big); + return v; + } + + return v1; +} + + +/******************************************************************************/ +/* + * Subtract a value. + */ + +value_t valueSub(value_t v1, value_t v2) +{ + value_t v; + + a_assert(v1.valid); + a_assert(v2.valid); + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + + if (v1.type != v2.type) + coerce_types(&v1, &v2); + switch (v1.type) { + default: + a_assert(0); + break; + +#if FLOATING_POINT_SUPPORT + case floating: + v1.value.floating -= v2.value.floating; + return v1; +#endif + + case flag: + v1.value.flag &= v2.value.flag; + return v1; + + case byteint: + case percent: + v1.value.byteint -= v2.value.byteint; + return v1; + + case shortint: + v1.value.shortint -= v2.value.shortint; + return v1; + + case hex: + case integer: + case octal: + v1.value.integer -= v2.value.integer; + return v1; + + case big: + v.type = big; + bsub(v.value.big, v1.value.big, v2.value.big); + return v; + } + + return v1; +} + + +/******************************************************************************/ +/* + * Multiply a value. + */ + +value_t valueMul(value_t v1, value_t v2) +{ + value_t v; + + a_assert(v1.valid); + a_assert(v2.valid); + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + + if (v1.type != v2.type) + coerce_types(&v1, &v2); + switch (v1.type) { + default: + a_assert(0); + break; + + case flag: + a_assert(v1.type != flag); + break; + +#if FLOATING_POINT_SUPPORT + case floating: + v1.value.floating *= v2.value.floating; + return v1; +#endif + + case byteint: + case percent: + v1.value.byteint *= v2.value.byteint; + return v1; + + case shortint: + v1.value.shortint *= v2.value.shortint; + return v1; + + case hex: + case integer: + case octal: + v1.value.integer *= v2.value.integer; + return v1; + + case big: + v.type = big; + bmul(v.value.big, v1.value.big, v2.value.big); + return v; + } + + return v1; +} + + +/******************************************************************************/ +/* + * Divide a value. + */ + +value_t valueDiv(value_t v1, value_t v2) +{ + value_t v; + + a_assert(v1.valid); + a_assert(v2.valid); + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + + if (v1.type != v2.type) + coerce_types(&v1, &v2); + switch (v1.type) { + default: + a_assert(0); + break; + + case flag: + a_assert(v1.type != flag); + break; + +#if FLOATING_POINT_SUPPORT + case floating: + v1.value.floating /= v2.value.floating; + return v1; +#endif + + case byteint: + case percent: + v1.value.byteint /= v2.value.byteint; + return v1; + + case shortint: + v1.value.shortint /= v2.value.shortint; + return v1; + + case hex: + case integer: + case octal: + v1.value.integer /= v2.value.integer; + return v1; + + case big: + v.type = big; + bdiv(v.value.big, v1.value.big, v2.value.big); + return v; + } + + return v1; +} + + +/******************************************************************************/ +/* + * Compare a value. + */ + +int valueCmp(value_t v1, value_t v2) +{ + a_assert(v1.valid); + a_assert(v2.valid); + + if (v1.type != v2.type) + coerce_types(&v1, &v2); + if (v1.type != v2.type) { +/* + * Make v2 == v1 + */ + a_assert(v1.type == v2.type); + v2 = v1; + return 0; + } + switch (v1.type) { + case string: + if (v1.value.string == NULL && v2.value.string == NULL) { + return 0; + } else if (v1.value.string == NULL) { + return -1; + } else if (v2.value.string == NULL) { + return 1; + } else { + return gstrcmp(v1.value.string, v2.value.string); + } + /* Nobody here */ + + case flag: + if (v1.value.flag < v2.value.flag) + return -1; + else if (v1.value.flag == v2.value.flag) + return 0; + else return 1; + +#if FLOATING_POINT_SUPPORT + case floating: + if (v1.value.floating < v2.value.floating) + return -1; + else if (v1.value.floating == v2.value.floating) + return 0; + else return 1; +#endif + + case byteint: + case percent: + if (v1.value.byteint < v2.value.byteint) + return -1; + else if (v1.value.byteint == v2.value.byteint) + return 0; + else return 1; + + case shortint: + if (v1.value.shortint < v2.value.shortint) + return -1; + else if (v1.value.shortint == v2.value.shortint) + return 0; + else return 1; + + case hex: + case integer: + case octal: + if (v1.value.integer < v2.value.integer) + return -1; + else if (v1.value.integer == v2.value.integer) + return 0; + else return 1; + + case big: + return bcompare(v1.value.big, v2.value.big); + + default: + a_assert(0); + return 0; + } +} + + +/******************************************************************************/ +/* + * If type mismatch, then coerce types to big. + * Note: Known bug, casting of negative bigs to floats doesn't work. + */ + +static void coerce_types(register value_t* v1, register value_t* v2) +{ +#if FLOATING_POINT_SUPPORT + if (v1->type == floating) { + v2->type = floating; + v2->value.floating = (double) v2->value.integer; + if (v2->type == big) + v2->value.floating = (double) v2->value.big[BLOW] + + (double) v2->value.big[BHIGH] * (double) MAXINT; + + } else if (v2->type == floating) { + v1->type = floating; + v1->value.floating = (double) v1->value.integer; + if (v1->type == big) + v1->value.floating = (double) v1->value.big[BLOW] + + (double) v1->value.big[BHIGH] * (double) MAXINT; + + } else if (v1->type == big) { +#else + if (v1->type == big) { +#endif /* FLOATING_POINT_SUPPORT */ + v2->value.big[BLOW] = value_to_integer(v2); + if (valueNegative(v2)) + v2->value.big[BHIGH] = -1; + else + v2->value.big[BHIGH] = 0; + v2->type = big; + + } else if (v2->type == big) { + if (valueNegative(v1)) + v1->value.big[BHIGH] = -1; + else + v1->value.big[BHIGH] = 0; + v1->value.big[BLOW] = value_to_integer(v1); + v1->type = big; + + + } else if (v1->type == integer) { + v2->value.integer = value_to_integer(v2); + v2->type = integer; + + } else if (v2->type == integer) { + v1->value.integer = value_to_integer(v1); + v1->type = integer; + + } else if (v1->type != integer) { + v2->type = v1->type; + + } else if (v2->type != integer) { + v1->type = v2->type; + + } + a_assert(v1->type == v2->type); +} + + +/******************************************************************************/ +/* + * Return true if the value is numeric and negative. Otherwise return 0. + */ + +int valueNegative(value_t* vp) +{ + switch (vp->type) { + default: + case string: + case bytes: + return 0; + +#if FLOATING_POINT_SUPPORT + case floating: + if (vp->value.floating < 0) + return 1; + return 0; +#endif + + case flag: + if ((signed char)vp->value.flag < 0) + return 1; + return 0; + + case byteint: + case percent: + if ((signed char)vp->value.byteint < 0) + return 1; + return 0; + + case shortint: + if (vp->value.shortint < 0) + return 1; + return 0; + + case hex: + case integer: + case octal: + if (vp->value.integer < 0) + return 1; + return 0; + + case big: + if (vp->value.big[BHIGH] < 0) + return 1; + return 0; + } +} + +/******************************************************************************/ +/* + * Return true if the value is numeric and zero. Otherwise return 0. + */ + +int valueZero(value_t* vp) +{ + switch (vp->type) { + default: + case string: + case bytes: + return 0; + +#if FLOATING_POINT_SUPPORT + case floating: + if (vp->value.floating == 0) + return 1; + return 0; +#endif + + case flag: + if (vp->value.flag == 0) + return 1; + return 0; + + case byteint: + case percent: + if (vp->value.byteint == 0) + return 1; + return 0; + + case shortint: + if (vp->value.shortint == 0) + return 1; + return 0; + + case hex: + case integer: + case octal: + if (vp->value.integer == 0) + return 1; + return 0; + + case big: + if (vp->value.big[BHIGH] == 0 && vp->value.big[BLOW] == 0) + return 1; + return 0; + } +} + + +/******************************************************************************/ +/* + * Cast a value to an integer. Cannot be called for floating, non-numerics + * or bigs. + */ + +static int value_to_integer(value_t* vp) +{ + switch (vp->type) { + default: + case string: + case bytes: + case big: +#if FLOATING_POINT_SUPPORT + case floating: + a_assert(0); + return -1; +#endif + + case flag: + return (int) vp->value.flag; + + case byteint: + case percent: + return (int) vp->value.byteint; + + case shortint: + return (int) vp->value.shortint; + + case hex: + case integer: + case octal: + return (int) vp->value.integer; + } +} + + +/******************************************************************************/ +/* + * Convert a value to a text based representation of its value + */ + +void valueSprintf(char_t** out, int size, char_t* fmt, value_t vp) +{ + char_t *src, *dst, *tmp, *dst_start; + + a_assert(out); + + *out = NULL; + + if (! vp.valid) { + *out = bstrdup(B_L, T("Invalid")); + return; + } + + switch (vp.type) { + case flag: + if (fmt == NULL || *fmt == '\0') { + *out = bstrdup(B_L, (vp.value.flag) ? T("true") : T("false")); + } else { + fmtAlloc(out, size, fmt, (vp.value.flag) ? T("true") : T("false")); + } + break; + +#if FLOATING_POINT_SUPPORT + case floating: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("%f"), vp.value.floating); + } else { + fmtAlloc(out, size, fmt, vp.value.floating); + } + break; +#endif + + case hex: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("0x%lx"), vp.value.hex); + } else { + fmtAlloc(out, size, fmt, vp.value.hex); + } + break; + + case big: + if (*out == NULL) { + *out = btoa(vp.value.big, NULL, 0); + } else { + btoa(vp.value.big, *out, size); + } + break; + + case integer: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("%ld"), vp.value.integer); + } else { + fmtAlloc(out, size, fmt, vp.value.integer); + } + break; + + case octal: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("0%lo"), vp.value.octal); + } else { + fmtAlloc(out, size, fmt, vp.value.octal); + } + break; + + case percent: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("%d%%"), vp.value.percent); + } else { + fmtAlloc(out, size, fmt, vp.value.percent); + } + break; + + case byteint: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("%d"), (int) vp.value.byteint); + } else { + fmtAlloc(out, size, fmt, (int) vp.value.byteint); + } + break; + + case shortint: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("%d"), (int) vp.value.shortint); + } else { + fmtAlloc(out, size, fmt, (int) vp.value.shortint); + } + break; + + case string: + case errmsg: + src = vp.value.string; + + if (src == NULL) { + *out = bstrdup(B_L, T("NULL")); + } else if (fmt && *fmt) { + fmtAlloc(out, size, fmt, src); + + } else { + + *out = balloc(B_L, size); + dst_start = dst = *out; + for (; *src != '\0'; src++) { + if (dst >= &dst_start[VALUE_MAX_STRING - 5]) + break; + switch (*src) { + case '\a': *dst++ = '\\'; *dst++ = 'a'; break; + case '\b': *dst++ = '\\'; *dst++ = 'b'; break; + case '\f': *dst++ = '\\'; *dst++ = 'f'; break; + case '\n': *dst++ = '\\'; *dst++ = 'n'; break; + case '\r': *dst++ = '\\'; *dst++ = 'r'; break; + case '\t': *dst++ = '\\'; *dst++ = 't'; break; + case '\v': *dst++ = '\\'; *dst++ = 'v'; break; + case '\\': *dst++ = '\\'; *dst++ = '\\'; break; + case '"': *dst++ = '\\'; *dst++ = '\"'; break; + default: + if (gisprint(*src)) { + *dst++ = *src; + } else { + fmtAlloc(&tmp, size, T("\\x%02x"), + (unsigned int) *src); + gstrcpy(dst, tmp); + bfreeSafe(B_L, tmp); + dst += 4; + } + break; + } + } + *dst++ = '\0'; + } + break; + +#if UNUSED + case bytes: + asrc = vp.value.bytes; + + if (asrc == NULL) { + *out = bstrdup(B_L, T("NULL")); + + } else if (fmt && *fmt) { + fmtAlloc(out, size, fmt, asrc); + + } else { + + dst_start = dst; + for (; *asrc != '\0'; asrc++) { + if (dst >= &dst_start[VALUE_MAX_STRING - 5]) + break; + switch (*asrc) { + case '\a': *dst++ = '\\'; *dst++ = 'a'; break; + case '\b': *dst++ = '\\'; *dst++ = 'b'; break; + case '\f': *dst++ = '\\'; *dst++ = 'f'; break; + case '\n': *dst++ = '\\'; *dst++ = 'n'; break; + case '\r': *dst++ = '\\'; *dst++ = 'r'; break; + case '\t': *dst++ = '\\'; *dst++ = 't'; break; + case '\v': *dst++ = '\\'; *dst++ = 'v'; break; + case '\\': *dst++ = '\\'; *dst++ = '\\'; break; + case '"': *dst++ = '\\'; *dst++ = '\"'; break; + default: + if (gisprint(*asrc)) { + *dst++ = *asrc; + } else { + fmtAlloc(dst, size, + T("\\x%02x"), (unsigned int) *asrc); + dst += 4; + } + break; + } + } + *dst++ = '\0'; + } + break; +#endif + + default: + a_assert(0); + } +} + +/******************************************************************************/ +/* + * Print a value to the named file descriptor + */ + +void valueFprintf(FILE* fp, char_t* fmt, value_t vp) +{ + char_t *buf; + + buf = NULL; + valueSprintf(&buf, VALUE_MAX_STRING, fmt, vp); + gfputs(buf, fp); + bfreeSafe(B_L, buf); + fflush(fp); +} + +/******************************************************************************/ +/* + * Ascii to value conversion + */ + +value_t valueAtov(char_t* s, int pref_type) +{ + vtype_t type; + value_t v; + long tmp[2], tmp2[2], base[2]; + int i, len, num; + + a_assert(0 <= pref_type && pref_type < 99); /* Sanity check */ + a_assert(s); + + v = value_null; + if (s == NULL) { + return value_null; + } + + base[BLOW] = 10; + base[BHIGH] = 0; + len = gstrlen(s); + +/* + * Determine the value type + */ + type = undefined; + if (pref_type <= 0) { + if (gisdigit(*s)) { + base[BHIGH] = 0; + if (s[len - 1] == '%') { + type = percent; + len --; + base[BLOW] = 10; + } else if (*s == '0') { + if (s[1] == 'x') { + type = hex; + s += 2; + len -= 2; + base[BLOW] = 16; + } else if (s[1] == '\0') { + type = integer; + base[BLOW] = 10; + } else { + type = octal; + s++; + len--; + base[BLOW] = 8; + } + } else { + type = integer; + base[BLOW] = 10; + } + + } else { + if (gstrcmp(s, T("true")) == 0 || gstrcmp(s, T("false")) == 0) { + type = flag; + } else if (*s == '\'' && s[len - 1] == '\'') { + type = string; + s++; + len -= 2; + } else if (*s == '\"' && s[len - 1] == '\"') { + type = string; + s++; + len -= 2; + } else { + type = string; + } + } + v.type = type; + + } else + v.type = pref_type; + v.valid = 1; + +/* + * Do the conversion. Always use big arithmetic + */ + switch (v.type) { + case hex: + if (!isdigit(s[0])) { + if (gtolower(s[0]) >= 'a' || gtolower(s[0]) <= 'f') { + v.value.big[BLOW] = 10 + gtolower(s[0]) - 'a'; + } else { + v.value.big[BLOW] = 0; + } + } else { + v.value.big[BLOW] = s[0] - '0'; + } + v.value.big[BHIGH] = 0; + for (i = 1; i < len; i++) { + if (!isdigit(s[i])) { + if (gtolower(s[i]) < 'a' || gtolower(s[i]) > 'f') { + break; + } + num = 10 + gtolower(s[i]) - 'a'; + } else { + num = s[i] - '0'; + } + bmul(tmp, v.value.big, base); + binit(tmp2, 0, num); + badd(v.value.big, tmp, tmp2); + } + v.value.hex = v.value.big[BLOW]; + break; + + case shortint: + case byteint: + case integer: + case percent: + case octal: + case big: + v.value.big[BHIGH] = 0; + if (gisdigit(s[0])) + v.value.big[BLOW] = s[0] - '0'; + else + v.value.big[BLOW] = 0; + for (i = 1; i < len && gisdigit(s[i]); i++) { + bmul(tmp, v.value.big, base); + binit(tmp2, 0, s[i] - '0'); + badd(v.value.big, tmp, tmp2); + } + switch (v.type) { + case shortint: + v.value.shortint = (short) v.value.big[BLOW]; + break; + case byteint: + v.value.byteint = (char) v.value.big[BLOW]; + break; + case integer: + v.value.integer = (int) v.value.big[BLOW]; + break; + case percent: + v.value.percent = (char) v.value.big[BLOW]; + break; + case octal: + v.value.octal = (int) v.value.big[BLOW]; + break; + default: + break; + } + break; + +#if FLOATING_POINT_SUPPORT + case floating: + gsscanf(s, T("%f"), &v.value.floating); + break; +#endif + + case flag: + if (*s == 't') + v.value.flag = 1; + else v.value.flag = 0; + break; + + case string: +/* + * Note this always ballocs a string + */ + v = valueString(s, VALUE_ALLOCATE); + break; + + case bytes: + v = valueBytes((char*) s, VALUE_ALLOCATE); + break; + +#if UNUSED + case literal: + v = value_literal(bstrdup(B_L, s)); + v.value.literal[len] = '\0'; + break; +#endif + + case undefined: + case symbol: + default: + v.valid = 0; + a_assert(0); + } + return v; +} + +#endif /* !UEMF */ /******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/wbase64.c b/c/src/libnetworking/rtems_webserver/wbase64.c deleted file mode 100644 index 0898b16728..0000000000 --- a/c/src/libnetworking/rtems_webserver/wbase64.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * base64.c -- Base64 Mime encoding - * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. - * - * See the file "license.txt" for usage and redistribution license requirements - */ - -/******************************** Description *********************************/ - -/* - * The base64 command encodes and decodes a string in mime base64 format - */ - -/********************************* Includes ***********************************/ - -#include "wsIntrn.h" - -/******************************** Local Data **********************************/ -/* - * Mapping of ANSI chars to base64 Mime encoding alphabet (see below) - */ - -static char_t map64[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -}; - -static char_t alphabet64[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/', -}; - -/*********************************** Code *************************************/ -/* - * Decode a buffer from "string" and into "outbuf" - */ - -int websDecode64(char_t* outbuf, char_t* string, int outlen) -{ - unsigned long shiftbuf; - char_t* cp; - char_t* op; - int c, i, j, shift; - - op = outbuf; - *op = '\0'; - cp = string; - while (*cp && *cp != '=') { -/* - * Map 4 (6bit) input bytes and store in a single long (shiftbuf) - */ - shiftbuf = 0; - shift = 18; - for (i = 0; i < 4 && *cp && *cp != '='; i++, cp++) { - c = map64[*cp & 0xff]; - if (c == -1) { - error(E_L, E_LOG, T("Bad string: %s at %c index %d"), string, - c, i); - return -1; - } - shiftbuf = shiftbuf | (c << shift); - shift -= 6; - } -/* - * Interpret as 3 normal 8 bit bytes (fill in reverse order). - * Check for potential buffer overflow before filling. - */ - --i; - if ((op + i) >= &outbuf[outlen]) { - gstrcpy(outbuf, T("String too big")); - return -1; - } - for (j = 0; j < i; j++) { - *op++ = (char_t) ((shiftbuf >> (8 * (2 - j))) & 0xff); - } - *op = '\0'; - } - return 0; -} - - -/******************************************************************************/ -/* - * Encode a buffer from "string" into "outbuf" - */ - -void websEncode64(char_t* outbuf, char_t* string, int outlen) -{ - unsigned long shiftbuf; - char_t* cp; - char_t* op; - int x, i, j, shift; - - op = outbuf; - *op = '\0'; - cp = string; - while (*cp) { -/* - * Take three characters and create a 24 bit number in shiftbuf - */ - shiftbuf = 0; - for (j = 2; j >= 0 && *cp; j--, cp++) { - shiftbuf |= ((*cp & 0xff) << (j * 8)); - } -/* - * Now convert shiftbuf to 4 base64 letters. The i,j magic calculates - * how many letters need to be output. - */ - shift = 18; - for (i = ++j; i < 4 && op < &outbuf[outlen] ; i++) { - x = (shiftbuf >> shift) & 0x3f; - *op++ = alphabet64[(shiftbuf >> shift) & 0x3f]; - shift -= 6; - } -/* - * Pad at the end with '=' - */ - while (j-- > 0) { - *op++ = '='; - } - *op = '\0'; - } -} -/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/webcomp.c b/c/src/libnetworking/rtems_webserver/webcomp.c index d642a144c0..575f4be46b 100644 --- a/c/src/libnetworking/rtems_webserver/webcomp.c +++ b/c/src/libnetworking/rtems_webserver/webcomp.c @@ -1,7 +1,7 @@ /* * webcomp -- Compile web pages into C source * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -71,10 +71,10 @@ static int compile(char_t *fileList, char_t *prefix) FILE *lp; time_t now; char_t file[FNAMESIZE]; - char_t *cp; + char_t *cp, *sl; char buf[512]; - char *p; - int j, i, len, fd, nFile; + unsigned char *p; + int j, i, len, fd, nFile; /* * Open list of files @@ -87,7 +87,7 @@ static int compile(char_t *fileList, char_t *prefix) time(&now); fprintf(stdout, "/*\n * webrom.c -- Compiled Web Pages\n *\n"); fprintf(stdout, " * Compiled by GoAhead WebCompile: %s */\n\n", - ctime(&now)); + gctime(&now)); fprintf(stdout, "#include \"wsIntrn.h\"\n\n"); fprintf(stdout, "#ifndef WEBS_PAGE_ROM\n"); fprintf(stdout, "websRomPageIndexType websRomPageIndex[] = {\n"); @@ -102,6 +102,9 @@ static int compile(char_t *fileList, char_t *prefix) if ((p = strchr(file, '\n')) || (p = strchr(file, '\r'))) { *p = '\0'; } + if (*file == '\0') { + continue; + } if (gstat(file, &sbuf) == 0 && sbuf.st_mode & S_IFDIR) { continue; } @@ -109,7 +112,7 @@ static int compile(char_t *fileList, char_t *prefix) fprintf(stderr, "Can't open file %s\n", file); return -1; } - fprintf(stdout, "static unsigned char page_%d[] = {\n", nFile); + fprintf(stdout, "static const unsigned char page_%d[] = {\n", nFile); while ((len = read(fd, buf, sizeof(buf))) > 0) { p = buf; @@ -143,6 +146,9 @@ static int compile(char_t *fileList, char_t *prefix) if ((p = strchr(file, '\n')) || (p = strchr(file, '\r'))) { *p = '\0'; } + if (*file == '\0') { + continue; + } /* * Remove the prefix and add a leading "/" when we print the path */ @@ -151,6 +157,9 @@ static int compile(char_t *fileList, char_t *prefix) } else { cp = file; } + while((sl = strchr(file, '\\')) != NULL) { + *sl = '/'; + } if (*cp == '/') { cp++; } @@ -159,8 +168,8 @@ static int compile(char_t *fileList, char_t *prefix) fprintf(stdout, " { T(\"/%s\"), 0, 0 },\n", cp); continue; } - fprintf(stdout, " { T(\"/%s\"), page_%d, %ld },\n", cp, nFile, - (long) sbuf.st_size); + fprintf(stdout, " { T(\"/%s\"), page_%d, %d },\n", cp, nFile, + sbuf.st_size); nFile++; } fclose(lp); diff --git a/c/src/libnetworking/rtems_webserver/webmain.c b/c/src/libnetworking/rtems_webserver/webmain.c index 9f467b5167..264a8d66c5 100644 --- a/c/src/libnetworking/rtems_webserver/webmain.c +++ b/c/src/libnetworking/rtems_webserver/webmain.c @@ -25,12 +25,22 @@ #include +#ifdef WEBS_SSL_SUPPORT +#include "websSSL.h" +#endif + +#ifdef USER_MANAGEMENT_SUPPORT +#include "um.h" +void formDefineUserMgmt(void); +#endif + /*********************************** Locals ***********************************/ /* * Change configuration here */ -static char_t *rootWeb = T("web"); /* Root web directory */ +extern const char *tftpServer; +static char_t *rootWeb = T("goahead"); /* Root web directory */ static char_t *password = T(""); /* Security password */ static int port = 80; /* Server port */ static int retries = 5; /* Server port retries */ @@ -113,33 +123,47 @@ rtems_httpd_daemon() /* * Initialize the web server */ - while (initWebs() < 0) { - printf("\nUnable to initialize Web server !!\n" - " Suspending the task. Resume to try again.\n"); - rtems_task_suspend( RTEMS_SELF); + if (initWebs() < 0) { + rtems_panic("Unable to initialize Web server !!\n"); } +#ifdef WEBS_SSL_SUPPORT + websSSLOpen(); +#endif + /* * Basic event loop. SocketReady returns true when a socket is ready for * service. SocketSelect will block until an event occurs. SocketProcess * will actually do the servicing. */ while (!finished) { - if (socketReady() || socketSelect()) { - socketProcess(); + if (socketReady(-1) || socketSelect(-1, 2000)) { + socketProcess(-1); } + /*websCgiCleanup();*/ + emfSchedProcess(); } +#ifdef WEBS_SSL_SUPPORT + websSSLClose(); +#endif + +#ifdef USER_MANAGEMENT_SUPPORT + umClose(); +#endif + /* * Close the socket module, report memory leaks and close the memory allocator */ websCloseServer(); + websDefaultClose(); socketClose(); + symSubClose(); #if B_STATS memLeaks(); #endif bclose(); - rtems_task_delete( RTEMS_SELF ); + rtems_task_delete( RTEMS_SELF ); } /******************************************************************************/ @@ -177,15 +201,14 @@ static int initWebs() memcpy((char *) &intaddr, (char *) hp->h_addr_list[0], (size_t) hp->h_length); +#if 0 /* - * Set ../web as the root web. Modify this to suit your needs + * Set /TFTP/x.y.z.w/goahead as the root web. Modify to suit your needs */ - getcwd(dir, sizeof(dir)); - if ((cp = strrchr(dir, '/'))) { - *cp = '\0'; - } - sprintf(webdir, "%s/%s", dir, rootWeb); - + sprintf(webdir, "/TFTP/%s/%s", tftpServer, rootWeb); +#else + sprintf(webdir, "/"); +#endif /* * Configure the web server options before opening the web server */ @@ -198,7 +221,11 @@ static int initWebs() /* * Configure the web server options before opening the web server */ +#if 0 websSetDefaultPage(T("default.asp")); +#else + websSetDefaultPage(T("index.html")); +#endif websSetPassword(password); /* @@ -380,7 +407,11 @@ static int websHomePageHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, * If the empty or "/" URL is invoked, redirect default URLs to the home page */ if (*url == '\0' || gstrcmp(url, T("/")) == 0) { +#if 0 websRedirect(wp, T("home.asp")); +#else + websRedirect(wp, T("index.html")); +#endif return 1; } return 0; @@ -391,12 +422,14 @@ static int websHomePageHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, #if B_STATS static void memLeaks() { - int fd; + int fd=1; - if ((fd = gopen(T("leak.txt"), O_CREAT | O_TRUNC | O_WRONLY)) >= 0) { + /* if ((fd = gopen(T("leak.txt"), O_CREAT | O_TRUNC | O_WRONLY)) >= 0) { */ bstats(fd, printMemStats); + /* close(fd); } + */ } /******************************************************************************/ @@ -416,4 +449,51 @@ static void printMemStats(int handle, char_t *fmt, ...) } #endif -/******************************************************************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/* + * Default error handler. The developer should insert code to handle + * error messages in the desired manner. + */ + +void defaultErrorHandler(int etype, char_t *msg) +{ +#if 1 + write(1, msg, gstrlen(msg)); +#endif +} + +/*****************************************************************************/ +/* + * Trace log. Customize this function to log trace output + */ + +void defaultTraceHandler(int level, char_t *buf) +{ +/* + * The following code would write all trace regardless of level + * to stdout. + */ +#if 1 + if (buf) { + write(1, buf, gstrlen(buf)); + } +#endif +} + +/*****************************************************************************/ +/* + * Returns a pointer to an allocated qualified unique temporary file name. + * This filename must eventually be deleted with bfree(); + */ + +char_t *websGetCgiCommName() +{ + char_t *pname1, *pname2; + + pname1 = tempnam(NULL, T("cgi")); + pname2 = bstrdup(B_L, pname1); + free(pname1); + return pname2; +} diff --git a/c/src/libnetworking/rtems_webserver/webpage.c b/c/src/libnetworking/rtems_webserver/webpage.c index d2b976e38f..efac13f508 100644 --- a/c/src/libnetworking/rtems_webserver/webpage.c +++ b/c/src/libnetworking/rtems_webserver/webpage.c @@ -1,7 +1,7 @@ /* * Page.c -- Support for page retrieval. * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -41,6 +41,8 @@ int websPageOpen(webs_t wp, char_t *lpath, char_t *path, int mode, int perm) void websPageClose(webs_t wp) { + a_assert(websValid(wp)); + #if WEBS_PAGE_ROM websRomPageClose(wp->docfd); #else diff --git a/c/src/libnetworking/rtems_webserver/webrom.c b/c/src/libnetworking/rtems_webserver/webrom.c index 225d8bbf22..61d795949e 100644 --- a/c/src/libnetworking/rtems_webserver/webrom.c +++ b/c/src/libnetworking/rtems_webserver/webrom.c @@ -1,7 +1,8 @@ /* * webrom.c -- Compiled Web Pages * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * * See the file "license.txt" for usage and redistribution license requirements */ diff --git a/c/src/libnetworking/rtems_webserver/webs.c b/c/src/libnetworking/rtems_webserver/webs.c index cee03ee01c..3181c602ec 100644 --- a/c/src/libnetworking/rtems_webserver/webs.c +++ b/c/src/libnetworking/rtems_webserver/webs.c @@ -1,32 +1,38 @@ /* * webs.c -- GoAhead Embedded HTTP webs server * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ */ /******************************** Description *********************************/ /* - * This module implements an embedded HTTP/1.1 webs server. It supports + * This module implements an embedded HTTP/1.1 web server. It supports * loadable URL handlers that define the nature of URL processing performed. */ /********************************* Includes ***********************************/ #include "wsIntrn.h" +#ifdef DIGEST_ACCESS_SUPPORT +#include "websda.h" +#endif /******************************** Global Data *********************************/ -websStatsType websStats; /* Web access stats */ +websStatsType websStats; /* Web access stats */ webs_t *webs; /* Open connection list head */ -sym_fd_t websMime; /* Set of mime types */ -int websMax; /* List size */ -int websPort; /* Listen port for server */ -char_t websHost[64]; /* Host name for the server */ -char_t websIpaddr[64]; /* IP address for the server */ +sym_fd_t websMime; /* Set of mime types */ +int websMax; /* List size */ +int websPort; /* Listen port for server */ +char_t websHost[64]; /* Host name for the server */ +char_t websIpaddr[64]; /* IP address for the server */ char_t *websHostUrl = NULL; /* URL to access server */ +char_t *websIpaddrUrl = NULL; /* URL to access server */ /*********************************** Locals ***********************************/ /* @@ -40,11 +46,13 @@ websErrorType websErrors[] = { { 302, T("Redirect") }, { 304, T("User local copy") }, { 400, T("Page not found") }, - { 401, T("Password Required") }, + { 401, T("Unauthorized") }, + { 403, T("Forbidden") }, { 404, T("Site or Page Not Found") }, { 405, T("Access Denied") }, { 500, T("Web Error") }, - { 503, T("Site Temporarily Unavailable. Try again") }, + { 501, T("Not Implemented") }, + { 503, T("Site Temporarily Unavailable. Try again.") }, { 0, NULL } }; @@ -54,29 +62,25 @@ static int websLogFd; /* Log file handle */ #endif static int websListenSock; /* Listen socket */ +static char_t websRealm[64] = T("GoAhead"); /* Realm name */ + +static int websOpenCount = 0; /* count of apps using this module */ /**************************** Forward Declarations ****************************/ -static int websAccept(int sid, char *ipaddr, int port); -static int websAlloc(int sid); + static char_t *websErrorMsg(int code); -static void websFree(webs_t wp); -static void websFreeVar(sym_t* sp); -static int websGetInput(webs_t wp, char_t **ptext, int *nbytes); -static int websParseFirst(webs_t wp, char_t *text); -static void websParseRequest(webs_t wp); -static void websReadEvent(webs_t wp); -static void websSocketEvent(int sid, int mask, int data); -static void websTimeout(long wp); -static void websTimeoutCancel(webs_t wp); -static void websMarkTime(webs_t wp); -static int websGetTimeSinceMark(webs_t wp); +static int websGetInput(webs_t wp, char_t **ptext, int *nbytes); +static int websParseFirst(webs_t wp, char_t *text); +static void websParseRequest(webs_t wp); +static void websSocketEvent(int sid, int mask, int data); +static int websGetTimeSinceMark(webs_t wp); #if WEBS_LOG_SUPPORT -static void websLog(webs_t wp, int code); +static void websLog(webs_t wp, int code); #endif #if WEBS_IF_MODIFIED_SUPPORT -static time_t dateParse(time_t tip, char_t *cmd); +static time_t dateParse(time_t tip, char_t *cmd); #endif /*********************************** Code *************************************/ @@ -88,6 +92,10 @@ int websOpenServer(int port, int retries) { websMimeType *mt; + if (++websOpenCount != 1) { + return websPort; + } + a_assert(port > 0); a_assert(retries >= 0); @@ -95,10 +103,12 @@ int websOpenServer(int port, int retries) websRomOpen(); #endif + webs = NULL; + websMax = 0; /* * Create a mime type lookup table for quickly determining the content type */ - websMime = symOpen(256); + websMime = symOpen(WEBS_SYM_INIT * 4); a_assert(websMime >= 0); for (mt = websMimeList; mt->type; mt++) { symEnter(websMime, mt->ext, valueString(mt->type, 0), 0); @@ -111,6 +121,7 @@ int websOpenServer(int port, int retries) if (websUrlHandlerOpen() < 0) { return -1; } + websFormOpen(); #if WEBS_LOG_SUPPORT /* @@ -134,6 +145,10 @@ void websCloseServer() webs_t wp; int wid; + if (--websOpenCount > 0) { + return; + } + /* * Close the listen handle first then all open connections. */ @@ -160,7 +175,7 @@ void websCloseServer() #if WEBS_PAGE_ROM websRomClose(); #endif - symClose(websMime, NULL); + symClose(websMime); websFormClose(); websUrlHandlerClose(); } @@ -193,19 +208,26 @@ int websOpenListen(int port, int retries) orig, port - 1); return -1; } - goahead_trace(0, T("webs: Listening for HTTP requests on port %d\n"), port); /* * Determine the full URL address to access the home page for this web server */ websPort = port; bfreeSafe(B_L, websHostUrl); - websHostUrl = NULL; + bfreeSafe(B_L, websIpaddrUrl); + websIpaddrUrl = websHostUrl = NULL; + if (port == 80) { websHostUrl = bstrdup(B_L, websHost); + websIpaddrUrl = bstrdup(B_L, websIpaddr); } else { - gsnprintf(&websHostUrl, WEBS_MAX_URL + 80, T("%s:%d"), websHost, port); + fmtAlloc(&websHostUrl, WEBS_MAX_URL + 80, T("%s:%d"), websHost, port); + fmtAlloc(&websIpaddrUrl, WEBS_MAX_URL + 80, T("%s:%d"), + websIpaddr, port); } + trace(0, T("webs: Listening for HTTP requests at address %s\n"), + websIpaddrUrl); + return port; } @@ -221,7 +243,8 @@ void websCloseListen() websListenSock = -1; } bfreeSafe(B_L, websHostUrl); - websHostUrl = NULL; + bfreeSafe(B_L, websIpaddrUrl); + websIpaddrUrl = websHostUrl = NULL; } /******************************************************************************/ @@ -229,7 +252,7 @@ void websCloseListen() * Accept a connection */ -static int websAccept(int sid, char *ipaddr, int port) +int websAccept(int sid, char *ipaddr, int port, int listenSid) { webs_t wp; int wid; @@ -247,6 +270,7 @@ static int websAccept(int sid, char *ipaddr, int port) } wp = webs[wid]; a_assert(wp); + wp->listenSid = listenSid; ascToUni(wp->ipaddr, ipaddr, sizeof(wp->ipaddr)); @@ -263,13 +287,13 @@ static int websAccept(int sid, char *ipaddr, int port) /* * Arrange for websSocketEvent to be called when read data is available */ - socketCreateHandler(sid, SOCKET_READABLE , websSocketEvent, (int) wp); + socketCreateHandler(sid, SOCKET_READABLE, websSocketEvent, (int) wp); /* * Arrange for a timeout to kill hung requests */ - wp->timeout = emfCreateTimer(WEBS_TIMEOUT, websTimeout, (long) wp); - goahead_trace(5, T("webs: accept request\n")); + wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout, (void *) wp); + trace(8, T("webs: accept request\n")); return 0; } @@ -308,10 +332,10 @@ static void websSocketEvent(int sid, int mask, int iwp) * Note: we never block as the socket is always in non-blocking mode. */ -static void websReadEvent(webs_t wp) +void websReadEvent(webs_t wp) { char_t *text; - int rc, nbytes, len, done; + int rc, nbytes, len, done, fd; a_assert(wp); a_assert(websValid(wp)); @@ -323,6 +347,7 @@ static void websReadEvent(webs_t wp) * and socketRead is called to read posted data. */ text = NULL; + fd = -1; for (done = 0; !done; ) { if (text) { bfree(B_L, text); @@ -366,15 +391,29 @@ static void websReadEvent(webs_t wp) * need to separate the lines with '\n' */ if (ringqLen(&wp->header) > 0) { - ringqPutstr(&wp->header, T("\n")); + ringqPutStr(&wp->header, T("\n")); } - ringqPutstr(&wp->header, text); + ringqPutStr(&wp->header, text); break; case WEBS_POST_CLEN: /* - * POST request with content specified by a content length + * POST request with content specified by a content length. + * If this is a CGI request, write the data to the cgi stdin. + * socketGets was used to get the data and it strips \n's so + * add them back in here. */ +#ifndef __NO_CGI_BIN + if (wp->flags & WEBS_CGI_REQUEST) { + if (fd == -1) { + fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY, + 0666); + } + gwrite(fd, text, gstrlen(text)); + gwrite(fd, T("\n"), sizeof(char_t)); + nbytes += 1; + } else +#endif if (wp->query) { if (wp->query[0] && !(wp->flags & WEBS_POST_DATA)) { /* @@ -412,7 +451,6 @@ static void websReadEvent(webs_t wp) wp->clen -= nbytes; if (wp->clen > 0) { if (nbytes > 0) { - done++; break; } done++; @@ -428,7 +466,21 @@ static void websReadEvent(webs_t wp) case WEBS_POST: /* * POST without content-length specification + * If this is a CGI request, write the data to the cgi stdin. + * socketGets was used to get the data and it strips \n's so + * add them back in here. */ + +#ifndef __NO_CGI_BIN + if (wp->flags & WEBS_CGI_REQUEST) { + if (fd == -1) { + fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY, + 0666); + } + gwrite(fd, text, gstrlen(text)); + gwrite(fd, T("\n"), sizeof(char_t)); + } else +#endif if (wp->query && *wp->query && !(wp->flags & WEBS_POST_DATA)) { len = gstrlen(wp->query); wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) + @@ -451,6 +503,11 @@ static void websReadEvent(webs_t wp) break; } } + + if (fd != -1) { + fd = gclose (fd); + } + if (text) { bfree(B_L, text); } @@ -459,8 +516,8 @@ static void websReadEvent(webs_t wp) /******************************************************************************/ /* * Get input from the browser. Return TRUE (!0) if the request has been - * handled. Return -1 on errors, 1 if input read, and 0 to instruct the - * caller to call again for more input. + * handled. Return -1 on errors or if the request has been processed, + * 1 if input read, and 0 to instruct the caller to call again for more input. * * Note: socketRead will Return the number of bytes read if successful. This * may be less than the requested "bufsize" and may be zero. It returns -1 for @@ -493,8 +550,16 @@ static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes) } if (len > 0) { - nbytes = socketRead(wp->sid, buf, len); +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + nbytes = websSSLRead(wp->wsp, buf, len); + } else { + nbytes = socketRead(wp->sid, buf, len); + } +#else + nbytes = socketRead(wp->sid, buf, len); +#endif if (nbytes < 0) { /* Error */ websDone(wp, 0); return -1; @@ -508,20 +573,47 @@ static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes) * is NULL terminated. */ buf[nbytes] = '\0'; - if ((text = ballocAscToUni(buf)) == NULL) { + if ((text = ballocAscToUni(buf, nbytes)) == NULL) { websError(wp, 503, T("Insufficient memory")); return -1; } } } else { +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + nbytes = websSSLGets(wp->wsp, &text); + } else { + nbytes = socketGets(wp->sid, &text); + } +#else nbytes = socketGets(wp->sid, &text); +#endif if (nbytes < 0) { + int eof; /* * Error, EOF or incomplete */ - if (socketEof(wp->sid)) { +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { +/* + * If state is WEBS_BEGIN and the request is secure, a -1 will + * usually indicate SSL negotiation + */ + if (wp->state == WEBS_BEGIN) { + eof = 1; + } else { + eof = websSSLEof(wp->wsp); + } + } else { + eof = socketEof(wp->sid); + } +#else + eof = socketEof(wp->sid); +#endif + + if (eof) { /* * If this is a post request without content length, process * the request as we now have all the data. Otherwise just @@ -559,10 +651,12 @@ static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes) clen = 1; } if (clen > 0) { - return 0; /* Get more data */ +/* + * Return 0 to get more data. + */ + return 0; } return 1; - } /* * We've read the header so go and handle the request @@ -570,7 +664,6 @@ static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes) websUrlHandlerRequest(wp); } return -1; - } } a_assert(text); @@ -587,7 +680,9 @@ static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes) static int websParseFirst(webs_t wp, char_t *text) { - char_t *op, *proto, *url, *host, *query, *path, *port, *ext, *buf; + char_t *op, *proto, *protoVer, *url, *host, *query, *path, *port, *ext; + char_t *buf; + int testPort; a_assert(websValid(wp)); a_assert(text && *text); @@ -621,6 +716,7 @@ static int websParseFirst(webs_t wp, char_t *text) websError(wp, 400, T("Bad HTTP request")); return -1; } + protoVer = gstrtok(NULL, T(" \t\n")); /* * Parse the URL and store all the various URL components. websUrlParse @@ -636,10 +732,28 @@ static int websParseFirst(webs_t wp, char_t *text) } wp->url = bstrdup(B_L, url); + +#ifndef __NO_CGI_BIN + if (gstrstr(url, CGI_BIN) != NULL) { + wp->flags |= WEBS_CGI_REQUEST; + if (wp->flags & WEBS_POST_REQUEST) { + wp->cgiStdin = websGetCgiCommName(); + } + } +#endif + wp->query = bstrdup(B_L, query); wp->host = bstrdup(B_L, host); wp->path = bstrdup(B_L, path); - wp->port = gatoi(port); + wp->protocol = bstrdup(B_L, proto); + wp->protoVersion = bstrdup(B_L, protoVer); + + if ((testPort = socketGetPort(wp->listenSid)) >= 0) { + wp->port = testPort; + } else { + wp->port = gatoi(port); + } + if (gstrcmp(ext, T(".asp")) == 0) { wp->flags |= WEBS_ASP; } @@ -673,9 +787,12 @@ static int websParseFirst(webs_t wp, char_t *text) * Parse a full request */ +#define isgoodchar(s) (gisalnum((s)) || ((s) == '/') || ((s) == '_') || \ + ((s) == '.') || ((s) == '-') ) + static void websParseRequest(webs_t wp) { - char_t *upperKey, *cp, *browser, *lp, *key, *value; + char_t *authType, *upperKey, *cp, *browser, *lp, *key, *value; a_assert(websValid(wp)); @@ -712,7 +829,7 @@ static void websParseRequest(webs_t wp) /* * Create a variable (CGI) for each line in the header */ - gsnprintf(&upperKey, (gstrlen(key) + 6), T("HTTP_%s"), key); + fmtAlloc(&upperKey, (gstrlen(key) + 6), T("HTTP_%s"), key); for (cp = upperKey; *cp; cp++) { if (*cp == '-') *cp = '_'; @@ -730,26 +847,155 @@ static void websParseRequest(webs_t wp) /* * Parse the user authorization. ie. password */ - } else if (gstrcmp(key, T("authorization")) == 0) { - char_t password[FNAMESIZE]; - + } else if (gstricmp(key, T("authorization")) == 0) { /* - * The incoming value is password:username + * Determine the type of Authorization Request */ - if ((cp = gstrchr(value, ' ')) != NULL) { - websDecode64(password, ++cp, sizeof(password)); - } else { - websDecode64(password, value, sizeof(password)); - } - if ((cp = gstrchr(password, ':')) != NULL) { - *cp++ = '\0'; + authType = bstrdup (B_L, value); + a_assert (authType); +/* + * Truncate authType at the next non-alpha character + */ + cp = authType; + while (gisalpha(*cp)) { + cp++; } - if (cp) { - wp->password = bstrdup(B_L, cp); + *cp = '\0'; + + wp->authType = bstrdup(B_L, authType); + bfree(B_L, authType); + + if (gstricmp(wp->authType, T("basic")) == 0) { + char_t userAuth[FNAMESIZE]; +/* + * The incoming value is username:password (Basic authentication) + */ + if ((cp = gstrchr(value, ' ')) != NULL) { + *cp = '\0'; + wp->authType = bstrdup(B_L, value); + websDecode64(userAuth, ++cp, sizeof(userAuth)); + } else { + websDecode64(userAuth, value, sizeof(userAuth)); + } +/* + * Split userAuth into userid and password + */ + if ((cp = gstrchr(userAuth, ':')) != NULL) { + *cp++ = '\0'; + } + if (cp) { + wp->userName = bstrdup(B_L, userAuth); + wp->password = bstrdup(B_L, cp); + } else { + wp->userName = bstrdup(B_L, T("")); + wp->password = bstrdup(B_L, T("")); + } +/* + * Set the flags to indicate digest authentication + */ + wp->flags |= WEBS_AUTH_BASIC; } else { - wp->password = bstrdup(B_L, T("")); - } +#ifdef DIGEST_ACCESS_SUPPORT +/* + * The incoming value is slightly more complicated (Digest) + */ + char_t *np; /* pointer to end of tag name */ + char_t tp; /* temporary character holding space */ + char_t *vp; /* pointer to value */ + char_t *npv; /* pointer to end of value, "next" pointer */ + char_t tpv; /* temporary character holding space */ +/* + * Set the flags to indicate digest authentication + */ + wp->flags |= WEBS_AUTH_DIGEST; +/* + * Move cp to Next word beyond "Digest", + * vp to first char after '='. + */ + cp = value; + while (isgoodchar(*cp)) { + cp++; + } + while (!isgoodchar(*cp)) { + cp++; + } + +/* + * Find beginning of value + */ + vp = gstrchr(cp, '='); + while (vp) { +/* + * Zero-terminate tag name + */ + np = cp; + while (isgoodchar(*np)) { + np++; + } + tp = *np; + *np = 0; +/* + * Advance value pointer to first legit character + */ + vp++; + while (!isgoodchar(*vp)) { + vp++; + } +/* + * Zero-terminate value + */ + npv = vp; + while (isgoodchar(*npv)) { + npv++; + } + tpv = *npv; + *npv = 0; +/* + * Extract the fields + */ + if (gstricmp(cp, T("username")) == 0) { + wp->userName = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("response")) == 0) { + wp->digest = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("opaque")) == 0) { + wp->opaque = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("uri")) == 0) { + wp->uri = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("realm")) == 0) { + wp->realm = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("nonce")) == 0) { + wp->nonce = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("nc")) == 0) { + wp->nc = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("cnonce")) == 0) { + wp->cnonce = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("qop")) == 0) { + wp->qop = bstrdup(B_L, vp); + } +/* + * Restore tag name and value zero-terminations + */ + *np = tp; + *npv = tpv; +/* + * Advance tag name and value pointers + */ + cp = npv; + while (*cp && isgoodchar(*cp)) { + cp++; + } + while (*cp && !isgoodchar(*cp)) { + cp++; + } + if (*cp) { + vp = gstrchr(cp, '='); + } else { + vp = NULL; + } + } +#endif /* DIGEST_ACCESS_SUPPORT */ + } /* if (gstrcmp(wp->authType)) */ /* * Parse the content length */ @@ -758,6 +1004,12 @@ static void websParseRequest(webs_t wp) wp->clen = gatoi(value); websSetVar(wp, T("CONTENT_LENGTH"), value); +/* + * Parse the content type + */ + } else if (gstrcmp(key, T("content-type")) == 0) { + websSetVar(wp, T("CONTENT_TYPE"), value); + #if WEBS_KEEP_ALIVE_SUPPORT } else if (gstrcmp(key, T("connection")) == 0) { strlower(value); @@ -778,7 +1030,7 @@ static void websParseRequest(webs_t wp) if (gstrstr(tmp, T("no-cache"))) { wp->flags |= WEBS_DONT_USE_CACHE; } -#endif +#endif /* WEBS_PROXY_SUPPORT */ /* * Store the cookie @@ -796,42 +1048,22 @@ static void websParseRequest(webs_t wp) char_t *cmd; time_t tip = 0; - if (cp = gstrchr(value, ';')) { + if ((cp = gstrchr(value, ';')) != NULL) { *cp = '\0'; } - if (cp = gstrstr(value, T(", "))) { - cp += 2; - } + fmtAlloc(&cmd, 64, T("%s"), value); - if (gstrstr(cp, T("GMT"))) { - gsnprintf(&cmd, 64, T("clock scan %s -gmt 1"), cp); - } else { - gsnprintf(&cmd, 64, T("clock scan %s"), cp); - } - if (wp->since = dateParse(tip, cmd)) { + if ((wp->since = dateParse(tip, cmd)) != 0) { wp->flags |= WEBS_IF_MODIFIED; } + bfreeSafe(B_L, cmd); -#endif +#endif /* WEBS_IF_MODIFIED_SUPPORT */ } } } - -#if WEBS_IF_MODIFIED_SUPPORT -/******************************************************************************/ -/* - * Parse the date and time string. - */ - -static time_t dateParse(time_t tip, char_t *cmd) -{ - return (time_t)0; -} -#endif - - /******************************************************************************/ /* * Set the variable (CGI) environment for this request. Create variables @@ -842,20 +1074,26 @@ static time_t dateParse(time_t tip, char_t *cmd) void websSetEnv(webs_t wp) { char_t portBuf[8]; - char_t *keyword, *value; + char_t *keyword, *value, *valCheck, *valNew; a_assert(websValid(wp)); websSetVar(wp, T("QUERY_STRING"), wp->query); websSetVar(wp, T("GATEWAY_INTERFACE"), T("CGI/1.1")); websSetVar(wp, T("SERVER_HOST"), websHost); + websSetVar(wp, T("SERVER_NAME"), websHost); websSetVar(wp, T("SERVER_URL"), websHostUrl); websSetVar(wp, T("REMOTE_HOST"), wp->ipaddr); websSetVar(wp, T("REMOTE_ADDR"), wp->ipaddr); websSetVar(wp, T("PATH_INFO"), wp->path); stritoa(websPort, portBuf, sizeof(portBuf)); websSetVar(wp, T("SERVER_PORT"), portBuf); - + websSetVar(wp, T("SERVER_ADDR"), websIpaddr); + fmtAlloc(&value, FNAMESIZE, T("%s/%s"), WEBS_NAME, WEBS_VERSION); + websSetVar(wp, T("SERVER_SOFTWARE"), value); + bfreeSafe(B_L, value); + websSetVar(wp, T("SERVER_PROTOCOL"), wp->protoVersion); + /* * Decode and create an environment query variable for each query keyword. * We split into pairs at each '&', then split pairs at the '='. @@ -869,13 +1107,22 @@ void websSetEnv(webs_t wp) *value++ = '\0'; websDecodeUrl(keyword, keyword, gstrlen(keyword)); websDecodeUrl(value, value, gstrlen(value)); - } else { value = T(""); } if (*keyword) { - websSetVar(wp, keyword, value); +/* + * If keyword has already been set, append the new value to what has + * been stored. + */ + if ((valCheck = websGetVar(wp, keyword, NULL)) != 0) { + fmtAlloc(&valNew, 256, T("%s %s"), valCheck, value); + websSetVar(wp, keyword, valNew); + bfreeSafe(B_L, valNew); + } else { + websSetVar(wp, keyword, value); + } } keyword = gstrtok(NULL, T("&")); } @@ -935,7 +1182,7 @@ int websTestVar(webs_t wp, char_t *var) /******************************************************************************/ /* * Get a webs variable but return a default value if string not found. - * Note, defaultGetValue can be NULL to permit testing existance. + * Note, defaultGetValue can be NULL to permit testing existence. */ char_t *websGetVar(webs_t wp, char_t *var, char_t *defaultGetValue) @@ -956,18 +1203,34 @@ char_t *websGetVar(webs_t wp, char_t *var, char_t *defaultGetValue) return defaultGetValue; } +/******************************************************************************/ +/* + * Return TRUE if a webs variable is set to a given value + */ + +int websCompareVar(webs_t wp, char_t *var, char_t *value) +{ + a_assert(websValid(wp)); + a_assert(var && *var); + + if (gstrcmp(value, websGetVar(wp, var, T(" __UNDEF__ "))) == 0) { + return 1; + } + return 0; +} + /******************************************************************************/ /* * Cancel the request timeout. Note may be called multiple times. */ -static void websTimeoutCancel(webs_t wp) +void websTimeoutCancel(webs_t wp) { a_assert(websValid(wp)); - if (wp->timeout) { - emfDeleteTimer(wp->timeout); - wp->timeout = NULL; + if (wp->timeout >= 0) { + emfUnschedCallback(wp->timeout); + wp->timeout = -1; } } @@ -993,24 +1256,53 @@ void websResponse(webs_t wp, int code, char_t *message, char_t *redirect) */ if ( !(wp->flags & WEBS_HEADER_DONE)) { wp->flags |= WEBS_HEADER_DONE; - websWrite(wp, T("HTTP/1.0 %d %s\r\n"), code, websErrorMsg(code)); + websWrite(wp, T("HTTP/1.1 %d %s\r\n"), code, websErrorMsg(code)); +/* + * By license terms the following line of code must not be modified. + */ + websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); - /* by license terms the following line of code must - * not be modified. - */ - websWrite(wp, T("Server: GoAhead-Webs\r\n")); +/* + * Timestamp/Date is usually the next to go + */ + if ((date = websGetDateString(NULL)) != NULL) { + websWrite(wp, T("Date: %s\r\n"), date); + bfree(B_L, date); + } +/* + * If authentication is required, send the auth header info + */ + if (code == 401) { + if (!(wp->flags & WEBS_AUTH_DIGEST)) { + websWrite(wp, T("WWW-Authenticate: Basic realm=\"%s\"\r\n"), + websGetRealm()); +#ifdef DIGEST_ACCESS_SUPPORT + } else { + char_t *nonce, *opaque; + + nonce = websCalcNonce(wp), + opaque = websCalcOpaque(wp), + websWrite(wp, + T("WWW-Authenticate: Digest realm=\"%s\", domain=\"%s\",") + T("qop=\"%s\", nonce=\"%s\", opaque=\"%s\",") + T("algorithm=\"%s\", stale=\"%s\"\r\n"), + websGetRealm(), + websGetHostUrl(), + T("auth"), + nonce, + opaque, T("MD5"), T("FALSE")); + bfree(B_L, nonce); + bfree(B_L, opaque); +#endif + } + } if (wp->flags & WEBS_KEEP_ALIVE) { websWrite(wp, T("Connection: keep-alive\r\n")); } - websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n")); - if ((date = websGetDateString(NULL)) != NULL) { - websWrite(wp, T("Date: %s\r\n"), date); - bfree(B_L, date); - } + websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n")); websWrite(wp, T("Content-Type: text/html\r\n")); - /* * We don't do a string length here as the message may be multi-line. * Ie. will count as only one and we will have a content-length @@ -1024,7 +1316,10 @@ void websResponse(webs_t wp, int code, char_t *message, char_t *redirect) websWrite(wp, T("\r\n")); } - if (message && *message) { +/* + * If the browser didn't do a HEAD only request, send the message as well. + */ + if ((wp->flags & WEBS_HEAD_REQUEST) == 0 && message && *message) { websWrite(wp, T("%s\r\n"), message); } websDone(wp, code); @@ -1037,7 +1332,7 @@ void websResponse(webs_t wp, int code, char_t *message, char_t *redirect) void websRedirect(webs_t wp, char_t *url) { - char_t *msgbuf, *urlbuf; + char_t *msgbuf, *urlbuf, *redirectFmt; a_assert(websValid(wp)); a_assert(url); @@ -1052,7 +1347,16 @@ void websRedirect(webs_t wp, char_t *url) if (*url == '/') { url++; } - gsnprintf(&urlbuf, WEBS_MAX_URL + 80, T("http://%s/%s"), + + redirectFmt = T("http://%s/%s"); + +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + redirectFmt = T("https://%s/%s"); + } +#endif + + fmtAlloc(&urlbuf, WEBS_MAX_URL + 80, redirectFmt, websGetVar(wp, T("HTTP_HOST"), websHostUrl), url); url = urlbuf; } @@ -1060,7 +1364,7 @@ void websRedirect(webs_t wp, char_t *url) /* * Add human readable message for completeness. Should not be required. */ - gsnprintf(&msgbuf, WEBS_MAX_URL + 80, + fmtAlloc(&msgbuf, WEBS_MAX_URL + 80, T("\r\n\ This document has moved to a new location.\r\n\ Please update your documents to reflect the new location.\r\n\ @@ -1089,7 +1393,7 @@ void websError(webs_t wp, int code, char_t *fmt, ...) va_start(args, fmt); userMsg = NULL; - gvsnprintf(&userMsg, WEBS_BUFSIZE, fmt, args); + fmtValloc(&userMsg, WEBS_BUFSIZE, fmt, args); va_end(args); msg = T("Document Error: %s\r\n\ @@ -1099,7 +1403,7 @@ void websError(webs_t wp, int code, char_t *fmt, ...) * Ensure we have plenty of room */ buf = NULL; - gsnprintf(&buf, WEBS_BUFSIZE, msg, websErrorMsg(code), + fmtAlloc(&buf, WEBS_BUFSIZE, msg, websErrorMsg(code), websErrorMsg(code), wp->url, userMsg); websResponse(wp, code, buf, NULL); @@ -1114,7 +1418,7 @@ void websError(webs_t wp, int code, char_t *fmt, ...) static char_t *websErrorMsg(int code) { - websErrorType* ep; + websErrorType *ep; for (ep = websErrors; ep->code; ep++) { if (code == ep->code) { @@ -1131,7 +1435,7 @@ static char_t *websErrorMsg(int code) * write procedure. */ -int websWrite(webs_t wp, char_t* fmt, ...) +int websWrite(webs_t wp, char_t *fmt, ...) { va_list vargs; char_t *buf; @@ -1143,8 +1447,8 @@ int websWrite(webs_t wp, char_t* fmt, ...) buf = NULL; rc = 0; - if (gvsnprintf(&buf, WEBS_BUFSIZE, fmt, vargs) >= WEBS_BUFSIZE) { - goahead_trace(0, T("webs: websWrite lost data, buffer overflow\n")); + if (fmtValloc(&buf, WEBS_BUFSIZE, fmt, vargs) >= WEBS_BUFSIZE) { + trace(0, T("webs: websWrite lost data, buffer overflow\n")); } va_end(vargs); a_assert(buf); @@ -1160,61 +1464,94 @@ int websWrite(webs_t wp, char_t* fmt, ...) * Write a block of data of length "nChars" to the user's browser. Public * write block procedure. If unicode is turned on this function expects * buf to be a unicode string and it converts it to ASCII before writing. - * See websWriteBlockData to always write binary or ASCII data with no + * See websWriteDataNonBlock to always write binary or ASCII data with no * unicode conversion. This returns the number of char_t's processed. + * It spins until nChars are flushed to the socket. For non-blocking + * behavior, use websWriteDataNonBlock. */ int websWriteBlock(webs_t wp, char_t *buf, int nChars) { -#if ! UNICODE - return websWriteBlockData(wp, buf, nChars); -#else - int r; - char *charBuf; + int len, done; + char *asciiBuf, *pBuf; + a_assert(wp); + a_assert(websValid(wp)); a_assert(buf); a_assert(nChars >= 0); - if ((charBuf = ballocUniToAsc(buf, nChars)) == NULL) { - return -1; + done = len = 0; + +/* + * ballocUniToAsc will convert Unicode to strings to Ascii. If Unicode is + * not turned on then ballocUniToAsc will not do the conversion. + */ + pBuf = asciiBuf = ballocUniToAsc(buf, nChars); + + while (nChars > 0) { +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + if ((len = websSSLWrite(wp->wsp, pBuf, nChars)) < 0) { + bfree(B_L, asciiBuf); + return -1; + } + websSSLFlush(wp->wsp); + } else { + if ((len = socketWrite(wp->sid, pBuf, nChars)) < 0) { + bfree(B_L, asciiBuf); + return -1; + } + socketFlush(wp->sid); + } +#else /* ! WEBS_SSL_SUPPORT */ + if ((len = socketWrite(wp->sid, pBuf, nChars)) < 0) { + bfree(B_L, asciiBuf); + return -1; + } + socketFlush(wp->sid); +#endif /* WEBS_SSL_SUPPORT */ + nChars -= len; + pBuf += len; + done += len; } - r = websWriteBlockData(wp, charBuf, nChars); - bfree(B_L, charBuf); - return r; -#endif + + bfree(B_L, asciiBuf); + return done; } /******************************************************************************/ /* * Write a block of data of length "nChars" to the user's browser. Same as * websWriteBlock except that it expects straight ASCII or binary and does no - * unicode conversion before writing the data. - * This returns the number of chars processed. + * unicode conversion before writing the data. If the socket cannot hold all + * the data, it will return the number of bytes flushed to the socket before + * it would have blocked. This returns the number of chars processed or -1 + * if socketWrite fails. */ -int websWriteBlockData(webs_t wp, char *buf, int nChars) +int websWriteDataNonBlock(webs_t wp, char *buf, int nChars) { - int len, done; + int r; a_assert(wp); a_assert(websValid(wp)); a_assert(buf); a_assert(nChars >= 0); - done = len = 0; - while (nChars > 0) { - if ((len = socketWrite(wp->sid, buf, nChars)) < 0) { - return -1; - } -/* - * Block in flush if the last write could not take any more data - */ - socketFlush(wp->sid, len == 0); - nChars -= len; - buf += len; - done += len; +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + r = websSSLWrite(wp->wsp, buf, nChars); + websSSLFlush(wp->wsp); + } else { + r = socketWrite(wp->sid, buf, nChars); + socketFlush(wp->sid); } - return done; +#else + r = socketWrite(wp->sid, buf, nChars); + socketFlush(wp->sid); +#endif + + return r; } /******************************************************************************/ @@ -1229,7 +1566,6 @@ void websDecodeUrl(char_t *decoded, char_t *token, int len) a_assert(decoded); a_assert(token); - num = 0; op = decoded; for (ip = token; *ip && len > 0; ip++, op++) { @@ -1241,7 +1577,7 @@ void websDecodeUrl(char_t *decoded, char_t *token, int len) * Convert %nn to a single character */ ip++; - for (i = 0; i < 2; i++, ip++) { + for (i = 0, num = 0; i < 2; i++, ip++) { c = tolower(*ip); if (c >= 'a' && c <= 'f') { num = (num * 16) + 10 + c - 'a'; @@ -1275,7 +1611,7 @@ static void websLog(webs_t wp, int code) a_assert(websValid(wp)); buf = NULL; - gsnprintf(&buf, WEBS_MAX_URL + 80, T("%d %s %d %d\n"), time(0), + fmtAlloc(&buf, WEBS_MAX_URL + 80, T("%d %s %d %d\n"), time(0), wp->url, code, wp->written); len = gstrlen(buf); abuf = ballocUniToAsc(buf, len+1); @@ -1293,24 +1629,29 @@ static void websLog(webs_t wp, int code) * the browser, simply re-issue the timeout. */ -static void websTimeout(long iwp) +void websTimeout(void *arg, int id) { webs_t wp; int delay, tm; - wp = (webs_t) iwp; + wp = (webs_t) arg; a_assert(websValid(wp)); tm = websGetTimeSinceMark(wp) * 1000; if (tm >= WEBS_TIMEOUT) { websStats.timeouts++; - wp->timeout = NULL; + emfUnschedCallback(id); + +/* + * Clear the timeout id + */ + wp->timeout = -1; websDone(wp, 404); } else { delay = WEBS_TIMEOUT - tm; a_assert(delay > 0); - wp->timeout = emfCreateTimer(delay, websTimeout, (long) wp); + emfReschedCallback(id, delay); } } @@ -1349,12 +1690,25 @@ void websDone(webs_t wp, int code) */ websPageClose(wp); +/* + * Exit if secure. + */ +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + websTimeoutCancel(wp); + websSSLFlush(wp->wsp); + socketCloseConnection(wp->sid); + websFree(wp); + return; + } +#endif + /* * If using Keep Alive (HTTP/1.1) we keep the socket open for a period * while waiting for another request on the socket. */ if (wp->flags & WEBS_KEEP_ALIVE) { - if (socketFlush(wp->sid, 0) == 0) { + if (socketFlush(wp->sid) == 0) { wp->state = WEBS_BEGIN; wp->flags |= WEBS_REQUEST_DONE; if (wp->header.buf) { @@ -1363,11 +1717,14 @@ void websDone(webs_t wp, int code) socketCreateHandler(wp->sid, SOCKET_READABLE, websSocketEvent, (int) wp); websTimeoutCancel(wp); - wp->timeout = emfCreateTimer(WEBS_TIMEOUT, websTimeout, (long) wp); + wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout, + (void *) wp); return; } } else { websTimeoutCancel(wp); + socketSetBlock(wp->sid, 1); + socketFlush(wp->sid); socketCloseConnection(wp->sid); } websFree(wp); @@ -1378,7 +1735,7 @@ void websDone(webs_t wp, int code) * Allocate a new webs structure */ -static int websAlloc(int sid) +int websAlloc(int sid) { webs_t wp; int wid; @@ -1396,7 +1753,26 @@ static int websAlloc(int sid) wp->sid = sid; wp->state = WEBS_BEGIN; wp->docfd = -1; + wp->timeout = -1; wp->dir = NULL; + wp->authType = NULL; + wp->protocol = NULL; + wp->protoVersion = NULL; + wp->password = NULL; + wp->userName = NULL; +#ifdef DIGEST_ACCESS_SUPPORT + wp->realm = NULL; + wp->nonce = NULL; + wp->digest = NULL; + wp->uri = NULL; + wp->opaque = NULL; + wp->nc = NULL; + wp->cnonce = NULL; + wp->qop = NULL; +#endif +#ifdef WEBS_SSL_SUPPORT + wp->wsp = NULL; +#endif ringqOpen(&wp->header, WEBS_HEADER_BUFINC, WEBS_MAX_HEADER); @@ -1405,7 +1781,7 @@ static int websAlloc(int sid) * both the CGI variables and for the global functions. The function table * is common to all webs instances (ie. all browsers) */ - wp->cgiVars = symOpen(64); + wp->cgiVars = symOpen(WEBS_SYM_INIT); return wid; } @@ -1415,7 +1791,7 @@ static int websAlloc(int sid) * Free a webs structure */ -static void websFree(webs_t wp) +void websFree(webs_t wp) { a_assert(websValid(wp)); @@ -1431,6 +1807,8 @@ static void websFree(webs_t wp) bfree(B_L, wp->query); if (wp->decodedQuery) bfree(B_L, wp->decodedQuery); + if (wp->authType) + bfree(B_L, wp->authType); if (wp->password) bfree(B_L, wp->password); if (wp->userName) @@ -1441,8 +1819,36 @@ static void websFree(webs_t wp) bfree(B_L, wp->userAgent); if (wp->dir) bfree(B_L, wp->dir); - - symClose(wp->cgiVars, websFreeVar); + if (wp->protocol) + bfree(B_L, wp->protocol); + if (wp->protoVersion) + bfree(B_L, wp->protoVersion); + if (wp->cgiStdin) + bfree(B_L, wp->cgiStdin); + + +#ifdef DIGEST_ACCESS_SUPPORT + if (wp->realm) + bfree(B_L, wp->realm); + if (wp->uri) + bfree(B_L, wp->uri); + if (wp->digest) + bfree(B_L, wp->digest); + if (wp->opaque) + bfree(B_L, wp->opaque); + if (wp->nonce) + bfree(B_L, wp->nonce); + if (wp->nc) + bfree(B_L, wp->nc); + if (wp->cnonce) + bfree(B_L, wp->cnonce); + if (wp->qop) + bfree(B_L, wp->qop); +#endif +#ifdef WEBS_SSL_SUPPORT + websSSLFree(wp->wsp); +#endif + symClose(wp->cgiVars); if (wp->header.buf) { ringqClose(&wp->header); @@ -1455,22 +1861,22 @@ static void websFree(webs_t wp) /******************************************************************************/ /* - * Callback from symClose. Free the variable. + * Return the server address */ -static void websFreeVar(sym_t* sp) +char_t *websGetHost() { - valueFree(&sp->content); + return websHost; } /******************************************************************************/ /* - * Return the server address + * Return the the url to access the server. (ip address) */ -char_t* websGetHost() +char_t *websGetIpaddrUrl() { - return websHost; + return websIpaddrUrl; } /******************************************************************************/ @@ -1478,7 +1884,7 @@ char_t* websGetHost() * Return the server address */ -char_t* websGetHostUrl() +char_t *websGetHostUrl() { return websHostUrl; } @@ -1582,7 +1988,7 @@ char_t *websGetRequestPath(webs_t wp) * Return the password */ -char_t* websGetRequestPassword(webs_t wp) +char_t *websGetRequestPassword(webs_t wp) { a_assert(websValid(wp)); @@ -1594,7 +2000,7 @@ char_t* websGetRequestPassword(webs_t wp) * Return the request type */ -char_t* websGetRequestType(webs_t wp) +char_t *websGetRequestType(webs_t wp) { a_assert(websValid(wp)); @@ -1606,7 +2012,7 @@ char_t* websGetRequestType(webs_t wp) * Return the username */ -char_t* websGetRequestUserName(webs_t wp) +char_t *websGetRequestUserName(webs_t wp) { a_assert(websValid(wp)); @@ -1772,35 +2178,15 @@ int websValid(webs_t wp) return 0; } -/******************************************************************************/ -/* - * Close the document handle. - */ - -int websCloseFileHandle(webs_t wp) -{ - a_assert(websValid(wp)); - -#ifndef WEBS_PAGE_ROM - if (wp->docfd >= 0) { - close(wp->docfd); - wp->docfd = -1; - } -#endif - - return 0; -} - /******************************************************************************/ /* * Build an ASCII time string. If sbuf is NULL we use the current time, * else we use the last modified time of sbuf; */ -char_t* websGetDateString(websStatType* sbuf) +char_t *websGetDateString(websStatType *sbuf) { - char_t* cp; - char_t* r; + char_t* cp, *r; time_t now; if (sbuf == NULL) { @@ -1823,7 +2209,7 @@ char_t* websGetDateString(websStatType* sbuf) * "real" time, but rather a relative marker. */ -static void websMarkTime(webs_t wp) +void websMarkTime(webs_t wp) { wp->timestamp = time(0); } @@ -1838,4 +2224,615 @@ static int websGetTimeSinceMark(webs_t wp) return time(0) - wp->timestamp; } +/******************************************************************************/ +/* + * Store the new realm name + */ + +void websSetRealm(char_t *realmName) +{ + a_assert(realmName); + + gstrncpy(websRealm, realmName, TSZ(websRealm)); +} + +/******************************************************************************/ +/* + * Return the realm name (used for authorization) + */ + +char_t *websGetRealm() +{ + return websRealm; +} + + +#if WEBS_IF_MODIFIED_SUPPORT +/******************************************************************************/ +/* + * These functions are intended to closely mirror the syntax for HTTP-date + * from RFC 2616 (HTTP/1.1 spec). This code was submitted by Pete Bergstrom. + */ + +/* + * RFC1123Date = wkday "," SP date1 SP time SP "GMT" + * RFC850Date = weekday "," SP date2 SP time SP "GMT" + * ASCTimeDate = wkday SP date3 SP time SP 4DIGIT + * + * Each of these functions tries to parse the value and update the index to + * the point it leaves off parsing. + */ + +typedef enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC } MonthEnumeration; +typedef enum { SUN, MON, TUE, WED, THU, FRI, SAT } WeekdayEnumeration; + +/******************************************************************************/ +/* + * Parse an N-digit value + */ + +static int parseNDIGIT(char_t *buf, int digits, int *index) +{ + int tmpIndex, returnValue; + + returnValue = 0; + + for (tmpIndex = *index; tmpIndex < *index+digits; tmpIndex++) { + if (gisdigit(buf[tmpIndex])) { + returnValue = returnValue * 10 + (buf[tmpIndex] - T('0')); + } + } + *index = tmpIndex; + + return returnValue; +} + +/******************************************************************************/ +/* + * Return an index into the month array + */ + +static int parseMonth(char_t *buf, int *index) +{ +/* + * "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | + * "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec" + */ + int tmpIndex, returnValue; + + returnValue = -1; + tmpIndex = *index; + + switch (buf[tmpIndex]) { + case 'A': + switch (buf[tmpIndex+1]) { + case 'p': + returnValue = APR; + break; + case 'u': + returnValue = AUG; + break; + } + break; + case 'D': + returnValue = DEC; + break; + case 'F': + returnValue = FEB; + break; + case 'J': + switch (buf[tmpIndex+1]) { + case 'a': + returnValue = JAN; + break; + case 'u': + switch (buf[tmpIndex+2]) { + case 'l': + returnValue = JUL; + break; + case 'n': + returnValue = JUN; + break; + } + break; + } + break; + case 'M': + switch (buf[tmpIndex+1]) { + case 'a': + switch (buf[tmpIndex+2]) { + case 'r': + returnValue = MAR; + break; + case 'y': + returnValue = MAY; + break; + } + break; + } + break; + case 'N': + returnValue = NOV; + break; + case 'O': + returnValue = OCT; + break; + case 'S': + returnValue = SEP; + break; + } + + if (returnValue >= 0) { + *index += 3; + } + + return returnValue; +} + +/******************************************************************************/ +/* + * Parse a year value (either 2 or 4 digits) + */ + +static int parseYear(char_t *buf, int *index) +{ + int tmpIndex, returnValue; + + tmpIndex = *index; + returnValue = parseNDIGIT(buf, 4, &tmpIndex); + + if (returnValue >= 0) { + *index = tmpIndex; + } else { + returnValue = parseNDIGIT(buf, 2, &tmpIndex); + if (returnValue >= 0) { +/* + * Assume that any year earlier than the start of the + * epoch for time_t (1970) specifies 20xx + */ + if (returnValue < 70) { + returnValue += 2000; + } else { + returnValue += 1900; + } + + *index = tmpIndex; + } + } + + return returnValue; +} + +/******************************************************************************/ +/* + * The formulas used to build these functions are from "Calendrical Calculations", + * by Nachum Dershowitz, Edward M. Reingold, Cambridge University Press, 1997. + */ + +#include + +const int GregorianEpoch = 1; + +/******************************************************************************/ +/* + * Determine if year is a leap year + */ + +int GregorianLeapYearP(long year) +{ + int result; + long tmp; + + tmp = year % 400; + + if ((year % 4 == 0) && + (tmp != 100) && + (tmp != 200) && + (tmp != 300)) { + result = TRUE; + } else { + result = FALSE; + } + + return result; +} + +/******************************************************************************/ +/* + * Return the fixed date from the gregorian date + */ + +long FixedFromGregorian(long month, long day, long year) +{ + long fixedDate; + + fixedDate = (long)(GregorianEpoch - 1 + 365 * (year - 1) + + floor((year - 1) / 4.0) - + floor((double)(year - 1) / 100.0) + + floor((double)(year - 1) / 400.0) + + floor((367.0 * ((double)month) - 362.0) / 12.0)); + + if (month <= 2) { + fixedDate += 0; + } else if (TRUE == GregorianLeapYearP(year)) { + fixedDate += -1; + } else { + fixedDate += -2; + } + + fixedDate += day; + + return fixedDate; +} + +/******************************************************************************/ +/* + * Return the gregorian year from a fixed date + */ + +long GregorianYearFromFixed(long fixedDate) +{ + long result, d0, n400, d1, n100, d2, n4, d3, n1, d4, year; + + d0 = fixedDate - GregorianEpoch; + n400 = (long)(floor((double)d0 / (double)146097)); + d1 = d0 % 146097; + n100 = (long)(floor((double)d1 / (double)36524)); + d2 = d1 % 36524; + n4 = (long)(floor((double)d2 / (double)1461)); + d3 = d2 % 1461; + n1 = (long)(floor((double)d3 / (double)365)); + d4 = (d3 % 365) + 1; + year = 400 * n400 + 100 * n100 + 4 * n4 + n1; + + if ((n100 == 4) || (n1 == 4)) { + result = year; + } else { + result = year + 1; + } + + return result; +} + +/******************************************************************************/ +/* + * Returns the Gregorian date from a fixed date + * (not needed for this use, but included for completeness + */ + +#if 0 +GregorianFromFixed(long fixedDate, long *month, long *day, long *year) +{ + long priorDays, correction; + + *year = GregorianYearFromFixed(fixedDate); + priorDays = fixedDate - FixedFromGregorian(1, 1, *year); + + if (fixedDate < FixedFromGregorian(3,1,*year)) { + correction = 0; + } else if (true == GregorianLeapYearP(*year)) { + correction = 1; + } else { + correction = 2; + } + + *month = (long)(floor((12.0 * (double)(priorDays + correction) + 373.0) / 367.0)); + *day = fixedDate - FixedFromGregorian(*month, 1, *year); +} +#endif + +/******************************************************************************/ +/* + * Returns the difference between two Gregorian dates + */ + +long GregorianDateDifference( long month1, long day1, long year1, + long month2, long day2, long year2) +{ + return FixedFromGregorian(month2, day2, year2) - + FixedFromGregorian(month1, day1, year1); +} + + +/******************************************************************************/ +/* + * Return the number of seconds into the current day + */ + +#define SECONDS_PER_DAY 24*60*60 + +static int parseTime(char_t *buf, int *index) +{ +/* + * Format of buf is - 2DIGIT ":" 2DIGIT ":" 2DIGIT + */ + int returnValue, tmpIndex, hourValue, minuteValue, secondValue; + + hourValue = minuteValue = secondValue = -1; + returnValue = -1; + tmpIndex = *index; + + hourValue = parseNDIGIT(buf, 2, &tmpIndex); + + if (hourValue >= 0) { + tmpIndex++; + minuteValue = parseNDIGIT(buf, 2, &tmpIndex); + if (minuteValue >= 0) { + tmpIndex++; + secondValue = parseNDIGIT(buf, 2, &tmpIndex); + } + } + + if ((hourValue >= 0) && + (minuteValue >= 0) && + (secondValue >= 0)) { + returnValue = (((hourValue * 60) + minuteValue) * 60) + secondValue; + *index = tmpIndex; + } + + return returnValue; +} + +/******************************************************************************/ +/* + * Return the equivalent of time() given a gregorian date + */ + +static time_t dateToTimet(int year, int month, int day) +{ + long dayDifference; + + dayDifference = FixedFromGregorian(month, day, year) - + FixedFromGregorian(1, 1, 1970); + + return dayDifference * SECONDS_PER_DAY; +} + +/******************************************************************************/ +/* + * Return the number of seconds between Jan 1, 1970 and the parsed date + * (corresponds to documentation for time() function) + */ + +static time_t parseDate1or2(char_t *buf, int *index) +{ +/* + * Format of buf is either + * 2DIGIT SP month SP 4DIGIT + * or + * 2DIGIT "-" month "-" 2DIGIT + */ + int dayValue, monthValue, yearValue, tmpIndex; + time_t returnValue; + + returnValue = (time_t) -1; + tmpIndex = *index; + + dayValue = monthValue = yearValue = -1; + + if (buf[tmpIndex] == T(',')) { +/* + * Skip over the ", " + */ + tmpIndex += 2; + + dayValue = parseNDIGIT(buf, 2, &tmpIndex); + if (dayValue >= 0) { +/* + * Skip over the space or hyphen + */ + tmpIndex++; + monthValue = parseMonth(buf, &tmpIndex); + if (monthValue >= 0) { +/* + * Skip over the space or hyphen + */ + tmpIndex++; + yearValue = parseYear(buf, &tmpIndex); + } + } + + if ((dayValue >= 0) && + (monthValue >= 0) && + (yearValue >= 0)) { + if (yearValue < 1970) { +/* + * Allow for Microsoft IE's year 1601 dates + */ + returnValue = 0; + } else { + returnValue = dateToTimet(yearValue, monthValue, dayValue); + } + *index = tmpIndex; + } + } + + return returnValue; +} + +/******************************************************************************/ +/* + * Return the number of seconds between Jan 1, 1970 and the parsed date + */ + +static time_t parseDate3Time(char_t *buf, int *index) +{ +/* + * Format of buf is month SP ( 2DIGIT | ( SP 1DIGIT )) + */ + int dayValue, monthValue, yearValue, timeValue, tmpIndex; + time_t returnValue; + + returnValue = (time_t) -1; + tmpIndex = *index; + + dayValue = monthValue = yearValue = timeValue = -1; + + monthValue = parseMonth(buf, &tmpIndex); + if (monthValue >= 0) { +/* + * Skip over the space + */ + tmpIndex++; + if (buf[tmpIndex] == T(' ')) { +/* + * Skip over this space too + */ + tmpIndex++; + dayValue = parseNDIGIT(buf, 1, &tmpIndex); + } else { + dayValue = parseNDIGIT(buf, 2, &tmpIndex); + } +/* + * Now get the time and time SP 4DIGIT + */ + timeValue = parseTime(buf, &tmpIndex); + if (timeValue >= 0) { +/* + * Now grab the 4DIGIT year value + */ + yearValue = parseYear(buf, &tmpIndex); + } + } + + if ((dayValue >= 0) && + (monthValue >= 0) && + (yearValue >= 0)) { + returnValue = dateToTimet(yearValue, monthValue, dayValue); + returnValue += timeValue; + *index = tmpIndex; + } + + return returnValue; +} + + +/******************************************************************************/ +/* + * Although this looks like a trivial function, I found I was replicating the implementation + * seven times in the parseWeekday function. In the interests of minimizing code size + * and redundancy, it is broken out into a separate function. The cost of an extra + * function call I can live with given that it should only be called once per HTTP request. + */ + +static int bufferIndexIncrementGivenNTest(char_t *buf, int testIndex, char_t testChar, + int foundIncrement, int notfoundIncrement) +{ + if (buf[testIndex] == testChar) { + return foundIncrement; + } + + return notfoundIncrement; +} + +/******************************************************************************/ +/* + * Return an index into a logical weekday array + */ + +static int parseWeekday(char_t *buf, int *index) +{ +/* + * Format of buf is either + * "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun" + * or + * "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday" + */ + int tmpIndex, returnValue; + + returnValue = -1; + tmpIndex = *index; + + switch (buf[tmpIndex]) { + case 'F': + returnValue = FRI; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'd', sizeof("Friday"), 3); + break; + case 'M': + returnValue = MON; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'd', sizeof("Monday"), 3); + break; + case 'S': + switch (buf[tmpIndex+1]) { + case 'a': + returnValue = SAT; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'u', sizeof("Saturday"), 3); + break; + case 'u': + returnValue = SUN; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'd', sizeof("Sunday"), 3); + break; + } + break; + case 'T': + switch (buf[tmpIndex+1]) { + case 'h': + returnValue = THU; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'r', sizeof("Thursday"), 3); + break; + case 'u': + returnValue = TUE; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 's', sizeof("Tuesday"), 3); + break; + } + break; + case 'W': + returnValue = WED; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'n', sizeof("Wednesday"), 3); + break; + } + return returnValue; +} + +/******************************************************************************/ +/* + * Parse the date and time string. + */ + +static time_t dateParse(time_t tip, char_t *cmd) +{ + int index, tmpIndex, weekday, timeValue; + time_t parsedValue, dateValue; + + parsedValue = (time_t) 0; + index = timeValue = 0; + weekday = parseWeekday(cmd, &index); + + if (weekday >= 0) { + tmpIndex = index; + dateValue = parseDate1or2(cmd, &tmpIndex); + if (dateValue >= 0) { + index = tmpIndex + 1; +/* + * One of these two forms is being used + * wkday "," SP date1 SP time SP "GMT" + * weekday "," SP date2 SP time SP "GMT" + */ + timeValue = parseTime(cmd, &index); + if (timeValue >= 0) { +/* + * Now match up that "GMT" string for completeness + * Compute the final value if there were no problems in the parse + */ + if ((weekday >= 0) && + (dateValue >= 0) && + (timeValue >= 0)) { + parsedValue = dateValue + timeValue; + } + } + } else { +/* + * Try the other form - wkday SP date3 SP time SP 4DIGIT + */ + tmpIndex = index; + parsedValue = parseDate3Time(cmd, &tmpIndex); + } + } + + return parsedValue; +} + +#endif /* WEBS_IF_MODIFIED_SUPPORT */ + + /******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/webs.h b/c/src/libnetworking/rtems_webserver/webs.h index 46e11bc2fc..4b847502a5 100644 --- a/c/src/libnetworking/rtems_webserver/webs.h +++ b/c/src/libnetworking/rtems_webserver/webs.h @@ -1,7 +1,7 @@ /* - * webs.h -- Go Ahead Web public header + * webs.h -- GoAhead Web public header * - * Copyright (c) Go Ahead Software Inc., 1992-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved. * * See the file "license.txt" for information on usage and redistribution */ @@ -12,7 +12,7 @@ /******************************** Description *********************************/ /* - * Go Ahead Web Server header. This defines the Web public APIs. + * GoAhead Web Server header. This defines the Web public APIs. * Include this header for files that contain ASP or Form procedures. * Include wsIntrn.h when creating URL handlers. */ @@ -20,8 +20,17 @@ /********************************* Includes ***********************************/ #include "ej.h" +#ifdef WEBS_SSL_SUPPORT +#include "websSSL.h" +#endif /********************************** Defines ***********************************/ +/* + * By license terms the server software name defined in the following line of + * code must not be modified. + */ +#define WEBS_NAME T("GoAhead-Webs") +#define WEBS_VERSION T("2.1") #define WEBS_HEADER_BUFINC 512 /* Header buffer size */ #define WEBS_ASP_BUFINC 512 /* Asp expansion increment */ @@ -31,6 +40,9 @@ #define WEBS_MAX_URL 4096 /* Maximum URL size for sanity */ #define WEBS_SOCKET_BUFSIZ 256 /* Bytes read from socket */ +#define WEBS_HTTP_PORT T("httpPort") +#define CGI_BIN T("cgi-bin") + /* * Request flags. Also returned by websGetRequestFlags(). */ @@ -48,6 +60,10 @@ #define WEBS_FORM 0x800 /* Request is a form */ #define WEBS_REQUEST_DONE 0x1000 /* Request complete */ #define WEBS_POST_DATA 0x2000 /* Already appended post data */ +#define WEBS_CGI_REQUEST 0x4000 /* cgi-bin request */ +#define WEBS_SECURE 0x8000 /* connection uses SSL */ +#define WEBS_AUTH_BASIC 0x10000 /* Basic authentication request */ +#define WEBS_AUTH_DIGEST 0x20000 /* Digest authentication request */ #define WEBS_HEADER_DONE 0x40000 /* Already output the HTTP header */ /* @@ -65,38 +81,56 @@ typedef struct websRec { sym_fd_t cgiVars; /* CGI standard variables */ sym_fd_t cgiQuery; /* CGI decoded query string */ time_t timestamp; /* Last transaction with browser */ - void* timeout; /* Timeout handle */ + int timeout; /* Timeout handle */ char_t ipaddr[32]; /* Connecting ipaddress */ char_t type[64]; /* Mime type */ - char_t* dir; /* Directory containing the page */ - char_t* path; /* Path name without query */ - char_t* url; /* Full request url */ - char_t* host; /* Requested host */ - char_t* lpath; /* Cache local path name */ - char_t* query; /* Request query */ - char_t* decodedQuery; /* Decoded request query */ - char_t* password; /* Authorization password */ - char_t* userName; /* Authorization username */ - char_t* cookie; /* Cookie string */ - char_t* userAgent; /* User agent (browser) */ + char_t *dir; /* Directory containing the page */ + char_t *path; /* Path name without query */ + char_t *url; /* Full request url */ + char_t *host; /* Requested host */ + char_t *lpath; /* Cache local path name */ + char_t *query; /* Request query */ + char_t *decodedQuery; /* Decoded request query */ + char_t *authType; /* Authorization type (Basic/DAA) */ + char_t *password; /* Authorization password */ + char_t *userName; /* Authorization username */ + char_t *cookie; /* Cookie string */ + char_t *userAgent; /* User agent (browser) */ + char_t *protocol; /* Protocol (normally HTTP) */ + char_t *protoVersion; /* Protocol version */ int sid; /* Socket id (handler) */ + int listenSid; /* Listen Socket id */ int port; /* Request port number */ int state; /* Current state */ int flags; /* Current flags -- see above */ int code; /* Request result code */ int clen; /* Content length */ int wid; /* Index into webs */ + char_t *cgiStdin; /* filename for CGI stdin */ int docfd; /* Document file descriptor */ - int numbytes; /* Bytes to transfer to browser */ + int numbytes; /* Bytes to transfer to browser */ int written; /* Bytes actually transferred */ void (*writeSocket)(struct websRec *wp); +#ifdef DIGEST_ACCESS_SUPPORT + char_t *realm; /* usually the same as "host" from websRec */ + char_t *nonce; /* opaque-to-client string sent by server */ + char_t *digest; /* digest form of user password */ + char_t *uri; /* URI found in DAA header */ + char_t *opaque; /* opaque value passed from server */ + char_t *nc; /* nonce count */ + char_t *cnonce; /* check nonce */ + char_t *qop; /* quality operator */ +#endif +#ifdef WEBS_SSL_SUPPORT + websSSL_t *wsp; /* SSL data structure */ +#endif } websRec; typedef websRec *webs_t; typedef websRec websType; /******************************** Prototypes **********************************/ - +extern int websAccept(int sid, char *ipaddr, int port, int listenSid); extern int websAspDefine(char_t *name, int (*fn)(int ejid, webs_t wp, int argc, char_t **argv)); extern int websAspRequest(webs_t wp, char_t *lpath); @@ -112,9 +146,11 @@ extern int websFormDefine(char_t *name, void (*fn)(webs_t wp, extern char_t *websGetDefaultDir(); extern char_t *websGetDefaultPage(); extern char_t *websGetHostUrl(); +extern char_t *websGetIpaddrUrl(); extern char_t *websGetPassword(); extern int websGetPort(); extern char_t *websGetPublishDir(char_t *path, char_t **urlPrefix); +extern char_t *websGetRealm(); extern int websGetRequestBytes(webs_t wp); extern char_t *websGetRequestDir(webs_t wp); extern int websGetRequestFlags(webs_t wp); @@ -125,6 +161,7 @@ extern char_t *websGetRequestPassword(webs_t wp); extern char_t *websGetRequestType(webs_t wp); extern int websGetRequestWritten(webs_t wp); extern char_t *websGetVar(webs_t wp, char_t *var, char_t *def); +extern int websCompareVar(webs_t wp, char_t *var, char_t *value); extern void websHeader(webs_t wp); extern int websOpenListen(int port, int retries); extern int websPageOpen(webs_t wp, char_t *lpath, char_t *path, @@ -142,6 +179,7 @@ extern void websSetEnv(webs_t wp); extern void websSetHost(char_t *host); extern void websSetIpaddr(char_t *ipaddr); extern void websSetPassword(char_t *password); +extern void websSetRealm(char_t *realmName); extern void websSetRequestBytes(webs_t wp, int bytes); extern void websSetRequestFlags(webs_t wp, int flags); extern void websSetRequestLpath(webs_t wp, char_t *lpath); @@ -150,6 +188,7 @@ extern char_t *websGetRequestUserName(webs_t wp); extern void websSetRequestWritten(webs_t wp, int written); extern void websSetVar(webs_t wp, char_t *var, char_t *value); extern int websTestVar(webs_t wp, char_t *var); +extern void websTimeoutCancel(webs_t wp); extern int websUrlHandlerDefine(char_t *urlPrefix, char_t *webDir, int arg, int (*fn)(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, @@ -164,10 +203,18 @@ extern int websUrlParse(char_t *url, char_t **buf, char_t **host, extern char_t *websUrlType(char_t *webs, char_t *buf, int charCnt); extern int websWrite(webs_t wp, char_t* fmt, ...); extern int websWriteBlock(webs_t wp, char_t *buf, int nChars); -extern int websWriteBlockData(webs_t wp, char *buf, int nChars); +extern int websWriteDataNonBlock(webs_t wp, char *buf, int nChars); extern int websValid(webs_t wp); extern int websValidateUrl(webs_t wp, char_t *path); -extern int websCloseFileHandle(webs_t wp); +extern void websMarkTime(webs_t wp); + +/* + * The following prototypes are used by the SSL patch found in websSSL.c + */ +extern int websAlloc(int sid); +extern void websFree(webs_t wp); +extern void websTimeout(void *arg, int id); +extern void websReadEvent(webs_t wp); /* * Prototypes for functions available when running as part of the diff --git a/c/src/libnetworking/rtems_webserver/websuemf.c b/c/src/libnetworking/rtems_webserver/websuemf.c index 48a30f25d0..2315d9d234 100644 --- a/c/src/libnetworking/rtems_webserver/websuemf.c +++ b/c/src/libnetworking/rtems_webserver/websuemf.c @@ -1,7 +1,7 @@ /* * websuemf.c -- GoAhead Micro Embedded Management Framework * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -14,14 +14,32 @@ /*********************************** Includes *********************************/ +#include "ejIntrn.h" #include "wsIntrn.h" +/*********************************** Defines **********************************/ + +/* + * This structure stores scheduled events. + */ +typedef struct { + void (*routine)(void *arg, int id); + void *arg; + time_t at; + int schedid; +} sched_t; + +/*********************************** Locals ***********************************/ + +static sched_t **sched; +static int schedMax; + /************************************* Code ***********************************/ /* * Evaluate a script */ -int scriptEval(int engine, char_t* cmd, char_t** result, int chan) +int scriptEval(int engine, char_t *cmd, char_t **result, int chan) { int ejid; @@ -37,3 +55,156 @@ int scriptEval(int engine, char_t* cmd, char_t** result, int chan) } /******************************************************************************/ +/* + * Compare strings, ignoring case: normal strcmp return codes. + * + * WARNING: It is not good form to increment or decrement pointers inside a + * "call" to tolower et al. These can be MACROS, and have undesired side + * effects. + */ + +int strcmpci(char_t *s1, char_t *s2) +{ + int rc; + + a_assert(s1 && s2); + if (s1 == NULL || s2 == NULL) { + return 0; + } + + if (s1 == s2) { + return 0; + } + + do { + rc = gtolower(*s1) - gtolower(*s2); + if (*s1 == '\0') { + break; + } + s1++; + s2++; + } while (rc == 0); + return rc; +} + +/******************************************************************************/ +/* + * This function is called when a scheduled process time has come. + */ + +void TimerProc(int schedid) +{ + sched_t *s; + + a_assert(0 <= schedid && schedid < schedMax); + s = sched[schedid]; + a_assert(s); + + (s->routine)(s->arg, s->schedid); +} + +/******************************************************************************/ +/* + * Schedule an event in delay milliseconds time. We will use 1 second + * granularity for webServer. + */ + +int emfSchedCallback(int delay, emfSchedProc *proc, void *arg) +{ + sched_t *s; + int schedid; + + if ((schedid = hAllocEntry((void***) &sched, &schedMax, + sizeof(sched_t))) < 0) { + return -1; + } + s = sched[schedid]; + s->routine = proc; + s->arg = arg; + s->schedid = schedid; + +/* + * Round the delay up to seconds. + */ + s->at = ((delay + 500) / 1000) + time(0); + + return schedid; +} + +/******************************************************************************/ +/* + * Reschedule to a new delay. + */ + +void emfReschedCallback(int schedid, int delay) +{ + sched_t *s; + + if (sched == NULL || schedid == -1 || schedid >= schedMax || + (s = sched[schedid]) == NULL) { + return; + } + s->at = ((delay + 500) / 1000) + time(0); +} + +/******************************************************************************/ + +void emfUnschedCallback(int schedid) +{ + sched_t *s; + + if (sched == NULL || schedid == -1 || schedid >= schedMax || + (s = sched[schedid]) == NULL) { + return; + } + bfree(B_L, s); + schedMax = hFree((void***) &sched, schedid); +} + +/******************************************************************************/ +/* + * Take the tasks off the queue in a round robin fashion. + */ + +void emfSchedProcess() +{ + sched_t *s; + int schedid; + static int next = 0; + +/* + * If schedMax is 0, there are no tasks scheduled, so just return. + */ + if (schedMax <= 0) { + return; + } + +/* + * If next >= schedMax, the schedule queue was reduced in our absence + * so reset next to 0 to start from the begining of the queue again. + */ + if (next >= schedMax) { + next = 0; + } + + schedid = next; + for (;;) { + if ((s = sched[schedid]) != NULL && (int)s->at <= (int)time(0)) { + TimerProc(schedid); + next = schedid + 1; + return; + } + if (++schedid >= schedMax) { + schedid = 0; + } + if (schedid == next) { +/* + * We've gone all the way through the queue without finding + * anything to do so just return. + */ + return; + } + } +} + +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/wsIntrn.h b/c/src/libnetworking/rtems_webserver/wsIntrn.h index bd77ef8862..8f230d6fbe 100644 --- a/c/src/libnetworking/rtems_webserver/wsIntrn.h +++ b/c/src/libnetworking/rtems_webserver/wsIntrn.h @@ -1,18 +1,18 @@ /* - * wsIntrn.h -- Internal Go Ahead Web server header + * wsIntrn.h -- Internal GoAhead Web server header * - * Copyright (c) Go Ahead Software Inc., 1992-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved. * * See the file "license.txt" for information on usage and redistribution */ - + #ifndef _h_WEBS_INTERNAL #define _h_WEBS_INTERNAL 1 /******************************** Description *********************************/ /* - * Internal Go Ahead Web Server header. This defines the Web private APIs + * Internal GoAhead Web Server header. This defines the Web private APIs * Include this header when you want to create URL handlers. */ @@ -33,7 +33,6 @@ * #define WEBS_PROXY_SUPPORT 1 * * Define this to support reading pages from ROM - * Note: this is not yet fully implemented. * #define WEBS_PAGE_ROM 1 * * Define this to enable memory allocation and stack usage tracking @@ -64,6 +63,13 @@ #include #endif +#if SCOV5 + #include + #include + #include + #include +#endif + #if LYNX #include #include @@ -91,17 +97,24 @@ #include #endif -#if VXW486 +#if VXWORKS #include #include #include #endif +#if SOLARIS + #include + #include + #include +#endif + #if UEMF #include "uemf.h" - #include "ej.h" + #include "ejIntrn.h" #else #include "emf/emfInternal.h" + #include "ej/ejIntrn.h" #endif #include "webs.h" @@ -110,16 +123,18 @@ /* * Read handler flags and state */ -#define WEBS_BEGIN 0x1 /* Beginning state */ -#define WEBS_HEADER 0x2 /* Ready to read first line */ -#define WEBS_POST 0x4 /* POST without content */ -#define WEBS_POST_CLEN 0x8 /* Ready to read content for POST */ -#define WEBS_PROCESSING 0x10 /* Processing request */ -#define WEBS_KEEP_TIMEOUT 15000 /* Keep-alive timeout (15 secs) */ -#define WEBS_TIMEOUT 60000 /* General request timeout (60) */ - -#define PAGE_READ_BUFSIZE 512 /* bytes read from page files */ -#define MAX_PORT_LEN 10 /* max digits in port number */ +#define WEBS_BEGIN 0x1 /* Beginning state */ +#define WEBS_HEADER 0x2 /* Ready to read first line */ +#define WEBS_POST 0x4 /* POST without content */ +#define WEBS_POST_CLEN 0x8 /* Ready to read content for POST */ +#define WEBS_PROCESSING 0x10 /* Processing request */ +#define WEBS_KEEP_TIMEOUT 15000 /* Keep-alive timeout (15 secs) */ +#define WEBS_TIMEOUT 60000 /* General request timeout (60) */ + +#define PAGE_READ_BUFSIZE 512 /* bytes read from page files */ +#define MAX_PORT_LEN 10 /* max digits in port number */ +#define WEBS_SYM_INIT 64 /* initial # of sym table entries */ +#define WEBS_VERSION_STR T("2.1.3") /* version of web server s/w */ /* * URL handler structure. Stores the leading URL path and the handler @@ -150,6 +165,7 @@ typedef struct { long localHits; long remoteHits; long formHits; + long cgiHits; long handlerHits; } websStatsType; @@ -185,7 +201,7 @@ typedef struct { */ typedef struct { char_t *path; /* Web page URL path */ - unsigned char *page; /* Web page data */ + const unsigned char *page; /* Web page data */ int size; /* Size of web page in bytes */ int pos; /* Current read position */ } websRomPageIndexType; @@ -209,6 +225,7 @@ extern int websMax; /* List size */ extern char_t websHost[64]; /* Name of this host */ extern char_t websIpaddr[64]; /* IP address of this host */ extern char_t *websHostUrl; /* URL for this host */ +extern char_t *websIpaddrUrl; /* URL for this host */ extern int websPort; /* Port number */ /******************************** Prototypes **********************************/ @@ -224,12 +241,21 @@ extern int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *query); extern int websFormHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, char_t *query); +extern int websCgiHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, + int arg, char_t *url, char_t *path, char_t *query); +extern void websCgiCleanup(); +extern int websCheckCgiProc(int handle); +extern char_t *websGetCgiCommName(); + +extern int websLaunchCgiProc(char_t *cgiPath, char_t **argp, + char_t **envp, char_t *stdIn, char_t *stdOut); extern int websOpen(int sid); extern void websResponse(webs_t wp, int code, char_t *msg, char_t *redirect); extern int websJavaScriptEval(webs_t wp, char_t *script); extern int websPageReadData(webs_t wp, char *buf, int nBytes); -extern int websPageOpen(webs_t wp, char_t *lpath, char_t *path, int mode, int perm); +extern int websPageOpen(webs_t wp, char_t *lpath, char_t *path, int mode, + int perm); extern void websPageClose(webs_t wp); extern void websPageSeek(webs_t wp, long offset); extern int websPageStat(webs_t wp, char_t *lpath, char_t *path, @@ -253,6 +279,8 @@ extern int websOpenServer(int port, int retries); extern void websCloseServer(); extern char_t* websGetDateString(websStatType* sbuf); +extern int strcmpci(char_t* s1, char_t* s2); + /* * Prototypes for functions available when running as part of the * GoAhead Embedded Management Framework (EMF) @@ -263,6 +291,11 @@ extern void websEmfClose(); extern void websSetEmfEnvironment(webs_t wp); #endif +#if CE +extern int writeUniToAsc(int fid, void *buf, unsigned int len); +extern int readAscToUni(int fid, void **buf, unsigned int len); +#endif + #endif /* _h_WEBS_INTERNAL */ /******************************************************************************/ diff --git a/cpukit/httpd/Makefile.am b/cpukit/httpd/Makefile.am index f14ceaaf67..c85580a0c2 100644 --- a/cpukit/httpd/Makefile.am +++ b/cpukit/httpd/Makefile.am @@ -7,14 +7,15 @@ AUTOMAKE_OPTIONS = foreign 1.4 LIBNAME = lib.a LIB = $(ARCH)/$(LIBNAME) -C_FILES = asp.c balloc.c wbase64.c default.c ejlex.c ejparse.c form.c h.c \ - handler.c mime.c misc.c webpage.c ringq.c rom.c security.c socket.c \ - sym.c uemf.c url.c value.c webrom.c webs.c websuemf.c webmain.c +C_FILES = asp.c balloc.c base64.c default.c ejlex.c ejparse.c emfdb.c \ + form.c h.c handler.c md5c.c mime.c misc.c webpage.c ringq.c rom.c \ + security.c socket.c sym.c uemf.c um.c url.c value.c webrom.c webs.c \ + websuemf.c webmain.c C_O_FILES = $(C_FILES:%.c=$(ARCH)/%.o) OBJS = $(C_O_FILES) -H_FILES = ej.h uemf.h webs.h wsIntrn.h +H_FILES = ej.h ejIntrn.h emfdb.h md5.h uemf.h um.h webs.h wsIntrn.h include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg include $(top_srcdir)/../../../automake/lib.am @@ -52,7 +53,7 @@ endif EXTRA_DIST = asp.c balloc.c default.c ej.h ejlex.c ejparse.c form.c h.c \ handler.c mime.c misc.c ringq.c rom.c rtems_webserver.h security.c \ - socket.c sym.c uemf.c uemf.h url.c value.c wbase64.c webcomp.c webmain.c \ - webpage.c webrom.c webs.c webs.h websuemf.c wsIntrn.h + socket.c sym.c uemf.c uemf.h um.h url.c value.c base64.c webcomp.c \ + webmain.c webpage.c webrom.c webs.c webs.h websuemf.c wsIntrn.h include $(top_srcdir)/../../../automake/local.am diff --git a/cpukit/httpd/NOTES b/cpukit/httpd/NOTES new file mode 100644 index 0000000000..6b0e4c55b0 --- /dev/null +++ b/cpukit/httpd/NOTES @@ -0,0 +1,29 @@ +# +# $Id$ +# + +Notes on merging GoAhead Webs 2.1. Eventually RTEMS should be supported +in their distributions and this directory removed. + +Applied patch from Antti P Miettinen . + +Obtain the original distribution from http://www.goahead.com for +documentation. + +Tailoring +========= +socket.c is RTEMS specific + +Renames +======= + +Distributed as This Directory +============== ================ +base64.c wbase64.c +page.c webpage.c + +RTEMS Specific Additions +======================== +webmain.c +rtems_webserver.h + diff --git a/cpukit/httpd/asp.c b/cpukit/httpd/asp.c index 4611a8f422..9b37523e02 100644 --- a/cpukit/httpd/asp.c +++ b/cpukit/httpd/asp.c @@ -1,7 +1,7 @@ /* * asp.c -- Active Server Page Support * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -20,11 +20,12 @@ /********************************** Locals ************************************/ -static sym_fd_t websAspFunctions = -1; /* Symbol table of functions */ +static sym_fd_t websAspFunctions = -1; /* Symbol table of functions */ +static int aspOpenCount = 0; /* count of apps using this module */ /***************************** Forward Declarations ***************************/ -static char_t *strtokcmp(char_t* s1, char_t* s2); +static char_t *strtokcmp(char_t *s1, char_t *s2); static char_t *skipWhite(char_t *s); /************************************* Code ***********************************/ @@ -34,15 +35,17 @@ static char_t *skipWhite(char_t *s); int websAspOpen() { + if (++aspOpenCount == 1) { /* * Create the table for ASP functions */ - websAspFunctions = symOpen(128); + websAspFunctions = symOpen(WEBS_SYM_INIT * 2); /* * Create standard ASP commands */ - websAspDefine(T("write"), websAspWrite); + websAspDefine(T("write"), websAspWrite); + } return 0; } @@ -53,8 +56,11 @@ int websAspOpen() void websAspClose() { - if (websAspFunctions != -1) { - symClose(websAspFunctions, NULL); + if (--aspOpenCount <= 0) { + if (websAspFunctions != -1) { + symClose(websAspFunctions); + websAspFunctions = -1; + } } } @@ -65,7 +71,7 @@ void websAspClose() * documents, it is better to make them plain HTML files rather than ASPs. */ -int websAspRequest(webs_t wp, char_t* lpath) +int websAspRequest(webs_t wp, char_t *lpath) { websStatType sbuf; char *rbuf; @@ -84,7 +90,7 @@ int websAspRequest(webs_t wp, char_t* lpath) path = websGetRequestPath(wp); /* - * Create Ejscript instance incase it is needed + * Create Ejscript instance in case it is needed */ ejid = ejOpenEngine(wp->cgiVars, websAspFunctions); if (ejid < 0) { @@ -112,12 +118,12 @@ int websAspRequest(webs_t wp, char_t* lpath) websError(wp, 200, T("Cant read %s"), lpath); goto done; } - websCloseFileHandle(wp); + websPageClose(wp); /* * Convert to UNICODE if necessary. */ - if ((buf = ballocAscToUni(rbuf)) == NULL) { + if ((buf = ballocAscToUni(rbuf, len)) == NULL) { websError(wp, 200, T("Can't get memory")); goto done; } @@ -220,7 +226,7 @@ int websAspRequest(webs_t wp, char_t* lpath) */ done: if (websValid(wp)) { - websCloseFileHandle(wp); + websPageClose(wp); if (ejid >= 0) { ejCloseEngine(ejid); } @@ -252,9 +258,9 @@ int websAspWrite(int ejid, webs_t wp, int argc, char_t **argv) int i; a_assert(websValid(wp)); - a_assert(argv); - + for (i = 0; i < argc; ) { + a_assert(argv); if (websWriteBlock(wp, argv[i], gstrlen(argv[i])) < 0) { return -1; } @@ -273,7 +279,7 @@ int websAspWrite(int ejid, webs_t wp, int argc, char_t **argv) * Return a pointer to the location in s1 after s2 ends. */ -static char_t* strtokcmp(char_t* s1, char_t* s2) +static char_t *strtokcmp(char_t *s1, char_t *s2) { int len; @@ -310,4 +316,4 @@ static char_t *skipWhite(char_t *s) return s; } -/******************************************************************************/ \ No newline at end of file +/******************************************************************************/ diff --git a/cpukit/httpd/balloc.c b/cpukit/httpd/balloc.c index f2dae27867..d535a6605c 100644 --- a/cpukit/httpd/balloc.c +++ b/cpukit/httpd/balloc.c @@ -1,7 +1,7 @@ /* * balloc.c -- Block allocation module * - * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -11,7 +11,7 @@ /* * This module implements a very fast block allocation scheme suitable for * ROMed environments. It maintains block class queues for rapid allocation - * and minimal fragmentation. This modules does not coalesce blocks. The + * and minimal fragmentation. This module does not coalesce blocks. The * storage space may be populated statically or via the traditional malloc * mechanisms. Large blocks greater than the maximum class size may be * allocated from the O/S or run-time system via malloc. To permit the use @@ -38,14 +38,6 @@ #if !NO_BALLOC /********************************* Defines ************************************/ -typedef struct { - union { - void *next; /* Pointer to next in q */ - int size; /* Actual requested size */ - } u; - int flags; /* Per block allocation flags */ -} bType; - /* * Define B_STATS if you wish to track memory block and stack usage */ @@ -63,7 +55,9 @@ typedef struct { char_t file[FNAMESIZE]; long allocated; /* Bytes currently allocated */ long count; /* Current block count */ - long allocs; /* Count of alloc attempts */ + long times; /* Count of alloc attempts */ + long largest; /* largest allocated here */ + int q; } bStatsFileType; /* @@ -77,15 +71,26 @@ typedef struct { static bStatsType bStats[B_MAX_CLASS]; /* Per class stats */ static bStatsFileType bStatsFiles[B_MAX_FILES];/* Per file stats */ static bStatsBlkType bStatsBlks[B_MAX_BLOCKS];/* Per block stats */ -static int bStatsBlksMax; /* Max block entry */ -static int bStatsFilesMax; /* Max file entry */ -static int bStatsMemInUse; /* Memory currently in use */ -static int bStatsMemMax; /* Max memory ever used */ +static int bStatsBlksMax = 0; /* Max block entry */ +static int bStatsFilesMax = 0; /* Max file entry */ +static int bStatsMemInUse = 0; /* Memory currently in use */ +static int bStatsBallocInUse = 0; /* Memory currently balloced */ +static int bStatsMemMax = 0; /* Max memory ever used */ +static int bStatsBallocMax = 0; /* Max memory ever balloced */ static void *bStackMin = (void*) -1;/* Miniumum stack position */ static void *bStackStart; /* Starting stack position */ -static int bStatsMemMalloc; /* Malloced memory */ +static int bStatsMemMalloc = 0; /* Malloced memory */ #endif /* B_STATS */ +/* + * ROUNDUP4(size) returns the next higher integer value of size that is + * divisible by 4, or the value of size if size is divisible by 4. + * ROUNDUP4() is used in aligning memory allocations on 4-byte boundaries. + * + * Note: ROUNDUP4() is only required on some operating systems (IRIX). + */ + +#define ROUNDUP4(size) ((size) % 4) ? (size) + (4 - ((size) % 4)) : (size) /********************************** Locals ************************************/ /* @@ -98,6 +103,7 @@ static char *bFreeNext; /* Pointer to next free mem */ static int bFreeSize; /* Size of free memory */ static int bFreeLeft; /* Size of free left for use */ static int bFlags = B_USE_MALLOC; /* Default to auto-malloc */ +static int bopenCount = 0; /* Num tasks using balloc */ /*************************** Forward Declarations *****************************/ @@ -115,9 +121,11 @@ static void bFillBlock(void *buf, int bufsize); #if B_VERIFY_CAUSES_SEVERE_OVERHEAD static void verifyUsedBlock(bType *bp, int q); static void verifyFreeBlock(bType *bp, int q); -static void verifyBallocSpace(); +void verifyBallocSpace(); #endif +static int ballocGetSize(int size, int *q); + /********************************** Code **************************************/ /* * Initialize the balloc module. bopen should be called the very first thing @@ -133,10 +141,25 @@ int bopen(void *buf, int bufsize, int flags) { bFlags = flags; +#if BASTARD_TESTING + srand(time(0L)); +#endif /* BASTARD_TESTING */ + +/* + * If bopen already called by a shared process, just increment the count + * and return; + */ + if (++bopenCount > 1) { + return 0; + } + if (buf == NULL) { if (bufsize == 0) { bufsize = B_DEFAULT_MEM; } +#ifdef IRIX + bufsize = ROUNDUP4(bufsize); +#endif if ((buf = malloc(bufsize)) == NULL) { return -1; } @@ -149,6 +172,7 @@ int bopen(void *buf, int bufsize, int flags) bFreeSize = bFreeLeft = bufsize; bFreeBuf = bFreeNext = buf; + memset(bQhead, 0, sizeof(bQhead)); #if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD bFillBlock(buf, bufsize); #endif @@ -171,8 +195,9 @@ void bclose() #if B_VERIFY_CAUSES_SEVERE_OVERHEAD verifyBallocSpace(); #endif - if (! (bFlags & B_USER_BUF)) { + if (--bopenCount <= 0 && !(bFlags & B_USER_BUF)) { free(bFreeBuf); + bopenCount = 0; } } @@ -185,13 +210,13 @@ void bclose() void *balloc(B_ARGS_DEC, int size) { bType *bp; - int q, memSize, mask; + int q, memSize; /* * Call bopen with default values if the application has not yet done so */ if (bFreeBuf == NULL) { - if (bopen(NULL, B_DEFAULT_MEM , 0) < 0) { + if (bopen(NULL, B_DEFAULT_MEM, 0) < 0) { return NULL; } } @@ -202,17 +227,14 @@ void *balloc(B_ARGS_DEC, int size) return NULL; } -/* - * Determine the relevant block queue with a block big enough -- - * include room for the block header. - */ - mask = (size + sizeof(bType)) >> B_SHIFT; - for (q = 0; mask; mask >>= 1) { - q++; +#if BASTARD_TESTING + if (rand() == 0x7fff) { + return NULL; } +#endif /* BASTARD_TESTING */ + - a_assert(0 <= q && q <= B_MAX_CLASS); - memSize = (1 << (B_SHIFT + q)); + memSize = ballocGetSize(size, &q); if (q >= B_MAX_CLASS) { /* @@ -221,11 +243,13 @@ void *balloc(B_ARGS_DEC, int size) if (bFlags & B_USE_MALLOC) { #if B_STATS bstats(0, NULL); +#endif +#ifdef IRIX + memSize = ROUNDUP4(memSize); #endif bp = (bType*) malloc(memSize); if (bp == NULL) { - goahead_trace(0, T("B: malloc failed for %s:%d, size %d\n"), - B_ARGS, memSize); + traceRaw(T("B: malloc failed\n")); return NULL; } #if B_STATS @@ -236,11 +260,14 @@ void *balloc(B_ARGS_DEC, int size) #endif } else { - goahead_trace(0, T("B: balloc failed for %s:%d, size %d\n"), - B_ARGS, memSize); + traceRaw(T("B: malloc failed\n")); return NULL; } - bp->u.size = size; + +/* + * the u.size is the actual size allocated for data + */ + bp->u.size = memSize - sizeof(bType); bp->flags = B_MALLOCED; } else if ((bp = bQhead[q]) != NULL) { @@ -254,7 +281,7 @@ void *balloc(B_ARGS_DEC, int size) #if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD bFillBlock(bp, memSize); #endif - bp->u.size = size; + bp->u.size = memSize - sizeof(bType); bp->flags = 0; } else { @@ -272,22 +299,24 @@ void *balloc(B_ARGS_DEC, int size) #if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD bFillBlock(bp, memSize); #endif - bp->u.size = size; + bp->u.size = memSize - sizeof(bType); bp->flags = 0; } else if (bFlags & B_USE_MALLOC) { - static int once = 0; - if (once++ < 20) { #if B_STATS + static int once = 0; + if (once++ == 0) { bstats(0, NULL); -#endif } +#endif /* * Nothing left on the primary free list, so malloc a new block */ +#ifdef IRIX + memSize = ROUNDUP4(memSize); +#endif if ((bp = (bType*) malloc(memSize)) == NULL) { - goahead_trace(0, T("B: malloc failed for %s:%d size %d\n"), - B_ARGS, memSize); + traceRaw(T("B: malloc failed\n")); return NULL; } #if B_STATS @@ -296,20 +325,31 @@ void *balloc(B_ARGS_DEC, int size) #if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD bFillBlock(bp, memSize); #endif - bp->u.size = size; + bp->u.size = memSize - sizeof(bType); bp->flags = B_MALLOCED; } else { - goahead_trace(0, T("B: alloc failed for %s:%d size %d\n"), B_ARGS, size); + traceRaw(T("B: malloc failed\n")); return NULL; } } #if B_STATS - bStatsAlloc(B_ARGS, bp, q, size); + bStatsAlloc(B_ARGS, bp, q, memSize); #endif bp->flags |= B_INTEGRITY; +/* + * The following is a good place to put a breakpoint when trying to reduce + * determine and reduce maximum memory use. + */ +#if 0 +#if B_STATS + if (bStatsBallocInUse == bStatsBallocMax) { + bstats(0, NULL); + } +#endif +#endif return (void*) ((char*) bp + sizeof(bType)); } @@ -323,41 +363,34 @@ void *balloc(B_ARGS_DEC, int size) void bfree(B_ARGS_DEC, void *mp) { bType *bp; - int mask, q; + int q, memSize; #if B_VERIFY_CAUSES_SEVERE_OVERHEAD verifyBallocSpace(); #endif - a_assert(mp); - bp = (bType*) ((char*) mp - sizeof(bType)); + a_assert((bp->flags & B_INTEGRITY_MASK) == B_INTEGRITY); + if ((bp->flags & B_INTEGRITY_MASK) != B_INTEGRITY) { return; } -/* - * Determine the relevant block queue - */ - mask = (bp->u.size + sizeof(bType)) >> B_SHIFT; - for (q = 0; mask; mask >>= 1) { - q++; - } - a_assert(0 <= q && q <= B_MAX_CLASS); + memSize = ballocGetSize(bp->u.size, &q); #if B_VERIFY_CAUSES_SEVERE_OVERHEAD verifyUsedBlock(bp,q); +#endif +#if B_STATS + bStatsFree(B_ARGS, bp, q, bp->u.size+sizeof(bType)); #endif if (bp->flags & B_MALLOCED) { free(bp); return; } -#if B_STATS - bStatsFree(B_ARGS, bp, q, bp->u.size); -#endif #if B_VERIFY_CAUSES_SEVERE_OVERHEAD - bFillBlock(bp, 1 << (B_SHIFT + q)); + bFillBlock(bp, memSize); #endif /* @@ -365,6 +398,8 @@ void bfree(B_ARGS_DEC, void *mp) */ bp->u.next = bQhead[q]; bQhead[q] = bp; + + bp->flags = B_FILL_WORD; } /******************************************************************************/ @@ -432,7 +467,7 @@ char_t *bstrdup(B_ARGS_DEC, char_t *s) void *brealloc(B_ARGS_DEC, void *mp, int newsize) { - bType* bp; + bType *bp; void *newbuf; if (mp == NULL) { @@ -440,6 +475,14 @@ void *brealloc(B_ARGS_DEC, void *mp, int newsize) } bp = (bType*) ((char*) mp - sizeof(bType)); a_assert((bp->flags & B_INTEGRITY_MASK) == B_INTEGRITY); + +/* + * If the allocated memory already has enough room just return the previously + * allocated address. + */ + if (bp->u.size >= newsize) { + return mp; + } if ((newbuf = balloc(B_ARGS, newsize)) != NULL) { memcpy(newbuf, mp, bp->u.size); bfree(B_ARGS, mp); @@ -447,6 +490,24 @@ void *brealloc(B_ARGS_DEC, void *mp, int newsize) return newbuf; } +/******************************************************************************/ +/* + * Find the size of the block to be balloc'ed. It takes in a size, finds the + * smallest binary block it fits into, adds an overhead amount and returns. + * q is the binary size used to keep track of block sizes in use. Called + * from both balloc and bfree. + */ + +static int ballocGetSize(int size, int *q) +{ + int mask; + + mask = (size == 0) ? 0 : (size-1) >> B_SHIFT; + for (*q = 0; mask; mask >>= 1) { + *q = *q + 1; + } + return ((1 << (B_SHIFT + *q)) + sizeof(bType)); +} /******************************************************************************/ #if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD @@ -464,17 +525,20 @@ static void bFillBlock(void *buf, int bufsize) #if B_STATS /* * Statistics. Do output via calling the writefn callback function with - * "handle" as the output file handle. + * "handle" as the output file handle. */ void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) { - bStatsFileType *fp; + bStatsFileType *fp, *files; + bStatsBlkType *blkp; bType *bp; - int q, count, mem, total; + char_t *cp; + int q, count, mem, total, len; static int recurseProtect = 0; if (recurseProtect++ > 0) { + recurseProtect--; return; } @@ -492,19 +556,18 @@ void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) * Q Size Free Bytes Inuse Bytes Allocs * dd ddddd ddd ddddd dddd ddddd dddd */ - (*writefn)(handle, " Q Size Free Bytes Inuse Bytes Allocs\n"); + (*writefn)(handle, " Q Size Free Bytes Inuse Bytes Allocs\n"); total = 0; for (q = 0; q < B_MAX_CLASS; q++) { count = 0; for (bp = bQhead[q]; bp; bp = bp->u.next) { - a_assert((bp->flags & B_INTEGRITY_MASK) == B_INTEGRITY); count++; } mem = count * (1 << (q + B_SHIFT)); total += mem; (*writefn)(handle, - T("%2d %5d %3d %5d %4d %5d %4d\n"), + T("%2d %5d %4d %6d %4d %5d %4d\n"), q, 1 << (q + B_SHIFT), count, mem, bStats[q].inuse, bStats[q].inuse * (1 << (q + B_SHIFT)), bStats[q].alloc); } @@ -513,11 +576,26 @@ void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) /* * Print summary stats + * + * bFreeSize Initial memory reserved with bopen call + * bStatsMemMalloc memory from calls to system MALLOC + * bStatsMemMax + * bStatsBallocMax largest amount of memory from balloc calls + * bStatsMemInUse + * bStatsBallocInUse present balloced memory being used + * bStatsBlksMax); + * bStackStart + * bStackMin); + * total); + * bFreeLeft); + * */ (*writefn)(handle, T("Initial free list size %7d\n"), bFreeSize); (*writefn)(handle, T("Max memory malloced %7d\n"), bStatsMemMalloc); (*writefn)(handle, T("Max memory ever used %7d\n"), bStatsMemMax); + (*writefn)(handle, T("Max memory ever balloced %7d\n"), bStatsBallocMax); (*writefn)(handle, T("Memory currently in use %7d\n"), bStatsMemInUse); + (*writefn)(handle, T("Memory currently balloced %7d\n"), bStatsBallocInUse); (*writefn)(handle, T("Max blocks allocated %7d\n"), bStatsBlksMax); (*writefn)(handle, T("Maximum stack used %7d\n"), (int) bStackStart - (int) bStackMin); @@ -527,20 +605,49 @@ void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) (*writefn)(handle, T("Total free memory %7d\n"), bFreeLeft + total); /* - * Print per file allocation stats + * Print per file allocation stats. Sort the copied table. */ - qsort(bStatsFiles, bStatsFilesMax, sizeof(bStatsFileType), bStatsFileSort); - (*writefn)(handle, T("\nPer File Memory Stats\n")); + len = sizeof(bStatsFileType) * B_MAX_FILES; + files = malloc(len); + if (files == NULL) { + (*writefn)(handle, T("Can't allocate stats memory\n")); + recurseProtect--; + return; + } + memcpy(files, bStatsFiles, len); + qsort(files, bStatsFilesMax, sizeof(bStatsFileType), bStatsFileSort); + + (*writefn)(handle, T("\nMemory Currently Allocated\n")); total = 0; - for (fp = bStatsFiles; fp < &bStatsFiles[bStatsFilesMax]; fp++) { + (*writefn)(handle, + T(" bytes, blocks in use, total times,") + T("largest, q\n")); + + for (fp = files; fp < &files[bStatsFilesMax]; fp++) { if (fp->file[0]) { - (*writefn)(handle, - T("%18s, bytes %7d, blocks in use %5d, total allocs %6d\n"), - fp->file, fp->allocated, fp->count, fp->allocs); + (*writefn)(handle, T("%18s, %7d, %5d, %6d, %7d,%4d\n"), + fp->file, fp->allocated, fp->count, fp->times, fp->largest, + fp->q); total += fp->allocated; } } - (*writefn)(handle, T("\nTotal allocated %7d\n"), total); + (*writefn)(handle, T("\nTotal allocated %7d\n\n"), total); + +/* + * Dump the actual strings + */ + (*writefn)(handle, T("\nStrings\n")); + for (blkp = &bStatsBlks[bStatsBlksMax - 1]; blkp >= bStatsBlks; blkp--) { + if (blkp->ptr) { + cp = (char_t*) ((char*) blkp->ptr + sizeof(bType)); + fp = blkp->who; + if (gisalnum(*cp)) { + (*writefn)(handle, T("%-50s allocated by %s\n"), cp, + fp->file); + } + } + } + free(files); recurseProtect--; } @@ -563,26 +670,6 @@ static int bStatsFileSort(const void *cp1, const void *cp2) return 1; } -/******************************************************************************/ -/* - * Default output function. Just send to trace channel. - */ - -static void bstatsWrite(int handle, char_t *fmt, ...) -{ - va_list args; - char_t *buf; - - va_start(args, fmt); - buf = NULL; - gvsnprintf(&buf, VALUE_MAX_STRING, fmt, args); - va_end(args); - goahead_trace(0, buf); - if (buf) { - bfree(B_L, buf); - } -} - /******************************************************************************/ /* * Accumulate allocation statistics @@ -590,23 +677,24 @@ static void bstatsWrite(int handle, char_t *fmt, ...) static void bStatsAlloc(B_ARGS_DEC, void *ptr, int q, int size) { + int memSize; bStatsFileType *fp; bStatsBlkType *bp; char_t name[FNAMESIZE + 10]; - a_assert(file && *file); - a_assert(0 <= q && q <= B_MAX_CLASS); - a_assert(size > 0); - gsprintf(name, T("%s:%d"), B_ARGS); bStats[q].alloc++; bStats[q].inuse++; bStatsMemInUse += size; - if (bStatsMemInUse > bStatsMemMax) { bStatsMemMax = bStatsMemInUse; } + memSize = (1 << (B_SHIFT + q)) + sizeof(bType); + bStatsBallocInUse += memSize; + if (bStatsBallocInUse > bStatsBallocMax) { + bStatsBallocMax = bStatsBallocInUse; + } /* * Track maximum stack usage. Assumes a stack growth down. Approximate as @@ -623,13 +711,17 @@ static void bStatsAlloc(B_ARGS_DEC, void *ptr, int q, int size) if (fp->file[0] == file[0] && gstrcmp(fp->file, name) == 0) { fp->allocated += size; fp->count++; - fp->allocs++; + fp->times++; + if (fp->largest < size) { + fp->largest = size; + fp->q = q; + } break; } } /* - * Find the first free slot for this file and add current block size. + * New entry: find the first free slot and create a new entry */ if (fp >= &bStatsFiles[bStatsFilesMax]) { for (fp = bStatsFiles; fp < &bStatsFiles[B_MAX_FILES]; fp++) { @@ -637,7 +729,9 @@ static void bStatsAlloc(B_ARGS_DEC, void *ptr, int q, int size) gstrncpy(fp->file, name, TSZ(fp->file)); fp->allocated += size; fp->count++; - fp->allocs++; + fp->times++; + fp->largest = size; + fp->q = q; if ((fp - bStatsFiles) >= bStatsFilesMax) { bStatsFilesMax = (fp - bStatsFiles) + 1; } @@ -668,35 +762,48 @@ static void bStatsAlloc(B_ARGS_DEC, void *ptr, int q, int size) static void bStatsFree(B_ARGS_DEC, void *ptr, int q, int size) { + int memSize; bStatsFileType *fp; bStatsBlkType *bp; - char_t name[FNAMESIZE + 10]; - - a_assert(file && *file); - a_assert(0 <= q && q <= B_MAX_CLASS); - a_assert(size > 0); + memSize = (1 << (B_SHIFT + q)) + sizeof(bType); bStatsMemInUse -= size; + bStatsBallocInUse -= memSize; bStats[q].inuse--; - gsprintf(name, T("%s:%d"), B_ARGS); - /* - * Update the per block stats + * Update the per block stats. Try from the end first */ - for (bp = bStatsBlks; bp < &bStatsBlks[bStatsBlksMax]; bp++) { + for (bp = &bStatsBlks[bStatsBlksMax - 1]; bp >= bStatsBlks; bp--) { if (bp->ptr == ptr) { bp->ptr = NULL; fp = bp->who; + bp->who = NULL; fp->allocated -= size; fp->count--; return; } } - a_assert(0); +} + +/******************************************************************************/ +/* + * Default output function. Just send to trace channel. + */ + +#undef sprintf +static void bstatsWrite(int handle, char_t *fmt, ...) +{ + va_list args; + char_t buf[BUF_MAX]; + va_start(args, fmt); + vsprintf(buf, fmt, args); + va_end(args); + traceRaw(buf); } + #else /* not B_STATS */ /******************************************************************************/ /* @@ -712,10 +819,10 @@ void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) #if B_VERIFY_CAUSES_SEVERE_OVERHEAD /* * The following routines verify the integrity of the balloc memory space. - * These functions depend use the B_FILL feature. Corruption is defined + * These functions use the B_FILL feature. Corruption is defined * as bad integrity flags in allocated blocks or data other than B_FILL_CHAR * being found anywhere in the space which is unallocated and that is not a - * next pointer in the free queues. a_assert is called if any corruption is + * next pointer in the free queues. a_assert is called if any corruption is * found. CAUTION: These functions add severe processing overhead and should * only be used when searching for a tough corruption problem. */ @@ -731,8 +838,8 @@ static void verifyUsedBlock(bType *bp, int q) int memSize, size; char *p; - memSize = (1 << (B_SHIFT + q)); - a_assert((bp->flags & ~B_MALLOCED) == B_INTEGRITY ); + memSize = (1 << (B_SHIFT + q)) + sizeof(bType); + a_assert((bp->flags & ~B_MALLOCED) == B_INTEGRITY); size = bp->u.size; for (p = ((char *)bp)+sizeof(bType)+size; p < ((char*)bp)+memSize; p++) { a_assert(*p == B_FILL_CHAR); @@ -750,28 +857,41 @@ static void verifyFreeBlock(bType *bp, int q) int memSize; char *p; - memSize = (1 << (B_SHIFT + q)); + memSize = (1 << (B_SHIFT + q)) + sizeof(bType); for (p = ((char *)bp)+sizeof(void*); p < ((char*)bp)+memSize; p++) { a_assert(*p == B_FILL_CHAR); } bp = (bType *)p; a_assert((bp->flags & ~B_MALLOCED) == B_INTEGRITY || - bp->flags == B_FILL_WORD); + bp->flags == B_FILL_WORD); } /******************************************************************************/ /* * verifyBallocSpace reads through the entire balloc memory space and - * verifies that all allocated blocks are uncorrupted and that with the - * exception of free list next pointers all other unallocated space is + * verifies that all allocated blocks are uncorrupted and that, with the + * exception of free list next pointers, all other unallocated space is * filled with B_FILL_CHAR. */ -static void verifyBallocSpace() +void verifyBallocSpace() { + int q; char *p; bType *bp; +/* + * First verify all the free blocks. + */ + for (q = 0; q < B_MAX_CLASS; q++) { + for (bp = bQhead[q]; bp != NULL; bp = bp->u.next) { + verifyFreeBlock(bp, q); + } + } + +/* + * Now verify other space + */ p = bFreeBuf; while (p < (bFreeBuf + bFreeSize)) { bp = (bType *)p; @@ -782,7 +902,7 @@ static void verifyBallocSpace() } } else { a_assert(((bp->flags & ~B_MALLOCED) == B_INTEGRITY) || - bp->flags == B_FILL_WORD); + bp->flags == B_FILL_WORD); p += (sizeof(bType) + bp->u.size); while (p < (bFreeBuf + bFreeSize) && *p == B_FILL_CHAR) { p++; @@ -807,19 +927,29 @@ void bclose() } /******************************************************************************/ -#if UNICODE -char_t* bstrdupNoBalloc(char_t* s) + +void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) +{ +} + +/******************************************************************************/ + +char_t *bstrdupNoBalloc(char_t *s) { +#if UNICODE if (s) { return wcsdup(s); } else { return wcsdup(T("")); } +#else + return bstrdupANoBalloc(s); +#endif } -#endif /* UNICODE */ /******************************************************************************/ -char* bstrdupANoBalloc(char* s) + +char *bstrdupANoBalloc(char *s) { char* buf; diff --git a/cpukit/httpd/base64.c b/cpukit/httpd/base64.c new file mode 100644 index 0000000000..b1eb6b01c5 --- /dev/null +++ b/cpukit/httpd/base64.c @@ -0,0 +1,147 @@ +/* + * base64.c -- Base64 Mime encoding + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + */ + +/******************************** Description *********************************/ + +/* + * The base64 command encodes and decodes a string in mime base64 format + */ + +/********************************* Includes ***********************************/ + +#include "wsIntrn.h" + +/******************************** Local Data **********************************/ +/* + * Mapping of ANSI chars to base64 Mime encoding alphabet (see below) + */ + +static char_t map64[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char_t alphabet64[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', +}; + +/*********************************** Code *************************************/ +/* + * Decode a buffer from "string" and into "outbuf" + */ + +int websDecode64(char_t *outbuf, char_t *string, int outlen) +{ + unsigned long shiftbuf; + char_t *cp, *op; + int c, i, j, shift; + + op = outbuf; + *op = '\0'; + cp = string; + while (*cp && *cp != '=') { +/* + * Map 4 (6bit) input bytes and store in a single long (shiftbuf) + */ + shiftbuf = 0; + shift = 18; + for (i = 0; i < 4 && *cp && *cp != '='; i++, cp++) { + c = map64[*cp & 0xff]; + if (c == -1) { + error(E_L, E_LOG, T("Bad string: %s at %c index %d"), string, + c, i); + return -1; + } + shiftbuf = shiftbuf | (c << shift); + shift -= 6; + } +/* + * Interpret as 3 normal 8 bit bytes (fill in reverse order). + * Check for potential buffer overflow before filling. + */ + --i; + if ((op + i) >= &outbuf[outlen]) { + gstrcpy(outbuf, T("String too big")); + return -1; + } + for (j = 0; j < i; j++) { + *op++ = (char_t) ((shiftbuf >> (8 * (2 - j))) & 0xff); + } + *op = '\0'; + } + return 0; +} + + +/******************************************************************************/ +/* + * Encode a buffer from "string" into "outbuf" + */ + +void websEncode64(char_t *outbuf, char_t *string, int outlen) +{ + unsigned long shiftbuf; + char_t *cp, *op; + int x, i, j, shift; + + op = outbuf; + *op = '\0'; + cp = string; + while (*cp) { +/* + * Take three characters and create a 24 bit number in shiftbuf + */ + shiftbuf = 0; + for (j = 2; j >= 0 && *cp; j--, cp++) { + shiftbuf |= ((*cp & 0xff) << (j * 8)); + } +/* + * Now convert shiftbuf to 4 base64 letters. The i,j magic calculates + * how many letters need to be output. + */ + shift = 18; + for (i = ++j; i < 4 && op < &outbuf[outlen] ; i++) { + x = (shiftbuf >> shift) & 0x3f; + *op++ = alphabet64[(shiftbuf >> shift) & 0x3f]; + shift -= 6; + } +/* + * Pad at the end with '=' + */ + while (j-- > 0) { + *op++ = '='; + } + *op = '\0'; + } +} +/******************************************************************************/ diff --git a/cpukit/httpd/default.c b/cpukit/httpd/default.c index c485c22285..6851995872 100644 --- a/cpukit/httpd/default.c +++ b/cpukit/httpd/default.c @@ -1,9 +1,11 @@ /* * default.c -- Default URL handler. Includes support for ASP. * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ */ /******************************** Description *********************************/ @@ -38,16 +40,15 @@ static void websDefaultWriteEvent(webs_t wp); */ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, - char_t *url, char_t *path, char_t* query) + char_t *url, char_t *path, char_t *query) { websStatType sbuf; - char_t *lpath, *tmp; - char_t *date; + char_t *lpath, *tmp, *date; int bytes, flags, nchars; a_assert(websValid(wp)); a_assert(url && *url); - a_assert(path && *path); + a_assert(path); a_assert(query); /* @@ -74,8 +75,7 @@ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, path[--nchars] = '\0'; } nchars += gstrlen(websDefaultPage) + 2; - tmp = NULL; - gsnprintf(&tmp, nchars, T("%s/%s"), path, websDefaultPage); + fmtAlloc(&tmp, nchars, T("%s/%s"), path, websDefaultPage); websRedirect(wp, tmp); bfreeSafe(B_L, tmp); return 1; @@ -87,13 +87,13 @@ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, if (websPageOpen(wp, lpath, path, SOCKET_RDONLY | SOCKET_BINARY, 0666) < 0) { websError(wp, 400, - T("Can't open document %s
for URL %s"), - lpath, url); + T("Cannot open URL %s"), url); return 1; } if (websPageStat(wp, lpath, path, &sbuf) < 0) { - websError(wp, 400, T("Can't stat page %s
for URL %s"), - lpath, url); + websError(wp, 400, T("Cannot stat page for URL %s"), + url); + return 1; } /* @@ -107,12 +107,13 @@ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, if (sbuf.mtime <= wp->since) { websWrite(wp, T("HTTP/1.0 304 Use local copy\r\n")); - /* by license terms the following line of code must - * not be modified. - */ - websWrite(wp, T("Server: GoAhead-Webs\r\n")); +/* + * by license terms the following line of code must + * not be modified. + */ + websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); - if (flags && WEBS_KEEP_ALIVE) { + if (flags & WEBS_KEEP_ALIVE) { websWrite(wp, T("Connection: keep-alive\r\n")); } websWrite(wp, T("\r\n")); @@ -131,8 +132,8 @@ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, /* * By license terms the following line of code must not be modified. -*/ - websWrite(wp, T("Server: GoAhead-Webs\r\n")); + */ + websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); bfree(B_L, date); } flags |= WEBS_HEADER_DONE; @@ -164,6 +165,14 @@ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, } websWrite(wp, T("\r\n")); +/* + * All done if the browser did a HEAD request + */ + if (flags & WEBS_HEAD_REQUEST) { + websDone(wp, 200); + return 1; + } + /* * Evaluate ASP requests */ @@ -175,17 +184,18 @@ int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, return 1; } -/* - * All done if the browser did a HEAD request - */ - if (flags & WEBS_HEAD_REQUEST) { - websDone(wp, 200); - return 1; +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + websDefaultWriteEvent(wp); + } else { + websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent); } +#else /* * For normal web documents, return the data via background write */ websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent); +#endif return 1; } @@ -240,7 +250,7 @@ int websValidateUrl(webs_t wp, char_t *path) /* * Create local path for document. Need extra space all "/" and null. */ - if (npart) { + if (npart || (gstrcmp(path, T("/")) == 0) || (path[0] == '\0')) { lpath = balloc(B_L, (gstrlen(dir) + 1 + len + 1) * sizeof(char_t)); gstrcpy(lpath, dir); @@ -267,15 +277,16 @@ int websValidateUrl(webs_t wp, char_t *path) static void websDefaultWriteEvent(webs_t wp) { - int len, wrote, flags, bytes, written; - char * buf; + int len, wrote, flags, bytes, written; + char *buf; a_assert(websValid(wp)); flags = websGetRequestFlags(wp); - wrote = 0; - bytes = 0; + websMarkTime(wp); + + wrote = bytes = 0; written = websGetRequestWritten(wp); /* @@ -284,20 +295,20 @@ static void websDefaultWriteEvent(webs_t wp) if ( !(flags & WEBS_ASP)) { bytes = websGetRequestBytes(wp); /* - * Note: websWriteBlock may return less than we wanted. It will return - * -1 on a socket error + * Note: websWriteDataNonBlock may return less than we wanted. It will + * return -1 on a socket error */ if ((buf = balloc(B_L, PAGE_READ_BUFSIZE)) == NULL) { websError(wp, 200, T("Can't get memory")); } else { while ((len = websPageReadData(wp, buf, PAGE_READ_BUFSIZE)) > 0) { - if ((wrote = websWriteBlockData(wp, buf, len)) < 0) { + if ((wrote = websWriteDataNonBlock(wp, buf, len)) < 0) { break; } written += wrote; if (wrote != len) { - websPageSeek(wp, - (wrote - len)); + websPageSeek(wp, - (len - wrote)); break; } } @@ -330,9 +341,11 @@ void websDefaultClose() { if (websDefaultPage) { bfree(B_L, websDefaultPage); + websDefaultPage = NULL; } if (websDefaultDir) { bfree(B_L, websDefaultDir); + websDefaultDir = NULL; } } diff --git a/cpukit/httpd/ej.h b/cpukit/httpd/ej.h index 91eb72a4d2..fa7a51a771 100644 --- a/cpukit/httpd/ej.h +++ b/cpukit/httpd/ej.h @@ -1,7 +1,7 @@ /* * ej.h -- Ejscript(TM) header * - * Copyright (c) Go Ahead Software, Inc., 1992-1999 + * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved. * * See the file "license.txt" for information on usage and redistribution */ @@ -12,214 +12,32 @@ /******************************** Description *********************************/ /* - * Go Ahead Ejscript(TM) header. This defines the Ejscript API and internal + * GoAhead Ejscript(TM) header. This defines the Ejscript API and internal * structures. */ /********************************* Includes ***********************************/ -#include -#include -#include -#ifndef CE - #include -#endif - -#if LYNX - #include -#endif - -#ifdef QNX4 - #include -#endif - -#if UEMF - #include "uemf.h" -#else - #include - #include - #include "basic/basicInternal.h" +#if ! UEMF + #include "basic/basic.h" #include "emf/emf.h" - #include "webs/webs.h" +#else + #include "uemf.h" #endif /********************************** Defines ***********************************/ -/* - * Constants - */ -#define EJ_INC 110 /* Growth for tags/tokens */ -#define EJ_OFFSET 1 /* hAlloc doesn't like 0 entries */ -#define EJ_MAX_RECURSE 100 /* Sanity for maximum recursion */ - -/* - * Ejscript Lexical analyser tokens - */ -#define TOK_ERR -1 /* Any error */ -#define TOK_LPAREN 1 /* ( */ -#define TOK_RPAREN 2 /* ) */ -#define TOK_IF 3 /* if */ -#define TOK_ELSE 4 /* else */ -#define TOK_LBRACE 5 /* { */ -#define TOK_RBRACE 6 /* } */ -#define TOK_LOGICAL 7 /* ||, &&, ! */ -#define TOK_EXPR 8 /* +, -, /, % */ -#define TOK_SEMI 9 /* ; */ -#define TOK_LITERAL 10 /* literal string */ -#define TOK_FUNCTION 11 /* function name */ -#define TOK_NEWLINE 12 /* newline white space */ -#define TOK_ID 13 /* function name */ -#define TOK_EOF 14 /* End of script */ -#define TOK_COMMA 15 /* Comma */ -#define TOK_VAR 16 /* var */ -#define TOK_ASSIGNMENT 17 /* = */ -#define TOK_FOR 18 /* for */ -#define TOK_INC_DEC 19 /* ++, -- */ -#define TOK_RETURN 20 /* return */ - -/* - * Expression operators - */ -#define EXPR_LESS 1 /* < */ -#define EXPR_LESSEQ 2 /* <= */ -#define EXPR_GREATER 3 /* > */ -#define EXPR_GREATEREQ 4 /* >= */ -#define EXPR_EQ 5 /* == */ -#define EXPR_NOTEQ 6 /* != */ -#define EXPR_PLUS 7 /* + */ -#define EXPR_MINUS 8 /* - */ -#define EXPR_DIV 9 /* / */ -#define EXPR_MOD 10 /* % */ -#define EXPR_LSHIFT 11 /* << */ -#define EXPR_RSHIFT 12 /* >> */ -#define EXPR_MUL 13 /* * */ -#define EXPR_ASSIGNMENT 14 /* = */ -#define EXPR_INC 15 /* ++ */ -#define EXPR_DEC 16 /* -- */ - -/* - * Conditional operators - */ -#define COND_AND 1 /* && */ -#define COND_OR 2 /* || */ -#define COND_NOT 3 /* ! */ - -/* - * States - */ -#define STATE_ERR -1 /* Error state */ -#define STATE_EOF 1 /* End of file */ -#define STATE_COND 2 /* Parsing a "(conditional)" stmt */ -#define STATE_COND_DONE 3 -#define STATE_RELEXP 4 /* Parsing a relational expr */ -#define STATE_RELEXP_DONE 5 -#define STATE_EXPR 6 /* Parsing an expression */ -#define STATE_EXPR_DONE 7 -#define STATE_STMT 8 /* Parsing General statement */ -#define STATE_STMT_DONE 9 -#define STATE_STMT_BLOCK_DONE 10 /* End of block "}" */ -#define STATE_ARG_LIST 11 /* Function arg list */ -#define STATE_ARG_LIST_DONE 12 -#define STATE_DEC_LIST 16 /* Declaration list */ -#define STATE_DEC_LIST_DONE 17 -#define STATE_DEC 18 -#define STATE_DEC_DONE 19 -#define STATE_BEGIN STATE_STMT - -/* - * Flags. Used in ej_t and as parameter to parse() - */ -#define FLAGS_EXE 0x1 /* Execute statements */ -#define FLAGS_VARIABLES 0x2 /* Allocated variables store */ -#define FLAGS_FUNCTIONS 0x4 /* Allocated function store */ - -/* - * Function call structure - */ -typedef struct { - char_t *fname; /* Function name */ - char_t **args; /* Args for function (halloc) */ - int nArgs; /* Number of args */ -} ejfunc_t; - -/* - * EJ evaluation block structure - */ -typedef struct ejEval { - ringq_t tokbuf; /* Current token */ - ringq_t script; /* Input script for parsing */ - char_t *putBackToken; /* Putback token string */ - int putBackTokenId; /* Putback token ID */ - char_t *line; /* Current line */ - int lineLength; /* Current line length */ - int lineNumber; /* Parse line number */ - int lineColumn; /* Column in line */ -} ejinput_t; - -/* - * Per Ejscript session structure - */ -typedef struct ej { - ejinput_t *input; /* Input evaluation block */ - sym_fd_t functions; /* Symbol table for functions */ - sym_fd_t *variables; /* hAlloc list of variables */ - int variableMax; /* Number of entries */ - ejfunc_t *func; /* Current function */ - char_t *result; /* Current expression result */ - char_t *error; /* Error message */ - char_t *token; /* Pointer to token string */ - int tid; /* Current token id */ - int eid; /* Halloc handle */ - int flags; /* Flags */ - int userHandle; /* User defined handle */ -} ej_t; /******************************** Prototypes **********************************/ +extern int ejArgs(int argc, char_t **argv, char_t *fmt, ...); +extern void ejSetResult(int eid, char_t *s); extern int ejOpenEngine(sym_fd_t variables, sym_fd_t functions); extern void ejCloseEngine(int eid); -extern int ejOpenBlock(int eid); -extern int ejCloseBlock(int eid, int vid); -extern char_t *ejEval(int eid, char_t *script, char_t **emsg); -extern char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg); -extern char_t *ejEvalFile(int eid, char_t *path, char_t **emsg); extern int ejSetGlobalFunction(int eid, char_t *name, int (*fn)(int eid, void *handle, int argc, char_t **argv)); -extern void *ejGetGlobalFunction(int eid, char_t *name); -extern int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, - int (*fn)(int eid, void *handle, int argc, char_t **argv)); -extern int ejArgs(int argc, char_t **argv, char_t *fmt, ...); -extern void ejError(ej_t* ep, char_t* fmt, ...); -extern void ejSetUserHandle(int eid, int handle); -extern int ejGetUserHandle(int eid); -extern int ejGetLineNumber(int eid); -extern void ejSetResult(int eid, char_t *s); -extern char_t *ejGetResult(int eid); extern void ejSetVar(int eid, char_t *var, char_t *value); -extern void ejSetLocalVar(int eid, char_t *var, char_t *value); extern int ejGetVar(int eid, char_t *var, char_t **value); -extern void ejSetGlobalVar(int eid, char_t *var, char_t *value); - -extern int ejLexOpen(ej_t* ep); -extern void ejLexClose(ej_t* ep); -extern int ejLexOpenScript(ej_t* ep, char_t *script); -extern void ejLexCloseScript(ej_t* ep); -extern void ejLexSaveInputState(ej_t* ep, ejinput_t* state); -extern void ejLexFreeInputState(ej_t* ep, ejinput_t* state); -extern void ejLexRestoreInputState(ej_t* ep, ejinput_t* state); -extern int ejLexGetToken(ej_t* ep, int state); -extern void ejLexPutbackToken(ej_t* ep, int tid, char_t *string); - -extern sym_fd_t ejGetVariableTable(int eid); -extern sym_fd_t ejGetFunctionTable(int eid); - -extern int ejEmfOpen(int eid); -extern void ejEmfClose(int eid); - -extern int ejEmfDbRead(int eid, void *handle, int argc, char_t **argv); -extern int ejEmfDbReadKeyed(int eid, void *handle, int argc, char_t **argv); -extern int ejEmfDbTableGetNrow(int eid, void *handle, int argc, char_t **argv); -extern int ejEmfTrace(int eid, void *handle, int argc, char_t **argv); -extern int ejEmfDbWrite(int eid, void *handle, int argc, char_t **argv); +extern char_t *ejEval(int eid, char_t *script, char_t **emsg); #endif /* _h_EJ */ diff --git a/cpukit/httpd/ejIntrn.h b/cpukit/httpd/ejIntrn.h new file mode 100644 index 0000000000..0ab7b9b5e5 --- /dev/null +++ b/cpukit/httpd/ejIntrn.h @@ -0,0 +1,228 @@ +/* + * ejIntrn.h -- Ejscript(TM) header + * + * Copyright (c) GoAhead Software, Inc., 1992-2000 + * + * See the file "license.txt" for information on usage and redistribution + */ + +#ifndef _h_EJINTERNAL +#define _h_EJINTERNAL 1 + +/******************************** Description *********************************/ + +/* + * GoAhead Ejscript(TM) header. This defines the Ejscript API and internal + * structures. + */ + +/********************************* Includes ***********************************/ + +#include +#include +#include + +#if CE +#if ! UEMF + #include +#endif +#endif + +#if LYNX + #include +#endif + +#ifdef QNX4 + #include +#endif + +#if UEMF + #include "uemf.h" +#else + #include + #include + #include "basic/basicInternal.h" + #include "emf/emfInternal.h" +#endif + +#include "ej.h" + +/********************************** Defines ***********************************/ +/* + * Constants + */ +#define EJ_INC 110 /* Growth for tags/tokens */ +#define EJ_SCRIPT_INC 1023 /* Growth for ej scripts */ +#define EJ_OFFSET 1 /* hAlloc doesn't like 0 entries */ +#define EJ_MAX_RECURSE 100 /* Sanity for maximum recursion */ + +/* + * Ejscript Lexical analyser tokens + */ +#define TOK_ERR -1 /* Any error */ +#define TOK_LPAREN 1 /* ( */ +#define TOK_RPAREN 2 /* ) */ +#define TOK_IF 3 /* if */ +#define TOK_ELSE 4 /* else */ +#define TOK_LBRACE 5 /* { */ +#define TOK_RBRACE 6 /* } */ +#define TOK_LOGICAL 7 /* ||, &&, ! */ +#define TOK_EXPR 8 /* +, -, /, % */ +#define TOK_SEMI 9 /* ; */ +#define TOK_LITERAL 10 /* literal string */ +#define TOK_FUNCTION 11 /* function name */ +#define TOK_NEWLINE 12 /* newline white space */ +#define TOK_ID 13 /* function name */ +#define TOK_EOF 14 /* End of script */ +#define TOK_COMMA 15 /* Comma */ +#define TOK_VAR 16 /* var */ +#define TOK_ASSIGNMENT 17 /* = */ +#define TOK_FOR 18 /* for */ +#define TOK_INC_DEC 19 /* ++, -- */ +#define TOK_RETURN 20 /* return */ + +/* + * Expression operators + */ +#define EXPR_LESS 1 /* < */ +#define EXPR_LESSEQ 2 /* <= */ +#define EXPR_GREATER 3 /* > */ +#define EXPR_GREATEREQ 4 /* >= */ +#define EXPR_EQ 5 /* == */ +#define EXPR_NOTEQ 6 /* != */ +#define EXPR_PLUS 7 /* + */ +#define EXPR_MINUS 8 /* - */ +#define EXPR_DIV 9 /* / */ +#define EXPR_MOD 10 /* % */ +#define EXPR_LSHIFT 11 /* << */ +#define EXPR_RSHIFT 12 /* >> */ +#define EXPR_MUL 13 /* * */ +#define EXPR_ASSIGNMENT 14 /* = */ +#define EXPR_INC 15 /* ++ */ +#define EXPR_DEC 16 /* -- */ +#define EXPR_BOOL_COMP 17 /* ! */ +/* + * Conditional operators + */ +#define COND_AND 1 /* && */ +#define COND_OR 2 /* || */ +#define COND_NOT 3 /* ! */ + +/* + * States + */ +#define STATE_ERR -1 /* Error state */ +#define STATE_EOF 1 /* End of file */ +#define STATE_COND 2 /* Parsing a "(conditional)" stmt */ +#define STATE_COND_DONE 3 +#define STATE_RELEXP 4 /* Parsing a relational expr */ +#define STATE_RELEXP_DONE 5 +#define STATE_EXPR 6 /* Parsing an expression */ +#define STATE_EXPR_DONE 7 +#define STATE_STMT 8 /* Parsing General statement */ +#define STATE_STMT_DONE 9 +#define STATE_STMT_BLOCK_DONE 10 /* End of block "}" */ +#define STATE_ARG_LIST 11 /* Function arg list */ +#define STATE_ARG_LIST_DONE 12 +#define STATE_DEC_LIST 16 /* Declaration list */ +#define STATE_DEC_LIST_DONE 17 +#define STATE_DEC 18 +#define STATE_DEC_DONE 19 + +#define STATE_RET 20 /* Return statement */ + +#define STATE_BEGIN STATE_STMT + +/* + * Flags. Used in ej_t and as parameter to parse() + */ +#define FLAGS_EXE 0x1 /* Execute statements */ +#define FLAGS_VARIABLES 0x2 /* Allocated variables store */ +#define FLAGS_FUNCTIONS 0x4 /* Allocated function store */ + +/* + * Function call structure + */ +typedef struct { + char_t *fname; /* Function name */ + char_t **args; /* Args for function (halloc) */ + int nArgs; /* Number of args */ +} ejfunc_t; + +/* + * EJ evaluation block structure + */ +typedef struct ejEval { + ringq_t tokbuf; /* Current token */ + ringq_t script; /* Input script for parsing */ + char_t *putBackToken; /* Putback token string */ + int putBackTokenId; /* Putback token ID */ + char_t *line; /* Current line */ + int lineLength; /* Current line length */ + int lineNumber; /* Parse line number */ + int lineColumn; /* Column in line */ +} ejinput_t; + +/* + * Per Ejscript session structure + */ +typedef struct ej { + ejinput_t *input; /* Input evaluation block */ + sym_fd_t functions; /* Symbol table for functions */ + sym_fd_t *variables; /* hAlloc list of variables */ + int variableMax; /* Number of entries */ + ejfunc_t *func; /* Current function */ + char_t *result; /* Current expression result */ + char_t *error; /* Error message */ + char_t *token; /* Pointer to token string */ + int tid; /* Current token id */ + int eid; /* Halloc handle */ + int flags; /* Flags */ + int userHandle; /* User defined handle */ +} ej_t; + +/******************************** Prototypes **********************************/ + +extern int ejOpenBlock(int eid); +extern int ejCloseBlock(int eid, int vid); +extern char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg); +#ifndef __NO_EJ_FILE +extern char_t *ejEvalFile(int eid, char_t *path, char_t **emsg); +#endif +extern int ejRemoveGlobalFunction(int eid, char_t *name); +extern void *ejGetGlobalFunction(int eid, char_t *name); +extern int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, + int (*fn)(int eid, void *handle, int argc, char_t **argv)); +extern void ejError(ej_t* ep, char_t* fmt, ...); +extern void ejSetUserHandle(int eid, int handle); +extern int ejGetUserHandle(int eid); +extern int ejGetLineNumber(int eid); +extern char_t *ejGetResult(int eid); +extern void ejSetLocalVar(int eid, char_t *var, char_t *value); +extern void ejSetGlobalVar(int eid, char_t *var, char_t *value); + +extern int ejLexOpen(ej_t* ep); +extern void ejLexClose(ej_t* ep); +extern int ejLexOpenScript(ej_t* ep, char_t *script); +extern void ejLexCloseScript(ej_t* ep); +extern void ejLexSaveInputState(ej_t* ep, ejinput_t* state); +extern void ejLexFreeInputState(ej_t* ep, ejinput_t* state); +extern void ejLexRestoreInputState(ej_t* ep, ejinput_t* state); +extern int ejLexGetToken(ej_t* ep, int state); +extern void ejLexPutbackToken(ej_t* ep, int tid, char_t *string); + +extern sym_fd_t ejGetVariableTable(int eid); +extern sym_fd_t ejGetFunctionTable(int eid); + +extern int ejEmfOpen(int eid); +extern void ejEmfClose(int eid); + +extern int ejEmfDbRead(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbReadKeyed(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbTableGetNrow(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbDeleteRow(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfTrace(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbWrite(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbCollectTable(int eid, void *handle, int argc, char_t **argv); + +#endif /* _h_EJINTERNAL */ diff --git a/cpukit/httpd/ejlex.c b/cpukit/httpd/ejlex.c index 091d17411b..ea5cead255 100644 --- a/cpukit/httpd/ejlex.c +++ b/cpukit/httpd/ejlex.c @@ -1,7 +1,7 @@ /* * ejlex.c -- Ejscript(TM) Lexical Analyser * - * Copyright (c) Go Ahead Software, Inc., 1995-1999 + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -15,7 +15,7 @@ /********************************** Includes **********************************/ -#include "ej.h" +#include "ejIntrn.h" #if UEMF #include "uemf.h" @@ -23,12 +23,16 @@ #include "basic/basicInternal.h" #endif +/********************************** Defines ***********************************/ +#define OCTAL 8 +#define HEX 16 /****************************** Forward Declarations **************************/ static int getLexicalToken(ej_t* ep, int state); static int tokenAddChar(ej_t *ep, int c); static int inputGetc(ej_t* ep); static void inputPutback(ej_t* ep, int c); +static int charConvert(ej_t* ep, int base, int maxDig); /************************************* Code ***********************************/ /* @@ -77,13 +81,13 @@ int ejLexOpenScript(ej_t* ep, char_t *script) if (ringqOpen(&ip->tokbuf, EJ_INC, -1) < 0) { return -1; } - if (ringqOpen(&ip->script, EJ_INC, -1) < 0) { + if (ringqOpen(&ip->script, EJ_SCRIPT_INC, -1) < 0) { return -1; } /* * Put the Ejscript into a ring queue for easy parsing */ - ringqPutstr(&ip->script, script); + ringqPutStr(&ip->script, script); ip->lineNumber = 1; ip->lineLength = 0; @@ -178,6 +182,7 @@ void ejLexFreeInputState(ej_t* ep, ejinput_t* state) { if (state->putBackToken) { bfree(B_L, state->putBackToken); + state->putBackToken = NULL; } } @@ -189,7 +194,7 @@ void ejLexFreeInputState(ej_t* ep, ejinput_t* state) int ejLexGetToken(ej_t* ep, int state) { ep->tid = getLexicalToken(ep, state); - goahead_trace(7, T("ejGetToken: %d, \"%s\"\n"), ep->tid, ep->token); + trace(9, T("ejGetToken: %d, \"%s\"\n"), ep->tid, ep->token); return ep->tid; } @@ -202,7 +207,7 @@ static int getLexicalToken(ej_t* ep, int state) { ringq_t *inq, *tokq; ejinput_t* ip; - int done, tid, c, quote, style, back_quoted, lval, i; + int done, tid, c, quote, style; a_assert(ep); ip = ep->input; @@ -218,7 +223,7 @@ static int getLexicalToken(ej_t* ep, int state) ringqFlush(tokq); if (ip->putBackTokenId > 0) { - ringqPutstr(tokq, ip->putBackToken); + ringqPutStr(tokq, ip->putBackToken); tid = ip->putBackTokenId; ip->putBackTokenId = 0; ep->token = (char_t*) tokq->servp; @@ -385,7 +390,7 @@ static int getLexicalToken(ej_t* ep, int state) inputPutback(ep, c); return TOK_ASSIGNMENT; - case '!': /* "!=" */ + case '!': /* "!=" or "!"*/ if ((c = inputGetc(ep)) < 0) { ejError(ep, T("Syntax Error")); return TOK_ERR; @@ -394,8 +399,9 @@ static int getLexicalToken(ej_t* ep, int state) tokenAddChar(ep, EXPR_NOTEQ); return TOK_EXPR; } - tokenAddChar(ep, COND_NOT); - return TOK_LOGICAL; + inputPutback(ep, c); + tokenAddChar(ep, EXPR_BOOL_COMP); + return TOK_EXPR; case ';': tokenAddChar(ep, c); @@ -428,25 +434,23 @@ static int getLexicalToken(ej_t* ep, int state) ejError(ep, T("Syntax Error")); return TOK_ERR; } - back_quoted = 0; + while (c != quote) { - if (c == '\\' && !back_quoted) { - back_quoted++; - } else if (back_quoted) { - if (gisdigit((char_t) c)) { - lval = 0; - for (i = 0; i < 3; i++) { - if ('0' <= c && c <= '7') { - break; - } - lval = lval * 8 + c; - if ((c = inputGetc(ep)) < 0) { - break; - } - } - c = (int) lval; +/* + * check for escape sequence characters + */ + if (c == '\\') { + c = inputGetc(ep); + + if (gisdigit(c)) { +/* + * octal support, \101 maps to 65 = 'A'. put first char + * back so converter will work properly. + */ + inputPutback(ep, c); + c = charConvert(ep, OCTAL, 3); - } else if (back_quoted) { + } else { switch (c) { case 'n': c = '\n'; break; @@ -459,37 +463,28 @@ static int getLexicalToken(ej_t* ep, int state) case 't': c = '\t'; break; case 'x': - lval = 0; - for (i = 0; i < 2; i++) { - if (! gisxdigit((char_t) c)) { - break; - } - lval = lval * 16 + c; - if ((c = inputGetc(ep)) < 0) { - break; - } - } - c = (int) lval; +/* + * hex support, \x41 maps to 65 = 'A' + */ + c = charConvert(ep, HEX, 2); break; case 'u': - lval = 0; - for (i = 0; i < 4; i++) { - if (! gisxdigit((char_t) c)) { - break; - } - lval = lval * 16 + c; - if ((c = inputGetc(ep)) < 0) { - break; - } - } - c = (int) lval; +/* + * unicode support, \x0401 maps to 65 = 'A' + */ + c = charConvert(ep, HEX, 2); + c = c*16 + charConvert(ep, HEX, 2); + break; case '\'': case '\"': + case '\\': break; + default: + ejError(ep, T("Invalid Escape Sequence")); + return TOK_ERR; } } - back_quoted = 0; if (tokenAddChar(ep, c) < 0) { return TOK_ERR; } @@ -513,7 +508,7 @@ static int getLexicalToken(ej_t* ep, int state) } if ((c = inputGetc(ep)) < 0) break; - } while (gisdigit((char_t) c)); + } while (gisdigit(c)); inputPutback(ep, c); return TOK_LITERAL; @@ -521,21 +516,19 @@ static int getLexicalToken(ej_t* ep, int state) /* * Identifiers or a function names */ - back_quoted = 0; while (1) { - if (c == '\\' && !back_quoted) { - back_quoted++; - } else { - back_quoted = 0; - if (tokenAddChar(ep, c) < 0) { + if (c == '\\') { +/* + * just ignore any \ characters. + */ + } else if (tokenAddChar(ep, c) < 0) { break; - } } if ((c = inputGetc(ep)) < 0) { break; } - if (!back_quoted && (!gisalnum((char_t) c) && c != '$' && - c != '_')) { + if (!gisalnum(c) && c != '$' && c != '_' && + c != '\\') { break; } } @@ -558,13 +551,16 @@ static int getLexicalToken(ej_t* ep, int state) } else if (gstrcmp(ep->token, T("for")) == 0) { return TOK_FOR; } else if (gstrcmp(ep->token, T("return")) == 0) { + if ((c == ';') || (c == '(')) { + inputPutback(ep, c); + } return TOK_RETURN; } } -/* - * skip white space after token to find out whether this is - * a function or not. +/* + * Skip white space after token to find out whether this is + * a function or not. */ while (c == ' ' || c == '\t' || c == '\r' || c == '\n') { if ((c = inputGetc(ep)) < 0) @@ -677,3 +673,41 @@ static void inputPutback(ej_t* ep, int c) } /******************************************************************************/ +/* + * Convert a hex or octal character back to binary, return original char if + * not a hex digit + */ + +static int charConvert(ej_t* ep, int base, int maxDig) +{ + int i, c, lval, convChar; + + lval = 0; + for (i = 0; i < maxDig; i++) { + if ((c = inputGetc(ep)) < 0) { + break; + } +/* + * Initialize to out of range value + */ + convChar = base; + if (gisdigit(c)) { + convChar = c - '0'; + } else if (c >= 'a' && c <= 'f') { + convChar = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + convChar = c - 'A' + 10; + } +/* + * if unexpected character then return it to buffer. + */ + if (convChar >= base) { + inputPutback(ep, c); + break; + } + lval = (lval * base) + convChar; + } + return lval; +} + +/******************************************************************************/ diff --git a/cpukit/httpd/ejparse.c b/cpukit/httpd/ejparse.c index 1734f6f719..8863c069ea 100644 --- a/cpukit/httpd/ejparse.c +++ b/cpukit/httpd/ejparse.c @@ -1,7 +1,7 @@ /* * ejparse.c -- Ejscript(TM) Parser * - * Copyright (c) Go Ahead Software, Inc., 1995-1999 + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -15,7 +15,11 @@ /********************************** Includes **********************************/ -#include "ej.h" +#include "ejIntrn.h" + +#if CE + #include "CE/wincompat.h" +#endif /********************************** Local Data ********************************/ @@ -24,11 +28,14 @@ int ejMax = -1; /* Maximum size of */ /****************************** Forward Declarations **************************/ +#ifndef B_STATS +#define setString(a,b,c) setstring(b,c) +#endif + static ej_t *ejPtr(int eid); static void clearString(char_t **ptr); -static void setString(char_t **ptr, char_t *s); +static void setString(B_ARGS_DEC, char_t **ptr, char_t *s); static void appendString(char_t **ptr, char_t *s); -static void freeVar(sym_t* sp); static int parse(ej_t *ep, int state, int flags); static int parseStmt(ej_t *ep, int state, int flags); static int parseDeclaration(ej_t *ep, int state, int flags); @@ -39,6 +46,7 @@ static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs); static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs); static int evalFunction(ej_t *ep); static void freeFunc(ejfunc_t *func); +static void ejRemoveNewlines(ej_t *ep, int state); /************************************* Code ***********************************/ /* @@ -57,8 +65,8 @@ int ejOpenEngine(sym_fd_t variables, sym_fd_t functions) ep->eid = eid; /* - * Create a top level symbol table if one is not provided for variables and - * functions. Variables may create other symbol tables for block level + * Create a top level symbol table if one is not provided for variables and + * functions. Variables may create other symbol tables for block level * declarations so we use hAlloc to manage a list of variable tables. */ if ((vid = hAlloc((void***) &ep->variables)) < 0) { @@ -72,7 +80,6 @@ int ejOpenEngine(sym_fd_t variables, sym_fd_t functions) if (variables == -1) { ep->variables[vid] = symOpen(64) + EJ_OFFSET; ep->flags |= FLAGS_VARIABLES; - } else { ep->variables[vid] = variables + EJ_OFFSET; } @@ -122,36 +129,27 @@ void ejCloseEngine(int eid) ejLexClose(ep); - if (ep->flags & FLAGS_VARIABLES) { - for (i = ep->variableMax - 1; i >= 0; i--) { - symClose(ep->variables[i] - EJ_OFFSET, freeVar); - ep->variableMax = hFree((void***) &ep->variables, i); + for (i = ep->variableMax - 1; i >= 0; i--) { + if (ep->flags & FLAGS_VARIABLES) { + symClose(ep->variables[i] - EJ_OFFSET); } + ep->variableMax = hFree((void***) &ep->variables, i); } + if (ep->flags & FLAGS_FUNCTIONS) { - symClose(ep->functions, freeVar); + symClose(ep->functions); } ejMax = hFree((void***) &ejHandles, ep->eid); bfree(B_L, ep); } -/******************************************************************************/ -/* - * Callback from symClose. Free the variable. - */ - -static void freeVar(sym_t* sp) -{ - valueFree(&sp->content); -} - +#ifndef __NO_EJ_FILE /******************************************************************************/ /* * Evaluate a Ejscript file */ -#if DEV char_t *ejEvalFile(int eid, char_t *path, char_t **emsg) { gstat_t sbuf; @@ -165,6 +163,7 @@ char_t *ejEvalFile(int eid, char_t *path, char_t **emsg) if (emsg) { *emsg = NULL; } + if ((ep = ejPtr(eid)) == NULL) { return NULL; } @@ -173,26 +172,30 @@ char_t *ejEvalFile(int eid, char_t *path, char_t **emsg) ejError(ep, T("Bad handle %d"), eid); return NULL; } + if (gstat(path, &sbuf) < 0) { - close(fd); + gclose(fd); ejError(ep, T("Cant stat %s"), path); return NULL; } + if ((fileBuf = balloc(B_L, sbuf.st_size + 1)) == NULL) { - close(fd); + gclose(fd); ejError(ep, T("Cant malloc %d"), sbuf.st_size); return NULL; } - if (read(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) { - close(fd); + + if (gread(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) { + gclose(fd); bfree(B_L, fileBuf); ejError(ep, T("Error reading %s"), path); return NULL; } + fileBuf[sbuf.st_size] = '\0'; - close(fd); + gclose(fd); - if ((script = ballocAscToUni(fileBuf)) == NULL) { + if ((script = ballocAscToUni(fileBuf, sbuf.st_size)) == NULL) { bfree(B_L, fileBuf); ejError(ep, T("Cant malloc %d"), sbuf.st_size + 1); return NULL; @@ -204,29 +207,31 @@ char_t *ejEvalFile(int eid, char_t *path, char_t **emsg) bfree(B_L, script); return rs; } -#endif +#endif /* __NO_EJ_FILE */ /******************************************************************************/ /* * Create a new variable scope block so that consecutive ejEval calls may * be made with the same varible scope. This space MUST be closed with - * ejCloseBlock when the evaluations are complete. + * ejCloseBlock when the evaluations are complete. */ int ejOpenBlock(int eid) { ej_t *ep; - int vid; + int vid; if((ep = ejPtr(eid)) == NULL) { return -1; - } + } + if ((vid = hAlloc((void***) &ep->variables)) < 0) { return -1; } + if (vid >= ep->variableMax) { ep->variableMax = vid + 1; - } + } ep->variables[vid] = symOpen(64) + EJ_OFFSET; return vid; @@ -241,15 +246,16 @@ int ejOpenBlock(int eid) int ejCloseBlock(int eid, int vid) { ej_t *ep; - + if((ep = ejPtr(eid)) == NULL) { return -1; } - symClose(ep->variables[vid] - EJ_OFFSET, freeVar); + symClose(ep->variables[vid] - EJ_OFFSET); ep->variableMax = hFree((void***) &ep->variables, vid); - return 0; + return 0; } + /******************************************************************************/ /* * Create a new variable scope block and evaluate a script. All variables @@ -263,7 +269,7 @@ char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg) a_assert(script); - vid = ejOpenBlock(eid); + vid = ejOpenBlock(eid); returnVal = ejEval(eid, script, emsg); ejCloseBlock(eid, vid); @@ -272,7 +278,7 @@ char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg) /******************************************************************************/ /* - * Parse and evaluate a Ejscript. The caller may provide a symbol table to + * Parse and evaluate a Ejscript. The caller may provide a symbol table to * use for variables and function definitions. Return char_t pointer on * success otherwise NULL pointer is returned. */ @@ -282,17 +288,21 @@ char_t *ejEval(int eid, char_t *script, char_t **emsg) ej_t *ep; ejinput_t *oldBlock; int state; - + void *endlessLoopTest; + int loopCounter; + + a_assert(script); if (emsg) { *emsg = NULL; - } + } + if ((ep = ejPtr(eid)) == NULL) { return NULL; } - setString(&ep->result, T("")); + setString(B_L, &ep->result, T("")); /* * Allocate a new evaluation block, and save the old one @@ -303,8 +313,29 @@ char_t *ejEval(int eid, char_t *script, char_t **emsg) /* * Do the actual parsing and evaluation */ + loopCounter = 0; + endlessLoopTest = NULL; + do { state = parse(ep, STATE_BEGIN, FLAGS_EXE); + + if (state == STATE_RET) { + state = STATE_EOF; + } +/* + * prevent parser from going into infinite loop. If parsing the same + * line 10 times then fail and report Syntax error. Most normal error + * are caught in the parser itself. + */ + if (endlessLoopTest == ep->input->script.servp) { + if (loopCounter++ > 10) { + state = STATE_ERR; + ejError(ep, T("Syntax error")); + } + } else { + endlessLoopTest = ep->input->script.servp; + loopCounter = 0; + } } while (state != STATE_EOF && state != STATE_ERR); ejLexCloseScript(ep); @@ -324,9 +355,11 @@ char_t *ejEval(int eid, char_t *script, char_t **emsg) if (state == STATE_EOF) { return ep->result; } + if (state == STATE_ERR) { return NULL; } + return ep->result; } @@ -344,12 +377,25 @@ static int parse(ej_t *ep, int state, int flags) * Any statement, function arguments or conditional expressions */ case STATE_STMT: + if ((state = parseStmt(ep, state, flags)) != STATE_STMT_DONE && + state != STATE_EOF && state != STATE_STMT_BLOCK_DONE && + state != STATE_RET) { + state = STATE_ERR; + } + break; + case STATE_DEC: - state = parseStmt(ep, state, flags); + if ((state = parseStmt(ep, state, flags)) != STATE_DEC_DONE && + state != STATE_EOF) { + state = STATE_ERR; + } break; case STATE_EXPR: - state = parseStmt(ep, state, flags); + if ((state = parseStmt(ep, state, flags)) != STATE_EXPR_DONE && + state != STATE_EOF) { + state = STATE_ERR; + } break; /* @@ -397,9 +443,9 @@ static int parseStmt(ej_t *ep, int state, int flags) ejfunc_t func; ejfunc_t *saveFunc; ejinput_t condScript, endScript, bodyScript, incrScript; - char_t *value; - char_t *identifier; + char_t *value, *identifier; int done, expectSemi, thenFlags, elseFlags, tid, cond, forFlags; + int ejVarType; a_assert(ep); @@ -451,21 +497,22 @@ static int parseStmt(ej_t *ep, int state, int flags) * This could either be a reference to a variable or an assignment */ identifier = NULL; - setString(&identifier, ep->token); + setString(B_L, &identifier, ep->token); /* * Peek ahead to see if this is an assignment */ tid = ejLexGetToken(ep, state); if (tid == TOK_ASSIGNMENT) { if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) { + clearString(&identifier); goto error; } if (flags & FLAGS_EXE) { if ( state == STATE_DEC ) { ejSetLocalVar(ep->eid, identifier, ep->result); - } - else { - if (ejGetVar(ep->eid, identifier, &value) > 0) { + } else { + ejVarType = ejGetVar(ep->eid, identifier, &value); + if (ejVarType > 0) { ejSetLocalVar(ep->eid, identifier, ep->result); } else { ejSetGlobalVar(ep->eid, identifier, ep->result); @@ -475,17 +522,23 @@ static int parseStmt(ej_t *ep, int state, int flags) } else if (tid == TOK_INC_DEC ) { value = NULL; - if ( flags & FLAGS_EXE ) { - if (ejGetVar(ep->eid, identifier, &value) < 0) { + if (flags & FLAGS_EXE) { + ejVarType = ejGetVar(ep->eid, identifier, &value); + if (ejVarType < 0) { ejError(ep, T("Undefined variable %s\n"), identifier); goto error; } - setString(&ep->result, value); + setString(B_L, &ep->result, value); if (evalExpr(ep, value, (int) *ep->token, T("1")) < 0) { state = STATE_ERR; break; } - ejSetGlobalVar(ep->eid, identifier, ep->result); + + if (ejVarType > 0) { + ejSetLocalVar(ep->eid, identifier, ep->result); + } else { + ejSetGlobalVar(ep->eid, identifier, ep->result); + } } } else { @@ -495,7 +548,7 @@ static int parseStmt(ej_t *ep, int state, int flags) value = NULL; if (state == STATE_DEC) { if (ejGetVar(ep->eid, identifier, &value) > 0) { - ejError(ep, T("Variable already declared"), + ejError(ep, T("Variable already declared"), identifier); clearString(&identifier); goto error; @@ -504,14 +557,14 @@ static int parseStmt(ej_t *ep, int state, int flags) } else { if ( flags & FLAGS_EXE ) { if (ejGetVar(ep->eid, identifier, &value) < 0) { - ejError(ep, T("Undefined variable %s\n"), + ejError(ep, T("Undefined variable %s\n"), identifier); clearString(&identifier); goto error; } } } - setString(&ep->result, value); + setString(B_L, &ep->result, value); ejLexPutbackToken(ep, tid, ep->token); } clearString(&identifier); @@ -526,7 +579,7 @@ static int parseStmt(ej_t *ep, int state, int flags) /* * Set the result to the literal (number or string constant) */ - setString(&ep->result, ep->token); + setString(B_L, &ep->result, ep->token); if (state == STATE_STMT) { expectSemi++; } @@ -541,10 +594,10 @@ static int parseStmt(ej_t *ep, int state, int flags) saveFunc = ep->func; } memset(&func, 0, sizeof(ejfunc_t)); - setString(&func.fname, ep->token); + setString(B_L, &func.fname, ep->token); ep->func = &func; - setString(&ep->result, T("")); + setString(B_L, &ep->result, T("")); if (ejLexGetToken(ep, state) != TOK_LPAREN) { freeFunc(&func); goto error; @@ -604,11 +657,20 @@ static int parseStmt(ej_t *ep, int state, int flags) elseFlags = flags; } /* - * Process the "then" case + * Process the "then" case. Allow for RETURN statement */ - if (parse(ep, STATE_STMT, thenFlags) != STATE_STMT_DONE) { + switch (parse(ep, STATE_STMT, thenFlags)) { + case STATE_RET: + return STATE_RET; + case STATE_STMT_DONE: + break; + default: goto error; } +/* + * check to see if there is an "else" case + */ + ejRemoveNewlines(ep, state); tid = ejLexGetToken(ep, state); if (tid != TOK_ELSE) { ejLexPutbackToken(ep, tid, ep->token); @@ -616,9 +678,14 @@ static int parseStmt(ej_t *ep, int state, int flags) break; } /* - * Process the "else" case + * Process the "else" case. Allow for return. */ - if (parse(ep, STATE_STMT, elseFlags) != STATE_STMT_DONE) { + switch (parse(ep, STATE_STMT, elseFlags)) { + case STATE_RET: + return STATE_RET; + case STATE_STMT_DONE: + break; + default: goto error; } done++; @@ -650,7 +717,7 @@ static int parseStmt(ej_t *ep, int state, int flags) } /* - * The first time through, we save the current input context just + * The first time through, we save the current input context just * to each step: prior to the conditional, the loop increment and the * loop body. */ @@ -663,7 +730,7 @@ static int parseStmt(ej_t *ep, int state, int flags) if (ejLexGetToken(ep, state) != TOK_SEMI) { goto error; } - + /* * Don't execute the loop increment statement or the body first time */ @@ -677,7 +744,7 @@ static int parseStmt(ej_t *ep, int state, int flags) } /* - * Parse the body and remember the end of the body script + * Parse the body and remember the end of the body script */ ejLexSaveInputState(ep, &bodyScript); if (parse(ep, STATE_STMT, forFlags) != STATE_STMT_DONE) { @@ -693,7 +760,13 @@ static int parseStmt(ej_t *ep, int state, int flags) * Evaluate the body */ ejLexRestoreInputState(ep, &bodyScript); - if (parse(ep, STATE_STMT, flags) != STATE_STMT_DONE) { + + switch (parse(ep, STATE_STMT, flags)) { + case STATE_RET: + return STATE_RET; + case STATE_STMT_DONE: + break; + default: goto error; } /* @@ -760,6 +833,13 @@ static int parseStmt(ej_t *ep, int state, int flags) state = parse(ep, STATE_STMT, flags); } while (state == STATE_STMT_DONE); +/* + * Allow return statement. + */ + if (state == STATE_RET) { + return state; + } + if (ejLexGetToken(ep, state) != TOK_RBRACE) { goto error; } @@ -779,7 +859,7 @@ static int parseStmt(ej_t *ep, int state, int flags) if (flags & FLAGS_EXE) { while ( ejLexGetToken(ep, state) != TOK_EOF ); done++; - return STATE_EOF; + return STATE_RET; } break; } @@ -794,10 +874,7 @@ static int parseStmt(ej_t *ep, int state, int flags) /* * Skip newline after semi-colon */ - tid = ejLexGetToken(ep, state); - if (tid != TOK_NEWLINE) { - ejLexPutbackToken(ep, tid, ep->token); - } + ejRemoveNewlines(ep, state); } /* @@ -810,6 +887,7 @@ doneParse: ejLexFreeInputState(ep, &endScript); ejLexFreeInputState(ep, &bodyScript); } + if (state == STATE_STMT) { return STATE_STMT_DONE; } else if (state == STATE_DEC) { @@ -928,7 +1006,7 @@ static int parseCond(ej_t *ep, int state, int flags) a_assert(ep); - setString(&ep->result, T("")); + setString(B_L, &ep->result, T("")); rhs = lhs = NULL; operator = 0; @@ -942,14 +1020,15 @@ static int parseCond(ej_t *ep, int state, int flags) state = STATE_ERR; break; } + if (operator > 0) { - setString(&rhs, ep->result); + setString(B_L, &rhs, ep->result); if (evalCond(ep, lhs, operator, rhs) < 0) { state = STATE_ERR; break; } } - setString(&lhs, ep->result); + setString(B_L, &lhs, ep->result); tid = ejLexGetToken(ep, state); if (tid == TOK_LOGICAL) { @@ -969,6 +1048,7 @@ static int parseCond(ej_t *ep, int state, int flags) if (lhs) { bfree(B_L, lhs); } + if (rhs) { bfree(B_L, rhs); } @@ -987,33 +1067,46 @@ static int parseExpr(ej_t *ep, int state, int flags) a_assert(ep); - setString(&ep->result, T("")); + setString(B_L, &ep->result, T("")); rhs = lhs = NULL; rel = 0; + tid = 0; do { /* * This loop will handle an entire expression list. We call parse * to evalutate each term which returns the result in ep->result. */ - state = parse(ep, STATE_EXPR, flags); - if (state != STATE_EXPR_DONE) { - state = STATE_ERR; - break; - } - if (rel > 0) { - setString(&rhs, ep->result); - if (evalExpr(ep, lhs, rel, rhs) < 0) { + if (tid == TOK_LOGICAL) { + if ((state = parse(ep, STATE_RELEXP, flags)) != STATE_RELEXP_DONE) { + state = STATE_ERR; + break; + } + } else { + if ((state = parse(ep, STATE_EXPR, flags)) != STATE_EXPR_DONE) { state = STATE_ERR; break; } } - setString(&lhs, ep->result); - if ((tid = ejLexGetToken(ep, state)) == TOK_EXPR) { - rel = (int) *ep->token; + if (rel > 0) { + setString(B_L, &rhs, ep->result); + if (tid == TOK_LOGICAL) { + if (evalCond(ep, lhs, rel, rhs) < 0) { + state = STATE_ERR; + break; + } + } else { + if (evalExpr(ep, lhs, rel, rhs) < 0) { + state = STATE_ERR; + break; + } + } + } + setString(B_L, &lhs, ep->result); - } else if (tid == TOK_INC_DEC) { + if ((tid = ejLexGetToken(ep, state)) == TOK_EXPR || + tid == TOK_INC_DEC || tid == TOK_LOGICAL) { rel = (int) *ep->token; } else { @@ -1023,10 +1116,14 @@ static int parseExpr(ej_t *ep, int state, int flags) } while (state == STATE_EXPR_DONE); - if (rhs) + if (rhs) { bfree(B_L, rhs); - if (lhs) + } + + if (lhs) { bfree(B_L, lhs); + } + return state; } @@ -1045,7 +1142,7 @@ static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs) a_assert(rel > 0); lval = 0; - if (gisdigit(*lhs) && gisdigit(*rhs)) { + if (gisdigit((int)*lhs) && gisdigit((int)*rhs)) { l = gatoi(lhs); r = gatoi(rhs); switch (rel) { @@ -1060,15 +1157,15 @@ static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs) return -1; } } else { - if (!gisdigit(*lhs)) { + if (!gisdigit((int)*lhs)) { ejError(ep, T("Conditional must be numeric"), lhs); } else { ejError(ep, T("Conditional must be numeric"), rhs); } } - + stritoa(lval, buf, sizeof(buf)); - setString(&ep->result, buf); + setString(B_L, &ep->result, buf); return 0; } @@ -1091,19 +1188,21 @@ static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs) */ numeric = 1; for (cp = lhs; *cp; cp++) { - if (!gisdigit(*cp)) { + if (!gisdigit((int)*cp)) { numeric = 0; break; } } + if (numeric) { for (cp = rhs; *cp; cp++) { - if (!gisdigit(*cp)) { + if (!gisdigit((int)*cp)) { numeric = 0; break; } } } + if (numeric) { l = gatoi(lhs); r = gatoi(rhs); @@ -1161,6 +1260,9 @@ static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs) case EXPR_GREATEREQ: lval = (l >= r) ? 1 : 0; break; + case EXPR_BOOL_COMP: + lval = (r == 0) ? 1 : 0; + break; default: ejError(ep, T("Bad operator %d"), rel); return -1; @@ -1205,7 +1307,7 @@ static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs) } stritoa(lval, buf, sizeof(buf)); - setString(&ep->result, buf); + setString(B_L, &ep->result, buf); return 0; } @@ -1223,13 +1325,14 @@ static int evalFunction(ej_t *ep) ejError(ep, T("Undefined procedure %s"), ep->func->fname); return -1; } + fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer; if (fn == NULL) { ejError(ep, T("Undefined procedure %s"), ep->func->fname); return -1; } - return (*fn)(ep->eid, (void*) ep->userHandle, ep->func->nArgs, + return (*fn)(ep->eid, (void*) ep->userHandle, ep->func->nArgs, ep->func->args); } @@ -1250,12 +1353,11 @@ void ejError(ej_t* ep, char_t* fmt, ...) va_start(args, fmt); msgbuf = NULL; - gvsnprintf(&msgbuf, E_MAX_ERROR, fmt, args); + fmtValloc(&msgbuf, E_MAX_ERROR, fmt, args); va_end(args); if (ep && ip) { - errbuf = NULL; - gsnprintf(&errbuf, E_MAX_ERROR, T("%s\n At line %d, line => \n\n%s\n"), + fmtAlloc(&errbuf, E_MAX_ERROR, T("%s\n At line %d, line => \n\n%s\n"), msgbuf, ip->lineNumber, ip->line); bfreeSafe(B_L, ep->error); ep->error = errbuf; @@ -1283,14 +1385,14 @@ static void clearString(char_t **ptr) * Set a string value */ -static void setString(char_t **ptr, char_t *s) +static void setString(B_ARGS_DEC, char_t **ptr, char_t *s) { a_assert(ptr); if (*ptr) { - bfree(B_L, *ptr); + bfree(B_ARGS, *ptr); } - *ptr = bstrdup(B_L, s); + *ptr = bstrdup(B_ARGS, s); } /******************************************************************************/ @@ -1319,7 +1421,7 @@ static void appendString(char_t **ptr, char_t *s) * Define a function */ -int ejSetGlobalFunction(int eid, char_t *name, +int ejSetGlobalFunction(int eid, char_t *name, int (*fn)(int eid, void *handle, int argc, char_t **argv)) { ej_t *ep; @@ -1335,7 +1437,7 @@ int ejSetGlobalFunction(int eid, char_t *name, * Define a function directly into the function symbol table. */ -int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, +int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, int (*fn)(int eid, void *handle, int argc, char_t **argv)) { if (symEnter(functions, name, valueInteger((long) fn), 0) == NULL) { @@ -1344,6 +1446,21 @@ int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, return 0; } +/******************************************************************************/ +/* + * Remove ("undefine") a function + */ + +int ejRemoveGlobalFunction(int eid, char_t *name) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return symDelete(ep->functions, name); +} + /******************************************************************************/ /* * Get a function definition @@ -1358,6 +1475,7 @@ void *ejGetGlobalFunction(int eid, char_t *name) if ((ep = ejPtr(eid)) == NULL) { return NULL; } + if ((sp = symLookup(ep->functions, name)) != NULL) { fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer; return (void*) fn; @@ -1367,7 +1485,7 @@ void *ejGetGlobalFunction(int eid, char_t *name) /******************************************************************************/ /* - * Utility routine to crack Ejscript arguments. Return the number of args + * Utility routine to crack Ejscript arguments. Return the number of args * seen. This routine only supports %s and %d type args. * * Typical usage: @@ -1477,7 +1595,7 @@ void ejSetResult(int eid, char_t *s) if ((ep = ejPtr(eid)) == NULL) { return; } - setString(&ep->result, s); + setString(B_L, &ep->result, s); } /******************************************************************************/ @@ -1511,6 +1629,7 @@ void ejSetVar(int eid, char_t *var, char_t *value) if ((ep = ejPtr(eid)) == NULL) { return; } + if (value == NULL) { v = valueString(value, 0); } else { @@ -1535,6 +1654,7 @@ void ejSetLocalVar(int eid, char_t *var, char_t *value) if ((ep = ejPtr(eid)) == NULL) { return; } + if (value == NULL) { v = valueString(value, 0); } else { @@ -1545,7 +1665,7 @@ void ejSetLocalVar(int eid, char_t *var, char_t *value) /******************************************************************************/ /* - * Set a global variable. Note: a variable with a value of NULL means + * Set a global variable. Note: a variable with a value of NULL means * declared but undefined. The value is defined in the global variable frame. */ @@ -1559,6 +1679,7 @@ void ejSetGlobalVar(int eid, char_t *var, char_t *value) if ((ep = ejPtr(eid)) == NULL) { return; } + if (value == NULL) { v = valueString(value, 0); } else { @@ -1585,19 +1706,19 @@ int ejGetVar(int eid, char_t *var, char_t **value) return -1; } - for (i = ep->variableMax - 1; i >= 0; i--) { - if ((sp = symLookup(ep->variables[i] - EJ_OFFSET, var)) == NULL) { - continue; + i = ep->variableMax - 1; + if ((sp = symLookup(ep->variables[i] - EJ_OFFSET, var)) == NULL) { + i = 0; + if ((sp = symLookup(ep->variables[0] - EJ_OFFSET, var)) == NULL) { + return -1; } - a_assert(sp->content.type == string); - *value = sp->content.value.string; - return i; } - return -1; + a_assert(sp->content.type == string); + *value = sp->content.value.string; + return i; } /******************************************************************************/ -#if UNUSED /* * Get the variable symbol table */ @@ -1609,9 +1730,9 @@ sym_fd_t ejGetVariableTable(int eid) if ((ep = ejPtr(eid)) == NULL) { return -1; } - return ep->variables; + return *ep->variables; } -#endif + /******************************************************************************/ /* * Get the functions symbol table @@ -1640,6 +1761,7 @@ static void freeFunc(ejfunc_t *func) bfree(B_L, func->args[i]); func->nArgs = hFree((void***) &func->args, i); } + if (func->fname) { bfree(B_L, func->fname); func->fname = NULL; @@ -1647,7 +1769,7 @@ static void freeFunc(ejfunc_t *func) } /******************************************************************************/ -/* +/* * Get Ejscript pointer */ @@ -1663,3 +1785,18 @@ static ej_t *ejPtr(int eid) } /******************************************************************************/ +/* + * This function removes any new lines. Used for else cases, etc. + */ +static void ejRemoveNewlines(ej_t *ep, int state) +{ + int tid; + + do { + tid = ejLexGetToken(ep, state); + } while (tid == TOK_NEWLINE); + + ejLexPutbackToken(ep, tid, ep->token); +} + +/******************************************************************************/ diff --git a/cpukit/httpd/emfdb.c b/cpukit/httpd/emfdb.c new file mode 100644 index 0000000000..a316c1a454 --- /dev/null +++ b/cpukit/httpd/emfdb.c @@ -0,0 +1,1050 @@ +/* + * emfdb.c -- EMF database compatability functions for GoAhead WebServer. + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ + */ + +/******************************** Description *********************************/ +/* + * Textfile-based database support for WebServer 2.1. + */ + +/********************************* Includes ***********************************/ + +#include "emfdb.h" +#include "wsIntrn.h" + +/********************************* Defines ************************************/ + +#define KEYWORD_TABLE T("TABLE") +#define KEYWORD_ROW T("ROW") + +/*********************************** Locals ***********************************/ + +/* + * Variable to support the basicSet and basicGet functions. + */ + +static char_t *basicProdDir = NULL; +static char_t *basicDefaultDir = T("."); /* Default set to current */ + +/* + * hAlloc chain list of table schemas to be closed + */ + +static int dbMaxTables = 0; +static dbTable_t **dbListTables = NULL; + +/****************************** Forward Declarations **************************/ + +static int crack(char_t *buf, char_t **key, char_t **val); +static char_t *trim(char_t *str); +static int GetColumnIndex(int tid, char_t *colName); + +/******************************************************************************/ +/* + * Add a schema to the module-internal schema database + */ + +int dbRegisterDBSchema(dbTable_t *pTableRegister) +{ + dbTable_t *pTable; + int tid; + + a_assert(pTableRegister); + + trace(4, T("DB: Registering database table <%s>\n"), + pTableRegister->name); + +/* + * Bump up the size of the table array + */ + tid = hAllocEntry((void***) &dbListTables, + &dbMaxTables, sizeof(dbTable_t)); + +/* + * Copy the table schema to the last spot in schema array + */ + a_assert(dbListTables); + pTable = dbListTables[tid]; + a_assert(pTable); + +/* + * Copy the name of the table + */ + pTable->name = bstrdup(B_L, pTableRegister->name); + +/* + * Copy the number of columns + */ + pTable->nColumns = pTableRegister->nColumns; + +/* + * Copy the column definitions + */ + if (pTable->nColumns > 0) { + int i; + pTable->columnNames = balloc(B_L, sizeof(char_t *) * pTable->nColumns); + pTable->columnTypes = balloc(B_L, sizeof(int *) * pTable->nColumns); + + for (i = 0; (i < pTableRegister->nColumns); i++) { + pTable->columnNames[i] = + bstrdup(B_L, pTableRegister->columnNames[i]); + pTable->columnTypes[i] = pTableRegister->columnTypes[i]; + } + + } else { + pTable->columnNames = NULL; + pTable->columnTypes = NULL; + } + +/* + * Zero out the table's data (very important!) + */ + pTable->nRows = 0; + pTable->rows = NULL; + + return 0; +} + +/******************************************************************************/ +/* + * This is provided for compatibility with EMF. Tables are "registered" + * with staticly defined schemas. There is only one did in this package: 0. + */ + +int dbOpen(char_t *tablename, char_t *filename, + int (*gettime)(int did), int flags) +{ + basicProdDir = NULL; + basicDefaultDir = T("."); + dbMaxTables = 0; + dbListTables = NULL; + return 0; +} + +/******************************************************************************/ +/* + * Delete all the rows of the tables, and all of the tables + */ + +void dbClose(int did) +{ + int table, column; + dbTable_t *pTable; + +/* + * Before doing anything, delete all the contents of the database + */ + dbZero(did); + +/* + * Now delete the tables + */ + for (table = 0; table < dbMaxTables; table++) { + pTable = dbListTables[table]; + + if (pTable != NULL) { +/* + * Delete the table schema + */ + if (pTable->nColumns) { + for (column = 0; column < pTable->nColumns; column++) { + bfreeSafe(B_L, pTable->columnNames[column]); + } + bfreeSafe(B_L, pTable->columnNames); + bfreeSafe(B_L, pTable->columnTypes); + } +/* + * Delete the table name + */ + bfreeSafe(B_L, pTable->name); +/* + * Free the table + */ + bfreeSafe(B_L, pTable); + hFree((void ***) &dbListTables, table); + } + } + + if (dbListTables) { + bfree(B_L, dbListTables); + } + +/* + * Set the global table list to a safe value + */ + dbListTables = NULL; + dbMaxTables = 0; +} + + +/******************************************************************************/ +/* + * Delete all the data records in all tables + */ + +void dbZero(int did) +{ + int table, row, column, nRows, nColumns; + int *pRow; + dbTable_t *pTable; + +/* + * Delete all data from all tables + */ + for (table = 0; table < dbMaxTables; table++) { + pTable = dbListTables[table]; +/* + * Delete the row data contained within the schema + */ + if (pTable) { + nColumns = pTable->nColumns; + nRows = pTable->nRows; + for (row = 0; row < nRows; row++) { + pRow = pTable->rows[row]; + if (pRow) { +/* + * Only delete the contents of rows not previously deleted! + */ + for (column = 0; column < nColumns; column++) { + if (pTable->columnTypes[column] == T_STRING) { + bfreeSafe(B_L, (char_t *)(pRow[column])); + pRow[column] = (int)NULL; + } + } + + bfreeSafe(B_L, pRow); + hFree((void ***) &pTable->rows, row); + } + } + + pTable->rows = NULL; + pTable->nRows = 0; + } + } +} + +/******************************************************************************/ +/* + * Find the a row in the table with the given string in the given column + */ + +int dbSearchStr(int did, char_t *tablename, + char_t *colName, char_t *value, int flags) +{ + int tid, nRows, nColumns, column; + dbTable_t *pTable; + + a_assert(tablename); + a_assert(colName); + a_assert(value); + + tid = dbGetTableId(0, tablename); + a_assert(tid >= 0); + + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + pTable = dbListTables[tid]; + } else { + return DB_ERR_TABLE_NOT_FOUND; + } + + nColumns = pTable->nColumns; + nRows = pTable->nRows; + column = GetColumnIndex(tid, colName); + a_assert (column >= 0); + + if (column >= 0) { + char_t *compareVal; + int row, *pRow; +/* + * Scan through rows until we find a match. + * Note that some of these rows may be deleted! + */ + row = 0; + while (row < nRows) { + pRow = pTable->rows[row]; + if (pRow) { + compareVal = (char_t *)(pRow[column]); + if (compareVal && (gstrcmp(compareVal, value) == 0)) { + return row; + } + } + row++; + } + } else { +/* + * Return -2 if search column was not found + */ + trace(3, T("DB: Unable to find column <%s> in table <%s>\n"), + colName, tablename); + return DB_ERR_COL_NOT_FOUND; + } + + return -1; +} + +/******************************************************************************/ +/* + * Add a new row to the given table. Return the new row ID. + */ + +int dbAddRow(int did, char_t *tablename) +{ + int tid, size; + dbTable_t *pTable; + + a_assert(tablename); + + tid = dbGetTableId(0, tablename); + a_assert(tid >= 0); + + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + pTable = dbListTables[tid]; + } else { + return DB_ERR_TABLE_NOT_FOUND; + } + + a_assert(pTable); + + if (pTable) { + trace(5, T("DB: Adding a row to table <%s>\n"), tablename); + + size = pTable->nColumns * max(sizeof(int), sizeof(char_t *)); + return hAllocEntry((void***) &(pTable->rows), &(pTable->nRows), size); + } + + return -1; +} + +/******************************************************************************/ +/* + * Delete a row in the table. + */ + +int dbDeleteRow(int did, char_t *tablename, int row) +{ + int tid, nColumns, nRows; + dbTable_t *pTable; + + a_assert(tablename); + tid = dbGetTableId(0, tablename); + a_assert(tid >= 0); + + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + pTable = dbListTables[tid]; + } else { + return DB_ERR_TABLE_NOT_FOUND; + } + + nColumns = pTable->nColumns; + nRows = pTable->nRows; + + if ((row >= 0) && (row < nRows)) { + int *pRow = pTable->rows[row]; + + if (pRow) { + int column = 0; +/* + * Free up any allocated strings + */ + while (column < nColumns) { + if (pRow[column] && + (pTable->columnTypes[column] == T_STRING)) { + bfree(B_L, (char_t *)pRow[column]); + } + + column++; + } +/* + * Zero out the row for safety + */ + memset(pRow, 0, nColumns * max(sizeof(int), sizeof(char_t *))); + + bfreeSafe(B_L, pRow); + pTable->nRows = hFree((void ***)&pTable->rows, row); + trace(5, T("DB: Deleted row <%d> from table <%s>\n"), + row, tablename); + } + return 0; + } else { + trace(3, T("DB: Unable to delete row <%d> from table <%s>\n"), + row, tablename); + } + + return -1; +} + +/*****************************************************************************/ +/* + * Grow the rows in the table to the nominated size. + */ + +int dbSetTableNrow(int did, char_t *tablename, int nNewRows) +{ + int nRet, tid, nRows, nColumns; + dbTable_t *pTable; + + a_assert(tablename); + tid = dbGetTableId(0, tablename); + a_assert(tid >= 0) ; + + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + pTable = dbListTables[tid]; + } else { + return DB_ERR_TABLE_NOT_FOUND; + } + + nRet = -1; + + a_assert(pTable); + if (pTable) { + nColumns = pTable->nColumns; + nRows = pTable->nRows; + nRet = 0; + + if (nRows >= nNewRows) { +/* + * If number of rows already allocated exceeds requested number, do nothing + */ + trace(4, T("DB: Ignoring row set to <%d> in table <%s>\n"), + nNewRows, tablename); + } else { + trace(4, T("DB: Setting rows to <%d> in table <%s>\n"), + nNewRows, tablename); + while (pTable->nRows < nNewRows) { + if (dbAddRow(did, tablename) < 0) { + return -1; + } + } + } + } + + return nRet; +} + +/******************************************************************************/ +/* + * Return the number of rows in the given table + */ + +int dbGetTableNrow(int did, char_t *tablename) +{ + int tid; + + a_assert(tablename); + tid = dbGetTableId(did, tablename); + + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + return (dbListTables[tid])->nRows; + } else { + return -1; + } +} + +/******************************************************************************/ +/* + * Do table driven read of the database + */ + +int dbReadInt(int did, char_t *table, char_t *column, int row, int *returnValue) +{ + int colIndex, *pRow, tid; + dbTable_t *pTable; + + a_assert(table); + a_assert(column); + a_assert(returnValue); + + tid = dbGetTableId(0, table); + a_assert(tid >= 0); + +/* + * Return -6 if table is not found + */ + if (tid < 0) { + return DB_ERR_TABLE_NOT_FOUND; + } + +/* + * Return -7 if table id has been deleted + */ + pTable = dbListTables[tid]; + if (pTable == NULL) { + return DB_ERR_TABLE_DELETED; + } + + a_assert(row >= 0); + + if ((row >= 0) && (row < pTable->nRows)) { + colIndex = GetColumnIndex(tid, column); + a_assert(colIndex >= 0); + + if (colIndex >= 0) { + pRow = pTable->rows[row]; + if (pRow) { + *returnValue = pRow[colIndex]; + return 0; + } + return DB_ERR_ROW_DELETED; + } + return DB_ERR_COL_NOT_FOUND; + } + + return DB_ERR_ROW_NOT_FOUND; +} + +/******************************************************************************/ +/* + * dbReadStr calls dbReadInt to do table driven read of database + */ + +int dbReadStr(int did, char_t *table, char_t *column, int row, + char_t **returnValue) +{ + return dbReadInt(did, table, column, row, (int *)returnValue); +} + +/******************************************************************************/ +/* + * The dbWriteInt function writes a value into a table at a given row and + * column. The existence of the row and column is verified before the + * write. 0 is returned on succes, -1 is returned on error. + */ + +int dbWriteInt(int did, char_t *table, char_t *column, int row, int iData) +{ + int tid, colIndex, *pRow; + dbTable_t *pTable; + + a_assert(table); + a_assert(column); + +/* + * Make sure that this table exists + */ + tid = dbGetTableId(0, table); + a_assert(tid >= 0); + + if (tid < 0) { + return DB_ERR_TABLE_NOT_FOUND; + } + + pTable = dbListTables[tid]; + + if (pTable) { +/* + * Make sure that the column exists + */ + colIndex = GetColumnIndex(tid, column); + a_assert(colIndex >= 0); + if (colIndex >= 0) { +/* + * Make sure that the row exists + */ + a_assert((row >= 0) && (row < pTable->nRows)); + if ((row >= 0) && (row < pTable->nRows)) { + pRow = pTable->rows[row]; + if (pRow) { + pRow[colIndex] = iData; + return 0; + } + return DB_ERR_ROW_DELETED; + } + return DB_ERR_ROW_NOT_FOUND; + } + return DB_ERR_COL_NOT_FOUND; + } + + return DB_ERR_TABLE_DELETED; +} + +/******************************************************************************/ +/* + * The dbWriteStr function writes a string value into a table at a given row + * and column. The existence of the row and column is verified before the + * write. The column is also checked to confirm it is a string field. + * 0 is returned on succes, -1 is returned on error. + */ + +int dbWriteStr(int did, char_t *table, char_t *column, int row, char_t *s) +{ + int tid, colIndex; + int *pRow; + char_t *ptr; + dbTable_t *pTable; + + a_assert(table); + a_assert(column); + + tid = dbGetTableId(0, table); + a_assert(tid >= 0); + + if (tid < 0) { + return DB_ERR_TABLE_NOT_FOUND; + } + +/* + * Make sure that this table exists + */ + pTable = dbListTables[tid]; + a_assert(pTable); + if (!pTable) { + return DB_ERR_TABLE_DELETED; + } + +/* + * Make sure that this column exists + */ + colIndex = GetColumnIndex(tid, column); + if (colIndex < 0) { + return DB_ERR_COL_NOT_FOUND; + } + +/* + * Make sure that this column is a string column + */ + if (pTable->columnTypes[colIndex] != T_STRING) { + return DB_ERR_BAD_FORMAT; + } + +/* + * Make sure that the row exists + */ + a_assert((row >= 0) && (row < pTable->nRows)); + if ((row >= 0) && (row < pTable->nRows)) { + pRow = pTable->rows[row]; + } else { + return DB_ERR_ROW_NOT_FOUND; + } + + if (!pRow) { + return DB_ERR_ROW_DELETED; + } + +/* + * If the column already has a value, be sure to delete it to prevent + * memory leaks. + */ + if (pRow[colIndex]) { + bfree(B_L, (char_t *) pRow[colIndex]); + } + +/* + * Make sure we make a copy of the string to write into the column. + * This allocated string will be deleted when the row is deleted. + */ + ptr = bstrdup(B_L, s); + pRow[colIndex] = (int)ptr; + + return 0; +} + +/******************************************************************************/ +/* + * Print a key-value pair to a file + */ + +static int dbWriteKeyValue(int fd, char_t *key, char_t *value) +{ + int rc; + int len; + char_t *pLineOut; + + a_assert(key && *key); + a_assert(value); + + fmtAlloc(&pLineOut, BUF_MAX, T("%s=%s\n"), key, value); + + if (pLineOut) { + len = gstrlen(pLineOut); +#if CE + rc = writeUniToAsc(fd, pLineOut, len); +#else + rc = gwrite(fd, pLineOut, len); +#endif + bfree(B_L, pLineOut); + } else { + rc = -1; + } + + return rc; +} + +/******************************************************************************/ +/* + * Persist a database to a file + */ + +int dbSave(int did, char_t *filename, int flags) +{ + int row, column, nColumns, nRows, fd, rc; + int *colTypes, *pRow, nRet, tid; + char_t *path, *tmpFile, *tmpNum; + char_t **colNames; + dbTable_t *pTable; + + trace(5, T("DB: About to save database to file\n")); + + a_assert(dbMaxTables > 0); + +/* + * First write to a temporary file, then switch around later. + */ + fmtAlloc(&tmpFile, FNAMESIZE, T("%s/data.tmp"), basicGetProductDir()); + if ((fd = gopen(tmpFile, + O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666)) < 0) { + trace(1, T("WARNING: Failed to open file %s\n"), tmpFile); + bfree(B_L, tmpFile); + return -1; + } + + nRet = 0; + + for (tid = 0; (tid < dbMaxTables) && (nRet != -1); tid++) { + pTable = dbListTables[tid]; + + if (pTable) { +/* + * Print the TABLE=tableName directive to the file + */ + rc = dbWriteKeyValue(fd, KEYWORD_TABLE, pTable->name); + + nColumns = pTable->nColumns; + nRows = pTable->nRows; + + for (row = 0; (row < nRows) && (nRet == 0); row++) { + pRow = pTable->rows[row]; +/* + * if row is NULL, the row has been deleted, so don't + * write it out. + */ + if ((pRow == NULL) || (pRow[0] == '\0') || + (*(char_t *)(pRow[0]) == '\0')) { + continue; + } +/* + * Print the ROW=rowNumber directive to the file + */ + fmtAlloc(&tmpNum, 20, T("%d"), row); + rc = dbWriteKeyValue(fd, KEYWORD_ROW, tmpNum); + bfreeSafe(B_L, tmpNum); + + colNames = pTable->columnNames; + colTypes = pTable->columnTypes; +/* + * Print the key-value pairs (COLUMN=value) for data cells + */ + for (column = 0; (column < nColumns) && (rc >= 0); + column++, colNames++, colTypes++) { + if (*colTypes == T_STRING) { + rc = dbWriteKeyValue(fd, *colNames, + (char_t *)(pRow[column])); + } else { + fmtAlloc(&tmpNum, 20, T("%d"), pRow[column]); + rc = dbWriteKeyValue(fd, *colNames, tmpNum); + bfreeSafe(B_L, tmpNum); + } + } + + if (rc < 0) { + trace(1, T("WARNING: Failed to write to file %s\n"), + tmpFile); + nRet = -1; + } + } + } + } + + gclose(fd); + +/* + * Replace the existing file with the temporary file, if no errors + */ + if (nRet == 0) { + fmtAlloc(&path, FNAMESIZE, T("%s/%s"), basicGetProductDir(), filename); + + gunlink(path); + if (grename(tmpFile, path) != 0) { + trace(1, T("WARNING: Failed to rename %s to %s\n"), tmpFile, path); + nRet = -1; + } + + bfree(B_L, path); + } + + bfree(B_L, tmpFile); + + return nRet; +} + +/******************************************************************************/ +/* + * Crack a keyword=value string into keyword and value. We can change buf. + */ + +static int crack(char_t *buf, char_t **key, char_t **val) +{ + char_t *ptr; + + if ((ptr = gstrrchr(buf, '\n')) != NULL || + (ptr = gstrrchr(buf, '\r')) != NULL) { + *ptr = '\0'; + } + +/* + * Find the = sign. It must exist. + */ + if ((ptr = gstrstr(buf, T("="))) == NULL) { + return -1; + } + + *ptr++ = '\0'; + *key = trim(buf); + *val = trim(ptr); + + return 0; +} + +/******************************************************************************/ +/* + * Parse the file. These files consist of key-value pairs, separated by the + * "=" sign. Parsing of tables starts with the "TABLE=value" pair, and rows + * are parsed starting with the "ROW=value" pair. + */ + +int dbLoad(int did, char_t *filename, int flags) +{ + gstat_t sbuf; + char_t *buf, *keyword, *value, *path, *ptr; + char_t *tablename; + int fd, tid, row; + dbTable_t *pTable; + + a_assert(did >= 0); + + fmtAlloc(&path, FNAMESIZE, T("%s/%s"), basicGetProductDir(), filename); + trace(4, T("DB: About to read data file <%s>\n"), path); + + if (gstat(path, &sbuf) < 0) { + trace(3, T("DB: Failed to stat persistent data file.\n")); + bfree(B_L, path); + return -1; + } + + fd = gopen(path, O_RDONLY | O_BINARY, 0666); + bfree(B_L, path); + + if (fd < 0) { + trace(3, T("DB: No persistent data file present.\n")); + return -1; + } + + if (sbuf.st_size <= 0) { + trace(3, T("DB: Persistent data file is empty.\n")); + gclose(fd); + return -1; + } +/* + * Read entire file into temporary buffer + */ + buf = balloc(B_L, sbuf.st_size + 1); +#if CE + if (readAscToUni(fd, &buf, sbuf.st_size) != (int)sbuf.st_size) { +#else + if (gread(fd, buf, sbuf.st_size) != (int)sbuf.st_size) { +#endif + trace(3, T("DB: Persistent data read failed.\n")); + bfree(B_L, buf); + gclose(fd); + return -1; + } + + gclose(fd); + *(buf + sbuf.st_size) = '\0'; + + row = -1; + tid = -1; + pTable = NULL; + ptr = gstrtok(buf, T("\n")); + tablename = NULL; + + do { + if (crack(ptr, &keyword, &value) < 0) { + trace(5, T("DB: Failed to crack line %s\n"), ptr); + continue; + } + + a_assert(keyword && *keyword); + + if (gstrcmp(keyword, KEYWORD_TABLE) == 0) { +/* + * Table name found, check to see if it's registered + */ + if (tablename) { + bfree(B_L, tablename); + } + + tablename = bstrdup(B_L, value); + tid = dbGetTableId(did, tablename); + + if (tid >= 0) { + pTable = dbListTables[tid]; + } else { + pTable = NULL; + } + + } else if (gstrcmp(keyword, KEYWORD_ROW) == 0) { +/* + * Row/Record indicator found, add a new row to table + */ + if (tid >= 0) { + int nRows = dbGetTableNrow(did, tablename); + + if (dbSetTableNrow(did, tablename, nRows + 1) == 0) { + row = nRows; + } + } + + } else if (row != -1) { +/* + * some other data found, assume it's a COLUMN=value + */ + int nColumn = GetColumnIndex(tid, keyword); + + if ((nColumn >= 0) && (pTable != NULL)) { + int nColumnType = pTable->columnTypes[nColumn]; + if (nColumnType == T_STRING) { + dbWriteStr(did, tablename, keyword, row, value); + } else { + dbWriteInt(did, tablename, keyword, row, gstrtoi(value)); + } + } + } + } while ((ptr = gstrtok(NULL, T("\n"))) != NULL); + + if (tablename) { + bfree(B_L, tablename); + } + + bfree(B_L, buf); + + return 0; +} + +/******************************************************************************/ +/* + * Return a table id given the table name + */ + +int dbGetTableId(int did, char_t *tablename) +{ + int tid; + dbTable_t *pTable; + + a_assert(tablename); + + for (tid = 0; (tid < dbMaxTables); tid++) { + if ((pTable = dbListTables[tid]) != NULL) { + if (gstrcmp(tablename, pTable->name) == 0) { + return tid; + } + } + } + + return -1; +} + +/******************************************************************************/ +/* + * Return a pointer to the table name, given its ID + */ + +char_t *dbGetTableName(int did, int tid) +{ + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + return (dbListTables[tid])->name; + } + + return NULL; +} + +/******************************************************************************/ +/* + * Trim leading white space. + */ + +static char_t *trim(char_t *str) +{ + while (isspace((int)*str)) { + str++; + } + return str; +} + +/******************************************************************************/ +/* + * Return a column index given the column name + */ + +static int GetColumnIndex(int tid, char_t *colName) +{ + int column; + dbTable_t *pTable; + + a_assert(colName); + + if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { + pTable = dbListTables[tid]; + + for (column = 0; (column < pTable->nColumns); column++) { + if (gstrcmp(colName, pTable->columnNames[column]) == 0) + return column; + } + } + + return -1; +} + +/******************************************************************************/ +/* + * Set the prefix-directory + */ + +void basicSetProductDir(char_t *proddir) +{ + int len; + + if (basicProdDir != NULL); { + bfree(B_L, basicProdDir); + } + + basicProdDir = bstrdup(B_L, proddir); +/* + * Make sure that prefix-directory doesn't end with a '/' + */ + len = gstrlen(basicProdDir); + if ((len > 0) && *(basicProdDir + len - 1) == '/') { + *(basicProdDir+len-1) = '\0'; + } +} + +/******************************************************************************/ +/* + * Return the prefix-directory + */ + +char_t *basicGetProductDir() +{ + if (basicProdDir) { + return basicProdDir; + } else { + return basicDefaultDir; + } +} + +/******************************************************************************/ diff --git a/cpukit/httpd/emfdb.h b/cpukit/httpd/emfdb.h new file mode 100644 index 0000000000..0ba7e67401 --- /dev/null +++ b/cpukit/httpd/emfdb.h @@ -0,0 +1,102 @@ +/* + * emfdb.h -- EMF database compatability functions for GoAhead WebServer. + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ + */ + +/******************************** Description *********************************/ +/* + * Emf-like textfile database support for WebServer 2.1. + */ + +/********************************* Includes ***********************************/ + +#ifndef _h_EMFDB +#define _h_EMFDB 1 + +#if ! UEMF + #include "basic/basic.h" + #include "emf/emf.h" +#else + #include "uemf.h" +#endif + + +/********************************* Defines ************************************/ + +#define T_INT 0 +#define T_STRING 1 + +#define DB_OK 0 +#define DB_ERR_GENERAL -1 +#define DB_ERR_COL_NOT_FOUND -2 +#define DB_ERR_COL_DELETED -3 +#define DB_ERR_ROW_NOT_FOUND -4 +#define DB_ERR_ROW_DELETED -5 +#define DB_ERR_TABLE_NOT_FOUND -6 +#define DB_ERR_TABLE_DELETED -7 +#define DB_ERR_BAD_FORMAT -8 + +typedef struct dbTable_s { + char_t *name; + int nColumns; + char_t **columnNames; + int *columnTypes; + int nRows; + int **rows; +} dbTable_t; + +/********************************** Prototypes ********************************/ + +/* + * Add a schema to the module-internal schema database + */ +extern int dbRegisterDBSchema(dbTable_t *sTable); + +extern int dbOpen(char_t *databasename, char_t *filename, + int (*gettime)(int did), int flags); +extern void dbClose(int did); +extern int dbGetTableId(int did, char_t *tname); +extern char_t *dbGetTableName(int did, int tid); +extern int dbReadInt(int did, char_t *table, char_t *column, int row, + int *returnValue); +extern int dbReadStr(int did, char_t *table, char_t *column, int row, + char_t **returnValue); +extern int dbWriteInt(int did, char_t *table, char_t *column, int row, + int idata); +extern int dbWriteStr(int did, char_t *table, char_t *column, int row, + char_t *s); +extern int dbAddRow(int did, char_t *table); +extern int dbDeleteRow(int did, char_t *table, int rid); +extern int dbSetTableNrow(int did, char_t *table, int nNewRows); +extern int dbGetTableNrow(int did, char_t *table); + +/* + * Dump the contents of a database to file + */ +extern int dbSave(int did, char_t *filename, int flags); + +/* + * Load the contents of a database to file + */ +extern int dbLoad(int did, char_t *filename, int flags); + +/* + * Search for a data in a given column + */ +extern int dbSearchStr(int did, char_t *table, char_t *column, + char_t *value, int flags); + +extern void dbZero(int did); + +extern char_t *basicGetProductDir(); +extern void basicSetProductDir(char_t *proddir); + +#endif /* _h_EMFDB */ + +/******************************************************************************/ + diff --git a/cpukit/httpd/form.c b/cpukit/httpd/form.c index 05d5600ada..dbad2d2e78 100644 --- a/cpukit/httpd/form.c +++ b/cpukit/httpd/form.c @@ -1,7 +1,7 @@ /* * form.c -- Form processing (in-memory CGI) for the GoAhead Web server * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -29,12 +29,12 @@ static sym_fd_t formSymtab = -1; /* Symbol table for form handlers */ */ int websFormHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, - char_t *url, char_t *path, char_t* query) + char_t *url, char_t *path, char_t *query) { sym_t *sp; char_t formBuf[FNAMESIZE]; char_t *cp, *formName; - int (*fn)(void *sock, char_t* path, char_t *args); + int (*fn)(void *sock, char_t *path, char_t *args); a_assert(websValid(wp)); a_assert(url && *url); @@ -63,16 +63,23 @@ int websFormHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, if (sp == NULL) { websError(wp, 200, T("Form %s is not defined"), formName); } else { - fn = (int (*)(void*, char_t*, char_t*)) sp->content.value.integer; + fn = (int (*)(void *, char_t *, char_t *)) sp->content.value.integer; a_assert(fn); if (fn) { /* * For good practice, forms must call websDone() */ (*fn)((void*) wp, formName, query); + +/* + * Remove the test to force websDone, since this prevents + * the server "push" from a form> + */ +#if 0 /* push */ if (websValid(wp)) { websError(wp, 200, T("Form didn't call websDone")); } +#endif /* push */ } } return 1; @@ -86,8 +93,6 @@ int websFormHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, int websFormDefine(char_t *name, void (*fn)(webs_t wp, char_t *path, char_t *query)) { - static int once = 0; - a_assert(name && *name); a_assert(fn); @@ -95,9 +100,6 @@ int websFormDefine(char_t *name, void (*fn)(webs_t wp, char_t *path, return -1; } - if (once++ == 0) { - websFormOpen(); - } symEnter(formSymtab, name, valueInteger((int) fn), (int) NULL); return 0; } @@ -109,7 +111,7 @@ int websFormDefine(char_t *name, void (*fn)(webs_t wp, char_t *path, void websFormOpen() { - formSymtab = symOpen(64); + formSymtab = symOpen(WEBS_SYM_INIT); } /******************************************************************************/ @@ -120,7 +122,8 @@ void websFormOpen() void websFormClose() { if (formSymtab != -1) { - symClose(formSymtab, NULL); + symClose(formSymtab); + formSymtab = -1; } } @@ -139,7 +142,7 @@ void websHeader(webs_t wp) /* * By license terms the following line of code must not be modified */ - websWrite(wp, T("Server: GoAhead-Webs\r\n")); + websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); websWrite(wp, T("Pragma: no-cache\n")); websWrite(wp, T("Cache-control: no-cache\n")); diff --git a/cpukit/httpd/h.c b/cpukit/httpd/h.c index ed7a4553eb..23bc1ddd9c 100644 --- a/cpukit/httpd/h.c +++ b/cpukit/httpd/h.c @@ -1,15 +1,15 @@ /* * h.c -- Handle allocation module * - * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * See the file "license.txt" for usage and redistribution license requirements */ /******************************** Description *********************************/ /* - * This module provides a simple API to allocate and free handles - * It maintains a dynamic array of pointers. These usually point to + * This module provides a simple API to allocate and free handles + * It maintains a dynamic array of pointers. These usually point to * per-handle structures. */ @@ -36,12 +36,16 @@ /*********************************** Code *************************************/ /* - * Allocate a new file handle. On the first call, the caller must set the + * Allocate a new file handle. On the first call, the caller must set the * handle map to be a pointer to a null pointer. *map points to the second * element in the handle array. */ +#if B_STATS +int HALLOC(B_ARGS_DEC, void ***map) +#else int hAlloc(void ***map) +#endif { int *mp; int handle, len, memsize, incr; @@ -51,7 +55,11 @@ int hAlloc(void ***map) if (*map == NULL) { incr = H_INCR; memsize = (incr + H_OFFSET) * sizeof(void**); +#if B_STATS + if ((mp = (int*) balloc(B_ARGS, memsize)) == NULL) { +#else if ((mp = (int*) balloc(B_L, memsize)) == NULL) { +#endif return -1; } memset(mp, 0, memsize); @@ -68,11 +76,12 @@ int hAlloc(void ***map) * Find the first null handle */ if (mp[H_USED] < mp[H_LEN]) { - for (handle = 0; handle < len; handle++) + for (handle = 0; handle < len; handle++) { if (mp[handle+H_OFFSET] == 0) { mp[H_USED]++; return handle; } + } } else { handle = len; } @@ -139,7 +148,11 @@ int hFree(void ***map, int handle) * Allocate an entry in the halloc array. */ +#if B_STATS +int HALLOCENTRY(B_ARGS_DEC, void ***list, int *max, int size) +#else int hAllocEntry(void ***list, int *max, int size) +#endif { char_t *cp; int id; @@ -147,12 +160,20 @@ int hAllocEntry(void ***list, int *max, int size) a_assert(list); a_assert(max); +#if B_STATS + if ((id = HALLOC(B_ARGS, (void***) list)) < 0) { +#else if ((id = hAlloc((void***) list)) < 0) { +#endif return -1; } - + if (size > 0) { +#if B_STATS + if ((cp = balloc(B_ARGS, size)) == NULL) { +#else if ((cp = balloc(B_L, size)) == NULL) { +#endif hFree(list, id); return -1; } @@ -169,3 +190,4 @@ int hAllocEntry(void ***list, int *max, int size) } /******************************************************************************/ + diff --git a/cpukit/httpd/handler.c b/cpukit/httpd/handler.c index fb42d2d097..88693bf9a6 100644 --- a/cpukit/httpd/handler.c +++ b/cpukit/httpd/handler.c @@ -1,7 +1,7 @@ /* * handler.c -- URL handler support * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -19,14 +19,16 @@ /*********************************** Locals ***********************************/ -static websUrlHandlerType* websUrlHandler; /* URL handler list */ +static websUrlHandlerType *websUrlHandler; /* URL handler list */ static int websUrlHandlerMax; /* Number of entries */ +static int urlHandlerOpenCount = 0; /* count of apps */ /**************************** Forward Declarations ****************************/ -static int websUrlHandlerSort(const void* p1, const void* p2); +static int websUrlHandlerSort(const void *p1, const void *p2); static int websPublishHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int sid, char_t *url, char_t *path, char_t *query); +static int websTidyUrl(webs_t wp); /*********************************** Code *************************************/ /* @@ -35,7 +37,11 @@ static int websPublishHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int websUrlHandlerOpen() { - websAspOpen(); + if (++urlHandlerOpenCount == 1) { + websAspOpen(); + websUrlHandler = NULL; + websUrlHandlerMax = 0; + } return 0; } @@ -46,17 +52,20 @@ int websUrlHandlerOpen() void websUrlHandlerClose() { - websUrlHandlerType* sp; - - websAspClose(); - for (sp = websUrlHandler; sp < &websUrlHandler[websUrlHandlerMax]; sp++) { - bfree(B_L, sp->urlPrefix); - if (sp->webDir) { - bfree(B_L, sp->webDir); + websUrlHandlerType *sp; + + if (--urlHandlerOpenCount <= 0) { + websAspClose(); + for (sp = websUrlHandler; sp < &websUrlHandler[websUrlHandlerMax]; + sp++) { + bfree(B_L, sp->urlPrefix); + if (sp->webDir) { + bfree(B_L, sp->webDir); + } } + bfree(B_L, websUrlHandler); + websUrlHandlerMax = 0; } - bfree(B_L, websUrlHandler); - websUrlHandlerMax = 0; } /******************************************************************************/ @@ -135,7 +144,7 @@ int websUrlHandlerDelete(int (*handler)(webs_t wp, char_t *urlPrefix, * Sort in decreasing URL length order observing the flags for first and last */ -static int websUrlHandlerSort(const void* p1, const void* p2) +static int websUrlHandlerSort(const void *p1, const void *p2) { websUrlHandlerType *s1, *s2; int rc; @@ -247,6 +256,8 @@ int websUrlHandlerRequest(webs_t wp) websSetRequestPath(wp, websGetDefaultDir(), NULL); + websTidyUrl(wp); + /* * We loop over each handler in order till one accepts the request. * The security handler will handle the request if access is NOT allowed. @@ -264,7 +275,7 @@ int websUrlHandlerRequest(webs_t wp) return 1; } if (!websValid(wp)) { - goahead_trace(0, + trace(0, T("webs: handler %s called websDone, but didn't return 1\n"), sp->urlPrefix); return 1; @@ -272,7 +283,7 @@ int websUrlHandlerRequest(webs_t wp) } } /* - * If no handler processed the request, then return an error. Note: It was + * If no handler processed the request, then return an error. Note: It is * the handlers responsibility to call websDone */ if (i >= websUrlHandlerMax) { @@ -281,4 +292,70 @@ int websUrlHandlerRequest(webs_t wp) return 0; } + +/******************************************************************************/ +/* + * Tidy up the URL path. Return -1 if the URL is bad. + * Used to eliminate repeated directory delimiters ('/'). + */ + +static int websTidyUrl(webs_t wp) +{ + char_t *parts[64]; /* Array of ptr's to URL parts */ + char_t *token, *url, *tidyurl; + int i, len, npart; + + a_assert(websValid(wp)); + +/* + * Copy the string so we don't destroy the original (yet) + */ + url = bstrdup(B_L, wp->url); + websDecodeUrl(url, url, gstrlen(url)); + + len = npart = 0; + parts[0] = NULL; + token = gstrtok(url, T("/")); + +/* + * Look at each directory segment and process "." and ".." segments + * Don't allow the browser to pop outside the root web. + */ + while (token != NULL) { + if (gstrcmp(token, T("..")) == 0) { + if (npart > 0) { + npart--; + } + + } else if (gstrcmp(token, T(".")) != 0) { + parts[npart] = token; + len += gstrlen(token) + 1; + npart++; + } + token = gstrtok(NULL, T("/")); + } + +/* + * Re-construct URL. Need extra space all "/" and null. + */ + if (npart || (gstrcmp(url, T("/")) == 0) || (url[0] == '\0')) { + tidyurl = balloc(B_L, (len + 2) * sizeof(char_t)); + *tidyurl = '\0'; + + for (i = 0; i < npart; i++) { + gstrcat(tidyurl, T("/")); + gstrcat(tidyurl, parts[i]); + } + + bfree(B_L, url); + + bfree(B_L, wp->url); + wp->url = tidyurl; + return 0; + } else { + bfree(B_L, url); + return -1; + } +} + /******************************************************************************/ diff --git a/cpukit/httpd/md5.h b/cpukit/httpd/md5.h new file mode 100644 index 0000000000..d27031b44d --- /dev/null +++ b/cpukit/httpd/md5.h @@ -0,0 +1,48 @@ +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#ifndef _h_MD5 +#define _h_MD5 1 + +#ifndef UINT4 +#define UINT4 unsigned long +#endif + +#ifndef POINTER +#define POINTER unsigned char * +#endif + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CONTEXT; + +extern void MD5Init (MD5_CONTEXT *); +extern void MD5Update (MD5_CONTEXT *, unsigned char *, unsigned int); +extern void MD5Final (unsigned char [16], MD5_CONTEXT *); + +#endif /* _h_MD5 */ \ No newline at end of file diff --git a/cpukit/httpd/md5c.c b/cpukit/httpd/md5c.c new file mode 100644 index 0000000000..90142cf785 --- /dev/null +++ b/cpukit/httpd/md5c.c @@ -0,0 +1,335 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#include "md5.h" + +/* Constants for MD5Transform routine. + */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform (UINT4 [4], unsigned char [64]); +static void Encode (unsigned char *, UINT4 *, unsigned int); +static void Decode (UINT4 *, unsigned char *, unsigned int); +static void MD5_memcpy (POINTER, POINTER, unsigned int); +static void MD5_memset (POINTER, int, unsigned int); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * Note: The following MD5 macros can be implemented as functions + * for code compactness, (at the expense of execution speed). + */ + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void MD5Init (context) +MD5_CONTEXT *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void MD5Update (context, input, inputLen) +MD5_CONTEXT *context; /* context */ +unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. +*/ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CONTEXT *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. +*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. +*/ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform (state, block) +UINT4 state[4]; +unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. +*/ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (output, input, len) +UINT4 *output; +unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. + */ + +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} diff --git a/cpukit/httpd/mime.c b/cpukit/httpd/mime.c index 2aa2422ca9..daa467f9cc 100644 --- a/cpukit/httpd/mime.c +++ b/cpukit/httpd/mime.c @@ -1,7 +1,7 @@ /* * mime.c -- Web server mime types * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ diff --git a/cpukit/httpd/misc.c b/cpukit/httpd/misc.c index 88b65a2008..0b3a9049cb 100644 --- a/cpukit/httpd/misc.c +++ b/cpukit/httpd/misc.c @@ -1,7 +1,7 @@ /* * misc.c -- Miscellaneous routines. * - * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -16,12 +16,12 @@ /********************************* Defines ************************************/ /* - * Sprintf buffer structure. Make the increment 8 less than 64 so that + * Sprintf buffer structure. Make the increment 64 so that * a balloc can use a 64 byte block. */ #define STR_REALLOC 0x1 /* Reallocate the buffer as required */ -#define STR_INC 58 /* Growth increment */ +#define STR_INC 64 /* Growth increment */ typedef struct { char_t *s; /* Pointer to buffer */ @@ -59,11 +59,11 @@ static void put_ulong(strbuf_t *buf, unsigned long int value, int base, /************************************ Code ************************************/ /* * "basename" returns a pointer to the last component of a pathname - * LINUX, RTEMS, and LynxOS have their own basename function + * LINUX and LynxOS have their own basename function */ -#if ! LINUX & ! __rtems__ & ! LYNX -char_t *basename(char_t* name) +#if ! LINUX && ! LYNX && ! __rtems__ +char_t *basename(char_t *name) { char_t *cp; @@ -83,7 +83,7 @@ char_t *basename(char_t* name) return ++cp; } } -#endif /* ! LINUX && ! __rtems__ && ! LYNX */ +#endif /* ! LINUX & ! LYNX */ /******************************************************************************/ /* @@ -91,9 +91,9 @@ char_t *basename(char_t* name) * the size of the buffer in BYTES! */ -char_t *dirname(char_t* buf, char_t* name, int bufsize) +char_t *dirname(char_t *buf, char_t *name, int bufsize) { - char_t* cp; + char_t *cp; int len; a_assert(name); @@ -130,14 +130,15 @@ char_t *dirname(char_t* buf, char_t* name, int bufsize) return buf; } + /******************************************************************************/ /* * sprintf and vsprintf are bad, ok. You can easily clobber memory. Use - * gsnprintf and gvsnprintf instead! These functions do _not_ support floating + * fmtAlloc and fmtValloc instead! These functions do _not_ support floating * point, like %e, %f, %g... */ -int gsnprintf(char_t **s, int n, char_t *fmt, ...) +int fmtAlloc(char_t **s, int n, char_t *fmt, ...) { va_list ap; int result; @@ -147,7 +148,30 @@ int gsnprintf(char_t **s, int n, char_t *fmt, ...) *s = NULL; va_start(ap, fmt); - result = gvsnprintf(s, n, fmt, ap); + result = dsnprintf(s, n, fmt, ap, 0); + va_end(ap); + return result; +} + +/******************************************************************************/ +/* + * Support a static buffer version for small buffers only! + */ + +int fmtStatic(char_t *s, int n, char_t *fmt, ...) +{ + va_list ap; + int result; + + a_assert(s); + a_assert(fmt); + a_assert(n <= 256); + + if (n <= 0) { + return -1; + } + va_start(ap, fmt); + result = dsnprintf(&s, n, fmt, ap, 0); va_end(ap); return result; } @@ -158,7 +182,7 @@ int gsnprintf(char_t **s, int n, char_t *fmt, ...) * reallocing if required. */ -int gsprintfRealloc(char_t **s, int n, int msize, char_t *fmt, ...) +int fmtRealloc(char_t **s, int n, int msize, char_t *fmt, ...) { va_list ap; int result; @@ -180,17 +204,18 @@ int gsprintfRealloc(char_t **s, int n, int msize, char_t *fmt, ...) * A vsprintf replacement. */ -int gvsnprintf(char_t **s, int n, char_t *fmt, va_list arg) +int fmtValloc(char_t **s, int n, char_t *fmt, va_list arg) { a_assert(s); a_assert(fmt); + *s = NULL; return dsnprintf(s, n, fmt, arg, 0); } /******************************************************************************/ /* - * Dynamic sprintf implementation. Supports dynamic buffer allocation also. + * Dynamic sprintf implementation. Supports dynamic buffer allocation. * This function can be called multiple times to grow an existing allocated * buffer. In this case, msize is set to the size of the previously allocated * buffer. The buffer will be realloced, as required. If msize is set, we @@ -252,7 +277,7 @@ static int dsnprintf(char_t **s, int size, char_t *fmt, va_list arg, int msize) } c = *fmt++; } else { - for ( ; gisdigit(c); c = *fmt++) { + for ( ; gisdigit((int)c); c = *fmt++) { width = width * 10 + (c - '0'); } } @@ -263,7 +288,7 @@ static int dsnprintf(char_t **s, int size, char_t *fmt, va_list arg, int msize) prec = va_arg(arg, int); c = *fmt++; } else { - for (prec = 0; gisdigit(c); c = *fmt++) { + for (prec = 0; gisdigit((int)c); c = *fmt++) { prec = prec * 10 + (c - '0'); } } @@ -402,24 +427,28 @@ static int strnlen(char_t *s, unsigned int n) static void put_char(strbuf_t *buf, char_t c) { - if (buf->count >= buf->size) { + if (buf->count >= (buf->size - 1)) { if (! (buf->flags & STR_REALLOC)) { return; } buf->size += STR_INC; if (buf->size > buf->max && buf->size > STR_INC) { - a_assert(buf->size <= buf->max); +/* + * Caller should increase the size of the calling buffer + */ buf->size -= STR_INC; return; } if (buf->s == NULL) { - buf->s = balloc(B_L, buf->size * sizeof(char_t*)); + buf->s = balloc(B_L, buf->size * sizeof(char_t)); } else { - buf->s = brealloc(B_L, buf->s, buf->size * sizeof(char_t*)); + buf->s = brealloc(B_L, buf->s, buf->size * sizeof(char_t)); } } buf->s[buf->count] = c; - ++buf->count; + if (c != '\0') { + ++buf->count; + } } /******************************************************************************/ @@ -428,7 +457,7 @@ static void put_char(strbuf_t *buf, char_t c) */ static void put_string(strbuf_t *buf, char_t *s, int len, int width, - int prec, enum flag f) + int prec, enum flag f) { int i; @@ -458,7 +487,7 @@ static void put_string(strbuf_t *buf, char_t *s, int len, int width, */ static void put_ulong(strbuf_t *buf, unsigned long int value, int base, - int upper, char_t *prefix, int width, int prec, enum flag f) + int upper, char_t *prefix, int width, int prec, enum flag f) { unsigned long x, x2; int len, zeros, i; @@ -475,8 +504,14 @@ static void put_ulong(strbuf_t *buf, unsigned long int value, int base, width -= strnlen(prefix, ULONG_MAX); } if (!(f & flag_minus)) { - for (i = 0; i < width; ++i) { - put_char(buf, ' '); + if (f & flag_zero) { + for (i = 0; i < width; ++i) { + put_char(buf, '0'); + } + } else { + for (i = 0; i < width; ++i) { + put_char(buf, ' '); + } } } if (prefix != NULL) { @@ -524,7 +559,7 @@ char_t *ascToUni(char_t *ubuf, char *str, int nBytes) * N.B. nBytes is the number of _bytes_ in the destination buffer, buf. */ -char *uniToAsc(char *buf, char_t* ustr, int nBytes) +char *uniToAsc(char *buf, char_t *ustr, int nBytes) { #if UNICODE if (WideCharToMultiByte(CP_ACP, 0, ustr, nBytes, buf, nBytes, NULL, @@ -537,24 +572,26 @@ char *uniToAsc(char *buf, char_t* ustr, int nBytes) return (char*) buf; } - /******************************************************************************/ /* * allocate (balloc) a buffer and do ascii to unicode conversion into it. - * cp points to the ascii string which must be NULL terminated. - * Return a pointer to the unicode buffer which must be bfree'd later. - * Return NULL on failure to get buffer. + * cp points to the ascii buffer. alen is the length of the buffer to be + * converted not including a terminating NULL. Return a pointer to the + * unicode buffer which must be bfree'd later. Return NULL on failure to + * get buffer. The buffer returned is NULL terminated. */ -char_t *ballocAscToUni(char * cp) + +char_t *ballocAscToUni(char *cp, int alen) { - char_t * unip; + char_t *unip; int ulen; - ulen = (strlen(cp) + 1) * sizeof(char_t); + ulen = (alen + 1) * sizeof(char_t); if ((unip = balloc(B_L, ulen)) == NULL) { return NULL; } ascToUni(unip, cp, ulen); + unip[alen] = 0; return unip; } @@ -562,20 +599,68 @@ char_t *ballocAscToUni(char * cp) /* * allocate (balloc) a buffer and do unicode to ascii conversion into it. * unip points to the unicoded string. ulen is the number of characters - * in the unicode string including teminating null, if there is one. - * Return a pointer to the ascii buffer which must be bfree'd later. - * Return NULL on failure to get buffer. + * in the unicode string not including a teminating null. Return a pointer + * to the ascii buffer which must be bfree'd later. Return NULL on failure + * to get buffer. The buffer returned is NULL terminated. */ -char *ballocUniToAsc(char_t * unip, int ulen) + +char *ballocUniToAsc(char_t *unip, int ulen) { char * cp; - if ((cp = balloc(B_L, ulen)) == NULL) { + if ((cp = balloc(B_L, ulen+1)) == NULL) { return NULL; } uniToAsc(cp, unip, ulen); + cp[ulen] = '\0'; return cp; } /******************************************************************************/ +/* + * convert a hex string to an integer. The end of the string or a non-hex + * character will indicate the end of the hex specification. + */ + +unsigned int hextoi(char_t *hexstring) +{ + register char_t *h; + register unsigned int c, v; + + v = 0; + h = hexstring; + if (*h == '0' && (*(h+1) == 'x' || *(h+1) == 'X')) { + h += 2; + } + while ((c = (unsigned int)*h++) != 0) { + if (c >= '0' && c <= '9') { + c -= '0'; + } else if (c >= 'a' && c <= 'f') { + c = (c - 'a') + 10; + } else if (c >= 'A' && c <= 'F') { + c = (c - 'A') + 10; + } else { + break; + } + v = (v * 0x10) + c; + } + return v; +} + +/******************************************************************************/ +/* + * convert a string to an integer. If the string starts with "0x" or "0X" + * a hexidecimal conversion is done. + */ + +unsigned int gstrtoi(char_t *s) +{ + if (*s == '0' && (*(s+1) == 'x' || *(s+1) == 'X')) { + s += 2; + return hextoi(s); + } + return gatoi(s); +} + +/******************************************************************************/ diff --git a/cpukit/httpd/ringq.c b/cpukit/httpd/ringq.c index 62c4634eef..cd483ae007 100644 --- a/cpukit/httpd/ringq.c +++ b/cpukit/httpd/ringq.c @@ -1,7 +1,7 @@ /* * ringq.c -- Ring queue buffering module * - * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -10,7 +10,7 @@ /* * A ring queue allows maximum utilization of memory for data storage and is - * ideal for input/output buffering. This module provides a highly effecient + * ideal for input/output buffering. This module provides a highly efficient * implementation and a vehicle for dynamic strings. * * WARNING: This is a public implementation and callers have full access to @@ -36,10 +36,10 @@ * rq->buf rq->servp rq->endp rq->enduf * * The queue is empty when servp == endp. This means that the queue will hold - * at most rq->buflen -1 bytes. It is the fillers responsibility to ensure + * at most rq->buflen -1 bytes. It is the filler's responsibility to ensure * the ringq is never filled such that servp == endp. * - * It is the fillers responsibility to "wrap" the endp back to point to + * It is the filler's responsibility to "wrap" the endp back to point to * rq->buf when the pointer steps past the end. Correspondingly it is the * consumers responsibility to "wrap" the servp when it steps to rq->endbuf. * The ringqPutc and ringqGetc routines will do this automatically. @@ -65,7 +65,10 @@ /***************************** Forward Declarations ***************************/ -static int ringq_grow(ringq_t *rq); +static int ringqGrow(ringq_t *rq); +static int getBinBlockSize(int size); + +int ringqGrowCalls = 0; /*********************************** Code *************************************/ /* @@ -76,12 +79,15 @@ static int ringq_grow(ringq_t *rq); * dynamically allocated. Set maxsize */ -int ringqOpen(ringq_t *rq, int increment, int maxsize) +int ringqOpen(ringq_t *rq, int initSize, int maxsize) { + int increment; + a_assert(rq); - a_assert(increment >= 0); + a_assert(initSize >= 0); - if ((rq->buf = balloc(B_L, increment)) == NULL) { + increment = getBinBlockSize(initSize); + if ((rq->buf = balloc(B_L, (increment))) == NULL) { return -1; } rq->maxsize = maxsize; @@ -115,8 +121,8 @@ void ringqClose(ringq_t *rq) /******************************************************************************/ /* - * Return the length of the ringq. Users must fill the queue to a high - * water mark of at most one less than the queue size. + * Return the length of the data in the ringq. Users must fill the queue to + * a high water mark of at most one less than the queue size. */ int ringqLen(ringq_t *rq) @@ -126,8 +132,7 @@ int ringqLen(ringq_t *rq) if (rq->servp > rq->endp) { return rq->buflen + rq->endp - rq->servp; - } - else { + } else { return rq->endp - rq->servp; } } @@ -166,12 +171,12 @@ int ringqGetc(ringq_t *rq) int ringqPutc(ringq_t *rq, char_t c) { - char_t* cp; + char_t *cp; a_assert(rq); a_assert(rq->buflen == (rq->endbuf - rq->buf)); - if (ringqPutBlkMax(rq) < (int) sizeof(char_t) && !ringq_grow(rq)) { + if ((ringqPutBlkMax(rq) < (int) sizeof(char_t)) && !ringqGrow(rq)) { return -1; } @@ -191,12 +196,12 @@ int ringqPutc(ringq_t *rq, char_t c) int ringqInsertc(ringq_t *rq, char_t c) { - char_t* cp; + char_t *cp; a_assert(rq); a_assert(rq->buflen == (rq->endbuf - rq->buf)); - if (ringqPutBlkMax(rq) < (int) sizeof(char_t) && !ringq_grow(rq)) { + if (ringqPutBlkMax(rq) < (int) sizeof(char_t) && !ringqGrow(rq)) { return -1; } if (rq->servp <= rq->buf) { @@ -210,10 +215,10 @@ int ringqInsertc(ringq_t *rq, char_t c) /******************************************************************************/ /* - * Add a string to the queue. Add a trailing wide null (two nulls) + * Add a string to the queue. Add a trailing null (maybe two nulls) */ -int ringqPutstr(ringq_t *rq, char_t *str) +int ringqPutStr(ringq_t *rq, char_t *str) { int rc; @@ -226,6 +231,19 @@ int ringqPutstr(ringq_t *rq, char_t *str) return rc; } +/******************************************************************************/ +/* + * Add a null terminator. This does NOT increase the size of the queue + */ + +void ringqAddNull(ringq_t *rq) +{ + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + *((char_t*) rq->endp) = (char_t) '\0'; +} + /******************************************************************************/ #if UNICODE /* @@ -261,7 +279,7 @@ int ringqPutcA(ringq_t *rq, char c) a_assert(rq); a_assert(rq->buflen == (rq->endbuf - rq->buf)); - if (ringqPutBlkMax(rq) == 0 && !ringq_grow(rq)) { + if (ringqPutBlkMax(rq) == 0 && !ringqGrow(rq)) { return -1; } @@ -282,7 +300,7 @@ int ringqInsertcA(ringq_t *rq, char c) a_assert(rq); a_assert(rq->buflen == (rq->endbuf - rq->buf)); - if (ringqPutBlkMax(rq) == 0 && !ringq_grow(rq)) { + if (ringqPutBlkMax(rq) == 0 && !ringqGrow(rq)) { return -1; } if (rq->servp <= rq->buf) { @@ -298,7 +316,7 @@ int ringqInsertcA(ringq_t *rq, char c) * ie. beyond the last valid byte. */ -int ringqPutstrA(ringq_t *rq, char *str) +int ringqPutStrA(ringq_t *rq, char *str) { int rc; @@ -334,7 +352,7 @@ int ringqPutBlk(ringq_t *rq, unsigned char *buf, int size) while (size > 0) { this = min(ringqPutBlkMax(rq), size); if (this <= 0) { - if (! ringq_grow(rq)) { + if (! ringqGrow(rq)) { break; } this = min(ringqPutBlkMax(rq), size); @@ -485,10 +503,13 @@ void ringqGetBlkAdj(ringq_t *rq, int size) void ringqFlush(ringq_t *rq) { a_assert(rq); + a_assert(rq->servp); rq->servp = rq->buf; rq->endp = rq->buf; - *rq->servp = '\0'; + if (rq->servp) { + *rq->servp = '\0'; + } } /******************************************************************************/ @@ -498,7 +519,7 @@ void ringqFlush(ringq_t *rq) * the maximum possible size. */ -static int ringq_grow(ringq_t *rq) +static int ringqGrow(ringq_t *rq) { unsigned char *newbuf; int len; @@ -531,7 +552,31 @@ static int ringq_grow(ringq_t *rq) rq->endbuf = &rq->buf[rq->buflen]; ringqPutBlk(rq, newbuf, len); + +/* + * Double the increment so the next grow will line up with balloc'ed memory + */ + rq->increment = getBinBlockSize(2 * rq->increment); + return 1; } /******************************************************************************/ +/* + * Find the smallest binary memory size that "size" will fit into. This + * makes the ringq and ringqGrow routines much more efficient. The balloc + * routine likes powers of 2 minus 1. + */ + +static int getBinBlockSize(int size) +{ + int q; + + size = size >> B_SHIFT; + for (q = 0; size; size >>= 1) { + q++; + } + return (1 << (B_SHIFT + q)); +} + +/******************************************************************************/ diff --git a/cpukit/httpd/rom.c b/cpukit/httpd/rom.c index 4e6c8f1e93..6bb8a8da99 100644 --- a/cpukit/httpd/rom.c +++ b/cpukit/httpd/rom.c @@ -1,7 +1,7 @@ /* * rom.c -- Support for ROMed page retrieval. * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -20,13 +20,6 @@ #include -#if CE -#define EINVAL 22 -#define EBADF 9 -#else -#include -#endif - #include "wsIntrn.h" /******************************** Local Data **********************************/ @@ -46,7 +39,7 @@ int websRomOpen() int nchars; char_t name[SYM_MAX]; - romTab = symOpen(64); + romTab = symOpen(WEBS_SYM_INIT); for (wip = websRomPageIndex; wip->path; wip++) { gstrncpy(name, wip->path, SYM_MAX); @@ -67,7 +60,7 @@ int websRomOpen() void websRomClose() { - symClose(romTab, NULL); + symClose(romTab); } /******************************************************************************/ @@ -105,7 +98,7 @@ void websRomPageClose(int fd) * Stat a web page */ -int websRomPageStat(char_t *path, websStatType* sbuf) +int websRomPageStat(char_t *path, websStatType *sbuf) { websRomPageIndexType *wip; sym_t *sp; @@ -193,6 +186,6 @@ long websRomPageSeek(webs_t wp, long offset, int origin) return (wip->pos = pos); } -#endif +#endif /* WEBS_PAGE_ROM */ /******************************************************************************/ diff --git a/cpukit/httpd/security.c b/cpukit/httpd/security.c index 01d4000f40..f4579ad41e 100644 --- a/cpukit/httpd/security.c +++ b/cpukit/httpd/security.c @@ -1,7 +1,7 @@ /* * security.c -- Security handler * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -9,18 +9,42 @@ /******************************** Description *********************************/ /* - * This module provides a basic security policy. It supports a single global - * password and ignores the username. Encoding/decoding of the password is - * -not- done. + * This module provides a basic security policy. */ /********************************* Includes ***********************************/ #include "wsIntrn.h" +#include "um.h" +#ifdef DIGEST_ACCESS_SUPPORT +#include "websda.h" +#endif + +/********************************** Defines ***********************************/ +/* + * The following #defines change the behaviour of security in the absence + * of User Management. + * Note that use of User management functions require prior calling of + * umInit() to behave correctly + */ + +#ifndef USER_MANAGEMENT_SUPPORT +#define umGetAccessMethodForURL(url) AM_FULL +#define umUserExists(userid) 0 +#define umUserCanAccessURL(userid, url) 1 +#define umGetUserPassword(userid) websGetPassword() +#define umGetAccessLimitSecure(accessLimit) 0 +#define umGetAccessLimit(url) NULL +#endif /******************************** Local Data **********************************/ static char_t websPassword[WEBS_MAX_PASS]; /* Access password (decoded) */ +#ifdef _DEBUG +static int debugSecurity = 1; +#else +static int debugSecurity = 0; +#endif /*********************************** Code *************************************/ /* @@ -30,46 +54,143 @@ static char_t websPassword[WEBS_MAX_PASS]; /* Access password (decoded) */ int websSecurityHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, char_t *query) { - char_t *type, *password; - int flags; + char_t *type, *userid, *password, *accessLimit; + int flags, nRet; + accessMeth_t am; a_assert(websValid(wp)); a_assert(url && *url); a_assert(path && *path); - /* * Get the critical request details */ type = websGetRequestType(wp); password = websGetRequestPassword(wp); + userid = websGetRequestUserName(wp); flags = websGetRequestFlags(wp); +/* + * Get the access limit for the URL. Exit if none found. + */ + accessLimit = umGetAccessLimit(path); + if (accessLimit == NULL) { + return 0; + } + +/* + * Check to see if URL must be encrypted + */ +#ifdef WEBS_SSL_SUPPORT + nRet = umGetAccessLimitSecure(accessLimit); + if (nRet && ((flags | WEBS_SECURE) == 0)) { + websStats.access++; + websError(wp, 200, T("Access Denied\nSecure access is required.")); + trace(3, T("SEC: Non-secure access attempted on <%s>\n"), path); + return 1; + } +#endif + +/* + * Get the access limit for the URL + */ + am = umGetAccessMethodForURL(accessLimit); + + nRet = 0; + if ((flags & WEBS_LOCAL_REQUEST) && (debugSecurity == 0)) { +/* + * Local access is always allowed (defeat when debugging) + */ + } else if (am == AM_NONE) { +/* + * URL is supposed to be hidden! Make like it wasn't found. + */ + websStats.access++; + websError(wp, 400, T("Page Not Found")); + nRet = 1; + } else if (userid && *userid) { + if (!umUserExists(userid)) { + websStats.access++; + websError(wp, 200, T("Access Denied\nUnknown User")); + trace(3, T("SEC: Unknown user <%s> attempted to access <%s>\n"), + userid, path); + nRet = 1; + } else if (!umUserCanAccessURL(userid, accessLimit)) { + websStats.access++; + websError(wp, 403, T("Access Denied\nProhibited User")); + nRet = 1; + } else if (password && * password) { + char_t * userpass = umGetUserPassword(userid); + if (userpass) { + if (gstrcmp(password, userpass) != 0) { + websStats.access++; + websError(wp, 200, T("Access Denied\nWrong Password")); + trace(3, T("SEC: Password fail for user <%s>") + T("attempt to access <%s>\n"), userid, path); + nRet = 1; + } else { +/* + * User and password check out. + */ + } + + bfree (B_L, userpass); + } +#ifdef DIGEST_ACCESS_SUPPORT + } else if (flags & WEBS_AUTH_DIGEST) { + + char_t *digestCalc; /* - * Validate the users password if required (local access is always allowed) - * We compare the decoded form of the password. + * Check digest for equivalence */ - if (*websPassword && !(flags & WEBS_LOCAL_REQUEST)) { + wp->password = umGetUserPassword(userid); - if (password && *password) { - if (gstrcmp(password, websPassword) != 0) { + a_assert(wp->digest); + a_assert(wp->nonce); + a_assert(wp->password); + + digestCalc = websCalcDigest(wp); + a_assert(digestCalc); + + if (gstrcmp(wp->digest, digestCalc) != 0) { websStats.access++; websError(wp, 200, T("Access Denied\nWrong Password")); - websSetPassword(T("")); - return 1; + nRet = 1; } + + bfree (B_L, digestCalc); +#endif } else { /* - * This will cause the browser to display a password / username - * dialog + * No password has been specified */ +#ifdef DIGEST_ACCESS_SUPPORT + if (am == AM_DIGEST) { + wp->flags |= WEBS_AUTH_DIGEST; + } +#endif websStats.errors++; - websError(wp, 401, T("Access Denied\r\n\ - Access to this document requires a password.\ - \r\n")); - return 1; + websError(wp, 401, + T("Access to this document requires a password")); + nRet = 1; + } + } else if (am != AM_FULL) { +/* + * This will cause the browser to display a password / username + * dialog + */ +#ifdef DIGEST_ACCESS_SUPPORT + if (am == AM_DIGEST) { + wp->flags |= WEBS_AUTH_DIGEST; } +#endif + websStats.errors++; + websError(wp, 401, T("Access to this document requires a User ID")); + nRet = 1; } - return 0; + + bfree(B_L, accessLimit); + + return nRet; } /******************************************************************************/ @@ -102,7 +223,7 @@ void websSetPassword(char_t *password) char_t *websGetPassword() { - return websPassword; + return bstrdup(B_L, websPassword); } /******************************************************************************/ diff --git a/cpukit/httpd/socket.c b/cpukit/httpd/socket.c index 81d81eb6fd..200cf232b5 100644 --- a/cpukit/httpd/socket.c +++ b/cpukit/httpd/socket.c @@ -1,66 +1,53 @@ + /* - * socket.c -- Socket support module for UNIX + * sockGen.c -- Posix Socket support module for general posix use * - * Copyright (c) Go Ahead, 1995-1999 + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. */ /******************************** Description *********************************/ /* - * SCO Unix Socket Module. This supports non-blocking buffered socket I/O. + * Posix Socket Module. This supports blocking and non-blocking buffered + * socket I/O. */ +#if (!WIN) || LITTLEFOOT || WEBS + /********************************** Includes **********************************/ -#include -#include #include #include -#include -#if __rtems__ -#include +#if UEMF + #include "uemf.h" +#else + #include + #include + #include + #include "emfInternal.h" #endif -#include "uemf.h" - -/*********************************** Defines **********************************/ - -typedef struct { - char host[64]; /* Host name */ - ringq_t inBuf; /* Input ring queue */ - ringq_t outBuf; /* Output ring queue */ - ringq_t lineBuf; /* Line ring queue */ - socketAccept_t accept; /* Accept handler */ - socketHandler_t handler; /* User I/O handler */ - int handler_data; /* User handler data */ - int sid; /* Index into socket[] */ - int port; /* Port to listen on */ - int flags; /* Current state flags */ - int readyMask; /* Events now ready */ - int interestMask; /* Events interest */ - int error; /* Last error */ - int sock; /* Actual socket handle */ -} socket_t; +#if VXWORKS + #include +#endif + +#if __rtems__ + #include +#endif /************************************ Locals **********************************/ -static socket_t** socketList; /* List of open sockets */ -static int socketMax; /* Maximum size of socket */ -static int socketHighestFd = -1; /* Highest socket fd opened */ +extern socket_t **socketList; /* List of open sockets */ +extern int socketMax; /* Maximum size of socket */ +extern int socketHighestFd; /* Highest socket fd opened */ +static int socketOpenCount = 0; /* Number of task using sockets */ /***************************** Forward Declarations ***************************/ -static int socketAlloc(char* host, int port, socketAccept_t accept, int flags); -static void socketFree(int sid); -static void socketAccept(socket_t* sp); -static int socketGetInput(int sid, char* buf, int toRead, int* errCode); -static int socketDoOutput(socket_t* sp, char* buf, int toWrite, int* errCode); +static void socketAccept(socket_t *sp); static int socketDoEvent(socket_t *sp); -static int socketGetError(); -static int socketWaitForEvent(socket_t* sp, int events, int* errCode); -static int socketNonBlock(socket_t *sp); -static socket_t* socketPtr(int sid); +static int tryAlternateConnect(int sock, struct sockaddr *sockaddr); /*********************************** Code *************************************/ /* @@ -69,6 +56,27 @@ static socket_t* socketPtr(int sid); int socketOpen() { +#if CE || WIN + WSADATA wsaData; +#endif + + if (++socketOpenCount > 1) { + return 0; + } + +#if CE || WIN + if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) { + return -1; + } + if (wsaData.wVersion != MAKEWORD(1,1)) { + WSACleanup(); + return -1; + } +#endif + socketList = NULL; + socketMax = 0; + socketHighestFd = -1; + return 0; } @@ -81,10 +89,13 @@ void socketClose() { int i; - for (i = socketMax; i >= 0; i--) { - if (socketList && socketList[i]) { - socketCloseConnection(i); + if (--socketOpenCount <= 0) { + for (i = socketMax; i >= 0; i--) { + if (socketList && socketList[i]) { + socketCloseConnection(i); + } } + socketOpenCount = 0; } } @@ -93,13 +104,18 @@ void socketClose() * Open a client or server socket. Host is NULL if we want server capability. */ -int socketOpenConnection(char* host, int port, socketAccept_t accept, int flags) +int socketOpenConnection(char *host, int port, socketAccept_t accept, int flags) { +#if ! (NO_GETHOSTBYNAME || VXWORKS) + struct hostent *hostent; /* Host database entry */ +#endif /* ! (NO_GETHOSTBYNAME || VXWORKS) */ socket_t *sp; - struct sockaddr_in sockaddr; - struct hostent *hostent; /* Host database entry */ - int sid, rc; + struct sockaddr_in sockaddr; + int sid, bcast, dgram, rc; + if (port > SOCKET_PORT_MAX) { + return -1; + } /* * Allocate a socket structure */ @@ -121,68 +137,195 @@ int socketOpenConnection(char* host, int port, socketAccept_t accept, int flags) } else { sockaddr.sin_addr.s_addr = inet_addr(host); if (sockaddr.sin_addr.s_addr == INADDR_NONE) { +/* + * If the OS does not support gethostbyname functionality, the macro: + * NO_GETHOSTBYNAME should be defined to skip the use of gethostbyname. + * Unfortunatly there is no easy way to recover, the following code + * simply uses the basicGetHost IP for the sockaddr. + */ + +#if NO_GETHOSTBYNAME + if (strcmp(host, basicGetHost()) == 0) { + sockaddr.sin_addr.s_addr = inet_addr(basicGetAddress()); + } + if (sockaddr.sin_addr.s_addr == INADDR_NONE) { + socketFree(sid); + return -1; + } +#elif VXWORKS + sockaddr.sin_addr.s_addr = (unsigned long) hostGetByName(host); + if (sockaddr.sin_addr.s_addr == NULL) { + errno = ENXIO; + socketFree(sid); + return -1; + } +#else hostent = gethostbyname(host); if (hostent != NULL) { memcpy((char *) &sockaddr.sin_addr, (char *) hostent->h_addr_list[0], (size_t) hostent->h_length); } else { - errno = ENXIO; - socketFree(sid); - return -1; + char *asciiAddress; + char_t *address; + + address = basicGetAddress(); + asciiAddress = ballocUniToAsc(address, gstrlen(address)); + sockaddr.sin_addr.s_addr = inet_addr(asciiAddress); + bfree(B_L, asciiAddress); + if (sockaddr.sin_addr.s_addr == INADDR_NONE) { + errno = ENXIO; + socketFree(sid); + return -1; + } } +#endif /* (NO_GETHOSTBYNAME || VXWORKS) */ } } + bcast = sp->flags & SOCKET_BROADCAST; + if (bcast) { + sp->flags |= SOCKET_DATAGRAM; + } + dgram = sp->flags & SOCKET_DATAGRAM; + /* - * Create the socket. Set the close on exec flag so children don't - * inherit the socket. + * Create the socket. Support for datagram sockets. Set the close on + * exec flag so children don't inherit the socket. */ - sp->sock = socket(AF_INET, SOCK_STREAM, 0); + sp->sock = socket(AF_INET, dgram ? SOCK_DGRAM: SOCK_STREAM, 0); if (sp->sock < 0) { socketFree(sid); return -1; } +#ifndef __NO_FCNTL fcntl(sp->sock, F_SETFD, FD_CLOEXEC); +#endif socketHighestFd = max(socketHighestFd, sp->sock); +/* + * If broadcast, we need to turn on broadcast capability. + */ + if (bcast) { + int broadcastFlag = 1; + if (setsockopt(sp->sock, SOL_SOCKET, SO_BROADCAST, + (char *) &broadcastFlag, sizeof(broadcastFlag)) < 0) { + socketFree(sid); + return -1; + } + } + /* * Host is set if we are the client */ if (host) { /* - * Connect to the remote server + * Connect to the remote server in blocking mode, then go into + * non-blocking mode if desired. */ - if (connect(sp->sock, (struct sockaddr *) &sockaddr, - sizeof(sockaddr)) < 0) { - socketFree(sid); - return -1; - } - socketNonBlock(sp); + if (!dgram) { + if (! (sp->flags & SOCKET_BLOCK)) { +/* + * sockGen.c is only used for Windows products when blocking + * connects are expected. This applies to FieldUpgrader + * agents and open source webserver connectws. Therefore the + * asynchronous connect code here is not compiled. + */ +#if (WIN || CE) && !(LITTLEFOOT || WEBS) + int flag; + sp->flags |= SOCKET_ASYNC; +/* + * Set to non-blocking for an async connect + */ + flag = 1; + if (ioctlsocket(sp->sock, FIONBIO, &flag) == SOCKET_ERROR) { + socketFree(sid); + return -1; + } +#else + socketSetBlock(sid, 1); +#endif /* #if (WIN || CE) && !(LITTLEFOOT || WEBS) */ + + } + if ((rc = connect(sp->sock, (struct sockaddr *) &sockaddr, + sizeof(sockaddr))) < 0 && + (rc = tryAlternateConnect(sp->sock, + (struct sockaddr *) &sockaddr)) < 0) { +#if WIN || CE + if (socketGetError() != EWOULDBLOCK) { + socketFree(sid); + return -1; + } +#else + socketFree(sid); + return -1; + +#endif /* WIN || CE */ + + } + } } else { /* - * Bind to the socket endpoint with resule and the call listen() - ** to start listening + * Bind to the socket endpoint and the call listen() to start listening */ rc = 1; setsockopt(sp->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc)); - if (bind(sp->sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) - < 0) { + if (bind(sp->sock, (struct sockaddr *) &sockaddr, + sizeof(sockaddr)) < 0) { socketFree(sid); return -1; } - sp->flags |= SOCKET_LISTENING; - if (listen(sp->sock, SOMAXCONN) < 0) { - socketFree(sid); - return -1; + if (! dgram) { + if (listen(sp->sock, SOMAXCONN) < 0) { + socketFree(sid); + return -1; + } +#if !UEMF + sp->fileHandle = emfCreateFileHandler(sp->sock, SOCKET_READABLE, + (emfFileProc *) socketAccept, (void *) sp); +#else + sp->flags |= SOCKET_LISTENING; +#endif } - sp->interestMask = SOCKET_READABLE; + sp->handlerMask |= SOCKET_READABLE; + } + +/* + * Set the blocking mode + */ + + if (flags & SOCKET_BLOCK) { + socketSetBlock(sid, 1); + } else { + socketSetBlock(sid, 0); } return sid; } +/******************************************************************************/ +/* + * If the connection failed, swap the first two bytes in the + * sockaddr structure. This is a kludge due to a change in + * VxWorks between versions 5.3 and 5.4, but we want the + * product to run on either. + */ + +static int tryAlternateConnect(int sock, struct sockaddr *sockaddr) +{ +#if VXWORKS + char *ptr; + + ptr = (char *)sockaddr; + *ptr = *(ptr+1); + *(ptr+1) = 0; + return connect(sock, sockaddr, sizeof(struct sockaddr)); +#else + return -1; +#endif /* VXWORKS */ +} + /******************************************************************************/ /* * Close a socket @@ -190,30 +333,25 @@ int socketOpenConnection(char* host, int port, socketAccept_t accept, int flags) void socketCloseConnection(int sid) { - socket_t* sp; + socket_t *sp; if ((sp = socketPtr(sid)) == NULL) { return; } - -/* - * We always flush all output before closing. Unlink from the emf event - * mechanism and then free (and close) the connection - */ - socketFlush(sid, 1); socketFree(sid); } /******************************************************************************/ /* - * Accept a connection. Called by socketDoEvent + * Accept a connection. Called as a callback on incoming connection. */ -static void socketAccept(socket_t* sp) +static void socketAccept(socket_t *sp) { struct sockaddr_in addr; socket_t *nsp; - int len; + size_t len; + char *pString; int newSock, nid; a_assert(sp); @@ -225,462 +363,354 @@ static void socketAccept(socket_t* sp) if ((newSock = accept(sp->sock, (struct sockaddr *) &addr, &len)) < 0) { return; } +#ifndef __NO_FCNTL fcntl(newSock, F_SETFD, FD_CLOEXEC); +#endif socketHighestFd = max(socketHighestFd, newSock); /* * Create a socket structure and insert into the socket list */ - nid = socketAlloc(sp->host, sp->port, sp->accept, 0); + nid = socketAlloc(sp->host, sp->port, sp->accept, sp->flags); nsp = socketList[nid]; a_assert(nsp); nsp->sock = newSock; + nsp->flags &= ~SOCKET_LISTENING; if (nsp == NULL) { return; } /* - * Call the user accept callback, the user must call socketCreateHandler + * Set the blocking mode before calling the accept callback. + */ + + socketSetBlock(nid, (nsp->flags & SOCKET_BLOCK) ? 1: 0); +/* + * Call the user accept callback. The user must call socketCreateHandler * to register for further events of interest. */ if (sp->accept != NULL) { - if ((sp->accept)(nid, inet_ntoa(addr.sin_addr), - ntohs(addr.sin_port)) < 0) { + pString = inet_ntoa(addr.sin_addr); + if ((sp->accept)(nid, pString, ntohs(addr.sin_port), sp->sid) < 0) { socketFree(nid); - return; } +#if VXWORKS + free(pString); +#endif } - socketNonBlock(nsp); } /******************************************************************************/ /* - * Write to a socket. This may block if the underlying socket cannot - * absorb the data. Returns -1 on error, otherwise the number of bytes - * written. + * Get more input from the socket and return in buf. + * Returns 0 for EOF, -1 for errors and otherwise the number of bytes read. */ -int socketWrite(int sid, char* buf, int bufsize) +int socketGetInput(int sid, char *buf, int toRead, int *errCode) { - socket_t* sp; - ringq_t* rq; - int len, bytesWritten, room; + struct sockaddr_in server; + socket_t *sp; + int len, bytesRead; a_assert(buf); - a_assert(bufsize >= 0); + a_assert(errCode); + + *errCode = 0; if ((sp = socketPtr(sid)) == NULL) { return -1; } - -/* - * Loop adding as much data to the output ringq as we can absorb - * Flush when the ringq is too full and continue. - */ - rq = &sp->outBuf; - for (bytesWritten = 0; bufsize > 0; ) { - if ((room = ringqPutBlkMax(rq)) == 0) { - if (socketFlush(sid, 0) < 0) { - return -1; - } - if ((room = ringqPutBlkMax(rq)) == 0) { - break; - } - continue; - } - len = min(room, bufsize); - ringqPutBlk(rq, (unsigned char*) buf, len); - bytesWritten += len; - bufsize -= len; - buf += len; - } - return bytesWritten; -} -/******************************************************************************/ /* - * Read from a socket. Return the number of bytes read if successful. This - * may be less than the requested "bufsize" and may be zero. Return -1 for - * errors. Return 0 for EOF. Otherwise return the number of bytes read. Since - * this may be zero, callers should use socketEof() to distinguish between - * this and EOF. Note: this ignores the line buffer, so a previous socketGets - * which read a partial line may cause a subsequent socketRead to miss - * some data. + * If we have previously seen an EOF condition, then just return */ - -int socketRead(int sid, char* buf, int bufsize) -{ - socket_t* sp; - ringq_t* rq; - int len, room, errCode, bytesRead; - - a_assert(buf); - a_assert(bufsize > 0); - - if ((sp = socketPtr(sid)) == NULL) { - return -1; - } - if (sp->flags & SOCKET_EOF) { return 0; } +#if (WIN || CE) && !(LITTLEFOOT || WEBS) + if ( !(sp->flags & SOCKET_BLOCK) + && ! socketWaitForEvent(sp, FD_CONNECT, errCode)) { + return -1; + } +#endif - rq = &sp->inBuf; - for (bytesRead = 0; bufsize > 0; ) { - len = min(ringqLen(rq), bufsize); - if (len <= 0) { - room = ringqPutBlkMax(rq); - len = socketGetInput(sid, (char*) rq->endp, room, &errCode); - if (len < 0) { - if (errCode == EWOULDBLOCK) { - if (bytesRead >= 0) { - return bytesRead; - } - } - return -1; - - } else if (len == 0) { /* - * This is EOF, but we may have already read some data so pass that - * back first before notifying EOF. The next read will return 0 - * to indicate EOF. + * Read the data */ - return bytesRead; - } - ringqPutBlkAdj(rq, len); - len = min(len, bufsize); + if (sp->flags & SOCKET_DATAGRAM) { + len = sizeof(server); + bytesRead = recvfrom(sp->sock, buf, toRead, 0, + (struct sockaddr *) &server, &len); + } else { + bytesRead = recv(sp->sock, buf, toRead, 0); + } + if (bytesRead < 0) { + if (errno == ECONNRESET) { + return 0; } - memcpy(&buf[bytesRead], rq->servp, len); - ringqGetBlkAdj(rq, len); - bufsize -= len; - bytesRead += len; + *errCode = socketGetError(); + return -1; } return bytesRead; } /******************************************************************************/ /* - * Get a string from a socket. This returns data in *buf in a malloced string - * after trimming the '\n'. If there is zero bytes returned, *buf will be set - * to NULL. It returns -1 for error, EOF or when no complete line yet read. - * Otherwise the length of the line is returned. If a partial line is read - * socketInputBuffered or socketEof can be used to distinguish between EOF - * and partial line still buffered. This routine eats and ignores carriage - * returns. + * Process an event on the event queue */ -int socketGets(int sid, char** buf) +#ifndef UEMF + +static int socketEventProc(void *data, int mask) { - socket_t* sp; - ringq_t* lq; - char c; - int rc, len; + socket_t *sp; + ringq_t *rq; + int sid; - a_assert(buf); + sid = (int) data; + + a_assert(sid >= 0 && sid < socketMax); + a_assert(socketList[sid]); if ((sp = socketPtr(sid)) == NULL) { - return -1; + return 1; } - lq = &sp->lineBuf; - - while (1) { - if ((rc = socketRead(sid, &c, 1)) < 0) { - return rc; - - } else if (rc == 0) { -/* - * If there is a partial line and we are at EOF, pretend we saw a '\n' - */ - if (ringqLen(lq) > 0 && (sp->flags & SOCKET_EOF)) { - c = '\n'; - } else { - return -1; - } - } /* - * If a newline is seen, return the data excluding the new line to the - * caller. If carriage return is seen, just eat it. + * If now writable and flushing in the background, continue flushing */ - if (c == '\n') { - len = ringqLen(lq); - if (len > 0) { - if ((*buf = balloc(B_L, len + 1)) == NULL) { - return -1; - } - memset(*buf, 0, len + 1); - ringqGetBlk(lq, (unsigned char*) *buf, len); + if (mask & SOCKET_WRITABLE) { + if (sp->flags & SOCKET_FLUSHING) { + rq = &sp->outBuf; + if (ringqLen(rq) > 0) { + socketFlush(sp->sid); } else { - *buf = NULL; + sp->flags &= ~SOCKET_FLUSHING; } - return len; - - } else if (c == '\r') { - continue; } - ringqPutc(lq, c); } -} -/******************************************************************************/ /* - * Flush a socket. Do not wait, just initiate output and return. - * This will return -1 on errors and 0 if successful. - */ - -int socketFlush(int sid, int block) -{ - socket_t* sp; - ringq_t* rq; - int len, bytesWritten, errCode; - - a_assert(block == 0 || block == 1); - - if ((sp = socketPtr(sid)) == NULL) { - return -1; - } - rq = &sp->outBuf; - -/* - * Set the background flushing flag which socketDoEvent will check to - * continue the flush. + * Now invoke the users socket handler. NOTE: the handler may delete the + * socket, so we must be very careful after calling the handler. */ - if (!block) { - sp->flags |= SOCKET_FLUSHING; + if (sp->handler && (sp->handlerMask & mask)) { + (sp->handler)(sid, mask & sp->handlerMask, sp->handler_data); } - -/* - * Break from loop if not blocking after initiating output. If we are blocking - * we wait for a write event. - */ - while (ringqLen(rq) > 0) { - len = ringqGetBlkMax(&sp->outBuf); - bytesWritten = socketDoOutput(sp, (char*) rq->servp, len, &errCode); - if (bytesWritten < 0) { - if (errCode == EINTR) { - continue; - } else if (errCode == EWOULDBLOCK || errCode == EAGAIN) { - if (! block) { - return 0; - } - if (socketWaitForEvent(sp, SOCKET_WRITABLE | SOCKET_EXCEPTION, - &errCode)) { - continue; - } - } - return -1; - } - ringqGetBlkAdj(rq, bytesWritten); - if (! block) { - break; - } + if (socketList && sid < socketMax && socketList[sid] == sp) { + socketRegisterInterest(sp, sp->handlerMask); } - return 0; + return 1; } +#endif /* ! UEMF */ /******************************************************************************/ /* - * Return the count of input characters buffered. We look at both the line - * buffer and the input (raw) buffer. Return -1 on error or EOF. + * Define the events of interest */ -int socketInputBuffered(int sid) +void socketRegisterInterest(socket_t *sp, int handlerMask) { - socket_t* sp; + a_assert(sp); - if ((sp = socketPtr(sid)) == NULL) { - return -1; - } - if (socketEof(sid)) { - return -1; + sp->handlerMask = handlerMask; +#if !UEMF + if (handlerMask) { + sp->fileHandle = emfCreateFileHandler(sp->sock, handlerMask, + (emfFileProc *) socketEventProc, (void *) sp->sid); + } else { + emfDeleteFileHandler(sp->fileHandle); } - return ringqLen(&sp->lineBuf) + ringqLen(&sp->inBuf); +#endif /* ! UEMF */ } /******************************************************************************/ /* - * Return true if EOF + * Wait until an event occurs on a socket. Return 1 on success, 0 on failure. + * or -1 on exception (UEMF only) */ -int socketEof(int sid) +int socketWaitForEvent(socket_t *sp, int handlerMask, int *errCode) { - socket_t* sp; + int mask; - if ((sp = socketPtr(sid)) == NULL) { + a_assert(sp); + + mask = sp->handlerMask; + sp->handlerMask |= handlerMask; + while (socketSelect(sp->sid, 1000)) { + if (sp->currentEvents & (handlerMask | SOCKET_EXCEPTION)) { + break; + } + } + sp->handlerMask = mask; + if (sp->currentEvents & SOCKET_EXCEPTION) { return -1; + } else if (sp->currentEvents & handlerMask) { + return 1; } - return sp->flags & SOCKET_EOF; + if (errCode) { + *errCode = errno = EWOULDBLOCK; + } + return 0; } /******************************************************************************/ /* - * Create a user handler for this socket. The handler called whenever there - * is an event of interest as defined by interestMask (SOCKET_READABLE, ...) + * Return TRUE if there is a socket with an event ready to process, */ -void socketCreateHandler(int sid, int interestMask, socketHandler_t handler, - int data) +int socketReady(int sid) { - socket_t* sp; + socket_t *sp; + int all; - if ((sp = socketPtr(sid)) == NULL) { - return; + all = 0; + if (sid < 0) { + sid = 0; + all = 1; } - sp->handler = handler; - sp->handler_data = data; - sp->interestMask = interestMask; -} -/******************************************************************************/ + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (! all) { + break; + } else { + continue; + } + } + if (sp->currentEvents & sp->handlerMask) { + return 1; + } /* - * Delete a handler + * If there is input data, also call select to test for new events */ - -void socketDeleteHandler(int sid) -{ - socket_t* sp; - - if ((sp = socketPtr(sid)) == NULL) { - return; + if (sp->handlerMask & SOCKET_READABLE && socketInputBuffered(sid)) { + socketSelect(sid, 0); + return 1; + } + if (! all) { + break; + } } - sp->handler = NULL; - sp->interestMask = 0; + return 0; } /******************************************************************************/ /* - * Get more input from the socket and return in buf. - * Returns 0 for EOF, -1 for errors and otherwise the number of bytes read. + * Wait for a handle to become readable or writable and return a number of + * noticed events. Timeout is in milliseconds. */ -static int socketGetInput(int sid, char* buf, int toRead, int* errCode) -{ - struct sockaddr_in server; - socket_t* sp; - int len, bytesRead; - - a_assert(buf); - a_assert(errCode); +#if WIN || CE - *errCode = 0; +int socketSelect(int sid, int timeout) +{ + struct timeval tv; + socket_t *sp; + fd_set readFds, writeFds, exceptFds; + int nEvents; + int all, socketHighestFd; /* Highest socket fd opened */ + + FD_ZERO(&readFds); + FD_ZERO(&writeFds); + FD_ZERO(&exceptFds); + socketHighestFd = -1; - if ((sp = socketPtr(sid)) == NULL) { - return -1; - } + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; /* - * If we have previously seen an EOF condition, then just return + * Set the select event masks for events to watch */ - if (sp->flags & SOCKET_EOF) { - return 0; - } + all = nEvents = 0; -/* - * Read the data - */ - if (sp->flags & SOCKET_BROADCAST) { - server.sin_family = AF_INET; - server.sin_addr.s_addr = INADDR_BROADCAST; - server.sin_port = htons((short)(sp->port & 0xFFFF)); - len = sizeof(server); - bytesRead = recvfrom(sp->sock, buf, toRead, 0, - (struct sockaddr*) &server, &len); - } else { - bytesRead = recv(sp->sock, buf, toRead, 0); + if (sid < 0) { + all++; + sid = 0; } - if (bytesRead < 0) { - if (errno == ECONNRESET) { - return 0; + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + continue; } - *errCode = socketGetError(); - return -1; - - } else if (bytesRead == 0) { - sp->flags |= SOCKET_EOF; - } - return bytesRead; -} - -/******************************************************************************/ + a_assert(sp); /* - * Socket output procedure. Return -1 on errors otherwise return the number - * of bytes written. + * Set the appropriate bit in the ready masks for the sp->sock. */ - -static int socketDoOutput(socket_t* sp, char* buf, int toWrite, int* errCode) -{ - struct sockaddr_in server; - int bytes; - - a_assert(sp); - a_assert(buf); - a_assert(toWrite > 0); - a_assert(errCode); - - *errCode = 0; + if (sp->handlerMask & SOCKET_READABLE) { + FD_SET(sp->sock, &readFds); + nEvents++; + if (socketInputBuffered(sid) > 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + if (sp->handlerMask & SOCKET_WRITABLE) { + FD_SET(sp->sock, &writeFds); + nEvents++; + } + if (sp->handlerMask & SOCKET_EXCEPTION) { + FD_SET(sp->sock, &exceptFds); + nEvents++; + } + if (! all) { + break; + } + } /* - * Write the data + * Windows select() fails if no descriptors are set, instead of just sleeping + * like other, nice select() calls. So, if WIN, sleep. */ - if (sp->flags & SOCKET_BROADCAST) { - server.sin_family = AF_INET; - server.sin_addr.s_addr = INADDR_BROADCAST; - server.sin_port = htons((short)(sp->port & 0xFFFF)); - bytes = sendto(sp->sock, buf, toWrite, 0, - (struct sockaddr*) &server, sizeof(server)); - } else { - bytes = send(sp->sock, buf, toWrite, 0); - } - - if (bytes == 0 && bytes != toWrite) { - *errCode = EWOULDBLOCK; - return -1; - } - - if (bytes < 0) { - *errCode = socketGetError(); + if (nEvents == 0) { + Sleep(timeout); + return 0; } - return bytes; -} -/******************************************************************************/ /* - * Return TRUE if there is a socket with an event ready to process, + * Wait for the event or a timeout. */ + nEvents = select(socketHighestFd+1, &readFds, &writeFds, &exceptFds, &tv); -int socketReady() -{ - socket_t *sp; - int i; - - for (i = 0; i < socketMax; i++) { - if ((sp = socketList[i]) == NULL) { + if (all) { + sid = 0; + } + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { continue; - } - if (sp->readyMask & sp->interestMask) { - return 1; + } + + if (FD_ISSET(sp->sock, &readFds) || socketInputBuffered(sid) > 0) { + sp->currentEvents |= SOCKET_READABLE; + } + if (FD_ISSET(sp->sock, &writeFds)) { + sp->currentEvents |= SOCKET_WRITABLE; + } + if (FD_ISSET(sp->sock, &exceptFds)) { + sp->currentEvents |= SOCKET_EXCEPTION; + } + if (! all) { + break; } } - return 0; + + return nEvents; } -/******************************************************************************/ -/* - * Wait for a handle to become readable or writable and return a number of - * noticed events. - */ +#else /* not WIN || CE */ -int socketSelect() +int socketSelect(int sid, int timeout) { - socket_t *sp; - fd_mask *readFds, *writeFds, *exceptFds; - int sid, len, nwords, index, bit, nEvents; + socket_t *sp; + struct timeval tv; + fd_mask *readFds, *writeFds, *exceptFds; + int all, len, nwords, index, bit, nEvents; /* * Allocate and zero the select masks */ - nwords = (socketHighestFd + NFDBITS - 1) / NFDBITS; + nwords = (socketHighestFd + NFDBITS) / NFDBITS; len = nwords * sizeof(int); readFds = balloc(B_L, len); @@ -690,12 +720,26 @@ int socketSelect() exceptFds = balloc(B_L, len); memset(exceptFds, 0, len); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + /* * Set the select event masks for events to watch */ - for (sid = 0; sid < socketMax; sid++) { + all = nEvents = 0; + + if (sid < 0) { + all++; + sid = 0; + } + + for (; sid < socketMax; sid++) { if ((sp = socketList[sid]) == NULL) { - continue; + if (all == 0) { + break; + } else { + continue; + } } a_assert(sp); @@ -708,39 +752,61 @@ int socketSelect() /* * Set the appropriate bit in the ready masks for the sp->sock. */ - if (sp->interestMask & SOCKET_READABLE) { + if (sp->handlerMask & SOCKET_READABLE) { readFds[index] |= bit; + nEvents++; + if (socketInputBuffered(sid) > 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } } - if (sp->interestMask & SOCKET_WRITABLE) { + if (sp->handlerMask & SOCKET_WRITABLE) { writeFds[index] |= bit; + nEvents++; } - if (sp->interestMask & SOCKET_EXCEPTION) { + if (sp->handlerMask & SOCKET_EXCEPTION) { exceptFds[index] |= bit; + nEvents++; + } + if (! all) { + break; } } /* - * Wait for the event or a timeout. + * Wait for the event or a timeout. Reset nEvents to be the number of actual + * events now. */ nEvents = select(socketHighestFd + 1, (fd_set *) readFds, - (fd_set *) writeFds, (fd_set *) exceptFds, NULL); + (fd_set *) writeFds, (fd_set *) exceptFds, &tv); + if (nEvents > 0) { - for (sid = 0; sid < socketMax; sid++) { + if (all) { + sid = 0; + } + for (; sid < socketMax; sid++) { if ((sp = socketList[sid]) == NULL) { - continue; + if (all == 0) { + break; + } else { + continue; + } } index = sp->sock / (NBBY * sizeof(fd_mask)); bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask))); - - if (readFds[index] & bit) { - sp->readyMask |= SOCKET_READABLE; + + if (readFds[index] & bit || socketInputBuffered(sid) > 0) { + sp->currentEvents |= SOCKET_READABLE; } if (writeFds[index] & bit) { - sp->readyMask |= SOCKET_WRITABLE; + sp->currentEvents |= SOCKET_WRITABLE; } if (exceptFds[index] & bit) { - sp->readyMask |= SOCKET_EXCEPTION; + sp->currentEvents |= SOCKET_EXCEPTION; + } + if (! all) { + break; } } } @@ -751,58 +817,70 @@ int socketSelect() return nEvents; } +#endif /* WIN || CE */ /******************************************************************************/ /* * Process socket events */ -void socketProcess() +void socketProcess(int sid) { socket_t *sp; - int sid; + int all; + all = 0; + if (sid < 0) { + all = 1; + sid = 0; + } /* * Process each socket */ - for (sid = 0; sid < socketMax; sid++) { + for (; sid < socketMax; sid++) { if ((sp = socketList[sid]) == NULL) { - continue; + if (! all) { + break; + } else { + continue; + } } - if ((sp->readyMask & sp->interestMask) || - ((sp->interestMask & SOCKET_READABLE) && - socketInputBuffered(sid))) { + if (socketReady(sid)) { socketDoEvent(sp); } + if (! all) { + break; + } } } /******************************************************************************/ /* - * Process and event on the event queue + * Process an event on the event queue */ static int socketDoEvent(socket_t *sp) { - ringq_t* rq; - int sid; + ringq_t *rq; + int sid; a_assert(sp); sid = sp->sid; - if (sp->readyMask & SOCKET_READABLE) { - if (sp->flags & SOCKET_LISTENING) { + if (sp->currentEvents & SOCKET_READABLE) { + if (sp->flags & SOCKET_LISTENING) { socketAccept(sp); - sp->readyMask = 0; + sp->currentEvents = 0; return 1; - } + } + } else { /* * If there is still read data in the buffers, trigger the read handler * NOTE: this may busy spin if the read handler doesn't read the data */ - if (sp->interestMask & SOCKET_READABLE && socketInputBuffered(sid)) { - sp->readyMask |= SOCKET_READABLE; + if (sp->handlerMask & SOCKET_READABLE && socketInputBuffered(sid)) { + sp->currentEvents |= SOCKET_READABLE; } } @@ -810,11 +888,11 @@ static int socketDoEvent(socket_t *sp) /* * If now writable and flushing in the background, continue flushing */ - if (sp->readyMask & SOCKET_WRITABLE) { + if (sp->currentEvents & SOCKET_WRITABLE) { if (sp->flags & SOCKET_FLUSHING) { rq = &sp->outBuf; if (ringqLen(rq) > 0) { - socketFlush(sp->sid, 0); + socketFlush(sp->sid); } else { sp->flags &= ~SOCKET_FLUSHING; } @@ -825,15 +903,14 @@ static int socketDoEvent(socket_t *sp) * Now invoke the users socket handler. NOTE: the handler may delete the * socket, so we must be very careful after calling the handler. */ - if (sp->handler && (sp->interestMask & sp->readyMask)) { - (sp->handler)(sid, sp->interestMask & sp->readyMask, + if (sp->handler && (sp->handlerMask & sp->currentEvents)) { + (sp->handler)(sid, sp->handlerMask & sp->currentEvents, sp->handler_data); /* - * Make sure socket pointer is still valid, then set the readyMask - * to 0. + * Make sure socket pointer is still valid, then reset the currentEvents. */ - if (socketPtr(sid)) { - sp->readyMask = 0; + if (socketList && sid < socketMax && socketList[sid] == sp) { + sp->currentEvents = 0; } } return 1; @@ -841,154 +918,101 @@ static int socketDoEvent(socket_t *sp) /******************************************************************************/ /* - * Allocate a new socket structure + * Set the socket blocking mode */ -static int socketAlloc(char* host, int port, socketAccept_t accept, int flags) +int socketSetBlock(int sid, int on) { - socket_t *sp; - int sid; - - if ((sid = hAlloc((void***) &socketList)) < 0) { - return -1; - } - if ((sp = (socket_t*) balloc(B_L, sizeof(socket_t))) == NULL) { - hFree((void***) &socket, sid); - return -1; - } - memset(sp, 0, sizeof(socket_t)); - socketList[sid] = sp; - if (sid >= socketMax) - socketMax = sid + 1; - - sp->sid = sid; - sp->accept = accept; - sp->port = port; - sp->flags = flags; - - if (host) { - strncpy(sp->host, host, sizeof(sp->host)); - } + socket_t *sp; + unsigned long flag; + int iflag; + int oldBlock; - ringqOpen(&sp->inBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ); - ringqOpen(&sp->outBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ); - ringqOpen(&sp->lineBuf, SOCKET_BUFSIZ, -1); - - return sid; -} - -/******************************************************************************/ -/* - * Free a socket structure - */ - -static void socketFree(int sid) -{ - socket_t* sp; - int i; + flag = iflag = !on; if ((sp = socketPtr(sid)) == NULL) { - return; - } - if (sp->sock >= 0) { - close(sp->sock); + a_assert(0); + return 0; } - - ringqClose(&sp->inBuf); - ringqClose(&sp->outBuf); - ringqClose(&sp->lineBuf); - - bfree(B_L, sp); - socketMax = hFree((void***) &socketList, sid); - -/* - * Calculate the new highest socket number - */ - socketHighestFd = -1; - for (i = 0; i < socketMax; i++) { - if ((sp = socketList[i]) == NULL) { - continue; - } - socketHighestFd = max(socketHighestFd, sp->sock); + oldBlock = (sp->flags & SOCKET_BLOCK); + sp->flags &= ~(SOCKET_BLOCK); + if (on) { + sp->flags |= SOCKET_BLOCK; } -} -/******************************************************************************/ /* - * Validate a socket handle + * Put the socket into block / non-blocking mode */ + if (sp->flags & SOCKET_BLOCK) { +#if CE || WIN + ioctlsocket(sp->sock, FIONBIO, &flag); +#elif ECOS + int off; + off = 0; + ioctl(sp->sock, FIONBIO, &off); +#elif VXWORKS + ioctl(sp->sock, FIONBIO, (int)&iflag); +#else + fcntl(sp->sock, F_SETFL, fcntl(sp->sock, F_GETFL) & ~O_NONBLOCK); +#endif -static socket_t* socketPtr(int sid) -{ - if (sid < 0 || sid >= socketMax || socketList[sid] == NULL) { - a_assert(NULL); - return NULL; + } else { +#if CE || WIN + ioctlsocket(sp->sock, FIONBIO, &flag); +#elif ECOS + int on; + on = 1; + ioctl(sp->sock, FIONBIO, &on); +#elif VXWORKS + ioctl(sp->sock, FIONBIO, (int)&iflag); +#else + fcntl(sp->sock, F_SETFL, fcntl(sp->sock, F_GETFL) | O_NONBLOCK); +#endif } - - a_assert(socketList[sid]); - return socketList[sid]; + return oldBlock; } /******************************************************************************/ /* - * Get the operating system error code + * Return true if a readable socket has buffered data. - not public */ -static int socketGetError() +int socketDontBlock() { - return errno; -} - -/******************************************************************************/ -/* - * Wait until an event occurs on a socket. Return 1 on success, 0 on failure. - */ - -static int socketWaitForEvent(socket_t* sp, int interestMask, int* errCode) -{ - a_assert(sp); + socket_t *sp; + int i; - while (socketSelect()) { - if (sp->readyMask & interestMask) { - break; + for (i = 0; i < socketMax; i++) { + if ((sp = socketList[i]) == NULL || + (sp->handlerMask & SOCKET_READABLE) == 0) { + continue; + } + if (socketInputBuffered(i) > 0) { + return 1; } } - if (sp->readyMask & SOCKET_EXCEPTION) { - return -1; - } else if (sp->readyMask & SOCKET_WRITABLE) { - return 0; - } else { - *errCode = errno = EWOULDBLOCK; - return -1; - } + return 0; } /******************************************************************************/ /* - * Put the socket into non-blocking mode + * Return true if a particular socket buffered data. - not public */ -static int socketNonBlock(socket_t *sp) +int socketSockBuffered(int sock) { - int flags; + socket_t *sp; + int i; - flags = fcntl(sp->sock, F_GETFL) | O_NONBLOCK; - if (fcntl(sp->sock, F_SETFL, flags) < 0) { - return -1; - } + for (i = 0; i < socketMax; i++) { + if ((sp = socketList[i]) == NULL || sp->sock != sock) { + continue; + } + return socketInputBuffered(i); + } return 0; } -/******************************************************************************/ -/* - * Duplicate stdin and stdout - */ - -int DuplicateStdFile (int sid) -{ - if (0 != dup2(socketList[sid]->sock, 0) || 1 != dup2(socketList[sid]->sock, 1)) - return -1; - - return 0; +#endif /* (!WIN) | LITTLEFOOT | WEBS */ -} +/******************************************************************************/ diff --git a/cpukit/httpd/sym.c b/cpukit/httpd/sym.c index c7b47b5e00..cc06cffe2a 100644 --- a/cpukit/httpd/sym.c +++ b/cpukit/httpd/sym.c @@ -1,15 +1,15 @@ /* * sym.c -- Symbol Table module * - * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ /******************************** Description *********************************/ /* - * This module implements a highly efficient generic symbol table with - * update and access routines. Symbols are simple character strings and + * This module implements a highly efficient generic symbol table with + * update and access routines. Symbols are simple character strings and * the values they take can be flexible types as defined by value_t. * This modules allows multiple symbol tables to be created. */ @@ -32,24 +32,51 @@ typedef struct { /* Symbol table descriptor */ /********************************* Globals ************************************/ -static sym_tabent_t **sym; /* List of symbol tables */ -static int sym_max; /* One past the max symbol table */ +static sym_tabent_t **sym; /* List of symbol tables */ +static int symMax; /* One past the max symbol table */ +static int symOpenCount = 0; /* Count of apps using sym */ -static int htIndex; /* Current location in table */ -static sym_t* next; /* Next symbol in iteration */ +static int htIndex; /* Current location in table */ +static sym_t* next; /* Next symbol in iteration */ /**************************** Forward Declarations ****************************/ -static int hashIndex(sym_tabent_t *tp, char_t *name); -static sym_t *hash(sym_tabent_t *tp, char_t *name); -static int calc_prime(int size); +static int hashIndex(sym_tabent_t *tp, char_t *name); +static sym_t *hash(sym_tabent_t *tp, char_t *name); +static int calcPrime(int size); /*********************************** Code *************************************/ +/* + * Open the symbol table subSystem. + */ + +int symSubOpen() +{ + if (++symOpenCount == 1) { + symMax = 0; + sym = NULL; + } + return 0; +} + +/******************************************************************************/ +/* + * Close the symbol table subSystem. + */ + +void symSubClose() +{ + if (--symOpenCount <= 0) { + symOpenCount = 0; + } +} + +/******************************************************************************/ /* * Create a symbol table. */ -sym_fd_t symOpen(int hash_size) +sym_fd_t symOpen(int hash_size) { sym_fd_t sd; sym_tabent_t *tp; @@ -67,20 +94,20 @@ sym_fd_t symOpen(int hash_size) * Create a new symbol table structure and zero */ if ((tp = (sym_tabent_t*) balloc(B_L, sizeof(sym_tabent_t))) == NULL) { - sym_max = hFree((void***) &sym, sd); + symMax = hFree((void***) &sym, sd); return -1; } memset(tp, 0, sizeof(sym_tabent_t)); - if (sd >= sym_max) { - sym_max = sd + 1; + if (sd >= symMax) { + symMax = sd + 1; } - a_assert(0 <= sd && sd < sym_max); + a_assert(0 <= sd && sd < symMax); sym[sd] = tp; /* * Now create the hash table for fast indexing. */ - tp->hash_size = calc_prime(hash_size); + tp->hash_size = calcPrime(hash_size); tp->hash_table = (sym_t**) balloc(B_L, tp->hash_size * sizeof(sym_t*)); a_assert(tp->hash_table); memset(tp->hash_table, 0, tp->hash_size * sizeof(sym_t*)); @@ -94,13 +121,13 @@ sym_fd_t symOpen(int hash_size) * to free resources associated with each symbol table entry. */ -void symClose(sym_fd_t sd, void (*cleanup)(sym_t *symp)) +void symClose(sym_fd_t sd) { sym_tabent_t *tp; sym_t *sp, *forw; - int i; + int i; - a_assert(0 <= sd && sd < sym_max); + a_assert(0 <= sd && sd < symMax); tp = sym[sd]; a_assert(tp); @@ -110,30 +137,18 @@ void symClose(sym_fd_t sd, void (*cleanup)(sym_t *symp)) for (i = 0; i < tp->hash_size; i++) { for (sp = tp->hash_table[i]; sp; sp = forw) { forw = sp->forw; - if (cleanup) { - (*cleanup)(sp); - } valueFree(&sp->name); + valueFree(&sp->content); bfree(B_L, (void*) sp); sp = forw; } } bfree(B_L, (void*) tp->hash_table); - sym_max = hFree((void***) &sym, sd); + symMax = hFree((void***) &sym, sd); bfree(B_L, (void*) tp); } -/******************************************************************************/ -/* - * Default callback for freeing the value. - */ - -void symFreeVar(sym_t* sp) -{ - valueFree(&sp->content); -} - /******************************************************************************/ /* * Return the first symbol in the hashtable if there is one. This call is used @@ -145,9 +160,9 @@ sym_t* symFirst(sym_fd_t sd) { sym_tabent_t *tp; sym_t *sp, *forw; - int i; + int i; - a_assert(0 <= sd && sd < sym_max); + a_assert(0 <= sd && sd < symMax); tp = sym[sd]; a_assert(tp); @@ -180,9 +195,9 @@ sym_t* symNext(sym_fd_t sd) { sym_tabent_t *tp; sym_t *sp, *forw; - int i; + int i; - a_assert(0 <= sd && sd < sym_max); + a_assert(0 <= sd && sd < symMax); tp = sym[sd]; a_assert(tp); @@ -219,9 +234,10 @@ sym_t *symLookup(sym_fd_t sd, char_t *name) sym_t *sp; char_t *cp; - a_assert(0 <= sd && sd < sym_max); - tp = sym[sd]; - a_assert(tp); + a_assert(0 <= sd && sd < symMax); + if ((tp = sym[sd]) == NULL) { + return NULL; + } if (name == NULL || *name == '\0') { return NULL; @@ -241,8 +257,10 @@ sym_t *symLookup(sym_fd_t sd, char_t *name) /******************************************************************************/ /* - * Enter a symbol into the table. If already there, update its value. - * Always succeeds if memory available. + * Enter a symbol into the table. If already there, update its value. + * Always succeeds if memory available. We allocate a copy of "name" here + * so it can be a volatile variable. The value "v" is just a copy of the + * passed in value, so it MUST be persistent. */ sym_t *symEnter(sym_fd_t sd, char_t *name, value_t v, int arg) @@ -252,13 +270,13 @@ sym_t *symEnter(sym_fd_t sd, char_t *name, value_t v, int arg) char_t *cp; int hindex; - a_assert(name && *name); - a_assert(0 <= sd && sd < sym_max); + a_assert(name); + a_assert(0 <= sd && sd < symMax); tp = sym[sd]; a_assert(tp); /* - * Calculate the first daisy-chain from the hash table. If non-zero, then + * Calculate the first daisy-chain from the hash table. If non-zero, then * we have daisy-chain, so scan it and look for the symbol. */ last = NULL; @@ -333,12 +351,12 @@ int symDelete(sym_fd_t sd, char_t *name) int hindex; a_assert(name && *name); - a_assert(0 <= sd && sd < sym_max); + a_assert(0 <= sd && sd < symMax); tp = sym[sd]; a_assert(tp); /* - * Calculate the first daisy-chain from the hash table. If non-zero, then + * Calculate the first daisy-chain from the hash table. If non-zero, then * we have daisy-chain, so scan it and look for the symbol. */ last = NULL; @@ -355,7 +373,7 @@ int symDelete(sym_fd_t sd, char_t *name) if (sp == (sym_t*) NULL) { /* Not Found */ return -1; } - + /* * Unlink and free the symbol. Last will be set if the element to be deleted * is not first in the chain. @@ -366,6 +384,7 @@ int symDelete(sym_fd_t sd, char_t *name) tp->hash_table[hindex] = sp->forw; } valueFree(&sp->name); + valueFree(&sp->content); bfree(B_L, (void*) sp); return 0; @@ -393,13 +412,13 @@ static sym_t *hash(sym_tabent_t *tp, char_t *name) static int hashIndex(sym_tabent_t *tp, char_t *name) { - unsigned int sum; - int i; + unsigned int sum; + int i; a_assert(tp); /* * Add in each character shifted up progressively by 7 bits. The shift - * amount is rounded so as to not shift too far. It thus cycles with each + * amount is rounded so as to not shift too far. It thus cycles with each * new cycle placing character shifted up by one bit. */ i = 0; @@ -416,13 +435,14 @@ static int hashIndex(sym_tabent_t *tp, char_t *name) * Check if this number is a prime */ -static int is_prime(int n) +static int isPrime(int n) { - int i; + int i, max; a_assert(n > 0); - for (i = 2; i < n; i++) { + max = n / 2; + for (i = 2; i <= max; i++) { if (n % i == 0) { return 0; } @@ -435,14 +455,14 @@ static int is_prime(int n) * Calculate the largest prime smaller than size. */ -static int calc_prime(int size) +static int calcPrime(int size) { int count; a_assert(size > 0); for (count = size; count > 0; count--) { - if (is_prime(count)) { + if (isPrime(count)) { return count; } } @@ -450,3 +470,4 @@ static int calc_prime(int size) } /******************************************************************************/ + diff --git a/cpukit/httpd/uemf.c b/cpukit/httpd/uemf.c index ef7c8f3135..aa177849a2 100644 --- a/cpukit/httpd/uemf.c +++ b/cpukit/httpd/uemf.c @@ -1,7 +1,7 @@ /* * uemf.c -- GoAhead Micro Embedded Management Framework * - * Copyright (c) Go Ahead Software, Inc., 1995-1999 + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -22,31 +22,62 @@ int emfInst; /* Application instance handle */ +/****************************** Forward Declarations **************************/ + +extern void defaultErrorHandler(int etype, char_t *buf); +static void (*errorHandler)(int etype, char_t *msg) = defaultErrorHandler; + +extern void defaultTraceHandler(int level, char_t *buf); +static void (*traceHandler)(int level, char_t *buf) = defaultTraceHandler; + /************************************* Code ***********************************/ /* * Error message that doesn't need user attention. Customize this code - * to direct error messages to whereever the developer wishes + * to direct error messages to wherever the developer wishes */ -void error(E_ARGS_DEC, int flags, char_t *fmt, ...) +void error(E_ARGS_DEC, int etype, char_t *fmt, ...) { - va_list arglist; - - va_start(arglist, fmt); - - if (flags & E_LOG) { - /* Log error message */ + va_list args; + char_t *fmtBuf, *buf; - } else if (flags & E_ASSERT) { - /* Assert message */ + va_start(args, fmt); + fmtValloc(&fmtBuf, E_MAX_ERROR, fmt, args); - } else if (flags & E_USER) { - /* Display message to the user */ + if (etype == E_LOG) { + fmtAlloc(&buf, E_MAX_ERROR, T("%s\n"), fmtBuf); +#if DEV + } else if (etype == E_ASSERT) { + fmtAlloc(&buf, E_MAX_ERROR, + T("Assertion %s, failed at %s %d\n"), fmtBuf, E_ARGS); +#endif + } else if (etype == E_USER) { + fmtAlloc(&buf, E_MAX_ERROR, T("%s\n"), fmtBuf); + } + va_end(args); + bfree(B_L, fmtBuf); + + if (errorHandler) { + errorHandler(etype, buf); } - vprintf (fmt, arglist); - va_end(arglist); + bfreeSafe(B_L, buf); +} + +/******************************************************************************/ +/* + * Replace the default error handler. Return pointer to old error handler. + */ + +void (*errorSetHandler(void (*function)(int etype, char_t *msg))) \ + (int etype, char_t *msg) +{ + void (*oldHandler)(int etype, char_t *buf); + + oldHandler = errorHandler; + errorHandler = function; + return oldHandler; } /******************************************************************************/ @@ -54,21 +85,48 @@ void error(E_ARGS_DEC, int flags, char_t *fmt, ...) * Trace log. Customize this function to log trace output */ -void goahead_trace(int level, char_t *afmt, ...) +void trace(int level, char_t *fmt, ...) { -#if DEBUG va_list args; char_t *buf; - va_start(args, afmt); - buf = NULL; - gvsnprintf(&buf, VALUE_MAX_STRING, afmt, args); - if (buf) { - gprintf(buf); - bfree(B_L, buf); + va_start(args, fmt); + fmtValloc(&buf, VALUE_MAX_STRING, fmt, args); + + if (traceHandler) { + traceHandler(level, buf); } + bfreeSafe(B_L, buf); va_end(args); -#endif +} + +/******************************************************************************/ +/* + * Trace log. Customize this function to log trace output + */ + +void traceRaw(char_t *buf) +{ + if (traceHandler) { + traceHandler(0, buf); + } +} + +/******************************************************************************/ +/* + * Replace the default trace handler. Return a pointer to the old handler. + */ + +void (*traceSetHandler(void (*function)(int level, char_t *buf))) + (int level, char *buf) +{ + void (*oldHandler)(int level, char_t *buf); + + oldHandler = traceHandler; + if (function) { + traceHandler = function; + } + return oldHandler; } /******************************************************************************/ @@ -197,4 +255,27 @@ char_t *stritoa(int n, char_t *string, int width) } /******************************************************************************/ +/* + * Stubs + */ +char_t *basicGetProduct() +{ + return T("uemf"); +} + +char_t *basicGetAddress() +{ + return T("localhost"); +} + +int errorOpen(char_t *pname) +{ + return 0; +} + +void errorClose() +{ +} + +/******************************************************************************/ diff --git a/cpukit/httpd/uemf.h b/cpukit/httpd/uemf.h index 8945907dfb..59533aac00 100644 --- a/cpukit/httpd/uemf.h +++ b/cpukit/httpd/uemf.h @@ -1,7 +1,7 @@ /* - * uemf.h -- Go Ahead Micro Embedded Management Framework Header + * uemf.h -- GoAhead Micro Embedded Management Framework Header * - * Copyright (c) Go Ahead Software, Inc., 1995-1999 + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -12,7 +12,7 @@ /******************************** Description *********************************/ /* - * Go Ahead Web Server header. This defines the Web public APIs + * GoAhead Web Server header. This defines the Web public APIs */ /******************************* Per O/S Includes *****************************/ @@ -29,7 +29,9 @@ #include #include #include -#endif + #include + #include +#endif /* WIN */ #if CE #include @@ -37,15 +39,26 @@ #include #include #include "CE/wincompat.h" -#endif + #include +#endif /* CE */ #if NW #include -#endif +#endif /* NW */ + +#if SCOV5 + #include + #include + #include "sys/socket.h" + #include "sys/select.h" + #include "netinet/in.h" + #include "arpa/inet.h" + #include "netdb.h" +#endif /* SCOV5 */ #if UNIX #include -#endif +#endif /* UNIX */ #if LINUX || __rtems__ #include @@ -61,7 +74,9 @@ #include #include #include -#endif + #include + #include +#endif /* LINUX */ #if LYNX #include @@ -74,13 +89,15 @@ #include #include #include -#endif + #include + #include +#endif /* LYNX */ #if UW #include -#endif +#endif /* UW */ -#if VXW486 +#if VXWORKS #include #include #include @@ -90,7 +107,25 @@ #include #include #include -#endif + #include + #include +#endif /* VXWORKS */ + +#if SOLARIS + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif /* SOLARIS */ #if QNX4 #include @@ -100,7 +135,20 @@ #include #include #include -#endif + #include + #include + #include + #include +#endif /* QNX4 */ + +#if ECOS + #include + #include + #include + #include + #include + #include +#endif /* ECOS */ /********************************** Includes **********************************/ @@ -108,6 +156,69 @@ #include #include +#if ! WEBS +#include "messages.h" +#endif /* ! WEBS */ + +/******************************* Per O/S Defines *****************************/ + +#if UW + #define __NO_PACK 1 +#endif /* UW */ + +#if SCOV5 || VXWORKS || LINUX || LYNX || __rtems__ +#ifndef O_BINARY +#define O_BINARY 0 +#endif /* O_BINARY */ +#define SOCKET_ERROR -1 +#endif /* SCOV5 || VXWORKS || LINUX || LYNX */ + +#if WIN || CE +/* + * __NO_FCNTL means can't access fcntl function. Fcntl.h is still available. + */ +#define __NO_FCNTL 1 + +#undef R_OK +#define R_OK 4 +#undef W_OK +#define W_OK 2 +#undef X_OK +#define X_OK 1 +#undef F_OK +#define F_OK 0 +#endif /* WIN || CE */ + +#if LINUX && !__rtems__ && ! _STRUCT_TIMEVAL +struct timeval +{ + time_t tv_sec; /* Seconds. */ + time_t tv_usec; /* Microseconds. */ +}; +#define _STRUCT_TIMEVAL 1 +#endif /* LINUX && ! _STRUCT_TIMEVAL */ + +#if ECOS + #define O_RDONLY 1 + #define O_BINARY 2 + + #define __NO_PACK 1 + #define __NO_EJ_FILE 1 + #define __NO_CGI_BIN 1 + #define __NO_FCNTL 1 + +/* + * #define LIBKERN_INLINE to avoid kernel inline functions + */ + #define LIBKERN_INLINE + +#endif /* ECOS */ + +#if QNX4 + typedef long fd_mask; + #define NFDBITS (sizeof (fd_mask) * NBBY) /* bits per mask */ +#endif /* QNX4 */ + /********************************** Unicode ***********************************/ /* * Constants and limits. Also FNAMESIZE and PATHSIZE are currently defined @@ -119,34 +230,85 @@ #define XML_MAX 4096 /* Maximum size for tags/tokens */ #define BUF_MAX 4096 /* General sanity check for bufs */ -/* - * Type for unicode systems - */ -#if UNICODE +#if LITTLEFOOT || WEBS +#define LF_BUF_MAX (510) +#define LF_PATHSIZE LF_BUF_MAX +#else +#define LF_BUF_MAX BUF_MAX +#define LF_PATHSIZE PATHSIZE +#define UPPATHSIZE PATHSIZE +#endif /* LITTLEFOOT || WEBS */ +#ifndef CHAR_T_DEFINED +#define CHAR_T_DEFINED 1 +#if UNICODE /* * To convert strings to UNICODE. We have a level of indirection so things * like T(__FILE__) will expand properly. */ -#define T(x) __TXT(x) -#define __TXT(s) L ## s -typedef unsigned short char_t; -typedef unsigned short uchar_t; +#define T(x) __TXT(x) +#define __TXT(s) L ## s +typedef unsigned short char_t; +typedef unsigned short uchar_t; /* * Text size of buffer macro. A buffer bytes will hold (size / char size) * characters. */ -#define TSZ(x) (sizeof(x) / sizeof(char_t)) +#define TSZ(x) (sizeof(x) / sizeof(char_t)) /* * How many ASCII bytes are required to represent this UNICODE string? */ -#define TASTRL(x) ((wcslen(x) + 1) * sizeof(char_t)) +#define TASTRL(x) ((wcslen(x) + 1) * sizeof(char_t)) + +#else +#define T(s) s +typedef char char_t; +#define TSZ(x) (sizeof(x)) +#define TASTRL(x) (strlen(x) + 1) +#if WIN +typedef unsigned char uchar_t; +#endif /* WIN */ +#endif /* UNICODE */ + +#endif /* ! CHAR_T_DEFINED */ + +/* + * "Boolean" constants + */ + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* + * GoAhead Copyright. + */ +#define GOAHEAD_COPYRIGHT \ + T("Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.") + +/* + * The following include has to be after the unicode defines. By putting it + * here, many modules in various parts of the tree are cleaner. + */ +#if LITTLEFOOT && INMEM + #include "lf/inmem.h" +#endif /* LITTLEFOOT && INMEM */ + +/* + * Type for unicode systems + */ +#if UNICODE #define gmain wmain +#define gasctime _wasctime #define gsprintf swprintf #define gprintf wprintf #define gfprintf fwprintf @@ -165,15 +327,23 @@ typedef unsigned short uchar_t; #define gstrrchr wcsrchr #define gstrtok wcstok #define gstrnset wcsnset +#define gstrrchr wcsrchr #define gstrstr wcsstr +#define gstrtol wcstol #define gfopen _wfopen #define gopen _wopen +#define gclose close #define gcreat _wcreat #define gfgets fgetws #define gfputs fputws +#define gfscanf fwscanf +#define ggets _getws +#define glseek lseek #define gunlink _wunlink +#define gread read #define grename _wrename +#define gwrite write #define gtmpnam _wtmpnam #define gtempnam _wtempnam #define gfindfirst _wfindfirst @@ -182,24 +352,34 @@ typedef unsigned short uchar_t; #define gfindclose _findclose #define gstat _wstat #define gaccess _waccess +#define gchmod _wchmod typedef struct _stat gstat_t; #define gmkdir _wmkdir #define gchdir _wchdir #define grmdir _wrmdir -#define gremove _wremove #define ggetcwd _wgetcwd #define gtolower towlower #define gtoupper towupper +#if CE +#define gisspace isspace +#define gisdigit isdigit +#define gisxdigit isxdigit +#define gisupper isupper +#define gislower islower +#define gisprint isprint +#else +#define gremove _wremove #define gisspace iswspace #define gisdigit iswdigit #define gisxdigit iswxdigit -#define gisalnum iswalnum -#define gisalpha iswalpha #define gisupper iswupper #define gislower iswlower +#endif /* if CE */ +#define gisalnum iswalnum +#define gisalpha iswalpha #define gatoi(s) wcstol(s, NULL, 10) #define gctime _wctime @@ -208,14 +388,57 @@ typedef struct _stat gstat_t; #else /* ! UNICODE */ -#define T(s) s -#define TSZ(x) (sizeof(x)) -#define TASTRL(x) (strlen(x) + 1) -typedef char char_t; -#if WIN -typedef unsigned char uchar_t; -#endif +#ifndef gopen +#if INMEM +#define gclose imClose +#define gclosedir imClosedir +#define gchdir imChdir +#define gchmod imChmod +#define ggetcwd imGetcwd +#define glseek imLseek +#define gloadModule imLoadModule +#define gmkdir imMkdir +#define gopen imOpen +#define gopendir imOpendir +#define gread imRead +#define greaddir imReaddir +#define grename imRename +#define grmdir imRmdir +#define gstat imStat +#define gunlink imUnlink +#define gwrite imWrite +#else +#define gclose close +#define gclosedir closedir +#if VXWORKS +#define gchdir vxchdir +#define gmkdir vxmkdir +#define grmdir vxrmdir +#else +#if LYNX || LINUX || SOLARIS +#define gmkdir(s) mkdir(s,0755) +#else +#define gmkdir mkdir +#endif /* LYNX || LINUX || SOLARIS */ +#define grmdir rmdir +#define gchdir chdir +#endif /* VXWORKS */ +#define gchmod chmod +#define ggetcwd getcwd +#define glseek lseek +#define gloadModule loadModule +#define gopen open +#define gopendir opendir +#define gread read +#define greaddir readdir +#define grename rename +#define gstat stat +#define gunlink unlink +#define gwrite write +#endif /* INMEM */ +#endif /* ! gopen */ +#define gasctime asctime #define gsprintf sprintf #define gprintf printf #define gfprintf fprintf @@ -229,36 +452,32 @@ typedef unsigned char uchar_t; #define gstrcat strcat #define gstrcmp strcmp #define gstrncmp strncmp -#define gstricmp stricmp +#define gstricmp strcmpci #define gstrchr strchr #define gstrrchr strrchr #define gstrtok strtok #define gstrnset strnset +#define gstrrchr strrchr #define gstrstr strstr +#define gstrtol strtol #define gfopen fopen -#define gopen open #define gcreat creat #define gfgets fgets #define gfputs fputs -#define gunlink unlink -#define grename rename +#define gfscanf fscanf +#define ggets gets #define gtmpnam tmpnam #define gtempnam tempnam #define gfindfirst _findfirst #define gfinddata_t _finddata_t #define gfindnext _findnext #define gfindclose _findclose -#define gstat stat #define gaccess access typedef struct stat gstat_t; -#define gmkdir mkdir -#define gchdir chdir -#define grmdir rmdir #define gremove remove -#define ggetcwd getcwd #define gtolower tolower #define gtoupper toupper @@ -274,16 +493,23 @@ typedef struct stat gstat_t; #define gctime ctime #define ggetenv getenv #define gexecvp execvp -#ifndef VXW486 +#ifndef VXWORKS #define gmain main -#endif -#endif +#endif /* ! VXWORKS */ +#if VXWORKS +#define fcntl(a, b, c) +#endif /* VXWORKS */ +#endif /* ! UNICODE */ /********************************** Defines ***********************************/ -#define FNAMESIZE 256 /* Max length of file names */ +#ifndef FNAMESIZE +#define FNAMESIZE 254 /* Max length of file names */ +#endif /* FNAMESIZE */ #define E_MAX_ERROR 4096 +#define URL_MAX 4096 + /* * Error types */ @@ -291,79 +517,84 @@ typedef struct stat gstat_t; #define E_LOG 0x2 /* Log error to log file */ #define E_USER 0x3 /* Error that must be displayed */ -#define E_L T(__FILE__), __LINE__ -#define E_ARGS_DEC char_t *file, int line -#define E_ARGS file, line +#define E_L T(__FILE__), __LINE__ +#define E_ARGS_DEC char_t *file, int line +#define E_ARGS file, line -#if ASSERT - #define a_assert(C) if (C) ; else error(E_L, E_ASSERT, T("%s"), #C) +#if ASSERT || ASSERT_CE + #define a_assert(C) if (C) ; else error(E_L, E_ASSERT, T("%s"), T(#C)) #else #define a_assert(C) if (1) ; else -#endif - -#define VALUE_VALID { {0}, integer, 1 } -#define VALUE_INVALID { {0}, undefined, 0 } - -/* - * Allocation flags - */ -#define VALUE_ALLOCATE 0x1 - -#define value_numeric(t) (t == integer) -#define value_str(t) (t >= string || t <= bytes) -#define value_ok(t) (t > undefined && t <= symbol) +#endif /* ASSERT || ASSERT_CE */ +/******************************************************************************/ +/* VALUE */ +/******************************************************************************/ /* * These values are not prefixed so as to aid code readability */ -#if !defined(UW) && !defined(__rtems__) -#pragma pack(2) -#endif typedef enum { undefined = 0, - integer = 1, - string = 2, - bytes = 3, - errmsg = 4 -} value_type_t; + byteint = 1, + shortint = 2, + integer = 3, + hex = 4, + percent = 5, + octal = 6, + big = 7, + flag = 8, + floating = 9, + string = 10, + bytes = 11, + symbol = 12, + errmsg = 13 +} vtype_t; + +#ifndef __NO_PACK +#pragma pack(2) +#endif /* _NO_PACK */ -/* - * In UW, bit fields default to unsigned unless explicitly defined as signed. - * Unfortunately, enum become ints, but not explicitly signed. Thus, if using - * an enum type in a bit field, it becomes unsigned, but we need signed. So - * for UW we declare value_type_t to be a signed int to make this all work. - */ typedef struct { union { + char flag; + char byteint; + short shortint; + char percent; long integer; + long hex; + long octal; + long big[2]; +#if FLOATING_POINT_SUPPORT + double floating; +#endif /* FLOATING_POINT_SUPPORT */ char_t *string; - char_t *bytes; + char *bytes; char_t *errmsg; void *symbol; } value; -#if UW - signed int type : 8; -#else - value_type_t type : 8; -#endif - - unsigned int valid : 1; - unsigned int user_def_1 : 1; - unsigned int allocated : 1; /* String was balloced */ + vtype_t type : 16; + unsigned int valid : 8; + unsigned int allocated : 8; /* String was balloced */ } value_t; +#ifndef __NO_PACK +#pragma pack() +#endif /* __NO_PACK */ + /* - * Extract a string from the value depending whether inline or via pointer + * Allocation flags */ -#define value_strget(v) \ - (((v)->type == bytes) ? (v)->value.bytes : (v)->value.string) +#define VALUE_ALLOCATE 0x1 -#if !defined(UW) && !defined(__rtems__) -#pragma pack() -#endif +#define value_numeric(t) (t >= byteint && t <= big) +#define value_str(t) (t >= string && t <= bytes) +#define value_ok(t) (t > undefined && t <= symbol) + +#define VALUE_VALID { {0}, integer, 1 } +#define VALUE_INVALID { {0}, undefined, 0 } /******************************************************************************/ /* @@ -417,16 +648,28 @@ typedef struct { } ringq_t; /* - * Block allocation definitions + * Block allocation (balloc) definitions */ -#define B_L __FILE__, __LINE__ -#define B_ARGS_DEC char *file, int line +#ifdef B_STATS +#ifndef B_L +#define B_L T(__FILE__), __LINE__ +#define B_ARGS_DEC char_t *file, int line #define B_ARGS file, line +#endif /* B_L */ +#endif /* B_STATS */ /* * Block classes are: 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, * 16384, 32768, 65536 */ +typedef struct { + union { + void *next; /* Pointer to next in q */ + int size; /* Actual requested size */ + } u; + int flags; /* Per block allocation flags */ +} bType; + #define B_SHIFT 4 /* Convert size to class */ #define B_ROUND ((1 << (B_SHIFT)) - 1) #define B_MAX_CLASS 13 /* Maximum class number + 1 */ @@ -438,13 +681,12 @@ typedef struct { #define B_MAX_BLOCKS (64 * 1024) /* Maximum allocated blocks */ /* - * Flags. The integer value is used as an arbitrary value to fill the flags. + * Flags. The integrity value is used as an arbitrary value to fill the flags. */ - -#define B_USE_MALLOC 0x1 /* Okay to use malloc if required */ -#define B_USER_BUF 0x2 /* User supplied buffer for mem */ #define B_INTEGRITY 0x8124000 /* Integrity value */ #define B_INTEGRITY_MASK 0xFFFF000 /* Integrity mask */ +#define B_USE_MALLOC 0x1 /* Okay to use malloc if required */ +#define B_USER_BUF 0x2 /* User supplied buffer for mem */ /* * The symbol table record for each symbol entry @@ -459,15 +701,68 @@ typedef struct sym_t { typedef int sym_fd_t; /* Returned by symOpen */ +/* + * Script engines + */ +#define EMF_SCRIPT_JSCRIPT 0 /* javascript */ +#define EMF_SCRIPT_TCL 1 /* tcl */ +#define EMF_SCRIPT_EJSCRIPT 2 /* Ejscript */ +#define EMF_SCRIPT_MAX 3 + +#define MAXINT INT_MAX +#define BITSPERBYTE 8 +#define BITS(type) (BITSPERBYTE * (int) sizeof(type)) +#define STRSPACE T("\t \n\r\t") + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif /* max */ + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif /* min */ + +/******************************************************************************/ +/* CRON */ +/******************************************************************************/ + +typedef struct { + char_t *minute; + char_t *hour; + char_t *day; + char_t *month; + char_t *dayofweek; +} cron_t; + +extern long cronUntil(cron_t *cp, int period, time_t testTime); +extern int cronAlloc(cron_t *cp, char_t *str); +extern int cronFree(cron_t *cp); + +/******************************************************************************/ +/* SOCKET */ +/******************************************************************************/ /* * Socket flags */ + +#if (WIN || CE) && WEBS +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ENETDOWN WSAENETDOWN +#define ECONNRESET WSAECONNRESET +#endif /* (WIN || CE) && WEBS) */ + #define SOCKET_EOF 0x1 /* Seen end of file */ #define SOCKET_CONNECTING 0x2 /* Connect in progress */ #define SOCKET_BROADCAST 0x4 /* Broadcast mode */ #define SOCKET_PENDING 0x8 /* Message pending on this socket */ #define SOCKET_FLUSHING 0x10 /* Background flushing */ -#define SOCKET_LISTENING 0x20 /* Server socket listening */ +#define SOCKET_DATAGRAM 0x20 /* Use datagrams */ +#define SOCKET_ASYNC 0x40 /* Use async connect */ +#define SOCKET_BLOCK 0x80 /* Use blocking I/O */ +#define SOCKET_LISTENING 0x100 /* Socket is server listener */ +#define SOCKET_CLOSING 0x200 /* Socket is closing */ + +#define SOCKET_PORT_MAX 0xffff /* Max Port size */ /* * Socket error values @@ -485,181 +780,261 @@ typedef int sym_fd_t; /* Returned by symOpen */ #define SOCKET_READABLE 0x2 /* Make socket readable */ #define SOCKET_WRITABLE 0x4 /* Make socket writable */ #define SOCKET_EXCEPTION 0x8 /* Interested in exceptions */ +#define EMF_SOCKET_MESSAGE (WM_USER+13) -#define SOCKET_BUFSIZ 512 /* Underlying buffer size */ +#if LITTLEFOOT +#define SOCKET_BUFSIZ 510 /* Underlying buffer size */ +#else +#define SOCKET_BUFSIZ 1024 /* Underlying buffer size */ +#endif /* LITTLEFOOT */ typedef void (*socketHandler_t)(int sid, int mask, int data); -typedef int (*socketAccept_t)(int sid, char *ipaddr, int port); +typedef int (*socketAccept_t)(int sid, char *ipaddr, int port, + int listenSid); +typedef struct { + char host[64]; /* Host name */ + ringq_t inBuf; /* Input ring queue */ + ringq_t outBuf; /* Output ring queue */ + ringq_t lineBuf; /* Line ring queue */ + socketAccept_t accept; /* Accept handler */ + socketHandler_t handler; /* User I/O handler */ + int handler_data; /* User handler data */ + int handlerMask; /* Handler events of interest */ + int sid; /* Index into socket[] */ + int port; /* Port to listen on */ + int flags; /* Current state flags */ + int sock; /* Actual socket handle */ + int fileHandle; /* ID of the file handler */ + int interestEvents; /* Mask of events to watch for */ + int currentEvents; /* Mask of ready events (FD_xx) */ + int selectEvents; /* Events being selected */ + int saveMask; /* saved Mask for socketFlush */ + int error; /* Last error */ +} socket_t; +/********************************* Prototypes *********************************/ /* - * Script engines + * Balloc module + * */ -#define EMF_SCRIPT_JSCRIPT 0 /* javascript */ -#define EMF_SCRIPT_TCL 1 /* tcl */ -#define EMF_SCRIPT_EJSCRIPT 2 /* Ejscript */ -#define EMF_SCRIPT_MAX 3 -#define MAXINT INT_MAX -#define BITSPERBYTE 8 -#define BITS(type) (BITSPERBYTE * (int) sizeof(type)) - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif +extern void bclose(); +extern int bopen(void *buf, int bufsize, int flags); -/******************************* Per O/S Defines ******************************/ +/* + * Define NO_BALLOC to turn off our balloc module altogether + * #define NO_BALLOC 1 + */ -#if VXW486 || LINUX || __rtems__ || LYNX -#ifndef O_BINARY -#define O_BINARY 0 -#endif -#define SOCKET_ERROR -1 -#endif +#if NO_BALLOC +#define balloc(B_ARGS, num) malloc(num) +#define bfree(B_ARGS, p) free(p) +#define bfreeSafe(B_ARGS, p) \ + if (p) { free(p); } else +#define brealloc(B_ARGS, p, num) realloc(p, num) +extern char_t *bstrdupNoBalloc(char_t *s); +extern char *bstrdupANoBalloc(char *s); +#define bstrdup(B_ARGS, s) bstrdupNoBalloc(s) +#define bstrdupA(B_ARGS, s) bstrdupANoBalloc(s) +#define gstrdup(B_ARGS, s) bstrdupNoBalloc(s) -#if WIN -#undef R_OK -#define R_OK 4 -#undef W_OK -#define W_OK 2 -#undef X_OK -#define X_OK 1 -#undef F_OK -#define F_OK 0 -#endif +#else /* BALLOC */ -/********************************* Prototypes *********************************/ +#ifndef B_STATS +#define balloc(B_ARGS, num) balloc(num) +#define bfree(B_ARGS, p) bfree(p) +#define bfreeSafe(B_ARGS, p) bfreeSafe(p) +#define brealloc(B_ARGS, p, size) brealloc(p, size) +#define bstrdup(B_ARGS, p) bstrdup(p) -extern void bclose(); -extern int bopen(void *buf, int bufsize, int flags); -#if NO_BALLOC -#undef B_L -#define B_L x -#define balloc(x, num) malloc(num) -#define bfree(x, p) free(p) -#define bfreeSafe(x, p) \ - if (p) { bfree(x, p); } else -#define brealloc(x, p, num) realloc(p, num) -extern char *bstrdupANoBalloc(char *s); -#define bstrdupA(x, s) bstrdupANoBalloc(s) #if UNICODE -extern char_t *bstrdupNoBalloc(char_t *s); -#define bstrdup(x, s) bstrdupNoBalloc(s) -#define gstrdup(x, s) bstrdupNoBalloc(s) -#else /* Not UNICODE */ -#define bstrdup(x, s) bstrdupANoBalloc(s) -#define gstrdup(x, s) bstrdupANoBalloc(s) +#define bstrdupA(B_ARGS, p) bstrdupA(p) +#else /* UNICODE */ +#define bstrdupA bstrdup #endif /* UNICODE */ -#else /* BALLOC */ +#endif /* B_STATS */ + +#define gstrdup bstrdup extern void *balloc(B_ARGS_DEC, int size); -extern void bfree(B_ARGS_DEC, void *mp); -extern void *brealloc(B_ARGS_DEC, void *buf, int newsize); -extern void bstats(int handle, void (*writefn)(int fd, char_t *fmt, ...)); -extern char_t *bstrdup(B_ARGS_DEC, char_t *s); -extern void bfreeSafe(B_ARGS_DEC, void* mp); -#define gstrdup(B_ARGS, s) bstrdup(B_ARGS, s) +extern void bfree(B_ARGS_DEC, void *mp); +extern void bfreeSafe(B_ARGS_DEC, void *mp); +extern void *brealloc(B_ARGS_DEC, void *buf, int newsize); +extern char_t *bstrdup(B_ARGS_DEC, char_t *s); + #if UNICODE extern char *bstrdupA(B_ARGS_DEC, char *s); -#else +#else /* UNICODE */ #define bstrdupA bstrdup #endif /* UNICODE */ #endif /* BALLOC */ +extern void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)); + +/* + * Flags. The integrity value is used as an arbitrary value to fill the flags. + */ +#define B_USE_MALLOC 0x1 /* Okay to use malloc if required */ +#define B_USER_BUF 0x2 /* User supplied buffer for mem */ + + #if !LINUX && !__rtems__ -extern char_t* basename(char_t* name); -#endif +extern char_t *basename(char_t *name); +#endif /* !LINUX */ -extern void *emfCreateTimer(int delay, void (*routine)(long arg), long arg); -extern void emfDeleteTimer(void *id); -extern int emfInstGet(); -extern void emfInstSet(int inst); -extern void error(E_ARGS_DEC, int flags, char_t *fmt, ...); +#if UEMF && WEBS +/* + * The open source webserver uses a different callback/timer mechanism + * than other emf derivative products such as FieldUpgrader agents + * so redefine those API for webserver so that they can coexist in the + * same address space as the others. + */ +#define emfSchedCallback websSchedCallBack +#define emfUnschedCallback websUnschedCallBack +#define emfReschedCallback websReschedCallBack +#endif /* UEMF && WEBS */ + +typedef void (emfSchedProc)(void *data, int id); +extern int emfSchedCallback(int delay, emfSchedProc *proc, void *arg); +extern void emfUnschedCallback(int id); +extern void emfReschedCallback(int id, int delay); +extern void emfSchedProcess(); +extern int emfInstGet(); +extern void emfInstSet(int inst); +extern void error(E_ARGS_DEC, int flags, char_t *fmt, ...); +extern void (*errorSetHandler(void (*function)(int etype, char_t *msg))) \ + (int etype, char_t *msg); + +#if B_STATS +#define hAlloc(x) HALLOC(B_L, x) +#define hAllocEntry(x, y, z) HALLOCENTRY(B_L, x, y, z) +extern int HALLOC(B_ARGS_DEC, void ***map); +extern int HALLOCENTRY(B_ARGS_DEC, void ***list, int *max, int size); +#else +extern int hAlloc(void ***map); +extern int hAllocEntry(void ***list, int *max, int size); +#endif /* B_STATS */ -extern int hAlloc(void*** map); -extern int hFree(void*** map, int handle); -extern int hAllocEntry(void ***list, int *max, int size); +extern int hFree(void ***map, int handle); -extern int ringqOpen(ringq_t *rq, int increment, int maxsize); -extern void ringqClose(ringq_t *rq); -extern int ringqLen(ringq_t *rq); +extern int ringqOpen(ringq_t *rq, int increment, int maxsize); +extern void ringqClose(ringq_t *rq); +extern int ringqLen(ringq_t *rq); -extern int ringqPutc(ringq_t *rq, char_t c); -extern int ringqInsertc(ringq_t *rq, char_t c); -extern int ringqPutstr(ringq_t *rq, char_t *str); -extern int ringqGetc(ringq_t *rq); +extern int ringqPutc(ringq_t *rq, char_t c); +extern int ringqInsertc(ringq_t *rq, char_t c); +extern int ringqPutStr(ringq_t *rq, char_t *str); +extern int ringqGetc(ringq_t *rq); -extern int gvsnprintf(char_t **s, int n, char_t *fmt, va_list arg); -extern int gsnprintf(char_t **s, int n, char_t *fmt, ...); +extern int fmtValloc(char_t **s, int n, char_t *fmt, va_list arg); +extern int fmtAlloc(char_t **s, int n, char_t *fmt, ...); +extern int fmtStatic(char_t *s, int n, char_t *fmt, ...); #if UNICODE -extern int ringqPutcA(ringq_t* rq, char c); -extern int ringqInsertcA(ringq_t* rq, char c); -extern int ringqPutstrA(ringq_t* rq, char* str); -extern int ringqGetcA(ringq_t* rq); +extern int ringqPutcA(ringq_t *rq, char c); +extern int ringqInsertcA(ringq_t *rq, char c); +extern int ringqPutStrA(ringq_t *rq, char *str); +extern int ringqGetcA(ringq_t *rq); #else #define ringqPutcA ringqPutc #define ringqInsertcA ringqInsertc -#define ringqPutstrA ringqPutstr +#define ringqPutStrA ringqPutStr #define ringqGetcA ringqGetc -#endif - -extern int ringqPutBlk(ringq_t *rq, unsigned char *buf, int len); -extern int ringqPutBlkMax(ringq_t *rq); -extern void ringqPutBlkAdj(ringq_t *rq, int size); -extern int ringqGetBlk(ringq_t *rq, unsigned char *buf, int len); -extern int ringqGetBlkMax(ringq_t *rq); -extern void ringqGetBlkAdj(ringq_t *rq, int size); -extern void ringqFlush(ringq_t *rq); - -extern int scriptSetVar(int engine, char_t *var, char_t *value); -extern int scriptEval(int engine, char_t *cmd, char_t **rslt, int chan); +#endif /* UNICODE */ -extern void socketClose(); -extern void socketCloseConnection(int sid); -extern void socketCreateHandler(int sid, int mask, socketHandler_t +extern int ringqPutBlk(ringq_t *rq, unsigned char *buf, int len); +extern int ringqPutBlkMax(ringq_t *rq); +extern void ringqPutBlkAdj(ringq_t *rq, int size); +extern int ringqGetBlk(ringq_t *rq, unsigned char *buf, int len); +extern int ringqGetBlkMax(ringq_t *rq); +extern void ringqGetBlkAdj(ringq_t *rq, int size); +extern void ringqFlush(ringq_t *rq); +extern void ringqAddNull(ringq_t *rq); + +extern int scriptSetVar(int engine, char_t *var, char_t *value); +extern int scriptEval(int engine, char_t *cmd, char_t **rslt, int chan); + +extern void socketClose(); +extern void socketCloseConnection(int sid); +extern void socketCreateHandler(int sid, int mask, socketHandler_t handler, int arg); -extern void socketDeleteHandler(int sid); -extern int socketEof(int sid); -extern int socketFlush(int sid, int block); -extern int socketGets(int sid, char_t** buf); -extern int socketInputBuffered(int sid); -extern int socketOpen(); -extern int socketOpenConnection(char *host, int port, +extern void socketDeleteHandler(int sid); +extern int socketEof(int sid); +extern int socketCanWrite(int sid); +extern void socketSetBufferSize(int sid, int in, int line, int out); +extern int socketFlush(int sid); +extern int socketGets(int sid, char_t **buf); +extern int socketGetPort(int sid); +extern int socketInputBuffered(int sid); +extern int socketOpen(); +extern int socketOpenConnection(char *host, int port, socketAccept_t accept, int flags); -extern void socketProcess(); -extern int socketRead(int sid, char *buf, int len); -extern int socketReady(); -extern int socketWrite(int sid, char *buf, int len); -extern int socketWriteString(int sid, char_t *buf); -extern int socketSelect(); +extern void socketProcess(int hid); +extern int socketRead(int sid, char *buf, int len); +extern int socketReady(int hid); +extern int socketWrite(int sid, char *buf, int len); +extern int socketWriteString(int sid, char_t *buf); +extern int socketSelect(int hid, int timeout); +extern int socketGetHandle(int sid); +extern int socketSetBlock(int sid, int flags); +extern int socketGetBlock(int sid); +extern int socketAlloc(char *host, int port, socketAccept_t accept, + int flags); +extern void socketFree(int sid); +extern int socketGetError(); +extern socket_t *socketPtr(int sid); +extern int socketWaitForEvent(socket_t *sp, int events, int *errCode); +extern void socketRegisterInterest(socket_t *sp, int handlerMask); +extern int socketGetInput(int sid, char *buf, int toRead, int *errCode); extern char_t *strlower(char_t *string); extern char_t *strupper(char_t *string); extern char_t *stritoa(int n, char_t *string, int width); -extern sym_fd_t symOpen(int hash_size); -extern void symClose(sym_fd_t sd, void (*cleanup)(sym_t *sp)); +extern sym_fd_t symOpen(int hash_size); +extern void symClose(sym_fd_t sd); extern sym_t *symLookup(sym_fd_t sd, char_t *name); extern sym_t *symEnter(sym_fd_t sd, char_t *name, value_t v, int arg); -extern int symDelete(sym_fd_t sd, char_t *name); -extern void symWalk(sym_fd_t sd, void (*fn)(sym_t *symp)); - -extern void goahead_trace(int lev, char_t *fmt, ...); +extern int symDelete(sym_fd_t sd, char_t *name); +extern void symWalk(sym_fd_t sd, void (*fn)(sym_t *symp)); +extern sym_t *symFirst(sym_fd_t sd); +extern sym_t *symNext(sym_fd_t sd); +extern int symSubOpen(); +extern void symSubClose(); + +extern void trace(int lev, char_t *fmt, ...); +extern void traceRaw(char_t *buf); +extern void (*traceSetHandler(void (*function)(int level, char_t *buf))) + (int level, char_t *buf); -extern value_t valueInteger(long value); -extern value_t valueString(char_t *value, int flags); -extern value_t valueErrmsg(char_t *value); -extern void valueFree(value_t *v); +extern value_t valueInteger(long value); +extern value_t valueString(char_t *value, int flags); +extern value_t valueErrmsg(char_t *value); +extern void valueFree(value_t *v); +extern int vxchdir(char *dirname); + +extern unsigned int hextoi(char_t *hexstring); +extern unsigned int gstrtoi(char_t *s); +extern time_t timeMsec(); extern char_t *ascToUni(char_t *ubuf, char *str, int nBytes); extern char *uniToAsc(char *buf, char_t *ustr, int nBytes); -extern char_t *ballocAscToUni(char * cp); -extern char *ballocUniToAsc(char_t * unip, int ulen); +extern char_t *ballocAscToUni(char *cp, int alen); +extern char *ballocUniToAsc(char_t *unip, int ulen); + +extern char_t *basicGetHost(); +extern char_t *basicGetAddress(); +extern char_t *basicGetProduct(); +extern void basicSetHost(char_t *host); +extern void basicSetAddress(char_t *addr); + +extern int harnessOpen(char_t **argv); +extern void harnessClose(int status); +extern void harnessTesting(char_t *msg, ...); +extern void harnessPassed(); +extern void harnessFailed(int line); +extern int harnessLevel(); #endif /* _h_UEMF */ diff --git a/cpukit/httpd/um.c b/cpukit/httpd/um.c new file mode 100644 index 0000000000..ce9e171c01 --- /dev/null +++ b/cpukit/httpd/um.c @@ -0,0 +1,1415 @@ +/* + * um.c -- User Management + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ + */ + +/******************************** Description *********************************/ +/* + * User Management routines for adding/deleting/changing users and groups + * Also, routines for determining user access + */ + +/********************************* Includes ***********************************/ + +#include "um.h" +#include "emfdb.h" +#include "webs.h" + +/********************************** Defines ***********************************/ + +#define UM_DB_FILENAME T("um.xml") +#define UM_TXT_FILENAME T("umconfig.txt") + +/* + * Table names + */ +#define UM_USER_TABLENAME T("users") +#define UM_GROUP_TABLENAME T("groups") +#define UM_ACCESS_TABLENAME T("access") + +/* + * Column names + */ +#define UM_NAME T("name") +#define UM_PASS T("password") +#define UM_GROUP T("group") +#define UM_PROT T("prot") +#define UM_DISABLE T("disable") +#define UM_METHOD T("method") +#define UM_PRIVILEGE T("priv") +#define UM_SECURE T("secure") + +/* + * XOR encryption mask + * Note: This string should be modified for individual sites + * in order to enhance user password security. + */ +#define UM_XOR_ENCRYPT T("*j7a(L#yZ98sSd5HfSgGjMj8;Ss;d)(*&^#@$a2s0i3g") + +/******************************** Local Data **********************************/ + +#ifdef UEMF +/* + * User table definition + */ +#define NUMBER_OF_USER_COLUMNS 5 + +char_t *userColumnNames[NUMBER_OF_USER_COLUMNS] = { + UM_NAME, UM_PASS, UM_GROUP, UM_PROT, UM_DISABLE +}; + +int userColumnTypes[NUMBER_OF_USER_COLUMNS] = { + T_STRING, T_STRING, T_STRING, T_INT, T_INT +}; + +dbTable_t userTable = { + UM_USER_TABLENAME, + NUMBER_OF_USER_COLUMNS, + userColumnNames, + userColumnTypes, + 0, + NULL +}; + +/* + * Group table definition + */ +#define NUMBER_OF_GROUP_COLUMNS 5 + +char_t *groupColumnNames[NUMBER_OF_GROUP_COLUMNS] = { + UM_NAME, UM_PRIVILEGE, UM_METHOD, UM_PROT, UM_DISABLE +}; + +int groupColumnTypes[NUMBER_OF_GROUP_COLUMNS] = { + T_STRING, T_INT, T_INT, T_INT, T_INT +}; + +dbTable_t groupTable = { + UM_GROUP_TABLENAME, + NUMBER_OF_GROUP_COLUMNS, + groupColumnNames, + groupColumnTypes, + 0, + NULL +}; + +/* + * Access Limit table definition + */ +#define NUMBER_OF_ACCESS_COLUMNS 4 + +char_t *accessColumnNames[NUMBER_OF_ACCESS_COLUMNS] = { + UM_NAME, UM_METHOD, UM_SECURE, UM_GROUP +}; + +int accessColumnTypes[NUMBER_OF_ACCESS_COLUMNS] = { + T_STRING, T_INT, T_INT, T_STRING +}; + +dbTable_t accessTable = { + UM_ACCESS_TABLENAME, + NUMBER_OF_ACCESS_COLUMNS, + accessColumnNames, + accessColumnTypes, + 0, + NULL +}; +#endif /* #ifdef UEMF */ + +/* + * Database Identifier returned from dbOpen() + */ +static int didUM = -1; + +/* + * Configuration database persist filename + */ +static char_t *saveFilename = NULL; + +static int umOpenCount = 0; /* count of apps using this module */ + +/*************************** Forward Declarations *****************************/ + +static bool_t umCheckName(char_t *name); + +/*********************************** Code *************************************/ +/* + * umOpen() registers the UM tables in the fake emf-database + */ + +int umOpen() +{ + if (++umOpenCount != 1) { + return didUM; + } +/* + * Do not initialize if intialization has already taken place + */ + if (didUM == -1) { + didUM = dbOpen(UM_USER_TABLENAME, UM_DB_FILENAME, NULL, 0); +#ifdef UEMF + dbRegisterDBSchema(&userTable); + dbRegisterDBSchema(&groupTable); + dbRegisterDBSchema(&accessTable); +#endif + } + + if (saveFilename == NULL) { + saveFilename = bstrdup(B_L, UM_TXT_FILENAME); + } + + return didUM; +} + +/******************************************************************************/ +/* + * umClose() frees up the UM tables in the fake emf-database + */ + +void umClose() +{ + if (--umOpenCount > 0) { + return; + } +/* + * Do not close if intialization has not taken place + */ + if (didUM != -1) { + dbClose(didUM); + didUM = -1; + } + + if (saveFilename != NULL) { + bfree(B_L, saveFilename); + saveFilename = NULL; + } +} + +/******************************************************************************/ +/* + * umCommit() persists all of the UM tables + */ + +int umCommit(char_t *filename) +{ + if (filename && *filename) { + if (saveFilename != NULL) { + bfree(B_L, saveFilename); + } + + saveFilename = bstrdup(B_L, filename); + } + + a_assert (saveFilename && *saveFilename); + trace(3, T("UM: Writing User Configuration to file <%s>\n"), + saveFilename); + + return dbSave(didUM, saveFilename, 0); +} + +/******************************************************************************/ +/* + * umRestore() loads up the UM tables with persisted data + */ + +int umRestore(char_t *filename) +{ + if (filename && *filename) { + if (saveFilename != NULL) { + bfree(B_L, saveFilename); + } + + saveFilename = bstrdup(B_L, filename); + } + + a_assert(saveFilename && *saveFilename); + + trace(3, T("UM: Loading User Configuration from file <%s>\n"), + saveFilename); + +/* + * First empty the database, otherwise we wind up with duplicates! + */ + dbZero(didUM); + return dbLoad(didUM, saveFilename, 0); +} + +/******************************************************************************/ +/* + * Encrypt/Decrypt a text string. + * Returns the number of characters encrypted. + */ + +static int umEncryptString(char_t *textString) +{ + char_t *enMask; + char_t enChar; + int numChars; + + a_assert(textString); + + enMask = UM_XOR_ENCRYPT; + numChars = 0; + + while (*textString) { + enChar = *textString ^ *enMask; +/* + * Do not produce encrypted text with embedded linefeeds or tabs. + * Simply use existing character. + */ + if (enChar && !gisspace(enChar)) + *textString = enChar; +/* + * Increment all pointers. + */ + enMask++; + textString++; + numChars++; +/* + * Wrap encryption mask pointer if at end of length. + */ + if (*enMask == '\0') { + enMask = UM_XOR_ENCRYPT; + } + } + + return numChars; +} + +/******************************************************************************/ +/* + * umGetFirstRowData() - return a pointer to the first non-blank key value + * in the given column for the given table. + */ + +static char_t *umGetFirstRowData(char_t *tableName, char_t *columnName) +{ + char_t *columnData; + int row; + int check; + + a_assert(tableName && *tableName); + a_assert(columnName && *columnName); + + row = 0; +/* + * Move through table until we retrieve the first row with non-null + * column data. + */ + columnData = NULL; + while ((check = dbReadStr(didUM, tableName, columnName, row++, + &columnData)) == 0 || (check == DB_ERR_ROW_DELETED)) { + if (columnData && *columnData) { + return columnData; + } + } + + return NULL; +} + +/******************************************************************************/ +/* + * umGetNextRowData() - return a pointer to the first non-blank + * key value following the given one. + */ + +static char_t *umGetNextRowData(char_t *tableName, char_t *columnName, + char_t *keyLast) +{ + char_t *key; + int row; + int check; + + a_assert(tableName && *tableName); + a_assert(columnName && *columnName); + a_assert(keyLast && *keyLast); +/* + * Position row counter to row where the given key value was found + */ + row = 0; + key = NULL; + + while ((((check = dbReadStr(didUM, tableName, columnName, row++, + &key)) == 0) || (check == DB_ERR_ROW_DELETED)) && + ((key == NULL) || (gstrcmp(key, keyLast) != 0))) { + } +/* + * If the last key value was not found, return NULL + */ + if (!key || gstrcmp(key, keyLast) != 0) { + return NULL; + } +/* + * Move through table until we retrieve the next row with a non-null key + */ + while (((check = dbReadStr(didUM, tableName, columnName, row++, &key)) + == 0) || (check == DB_ERR_ROW_DELETED)) { + if (key && *key && (gstrcmp(key, keyLast) != 0)) { + return key; + } + } + + return NULL; +} + +/******************************************************************************/ +/* + * umAddUser() - Adds a user to the "users" table. + */ + +int umAddUser(char_t *user, char_t *pass, char_t *group, + bool_t prot, bool_t disabled) +{ + int row; + char_t *password; + + a_assert(user && *user); + a_assert(pass && *pass); + a_assert(group && *group); + + trace(3, T("UM: Adding User <%s>\n"), user); + +/* + * Do not allow duplicates + */ + if (umUserExists(user)) { + return UM_ERR_DUPLICATE; + } + +/* + * Make sure user name and password contain valid characters + */ + if (!umCheckName(user)) { + return UM_ERR_BAD_NAME; + } + + if (!umCheckName(pass)) { + return UM_ERR_BAD_PASSWORD; + } + +/* + * Make sure group exists + */ + if (!umGroupExists(group)) { + return UM_ERR_NOT_FOUND; + } + +/* + * Now create the user record + */ + row = dbAddRow(didUM, UM_USER_TABLENAME); + + if (row < 0) { + return UM_ERR_GENERAL; + } + + if (dbWriteStr(didUM, UM_USER_TABLENAME, UM_NAME, row, user) != 0) { + return UM_ERR_GENERAL; + } + + password = bstrdup(B_L, pass); + umEncryptString(password); + dbWriteStr(didUM, UM_USER_TABLENAME, UM_PASS, row, password); + bfree(B_L, password); + dbWriteStr(didUM, UM_USER_TABLENAME, UM_GROUP, row, group); + dbWriteInt(didUM, UM_USER_TABLENAME, UM_PROT, row, prot); + dbWriteInt(didUM, UM_USER_TABLENAME, UM_DISABLE, row, disabled); + + return 0; +} + +/******************************************************************************/ +/* + * umDeleteUser() - remove a user from the "users" table + */ + +int umDeleteUser(char_t *user) +{ + int row; + + a_assert(user && *user); + trace(3, T("UM: Deleting User <%s>\n"), user); +/* + * Check to see if user is delete-protected + */ + if (umGetUserProtected(user)) { + return UM_ERR_PROTECTED; + } + +/* + * If found, delete the user from the database + */ + if ((row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0)) >= 0) { + return dbDeleteRow(didUM, UM_USER_TABLENAME, row); + } + + return UM_ERR_NOT_FOUND; +} + +/******************************************************************************/ +/* + * umGetFirstUser() - Returns the user ID of the first user found in the + * "users" table. + */ + +char_t *umGetFirstUser() +{ + return umGetFirstRowData(UM_USER_TABLENAME, UM_NAME); +} + +/******************************************************************************/ +/* + * umGetNextUser() Returns the next user found in the "users" table after + * the given user. + */ + +char_t *umGetNextUser(char_t *userLast) +{ + return umGetNextRowData(UM_USER_TABLENAME, UM_NAME, userLast); +} + +/******************************************************************************/ +/* + * umUserExists() Returns TRUE if userid exists. + */ + +bool_t umUserExists(char_t *user) +{ + a_assert(user && *user); + + if (dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0) >= 0) { + return TRUE; + } else { + return FALSE; + } +} + +/******************************************************************************/ +/* + * umGetUserPassword() returns a de-crypted copy of the user password + */ + +char_t *umGetUserPassword(char_t *user) +{ + char_t *password; + int row; + + a_assert(user && *user); + + password = NULL; + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + + if (row >= 0) { + char_t *pass = NULL; + dbReadStr(didUM, UM_USER_TABLENAME, UM_PASS, row, &pass); +/* + * Decrypt password + * Note, this function returns a copy of the password, which must + * be deleted at some time in the future. + */ + password = bstrdup(B_L, pass); + umEncryptString(password); + } + + return password; +} + +/******************************************************************************/ +/* + * umSetUserPassword() updates the user password in the user "table" after + * encrypting the given password + */ + +int umSetUserPassword(char_t *user, char_t *pass) +{ + int row, nRet; + char_t *password; + + a_assert(user && *user); + a_assert(pass && *pass); + trace(3, T("UM: Attempting to change the password for user <%s>\n"), user); +/* + * Find the row of the user + */ + if ((row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0)) < 0) { + return UM_ERR_NOT_FOUND; + } + + password = bstrdup(B_L, pass); + umEncryptString(password); + nRet = dbWriteStr(didUM, UM_USER_TABLENAME, UM_PASS, row, password); + bfree(B_L, password); + + return nRet; +} + +/******************************************************************************/ +/* + * umGetUserGroup() returns the name of the user group + */ + +char_t *umGetUserGroup(char_t *user) +{ + char_t *group; + int row; + + a_assert(user && *user); + group = NULL; +/* + * Find the row of the user + */ + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + + if (row >= 0) { + dbReadStr(didUM, UM_USER_TABLENAME, UM_GROUP, row, &group); + } + + return group; +} + +/******************************************************************************/ +/* + * umSetUserGroup() Sets the name of the user group for the user + */ + +int umSetUserGroup(char_t *user, char_t *group) +{ + int row; + + a_assert(user && *user); + a_assert(group && *group); +/* + * Find the row of the user + */ + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + + if (row >= 0) { + return dbWriteStr(didUM, UM_USER_TABLENAME, UM_GROUP, row, group); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * umGetUserEnabled() - returns if the user is enabled + * Returns FALSE if the user is not found. + */ + +bool_t umGetUserEnabled(char_t *user) +{ + int disabled, row; + + a_assert(user && *user); + + disabled = 1; +/* + * Find the row of the user + */ + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + + if (row >= 0) { + dbReadInt(didUM, UM_USER_TABLENAME, UM_DISABLE, row, &disabled); + } + + return (bool_t)!disabled; +} + +/******************************************************************************/ +/* + * umSetUserEnabled() Enables/disables the user + */ +int umSetUserEnabled(char_t *user, bool_t enabled) +{ + int row; + + a_assert(user && *user); +/* + * Find the row of the user + */ + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + if (row >= 0) { + return dbWriteInt(didUM, UM_USER_TABLENAME, UM_DISABLE, row, !enabled); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * umGetUserProtected() - determine deletability of user + */ + +bool_t umGetUserProtected(char_t *user) +{ + int protect, row; + + a_assert(user && *user); +/* + * Find the row of the user + */ + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + protect = FALSE; + + if (row >= 0) { + dbReadInt(didUM, UM_USER_TABLENAME, UM_PROT, row, &protect); + } + + return (bool_t)protect; +} + +/******************************************************************************/ +/* + * umSetUserProtected() sets the delete protection for the user + */ +int umSetUserProtected(char_t *user, bool_t protect) +{ + int row; + + a_assert(user && *user); +/* + * Find the row of the user + */ + row = dbSearchStr(didUM, UM_USER_TABLENAME, UM_NAME, user, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_USER_TABLENAME, UM_PROT, row, protect); + } else { + return UM_ERR_NOT_FOUND; + } +} + + +/******************************************************************************/ +/* + * umAddGroup() adds a group to the "Group" table + */ + +int umAddGroup(char_t *group, short priv, accessMeth_t am, + bool_t prot, bool_t disabled) +{ + int row; + + a_assert(group && *group); + trace(3, T("UM: Adding group <%s>\n"), group); + +/* + * Do not allow duplicates + */ + if (umGroupExists(group)) { + return UM_ERR_DUPLICATE; + } + +/* + * Only allow valid characters in key field + */ + if (!umCheckName(group)) { + return UM_ERR_BAD_NAME; + } + +/* + * Add a new row to the table + */ + if ((row = dbAddRow(didUM, UM_GROUP_TABLENAME)) < 0) { + return UM_ERR_GENERAL; + } + +/* + * Write the key field + */ + if (dbWriteStr(didUM, UM_GROUP_TABLENAME, UM_NAME, row, group) != 0) { + return UM_ERR_GENERAL; + } + +/* + * Write the remaining fields + */ + dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_PRIVILEGE, row, priv); + dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_METHOD, row, (int) am); + dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_PROT, row, prot); + dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_DISABLE, row, disabled); + + return 0; +} + +/******************************************************************************/ +/* + * umDeleteGroup() - Delete a user group, if not protected + */ + +int umDeleteGroup(char_t *group) +{ + int row; + + a_assert(group && *group); + trace(3, T("UM: Deleting Group <%s>\n"), group); + +/* + * Check to see if the group is in use + */ + if (umGetGroupInUse(group)) { + return UM_ERR_IN_USE; + } + +/* + * Check to see if the group is delete-protected + */ + if (umGetGroupProtected(group)) { + return UM_ERR_PROTECTED; + } + +/* + * Find the row of the group to delete + */ + if ((row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0)) < 0) { + return UM_ERR_NOT_FOUND; + } + + return dbDeleteRow(didUM, UM_GROUP_TABLENAME, row); +} + +/******************************************************************************/ +/* + * umGroupExists() returns TRUE if group exists, FALSE otherwise + */ + +bool_t umGroupExists(char_t *group) +{ + a_assert(group && *group); + + if (dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0) >= 0) { + return TRUE; + } else { + return FALSE; + } +} + + +/******************************************************************************/ +/* + * umGetGroupInUse() returns TRUE if the group is referenced by a user or by + * an access limit. + */ + +bool_t umGetGroupInUse(char_t *group) +{ + a_assert(group && *group); + +/* + * First, check the user table + */ + if (dbSearchStr(didUM, UM_USER_TABLENAME, UM_GROUP, group, 0) >= 0) { + return TRUE; + } + +/* + * Second, check the access limit table + */ + if (dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_GROUP, group, 0) >= 0) { + return TRUE; + } + + return FALSE; +} + + +/******************************************************************************/ +/* + * umGetFirstGroup() - return a pointer to the first non-blank group name + */ + +char_t *umGetFirstGroup() +{ + return umGetFirstRowData(UM_GROUP_TABLENAME, UM_NAME); +} + +/******************************************************************************/ +/* + * umGetNextGroup() - return a pointer to the first non-blank group name + * following the given group name + */ + +char_t *umGetNextGroup(char_t *groupLast) +{ + return umGetNextRowData(UM_GROUP_TABLENAME, UM_NAME, groupLast); +} + +/******************************************************************************/ +/* + * Returns the default access method to use for a given group + */ + +accessMeth_t umGetGroupAccessMethod(char_t *group) +{ + int am, row; + + a_assert(group && *group); + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + + if (row >= 0) { + dbReadInt(didUM, UM_GROUP_TABLENAME, UM_METHOD, row, (int *)&am); + } else { + am = AM_INVALID; + } + + return (accessMeth_t) am; +} + +/******************************************************************************/ +/* + * Set the default access method to use for a given group + */ + +int umSetGroupAccessMethod(char_t *group, accessMeth_t am) +{ + int row; + + a_assert(group && *group); + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_METHOD, row, (int) am); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * Returns the privilege mask for a given group + */ + +short umGetGroupPrivilege(char_t *group) +{ + int privilege, row; + + a_assert(group && *group); + privilege = -1; + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + + if (row >= 0) { + dbReadInt(didUM, UM_GROUP_TABLENAME, UM_PRIVILEGE, row, &privilege); + } + + return (short) privilege; +} + +/******************************************************************************/ +/* + * Set the privilege mask for a given group + */ + +int umSetGroupPrivilege(char_t *group, short privilege) +{ + int row; + + a_assert(group && *group); + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_PRIVILEGE, row, + (int)privilege); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * Returns the enabled setting for a given group. + * Returns FALSE if group is not found. + */ + +bool_t umGetGroupEnabled(char_t *group) +{ + int disabled, row; + + a_assert(group && *group); + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + disabled = 1; + + if (row >= 0) { + dbReadInt(didUM, UM_GROUP_TABLENAME, UM_DISABLE, row, &disabled); + } + + return (bool_t) !disabled; +} + +/******************************************************************************/ +/* + * Sets the enabled setting for a given group. + */ + +int umSetGroupEnabled(char_t *group, bool_t enabled) +{ + int row; + + a_assert(group && *group); + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_DISABLE, row, + (int) !enabled); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * Returns the protected setting for a given group + * Returns FALSE if user is not found + */ + +bool_t umGetGroupProtected(char_t *group) +{ + int protect, row; + + a_assert(group && *group); + + protect = 0; + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + if (row >= 0) { + dbReadInt(didUM, UM_GROUP_TABLENAME, UM_PROT, row, &protect); + } + + return (bool_t) protect; +} + +/******************************************************************************/ +/* + * Sets the protected setting for a given group + */ + +int umSetGroupProtected(char_t *group, bool_t protect) +{ + int row; + + a_assert(group && *group); + row = dbSearchStr(didUM, UM_GROUP_TABLENAME, UM_NAME, group, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_GROUP_TABLENAME, UM_PROT, row, + (int) protect); + } else { + return UM_ERR_NOT_FOUND; + } +} + + +/******************************************************************************/ +/* + * umAddAccessLimit() adds an access limit to the "access" table + */ + +int umAddAccessLimit(char_t *url, accessMeth_t am, short secure, char_t *group) +{ + int row; + + a_assert(url && *url); + trace(3, T("UM: Adding Access Limit for <%s>\n"), url); + +/* + * Do not allow duplicates + */ + if (umAccessLimitExists(url)) { + return UM_ERR_DUPLICATE; + } + +/* + * Add a new row to the table + */ + if ((row = dbAddRow(didUM, UM_ACCESS_TABLENAME)) < 0) { + return UM_ERR_GENERAL; + } + +/* + * Write the key field + */ + if(dbWriteStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, row, url) < 0) { + return UM_ERR_GENERAL; + } + +/* + * Write the remaining fields + */ + dbWriteInt(didUM, UM_ACCESS_TABLENAME, UM_METHOD, row, (int)am); + dbWriteInt(didUM, UM_ACCESS_TABLENAME, UM_SECURE, row, (int)secure); + dbWriteStr(didUM, UM_ACCESS_TABLENAME, UM_GROUP, row, group); + + return 0; +} + +/******************************************************************************/ +/* + * umDeleteAccessLimit() + */ + +int umDeleteAccessLimit(char_t *url) +{ + int row; + + a_assert(url && *url); + trace(3, T("UM: Deleting Access Limit for <%s>\n"), url); +/* + * Find the row of the access limit to delete + */ + if ((row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0)) < 0) { + return UM_ERR_NOT_FOUND; + } + + return dbDeleteRow(didUM, UM_ACCESS_TABLENAME, row); +} + +/******************************************************************************/ +/* + * umGetFirstGroup() - return a pointer to the first non-blank access limit + */ + +char_t *umGetFirstAccessLimit() +{ + return umGetFirstRowData(UM_ACCESS_TABLENAME, UM_NAME); +} + +/******************************************************************************/ +/* + * umGetNextAccessLimit() - return a pointer to the first non-blank + * access limit following the given one + */ + +char_t *umGetNextAccessLimit(char_t *urlLast) +{ + return umGetNextRowData(UM_ACCESS_TABLENAME, UM_NAME, urlLast); +} + +/******************************************************************************/ +/* + * umAccessLimitExists() returns TRUE if this access limit exists + */ + +bool_t umAccessLimitExists(char_t *url) +{ + a_assert(url && *url); + + if (dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0) < 0) { + return FALSE; + } else { + return TRUE; + } +} + +/******************************************************************************/ +/* + * umGetAccessLimit() returns the Access Method for the URL + */ + +accessMeth_t umGetAccessLimitMethod(char_t *url) +{ + int am, row; + + am = (int) AM_INVALID; + row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0); + + if (row >= 0) { + dbReadInt(didUM, UM_ACCESS_TABLENAME, UM_METHOD, row, &am); + } + + return (accessMeth_t) am; +} + +/******************************************************************************/ +/* + * umSetAccessLimitMethod() - set Access Method for Access Limit + */ + +int umSetAccessLimitMethod(char_t *url, accessMeth_t am) +{ + int row; + + a_assert(url && *url); + row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_ACCESS_TABLENAME, UM_METHOD, row, (int) am); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * umGetAccessLimitSecure() - returns secure switch for access limit + */ + +short umGetAccessLimitSecure(char_t *url) +{ + int secure, row; + + a_assert(url && *url); + secure = -1; + row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0); + + if (row >= 0) { + dbReadInt(didUM, UM_ACCESS_TABLENAME, UM_SECURE, row, &secure); + } + + return (short)secure; +} + +/******************************************************************************/ +/* + * umSetAccessLimitSecure() - sets the secure flag for the URL + */ + +int umSetAccessLimitSecure(char_t *url, short secure) +{ + int row; + + a_assert(url && *url); + row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0); + + if (row >= 0) { + return dbWriteInt(didUM, UM_ACCESS_TABLENAME, UM_SECURE, row, + (int)secure); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * umGetAccessLimitGroup() - returns the user group of the access limit + */ + +char_t *umGetAccessLimitGroup(char_t *url) +{ + char_t *group; + int row; + + a_assert(url && *url); + group = NULL; + row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0); + + if (row >= 0) { + dbReadStr(didUM, UM_ACCESS_TABLENAME, UM_GROUP, row, &group); + } + + return group; +} + +/******************************************************************************/ +/* + * umSetAccessLimitGroup() - sets the user group for the access limit. + */ + +int umSetAccessLimitGroup(char_t *url, char_t *group) +{ + int row; + + a_assert(url && *url); + row = dbSearchStr(didUM, UM_ACCESS_TABLENAME, UM_NAME, url, 0); + + if (row >= 0) { + return dbWriteStr(didUM, UM_ACCESS_TABLENAME, UM_GROUP, row, group); + } else { + return UM_ERR_NOT_FOUND; + } +} + +/******************************************************************************/ +/* + * Returns the access limit to use for a given URL, by checking for URLs up + * the directory tree. Creates a new string that must be deleted. + */ + +char_t *umGetAccessLimit(char_t *url) +{ + char_t *urlRet, *urlCheck, *lastChar; + int len; + + a_assert(url && *url); + urlRet = NULL; + urlCheck = bstrdup(B_L, url); + a_assert(urlCheck); + len = gstrlen(urlCheck); +/* + * Scan back through URL to see if there is a "parent" access limit + */ + while (len && !urlRet) { + if (umAccessLimitExists(urlCheck)) { + urlRet = bstrdup(B_L, urlCheck); + } else { +/* + * Trim the end portion of the URL to the previous directory marker + */ + lastChar = urlCheck + len; + lastChar--; + + while ((lastChar >= urlCheck) && ((*lastChar == '/') || + (*lastChar == '\\'))) { + *lastChar = 0; + lastChar--; + } + + while ((lastChar >= urlCheck) && (*lastChar != '/') && + (*lastChar != '\\')) { + *lastChar = 0; + lastChar--; + } + + len = gstrlen(urlCheck); + } + } + bfree (B_L, urlCheck); + + return urlRet; +} + +/******************************************************************************/ +/* + * Returns the access method to use for a given URL + */ + +accessMeth_t umGetAccessMethodForURL(char_t *url) +{ + accessMeth_t amRet; + char_t *urlHavingLimit, *group; + + urlHavingLimit = umGetAccessLimit(url); + if (urlHavingLimit) { + group = umGetAccessLimitGroup(urlHavingLimit); + + if (group && *group) { + amRet = umGetGroupAccessMethod(group); + } else { + amRet = umGetAccessLimitMethod(urlHavingLimit); + } + + bfree(B_L, urlHavingLimit); + } else { + amRet = AM_FULL; + } + + return amRet; +} + +/******************************************************************************/ +/* + * Returns TRUE if user can access URL + */ + +bool_t umUserCanAccessURL(char_t *user, char_t *url) +{ + accessMeth_t amURL; + char_t *group, *usergroup, *urlHavingLimit; + short priv; + + a_assert(user && *user); + a_assert(url && *url); + +/* + * Make sure user exists + */ + if (!umUserExists(user)) { + return FALSE; + } + +/* + * Make sure user is enabled + */ + if (!umGetUserEnabled(user)) { + return FALSE; + } + +/* + * Make sure user has sufficient privileges (any will do) + */ + usergroup = umGetUserGroup(user); + priv = umGetGroupPrivilege(usergroup); + if (priv == 0) { + return FALSE; + } + +/* + * Make sure user's group is enabled + */ + if (!umGetGroupEnabled(usergroup)) { + return FALSE; + } + +/* + * The access method of the user group must not be AM_NONE + */ + if (umGetGroupAccessMethod(usergroup) == AM_NONE) { + return FALSE; + } + +/* + * Check to see if there is an Access Limit for this URL + */ + urlHavingLimit = umGetAccessLimit(url); + if (urlHavingLimit) { + amURL = umGetAccessLimitMethod(urlHavingLimit); + group = umGetAccessLimitGroup(urlHavingLimit); + bfree(B_L, urlHavingLimit); + } else { +/* + * If there isn't an access limit for the URL, user has full access + */ + return TRUE; + } + +/* + * If the access method for the URL is AM_NONE then + * the file "doesn't exist". + */ + if (amURL == AM_NONE) { + return FALSE; + } + +/* + * If Access Limit has a group specified, then the user must be a + * member of that group + */ + if (group && *group) { + if (usergroup && (gstrcmp(group, usergroup) != 0)) { + return FALSE; + } + } + +/* + * Otherwise, user can access the URL + */ + return TRUE; +} + +/******************************************************************************/ +/* + * Returns TRUE if given name has only valid chars + */ + +static bool_t umCheckName(char_t *name) +{ + a_assert(name && *name); + + if (name && *name) { + while (*name) { + if (gisspace(*name)) { + return FALSE; + } + + name++; + } + + return TRUE; + } + + return FALSE; +} + +/******************************************************************************/ diff --git a/cpukit/httpd/um.h b/cpukit/httpd/um.h new file mode 100644 index 0000000000..d44fa28e91 --- /dev/null +++ b/cpukit/httpd/um.h @@ -0,0 +1,184 @@ +/* + * um.h -- GoAhead User Management public header + * + * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved. + * + * See the file "license.txt" for information on usage and redistribution + * + * $Id$ + */ + +#ifndef _h_UM +#define _h_UM 1 + +/******************************** Description *********************************/ + +/* + * GoAhead User Management header. This defines the User Management + * public APIs. Include this header for files that contain access to + * user inquiry or management. + */ + +/********************************* Includes ***********************************/ + +#if ! UEMF + #include "basic/basic.h" + #include "emf/emf.h" +#else + #include "uemf.h" +#endif + +/********************************** Defines ***********************************/ + +/* + * Error Return Flags + */ +#define UM_OK 0 +#define UM_ERR_GENERAL -1 +#define UM_ERR_NOT_FOUND -2 +#define UM_ERR_PROTECTED -3 +#define UM_ERR_DUPLICATE -4 +#define UM_ERR_IN_USE -5 +#define UM_ERR_BAD_NAME -6 +#define UM_ERR_BAD_PASSWORD -7 + +/* + * Privilege Masks + */ +#define PRIV_NONE 0x00 +#define PRIV_READ 0x01 +#define PRIV_WRITE 0x02 +#define PRIV_ADMIN 0x04 + +/* + * User classes + */ +typedef short bool_t; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +typedef enum { + AM_NONE = 0, + AM_FULL, + AM_BASIC, + AM_DIGEST, + AM_INVALID +} accessMeth_t; + +/********************************** Prototypes ********************************/ + +/* + * umOpen() must be called before accessing User Management functions + */ +extern int umOpen(); + +/* + * umClose() should be called before shutdown to free memory + */ +extern void umClose(); + +/* + * umCommit() persists the user management database + */ +extern int umCommit(char_t *filename); + +/* + * umRestore() loads the user management database + */ +extern int umRestore(char_t *filename); + +/* + * umUser functions use a user ID for a key + */ +extern int umAddUser(char_t *user, char_t *password, + char_t *group, bool_t protect, bool_t disabled); + +extern int umDeleteUser(char_t *user); + +extern char_t *umGetFirstUser(); +extern char_t *umGetNextUser(char_t *lastUser); + +extern bool_t umUserExists(char_t *user); + +extern char_t *umGetUserPassword(char_t *user); +extern int umSetUserPassword(char_t *user, char_t *password); + +extern char_t *umGetUserGroup(char_t *user); +extern int umSetUserGroup(char_t *user, char_t *password); + +extern bool_t umGetUserEnabled(char_t *user); +extern int umSetUserEnabled(char_t *user, bool_t enabled); + +extern bool_t umGetUserProtected(char_t *user); +extern int umSetUserProtected(char_t *user, bool_t protect); + +/* + * umGroup functions use a group name for a key + */ +extern int umAddGroup(char_t *group, short privilege, + accessMeth_t am, bool_t protect, bool_t disabled); + +extern int umDeleteGroup(char_t *group); + +extern char_t *umGetFirstGroup(); +extern char_t *umGetNextGroup(char_t *lastUser); + +extern bool_t umGroupExists(char_t *group); +extern bool_t umGetGroupInUse(char_t *group); + +extern accessMeth_t umGetGroupAccessMethod(char_t *group); +extern int umSetGroupAccessMethod(char_t *group, accessMeth_t am); + +extern bool_t umGetGroupEnabled(char_t *group); +extern int umSetGroupEnabled(char_t *group, bool_t enabled); + +extern short umGetGroupPrivilege(char_t *group); +extern int umSetGroupPrivilege(char_t *group, short privileges); + +extern bool_t umGetGroupProtected(char_t *group); +extern int umSetGroupProtected(char_t *group, bool_t protect); + +/* + * umAccessLimit functions use a URL as a key + */ +extern int umAddAccessLimit(char_t *url, accessMeth_t am, + short secure, char_t *group); + +extern int umDeleteAccessLimit(char_t *url); + +extern char_t *umGetFirstAccessLimit(); +extern char_t *umGetNextAccessLimit(char_t *lastUser); + +/* + * Returns the name of an ancestor access limit if + */ +extern char_t *umGetAccessLimit(char_t *url); + +extern bool_t umAccessLimitExists(char_t *url); + +extern accessMeth_t umGetAccessLimitMethod(char_t *url); +extern int umSetAccessLimitMethod(char_t *url, accessMeth_t am); + +extern short umGetAccessLimitSecure(char_t *url); +extern int umSetAccessLimitSecure(char_t *url, short secure); + +extern char_t *umGetAccessLimitGroup(char_t *url); +extern int umSetAccessLimitGroup(char_t *url, char_t *group); + +/* + * Convenience Functions + */ + +extern accessMeth_t umGetAccessMethodForURL(char_t *url); +extern bool_t umUserCanAccessURL(char_t *user, char_t *url); + +#endif /* _h_UM */ + +/******************************************************************************/ + diff --git a/cpukit/httpd/url.c b/cpukit/httpd/url.c index 75746fd490..0258387c59 100644 --- a/cpukit/httpd/url.c +++ b/cpukit/httpd/url.c @@ -1,7 +1,7 @@ /* * url.c -- Parse URLs * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -83,7 +83,7 @@ int websUrlParse(char_t *url, char_t **pbuf, char_t **phost, char_t **ppath, ulen = gstrlen(url); /* - * We allocate enough to store a separate hostname and port number fields. + * We allocate enough to store separate hostname and port number fields. * As there are 3 strings in the one buffer, we need room for 3 null chars. * We allocate MAX_PORT_LEN char_t's for the port number. */ @@ -146,16 +146,23 @@ int websUrlParse(char_t *url, char_t **pbuf, char_t **phost, char_t **ppath, } /* - * Parse the tag and the query + * Parse the query string */ - if ((cp = gstrchr(tok, '#')) != NULL) { - *cp++ = '\0'; - path = tok; - tok = cp; - } if ((cp = gstrchr(tok, '?')) != NULL) { *cp++ = '\0'; query = cp; + path = tok; + tok = query; + } + +/* + * Parse the fragment identifier + */ + if ((cp = gstrchr(tok, '#')) != NULL) { + *cp++ = '\0'; + if (*query == 0) { + path = tok; + } } /* diff --git a/cpukit/httpd/value.c b/cpukit/httpd/value.c index 5872382556..97dba81633 100644 --- a/cpukit/httpd/value.c +++ b/cpukit/httpd/value.c @@ -1,47 +1,60 @@ /* * value.c -- Generic type (holds all types) * - * Copyright (c) Go Ahead Software, Inc., 1995-1999 - * - * See the file "license.txt" for usage and redistribution license requirements + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. */ /******************************** Description *********************************/ /* * This module provides a generic type that can hold all possible types. - * It is designed to provide maximum effeciency. + * It is designed to provide maximum effeciency. */ /********************************* Includes ***********************************/ -#include "uemf.h" +#if UEMF + #include "uemf.h" +#else + #include "basic/basicInternal.h" +#endif /*********************************** Locals ***********************************/ +#if !UEMF +static value_t value_null; /* All zeros */ +/***************************** Forward Declarations ***************************/ +static void coerce_types(value_t* v1, value_t* v2); +static int value_to_integer(value_t* vp); +#endif /*!UEMF*/ /*********************************** Code *************************************/ /* - * Initialize a integer value. + * Initialize a integer value. */ value_t valueInteger(long value) { - value_t v = VALUE_VALID; + value_t v; + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = integer; v.value.integer = value; return v; } /******************************************************************************/ /* - * Initialize a string value. Note: not allocation + * Initialize a string value. */ -value_t valueString(char_t *value, int flags) +value_t valueString(char_t* value, int flags) { - value_t v = VALUE_VALID; + value_t v; + memset(&v, 0x0, sizeof(v)); + v.valid = 1; v.type = string; if (flags & VALUE_ALLOCATE) { v.allocated = 1; @@ -55,20 +68,1144 @@ value_t valueString(char_t *value, int flags) /******************************************************************************/ /* - * Free any storage allocated for a value. + * Free any storage allocated for a value. */ -void valueFree(value_t *v) +void valueFree(value_t* v) { - a_assert(v); - - if (v->valid && v->allocated && v->type == string && + if (v->valid && v->allocated && v->type == string && v->value.string != NULL) { bfree(B_L, v->value.string); } +#if !UEMF + if (v->valid && v->type == symbol && v->value.symbol.data != NULL && + v->value.symbol.freeCb !=NULL) { + v->value.symbol.freeCb(v->value.symbol.data); + } +#endif v->type = undefined; v->valid = 0; v->allocated = 0; } +#if !UEMF + +/******************************************************************************/ +/* + * Initialize an invalid value. + */ + +value_t valueInvalid() +{ + value_t v; + v.valid = 0; + v.type = undefined; + return v; +} + +/******************************************************************************/ +/* + * Initialize a flag value. + */ + +value_t valueBool(int value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.type = flag; + v.valid = 1; + v.value.flag = (char) value; + return v; +} + +/******************************************************************************/ +/* + * Initialize a byteint value. + */ + +value_t valueByteint(char value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = byteint; + v.value.byteint = value; + return v; +} + +/******************************************************************************/ +/* + * Initialize a shortint value. + */ + +value_t valueShortint(short value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = shortint; + v.value.shortint = value; + return v; +} + +#if FLOATING_POINT_SUPPORT +/******************************************************************************/ +/* + * Initialize a floating value. + */ + +value_t valueFloating(double value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = floating; + v.value.floating = value; + return v; +} +#endif /* FLOATING_POINT_SUPPORT */ + +/******************************************************************************/ +/* + * Initialize a big value. + */ + +value_t valueBig(long high_word, long low_word) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = big; + v.value.big[BLOW] = low_word; + v.value.big[BHIGH] = high_word; + return v; +} + +/******************************************************************************/ +/* + * Initialize a hex value. + */ + +value_t valueHex(int value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = hex; + v.value.integer = value; + return v; +} + +/******************************************************************************/ +/* + * Initialize a octal value. + */ + +value_t valueOctal(int value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = octal; + v.value.integer = value; + return v; +} + +/******************************************************************************/ +/* + * Initialize a percent value. + */ + +value_t valuePercent(int value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = percent; + v.value.percent = (char) value; + return v; +} + +/******************************************************************************/ +/* + * Initialize an byte array. Note: no allocation, just store the ptr + */ + +value_t valueBytes(char* value, int flags) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = bytes; + if (flags & VALUE_ALLOCATE) { + v.allocated = 1; + v.value.bytes = bstrdupA(B_L, value); + } else { + v.allocated = 0; + v.value.bytes = value; + } + return v; +} + +/******************************************************************************/ +/* + * Initialize a symbol value. + * Value parameter can hold a pointer to any type of value + * Free parameter can be NULL, or a function pointer to a function that will + * free the value + */ + +value_t valueSymbol(void *value, freeCallback freeCb) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = symbol; + v.value.symbol.data = value; + v.value.symbol.freeCb = freeCb; + return v; +} + +/******************************************************************************/ +/* + * Initialize an error message value. + */ + +value_t valueErrmsg(char_t* value) +{ + value_t v; + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + v.type = errmsg; + v.value.errmsg = value; + return v; +} + +/******************************************************************************/ +/* + * Copy a value. If the type is 'string' then allocate another string. + * Note: we allow the copy of a null value. + */ + +value_t valueCopy(value_t v2) +{ + value_t v1; + + v1 = v2; + if (v2.valid && v2.type == string && v2.value.string != NULL) { + v1.value.string = gstrdup(B_L, v2.value.string); + v1.allocated = 1; + } + return v1; +} + + +/******************************************************************************/ +/* + * Add a value. + */ + +value_t valueAdd(value_t v1, value_t v2) +{ + value_t v; + + a_assert(v1.valid); + a_assert(v2.valid); + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + + if (v1.type != v2.type) + coerce_types(&v1, &v2); + + switch (v1.type) { + default: + case string: + case bytes: + a_assert(0); + break; + +#if FLOATING_POINT_SUPPORT + case floating: + v1.value.floating += v2.value.floating; + return v1; +#endif + + case flag: + v1.value.bool |= v2.value.flag; + return v1; + + case byteint: + case percent: + v1.value.byteint += v2.value.byteint; + return v1; + + case shortint: + v1.value.shortint += v2.value.shortint; + return v1; + + case hex: + case integer: + case octal: + v1.value.integer += v2.value.integer; + return v1; + + case big: + v.type = big; + badd(v.value.big, v1.value.big, v2.value.big); + return v; + } + + return v1; +} + + +/******************************************************************************/ +/* + * Subtract a value. + */ + +value_t valueSub(value_t v1, value_t v2) +{ + value_t v; + + a_assert(v1.valid); + a_assert(v2.valid); + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + + if (v1.type != v2.type) + coerce_types(&v1, &v2); + switch (v1.type) { + default: + a_assert(0); + break; + +#if FLOATING_POINT_SUPPORT + case floating: + v1.value.floating -= v2.value.floating; + return v1; +#endif + + case flag: + v1.value.flag &= v2.value.flag; + return v1; + + case byteint: + case percent: + v1.value.byteint -= v2.value.byteint; + return v1; + + case shortint: + v1.value.shortint -= v2.value.shortint; + return v1; + + case hex: + case integer: + case octal: + v1.value.integer -= v2.value.integer; + return v1; + + case big: + v.type = big; + bsub(v.value.big, v1.value.big, v2.value.big); + return v; + } + + return v1; +} + + +/******************************************************************************/ +/* + * Multiply a value. + */ + +value_t valueMul(value_t v1, value_t v2) +{ + value_t v; + + a_assert(v1.valid); + a_assert(v2.valid); + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + + if (v1.type != v2.type) + coerce_types(&v1, &v2); + switch (v1.type) { + default: + a_assert(0); + break; + + case flag: + a_assert(v1.type != flag); + break; + +#if FLOATING_POINT_SUPPORT + case floating: + v1.value.floating *= v2.value.floating; + return v1; +#endif + + case byteint: + case percent: + v1.value.byteint *= v2.value.byteint; + return v1; + + case shortint: + v1.value.shortint *= v2.value.shortint; + return v1; + + case hex: + case integer: + case octal: + v1.value.integer *= v2.value.integer; + return v1; + + case big: + v.type = big; + bmul(v.value.big, v1.value.big, v2.value.big); + return v; + } + + return v1; +} + + +/******************************************************************************/ +/* + * Divide a value. + */ + +value_t valueDiv(value_t v1, value_t v2) +{ + value_t v; + + a_assert(v1.valid); + a_assert(v2.valid); + + memset(&v, 0x0, sizeof(v)); + v.valid = 1; + + if (v1.type != v2.type) + coerce_types(&v1, &v2); + switch (v1.type) { + default: + a_assert(0); + break; + + case flag: + a_assert(v1.type != flag); + break; + +#if FLOATING_POINT_SUPPORT + case floating: + v1.value.floating /= v2.value.floating; + return v1; +#endif + + case byteint: + case percent: + v1.value.byteint /= v2.value.byteint; + return v1; + + case shortint: + v1.value.shortint /= v2.value.shortint; + return v1; + + case hex: + case integer: + case octal: + v1.value.integer /= v2.value.integer; + return v1; + + case big: + v.type = big; + bdiv(v.value.big, v1.value.big, v2.value.big); + return v; + } + + return v1; +} + + +/******************************************************************************/ +/* + * Compare a value. + */ + +int valueCmp(value_t v1, value_t v2) +{ + a_assert(v1.valid); + a_assert(v2.valid); + + if (v1.type != v2.type) + coerce_types(&v1, &v2); + if (v1.type != v2.type) { +/* + * Make v2 == v1 + */ + a_assert(v1.type == v2.type); + v2 = v1; + return 0; + } + switch (v1.type) { + case string: + if (v1.value.string == NULL && v2.value.string == NULL) { + return 0; + } else if (v1.value.string == NULL) { + return -1; + } else if (v2.value.string == NULL) { + return 1; + } else { + return gstrcmp(v1.value.string, v2.value.string); + } + /* Nobody here */ + + case flag: + if (v1.value.flag < v2.value.flag) + return -1; + else if (v1.value.flag == v2.value.flag) + return 0; + else return 1; + +#if FLOATING_POINT_SUPPORT + case floating: + if (v1.value.floating < v2.value.floating) + return -1; + else if (v1.value.floating == v2.value.floating) + return 0; + else return 1; +#endif + + case byteint: + case percent: + if (v1.value.byteint < v2.value.byteint) + return -1; + else if (v1.value.byteint == v2.value.byteint) + return 0; + else return 1; + + case shortint: + if (v1.value.shortint < v2.value.shortint) + return -1; + else if (v1.value.shortint == v2.value.shortint) + return 0; + else return 1; + + case hex: + case integer: + case octal: + if (v1.value.integer < v2.value.integer) + return -1; + else if (v1.value.integer == v2.value.integer) + return 0; + else return 1; + + case big: + return bcompare(v1.value.big, v2.value.big); + + default: + a_assert(0); + return 0; + } +} + + +/******************************************************************************/ +/* + * If type mismatch, then coerce types to big. + * Note: Known bug, casting of negative bigs to floats doesn't work. + */ + +static void coerce_types(register value_t* v1, register value_t* v2) +{ +#if FLOATING_POINT_SUPPORT + if (v1->type == floating) { + v2->type = floating; + v2->value.floating = (double) v2->value.integer; + if (v2->type == big) + v2->value.floating = (double) v2->value.big[BLOW] + + (double) v2->value.big[BHIGH] * (double) MAXINT; + + } else if (v2->type == floating) { + v1->type = floating; + v1->value.floating = (double) v1->value.integer; + if (v1->type == big) + v1->value.floating = (double) v1->value.big[BLOW] + + (double) v1->value.big[BHIGH] * (double) MAXINT; + + } else if (v1->type == big) { +#else + if (v1->type == big) { +#endif /* FLOATING_POINT_SUPPORT */ + v2->value.big[BLOW] = value_to_integer(v2); + if (valueNegative(v2)) + v2->value.big[BHIGH] = -1; + else + v2->value.big[BHIGH] = 0; + v2->type = big; + + } else if (v2->type == big) { + if (valueNegative(v1)) + v1->value.big[BHIGH] = -1; + else + v1->value.big[BHIGH] = 0; + v1->value.big[BLOW] = value_to_integer(v1); + v1->type = big; + + + } else if (v1->type == integer) { + v2->value.integer = value_to_integer(v2); + v2->type = integer; + + } else if (v2->type == integer) { + v1->value.integer = value_to_integer(v1); + v1->type = integer; + + } else if (v1->type != integer) { + v2->type = v1->type; + + } else if (v2->type != integer) { + v1->type = v2->type; + + } + a_assert(v1->type == v2->type); +} + + +/******************************************************************************/ +/* + * Return true if the value is numeric and negative. Otherwise return 0. + */ + +int valueNegative(value_t* vp) +{ + switch (vp->type) { + default: + case string: + case bytes: + return 0; + +#if FLOATING_POINT_SUPPORT + case floating: + if (vp->value.floating < 0) + return 1; + return 0; +#endif + + case flag: + if ((signed char)vp->value.flag < 0) + return 1; + return 0; + + case byteint: + case percent: + if ((signed char)vp->value.byteint < 0) + return 1; + return 0; + + case shortint: + if (vp->value.shortint < 0) + return 1; + return 0; + + case hex: + case integer: + case octal: + if (vp->value.integer < 0) + return 1; + return 0; + + case big: + if (vp->value.big[BHIGH] < 0) + return 1; + return 0; + } +} + +/******************************************************************************/ +/* + * Return true if the value is numeric and zero. Otherwise return 0. + */ + +int valueZero(value_t* vp) +{ + switch (vp->type) { + default: + case string: + case bytes: + return 0; + +#if FLOATING_POINT_SUPPORT + case floating: + if (vp->value.floating == 0) + return 1; + return 0; +#endif + + case flag: + if (vp->value.flag == 0) + return 1; + return 0; + + case byteint: + case percent: + if (vp->value.byteint == 0) + return 1; + return 0; + + case shortint: + if (vp->value.shortint == 0) + return 1; + return 0; + + case hex: + case integer: + case octal: + if (vp->value.integer == 0) + return 1; + return 0; + + case big: + if (vp->value.big[BHIGH] == 0 && vp->value.big[BLOW] == 0) + return 1; + return 0; + } +} + + +/******************************************************************************/ +/* + * Cast a value to an integer. Cannot be called for floating, non-numerics + * or bigs. + */ + +static int value_to_integer(value_t* vp) +{ + switch (vp->type) { + default: + case string: + case bytes: + case big: +#if FLOATING_POINT_SUPPORT + case floating: + a_assert(0); + return -1; +#endif + + case flag: + return (int) vp->value.flag; + + case byteint: + case percent: + return (int) vp->value.byteint; + + case shortint: + return (int) vp->value.shortint; + + case hex: + case integer: + case octal: + return (int) vp->value.integer; + } +} + + +/******************************************************************************/ +/* + * Convert a value to a text based representation of its value + */ + +void valueSprintf(char_t** out, int size, char_t* fmt, value_t vp) +{ + char_t *src, *dst, *tmp, *dst_start; + + a_assert(out); + + *out = NULL; + + if (! vp.valid) { + *out = bstrdup(B_L, T("Invalid")); + return; + } + + switch (vp.type) { + case flag: + if (fmt == NULL || *fmt == '\0') { + *out = bstrdup(B_L, (vp.value.flag) ? T("true") : T("false")); + } else { + fmtAlloc(out, size, fmt, (vp.value.flag) ? T("true") : T("false")); + } + break; + +#if FLOATING_POINT_SUPPORT + case floating: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("%f"), vp.value.floating); + } else { + fmtAlloc(out, size, fmt, vp.value.floating); + } + break; +#endif + + case hex: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("0x%lx"), vp.value.hex); + } else { + fmtAlloc(out, size, fmt, vp.value.hex); + } + break; + + case big: + if (*out == NULL) { + *out = btoa(vp.value.big, NULL, 0); + } else { + btoa(vp.value.big, *out, size); + } + break; + + case integer: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("%ld"), vp.value.integer); + } else { + fmtAlloc(out, size, fmt, vp.value.integer); + } + break; + + case octal: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("0%lo"), vp.value.octal); + } else { + fmtAlloc(out, size, fmt, vp.value.octal); + } + break; + + case percent: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("%d%%"), vp.value.percent); + } else { + fmtAlloc(out, size, fmt, vp.value.percent); + } + break; + + case byteint: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("%d"), (int) vp.value.byteint); + } else { + fmtAlloc(out, size, fmt, (int) vp.value.byteint); + } + break; + + case shortint: + if (fmt == NULL || *fmt == '\0') { + fmtAlloc(out, size, T("%d"), (int) vp.value.shortint); + } else { + fmtAlloc(out, size, fmt, (int) vp.value.shortint); + } + break; + + case string: + case errmsg: + src = vp.value.string; + + if (src == NULL) { + *out = bstrdup(B_L, T("NULL")); + } else if (fmt && *fmt) { + fmtAlloc(out, size, fmt, src); + + } else { + + *out = balloc(B_L, size); + dst_start = dst = *out; + for (; *src != '\0'; src++) { + if (dst >= &dst_start[VALUE_MAX_STRING - 5]) + break; + switch (*src) { + case '\a': *dst++ = '\\'; *dst++ = 'a'; break; + case '\b': *dst++ = '\\'; *dst++ = 'b'; break; + case '\f': *dst++ = '\\'; *dst++ = 'f'; break; + case '\n': *dst++ = '\\'; *dst++ = 'n'; break; + case '\r': *dst++ = '\\'; *dst++ = 'r'; break; + case '\t': *dst++ = '\\'; *dst++ = 't'; break; + case '\v': *dst++ = '\\'; *dst++ = 'v'; break; + case '\\': *dst++ = '\\'; *dst++ = '\\'; break; + case '"': *dst++ = '\\'; *dst++ = '\"'; break; + default: + if (gisprint(*src)) { + *dst++ = *src; + } else { + fmtAlloc(&tmp, size, T("\\x%02x"), + (unsigned int) *src); + gstrcpy(dst, tmp); + bfreeSafe(B_L, tmp); + dst += 4; + } + break; + } + } + *dst++ = '\0'; + } + break; + +#if UNUSED + case bytes: + asrc = vp.value.bytes; + + if (asrc == NULL) { + *out = bstrdup(B_L, T("NULL")); + + } else if (fmt && *fmt) { + fmtAlloc(out, size, fmt, asrc); + + } else { + + dst_start = dst; + for (; *asrc != '\0'; asrc++) { + if (dst >= &dst_start[VALUE_MAX_STRING - 5]) + break; + switch (*asrc) { + case '\a': *dst++ = '\\'; *dst++ = 'a'; break; + case '\b': *dst++ = '\\'; *dst++ = 'b'; break; + case '\f': *dst++ = '\\'; *dst++ = 'f'; break; + case '\n': *dst++ = '\\'; *dst++ = 'n'; break; + case '\r': *dst++ = '\\'; *dst++ = 'r'; break; + case '\t': *dst++ = '\\'; *dst++ = 't'; break; + case '\v': *dst++ = '\\'; *dst++ = 'v'; break; + case '\\': *dst++ = '\\'; *dst++ = '\\'; break; + case '"': *dst++ = '\\'; *dst++ = '\"'; break; + default: + if (gisprint(*asrc)) { + *dst++ = *asrc; + } else { + fmtAlloc(dst, size, + T("\\x%02x"), (unsigned int) *asrc); + dst += 4; + } + break; + } + } + *dst++ = '\0'; + } + break; +#endif + + default: + a_assert(0); + } +} + +/******************************************************************************/ +/* + * Print a value to the named file descriptor + */ + +void valueFprintf(FILE* fp, char_t* fmt, value_t vp) +{ + char_t *buf; + + buf = NULL; + valueSprintf(&buf, VALUE_MAX_STRING, fmt, vp); + gfputs(buf, fp); + bfreeSafe(B_L, buf); + fflush(fp); +} + +/******************************************************************************/ +/* + * Ascii to value conversion + */ + +value_t valueAtov(char_t* s, int pref_type) +{ + vtype_t type; + value_t v; + long tmp[2], tmp2[2], base[2]; + int i, len, num; + + a_assert(0 <= pref_type && pref_type < 99); /* Sanity check */ + a_assert(s); + + v = value_null; + if (s == NULL) { + return value_null; + } + + base[BLOW] = 10; + base[BHIGH] = 0; + len = gstrlen(s); + +/* + * Determine the value type + */ + type = undefined; + if (pref_type <= 0) { + if (gisdigit(*s)) { + base[BHIGH] = 0; + if (s[len - 1] == '%') { + type = percent; + len --; + base[BLOW] = 10; + } else if (*s == '0') { + if (s[1] == 'x') { + type = hex; + s += 2; + len -= 2; + base[BLOW] = 16; + } else if (s[1] == '\0') { + type = integer; + base[BLOW] = 10; + } else { + type = octal; + s++; + len--; + base[BLOW] = 8; + } + } else { + type = integer; + base[BLOW] = 10; + } + + } else { + if (gstrcmp(s, T("true")) == 0 || gstrcmp(s, T("false")) == 0) { + type = flag; + } else if (*s == '\'' && s[len - 1] == '\'') { + type = string; + s++; + len -= 2; + } else if (*s == '\"' && s[len - 1] == '\"') { + type = string; + s++; + len -= 2; + } else { + type = string; + } + } + v.type = type; + + } else + v.type = pref_type; + v.valid = 1; + +/* + * Do the conversion. Always use big arithmetic + */ + switch (v.type) { + case hex: + if (!isdigit(s[0])) { + if (gtolower(s[0]) >= 'a' || gtolower(s[0]) <= 'f') { + v.value.big[BLOW] = 10 + gtolower(s[0]) - 'a'; + } else { + v.value.big[BLOW] = 0; + } + } else { + v.value.big[BLOW] = s[0] - '0'; + } + v.value.big[BHIGH] = 0; + for (i = 1; i < len; i++) { + if (!isdigit(s[i])) { + if (gtolower(s[i]) < 'a' || gtolower(s[i]) > 'f') { + break; + } + num = 10 + gtolower(s[i]) - 'a'; + } else { + num = s[i] - '0'; + } + bmul(tmp, v.value.big, base); + binit(tmp2, 0, num); + badd(v.value.big, tmp, tmp2); + } + v.value.hex = v.value.big[BLOW]; + break; + + case shortint: + case byteint: + case integer: + case percent: + case octal: + case big: + v.value.big[BHIGH] = 0; + if (gisdigit(s[0])) + v.value.big[BLOW] = s[0] - '0'; + else + v.value.big[BLOW] = 0; + for (i = 1; i < len && gisdigit(s[i]); i++) { + bmul(tmp, v.value.big, base); + binit(tmp2, 0, s[i] - '0'); + badd(v.value.big, tmp, tmp2); + } + switch (v.type) { + case shortint: + v.value.shortint = (short) v.value.big[BLOW]; + break; + case byteint: + v.value.byteint = (char) v.value.big[BLOW]; + break; + case integer: + v.value.integer = (int) v.value.big[BLOW]; + break; + case percent: + v.value.percent = (char) v.value.big[BLOW]; + break; + case octal: + v.value.octal = (int) v.value.big[BLOW]; + break; + default: + break; + } + break; + +#if FLOATING_POINT_SUPPORT + case floating: + gsscanf(s, T("%f"), &v.value.floating); + break; +#endif + + case flag: + if (*s == 't') + v.value.flag = 1; + else v.value.flag = 0; + break; + + case string: +/* + * Note this always ballocs a string + */ + v = valueString(s, VALUE_ALLOCATE); + break; + + case bytes: + v = valueBytes((char*) s, VALUE_ALLOCATE); + break; + +#if UNUSED + case literal: + v = value_literal(bstrdup(B_L, s)); + v.value.literal[len] = '\0'; + break; +#endif + + case undefined: + case symbol: + default: + v.valid = 0; + a_assert(0); + } + return v; +} + +#endif /* !UEMF */ /******************************************************************************/ diff --git a/cpukit/httpd/wbase64.c b/cpukit/httpd/wbase64.c deleted file mode 100644 index 0898b16728..0000000000 --- a/cpukit/httpd/wbase64.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * base64.c -- Base64 Mime encoding - * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. - * - * See the file "license.txt" for usage and redistribution license requirements - */ - -/******************************** Description *********************************/ - -/* - * The base64 command encodes and decodes a string in mime base64 format - */ - -/********************************* Includes ***********************************/ - -#include "wsIntrn.h" - -/******************************** Local Data **********************************/ -/* - * Mapping of ANSI chars to base64 Mime encoding alphabet (see below) - */ - -static char_t map64[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -}; - -static char_t alphabet64[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/', -}; - -/*********************************** Code *************************************/ -/* - * Decode a buffer from "string" and into "outbuf" - */ - -int websDecode64(char_t* outbuf, char_t* string, int outlen) -{ - unsigned long shiftbuf; - char_t* cp; - char_t* op; - int c, i, j, shift; - - op = outbuf; - *op = '\0'; - cp = string; - while (*cp && *cp != '=') { -/* - * Map 4 (6bit) input bytes and store in a single long (shiftbuf) - */ - shiftbuf = 0; - shift = 18; - for (i = 0; i < 4 && *cp && *cp != '='; i++, cp++) { - c = map64[*cp & 0xff]; - if (c == -1) { - error(E_L, E_LOG, T("Bad string: %s at %c index %d"), string, - c, i); - return -1; - } - shiftbuf = shiftbuf | (c << shift); - shift -= 6; - } -/* - * Interpret as 3 normal 8 bit bytes (fill in reverse order). - * Check for potential buffer overflow before filling. - */ - --i; - if ((op + i) >= &outbuf[outlen]) { - gstrcpy(outbuf, T("String too big")); - return -1; - } - for (j = 0; j < i; j++) { - *op++ = (char_t) ((shiftbuf >> (8 * (2 - j))) & 0xff); - } - *op = '\0'; - } - return 0; -} - - -/******************************************************************************/ -/* - * Encode a buffer from "string" into "outbuf" - */ - -void websEncode64(char_t* outbuf, char_t* string, int outlen) -{ - unsigned long shiftbuf; - char_t* cp; - char_t* op; - int x, i, j, shift; - - op = outbuf; - *op = '\0'; - cp = string; - while (*cp) { -/* - * Take three characters and create a 24 bit number in shiftbuf - */ - shiftbuf = 0; - for (j = 2; j >= 0 && *cp; j--, cp++) { - shiftbuf |= ((*cp & 0xff) << (j * 8)); - } -/* - * Now convert shiftbuf to 4 base64 letters. The i,j magic calculates - * how many letters need to be output. - */ - shift = 18; - for (i = ++j; i < 4 && op < &outbuf[outlen] ; i++) { - x = (shiftbuf >> shift) & 0x3f; - *op++ = alphabet64[(shiftbuf >> shift) & 0x3f]; - shift -= 6; - } -/* - * Pad at the end with '=' - */ - while (j-- > 0) { - *op++ = '='; - } - *op = '\0'; - } -} -/******************************************************************************/ diff --git a/cpukit/httpd/webcomp.c b/cpukit/httpd/webcomp.c index d642a144c0..575f4be46b 100644 --- a/cpukit/httpd/webcomp.c +++ b/cpukit/httpd/webcomp.c @@ -1,7 +1,7 @@ /* * webcomp -- Compile web pages into C source * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -71,10 +71,10 @@ static int compile(char_t *fileList, char_t *prefix) FILE *lp; time_t now; char_t file[FNAMESIZE]; - char_t *cp; + char_t *cp, *sl; char buf[512]; - char *p; - int j, i, len, fd, nFile; + unsigned char *p; + int j, i, len, fd, nFile; /* * Open list of files @@ -87,7 +87,7 @@ static int compile(char_t *fileList, char_t *prefix) time(&now); fprintf(stdout, "/*\n * webrom.c -- Compiled Web Pages\n *\n"); fprintf(stdout, " * Compiled by GoAhead WebCompile: %s */\n\n", - ctime(&now)); + gctime(&now)); fprintf(stdout, "#include \"wsIntrn.h\"\n\n"); fprintf(stdout, "#ifndef WEBS_PAGE_ROM\n"); fprintf(stdout, "websRomPageIndexType websRomPageIndex[] = {\n"); @@ -102,6 +102,9 @@ static int compile(char_t *fileList, char_t *prefix) if ((p = strchr(file, '\n')) || (p = strchr(file, '\r'))) { *p = '\0'; } + if (*file == '\0') { + continue; + } if (gstat(file, &sbuf) == 0 && sbuf.st_mode & S_IFDIR) { continue; } @@ -109,7 +112,7 @@ static int compile(char_t *fileList, char_t *prefix) fprintf(stderr, "Can't open file %s\n", file); return -1; } - fprintf(stdout, "static unsigned char page_%d[] = {\n", nFile); + fprintf(stdout, "static const unsigned char page_%d[] = {\n", nFile); while ((len = read(fd, buf, sizeof(buf))) > 0) { p = buf; @@ -143,6 +146,9 @@ static int compile(char_t *fileList, char_t *prefix) if ((p = strchr(file, '\n')) || (p = strchr(file, '\r'))) { *p = '\0'; } + if (*file == '\0') { + continue; + } /* * Remove the prefix and add a leading "/" when we print the path */ @@ -151,6 +157,9 @@ static int compile(char_t *fileList, char_t *prefix) } else { cp = file; } + while((sl = strchr(file, '\\')) != NULL) { + *sl = '/'; + } if (*cp == '/') { cp++; } @@ -159,8 +168,8 @@ static int compile(char_t *fileList, char_t *prefix) fprintf(stdout, " { T(\"/%s\"), 0, 0 },\n", cp); continue; } - fprintf(stdout, " { T(\"/%s\"), page_%d, %ld },\n", cp, nFile, - (long) sbuf.st_size); + fprintf(stdout, " { T(\"/%s\"), page_%d, %d },\n", cp, nFile, + sbuf.st_size); nFile++; } fclose(lp); diff --git a/cpukit/httpd/webmain.c b/cpukit/httpd/webmain.c index 9f467b5167..264a8d66c5 100644 --- a/cpukit/httpd/webmain.c +++ b/cpukit/httpd/webmain.c @@ -25,12 +25,22 @@ #include +#ifdef WEBS_SSL_SUPPORT +#include "websSSL.h" +#endif + +#ifdef USER_MANAGEMENT_SUPPORT +#include "um.h" +void formDefineUserMgmt(void); +#endif + /*********************************** Locals ***********************************/ /* * Change configuration here */ -static char_t *rootWeb = T("web"); /* Root web directory */ +extern const char *tftpServer; +static char_t *rootWeb = T("goahead"); /* Root web directory */ static char_t *password = T(""); /* Security password */ static int port = 80; /* Server port */ static int retries = 5; /* Server port retries */ @@ -113,33 +123,47 @@ rtems_httpd_daemon() /* * Initialize the web server */ - while (initWebs() < 0) { - printf("\nUnable to initialize Web server !!\n" - " Suspending the task. Resume to try again.\n"); - rtems_task_suspend( RTEMS_SELF); + if (initWebs() < 0) { + rtems_panic("Unable to initialize Web server !!\n"); } +#ifdef WEBS_SSL_SUPPORT + websSSLOpen(); +#endif + /* * Basic event loop. SocketReady returns true when a socket is ready for * service. SocketSelect will block until an event occurs. SocketProcess * will actually do the servicing. */ while (!finished) { - if (socketReady() || socketSelect()) { - socketProcess(); + if (socketReady(-1) || socketSelect(-1, 2000)) { + socketProcess(-1); } + /*websCgiCleanup();*/ + emfSchedProcess(); } +#ifdef WEBS_SSL_SUPPORT + websSSLClose(); +#endif + +#ifdef USER_MANAGEMENT_SUPPORT + umClose(); +#endif + /* * Close the socket module, report memory leaks and close the memory allocator */ websCloseServer(); + websDefaultClose(); socketClose(); + symSubClose(); #if B_STATS memLeaks(); #endif bclose(); - rtems_task_delete( RTEMS_SELF ); + rtems_task_delete( RTEMS_SELF ); } /******************************************************************************/ @@ -177,15 +201,14 @@ static int initWebs() memcpy((char *) &intaddr, (char *) hp->h_addr_list[0], (size_t) hp->h_length); +#if 0 /* - * Set ../web as the root web. Modify this to suit your needs + * Set /TFTP/x.y.z.w/goahead as the root web. Modify to suit your needs */ - getcwd(dir, sizeof(dir)); - if ((cp = strrchr(dir, '/'))) { - *cp = '\0'; - } - sprintf(webdir, "%s/%s", dir, rootWeb); - + sprintf(webdir, "/TFTP/%s/%s", tftpServer, rootWeb); +#else + sprintf(webdir, "/"); +#endif /* * Configure the web server options before opening the web server */ @@ -198,7 +221,11 @@ static int initWebs() /* * Configure the web server options before opening the web server */ +#if 0 websSetDefaultPage(T("default.asp")); +#else + websSetDefaultPage(T("index.html")); +#endif websSetPassword(password); /* @@ -380,7 +407,11 @@ static int websHomePageHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, * If the empty or "/" URL is invoked, redirect default URLs to the home page */ if (*url == '\0' || gstrcmp(url, T("/")) == 0) { +#if 0 websRedirect(wp, T("home.asp")); +#else + websRedirect(wp, T("index.html")); +#endif return 1; } return 0; @@ -391,12 +422,14 @@ static int websHomePageHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, #if B_STATS static void memLeaks() { - int fd; + int fd=1; - if ((fd = gopen(T("leak.txt"), O_CREAT | O_TRUNC | O_WRONLY)) >= 0) { + /* if ((fd = gopen(T("leak.txt"), O_CREAT | O_TRUNC | O_WRONLY)) >= 0) { */ bstats(fd, printMemStats); + /* close(fd); } + */ } /******************************************************************************/ @@ -416,4 +449,51 @@ static void printMemStats(int handle, char_t *fmt, ...) } #endif -/******************************************************************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/* + * Default error handler. The developer should insert code to handle + * error messages in the desired manner. + */ + +void defaultErrorHandler(int etype, char_t *msg) +{ +#if 1 + write(1, msg, gstrlen(msg)); +#endif +} + +/*****************************************************************************/ +/* + * Trace log. Customize this function to log trace output + */ + +void defaultTraceHandler(int level, char_t *buf) +{ +/* + * The following code would write all trace regardless of level + * to stdout. + */ +#if 1 + if (buf) { + write(1, buf, gstrlen(buf)); + } +#endif +} + +/*****************************************************************************/ +/* + * Returns a pointer to an allocated qualified unique temporary file name. + * This filename must eventually be deleted with bfree(); + */ + +char_t *websGetCgiCommName() +{ + char_t *pname1, *pname2; + + pname1 = tempnam(NULL, T("cgi")); + pname2 = bstrdup(B_L, pname1); + free(pname1); + return pname2; +} diff --git a/cpukit/httpd/webpage.c b/cpukit/httpd/webpage.c index d2b976e38f..efac13f508 100644 --- a/cpukit/httpd/webpage.c +++ b/cpukit/httpd/webpage.c @@ -1,7 +1,7 @@ /* * Page.c -- Support for page retrieval. * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -41,6 +41,8 @@ int websPageOpen(webs_t wp, char_t *lpath, char_t *path, int mode, int perm) void websPageClose(webs_t wp) { + a_assert(websValid(wp)); + #if WEBS_PAGE_ROM websRomPageClose(wp->docfd); #else diff --git a/cpukit/httpd/webrom.c b/cpukit/httpd/webrom.c index 225d8bbf22..61d795949e 100644 --- a/cpukit/httpd/webrom.c +++ b/cpukit/httpd/webrom.c @@ -1,7 +1,8 @@ /* * webrom.c -- Compiled Web Pages * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * * See the file "license.txt" for usage and redistribution license requirements */ diff --git a/cpukit/httpd/webs.c b/cpukit/httpd/webs.c index cee03ee01c..3181c602ec 100644 --- a/cpukit/httpd/webs.c +++ b/cpukit/httpd/webs.c @@ -1,32 +1,38 @@ /* * webs.c -- GoAhead Embedded HTTP webs server * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ */ /******************************** Description *********************************/ /* - * This module implements an embedded HTTP/1.1 webs server. It supports + * This module implements an embedded HTTP/1.1 web server. It supports * loadable URL handlers that define the nature of URL processing performed. */ /********************************* Includes ***********************************/ #include "wsIntrn.h" +#ifdef DIGEST_ACCESS_SUPPORT +#include "websda.h" +#endif /******************************** Global Data *********************************/ -websStatsType websStats; /* Web access stats */ +websStatsType websStats; /* Web access stats */ webs_t *webs; /* Open connection list head */ -sym_fd_t websMime; /* Set of mime types */ -int websMax; /* List size */ -int websPort; /* Listen port for server */ -char_t websHost[64]; /* Host name for the server */ -char_t websIpaddr[64]; /* IP address for the server */ +sym_fd_t websMime; /* Set of mime types */ +int websMax; /* List size */ +int websPort; /* Listen port for server */ +char_t websHost[64]; /* Host name for the server */ +char_t websIpaddr[64]; /* IP address for the server */ char_t *websHostUrl = NULL; /* URL to access server */ +char_t *websIpaddrUrl = NULL; /* URL to access server */ /*********************************** Locals ***********************************/ /* @@ -40,11 +46,13 @@ websErrorType websErrors[] = { { 302, T("Redirect") }, { 304, T("User local copy") }, { 400, T("Page not found") }, - { 401, T("Password Required") }, + { 401, T("Unauthorized") }, + { 403, T("Forbidden") }, { 404, T("Site or Page Not Found") }, { 405, T("Access Denied") }, { 500, T("Web Error") }, - { 503, T("Site Temporarily Unavailable. Try again") }, + { 501, T("Not Implemented") }, + { 503, T("Site Temporarily Unavailable. Try again.") }, { 0, NULL } }; @@ -54,29 +62,25 @@ static int websLogFd; /* Log file handle */ #endif static int websListenSock; /* Listen socket */ +static char_t websRealm[64] = T("GoAhead"); /* Realm name */ + +static int websOpenCount = 0; /* count of apps using this module */ /**************************** Forward Declarations ****************************/ -static int websAccept(int sid, char *ipaddr, int port); -static int websAlloc(int sid); + static char_t *websErrorMsg(int code); -static void websFree(webs_t wp); -static void websFreeVar(sym_t* sp); -static int websGetInput(webs_t wp, char_t **ptext, int *nbytes); -static int websParseFirst(webs_t wp, char_t *text); -static void websParseRequest(webs_t wp); -static void websReadEvent(webs_t wp); -static void websSocketEvent(int sid, int mask, int data); -static void websTimeout(long wp); -static void websTimeoutCancel(webs_t wp); -static void websMarkTime(webs_t wp); -static int websGetTimeSinceMark(webs_t wp); +static int websGetInput(webs_t wp, char_t **ptext, int *nbytes); +static int websParseFirst(webs_t wp, char_t *text); +static void websParseRequest(webs_t wp); +static void websSocketEvent(int sid, int mask, int data); +static int websGetTimeSinceMark(webs_t wp); #if WEBS_LOG_SUPPORT -static void websLog(webs_t wp, int code); +static void websLog(webs_t wp, int code); #endif #if WEBS_IF_MODIFIED_SUPPORT -static time_t dateParse(time_t tip, char_t *cmd); +static time_t dateParse(time_t tip, char_t *cmd); #endif /*********************************** Code *************************************/ @@ -88,6 +92,10 @@ int websOpenServer(int port, int retries) { websMimeType *mt; + if (++websOpenCount != 1) { + return websPort; + } + a_assert(port > 0); a_assert(retries >= 0); @@ -95,10 +103,12 @@ int websOpenServer(int port, int retries) websRomOpen(); #endif + webs = NULL; + websMax = 0; /* * Create a mime type lookup table for quickly determining the content type */ - websMime = symOpen(256); + websMime = symOpen(WEBS_SYM_INIT * 4); a_assert(websMime >= 0); for (mt = websMimeList; mt->type; mt++) { symEnter(websMime, mt->ext, valueString(mt->type, 0), 0); @@ -111,6 +121,7 @@ int websOpenServer(int port, int retries) if (websUrlHandlerOpen() < 0) { return -1; } + websFormOpen(); #if WEBS_LOG_SUPPORT /* @@ -134,6 +145,10 @@ void websCloseServer() webs_t wp; int wid; + if (--websOpenCount > 0) { + return; + } + /* * Close the listen handle first then all open connections. */ @@ -160,7 +175,7 @@ void websCloseServer() #if WEBS_PAGE_ROM websRomClose(); #endif - symClose(websMime, NULL); + symClose(websMime); websFormClose(); websUrlHandlerClose(); } @@ -193,19 +208,26 @@ int websOpenListen(int port, int retries) orig, port - 1); return -1; } - goahead_trace(0, T("webs: Listening for HTTP requests on port %d\n"), port); /* * Determine the full URL address to access the home page for this web server */ websPort = port; bfreeSafe(B_L, websHostUrl); - websHostUrl = NULL; + bfreeSafe(B_L, websIpaddrUrl); + websIpaddrUrl = websHostUrl = NULL; + if (port == 80) { websHostUrl = bstrdup(B_L, websHost); + websIpaddrUrl = bstrdup(B_L, websIpaddr); } else { - gsnprintf(&websHostUrl, WEBS_MAX_URL + 80, T("%s:%d"), websHost, port); + fmtAlloc(&websHostUrl, WEBS_MAX_URL + 80, T("%s:%d"), websHost, port); + fmtAlloc(&websIpaddrUrl, WEBS_MAX_URL + 80, T("%s:%d"), + websIpaddr, port); } + trace(0, T("webs: Listening for HTTP requests at address %s\n"), + websIpaddrUrl); + return port; } @@ -221,7 +243,8 @@ void websCloseListen() websListenSock = -1; } bfreeSafe(B_L, websHostUrl); - websHostUrl = NULL; + bfreeSafe(B_L, websIpaddrUrl); + websIpaddrUrl = websHostUrl = NULL; } /******************************************************************************/ @@ -229,7 +252,7 @@ void websCloseListen() * Accept a connection */ -static int websAccept(int sid, char *ipaddr, int port) +int websAccept(int sid, char *ipaddr, int port, int listenSid) { webs_t wp; int wid; @@ -247,6 +270,7 @@ static int websAccept(int sid, char *ipaddr, int port) } wp = webs[wid]; a_assert(wp); + wp->listenSid = listenSid; ascToUni(wp->ipaddr, ipaddr, sizeof(wp->ipaddr)); @@ -263,13 +287,13 @@ static int websAccept(int sid, char *ipaddr, int port) /* * Arrange for websSocketEvent to be called when read data is available */ - socketCreateHandler(sid, SOCKET_READABLE , websSocketEvent, (int) wp); + socketCreateHandler(sid, SOCKET_READABLE, websSocketEvent, (int) wp); /* * Arrange for a timeout to kill hung requests */ - wp->timeout = emfCreateTimer(WEBS_TIMEOUT, websTimeout, (long) wp); - goahead_trace(5, T("webs: accept request\n")); + wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout, (void *) wp); + trace(8, T("webs: accept request\n")); return 0; } @@ -308,10 +332,10 @@ static void websSocketEvent(int sid, int mask, int iwp) * Note: we never block as the socket is always in non-blocking mode. */ -static void websReadEvent(webs_t wp) +void websReadEvent(webs_t wp) { char_t *text; - int rc, nbytes, len, done; + int rc, nbytes, len, done, fd; a_assert(wp); a_assert(websValid(wp)); @@ -323,6 +347,7 @@ static void websReadEvent(webs_t wp) * and socketRead is called to read posted data. */ text = NULL; + fd = -1; for (done = 0; !done; ) { if (text) { bfree(B_L, text); @@ -366,15 +391,29 @@ static void websReadEvent(webs_t wp) * need to separate the lines with '\n' */ if (ringqLen(&wp->header) > 0) { - ringqPutstr(&wp->header, T("\n")); + ringqPutStr(&wp->header, T("\n")); } - ringqPutstr(&wp->header, text); + ringqPutStr(&wp->header, text); break; case WEBS_POST_CLEN: /* - * POST request with content specified by a content length + * POST request with content specified by a content length. + * If this is a CGI request, write the data to the cgi stdin. + * socketGets was used to get the data and it strips \n's so + * add them back in here. */ +#ifndef __NO_CGI_BIN + if (wp->flags & WEBS_CGI_REQUEST) { + if (fd == -1) { + fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY, + 0666); + } + gwrite(fd, text, gstrlen(text)); + gwrite(fd, T("\n"), sizeof(char_t)); + nbytes += 1; + } else +#endif if (wp->query) { if (wp->query[0] && !(wp->flags & WEBS_POST_DATA)) { /* @@ -412,7 +451,6 @@ static void websReadEvent(webs_t wp) wp->clen -= nbytes; if (wp->clen > 0) { if (nbytes > 0) { - done++; break; } done++; @@ -428,7 +466,21 @@ static void websReadEvent(webs_t wp) case WEBS_POST: /* * POST without content-length specification + * If this is a CGI request, write the data to the cgi stdin. + * socketGets was used to get the data and it strips \n's so + * add them back in here. */ + +#ifndef __NO_CGI_BIN + if (wp->flags & WEBS_CGI_REQUEST) { + if (fd == -1) { + fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY, + 0666); + } + gwrite(fd, text, gstrlen(text)); + gwrite(fd, T("\n"), sizeof(char_t)); + } else +#endif if (wp->query && *wp->query && !(wp->flags & WEBS_POST_DATA)) { len = gstrlen(wp->query); wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) + @@ -451,6 +503,11 @@ static void websReadEvent(webs_t wp) break; } } + + if (fd != -1) { + fd = gclose (fd); + } + if (text) { bfree(B_L, text); } @@ -459,8 +516,8 @@ static void websReadEvent(webs_t wp) /******************************************************************************/ /* * Get input from the browser. Return TRUE (!0) if the request has been - * handled. Return -1 on errors, 1 if input read, and 0 to instruct the - * caller to call again for more input. + * handled. Return -1 on errors or if the request has been processed, + * 1 if input read, and 0 to instruct the caller to call again for more input. * * Note: socketRead will Return the number of bytes read if successful. This * may be less than the requested "bufsize" and may be zero. It returns -1 for @@ -493,8 +550,16 @@ static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes) } if (len > 0) { - nbytes = socketRead(wp->sid, buf, len); +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + nbytes = websSSLRead(wp->wsp, buf, len); + } else { + nbytes = socketRead(wp->sid, buf, len); + } +#else + nbytes = socketRead(wp->sid, buf, len); +#endif if (nbytes < 0) { /* Error */ websDone(wp, 0); return -1; @@ -508,20 +573,47 @@ static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes) * is NULL terminated. */ buf[nbytes] = '\0'; - if ((text = ballocAscToUni(buf)) == NULL) { + if ((text = ballocAscToUni(buf, nbytes)) == NULL) { websError(wp, 503, T("Insufficient memory")); return -1; } } } else { +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + nbytes = websSSLGets(wp->wsp, &text); + } else { + nbytes = socketGets(wp->sid, &text); + } +#else nbytes = socketGets(wp->sid, &text); +#endif if (nbytes < 0) { + int eof; /* * Error, EOF or incomplete */ - if (socketEof(wp->sid)) { +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { +/* + * If state is WEBS_BEGIN and the request is secure, a -1 will + * usually indicate SSL negotiation + */ + if (wp->state == WEBS_BEGIN) { + eof = 1; + } else { + eof = websSSLEof(wp->wsp); + } + } else { + eof = socketEof(wp->sid); + } +#else + eof = socketEof(wp->sid); +#endif + + if (eof) { /* * If this is a post request without content length, process * the request as we now have all the data. Otherwise just @@ -559,10 +651,12 @@ static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes) clen = 1; } if (clen > 0) { - return 0; /* Get more data */ +/* + * Return 0 to get more data. + */ + return 0; } return 1; - } /* * We've read the header so go and handle the request @@ -570,7 +664,6 @@ static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes) websUrlHandlerRequest(wp); } return -1; - } } a_assert(text); @@ -587,7 +680,9 @@ static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes) static int websParseFirst(webs_t wp, char_t *text) { - char_t *op, *proto, *url, *host, *query, *path, *port, *ext, *buf; + char_t *op, *proto, *protoVer, *url, *host, *query, *path, *port, *ext; + char_t *buf; + int testPort; a_assert(websValid(wp)); a_assert(text && *text); @@ -621,6 +716,7 @@ static int websParseFirst(webs_t wp, char_t *text) websError(wp, 400, T("Bad HTTP request")); return -1; } + protoVer = gstrtok(NULL, T(" \t\n")); /* * Parse the URL and store all the various URL components. websUrlParse @@ -636,10 +732,28 @@ static int websParseFirst(webs_t wp, char_t *text) } wp->url = bstrdup(B_L, url); + +#ifndef __NO_CGI_BIN + if (gstrstr(url, CGI_BIN) != NULL) { + wp->flags |= WEBS_CGI_REQUEST; + if (wp->flags & WEBS_POST_REQUEST) { + wp->cgiStdin = websGetCgiCommName(); + } + } +#endif + wp->query = bstrdup(B_L, query); wp->host = bstrdup(B_L, host); wp->path = bstrdup(B_L, path); - wp->port = gatoi(port); + wp->protocol = bstrdup(B_L, proto); + wp->protoVersion = bstrdup(B_L, protoVer); + + if ((testPort = socketGetPort(wp->listenSid)) >= 0) { + wp->port = testPort; + } else { + wp->port = gatoi(port); + } + if (gstrcmp(ext, T(".asp")) == 0) { wp->flags |= WEBS_ASP; } @@ -673,9 +787,12 @@ static int websParseFirst(webs_t wp, char_t *text) * Parse a full request */ +#define isgoodchar(s) (gisalnum((s)) || ((s) == '/') || ((s) == '_') || \ + ((s) == '.') || ((s) == '-') ) + static void websParseRequest(webs_t wp) { - char_t *upperKey, *cp, *browser, *lp, *key, *value; + char_t *authType, *upperKey, *cp, *browser, *lp, *key, *value; a_assert(websValid(wp)); @@ -712,7 +829,7 @@ static void websParseRequest(webs_t wp) /* * Create a variable (CGI) for each line in the header */ - gsnprintf(&upperKey, (gstrlen(key) + 6), T("HTTP_%s"), key); + fmtAlloc(&upperKey, (gstrlen(key) + 6), T("HTTP_%s"), key); for (cp = upperKey; *cp; cp++) { if (*cp == '-') *cp = '_'; @@ -730,26 +847,155 @@ static void websParseRequest(webs_t wp) /* * Parse the user authorization. ie. password */ - } else if (gstrcmp(key, T("authorization")) == 0) { - char_t password[FNAMESIZE]; - + } else if (gstricmp(key, T("authorization")) == 0) { /* - * The incoming value is password:username + * Determine the type of Authorization Request */ - if ((cp = gstrchr(value, ' ')) != NULL) { - websDecode64(password, ++cp, sizeof(password)); - } else { - websDecode64(password, value, sizeof(password)); - } - if ((cp = gstrchr(password, ':')) != NULL) { - *cp++ = '\0'; + authType = bstrdup (B_L, value); + a_assert (authType); +/* + * Truncate authType at the next non-alpha character + */ + cp = authType; + while (gisalpha(*cp)) { + cp++; } - if (cp) { - wp->password = bstrdup(B_L, cp); + *cp = '\0'; + + wp->authType = bstrdup(B_L, authType); + bfree(B_L, authType); + + if (gstricmp(wp->authType, T("basic")) == 0) { + char_t userAuth[FNAMESIZE]; +/* + * The incoming value is username:password (Basic authentication) + */ + if ((cp = gstrchr(value, ' ')) != NULL) { + *cp = '\0'; + wp->authType = bstrdup(B_L, value); + websDecode64(userAuth, ++cp, sizeof(userAuth)); + } else { + websDecode64(userAuth, value, sizeof(userAuth)); + } +/* + * Split userAuth into userid and password + */ + if ((cp = gstrchr(userAuth, ':')) != NULL) { + *cp++ = '\0'; + } + if (cp) { + wp->userName = bstrdup(B_L, userAuth); + wp->password = bstrdup(B_L, cp); + } else { + wp->userName = bstrdup(B_L, T("")); + wp->password = bstrdup(B_L, T("")); + } +/* + * Set the flags to indicate digest authentication + */ + wp->flags |= WEBS_AUTH_BASIC; } else { - wp->password = bstrdup(B_L, T("")); - } +#ifdef DIGEST_ACCESS_SUPPORT +/* + * The incoming value is slightly more complicated (Digest) + */ + char_t *np; /* pointer to end of tag name */ + char_t tp; /* temporary character holding space */ + char_t *vp; /* pointer to value */ + char_t *npv; /* pointer to end of value, "next" pointer */ + char_t tpv; /* temporary character holding space */ +/* + * Set the flags to indicate digest authentication + */ + wp->flags |= WEBS_AUTH_DIGEST; +/* + * Move cp to Next word beyond "Digest", + * vp to first char after '='. + */ + cp = value; + while (isgoodchar(*cp)) { + cp++; + } + while (!isgoodchar(*cp)) { + cp++; + } + +/* + * Find beginning of value + */ + vp = gstrchr(cp, '='); + while (vp) { +/* + * Zero-terminate tag name + */ + np = cp; + while (isgoodchar(*np)) { + np++; + } + tp = *np; + *np = 0; +/* + * Advance value pointer to first legit character + */ + vp++; + while (!isgoodchar(*vp)) { + vp++; + } +/* + * Zero-terminate value + */ + npv = vp; + while (isgoodchar(*npv)) { + npv++; + } + tpv = *npv; + *npv = 0; +/* + * Extract the fields + */ + if (gstricmp(cp, T("username")) == 0) { + wp->userName = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("response")) == 0) { + wp->digest = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("opaque")) == 0) { + wp->opaque = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("uri")) == 0) { + wp->uri = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("realm")) == 0) { + wp->realm = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("nonce")) == 0) { + wp->nonce = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("nc")) == 0) { + wp->nc = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("cnonce")) == 0) { + wp->cnonce = bstrdup(B_L, vp); + } else if (gstricmp(cp, T("qop")) == 0) { + wp->qop = bstrdup(B_L, vp); + } +/* + * Restore tag name and value zero-terminations + */ + *np = tp; + *npv = tpv; +/* + * Advance tag name and value pointers + */ + cp = npv; + while (*cp && isgoodchar(*cp)) { + cp++; + } + while (*cp && !isgoodchar(*cp)) { + cp++; + } + if (*cp) { + vp = gstrchr(cp, '='); + } else { + vp = NULL; + } + } +#endif /* DIGEST_ACCESS_SUPPORT */ + } /* if (gstrcmp(wp->authType)) */ /* * Parse the content length */ @@ -758,6 +1004,12 @@ static void websParseRequest(webs_t wp) wp->clen = gatoi(value); websSetVar(wp, T("CONTENT_LENGTH"), value); +/* + * Parse the content type + */ + } else if (gstrcmp(key, T("content-type")) == 0) { + websSetVar(wp, T("CONTENT_TYPE"), value); + #if WEBS_KEEP_ALIVE_SUPPORT } else if (gstrcmp(key, T("connection")) == 0) { strlower(value); @@ -778,7 +1030,7 @@ static void websParseRequest(webs_t wp) if (gstrstr(tmp, T("no-cache"))) { wp->flags |= WEBS_DONT_USE_CACHE; } -#endif +#endif /* WEBS_PROXY_SUPPORT */ /* * Store the cookie @@ -796,42 +1048,22 @@ static void websParseRequest(webs_t wp) char_t *cmd; time_t tip = 0; - if (cp = gstrchr(value, ';')) { + if ((cp = gstrchr(value, ';')) != NULL) { *cp = '\0'; } - if (cp = gstrstr(value, T(", "))) { - cp += 2; - } + fmtAlloc(&cmd, 64, T("%s"), value); - if (gstrstr(cp, T("GMT"))) { - gsnprintf(&cmd, 64, T("clock scan %s -gmt 1"), cp); - } else { - gsnprintf(&cmd, 64, T("clock scan %s"), cp); - } - if (wp->since = dateParse(tip, cmd)) { + if ((wp->since = dateParse(tip, cmd)) != 0) { wp->flags |= WEBS_IF_MODIFIED; } + bfreeSafe(B_L, cmd); -#endif +#endif /* WEBS_IF_MODIFIED_SUPPORT */ } } } - -#if WEBS_IF_MODIFIED_SUPPORT -/******************************************************************************/ -/* - * Parse the date and time string. - */ - -static time_t dateParse(time_t tip, char_t *cmd) -{ - return (time_t)0; -} -#endif - - /******************************************************************************/ /* * Set the variable (CGI) environment for this request. Create variables @@ -842,20 +1074,26 @@ static time_t dateParse(time_t tip, char_t *cmd) void websSetEnv(webs_t wp) { char_t portBuf[8]; - char_t *keyword, *value; + char_t *keyword, *value, *valCheck, *valNew; a_assert(websValid(wp)); websSetVar(wp, T("QUERY_STRING"), wp->query); websSetVar(wp, T("GATEWAY_INTERFACE"), T("CGI/1.1")); websSetVar(wp, T("SERVER_HOST"), websHost); + websSetVar(wp, T("SERVER_NAME"), websHost); websSetVar(wp, T("SERVER_URL"), websHostUrl); websSetVar(wp, T("REMOTE_HOST"), wp->ipaddr); websSetVar(wp, T("REMOTE_ADDR"), wp->ipaddr); websSetVar(wp, T("PATH_INFO"), wp->path); stritoa(websPort, portBuf, sizeof(portBuf)); websSetVar(wp, T("SERVER_PORT"), portBuf); - + websSetVar(wp, T("SERVER_ADDR"), websIpaddr); + fmtAlloc(&value, FNAMESIZE, T("%s/%s"), WEBS_NAME, WEBS_VERSION); + websSetVar(wp, T("SERVER_SOFTWARE"), value); + bfreeSafe(B_L, value); + websSetVar(wp, T("SERVER_PROTOCOL"), wp->protoVersion); + /* * Decode and create an environment query variable for each query keyword. * We split into pairs at each '&', then split pairs at the '='. @@ -869,13 +1107,22 @@ void websSetEnv(webs_t wp) *value++ = '\0'; websDecodeUrl(keyword, keyword, gstrlen(keyword)); websDecodeUrl(value, value, gstrlen(value)); - } else { value = T(""); } if (*keyword) { - websSetVar(wp, keyword, value); +/* + * If keyword has already been set, append the new value to what has + * been stored. + */ + if ((valCheck = websGetVar(wp, keyword, NULL)) != 0) { + fmtAlloc(&valNew, 256, T("%s %s"), valCheck, value); + websSetVar(wp, keyword, valNew); + bfreeSafe(B_L, valNew); + } else { + websSetVar(wp, keyword, value); + } } keyword = gstrtok(NULL, T("&")); } @@ -935,7 +1182,7 @@ int websTestVar(webs_t wp, char_t *var) /******************************************************************************/ /* * Get a webs variable but return a default value if string not found. - * Note, defaultGetValue can be NULL to permit testing existance. + * Note, defaultGetValue can be NULL to permit testing existence. */ char_t *websGetVar(webs_t wp, char_t *var, char_t *defaultGetValue) @@ -956,18 +1203,34 @@ char_t *websGetVar(webs_t wp, char_t *var, char_t *defaultGetValue) return defaultGetValue; } +/******************************************************************************/ +/* + * Return TRUE if a webs variable is set to a given value + */ + +int websCompareVar(webs_t wp, char_t *var, char_t *value) +{ + a_assert(websValid(wp)); + a_assert(var && *var); + + if (gstrcmp(value, websGetVar(wp, var, T(" __UNDEF__ "))) == 0) { + return 1; + } + return 0; +} + /******************************************************************************/ /* * Cancel the request timeout. Note may be called multiple times. */ -static void websTimeoutCancel(webs_t wp) +void websTimeoutCancel(webs_t wp) { a_assert(websValid(wp)); - if (wp->timeout) { - emfDeleteTimer(wp->timeout); - wp->timeout = NULL; + if (wp->timeout >= 0) { + emfUnschedCallback(wp->timeout); + wp->timeout = -1; } } @@ -993,24 +1256,53 @@ void websResponse(webs_t wp, int code, char_t *message, char_t *redirect) */ if ( !(wp->flags & WEBS_HEADER_DONE)) { wp->flags |= WEBS_HEADER_DONE; - websWrite(wp, T("HTTP/1.0 %d %s\r\n"), code, websErrorMsg(code)); + websWrite(wp, T("HTTP/1.1 %d %s\r\n"), code, websErrorMsg(code)); +/* + * By license terms the following line of code must not be modified. + */ + websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); - /* by license terms the following line of code must - * not be modified. - */ - websWrite(wp, T("Server: GoAhead-Webs\r\n")); +/* + * Timestamp/Date is usually the next to go + */ + if ((date = websGetDateString(NULL)) != NULL) { + websWrite(wp, T("Date: %s\r\n"), date); + bfree(B_L, date); + } +/* + * If authentication is required, send the auth header info + */ + if (code == 401) { + if (!(wp->flags & WEBS_AUTH_DIGEST)) { + websWrite(wp, T("WWW-Authenticate: Basic realm=\"%s\"\r\n"), + websGetRealm()); +#ifdef DIGEST_ACCESS_SUPPORT + } else { + char_t *nonce, *opaque; + + nonce = websCalcNonce(wp), + opaque = websCalcOpaque(wp), + websWrite(wp, + T("WWW-Authenticate: Digest realm=\"%s\", domain=\"%s\",") + T("qop=\"%s\", nonce=\"%s\", opaque=\"%s\",") + T("algorithm=\"%s\", stale=\"%s\"\r\n"), + websGetRealm(), + websGetHostUrl(), + T("auth"), + nonce, + opaque, T("MD5"), T("FALSE")); + bfree(B_L, nonce); + bfree(B_L, opaque); +#endif + } + } if (wp->flags & WEBS_KEEP_ALIVE) { websWrite(wp, T("Connection: keep-alive\r\n")); } - websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n")); - if ((date = websGetDateString(NULL)) != NULL) { - websWrite(wp, T("Date: %s\r\n"), date); - bfree(B_L, date); - } + websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n")); websWrite(wp, T("Content-Type: text/html\r\n")); - /* * We don't do a string length here as the message may be multi-line. * Ie. will count as only one and we will have a content-length @@ -1024,7 +1316,10 @@ void websResponse(webs_t wp, int code, char_t *message, char_t *redirect) websWrite(wp, T("\r\n")); } - if (message && *message) { +/* + * If the browser didn't do a HEAD only request, send the message as well. + */ + if ((wp->flags & WEBS_HEAD_REQUEST) == 0 && message && *message) { websWrite(wp, T("%s\r\n"), message); } websDone(wp, code); @@ -1037,7 +1332,7 @@ void websResponse(webs_t wp, int code, char_t *message, char_t *redirect) void websRedirect(webs_t wp, char_t *url) { - char_t *msgbuf, *urlbuf; + char_t *msgbuf, *urlbuf, *redirectFmt; a_assert(websValid(wp)); a_assert(url); @@ -1052,7 +1347,16 @@ void websRedirect(webs_t wp, char_t *url) if (*url == '/') { url++; } - gsnprintf(&urlbuf, WEBS_MAX_URL + 80, T("http://%s/%s"), + + redirectFmt = T("http://%s/%s"); + +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + redirectFmt = T("https://%s/%s"); + } +#endif + + fmtAlloc(&urlbuf, WEBS_MAX_URL + 80, redirectFmt, websGetVar(wp, T("HTTP_HOST"), websHostUrl), url); url = urlbuf; } @@ -1060,7 +1364,7 @@ void websRedirect(webs_t wp, char_t *url) /* * Add human readable message for completeness. Should not be required. */ - gsnprintf(&msgbuf, WEBS_MAX_URL + 80, + fmtAlloc(&msgbuf, WEBS_MAX_URL + 80, T("\r\n\ This document has moved to a new location.\r\n\ Please update your documents to reflect the new location.\r\n\ @@ -1089,7 +1393,7 @@ void websError(webs_t wp, int code, char_t *fmt, ...) va_start(args, fmt); userMsg = NULL; - gvsnprintf(&userMsg, WEBS_BUFSIZE, fmt, args); + fmtValloc(&userMsg, WEBS_BUFSIZE, fmt, args); va_end(args); msg = T("Document Error: %s\r\n\ @@ -1099,7 +1403,7 @@ void websError(webs_t wp, int code, char_t *fmt, ...) * Ensure we have plenty of room */ buf = NULL; - gsnprintf(&buf, WEBS_BUFSIZE, msg, websErrorMsg(code), + fmtAlloc(&buf, WEBS_BUFSIZE, msg, websErrorMsg(code), websErrorMsg(code), wp->url, userMsg); websResponse(wp, code, buf, NULL); @@ -1114,7 +1418,7 @@ void websError(webs_t wp, int code, char_t *fmt, ...) static char_t *websErrorMsg(int code) { - websErrorType* ep; + websErrorType *ep; for (ep = websErrors; ep->code; ep++) { if (code == ep->code) { @@ -1131,7 +1435,7 @@ static char_t *websErrorMsg(int code) * write procedure. */ -int websWrite(webs_t wp, char_t* fmt, ...) +int websWrite(webs_t wp, char_t *fmt, ...) { va_list vargs; char_t *buf; @@ -1143,8 +1447,8 @@ int websWrite(webs_t wp, char_t* fmt, ...) buf = NULL; rc = 0; - if (gvsnprintf(&buf, WEBS_BUFSIZE, fmt, vargs) >= WEBS_BUFSIZE) { - goahead_trace(0, T("webs: websWrite lost data, buffer overflow\n")); + if (fmtValloc(&buf, WEBS_BUFSIZE, fmt, vargs) >= WEBS_BUFSIZE) { + trace(0, T("webs: websWrite lost data, buffer overflow\n")); } va_end(vargs); a_assert(buf); @@ -1160,61 +1464,94 @@ int websWrite(webs_t wp, char_t* fmt, ...) * Write a block of data of length "nChars" to the user's browser. Public * write block procedure. If unicode is turned on this function expects * buf to be a unicode string and it converts it to ASCII before writing. - * See websWriteBlockData to always write binary or ASCII data with no + * See websWriteDataNonBlock to always write binary or ASCII data with no * unicode conversion. This returns the number of char_t's processed. + * It spins until nChars are flushed to the socket. For non-blocking + * behavior, use websWriteDataNonBlock. */ int websWriteBlock(webs_t wp, char_t *buf, int nChars) { -#if ! UNICODE - return websWriteBlockData(wp, buf, nChars); -#else - int r; - char *charBuf; + int len, done; + char *asciiBuf, *pBuf; + a_assert(wp); + a_assert(websValid(wp)); a_assert(buf); a_assert(nChars >= 0); - if ((charBuf = ballocUniToAsc(buf, nChars)) == NULL) { - return -1; + done = len = 0; + +/* + * ballocUniToAsc will convert Unicode to strings to Ascii. If Unicode is + * not turned on then ballocUniToAsc will not do the conversion. + */ + pBuf = asciiBuf = ballocUniToAsc(buf, nChars); + + while (nChars > 0) { +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + if ((len = websSSLWrite(wp->wsp, pBuf, nChars)) < 0) { + bfree(B_L, asciiBuf); + return -1; + } + websSSLFlush(wp->wsp); + } else { + if ((len = socketWrite(wp->sid, pBuf, nChars)) < 0) { + bfree(B_L, asciiBuf); + return -1; + } + socketFlush(wp->sid); + } +#else /* ! WEBS_SSL_SUPPORT */ + if ((len = socketWrite(wp->sid, pBuf, nChars)) < 0) { + bfree(B_L, asciiBuf); + return -1; + } + socketFlush(wp->sid); +#endif /* WEBS_SSL_SUPPORT */ + nChars -= len; + pBuf += len; + done += len; } - r = websWriteBlockData(wp, charBuf, nChars); - bfree(B_L, charBuf); - return r; -#endif + + bfree(B_L, asciiBuf); + return done; } /******************************************************************************/ /* * Write a block of data of length "nChars" to the user's browser. Same as * websWriteBlock except that it expects straight ASCII or binary and does no - * unicode conversion before writing the data. - * This returns the number of chars processed. + * unicode conversion before writing the data. If the socket cannot hold all + * the data, it will return the number of bytes flushed to the socket before + * it would have blocked. This returns the number of chars processed or -1 + * if socketWrite fails. */ -int websWriteBlockData(webs_t wp, char *buf, int nChars) +int websWriteDataNonBlock(webs_t wp, char *buf, int nChars) { - int len, done; + int r; a_assert(wp); a_assert(websValid(wp)); a_assert(buf); a_assert(nChars >= 0); - done = len = 0; - while (nChars > 0) { - if ((len = socketWrite(wp->sid, buf, nChars)) < 0) { - return -1; - } -/* - * Block in flush if the last write could not take any more data - */ - socketFlush(wp->sid, len == 0); - nChars -= len; - buf += len; - done += len; +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + r = websSSLWrite(wp->wsp, buf, nChars); + websSSLFlush(wp->wsp); + } else { + r = socketWrite(wp->sid, buf, nChars); + socketFlush(wp->sid); } - return done; +#else + r = socketWrite(wp->sid, buf, nChars); + socketFlush(wp->sid); +#endif + + return r; } /******************************************************************************/ @@ -1229,7 +1566,6 @@ void websDecodeUrl(char_t *decoded, char_t *token, int len) a_assert(decoded); a_assert(token); - num = 0; op = decoded; for (ip = token; *ip && len > 0; ip++, op++) { @@ -1241,7 +1577,7 @@ void websDecodeUrl(char_t *decoded, char_t *token, int len) * Convert %nn to a single character */ ip++; - for (i = 0; i < 2; i++, ip++) { + for (i = 0, num = 0; i < 2; i++, ip++) { c = tolower(*ip); if (c >= 'a' && c <= 'f') { num = (num * 16) + 10 + c - 'a'; @@ -1275,7 +1611,7 @@ static void websLog(webs_t wp, int code) a_assert(websValid(wp)); buf = NULL; - gsnprintf(&buf, WEBS_MAX_URL + 80, T("%d %s %d %d\n"), time(0), + fmtAlloc(&buf, WEBS_MAX_URL + 80, T("%d %s %d %d\n"), time(0), wp->url, code, wp->written); len = gstrlen(buf); abuf = ballocUniToAsc(buf, len+1); @@ -1293,24 +1629,29 @@ static void websLog(webs_t wp, int code) * the browser, simply re-issue the timeout. */ -static void websTimeout(long iwp) +void websTimeout(void *arg, int id) { webs_t wp; int delay, tm; - wp = (webs_t) iwp; + wp = (webs_t) arg; a_assert(websValid(wp)); tm = websGetTimeSinceMark(wp) * 1000; if (tm >= WEBS_TIMEOUT) { websStats.timeouts++; - wp->timeout = NULL; + emfUnschedCallback(id); + +/* + * Clear the timeout id + */ + wp->timeout = -1; websDone(wp, 404); } else { delay = WEBS_TIMEOUT - tm; a_assert(delay > 0); - wp->timeout = emfCreateTimer(delay, websTimeout, (long) wp); + emfReschedCallback(id, delay); } } @@ -1349,12 +1690,25 @@ void websDone(webs_t wp, int code) */ websPageClose(wp); +/* + * Exit if secure. + */ +#ifdef WEBS_SSL_SUPPORT + if (wp->flags & WEBS_SECURE) { + websTimeoutCancel(wp); + websSSLFlush(wp->wsp); + socketCloseConnection(wp->sid); + websFree(wp); + return; + } +#endif + /* * If using Keep Alive (HTTP/1.1) we keep the socket open for a period * while waiting for another request on the socket. */ if (wp->flags & WEBS_KEEP_ALIVE) { - if (socketFlush(wp->sid, 0) == 0) { + if (socketFlush(wp->sid) == 0) { wp->state = WEBS_BEGIN; wp->flags |= WEBS_REQUEST_DONE; if (wp->header.buf) { @@ -1363,11 +1717,14 @@ void websDone(webs_t wp, int code) socketCreateHandler(wp->sid, SOCKET_READABLE, websSocketEvent, (int) wp); websTimeoutCancel(wp); - wp->timeout = emfCreateTimer(WEBS_TIMEOUT, websTimeout, (long) wp); + wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout, + (void *) wp); return; } } else { websTimeoutCancel(wp); + socketSetBlock(wp->sid, 1); + socketFlush(wp->sid); socketCloseConnection(wp->sid); } websFree(wp); @@ -1378,7 +1735,7 @@ void websDone(webs_t wp, int code) * Allocate a new webs structure */ -static int websAlloc(int sid) +int websAlloc(int sid) { webs_t wp; int wid; @@ -1396,7 +1753,26 @@ static int websAlloc(int sid) wp->sid = sid; wp->state = WEBS_BEGIN; wp->docfd = -1; + wp->timeout = -1; wp->dir = NULL; + wp->authType = NULL; + wp->protocol = NULL; + wp->protoVersion = NULL; + wp->password = NULL; + wp->userName = NULL; +#ifdef DIGEST_ACCESS_SUPPORT + wp->realm = NULL; + wp->nonce = NULL; + wp->digest = NULL; + wp->uri = NULL; + wp->opaque = NULL; + wp->nc = NULL; + wp->cnonce = NULL; + wp->qop = NULL; +#endif +#ifdef WEBS_SSL_SUPPORT + wp->wsp = NULL; +#endif ringqOpen(&wp->header, WEBS_HEADER_BUFINC, WEBS_MAX_HEADER); @@ -1405,7 +1781,7 @@ static int websAlloc(int sid) * both the CGI variables and for the global functions. The function table * is common to all webs instances (ie. all browsers) */ - wp->cgiVars = symOpen(64); + wp->cgiVars = symOpen(WEBS_SYM_INIT); return wid; } @@ -1415,7 +1791,7 @@ static int websAlloc(int sid) * Free a webs structure */ -static void websFree(webs_t wp) +void websFree(webs_t wp) { a_assert(websValid(wp)); @@ -1431,6 +1807,8 @@ static void websFree(webs_t wp) bfree(B_L, wp->query); if (wp->decodedQuery) bfree(B_L, wp->decodedQuery); + if (wp->authType) + bfree(B_L, wp->authType); if (wp->password) bfree(B_L, wp->password); if (wp->userName) @@ -1441,8 +1819,36 @@ static void websFree(webs_t wp) bfree(B_L, wp->userAgent); if (wp->dir) bfree(B_L, wp->dir); - - symClose(wp->cgiVars, websFreeVar); + if (wp->protocol) + bfree(B_L, wp->protocol); + if (wp->protoVersion) + bfree(B_L, wp->protoVersion); + if (wp->cgiStdin) + bfree(B_L, wp->cgiStdin); + + +#ifdef DIGEST_ACCESS_SUPPORT + if (wp->realm) + bfree(B_L, wp->realm); + if (wp->uri) + bfree(B_L, wp->uri); + if (wp->digest) + bfree(B_L, wp->digest); + if (wp->opaque) + bfree(B_L, wp->opaque); + if (wp->nonce) + bfree(B_L, wp->nonce); + if (wp->nc) + bfree(B_L, wp->nc); + if (wp->cnonce) + bfree(B_L, wp->cnonce); + if (wp->qop) + bfree(B_L, wp->qop); +#endif +#ifdef WEBS_SSL_SUPPORT + websSSLFree(wp->wsp); +#endif + symClose(wp->cgiVars); if (wp->header.buf) { ringqClose(&wp->header); @@ -1455,22 +1861,22 @@ static void websFree(webs_t wp) /******************************************************************************/ /* - * Callback from symClose. Free the variable. + * Return the server address */ -static void websFreeVar(sym_t* sp) +char_t *websGetHost() { - valueFree(&sp->content); + return websHost; } /******************************************************************************/ /* - * Return the server address + * Return the the url to access the server. (ip address) */ -char_t* websGetHost() +char_t *websGetIpaddrUrl() { - return websHost; + return websIpaddrUrl; } /******************************************************************************/ @@ -1478,7 +1884,7 @@ char_t* websGetHost() * Return the server address */ -char_t* websGetHostUrl() +char_t *websGetHostUrl() { return websHostUrl; } @@ -1582,7 +1988,7 @@ char_t *websGetRequestPath(webs_t wp) * Return the password */ -char_t* websGetRequestPassword(webs_t wp) +char_t *websGetRequestPassword(webs_t wp) { a_assert(websValid(wp)); @@ -1594,7 +2000,7 @@ char_t* websGetRequestPassword(webs_t wp) * Return the request type */ -char_t* websGetRequestType(webs_t wp) +char_t *websGetRequestType(webs_t wp) { a_assert(websValid(wp)); @@ -1606,7 +2012,7 @@ char_t* websGetRequestType(webs_t wp) * Return the username */ -char_t* websGetRequestUserName(webs_t wp) +char_t *websGetRequestUserName(webs_t wp) { a_assert(websValid(wp)); @@ -1772,35 +2178,15 @@ int websValid(webs_t wp) return 0; } -/******************************************************************************/ -/* - * Close the document handle. - */ - -int websCloseFileHandle(webs_t wp) -{ - a_assert(websValid(wp)); - -#ifndef WEBS_PAGE_ROM - if (wp->docfd >= 0) { - close(wp->docfd); - wp->docfd = -1; - } -#endif - - return 0; -} - /******************************************************************************/ /* * Build an ASCII time string. If sbuf is NULL we use the current time, * else we use the last modified time of sbuf; */ -char_t* websGetDateString(websStatType* sbuf) +char_t *websGetDateString(websStatType *sbuf) { - char_t* cp; - char_t* r; + char_t* cp, *r; time_t now; if (sbuf == NULL) { @@ -1823,7 +2209,7 @@ char_t* websGetDateString(websStatType* sbuf) * "real" time, but rather a relative marker. */ -static void websMarkTime(webs_t wp) +void websMarkTime(webs_t wp) { wp->timestamp = time(0); } @@ -1838,4 +2224,615 @@ static int websGetTimeSinceMark(webs_t wp) return time(0) - wp->timestamp; } +/******************************************************************************/ +/* + * Store the new realm name + */ + +void websSetRealm(char_t *realmName) +{ + a_assert(realmName); + + gstrncpy(websRealm, realmName, TSZ(websRealm)); +} + +/******************************************************************************/ +/* + * Return the realm name (used for authorization) + */ + +char_t *websGetRealm() +{ + return websRealm; +} + + +#if WEBS_IF_MODIFIED_SUPPORT +/******************************************************************************/ +/* + * These functions are intended to closely mirror the syntax for HTTP-date + * from RFC 2616 (HTTP/1.1 spec). This code was submitted by Pete Bergstrom. + */ + +/* + * RFC1123Date = wkday "," SP date1 SP time SP "GMT" + * RFC850Date = weekday "," SP date2 SP time SP "GMT" + * ASCTimeDate = wkday SP date3 SP time SP 4DIGIT + * + * Each of these functions tries to parse the value and update the index to + * the point it leaves off parsing. + */ + +typedef enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC } MonthEnumeration; +typedef enum { SUN, MON, TUE, WED, THU, FRI, SAT } WeekdayEnumeration; + +/******************************************************************************/ +/* + * Parse an N-digit value + */ + +static int parseNDIGIT(char_t *buf, int digits, int *index) +{ + int tmpIndex, returnValue; + + returnValue = 0; + + for (tmpIndex = *index; tmpIndex < *index+digits; tmpIndex++) { + if (gisdigit(buf[tmpIndex])) { + returnValue = returnValue * 10 + (buf[tmpIndex] - T('0')); + } + } + *index = tmpIndex; + + return returnValue; +} + +/******************************************************************************/ +/* + * Return an index into the month array + */ + +static int parseMonth(char_t *buf, int *index) +{ +/* + * "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | + * "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec" + */ + int tmpIndex, returnValue; + + returnValue = -1; + tmpIndex = *index; + + switch (buf[tmpIndex]) { + case 'A': + switch (buf[tmpIndex+1]) { + case 'p': + returnValue = APR; + break; + case 'u': + returnValue = AUG; + break; + } + break; + case 'D': + returnValue = DEC; + break; + case 'F': + returnValue = FEB; + break; + case 'J': + switch (buf[tmpIndex+1]) { + case 'a': + returnValue = JAN; + break; + case 'u': + switch (buf[tmpIndex+2]) { + case 'l': + returnValue = JUL; + break; + case 'n': + returnValue = JUN; + break; + } + break; + } + break; + case 'M': + switch (buf[tmpIndex+1]) { + case 'a': + switch (buf[tmpIndex+2]) { + case 'r': + returnValue = MAR; + break; + case 'y': + returnValue = MAY; + break; + } + break; + } + break; + case 'N': + returnValue = NOV; + break; + case 'O': + returnValue = OCT; + break; + case 'S': + returnValue = SEP; + break; + } + + if (returnValue >= 0) { + *index += 3; + } + + return returnValue; +} + +/******************************************************************************/ +/* + * Parse a year value (either 2 or 4 digits) + */ + +static int parseYear(char_t *buf, int *index) +{ + int tmpIndex, returnValue; + + tmpIndex = *index; + returnValue = parseNDIGIT(buf, 4, &tmpIndex); + + if (returnValue >= 0) { + *index = tmpIndex; + } else { + returnValue = parseNDIGIT(buf, 2, &tmpIndex); + if (returnValue >= 0) { +/* + * Assume that any year earlier than the start of the + * epoch for time_t (1970) specifies 20xx + */ + if (returnValue < 70) { + returnValue += 2000; + } else { + returnValue += 1900; + } + + *index = tmpIndex; + } + } + + return returnValue; +} + +/******************************************************************************/ +/* + * The formulas used to build these functions are from "Calendrical Calculations", + * by Nachum Dershowitz, Edward M. Reingold, Cambridge University Press, 1997. + */ + +#include + +const int GregorianEpoch = 1; + +/******************************************************************************/ +/* + * Determine if year is a leap year + */ + +int GregorianLeapYearP(long year) +{ + int result; + long tmp; + + tmp = year % 400; + + if ((year % 4 == 0) && + (tmp != 100) && + (tmp != 200) && + (tmp != 300)) { + result = TRUE; + } else { + result = FALSE; + } + + return result; +} + +/******************************************************************************/ +/* + * Return the fixed date from the gregorian date + */ + +long FixedFromGregorian(long month, long day, long year) +{ + long fixedDate; + + fixedDate = (long)(GregorianEpoch - 1 + 365 * (year - 1) + + floor((year - 1) / 4.0) - + floor((double)(year - 1) / 100.0) + + floor((double)(year - 1) / 400.0) + + floor((367.0 * ((double)month) - 362.0) / 12.0)); + + if (month <= 2) { + fixedDate += 0; + } else if (TRUE == GregorianLeapYearP(year)) { + fixedDate += -1; + } else { + fixedDate += -2; + } + + fixedDate += day; + + return fixedDate; +} + +/******************************************************************************/ +/* + * Return the gregorian year from a fixed date + */ + +long GregorianYearFromFixed(long fixedDate) +{ + long result, d0, n400, d1, n100, d2, n4, d3, n1, d4, year; + + d0 = fixedDate - GregorianEpoch; + n400 = (long)(floor((double)d0 / (double)146097)); + d1 = d0 % 146097; + n100 = (long)(floor((double)d1 / (double)36524)); + d2 = d1 % 36524; + n4 = (long)(floor((double)d2 / (double)1461)); + d3 = d2 % 1461; + n1 = (long)(floor((double)d3 / (double)365)); + d4 = (d3 % 365) + 1; + year = 400 * n400 + 100 * n100 + 4 * n4 + n1; + + if ((n100 == 4) || (n1 == 4)) { + result = year; + } else { + result = year + 1; + } + + return result; +} + +/******************************************************************************/ +/* + * Returns the Gregorian date from a fixed date + * (not needed for this use, but included for completeness + */ + +#if 0 +GregorianFromFixed(long fixedDate, long *month, long *day, long *year) +{ + long priorDays, correction; + + *year = GregorianYearFromFixed(fixedDate); + priorDays = fixedDate - FixedFromGregorian(1, 1, *year); + + if (fixedDate < FixedFromGregorian(3,1,*year)) { + correction = 0; + } else if (true == GregorianLeapYearP(*year)) { + correction = 1; + } else { + correction = 2; + } + + *month = (long)(floor((12.0 * (double)(priorDays + correction) + 373.0) / 367.0)); + *day = fixedDate - FixedFromGregorian(*month, 1, *year); +} +#endif + +/******************************************************************************/ +/* + * Returns the difference between two Gregorian dates + */ + +long GregorianDateDifference( long month1, long day1, long year1, + long month2, long day2, long year2) +{ + return FixedFromGregorian(month2, day2, year2) - + FixedFromGregorian(month1, day1, year1); +} + + +/******************************************************************************/ +/* + * Return the number of seconds into the current day + */ + +#define SECONDS_PER_DAY 24*60*60 + +static int parseTime(char_t *buf, int *index) +{ +/* + * Format of buf is - 2DIGIT ":" 2DIGIT ":" 2DIGIT + */ + int returnValue, tmpIndex, hourValue, minuteValue, secondValue; + + hourValue = minuteValue = secondValue = -1; + returnValue = -1; + tmpIndex = *index; + + hourValue = parseNDIGIT(buf, 2, &tmpIndex); + + if (hourValue >= 0) { + tmpIndex++; + minuteValue = parseNDIGIT(buf, 2, &tmpIndex); + if (minuteValue >= 0) { + tmpIndex++; + secondValue = parseNDIGIT(buf, 2, &tmpIndex); + } + } + + if ((hourValue >= 0) && + (minuteValue >= 0) && + (secondValue >= 0)) { + returnValue = (((hourValue * 60) + minuteValue) * 60) + secondValue; + *index = tmpIndex; + } + + return returnValue; +} + +/******************************************************************************/ +/* + * Return the equivalent of time() given a gregorian date + */ + +static time_t dateToTimet(int year, int month, int day) +{ + long dayDifference; + + dayDifference = FixedFromGregorian(month, day, year) - + FixedFromGregorian(1, 1, 1970); + + return dayDifference * SECONDS_PER_DAY; +} + +/******************************************************************************/ +/* + * Return the number of seconds between Jan 1, 1970 and the parsed date + * (corresponds to documentation for time() function) + */ + +static time_t parseDate1or2(char_t *buf, int *index) +{ +/* + * Format of buf is either + * 2DIGIT SP month SP 4DIGIT + * or + * 2DIGIT "-" month "-" 2DIGIT + */ + int dayValue, monthValue, yearValue, tmpIndex; + time_t returnValue; + + returnValue = (time_t) -1; + tmpIndex = *index; + + dayValue = monthValue = yearValue = -1; + + if (buf[tmpIndex] == T(',')) { +/* + * Skip over the ", " + */ + tmpIndex += 2; + + dayValue = parseNDIGIT(buf, 2, &tmpIndex); + if (dayValue >= 0) { +/* + * Skip over the space or hyphen + */ + tmpIndex++; + monthValue = parseMonth(buf, &tmpIndex); + if (monthValue >= 0) { +/* + * Skip over the space or hyphen + */ + tmpIndex++; + yearValue = parseYear(buf, &tmpIndex); + } + } + + if ((dayValue >= 0) && + (monthValue >= 0) && + (yearValue >= 0)) { + if (yearValue < 1970) { +/* + * Allow for Microsoft IE's year 1601 dates + */ + returnValue = 0; + } else { + returnValue = dateToTimet(yearValue, monthValue, dayValue); + } + *index = tmpIndex; + } + } + + return returnValue; +} + +/******************************************************************************/ +/* + * Return the number of seconds between Jan 1, 1970 and the parsed date + */ + +static time_t parseDate3Time(char_t *buf, int *index) +{ +/* + * Format of buf is month SP ( 2DIGIT | ( SP 1DIGIT )) + */ + int dayValue, monthValue, yearValue, timeValue, tmpIndex; + time_t returnValue; + + returnValue = (time_t) -1; + tmpIndex = *index; + + dayValue = monthValue = yearValue = timeValue = -1; + + monthValue = parseMonth(buf, &tmpIndex); + if (monthValue >= 0) { +/* + * Skip over the space + */ + tmpIndex++; + if (buf[tmpIndex] == T(' ')) { +/* + * Skip over this space too + */ + tmpIndex++; + dayValue = parseNDIGIT(buf, 1, &tmpIndex); + } else { + dayValue = parseNDIGIT(buf, 2, &tmpIndex); + } +/* + * Now get the time and time SP 4DIGIT + */ + timeValue = parseTime(buf, &tmpIndex); + if (timeValue >= 0) { +/* + * Now grab the 4DIGIT year value + */ + yearValue = parseYear(buf, &tmpIndex); + } + } + + if ((dayValue >= 0) && + (monthValue >= 0) && + (yearValue >= 0)) { + returnValue = dateToTimet(yearValue, monthValue, dayValue); + returnValue += timeValue; + *index = tmpIndex; + } + + return returnValue; +} + + +/******************************************************************************/ +/* + * Although this looks like a trivial function, I found I was replicating the implementation + * seven times in the parseWeekday function. In the interests of minimizing code size + * and redundancy, it is broken out into a separate function. The cost of an extra + * function call I can live with given that it should only be called once per HTTP request. + */ + +static int bufferIndexIncrementGivenNTest(char_t *buf, int testIndex, char_t testChar, + int foundIncrement, int notfoundIncrement) +{ + if (buf[testIndex] == testChar) { + return foundIncrement; + } + + return notfoundIncrement; +} + +/******************************************************************************/ +/* + * Return an index into a logical weekday array + */ + +static int parseWeekday(char_t *buf, int *index) +{ +/* + * Format of buf is either + * "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun" + * or + * "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday" + */ + int tmpIndex, returnValue; + + returnValue = -1; + tmpIndex = *index; + + switch (buf[tmpIndex]) { + case 'F': + returnValue = FRI; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'd', sizeof("Friday"), 3); + break; + case 'M': + returnValue = MON; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'd', sizeof("Monday"), 3); + break; + case 'S': + switch (buf[tmpIndex+1]) { + case 'a': + returnValue = SAT; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'u', sizeof("Saturday"), 3); + break; + case 'u': + returnValue = SUN; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'd', sizeof("Sunday"), 3); + break; + } + break; + case 'T': + switch (buf[tmpIndex+1]) { + case 'h': + returnValue = THU; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'r', sizeof("Thursday"), 3); + break; + case 'u': + returnValue = TUE; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 's', sizeof("Tuesday"), 3); + break; + } + break; + case 'W': + returnValue = WED; + *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'n', sizeof("Wednesday"), 3); + break; + } + return returnValue; +} + +/******************************************************************************/ +/* + * Parse the date and time string. + */ + +static time_t dateParse(time_t tip, char_t *cmd) +{ + int index, tmpIndex, weekday, timeValue; + time_t parsedValue, dateValue; + + parsedValue = (time_t) 0; + index = timeValue = 0; + weekday = parseWeekday(cmd, &index); + + if (weekday >= 0) { + tmpIndex = index; + dateValue = parseDate1or2(cmd, &tmpIndex); + if (dateValue >= 0) { + index = tmpIndex + 1; +/* + * One of these two forms is being used + * wkday "," SP date1 SP time SP "GMT" + * weekday "," SP date2 SP time SP "GMT" + */ + timeValue = parseTime(cmd, &index); + if (timeValue >= 0) { +/* + * Now match up that "GMT" string for completeness + * Compute the final value if there were no problems in the parse + */ + if ((weekday >= 0) && + (dateValue >= 0) && + (timeValue >= 0)) { + parsedValue = dateValue + timeValue; + } + } + } else { +/* + * Try the other form - wkday SP date3 SP time SP 4DIGIT + */ + tmpIndex = index; + parsedValue = parseDate3Time(cmd, &tmpIndex); + } + } + + return parsedValue; +} + +#endif /* WEBS_IF_MODIFIED_SUPPORT */ + + /******************************************************************************/ diff --git a/cpukit/httpd/webs.h b/cpukit/httpd/webs.h index 46e11bc2fc..4b847502a5 100644 --- a/cpukit/httpd/webs.h +++ b/cpukit/httpd/webs.h @@ -1,7 +1,7 @@ /* - * webs.h -- Go Ahead Web public header + * webs.h -- GoAhead Web public header * - * Copyright (c) Go Ahead Software Inc., 1992-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved. * * See the file "license.txt" for information on usage and redistribution */ @@ -12,7 +12,7 @@ /******************************** Description *********************************/ /* - * Go Ahead Web Server header. This defines the Web public APIs. + * GoAhead Web Server header. This defines the Web public APIs. * Include this header for files that contain ASP or Form procedures. * Include wsIntrn.h when creating URL handlers. */ @@ -20,8 +20,17 @@ /********************************* Includes ***********************************/ #include "ej.h" +#ifdef WEBS_SSL_SUPPORT +#include "websSSL.h" +#endif /********************************** Defines ***********************************/ +/* + * By license terms the server software name defined in the following line of + * code must not be modified. + */ +#define WEBS_NAME T("GoAhead-Webs") +#define WEBS_VERSION T("2.1") #define WEBS_HEADER_BUFINC 512 /* Header buffer size */ #define WEBS_ASP_BUFINC 512 /* Asp expansion increment */ @@ -31,6 +40,9 @@ #define WEBS_MAX_URL 4096 /* Maximum URL size for sanity */ #define WEBS_SOCKET_BUFSIZ 256 /* Bytes read from socket */ +#define WEBS_HTTP_PORT T("httpPort") +#define CGI_BIN T("cgi-bin") + /* * Request flags. Also returned by websGetRequestFlags(). */ @@ -48,6 +60,10 @@ #define WEBS_FORM 0x800 /* Request is a form */ #define WEBS_REQUEST_DONE 0x1000 /* Request complete */ #define WEBS_POST_DATA 0x2000 /* Already appended post data */ +#define WEBS_CGI_REQUEST 0x4000 /* cgi-bin request */ +#define WEBS_SECURE 0x8000 /* connection uses SSL */ +#define WEBS_AUTH_BASIC 0x10000 /* Basic authentication request */ +#define WEBS_AUTH_DIGEST 0x20000 /* Digest authentication request */ #define WEBS_HEADER_DONE 0x40000 /* Already output the HTTP header */ /* @@ -65,38 +81,56 @@ typedef struct websRec { sym_fd_t cgiVars; /* CGI standard variables */ sym_fd_t cgiQuery; /* CGI decoded query string */ time_t timestamp; /* Last transaction with browser */ - void* timeout; /* Timeout handle */ + int timeout; /* Timeout handle */ char_t ipaddr[32]; /* Connecting ipaddress */ char_t type[64]; /* Mime type */ - char_t* dir; /* Directory containing the page */ - char_t* path; /* Path name without query */ - char_t* url; /* Full request url */ - char_t* host; /* Requested host */ - char_t* lpath; /* Cache local path name */ - char_t* query; /* Request query */ - char_t* decodedQuery; /* Decoded request query */ - char_t* password; /* Authorization password */ - char_t* userName; /* Authorization username */ - char_t* cookie; /* Cookie string */ - char_t* userAgent; /* User agent (browser) */ + char_t *dir; /* Directory containing the page */ + char_t *path; /* Path name without query */ + char_t *url; /* Full request url */ + char_t *host; /* Requested host */ + char_t *lpath; /* Cache local path name */ + char_t *query; /* Request query */ + char_t *decodedQuery; /* Decoded request query */ + char_t *authType; /* Authorization type (Basic/DAA) */ + char_t *password; /* Authorization password */ + char_t *userName; /* Authorization username */ + char_t *cookie; /* Cookie string */ + char_t *userAgent; /* User agent (browser) */ + char_t *protocol; /* Protocol (normally HTTP) */ + char_t *protoVersion; /* Protocol version */ int sid; /* Socket id (handler) */ + int listenSid; /* Listen Socket id */ int port; /* Request port number */ int state; /* Current state */ int flags; /* Current flags -- see above */ int code; /* Request result code */ int clen; /* Content length */ int wid; /* Index into webs */ + char_t *cgiStdin; /* filename for CGI stdin */ int docfd; /* Document file descriptor */ - int numbytes; /* Bytes to transfer to browser */ + int numbytes; /* Bytes to transfer to browser */ int written; /* Bytes actually transferred */ void (*writeSocket)(struct websRec *wp); +#ifdef DIGEST_ACCESS_SUPPORT + char_t *realm; /* usually the same as "host" from websRec */ + char_t *nonce; /* opaque-to-client string sent by server */ + char_t *digest; /* digest form of user password */ + char_t *uri; /* URI found in DAA header */ + char_t *opaque; /* opaque value passed from server */ + char_t *nc; /* nonce count */ + char_t *cnonce; /* check nonce */ + char_t *qop; /* quality operator */ +#endif +#ifdef WEBS_SSL_SUPPORT + websSSL_t *wsp; /* SSL data structure */ +#endif } websRec; typedef websRec *webs_t; typedef websRec websType; /******************************** Prototypes **********************************/ - +extern int websAccept(int sid, char *ipaddr, int port, int listenSid); extern int websAspDefine(char_t *name, int (*fn)(int ejid, webs_t wp, int argc, char_t **argv)); extern int websAspRequest(webs_t wp, char_t *lpath); @@ -112,9 +146,11 @@ extern int websFormDefine(char_t *name, void (*fn)(webs_t wp, extern char_t *websGetDefaultDir(); extern char_t *websGetDefaultPage(); extern char_t *websGetHostUrl(); +extern char_t *websGetIpaddrUrl(); extern char_t *websGetPassword(); extern int websGetPort(); extern char_t *websGetPublishDir(char_t *path, char_t **urlPrefix); +extern char_t *websGetRealm(); extern int websGetRequestBytes(webs_t wp); extern char_t *websGetRequestDir(webs_t wp); extern int websGetRequestFlags(webs_t wp); @@ -125,6 +161,7 @@ extern char_t *websGetRequestPassword(webs_t wp); extern char_t *websGetRequestType(webs_t wp); extern int websGetRequestWritten(webs_t wp); extern char_t *websGetVar(webs_t wp, char_t *var, char_t *def); +extern int websCompareVar(webs_t wp, char_t *var, char_t *value); extern void websHeader(webs_t wp); extern int websOpenListen(int port, int retries); extern int websPageOpen(webs_t wp, char_t *lpath, char_t *path, @@ -142,6 +179,7 @@ extern void websSetEnv(webs_t wp); extern void websSetHost(char_t *host); extern void websSetIpaddr(char_t *ipaddr); extern void websSetPassword(char_t *password); +extern void websSetRealm(char_t *realmName); extern void websSetRequestBytes(webs_t wp, int bytes); extern void websSetRequestFlags(webs_t wp, int flags); extern void websSetRequestLpath(webs_t wp, char_t *lpath); @@ -150,6 +188,7 @@ extern char_t *websGetRequestUserName(webs_t wp); extern void websSetRequestWritten(webs_t wp, int written); extern void websSetVar(webs_t wp, char_t *var, char_t *value); extern int websTestVar(webs_t wp, char_t *var); +extern void websTimeoutCancel(webs_t wp); extern int websUrlHandlerDefine(char_t *urlPrefix, char_t *webDir, int arg, int (*fn)(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, @@ -164,10 +203,18 @@ extern int websUrlParse(char_t *url, char_t **buf, char_t **host, extern char_t *websUrlType(char_t *webs, char_t *buf, int charCnt); extern int websWrite(webs_t wp, char_t* fmt, ...); extern int websWriteBlock(webs_t wp, char_t *buf, int nChars); -extern int websWriteBlockData(webs_t wp, char *buf, int nChars); +extern int websWriteDataNonBlock(webs_t wp, char *buf, int nChars); extern int websValid(webs_t wp); extern int websValidateUrl(webs_t wp, char_t *path); -extern int websCloseFileHandle(webs_t wp); +extern void websMarkTime(webs_t wp); + +/* + * The following prototypes are used by the SSL patch found in websSSL.c + */ +extern int websAlloc(int sid); +extern void websFree(webs_t wp); +extern void websTimeout(void *arg, int id); +extern void websReadEvent(webs_t wp); /* * Prototypes for functions available when running as part of the diff --git a/cpukit/httpd/websuemf.c b/cpukit/httpd/websuemf.c index 48a30f25d0..2315d9d234 100644 --- a/cpukit/httpd/websuemf.c +++ b/cpukit/httpd/websuemf.c @@ -1,7 +1,7 @@ /* * websuemf.c -- GoAhead Micro Embedded Management Framework * - * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements */ @@ -14,14 +14,32 @@ /*********************************** Includes *********************************/ +#include "ejIntrn.h" #include "wsIntrn.h" +/*********************************** Defines **********************************/ + +/* + * This structure stores scheduled events. + */ +typedef struct { + void (*routine)(void *arg, int id); + void *arg; + time_t at; + int schedid; +} sched_t; + +/*********************************** Locals ***********************************/ + +static sched_t **sched; +static int schedMax; + /************************************* Code ***********************************/ /* * Evaluate a script */ -int scriptEval(int engine, char_t* cmd, char_t** result, int chan) +int scriptEval(int engine, char_t *cmd, char_t **result, int chan) { int ejid; @@ -37,3 +55,156 @@ int scriptEval(int engine, char_t* cmd, char_t** result, int chan) } /******************************************************************************/ +/* + * Compare strings, ignoring case: normal strcmp return codes. + * + * WARNING: It is not good form to increment or decrement pointers inside a + * "call" to tolower et al. These can be MACROS, and have undesired side + * effects. + */ + +int strcmpci(char_t *s1, char_t *s2) +{ + int rc; + + a_assert(s1 && s2); + if (s1 == NULL || s2 == NULL) { + return 0; + } + + if (s1 == s2) { + return 0; + } + + do { + rc = gtolower(*s1) - gtolower(*s2); + if (*s1 == '\0') { + break; + } + s1++; + s2++; + } while (rc == 0); + return rc; +} + +/******************************************************************************/ +/* + * This function is called when a scheduled process time has come. + */ + +void TimerProc(int schedid) +{ + sched_t *s; + + a_assert(0 <= schedid && schedid < schedMax); + s = sched[schedid]; + a_assert(s); + + (s->routine)(s->arg, s->schedid); +} + +/******************************************************************************/ +/* + * Schedule an event in delay milliseconds time. We will use 1 second + * granularity for webServer. + */ + +int emfSchedCallback(int delay, emfSchedProc *proc, void *arg) +{ + sched_t *s; + int schedid; + + if ((schedid = hAllocEntry((void***) &sched, &schedMax, + sizeof(sched_t))) < 0) { + return -1; + } + s = sched[schedid]; + s->routine = proc; + s->arg = arg; + s->schedid = schedid; + +/* + * Round the delay up to seconds. + */ + s->at = ((delay + 500) / 1000) + time(0); + + return schedid; +} + +/******************************************************************************/ +/* + * Reschedule to a new delay. + */ + +void emfReschedCallback(int schedid, int delay) +{ + sched_t *s; + + if (sched == NULL || schedid == -1 || schedid >= schedMax || + (s = sched[schedid]) == NULL) { + return; + } + s->at = ((delay + 500) / 1000) + time(0); +} + +/******************************************************************************/ + +void emfUnschedCallback(int schedid) +{ + sched_t *s; + + if (sched == NULL || schedid == -1 || schedid >= schedMax || + (s = sched[schedid]) == NULL) { + return; + } + bfree(B_L, s); + schedMax = hFree((void***) &sched, schedid); +} + +/******************************************************************************/ +/* + * Take the tasks off the queue in a round robin fashion. + */ + +void emfSchedProcess() +{ + sched_t *s; + int schedid; + static int next = 0; + +/* + * If schedMax is 0, there are no tasks scheduled, so just return. + */ + if (schedMax <= 0) { + return; + } + +/* + * If next >= schedMax, the schedule queue was reduced in our absence + * so reset next to 0 to start from the begining of the queue again. + */ + if (next >= schedMax) { + next = 0; + } + + schedid = next; + for (;;) { + if ((s = sched[schedid]) != NULL && (int)s->at <= (int)time(0)) { + TimerProc(schedid); + next = schedid + 1; + return; + } + if (++schedid >= schedMax) { + schedid = 0; + } + if (schedid == next) { +/* + * We've gone all the way through the queue without finding + * anything to do so just return. + */ + return; + } + } +} + +/******************************************************************************/ diff --git a/cpukit/httpd/wsIntrn.h b/cpukit/httpd/wsIntrn.h index bd77ef8862..8f230d6fbe 100644 --- a/cpukit/httpd/wsIntrn.h +++ b/cpukit/httpd/wsIntrn.h @@ -1,18 +1,18 @@ /* - * wsIntrn.h -- Internal Go Ahead Web server header + * wsIntrn.h -- Internal GoAhead Web server header * - * Copyright (c) Go Ahead Software Inc., 1992-1999. All Rights Reserved. + * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved. * * See the file "license.txt" for information on usage and redistribution */ - + #ifndef _h_WEBS_INTERNAL #define _h_WEBS_INTERNAL 1 /******************************** Description *********************************/ /* - * Internal Go Ahead Web Server header. This defines the Web private APIs + * Internal GoAhead Web Server header. This defines the Web private APIs * Include this header when you want to create URL handlers. */ @@ -33,7 +33,6 @@ * #define WEBS_PROXY_SUPPORT 1 * * Define this to support reading pages from ROM - * Note: this is not yet fully implemented. * #define WEBS_PAGE_ROM 1 * * Define this to enable memory allocation and stack usage tracking @@ -64,6 +63,13 @@ #include #endif +#if SCOV5 + #include + #include + #include + #include +#endif + #if LYNX #include #include @@ -91,17 +97,24 @@ #include #endif -#if VXW486 +#if VXWORKS #include #include #include #endif +#if SOLARIS + #include + #include + #include +#endif + #if UEMF #include "uemf.h" - #include "ej.h" + #include "ejIntrn.h" #else #include "emf/emfInternal.h" + #include "ej/ejIntrn.h" #endif #include "webs.h" @@ -110,16 +123,18 @@ /* * Read handler flags and state */ -#define WEBS_BEGIN 0x1 /* Beginning state */ -#define WEBS_HEADER 0x2 /* Ready to read first line */ -#define WEBS_POST 0x4 /* POST without content */ -#define WEBS_POST_CLEN 0x8 /* Ready to read content for POST */ -#define WEBS_PROCESSING 0x10 /* Processing request */ -#define WEBS_KEEP_TIMEOUT 15000 /* Keep-alive timeout (15 secs) */ -#define WEBS_TIMEOUT 60000 /* General request timeout (60) */ - -#define PAGE_READ_BUFSIZE 512 /* bytes read from page files */ -#define MAX_PORT_LEN 10 /* max digits in port number */ +#define WEBS_BEGIN 0x1 /* Beginning state */ +#define WEBS_HEADER 0x2 /* Ready to read first line */ +#define WEBS_POST 0x4 /* POST without content */ +#define WEBS_POST_CLEN 0x8 /* Ready to read content for POST */ +#define WEBS_PROCESSING 0x10 /* Processing request */ +#define WEBS_KEEP_TIMEOUT 15000 /* Keep-alive timeout (15 secs) */ +#define WEBS_TIMEOUT 60000 /* General request timeout (60) */ + +#define PAGE_READ_BUFSIZE 512 /* bytes read from page files */ +#define MAX_PORT_LEN 10 /* max digits in port number */ +#define WEBS_SYM_INIT 64 /* initial # of sym table entries */ +#define WEBS_VERSION_STR T("2.1.3") /* version of web server s/w */ /* * URL handler structure. Stores the leading URL path and the handler @@ -150,6 +165,7 @@ typedef struct { long localHits; long remoteHits; long formHits; + long cgiHits; long handlerHits; } websStatsType; @@ -185,7 +201,7 @@ typedef struct { */ typedef struct { char_t *path; /* Web page URL path */ - unsigned char *page; /* Web page data */ + const unsigned char *page; /* Web page data */ int size; /* Size of web page in bytes */ int pos; /* Current read position */ } websRomPageIndexType; @@ -209,6 +225,7 @@ extern int websMax; /* List size */ extern char_t websHost[64]; /* Name of this host */ extern char_t websIpaddr[64]; /* IP address of this host */ extern char_t *websHostUrl; /* URL for this host */ +extern char_t *websIpaddrUrl; /* URL for this host */ extern int websPort; /* Port number */ /******************************** Prototypes **********************************/ @@ -224,12 +241,21 @@ extern int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *query); extern int websFormHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, char_t *query); +extern int websCgiHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, + int arg, char_t *url, char_t *path, char_t *query); +extern void websCgiCleanup(); +extern int websCheckCgiProc(int handle); +extern char_t *websGetCgiCommName(); + +extern int websLaunchCgiProc(char_t *cgiPath, char_t **argp, + char_t **envp, char_t *stdIn, char_t *stdOut); extern int websOpen(int sid); extern void websResponse(webs_t wp, int code, char_t *msg, char_t *redirect); extern int websJavaScriptEval(webs_t wp, char_t *script); extern int websPageReadData(webs_t wp, char *buf, int nBytes); -extern int websPageOpen(webs_t wp, char_t *lpath, char_t *path, int mode, int perm); +extern int websPageOpen(webs_t wp, char_t *lpath, char_t *path, int mode, + int perm); extern void websPageClose(webs_t wp); extern void websPageSeek(webs_t wp, long offset); extern int websPageStat(webs_t wp, char_t *lpath, char_t *path, @@ -253,6 +279,8 @@ extern int websOpenServer(int port, int retries); extern void websCloseServer(); extern char_t* websGetDateString(websStatType* sbuf); +extern int strcmpci(char_t* s1, char_t* s2); + /* * Prototypes for functions available when running as part of the * GoAhead Embedded Management Framework (EMF) @@ -263,6 +291,11 @@ extern void websEmfClose(); extern void websSetEmfEnvironment(webs_t wp); #endif +#if CE +extern int writeUniToAsc(int fid, void *buf, unsigned int len); +extern int readAscToUni(int fid, void **buf, unsigned int len); +#endif + #endif /* _h_WEBS_INTERNAL */ /******************************************************************************/ diff --git a/cpukit/libnetworking/ChangeLog b/cpukit/libnetworking/ChangeLog index fba8f2452f..61dfc66622 100644 --- a/cpukit/libnetworking/ChangeLog +++ b/cpukit/libnetworking/ChangeLog @@ -1,3 +1,15 @@ +2000-08-31 Joel Sherrill + + * Merged version 2.1 of GoAhead webserver. This update + was submitted by Antti P Miettinen . + * NOTES, base64.c, ejIntrn.h, emfdb.c, emfdb.h, md5.h, md5c.c, + um.c, um.h: New files. + * wbase64.c: Removed. + * Makefile.am, asp.c, balloc.c, default.c, ej.h, ejlex.c, ejparse.c, + form.c, h.c, handler.c, mime.c, misc.c, ringq.c, rom.c, security.c, + socket.c, sym.c, uemf.c, uemf.h, url.c, value.c, webcomp.c, webmain.c, + webpage.c, webrom.c, webs.c, webs.h, websuemf.c, wsIntrn.h: Modified. + 2000-08-31 Ralf Corsepius * netinet/tcp_input.c: Spelling corrections. -- cgit v1.2.3