summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>2000-09-01 10:57:21 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>2000-09-01 10:57:21 +0000
commita6b4c0df5f74d1238337f41d1d13f4f168ad01f1 (patch)
treeae171319a76a707e9f3aa638b7c690e40c9f5d5e
parent2000-08-31 Ralf Corsepius <corsepiu@faw.uni-ulm.de> (diff)
downloadrtems-a6b4c0df5f74d1238337f41d1d13f4f168ad01f1.tar.bz2
2000-08-30 Joel Sherrill <joel@OARcorp.com>
* Merged version 2.1 of GoAhead webserver. This update was submitted by Antti P Miettinen <antti.p.miettinen@nokia.com>. * 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.
-rw-r--r--c/src/exec/libnetworking/ChangeLog12
-rw-r--r--c/src/libnetworking/ChangeLog12
-rw-r--r--c/src/libnetworking/rtems_webserver/Makefile.am13
-rw-r--r--c/src/libnetworking/rtems_webserver/NOTES29
-rw-r--r--c/src/libnetworking/rtems_webserver/asp.c38
-rw-r--r--c/src/libnetworking/rtems_webserver/balloc.c382
-rw-r--r--c/src/libnetworking/rtems_webserver/base64.c (renamed from c/src/libnetworking/rtems_webserver/wbase64.c)12
-rw-r--r--c/src/libnetworking/rtems_webserver/default.c79
-rw-r--r--c/src/libnetworking/rtems_webserver/ej.h200
-rw-r--r--c/src/libnetworking/rtems_webserver/ejIntrn.h228
-rw-r--r--c/src/libnetworking/rtems_webserver/ejlex.c160
-rw-r--r--c/src/libnetworking/rtems_webserver/ejparse.c379
-rw-r--r--c/src/libnetworking/rtems_webserver/emfdb.c1050
-rw-r--r--c/src/libnetworking/rtems_webserver/emfdb.h102
-rw-r--r--c/src/libnetworking/rtems_webserver/form.c27
-rw-r--r--c/src/libnetworking/rtems_webserver/h.c34
-rw-r--r--c/src/libnetworking/rtems_webserver/handler.c109
-rw-r--r--c/src/libnetworking/rtems_webserver/md5.h48
-rw-r--r--c/src/libnetworking/rtems_webserver/md5c.c335
-rw-r--r--c/src/libnetworking/rtems_webserver/mime.c2
-rw-r--r--c/src/libnetworking/rtems_webserver/misc.c163
-rw-r--r--c/src/libnetworking/rtems_webserver/ringq.c93
-rw-r--r--c/src/libnetworking/rtems_webserver/rom.c17
-rw-r--r--c/src/libnetworking/rtems_webserver/security.c165
-rw-r--r--c/src/libnetworking/rtems_webserver/socket.c1130
-rw-r--r--c/src/libnetworking/rtems_webserver/sym.c133
-rw-r--r--c/src/libnetworking/rtems_webserver/uemf.c129
-rw-r--r--c/src/libnetworking/rtems_webserver/uemf.h831
-rw-r--r--c/src/libnetworking/rtems_webserver/um.c1415
-rw-r--r--c/src/libnetworking/rtems_webserver/um.h184
-rw-r--r--c/src/libnetworking/rtems_webserver/url.c23
-rw-r--r--c/src/libnetworking/rtems_webserver/value.c1167
-rw-r--r--c/src/libnetworking/rtems_webserver/webcomp.c25
-rw-r--r--c/src/libnetworking/rtems_webserver/webmain.c116
-rw-r--r--c/src/libnetworking/rtems_webserver/webpage.c4
-rw-r--r--c/src/libnetworking/rtems_webserver/webrom.c3
-rw-r--r--c/src/libnetworking/rtems_webserver/webs.c1397
-rw-r--r--c/src/libnetworking/rtems_webserver/webs.h85
-rw-r--r--c/src/libnetworking/rtems_webserver/websuemf.c175
-rw-r--r--c/src/libnetworking/rtems_webserver/wsIntrn.h71
-rw-r--r--cpukit/httpd/Makefile.am13
-rw-r--r--cpukit/httpd/NOTES29
-rw-r--r--cpukit/httpd/asp.c38
-rw-r--r--cpukit/httpd/balloc.c382
-rw-r--r--cpukit/httpd/base64.c (renamed from cpukit/httpd/wbase64.c)12
-rw-r--r--cpukit/httpd/default.c79
-rw-r--r--cpukit/httpd/ej.h200
-rw-r--r--cpukit/httpd/ejIntrn.h228
-rw-r--r--cpukit/httpd/ejlex.c160
-rw-r--r--cpukit/httpd/ejparse.c379
-rw-r--r--cpukit/httpd/emfdb.c1050
-rw-r--r--cpukit/httpd/emfdb.h102
-rw-r--r--cpukit/httpd/form.c27
-rw-r--r--cpukit/httpd/h.c34
-rw-r--r--cpukit/httpd/handler.c109
-rw-r--r--cpukit/httpd/md5.h48
-rw-r--r--cpukit/httpd/md5c.c335
-rw-r--r--cpukit/httpd/mime.c2
-rw-r--r--cpukit/httpd/misc.c163
-rw-r--r--cpukit/httpd/ringq.c93
-rw-r--r--cpukit/httpd/rom.c17
-rw-r--r--cpukit/httpd/security.c165
-rw-r--r--cpukit/httpd/socket.c1130
-rw-r--r--cpukit/httpd/sym.c133
-rw-r--r--cpukit/httpd/uemf.c129
-rw-r--r--cpukit/httpd/uemf.h831
-rw-r--r--cpukit/httpd/um.c1415
-rw-r--r--cpukit/httpd/um.h184
-rw-r--r--cpukit/httpd/url.c23
-rw-r--r--cpukit/httpd/value.c1167
-rw-r--r--cpukit/httpd/webcomp.c25
-rw-r--r--cpukit/httpd/webmain.c116
-rw-r--r--cpukit/httpd/webpage.c4
-rw-r--r--cpukit/httpd/webrom.c3
-rw-r--r--cpukit/httpd/webs.c1397
-rw-r--r--cpukit/httpd/webs.h85
-rw-r--r--cpukit/httpd/websuemf.c175
-rw-r--r--cpukit/httpd/wsIntrn.h71
-rw-r--r--cpukit/libnetworking/ChangeLog12
79 files changed, 17448 insertions, 3694 deletions
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 <joel@OARcorp.com>
+
+ * Merged version 2.1 of GoAhead webserver. This update
+ was submitted by Antti P Miettinen <antti.p.miettinen@nokia.com>.
+ * 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 <corsepiu@faw.uni-ulm.de>
* 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 <joel@OARcorp.com>
+
+ * Merged version 2.1 of GoAhead webserver. This update
+ was submitted by Antti P Miettinen <antti.p.miettinen@nokia.com>.
+ * 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 <corsepiu@faw.uni-ulm.de>
* 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 <antti.p.miettinen@nokia.com>.
+
+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) {
/*
@@ -222,10 +244,12 @@ void *balloc(B_ARGS_DEC, int size)
#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--;
}
@@ -565,48 +672,29 @@ static int bStatsFileSort(const void *cp1, const void *cp2)
/******************************************************************************/
/*
- * 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
*/
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/wbase64.c b/c/src/libnetworking/rtems_webserver/base64.c
index 0898b16728..b1eb6b01c5 100644
--- a/c/src/libnetworking/rtems_webserver/wbase64.c
+++ b/c/src/libnetworking/rtems_webserver/base64.c
@@ -1,7 +1,7 @@
/*
* base64.c -- Base64 Mime encoding
*
- * 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
*/
@@ -60,11 +60,10 @@ static char_t alphabet64[] = {
* Decode a buffer from "string" and into "outbuf"
*/
-int websDecode64(char_t* outbuf, char_t* string, int outlen)
+int websDecode64(char_t *outbuf, char_t *string, int outlen)
{
unsigned long shiftbuf;
- char_t* cp;
- char_t* op;
+ char_t *cp, *op;
int c, i, j, shift;
op = outbuf;
@@ -109,11 +108,10 @@ int websDecode64(char_t* outbuf, char_t* string, int outlen)
* Encode a buffer from "string" into "outbuf"
*/
-void websEncode64(char_t* outbuf, char_t* string, int outlen)
+void websEncode64(char_t *outbuf, char_t *string, int outlen)
{
unsigned long shiftbuf;
- char_t* cp;
- char_t* op;
+ char_t *cp, *op;
int x, i, j, shift;
op = outbuf;
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 <b>%s</b><br>for URL <b>%s</b>"),
- lpath, url);
+ T("Cannot open URL <b>%s</b>"), url);
return 1;
}
if (websPageStat(wp, lpath, path, &sbuf) < 0) {
- websError(wp, 400, T("Can't stat page <b>%s</b><br>for URL <b>%s</b>"),
- lpath, url);
+ websError(wp, 400, T("Cannot stat page for URL <b>%s</b>"),
+ 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;
@@ -165,6 +166,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
*/
if (flags & WEBS_ASP) {
@@ -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 <ctype.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#ifndef CE
- #include <fcntl.h>
-#endif
-
-#if LYNX
- #include <unistd.h>
-#endif
-
-#ifdef QNX4
- #include <dirent.h>
-#endif
-
-#if UEMF
- #include "uemf.h"
-#else
- #include <param.h>
- #include <stat.h>
- #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 <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#if CE
+#if ! UEMF
+ #include <io.h>
+#endif
+#endif
+
+#if LYNX
+ #include <unistd.h>
+#endif
+
+#ifdef QNX4
+ #include <dirent.h>
+#endif
+
+#if UEMF
+ #include "uemf.h"
+#else
+ #include <param.h>
+ #include <stat.h>
+ #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) {
@@ -1346,6 +1448,21 @@ int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name,
/******************************************************************************/
/*
+ * 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;
@@ -227,6 +232,19 @@ int ringqPutstr(ringq_t *rq, char_t *str)
}
/******************************************************************************/
+/*
+ * 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
/*
* Get a byte from the queue
@@ -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 <stdlib.h>
-#if CE
-#define EINVAL 22
-#define EBADF 9
-#else
-#include <errno.h>
-#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("<html><head>Access Denied</head><body>\r\n\
- Access to this document requires a password.</body>\
- </html>\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 <errno.h>
-#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
-#include <unistd.h>
-#if __rtems__
-#include <sys/select.h>
+#if UEMF
+ #include "uemf.h"
+#else
+ #include <socket.h>
+ #include <types.h>
+ #include <unistd.h>
+ #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 <hostLib.h>
+#endif
+
+#if __rtems__
+ #include <sys/select.h>
+#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,99 +137,221 @@ 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
*/
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,32 +137,20 @@ 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
* as the first step in traversing the table. A call to symFirst should be
* followed by calls to symNext to get all the rest of the entries.
@@ -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 <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
-#endif
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* WIN */
#if CE
#include <limits.h>
@@ -37,15 +39,26 @@
#include <windows.h>
#include <winnls.h>
#include "CE/wincompat.h"
-#endif
+ #include <winsock.h>
+#endif /* CE */
#if NW
#include <stdio.h>
-#endif
+#endif /* NW */
+
+#if SCOV5
+ #include <sys/types.h>
+ #include <stdio.h>
+ #include "sys/socket.h"
+ #include "sys/select.h"
+ #include "netinet/in.h"
+ #include "arpa/inet.h"
+ #include "netdb.h"
+#endif /* SCOV5 */
#if UNIX
#include <stdio.h>
-#endif
+#endif /* UNIX */
#if LINUX || __rtems__
#include <sys/types.h>
@@ -61,7 +74,9 @@
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
-#endif
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* LINUX */
#if LYNX
#include <limits.h>
@@ -74,13 +89,15 @@
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
-#endif
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* LYNX */
#if UW
#include <stdio.h>
-#endif
+#endif /* UW */
-#if VXW486
+#if VXWORKS
#include <vxWorks.h>
#include <sockLib.h>
#include <selectLib.h>
@@ -90,7 +107,25 @@
#include <stat.h>
#include <time.h>
#include <usrLib.h>
-#endif
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* VXWORKS */
+
+#if SOLARIS
+ #include <sys/types.h>
+ #include <limits.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <socket.h>
+ #include <sys/select.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <time.h>
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* SOLARIS */
#if QNX4
#include <sys/types.h>
@@ -100,7 +135,20 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
-#endif
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <sys/uio.h>
+ #include <sys/wait.h>
+#endif /* QNX4 */
+
+#if ECOS
+ #include <limits.h>
+ #include <cyg/infra/cyg_type.h>
+ #include <cyg/kernel/kapi.h>
+ #include <time.h>
+ #include <network.h>
+ #include <errno.h>
+#endif /* ECOS */
/********************************** Includes **********************************/
@@ -108,6 +156,69 @@
#include <stdarg.h>
#include <string.h>
+#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
@@ -460,14 +702,67 @@ 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/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 <rtems/error.h>
+#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,27 +847,156 @@ 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
*/
} else if (gstrcmp(key, T("content-length")) == 0) {
@@ -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)
@@ -958,16 +1205,32 @@ char_t *websGetVar(webs_t wp, char_t *var, char_t *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. <CR><LF> 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("<html><head></head><body>\r\n\
This document has moved to a new <a href=\"%s\">location</a>.\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("<html><head><title>Document Error: %s</title></head>\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);
}
}
@@ -1350,11 +1691,24 @@ 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));
@@ -1774,33 +2180,13 @@ int websValid(webs_t wp)
/******************************************************************************/
/*
- * 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);
}
@@ -1839,3 +2225,614 @@ static int websGetTimeSinceMark(webs_t wp)
}
/******************************************************************************/
+/*
+ * 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 <math.h>
+
+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 <sys/stat.h>
#endif
+#if SCOV5
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <signal.h>
+ #include <unistd.h>
+#endif
+
#if LYNX
#include <fcntl.h>
#include <sys/stat.h>
@@ -91,17 +97,24 @@
#include <sys/stat.h>
#endif
-#if VXW486
+#if VXWORKS
#include <vxWorks.h>
#include <fcntl.h>
#include <sys/stat.h>
#endif
+#if SOLARIS
+ #include <macros.h>
+ #include <fcntl.h>
+ #include <sys/stat.h>
+#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 <antti.p.miettinen@nokia.com>.
+
+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) {
/*
@@ -222,10 +244,12 @@ void *balloc(B_ARGS_DEC, int size)
#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--;
}
@@ -565,48 +672,29 @@ static int bStatsFileSort(const void *cp1, const void *cp2)
/******************************************************************************/
/*
- * 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
*/
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/wbase64.c b/cpukit/httpd/base64.c
index 0898b16728..b1eb6b01c5 100644
--- a/cpukit/httpd/wbase64.c
+++ b/cpukit/httpd/base64.c
@@ -1,7 +1,7 @@
/*
* base64.c -- Base64 Mime encoding
*
- * 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
*/
@@ -60,11 +60,10 @@ static char_t alphabet64[] = {
* Decode a buffer from "string" and into "outbuf"
*/
-int websDecode64(char_t* outbuf, char_t* string, int outlen)
+int websDecode64(char_t *outbuf, char_t *string, int outlen)
{
unsigned long shiftbuf;
- char_t* cp;
- char_t* op;
+ char_t *cp, *op;
int c, i, j, shift;
op = outbuf;
@@ -109,11 +108,10 @@ int websDecode64(char_t* outbuf, char_t* string, int outlen)
* Encode a buffer from "string" into "outbuf"
*/
-void websEncode64(char_t* outbuf, char_t* string, int outlen)
+void websEncode64(char_t *outbuf, char_t *string, int outlen)
{
unsigned long shiftbuf;
- char_t* cp;
- char_t* op;
+ char_t *cp, *op;
int x, i, j, shift;
op = outbuf;
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 <b>%s</b><br>for URL <b>%s</b>"),
- lpath, url);
+ T("Cannot open URL <b>%s</b>"), url);
return 1;
}
if (websPageStat(wp, lpath, path, &sbuf) < 0) {
- websError(wp, 400, T("Can't stat page <b>%s</b><br>for URL <b>%s</b>"),
- lpath, url);
+ websError(wp, 400, T("Cannot stat page for URL <b>%s</b>"),
+ 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;
@@ -165,6 +166,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
*/
if (flags & WEBS_ASP) {
@@ -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 <ctype.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#ifndef CE
- #include <fcntl.h>
-#endif
-
-#if LYNX
- #include <unistd.h>
-#endif
-
-#ifdef QNX4
- #include <dirent.h>
-#endif
-
-#if UEMF
- #include "uemf.h"
-#else
- #include <param.h>
- #include <stat.h>
- #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 <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#if CE
+#if ! UEMF
+ #include <io.h>
+#endif
+#endif
+
+#if LYNX
+ #include <unistd.h>
+#endif
+
+#ifdef QNX4
+ #include <dirent.h>
+#endif
+
+#if UEMF
+ #include "uemf.h"
+#else
+ #include <param.h>
+ #include <stat.h>
+ #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) {
@@ -1346,6 +1448,21 @@ int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name,
/******************************************************************************/
/*
+ * 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;
@@ -227,6 +232,19 @@ int ringqPutstr(ringq_t *rq, char_t *str)
}
/******************************************************************************/
+/*
+ * 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
/*
* Get a byte from the queue
@@ -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 <stdlib.h>
-#if CE
-#define EINVAL 22
-#define EBADF 9
-#else
-#include <errno.h>
-#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("<html><head>Access Denied</head><body>\r\n\
- Access to this document requires a password.</body>\
- </html>\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 <errno.h>
-#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
-#include <unistd.h>
-#if __rtems__
-#include <sys/select.h>
+#if UEMF
+ #include "uemf.h"
+#else
+ #include <socket.h>
+ #include <types.h>
+ #include <unistd.h>
+ #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 <hostLib.h>
+#endif
+
+#if __rtems__
+ #include <sys/select.h>
+#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,99 +137,221 @@ 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
*/
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,32 +137,20 @@ 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
* as the first step in traversing the table. A call to symFirst should be
* followed by calls to symNext to get all the rest of the entries.
@@ -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 <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
-#endif
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* WIN */
#if CE
#include <limits.h>
@@ -37,15 +39,26 @@
#include <windows.h>
#include <winnls.h>
#include "CE/wincompat.h"
-#endif
+ #include <winsock.h>
+#endif /* CE */
#if NW
#include <stdio.h>
-#endif
+#endif /* NW */
+
+#if SCOV5
+ #include <sys/types.h>
+ #include <stdio.h>
+ #include "sys/socket.h"
+ #include "sys/select.h"
+ #include "netinet/in.h"
+ #include "arpa/inet.h"
+ #include "netdb.h"
+#endif /* SCOV5 */
#if UNIX
#include <stdio.h>
-#endif
+#endif /* UNIX */
#if LINUX || __rtems__
#include <sys/types.h>
@@ -61,7 +74,9 @@
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
-#endif
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* LINUX */
#if LYNX
#include <limits.h>
@@ -74,13 +89,15 @@
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
-#endif
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* LYNX */
#if UW
#include <stdio.h>
-#endif
+#endif /* UW */
-#if VXW486
+#if VXWORKS
#include <vxWorks.h>
#include <sockLib.h>
#include <selectLib.h>
@@ -90,7 +107,25 @@
#include <stat.h>
#include <time.h>
#include <usrLib.h>
-#endif
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* VXWORKS */
+
+#if SOLARIS
+ #include <sys/types.h>
+ #include <limits.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <socket.h>
+ #include <sys/select.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <time.h>
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* SOLARIS */
#if QNX4
#include <sys/types.h>
@@ -100,7 +135,20 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
-#endif
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <sys/uio.h>
+ #include <sys/wait.h>
+#endif /* QNX4 */
+
+#if ECOS
+ #include <limits.h>
+ #include <cyg/infra/cyg_type.h>
+ #include <cyg/kernel/kapi.h>
+ #include <time.h>
+ #include <network.h>
+ #include <errno.h>
+#endif /* ECOS */
/********************************** Includes **********************************/
@@ -108,6 +156,69 @@
#include <stdarg.h>
#include <string.h>
+#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
@@ -460,14 +702,67 @@ 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/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 <rtems/error.h>
+#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,27 +847,156 @@ 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
*/
} else if (gstrcmp(key, T("content-length")) == 0) {
@@ -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)
@@ -958,16 +1205,32 @@ char_t *websGetVar(webs_t wp, char_t *var, char_t *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. <CR><LF> 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("<html><head></head><body>\r\n\
This document has moved to a new <a href=\"%s\">location</a>.\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("<html><head><title>Document Error: %s</title></head>\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);
}
}
@@ -1350,11 +1691,24 @@ 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));
@@ -1774,33 +2180,13 @@ int websValid(webs_t wp)
/******************************************************************************/
/*
- * 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);
}
@@ -1839,3 +2225,614 @@ static int websGetTimeSinceMark(webs_t wp)
}
/******************************************************************************/
+/*
+ * 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 <math.h>
+
+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 <sys/stat.h>
#endif
+#if SCOV5
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <signal.h>
+ #include <unistd.h>
+#endif
+
#if LYNX
#include <fcntl.h>
#include <sys/stat.h>
@@ -91,17 +97,24 @@
#include <sys/stat.h>
#endif
-#if VXW486
+#if VXWORKS
#include <vxWorks.h>
#include <fcntl.h>
#include <sys/stat.h>
#endif
+#if SOLARIS
+ #include <macros.h>
+ #include <fcntl.h>
+ #include <sys/stat.h>
+#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 <joel@OARcorp.com>
+
+ * Merged version 2.1 of GoAhead webserver. This update
+ was submitted by Antti P Miettinen <antti.p.miettinen@nokia.com>.
+ * 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 <corsepiu@faw.uni-ulm.de>
* netinet/tcp_input.c: Spelling corrections.