From faafae20c138004d15b07a5ab2523edf288c79b7 Mon Sep 17 00:00:00 2001 From: gardasevic Date: Thu, 8 Oct 2009 16:12:35 +0000 Subject: [cleo][spidgoahead] Goahead web server integration(clear version). git-svn-id: svn+ssh://pessac/svn/cesar/trunk@6037 017c9cb6-072f-447c-8318-d5b54f68fe89 --- cleopatre/application/spidgoahead/LINUX/Makefile | 155 + cleopatre/application/spidgoahead/LINUX/main.c | 428 +++ cleopatre/application/spidgoahead/Makefile | 27 + cleopatre/application/spidgoahead/asp.c | 322 ++ cleopatre/application/spidgoahead/balloc.c | 974 +++++++ cleopatre/application/spidgoahead/base64.c | 150 + cleopatre/application/spidgoahead/cgi.c | 332 +++ cleopatre/application/spidgoahead/default.c | 603 ++++ cleopatre/application/spidgoahead/default.css | 188 ++ cleopatre/application/spidgoahead/ej.h | 47 + cleopatre/application/spidgoahead/ejIntrn.h | 231 ++ cleopatre/application/spidgoahead/ejlex.c | 722 +++++ cleopatre/application/spidgoahead/ejparse.c | 1805 ++++++++++++ cleopatre/application/spidgoahead/emfdb.c | 1064 +++++++ cleopatre/application/spidgoahead/emfdb.h | 111 + cleopatre/application/spidgoahead/form.c | 169 ++ cleopatre/application/spidgoahead/h.c | 196 ++ cleopatre/application/spidgoahead/handler.c | 417 +++ cleopatre/application/spidgoahead/license.txt | 282 ++ cleopatre/application/spidgoahead/md5.h | 50 + cleopatre/application/spidgoahead/md5c.c | 338 +++ cleopatre/application/spidgoahead/mime.c | 119 + cleopatre/application/spidgoahead/misc.c | 690 +++++ cleopatre/application/spidgoahead/mocana_ssl.c | 539 ++++ cleopatre/application/spidgoahead/page.c | 143 + cleopatre/application/spidgoahead/readme.txt | 3 + cleopatre/application/spidgoahead/release.htm | 637 ++++ cleopatre/application/spidgoahead/release.txt | 625 ++++ cleopatre/application/spidgoahead/ringq.c | 595 ++++ cleopatre/application/spidgoahead/rom.c | 194 ++ cleopatre/application/spidgoahead/security.c | 240 ++ cleopatre/application/spidgoahead/sock.c | 792 +++++ cleopatre/application/spidgoahead/sockGen.c | 1046 +++++++ cleopatre/application/spidgoahead/sym.c | 476 +++ cleopatre/application/spidgoahead/uemf.c | 294 ++ cleopatre/application/spidgoahead/uemf.h | 1094 +++++++ cleopatre/application/spidgoahead/um.c | 1436 +++++++++ cleopatre/application/spidgoahead/um.h | 185 ++ cleopatre/application/spidgoahead/umui.c | 642 ++++ cleopatre/application/spidgoahead/url.c | 223 ++ cleopatre/application/spidgoahead/value.c | 1214 ++++++++ cleopatre/application/spidgoahead/web/FAQ.htm | 181 ++ .../spidgoahead/web/Support/support.htm | 31 + cleopatre/application/spidgoahead/web/about.htm | 68 + cleopatre/application/spidgoahead/web/addgroup.asp | 43 + cleopatre/application/spidgoahead/web/addlimit.asp | 37 + cleopatre/application/spidgoahead/web/adduser.asp | 51 + cleopatre/application/spidgoahead/web/asp.asp | 30 + cleopatre/application/spidgoahead/web/bogus.asp | 13 + .../application/spidgoahead/web/cgi-bin/cgitest.c | 328 +++ .../application/spidgoahead/web/classes/Status.jar | Bin 0 -> 21423 bytes .../application/spidgoahead/web/classes/doctor.jar | Bin 0 -> 21939 bytes .../application/spidgoahead/web/classes/tabs.jar | Bin 0 -> 3241 bytes .../spidgoahead/web/classes/treeapp.jar | Bin 0 -> 16307 bytes cleopatre/application/spidgoahead/web/contents.asp | 65 + cleopatre/application/spidgoahead/web/delgroup.asp | 28 + cleopatre/application/spidgoahead/web/dellimit.asp | 28 + cleopatre/application/spidgoahead/web/deluser.asp | 28 + cleopatre/application/spidgoahead/web/docs.htm | 22 + cleopatre/application/spidgoahead/web/dspuser.asp | 28 + cleopatre/application/spidgoahead/web/forms.asp | 30 + .../spidgoahead/web/glossary/wsglossary.htm | 240 ++ .../spidgoahead/web/graphics/sidebar.gif | Bin 0 -> 7907 bytes .../spidgoahead/web/graphics/topbar.gif | Bin 0 -> 19501 bytes .../spidgoahead/web/graphics/webserver_logo1.gif | Bin 0 -> 1661 bytes .../spidgoahead/web/graphics/webserver_logo2.gif | Bin 0 -> 797 bytes cleopatre/application/spidgoahead/web/home.asp | 24 + cleopatre/application/spidgoahead/web/loadcfg.asp | 24 + cleopatre/application/spidgoahead/web/overview.htm | 26 + cleopatre/application/spidgoahead/web/savecfg.asp | 24 + cleopatre/application/spidgoahead/web/small.htm | 18 + .../application/spidgoahead/web/style/doc.css | 42 + .../application/spidgoahead/web/style/help.htm | 40 + .../application/spidgoahead/web/style/menu.htm | 55 + .../spidgoahead/web/style/normal_ws.css | 37 + .../application/spidgoahead/web/style/option.htm | 28 + cleopatre/application/spidgoahead/web/tests.htm | 13 + cleopatre/application/spidgoahead/web/title.htm | 18 + cleopatre/application/spidgoahead/web/treeapp.asp | 29 + cleopatre/application/spidgoahead/web/um.htm | 10 + cleopatre/application/spidgoahead/webcomp.c | 189 ++ cleopatre/application/spidgoahead/webrom.c | 16 + cleopatre/application/spidgoahead/webs.c | 3078 ++++++++++++++++++++ cleopatre/application/spidgoahead/webs.h | 234 ++ cleopatre/application/spidgoahead/websSSL.c | 709 +++++ cleopatre/application/spidgoahead/websSSL.h | 66 + cleopatre/application/spidgoahead/websda.c | 245 ++ cleopatre/application/spidgoahead/websda.h | 42 + cleopatre/application/spidgoahead/websuemf.c | 218 ++ cleopatre/application/spidgoahead/wsIntrn.h | 310 ++ 90 files changed, 26544 insertions(+) create mode 100644 cleopatre/application/spidgoahead/LINUX/Makefile create mode 100644 cleopatre/application/spidgoahead/LINUX/main.c create mode 100644 cleopatre/application/spidgoahead/Makefile create mode 100644 cleopatre/application/spidgoahead/asp.c create mode 100644 cleopatre/application/spidgoahead/balloc.c create mode 100644 cleopatre/application/spidgoahead/base64.c create mode 100644 cleopatre/application/spidgoahead/cgi.c create mode 100644 cleopatre/application/spidgoahead/default.c create mode 100644 cleopatre/application/spidgoahead/default.css create mode 100644 cleopatre/application/spidgoahead/ej.h create mode 100644 cleopatre/application/spidgoahead/ejIntrn.h create mode 100644 cleopatre/application/spidgoahead/ejlex.c create mode 100644 cleopatre/application/spidgoahead/ejparse.c create mode 100644 cleopatre/application/spidgoahead/emfdb.c create mode 100644 cleopatre/application/spidgoahead/emfdb.h create mode 100644 cleopatre/application/spidgoahead/form.c create mode 100644 cleopatre/application/spidgoahead/h.c create mode 100644 cleopatre/application/spidgoahead/handler.c create mode 100644 cleopatre/application/spidgoahead/license.txt create mode 100644 cleopatre/application/spidgoahead/md5.h create mode 100644 cleopatre/application/spidgoahead/md5c.c create mode 100644 cleopatre/application/spidgoahead/mime.c create mode 100644 cleopatre/application/spidgoahead/misc.c create mode 100644 cleopatre/application/spidgoahead/mocana_ssl.c create mode 100644 cleopatre/application/spidgoahead/page.c create mode 100644 cleopatre/application/spidgoahead/readme.txt create mode 100644 cleopatre/application/spidgoahead/release.htm create mode 100644 cleopatre/application/spidgoahead/release.txt create mode 100644 cleopatre/application/spidgoahead/ringq.c create mode 100644 cleopatre/application/spidgoahead/rom.c create mode 100644 cleopatre/application/spidgoahead/security.c create mode 100644 cleopatre/application/spidgoahead/sock.c create mode 100644 cleopatre/application/spidgoahead/sockGen.c create mode 100644 cleopatre/application/spidgoahead/sym.c create mode 100644 cleopatre/application/spidgoahead/uemf.c create mode 100644 cleopatre/application/spidgoahead/uemf.h create mode 100644 cleopatre/application/spidgoahead/um.c create mode 100644 cleopatre/application/spidgoahead/um.h create mode 100644 cleopatre/application/spidgoahead/umui.c create mode 100644 cleopatre/application/spidgoahead/url.c create mode 100644 cleopatre/application/spidgoahead/value.c create mode 100644 cleopatre/application/spidgoahead/web/FAQ.htm create mode 100644 cleopatre/application/spidgoahead/web/Support/support.htm create mode 100644 cleopatre/application/spidgoahead/web/about.htm create mode 100644 cleopatre/application/spidgoahead/web/addgroup.asp create mode 100644 cleopatre/application/spidgoahead/web/addlimit.asp create mode 100644 cleopatre/application/spidgoahead/web/adduser.asp create mode 100644 cleopatre/application/spidgoahead/web/asp.asp create mode 100644 cleopatre/application/spidgoahead/web/bogus.asp create mode 100644 cleopatre/application/spidgoahead/web/cgi-bin/cgitest.c create mode 100644 cleopatre/application/spidgoahead/web/classes/Status.jar create mode 100644 cleopatre/application/spidgoahead/web/classes/doctor.jar create mode 100644 cleopatre/application/spidgoahead/web/classes/tabs.jar create mode 100644 cleopatre/application/spidgoahead/web/classes/treeapp.jar create mode 100644 cleopatre/application/spidgoahead/web/contents.asp create mode 100644 cleopatre/application/spidgoahead/web/delgroup.asp create mode 100644 cleopatre/application/spidgoahead/web/dellimit.asp create mode 100644 cleopatre/application/spidgoahead/web/deluser.asp create mode 100644 cleopatre/application/spidgoahead/web/docs.htm create mode 100644 cleopatre/application/spidgoahead/web/dspuser.asp create mode 100644 cleopatre/application/spidgoahead/web/forms.asp create mode 100644 cleopatre/application/spidgoahead/web/glossary/wsglossary.htm create mode 100644 cleopatre/application/spidgoahead/web/graphics/sidebar.gif create mode 100644 cleopatre/application/spidgoahead/web/graphics/topbar.gif create mode 100644 cleopatre/application/spidgoahead/web/graphics/webserver_logo1.gif create mode 100644 cleopatre/application/spidgoahead/web/graphics/webserver_logo2.gif create mode 100644 cleopatre/application/spidgoahead/web/home.asp create mode 100644 cleopatre/application/spidgoahead/web/loadcfg.asp create mode 100644 cleopatre/application/spidgoahead/web/overview.htm create mode 100644 cleopatre/application/spidgoahead/web/savecfg.asp create mode 100644 cleopatre/application/spidgoahead/web/small.htm create mode 100644 cleopatre/application/spidgoahead/web/style/doc.css create mode 100644 cleopatre/application/spidgoahead/web/style/help.htm create mode 100644 cleopatre/application/spidgoahead/web/style/menu.htm create mode 100644 cleopatre/application/spidgoahead/web/style/normal_ws.css create mode 100644 cleopatre/application/spidgoahead/web/style/option.htm create mode 100644 cleopatre/application/spidgoahead/web/tests.htm create mode 100644 cleopatre/application/spidgoahead/web/title.htm create mode 100644 cleopatre/application/spidgoahead/web/treeapp.asp create mode 100644 cleopatre/application/spidgoahead/web/um.htm create mode 100644 cleopatre/application/spidgoahead/webcomp.c create mode 100644 cleopatre/application/spidgoahead/webrom.c create mode 100644 cleopatre/application/spidgoahead/webs.c create mode 100644 cleopatre/application/spidgoahead/webs.h create mode 100644 cleopatre/application/spidgoahead/websSSL.c create mode 100644 cleopatre/application/spidgoahead/websSSL.h create mode 100644 cleopatre/application/spidgoahead/websda.c create mode 100644 cleopatre/application/spidgoahead/websda.h create mode 100644 cleopatre/application/spidgoahead/websuemf.c create mode 100644 cleopatre/application/spidgoahead/wsIntrn.h diff --git a/cleopatre/application/spidgoahead/LINUX/Makefile b/cleopatre/application/spidgoahead/LINUX/Makefile new file mode 100644 index 0000000000..3bcfc47ad1 --- /dev/null +++ b/cleopatre/application/spidgoahead/LINUX/Makefile @@ -0,0 +1,155 @@ +# +# Makefile for the GoAhead web server reference source base +# for the Linux OS +# +# Copyright (c) GoAhead Software, Inc. 1995-2000 +# +# $Id: Makefile,v 1.2 2002/11/01 16:20:19 mmitchell Exp $ +# + +all: compile + +ARCH = libwebs.a +NAME = webs + +# User Management switch +UMSW = -DUSER_MANAGEMENT_SUPPORT + +# Digest Access switch +DASW = -DDIGEST_ACCESS_SUPPORT + +# +# SSL switches +# +# You must uncomment this line to enable SSL +# SSLPATCHFILE = ../websSSL.o +# +# Then uncomment these three lines if you are using the RSAREF library +# and set them to the correct path. +# +# SSLINC = /RSASSL/library/include +# SSLLIB = /RSASSL/library/lib/libsslc.a +# SSLSW = -DWEBS_SSL_SUPPORT -I$(SSLINC) +# +# Or alternatively, +# +# Uncomment these three lines if you are using the OpenSSL library. +# Change the value of SSLINC to the path where your OpenSSL header +# files reside, and change the value of SSLLIB to the path to your +# OpenSSL libssl.a and libcrypto.a. +# +# SSLINC = /usr/include/openssl +# SSLLIB = /usr/lib/libssl.a /usr/lib/libcrypto.a +# SSLSW = -DWEBS_SSL_SUPPORT -I$(SSLINC) -DOPENSSL + + +# If-Modified-Support switches (requires math library, libm.a) +# IFMODSW = -DWEBS_IF_MODIFIED_SUPPORT +# IFMODLIB = /usr/lib/libm.a + +# Dependencies +DEPEND_FILES = ../asp.o ../balloc.o ../base64.o ../cgi.o ../default.o \ + ../ejlex.o ../ejparse.o ../form.o \ + ../h.o ../handler.o ../mime.o ../misc.o ../page.o \ + ../ringq.o ../rom.o \ + ../sock.o ../sockGen.o $(SSLPATCHFILE) \ + ../security.o ../sym.o ../uemf.o ../url.o ../value.o \ + ../md5c.o ../um.o ../umui.o ../websda.o ../emfdb.o \ + ../webrom.o ../webs.o ../websuemf.o main.o +CFLAGS = -DWEBS -DUEMF -DOS="LINUX" -DLINUX $(UMSW) $(DASW) $(SSLSW) $(IFMODSW) +OTHERS = -DB_STATS -DB_FILL -DDEBUG +DEBUG = -g -Wall +IFLAGS = -I.. +LDFLAGS = $(SSLLIB) $(IFMODLIB) + +compile: $(NAME) + +# +# Build archive of objects +# +$(ARCH): $(DEPEND_FILES) + $(AR) $(ARFLAGS) $(ARCH) $? + +# +# Primary link +# +$(NAME): Makefile $(ARCH) + $(CC) -o $(NAME) $(DEBUG) \ + $(CFLAGS) $(IFLAGS) \ + main.o $(ARCH) $(LDFLAGS) + +clean: + rm -f $(NAME) $(ARCH) $(DEPEND_FILES) + +# +# Dependencies +# +../asp.o: ../webs.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +../balloc.o: ../balloc.c ../uemf.h + +../base64.o: ../base64.c ../webs.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +../cgi.o: ../webs.h ../wsIntrn.h ../uemf.h + +../default.o: ../default.c ../webs.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +../ejlex.o: ../ejlex.c ../ej.h ../ejIntrn.h ../uemf.h + +../ejparse.o: ../ejparse.c ../ej.h ../ejIntrn.h ../uemf.h + +../emfdb.o: ../emfdb.h ../wsIntrn.h ../uemf.h + +../form.o: ../form.c ../webs.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +../h.o: ../h.c ../uemf.h + +../handler.o: ../handler.c ../webs.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +../md5c.o: ../md5.h ../wsIntrn.h ../uemf.h + +../mime.o: ../mime.c ../webs.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +../misc.o: ../misc.c ../uemf.h + +../page.o: ../page.c ../webs.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +../ringq.o: ../ringq.c ../uemf.h + +../rom.o: ../rom.c ../webs.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +../security.o: ../security.c ../webs.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +../sock.o: ../sock.c ../uemf.h + +../sockGen.o: ../sockGen.c ../uemf.h + +../sym.o: ../sym.c ../uemf.h + +../uemf.o: ../uemf.c ../uemf.h + +../um.o: ../webs.h ../wsIntrn.h ../um.h ../uemf.h + +../umui.o: ../webs.h ../wsIntrn.h ../um.h ../uemf.h + +../url.o: ../url.c ../webs.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +../value.o: ../value.c ../uemf.h + +../webrom.o: ../webrom.c ../webs.h ../wsIntrn.h ../uemf.h + +../webs.o: ../webs.c ../webs.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +../websda.o: ../webs.h ../wsIntrn.h ../websda.h ../uemf.h + +../websuemf.o: ../websuemf.c ../webs.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +../websSSL.o: ../websSSL.c ../websSSL.h ../wsIntrn.h ../ej.h ../ejIntrn.h ../uemf.h + +main.o: main.c ../wsIntrn.h ../webs.h ../ej.h ../ejIntrn.h ../uemf.h + +# +# Transition rules (add -o to put object in right directory) +# +.c.o: + cc -c -o $@ $(DEBUG) $(CFLAGS) $(IFLAGS) $< diff --git a/cleopatre/application/spidgoahead/LINUX/main.c b/cleopatre/application/spidgoahead/LINUX/main.c new file mode 100644 index 0000000000..2af72c3337 --- /dev/null +++ b/cleopatre/application/spidgoahead/LINUX/main.c @@ -0,0 +1,428 @@ +/* + * main.c -- Main program for the GoAhead WebServer (LINUX version) + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: main.c,v 1.5 2003/09/11 14:03:46 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * Main program for for the GoAhead WebServer. This is a demonstration + * main program to initialize and configure the web server. + */ + +/********************************* Includes ***********************************/ + +#include "../uemf.h" +#include "../wsIntrn.h" +#include +#include +#include +#include + +#ifdef WEBS_SSL_SUPPORT +#include "../websSSL.h" +#endif + +#ifdef USER_MANAGEMENT_SUPPORT +#include "../um.h" +void formDefineUserMgmt(void); +#endif + + +/*********************************** Locals ***********************************/ +/* + * Change configuration here + */ + +static char_t *rootWeb = T("web"); /* Root web directory */ +static char_t *password = T(""); /* Security password */ +static int port = 80; /* Server port */ +static int retries = 5; /* Server port retries */ +static int finished; /* Finished flag */ + +/****************************** Forward Declarations **************************/ + +static int initWebs(); +static int aspTest(int eid, webs_t wp, int argc, char_t **argv); +static void formTest(webs_t wp, char_t *path, char_t *query); +static int websHomePageHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, + int arg, char_t *url, char_t *path, char_t *query); +extern void defaultErrorHandler(int etype, char_t *msg); +extern void defaultTraceHandler(int level, char_t *buf); +#ifdef B_STATS +static void printMemStats(int handle, char_t *fmt, ...); +static void memLeaks(); +#endif + +/*********************************** Code *************************************/ +/* + * Main -- entry point from LINUX + */ + +int main(int argc, char** argv) +{ +/* + * Initialize the memory allocator. Allow use of malloc and start + * with a 60K heap. For each page request approx 8KB is allocated. + * 60KB allows for several concurrent page requests. If more space + * is required, malloc will be used for the overflow. + */ + bopen(NULL, (60 * 1024), B_USE_MALLOC); + signal(SIGPIPE, SIG_IGN); + +/* + * Initialize the web server + */ + if (initWebs() < 0) { + return -1; + } + +#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(-1) || socketSelect(-1, 1000)) { + 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(); + socketClose(); +#ifdef B_STATS + memLeaks(); +#endif + bclose(); + return 0; +} + +/******************************************************************************/ +/* + * Initialize the web server. + */ + +static int initWebs() +{ + struct hostent *hp; + struct in_addr intaddr; + char host[128], dir[128], webdir[128]; + char *cp; + char_t wbuf[128]; + +/* + * Initialize the socket subsystem + */ + socketOpen(); + +#ifdef USER_MANAGEMENT_SUPPORT +/* + * Initialize the User Management database + */ + umOpen(); + umRestore(T("umconfig.txt")); +#endif + +/* + * Define the local Ip address, host name, default home page and the + * root web directory. + */ + if (gethostname(host, sizeof(host)) < 0) { + error(E_L, E_LOG, T("Can't get hostname")); + return -1; + } + if ((hp = gethostbyname(host)) == NULL) { + error(E_L, E_LOG, T("Can't get host address")); + return -1; + } + memcpy((char *) &intaddr, (char *) hp->h_addr_list[0], + (size_t) hp->h_length); + + + +/* + * Set ../web as the root web. Modify this to suit your needs + */ + getcwd(dir, sizeof(dir)); + if ((cp = strrchr(dir, '/'))) { + *cp = '\0'; + } + sprintf(webdir, "%s/%s", dir, rootWeb); + +/* + * Configure the web server options before opening the web server + */ + websSetDefaultDir(webdir); + cp = inet_ntoa(intaddr); + ascToUni(wbuf, cp, min(strlen(cp) + 1, sizeof(wbuf))); + websSetIpaddr(wbuf); + ascToUni(wbuf, host, min(strlen(host) + 1, sizeof(wbuf))); + websSetHost(wbuf); + +/* + * Configure the web server options before opening the web server + */ + websSetDefaultPage(T("default.asp")); + websSetPassword(password); + +/* + * Open the web server on the given port. If that port is taken, try + * the next sequential port for up to "retries" attempts. + */ + websOpenServer(port, retries); + +/* + * First create the URL handlers. Note: handlers are called in sorted order + * with the longest path handler examined first. Here we define the security + * handler, forms handler and the default web page handler. + */ + websUrlHandlerDefine(T(""), NULL, 0, websSecurityHandler, + WEBS_HANDLER_FIRST); + websUrlHandlerDefine(T("/goform"), NULL, 0, websFormHandler, 0); + websUrlHandlerDefine(T("/cgi-bin"), NULL, 0, websCgiHandler, 0); + websUrlHandlerDefine(T(""), NULL, 0, websDefaultHandler, + WEBS_HANDLER_LAST); + +/* + * Now define two test procedures. Replace these with your application + * relevant ASP script procedures and form functions. + */ + websAspDefine(T("aspTest"), aspTest); + websFormDefine(T("formTest"), formTest); + +/* + * Create the Form handlers for the User Management pages + */ +#ifdef USER_MANAGEMENT_SUPPORT + formDefineUserMgmt(); +#endif + +/* + * Create a handler for the default home page + */ + websUrlHandlerDefine(T("/"), NULL, 0, websHomePageHandler, 0); + return 0; +} + +/******************************************************************************/ +/* + * Test Javascript binding for ASP. This will be invoked when "aspTest" is + * embedded in an ASP page. See web/asp.asp for usage. Set browser to + * "localhost/asp.asp" to test. + */ + +static int aspTest(int eid, webs_t wp, int argc, char_t **argv) +{ + char_t *name, *address; + + if (ejArgs(argc, argv, T("%s %s"), &name, &address) < 2) { + websError(wp, 400, T("Insufficient args\n")); + return -1; + } + return websWrite(wp, T("Name: %s, Address %s"), name, address); +} + +/******************************************************************************/ +/* + * Test form for posted data (in-memory CGI). This will be called when the + * form in web/forms.asp is invoked. Set browser to "localhost/forms.asp" to test. + */ + +static void formTest(webs_t wp, char_t *path, char_t *query) +{ + char_t *name, *address; + + name = websGetVar(wp, T("name"), T("Joe Smith")); + address = websGetVar(wp, T("address"), T("1212 Milky Way Ave.")); + + websHeader(wp); + websWrite(wp, T("

Name: %s, Address: %s

\n"), name, address); + websFooter(wp); + websDone(wp, 200); +} + +/******************************************************************************/ +/* + * Home page handler + */ + +static int websHomePageHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, + int arg, char_t *url, char_t *path, char_t *query) +{ +/* + * If the empty or "/" URL is invoked, redirect default URLs to the home page + */ + if (*url == '\0' || gstrcmp(url, T("/")) == 0) { + websRedirect(wp, T("home.asp")); + return 1; + } + return 0; +} + +/******************************************************************************/ +/* + * Default error handler. The developer should insert code to handle + * error messages in the desired manner. + */ + +void defaultErrorHandler(int etype, char_t *msg) +{ +#if 0 + 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 0 + 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; +} + +/******************************************************************************/ +/* + * Launch the CGI process and return a handle to it. + */ + +int websLaunchCgiProc(char_t *cgiPath, char_t **argp, char_t **envp, + char_t *stdIn, char_t *stdOut) +{ + int pid, fdin, fdout, hstdin, hstdout, rc; + + fdin = fdout = hstdin = hstdout = rc = -1; + if ((fdin = open(stdIn, O_RDWR | O_CREAT, 0666)) < 0 || + (fdout = open(stdOut, O_RDWR | O_CREAT, 0666)) < 0 || + (hstdin = dup(0)) == -1 || + (hstdout = dup(1)) == -1 || + dup2(fdin, 0) == -1 || + dup2(fdout, 1) == -1) { + goto DONE; + } + + rc = pid = fork(); + if (pid == 0) { +/* + * if pid == 0, then we are in the child process + */ + if (execve(cgiPath, argp, envp) == -1) { + printf("content-type: text/html\n\n" + "Execution of cgi process failed\n"); + } + exit (0); + } + +DONE: + if (hstdout >= 0) { + dup2(hstdout, 1); + close(hstdout); + } + if (hstdin >= 0) { + dup2(hstdin, 0); + close(hstdin); + } + if (fdout >= 0) { + close(fdout); + } + if (fdin >= 0) { + close(fdin); + } + return rc; +} + +/******************************************************************************/ +/* + * Check the CGI process. Return 0 if it does not exist; non 0 if it does. + */ + +int websCheckCgiProc(int handle) +{ +/* + * Check to see if the CGI child process has terminated or not yet. + */ + if (waitpid(handle, NULL, WNOHANG) == handle) { + return 0; + } else { + return 1; + } +} + +/******************************************************************************/ + +#ifdef B_STATS +static void memLeaks() +{ + int fd; + + if ((fd = gopen(T("leak.txt"), O_CREAT | O_TRUNC | O_WRONLY, 0666)) >= 0) { + bstats(fd, printMemStats); + close(fd); + } +} + +/******************************************************************************/ +/* + * Print memory usage / leaks + */ + +static void printMemStats(int handle, char_t *fmt, ...) +{ + va_list args; + char_t buf[256]; + + va_start(args, fmt); + vsprintf(buf, fmt, args); + va_end(args); + write(handle, buf, strlen(buf)); +} +#endif + +/******************************************************************************/ diff --git a/cleopatre/application/spidgoahead/Makefile b/cleopatre/application/spidgoahead/Makefile new file mode 100644 index 0000000000..01b40a93da --- /dev/null +++ b/cleopatre/application/spidgoahead/Makefile @@ -0,0 +1,27 @@ +# +# Makefile for the webs package directory +# +# Copyright (c) Go Ahead, 1995-1999 +# + +all: compile + +CLEANT = cleant +DEPENDT = dependt +PACKAGET= packaget + +include make.dep +include $(TARGET)/$(OS)/make.$(CUT) + +packaget: + rm -f webs.zip + "C:/Program Files/winzip/winzip32" -a -P webs.zip @files + "C:/Program Files/winzip/winzip32" -a -P webs.zip @docfiles + +cleant: + rm -fr webs.zip websdoc.zip + rm -fr web/doc/basic/* web/doc/ej/* web/doc/ws/* + $(TCL) import.tcl clean + +dependt: + $(TCL) import.tcl import diff --git a/cleopatre/application/spidgoahead/asp.c b/cleopatre/application/spidgoahead/asp.c new file mode 100644 index 0000000000..050e3fd881 --- /dev/null +++ b/cleopatre/application/spidgoahead/asp.c @@ -0,0 +1,322 @@ +/* + * asp.c -- Active Server Page Support + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: asp.c,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * The ASP module processes ASP pages and executes embedded scripts. It + * support an open scripting architecture with in-built support for + * Ejscript(TM). + */ + +/********************************* Includes ***********************************/ + +#include "wsIntrn.h" + +/********************************** Locals ************************************/ + +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 *skipWhite(char_t *s); + +/************************************* Code ***********************************/ +/* + * Create script spaces and commands + */ + +int websAspOpen() +{ + if (++aspOpenCount == 1) { +/* + * Create the table for ASP functions + */ + websAspFunctions = symOpen(WEBS_SYM_INIT * 2); + +/* + * Create standard ASP commands + */ + websAspDefine(T("write"), websAspWrite); + } + return 0; +} + +/************************************* Code ***********************************/ +/* + * Close Asp symbol table. + */ + +void websAspClose() +{ + if (--aspOpenCount <= 0) { + if (websAspFunctions != -1) { + symClose(websAspFunctions); + websAspFunctions = -1; + } + } +} + +/******************************************************************************/ +/* + * Process ASP requests and expand all scripting commands. We read the + * entire ASP page into memory and then process. If you have really big + * documents, it is better to make them plain HTML files rather than ASPs. + */ + +int websAspRequest(webs_t wp, char_t *lpath) +{ + websStatType sbuf; + char *rbuf; + char_t *token, *lang, *result, *path, *ep, *cp, *buf, *nextp; + char_t *last; + int rc, engine, len, ejid; + + a_assert(websValid(wp)); + a_assert(lpath && *lpath); + + rc = -1; + buf = NULL; + rbuf = NULL; + engine = EMF_SCRIPT_EJSCRIPT; + wp->flags |= WEBS_HEADER_DONE; + path = websGetRequestPath(wp); + +/* + * Create Ejscript instance in case it is needed + */ + ejid = ejOpenEngine(wp->cgiVars, websAspFunctions); + if (ejid < 0) { + websError(wp, 200, T("Can't create Ejscript engine")); + goto done; + } + ejSetUserHandle(ejid, (int) wp); + + if (websPageStat(wp, lpath, path, &sbuf) < 0) { + websError(wp, 200, T("Can't stat %s"), lpath); + goto done; + } + +/* + * Create a buffer to hold the ASP file in-memory + */ + len = sbuf.size * sizeof(char); + if ((rbuf = balloc(B_L, len + 1)) == NULL) { + websError(wp, 200, T("Can't get memory")); + goto done; + } + rbuf[len] = '\0'; + + if (websPageReadData(wp, rbuf, len) != len) { + websError(wp, 200, T("Cant read %s"), lpath); + goto done; + } + websPageClose(wp); + +/* + * Convert to UNICODE if necessary. + */ + if ((buf = ballocAscToUni(rbuf, len)) == NULL) { + websError(wp, 200, T("Can't get memory")); + goto done; + } + +/* + * Scan for the next "<%" + */ + last = buf; + rc = 0; + while (rc == 0 && *last && ((nextp = gstrstr(last, T("<%"))) != NULL)) { + websWriteBlock(wp, last, (nextp - last)); + nextp = skipWhite(nextp + 2); + +/* + * Decode the language + */ + token = T("language"); + + if ((lang = strtokcmp(nextp, token)) != NULL) { + if ((cp = strtokcmp(lang, T("=javascript"))) != NULL) { + engine = EMF_SCRIPT_EJSCRIPT; + } else { + cp = nextp; + } + nextp = cp; + } + +/* + * Find tailing bracket and then evaluate the script + */ + if ((ep = gstrstr(nextp, T("%>"))) != NULL) { + + *ep = '\0'; + last = ep + 2; + nextp = skipWhite(nextp); +/* + * Handle backquoted newlines + */ + for (cp = nextp; *cp; ) { + if (*cp == '\\' && (cp[1] == '\r' || cp[1] == '\n')) { + *cp++ = ' '; + while (*cp == '\r' || *cp == '\n') { + *cp++ = ' '; + } + } else { + cp++; + } + } + +/* + * Now call the relevant script engine. Output is done directly + * by the ASP script procedure by calling websWrite() + */ + if (*nextp) { + result = NULL; + if (engine == EMF_SCRIPT_EJSCRIPT) { + rc = scriptEval(engine, nextp, &result, ejid); + } else { + rc = scriptEval(engine, nextp, &result, (int) wp); + } + if (rc < 0) { +/* + * On an error, discard all output accumulated so far + * and store the error in the result buffer. Be careful if the + * user has called websError() already. + */ + if (websValid(wp)) { + if (result) { + websWrite(wp, T("

ASP Error: %s

\n"), + result); + websWrite(wp, T("
%s
"), nextp); + bfree(B_L, result); + } else { + websWrite(wp, T("

ASP Error

\n%s\n"), + nextp); + } + websWrite(wp, T("\n")); + rc = 0; + } + goto done; + } + } + + } else { + websError(wp, 200, T("Unterminated script in %s: \n"), lpath); + rc = -1; + goto done; + } + } +/* + * Output any trailing HTML page text + */ + if (last && *last && rc == 0) { + websWriteBlock(wp, last, gstrlen(last)); + } + rc = 0; + +/* + * Common exit and cleanup + */ +done: + if (websValid(wp)) { + websPageClose(wp); + if (ejid >= 0) { + ejCloseEngine(ejid); + } + } + bfreeSafe(B_L, buf); + bfreeSafe(B_L, rbuf); + return rc; +} + +/******************************************************************************/ +/* + * Define an ASP Ejscript function. Bind an ASP name to a C procedure. + */ + +int websAspDefine(char_t *name, + int (*fn)(int ejid, webs_t wp, int argc, char_t **argv)) +{ + return ejSetGlobalFunctionDirect(websAspFunctions, name, + (int (*)(int, void*, int, char_t**)) fn); +} + +/******************************************************************************/ +/* + * Asp write command. This implemements <% write("text"); %> command + */ + +int websAspWrite(int ejid, webs_t wp, int argc, char_t **argv) +{ + int i; + + a_assert(websValid(wp)); + + for (i = 0; i < argc; ) { + a_assert(argv); + if (websWriteBlock(wp, argv[i], gstrlen(argv[i])) < 0) { + return -1; + } + if (++i < argc) { + if (websWriteBlock(wp, T(" "), 2) < 0) { + return -1; + } + } + } + return 0; +} + +/******************************************************************************/ +/* + * strtokcmp -- Find s2 in s1. We skip leading white space in s1. + * Return a pointer to the location in s1 after s2 ends. + */ + +static char_t *strtokcmp(char_t *s1, char_t *s2) +{ + int len; + + s1 = skipWhite(s1); + len = gstrlen(s2); + for (len = gstrlen(s2); len > 0 && (tolower(*s1) == tolower(*s2)); len--) { + if (*s2 == '\0') { + return s1; + } + s1++; + s2++; + } + if (len == 0) { + return s1; + } + return NULL; +} + +/******************************************************************************/ +/* + * Skip white space + */ + +static char_t *skipWhite(char_t *s) +{ + a_assert(s); + + if (s == NULL) { + return s; + } + while (*s && gisspace(*s)) { + s++; + } + return s; +} + +/******************************************************************************/ + diff --git a/cleopatre/application/spidgoahead/balloc.c b/cleopatre/application/spidgoahead/balloc.c new file mode 100644 index 0000000000..ea5fc5f9dd --- /dev/null +++ b/cleopatre/application/spidgoahead/balloc.c @@ -0,0 +1,974 @@ +/* + * balloc.c -- Block allocation module + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: balloc.c,v 1.4 2002/12/02 15:34:19 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * 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 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 + * of malloc, call bopen with flags set to B_USE_MALLOC (this is the default). + * It is recommended that bopen be called first thing in the application. + * If it is not, it will be called with default values on the first call to + * balloc(). Note that this code is not designed for multi-threading purposes + * and it depends on newly declared variables being initialized to zero. + */ + +/********************************* Includes ***********************************/ + +#define IN_BALLOC + +#ifdef UEMF + #include "uemf.h" +#else + #include "basic/basicInternal.h" +#endif + +#include +#include + +#ifndef NO_BALLOC +/********************************* Defines ************************************/ + +/* + * Define B_STATS if you wish to track memory block and stack usage + */ +#ifdef B_STATS +/* + * Optional statistics + */ + +typedef struct { + long alloc; /* Block allocation calls */ + long inuse; /* Blocks in use */ +} bStatsType; + +typedef struct { + char_t file[FNAMESIZE]; + long allocated; /* Bytes currently allocated */ + long count; /* Current block count */ + long times; /* Count of alloc attempts */ + long largest; /* largest allocated here */ + int q; +} bStatsFileType; + +/* + * This one is very expensive but great stats + */ +typedef struct { + void *ptr; /* Pointer to memory */ + bStatsFileType *who; /* Who allocated the memory */ +} bStatsBlkType; + +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 = 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 = 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 ************************************/ +/* + * bQhead blocks are created as the original memory allocation is freed up. + * See bfree. + */ +static bType *bQhead[B_MAX_CLASS]; /* Per class block q head */ +static char *bFreeBuf; /* Pointer to free memory */ +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 *****************************/ + +#ifdef B_STATS +static void bStatsAlloc(B_ARGS_DEC, void *ptr, int q, int size); +static void bStatsFree(B_ARGS_DEC, void *ptr, int q, int size); +static void bstatsWrite(int handle, char_t *fmt, ...); +static int bStatsFileSort(const void *cp1, const void *cp2); +#endif /* B_STATS */ + +#if (defined (B_FILL) || defined (B_VERIFY_CAUSES_SEVERE_OVERHEAD)) +static void bFillBlock(void *buf, int bufsize); +#endif + +#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD +static void verifyUsedBlock(bType *bp, int q); +static void verifyFreeBlock(bType *bp, int q); +void verifyBallocSpace(); +#endif + +static int ballocGetSize(int size, int *q); + +/********************************** Code **************************************/ +/* + * Initialize the balloc module. bopen should be called the very first thing + * after the application starts and bclose should be called the last thing + * before exiting. If bopen is not called, it will be called on the first + * allocation with default values. "buf" points to memory to use of size + * "bufsize". If buf is NULL, memory is allocated using malloc. flags may + * be set to B_USE_MALLOC if using malloc is okay. This routine will allocate + * an initial buffer of size bufsize for use by the application. + */ + +int bopen(void *buf, int bufsize, int flags) +{ + bFlags = flags; + +#ifdef 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) { + /* resetting bopenCount lets client code decide to attempt to call + * bopen() again with a smaller memory request, should it desire to. + * Fix suggested by Simon Byholm. + */ + --bopenCount; + return -1; + } +#ifdef B_STATS + bStatsMemMalloc += bufsize; +#endif + } else { + bFlags |= B_USER_BUF; + } + + bFreeSize = bFreeLeft = bufsize; + bFreeBuf = bFreeNext = buf; + memset(bQhead, 0, sizeof(bQhead)); +#if (defined (B_FILL) || defined (B_VERIFY_CAUSES_SEVERE_OVERHEAD)) + bFillBlock(buf, bufsize); +#endif +#ifdef B_STATS + bStackStart = &buf; +#endif +#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD + verifyFreeBlock(buf, bufsize); +#endif + return 0; +} + +/******************************************************************************/ +/* + * Close down the balloc module and free all malloced memory. + */ + +void bclose() +{ +#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD + verifyBallocSpace(); +#endif + if (--bopenCount <= 0 && !(bFlags & B_USER_BUF)) { + free(bFreeBuf); + bopenCount = 0; + } +} + +/******************************************************************************/ +/* + * Allocate a block of the requested size. First check the block + * queues for a suitable one. + */ + +void *balloc(B_ARGS_DEC, int size) +{ + bType *bp; + 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) { + return NULL; + } + } +#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD + verifyBallocSpace(); +#endif + if (size < 0) { + return NULL; + } + +#ifdef BASTARD_TESTING + if (rand() == 0x7fff) { + return NULL; + } +#endif /* BASTARD_TESTING */ + + + memSize = ballocGetSize(size, &q); + + if (q >= B_MAX_CLASS) { +/* + * Size if bigger than the maximum class. Malloc if use has been okayed + */ + if (bFlags & B_USE_MALLOC) { +#ifdef B_STATS + bstats(0, NULL); +#endif +#ifdef IRIX + memSize = ROUNDUP4(memSize); +#endif + bp = (bType*) malloc(memSize); + if (bp == NULL) { + traceRaw(T("B: malloc failed\n")); + return NULL; + } +#ifdef B_STATS + bStatsMemMalloc += memSize; +#endif +#if (defined (B_FILL) || defined (B_VERIFY_CAUSES_SEVERE_OVERHEAD)) + bFillBlock(bp, memSize); +#endif + + } else { + traceRaw(T("B: malloc failed\n")); + return NULL; + } + +/* + * 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) { +/* + * Take first block off the relevant q if non-empty + */ + bQhead[q] = bp->u.next; +#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD + verifyFreeBlock(bp, q); +#endif +#if (defined (B_FILL) || defined (B_VERIFY_CAUSES_SEVERE_OVERHEAD)) + bFillBlock(bp, memSize); +#endif + bp->u.size = memSize - sizeof(bType); + bp->flags = 0; + + } else { + if (bFreeLeft > memSize) { +/* + * The q was empty, and the free list has spare memory so + * create a new block out of the primary free block + */ + bp = (bType*) bFreeNext; +#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD + verifyFreeBlock(bp, q); +#endif + bFreeNext += memSize; + bFreeLeft -= memSize; +#if (defined (B_FILL) || defined (B_VERIFY_CAUSES_SEVERE_OVERHEAD)) + bFillBlock(bp, memSize); +#endif + bp->u.size = memSize - sizeof(bType); + bp->flags = 0; + + } else if (bFlags & B_USE_MALLOC) { +#ifdef B_STATS + static int once = 0; + if (once++ == 0) { + bstats(0, NULL); + } +#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) { + traceRaw(T("B: malloc failed\n")); + return NULL; + } +#ifdef B_STATS + bStatsMemMalloc += memSize; +#endif +#if (defined (B_FILL) || defined (B_VERIFY_CAUSES_SEVERE_OVERHEAD)) + bFillBlock(bp, memSize); +#endif + bp->u.size = memSize - sizeof(bType); + bp->flags = B_MALLOCED; + + } else { + traceRaw(T("B: malloc failed\n")); + return NULL; + } + } + +#ifdef B_STATS + 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 +#ifdef B_STATS + if (bStatsBallocInUse == bStatsBallocMax) { + bstats(0, NULL); + } +#endif +#endif + return (void*) ((char*) bp + sizeof(bType)); +} + +/******************************************************************************/ +/* + * Free a block back to the relevant free q. We don't free back to the O/S + * or run time system unless the block is greater than the maximum class size. + * We also do not coalesce blocks. + */ + +void bfree(B_ARGS_DEC, void *mp) +{ + bType *bp; + int q, memSize; + +#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD + verifyBallocSpace(); +#endif + bp = (bType*) ((char*) mp - sizeof(bType)); + + a_assert((bp->flags & B_INTEGRITY_MASK) == B_INTEGRITY); + + if ((bp->flags & B_INTEGRITY_MASK) != B_INTEGRITY) { + return; + } + + memSize = ballocGetSize(bp->u.size, &q); + +#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD + verifyUsedBlock(bp,q); +#endif +#ifdef B_STATS + bStatsFree(B_ARGS, bp, q, bp->u.size+sizeof(bType)); +#endif + if (bp->flags & B_MALLOCED) { + free(bp); + return; + } + +#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD + bFillBlock(bp, memSize); +#endif + +/* + * Simply link onto the head of the relevant q + */ + bp->u.next = bQhead[q]; + bQhead[q] = bp; + + bp->flags = B_FILL_WORD; +} + +/******************************************************************************/ +/* + * Safe free + */ + +void bfreeSafe(B_ARGS_DEC, void *mp) +{ + if (mp) { + bfree(B_ARGS, mp); + } +} + +/******************************************************************************/ +#ifdef UNICODE +/* + * Duplicate a string, allow NULL pointers and then dup an empty string. + */ + +char *bstrdupA(B_ARGS_DEC, char *s) +{ + char *cp; + int len; + + if (s == NULL) { + s = ""; + } + len = strlen(s) + 1; + if (cp = balloc(B_ARGS, len)) { + strcpy(cp, s); + } + return cp; +} + +#endif /* UNICODE */ +/******************************************************************************/ +/* + * Duplicate an ascii string, allow NULL pointers and then dup an empty string. + * If UNICODE, bstrdup above works with wide chars, so we need this routine + * for ascii strings. + */ + +char_t *bstrdup(B_ARGS_DEC, char_t *s) +{ + char_t *cp; + int len; + + if (s == NULL) { + s = T(""); + } + len = gstrlen(s) + 1; + if ((cp = balloc(B_ARGS, len * sizeof(char_t))) != NULL) { + gstrcpy(cp, s); + } + return cp; +} + +/******************************************************************************/ +/* + * Reallocate a block. Allow NULL pointers and just do a malloc. + * Note: if the realloc fails, we return NULL and the previous buffer is + * preserved. + */ + +void *brealloc(B_ARGS_DEC, void *mp, int newsize) +{ + bType *bp; + void *newbuf; + + if (mp == NULL) { + return balloc(B_ARGS, 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); + } + 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 (defined (B_FILL) || defined (B_VERIFY_CAUSES_SEVERE_OVERHEAD)) +/* + * Fill the block (useful during development to catch zero fill assumptions) + */ + +static void bFillBlock(void *buf, int bufsize) +{ + memset(buf, B_FILL_CHAR, bufsize); +} +#endif + +/******************************************************************************/ +#ifdef B_STATS +/* + * Statistics. Do output via calling the writefn callback function with + * "handle" as the output file handle. + */ + +void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) +{ + bStatsFileType *fp, *files; + bStatsBlkType *blkp; + bType *bp; + char_t *cp; + int q, count, mem, total, len; + static int recurseProtect = 0; + + if (recurseProtect++ > 0) { + recurseProtect--; + return; + } + + if (writefn == NULL) { + writefn = bstatsWrite; + } + +/* + * Print stats for each memory block + */ + (*writefn)(handle, T("\nMemory Stats\n")); + +/* + * The following tabular format is now used for the output. + * Q Size Free Bytes Inuse Bytes Allocs + * dd ddddd ddd ddddd dddd ddddd dddd + */ + (*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) { + count++; + } + mem = count * (1 << (q + B_SHIFT)); + total += mem; + (*writefn)(handle, + 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); + } + + (*writefn)(handle, T("\n")); + +/* + * 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); + + (*writefn)(handle, T("Free memory on all queues %7d\n"), total); + (*writefn)(handle, T("Free list buffer left %7d\n"), bFreeLeft); + (*writefn)(handle, T("Total free memory %7d\n"), bFreeLeft + total); + +/* + * Print per file allocation stats. Sort the copied table. + */ + 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; + (*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, %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\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--; +} + +/******************************************************************************/ +/* + * File sort function. Used to sort per file stats + */ + +static int bStatsFileSort(const void *cp1, const void *cp2) +{ + bStatsFileType *s1, *s2; + + s1 = (bStatsFileType*) cp1; + s2 = (bStatsFileType*) cp2; + + if (s1->allocated < s2->allocated) + return -1; + else if (s1->allocated == s2->allocated) + return 0; + return 1; +} + +/******************************************************************************/ +/* + * 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]; + + 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 + * we only measure this on block allocation. + */ + if ((void*) &file < bStackMin) { + bStackMin = (void*) &file; + } + +/* + * Find the file and adjust the stats for this file + */ + for (fp = bStatsFiles; fp < &bStatsFiles[bStatsFilesMax]; fp++) { + if (fp->file[0] == file[0] && gstrcmp(fp->file, name) == 0) { + fp->allocated += size; + fp->count++; + fp->times++; + if (fp->largest < size) { + fp->largest = size; + fp->q = q; + } + break; + } + } + +/* + * 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++) { + if (fp->file[0] == '\0') { + gstrncpy(fp->file, name, TSZ(fp->file)); + fp->allocated += size; + fp->count++; + fp->times++; + fp->largest = size; + fp->q = q; + if ((fp - bStatsFiles) >= bStatsFilesMax) { + bStatsFilesMax = (fp - bStatsFiles) + 1; + } + break; + } + } + } + +/* + * Update the per block stats. Allocate a new slot. + */ + for (bp = bStatsBlks; bp < &bStatsBlks[B_MAX_BLOCKS]; bp++) { + if (bp->ptr == NULL) { + bp->ptr = ptr; + bp->who = fp; + if ((bp - bStatsBlks) >= bStatsBlksMax) { + bStatsBlksMax = (bp - bStatsBlks) + 1; + } + break; + } + } +} + +/******************************************************************************/ +/* + * Free statistics + */ + +static void bStatsFree(B_ARGS_DEC, void *ptr, int q, int size) +{ + int memSize; + bStatsFileType *fp; + bStatsBlkType *bp; + + memSize = (1 << (B_SHIFT + q)) + sizeof(bType); + bStatsMemInUse -= size; + bStatsBallocInUse -= memSize; + bStats[q].inuse--; + +/* + * Update the per block stats. Try from the end first + */ + 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; + } + } +} + +/******************************************************************************/ +/* + * 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 */ +/******************************************************************************/ +/* + * Dummy bstats for external calls that aren't protected by #if B_STATS. + */ + +void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) +{ +} +#endif /* B_STATS */ + +/******************************************************************************/ +#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD +/* + * The following routines verify the integrity of the balloc memory space. + * 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 + * found. CAUTION: These functions add severe processing overhead and should + * only be used when searching for a tough corruption problem. + */ + +/******************************************************************************/ +/* + * verifyUsedBlock verifies that a block which was previously allocated is + * still uncorrupted. + */ + +static void verifyUsedBlock(bType *bp, int q) +{ + int memSize, size; + char *p; + + 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); + } +} + +/******************************************************************************/ +/* + * verifyFreeBlock verifies that a previously free'd block in one of the queues + * is still uncorrupted. + */ + +static void verifyFreeBlock(bType *bp, int q) +{ + int memSize; + char *p; + + 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); +} + +/******************************************************************************/ +/* + * 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 + * filled with B_FILL_CHAR. + */ + +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; + if (bp->u.size > 0xFFFFF) { + p += sizeof(bp->u); + while (p < (bFreeBuf + bFreeSize) && *p == B_FILL_CHAR) { + p++; + } + } else { + a_assert(((bp->flags & ~B_MALLOCED) == B_INTEGRITY) || + bp->flags == B_FILL_WORD); + p += (sizeof(bType) + bp->u.size); + while (p < (bFreeBuf + bFreeSize) && *p == B_FILL_CHAR) { + p++; + } + } + } +} +#endif /* B_VERIFY_CAUSES_SEVERE_OVERHEAD */ + +/******************************************************************************/ + +#else /* NO_BALLOC */ +int bopen(void *buf, int bufsize, int flags) +{ + return 0; +} + +/******************************************************************************/ + +void bclose() +{ +} + +/******************************************************************************/ + +void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...)) +{ +} + +/******************************************************************************/ + +char_t *bstrdupNoBalloc(char_t *s) +{ +#ifdef UNICODE + if (s) { + return wcsdup(s); + } else { + return wcsdup(T("")); + } +#else + return bstrdupANoBalloc(s); +#endif +} + +/******************************************************************************/ + +char *bstrdupANoBalloc(char *s) +{ + char* buf; + + if (s == NULL) { + s = ""; + } + buf = malloc(strlen(s)+1); + strcpy(buf, s); + return buf; +} + +#endif /* NO_BALLOC */ +/******************************************************************************/ + + diff --git a/cleopatre/application/spidgoahead/base64.c b/cleopatre/application/spidgoahead/base64.c new file mode 100644 index 0000000000..106cbee4f9 --- /dev/null +++ b/cleopatre/application/spidgoahead/base64.c @@ -0,0 +1,150 @@ +/* + * base64.c -- Base64 Mime encoding + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: base64.c,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * The base64 command encodes and decodes a string in mime base64 format + */ + +/********************************* Includes ***********************************/ + +#include "wsIntrn.h" + +/******************************** Local Data **********************************/ +/* + * Mapping of ANSI chars to base64 Mime encoding alphabet (see below) + */ + +static char_t map64[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char_t alphabet64[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', +}; + +/*********************************** Code *************************************/ +/* + * Decode a buffer from "string" and into "outbuf" + */ + +int websDecode64(char_t *outbuf, char_t *string, int outlen) +{ + unsigned long shiftbuf; + char_t *cp, *op; + int c, i, j, shift; + + op = outbuf; + *op = '\0'; + cp = string; + while (*cp && *cp != '=') { +/* + * Map 4 (6bit) input bytes and store in a single long (shiftbuf) + */ + shiftbuf = 0; + shift = 18; + for (i = 0; i < 4 && *cp && *cp != '='; i++, cp++) { + c = map64[*cp & 0xff]; + if (c == -1) { + error(E_L, E_LOG, T("Bad string: %s at %c index %d"), string, + c, i); + return -1; + } + shiftbuf = shiftbuf | (c << shift); + shift -= 6; + } +/* + * Interpret as 3 normal 8 bit bytes (fill in reverse order). + * Check for potential buffer overflow before filling. + */ + --i; + if ((op + i) >= &outbuf[outlen]) { + gstrcpy(outbuf, T("String too big")); + return -1; + } + for (j = 0; j < i; j++) { + *op++ = (char_t) ((shiftbuf >> (8 * (2 - j))) & 0xff); + } + *op = '\0'; + } + return 0; +} + + +/******************************************************************************/ +/* + * Encode a buffer from "string" into "outbuf" + */ + +void websEncode64(char_t *outbuf, char_t *string, int outlen) +{ + unsigned long shiftbuf; + char_t *cp, *op; + int x, i, j, shift; + + op = outbuf; + *op = '\0'; + cp = string; + while (*cp) { +/* + * Take three characters and create a 24 bit number in shiftbuf + */ + shiftbuf = 0; + for (j = 2; j >= 0 && *cp; j--, cp++) { + shiftbuf |= ((*cp & 0xff) << (j * 8)); + } +/* + * Now convert shiftbuf to 4 base64 letters. The i,j magic calculates + * how many letters need to be output. + */ + shift = 18; + for (i = ++j; i < 4 && op < &outbuf[outlen] ; i++) { + x = (shiftbuf >> shift) & 0x3f; + *op++ = alphabet64[(shiftbuf >> shift) & 0x3f]; + shift -= 6; + } +/* + * Pad at the end with '=' + */ + while (j-- > 0) { + *op++ = '='; + } + *op = '\0'; + } +} +/******************************************************************************/ + diff --git a/cleopatre/application/spidgoahead/cgi.c b/cleopatre/application/spidgoahead/cgi.c new file mode 100644 index 0000000000..3e47abccd6 --- /dev/null +++ b/cleopatre/application/spidgoahead/cgi.c @@ -0,0 +1,332 @@ +/* + * cgi.c -- CGI processing (for the GoAhead Web server + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: cgi.c,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +/********************************** Description *******************************/ +/* + * This module implements the /cgi-bin handler. CGI processing differs from + * goforms processing in that each CGI request is executed as a separate + * process, rather than within the webserver process. For each CGI request the + * environment of the new process must be set to include all the CGI variables + * and its standard input and output must be directed to the socket. This + * is done using temporary files. + */ + +/*********************************** Includes *********************************/ +#include "wsIntrn.h" +#ifdef UEMF + #include "uemf.h" +#else + #include "basic/basicInternal.h" +#endif + +/************************************ Locals **********************************/ +typedef struct { /* Struct for CGI tasks which have completed */ + webs_t wp; /* pointer to session websRec */ + char_t *stdIn; /* file desc. for task's temp input fd */ + char_t *stdOut; /* file desc. for task's temp output fd */ + char_t *cgiPath; /* path to executable process file */ + char_t **argp; /* pointer to buf containing argv tokens */ + char_t **envp; /* pointer to array of environment strings */ + int handle; /* process handle of the task */ + long fplacemark; /* seek location for CGI output file */ +} cgiRec; +static cgiRec **cgiList; /* hAlloc chain list of wp's to be closed */ +static int cgiMax; /* Size of hAlloc list */ + +/************************************* Code ***********************************/ + +/* + * Process a form request. Returns 1 always to indicate it handled the URL + */ +int websCgiHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, + char_t *url, char_t *path, char_t* query) +{ + cgiRec *cgip; + sym_t *s; + char_t cgiBuf[FNAMESIZE], *stdIn, *stdOut, cwd[FNAMESIZE]; + char_t *cp, *cgiName, *cgiPath, **argp, **envp, **ep; + int n, envpsize, argpsize, pHandle, cid; + a_assert(websValid(wp)); + a_assert(url && *url); + a_assert(path && *path == '/'); + websStats.cgiHits++; +/* + * Extract the form name and then build the full path name. The form + * name will follow the first '/' in path. + */ + gstrncpy(cgiBuf, path, TSZ(cgiBuf)); + if ((cgiName = gstrchr(&cgiBuf[1], '/')) == NULL) { + websError(wp, 200, T("Missing CGI name")); + return 1; + } + cgiName++; + if ((cp = gstrchr(cgiName, '/')) != NULL) { + *cp = '\0'; + } + fmtAlloc(&cgiPath, FNAMESIZE, T("%s/%s/%s"), websGetDefaultDir(), + CGI_BIN, cgiName); +#ifndef VXWORKS +/* + * See if the file exists and is executable. If not error out. + * Don't do this step for VxWorks, since the module may already + * be part of the OS image, rather than in the file system. + */ + { + gstat_t sbuf; + if (gstat(cgiPath, &sbuf) != 0 || (sbuf.st_mode & S_IFREG) == 0) { + websError(wp, 200, T("CGI process file does not exist")); + bfree(B_L, cgiPath); + return 1; + } +#if (defined (WIN) || defined (CE)) + if (gstrstr(cgiPath, T(".exe")) == NULL && + gstrstr(cgiPath, T(".bat")) == NULL) { +#elif (defined (NW)) + if (gstrstr(cgiPath, T(".nlm")) == NULL) { +#else + if (gaccess(cgiPath, X_OK) != 0) { +#endif /* WIN || CE */ + websError(wp, 200, T("CGI process file is not executable")); + bfree(B_L, cgiPath); + return 1; + } + } +#endif /* ! VXWORKS */ + + +/* + * Get the CWD for resetting after launching the child process CGI + */ + ggetcwd(cwd, FNAMESIZE); +/* + * Retrieve the directory of the child process CGI + */ + if ((cp = gstrrchr(cgiPath, '/')) != NULL) { + *cp = '\0'; + gchdir(cgiPath); + *cp = '/'; + } +/* + * Build command line arguments. Only used if there is no non-encoded + * = character. This is indicative of a ISINDEX query. POST separators + * are & and others are +. argp will point to a balloc'd array of + * pointers. Each pointer will point to substring within the + * query string. This array of string pointers is how the spawn or + * exec routines expect command line arguments to be passed. Since + * we don't know ahead of time how many individual items there are in + * the query string, the for loop includes logic to grow the array + * size via brealloc. + */ + argpsize = 10; + argp = balloc(B_L, argpsize * sizeof(char_t *)); + *argp = cgiPath; + n = 1; + if (gstrchr(query, '=') == NULL) { + websDecodeUrl(query, query, gstrlen(query)); + for (cp = gstrtok(query, T(" ")); cp != NULL; ) { + *(argp+n) = cp; + n++; + if (n >= argpsize) { + argpsize *= 2; + argp = brealloc(B_L, argp, argpsize * sizeof(char_t *)); + } + cp = gstrtok(NULL, T(" ")); + } + } + *(argp+n) = NULL; +/* + * Add all CGI variables to the environment strings to be passed + * to the spawned CGI process. This includes a few we don't + * already have in the symbol table, plus all those that are in + * the cgiVars symbol table. envp will point to a balloc'd array of + * pointers. Each pointer will point to a balloc'd string containing + * the keyword value pair in the form keyword=value. Since we don't + * know ahead of time how many environment strings there will be the + * for loop includes logic to grow the array size via brealloc. + */ + envpsize = WEBS_SYM_INIT; + envp = balloc(B_L, envpsize * sizeof(char_t *)); + n = 0; + fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("PATH_TRANSLATED"), cgiPath); + n++; + fmtAlloc(envp+n, FNAMESIZE, T("%s=%s/%s"),T("SCRIPT_NAME"), + CGI_BIN, cgiName); + n++; + fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("REMOTE_USER"), wp->userName); + n++; + fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("AUTH_TYPE"), wp->authType); + n++; + for (s = symFirst(wp->cgiVars); s != NULL; s = symNext(wp->cgiVars)) { + if (s->content.valid && s->content.type == string && + gstrcmp(s->name.value.string, T("REMOTE_HOST")) != 0 && + gstrcmp(s->name.value.string, T("HTTP_AUTHORIZATION")) != 0) { + fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"), s->name.value.string, + s->content.value.string); + n++; + if (n >= envpsize) { + envpsize *= 2; + envp = brealloc(B_L, envp, envpsize * sizeof(char_t *)); + } + } + } + *(envp+n) = NULL; +/* + * Create temporary file name(s) for the child's stdin and stdout. + * For POST data the stdin temp file (and name) should already exist. + */ + if (wp->cgiStdin == NULL) { + wp->cgiStdin = websGetCgiCommName(); + } + stdIn = wp->cgiStdin; + stdOut = websGetCgiCommName(); +/* + * Now launch the process. If not successful, do the cleanup of resources. + * If successful, the cleanup will be done after the process completes. + */ + if ((pHandle = websLaunchCgiProc(cgiPath, argp, envp, stdIn, stdOut)) + == -1) { + websError(wp, 200, T("failed to spawn CGI task")); + for (ep = envp; *ep != NULL; ep++) { + bfreeSafe(B_L, *ep); + } + bfreeSafe(B_L, cgiPath); + bfreeSafe(B_L, argp); + bfreeSafe(B_L, envp); + bfreeSafe(B_L, stdOut); + } else { +/* + * If the spawn was successful, put this wp on a queue to be + * checked for completion. + */ + cid = hAllocEntry((void***) &cgiList, &cgiMax, sizeof(cgiRec)); + cgip = cgiList[cid]; + cgip->handle = pHandle; + cgip->stdIn = stdIn; + cgip->stdOut = stdOut; + cgip->cgiPath = cgiPath; + cgip->argp = argp; + cgip->envp = envp; + cgip->wp = wp; + cgip->fplacemark = 0; + websTimeoutCancel(wp); + } +/* + * Restore the current working directory after spawning child CGI + */ + gchdir(cwd); + return 1; +} + + + +/******************************************************************************/ +/* + * Any entry in the cgiList need to be checked to see if it has + */ +void websCgiGatherOutput (cgiRec *cgip) +{ + gstat_t sbuf; + char_t cgiBuf[FNAMESIZE]; + if ((gstat(cgip->stdOut, &sbuf) == 0) && + (sbuf.st_size > cgip->fplacemark)) { + int fdout; + fdout = gopen(cgip->stdOut, O_RDONLY | O_BINARY, 0444 ); +/* + * Check to see if any data is available in the + * output file and send its contents to the socket. + */ + if (fdout >= 0) { + webs_t wp = cgip->wp; + int nRead; +/* + * Write the HTTP header on our first pass + */ + if (cgip->fplacemark == 0) { + websWrite(wp, T("HTTP/1.0 200 OK\r\n")); + } + glseek(fdout, cgip->fplacemark, SEEK_SET); + while ((nRead = gread(fdout, cgiBuf, FNAMESIZE)) > 0) { + websWriteBlock(wp, cgiBuf, nRead); + cgip->fplacemark += nRead; + } + gclose(fdout); + } + } +} + + + +/******************************************************************************/ +/* + * Any entry in the cgiList need to be checked to see if it has + * completed, and if so, process its output and clean up. + */ +void websCgiCleanup() +{ + cgiRec *cgip; + webs_t wp; + char_t **ep; + int cid, nTries; + for (cid = 0; cid < cgiMax; cid++) { + if ((cgip = cgiList[cid]) != NULL) { + wp = cgip->wp; + websCgiGatherOutput (cgip); + if (websCheckCgiProc(cgip->handle) == 0) { +/* + * We get here if the CGI process has terminated. Clean up. + */ + nTries = 0; +/* + * Make sure we didn't miss something during a task switch. + * Maximum wait is 100 times 10 msecs (1 second). + */ + while ((cgip->fplacemark == 0) && (nTries < 100)) { + websCgiGatherOutput(cgip); +/* + * There are some cases when we detect app exit + * before the file is ready. + */ + if (cgip->fplacemark == 0) { +#ifdef WIN + Sleep(10); +#endif /* WIN*/ + } + nTries++; + } + if (cgip->fplacemark == 0) { + websError(wp, 200, T("CGI generated no output")); + } else { + websDone(wp, 200); + } +/* + * Remove the temporary re-direction files + */ + gunlink(cgip->stdIn); + gunlink(cgip->stdOut); +/* + * Free all the memory buffers pointed to by cgip. + * The stdin file name (wp->cgiStdin) gets freed as + * part of websFree(). + */ + cgiMax = hFree((void***) &cgiList, cid); + for (ep = cgip->envp; ep != NULL && *ep != NULL; ep++) { + bfreeSafe(B_L, *ep); + } + bfreeSafe(B_L, cgip->cgiPath); + bfreeSafe(B_L, cgip->argp); + bfreeSafe(B_L, cgip->envp); + bfreeSafe(B_L, cgip->stdOut); + bfreeSafe(B_L, cgip); + } + } + } +} +/******************************************************************************/ + diff --git a/cleopatre/application/spidgoahead/default.c b/cleopatre/application/spidgoahead/default.c new file mode 100644 index 0000000000..a2ba7def3e --- /dev/null +++ b/cleopatre/application/spidgoahead/default.c @@ -0,0 +1,603 @@ +/* + * default.c -- Default URL handler. Includes support for ASP. + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: default.c,v 1.9 2003/04/11 18:10:28 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * This module provides default URL handling and Active Server Page support. + * + * In many cases we don't check the return code of calls to websWrite as + * it is easier, smaller and non-fatal to continue even when the requesting + * browser has gone away. + */ + +/********************************* Includes ***********************************/ + +#include "wsIntrn.h" + +/*********************************** Locals ***********************************/ + +static char_t *websDefaultPage; /* Default page name */ +static char_t *websDefaultDir; /* Default Web page directory */ + +/**************************** Forward Declarations ****************************/ + +static void websDefaultWriteEvent(webs_t wp); + +/*********************************** Code *************************************/ +/* + * Process a default URL request. This will validate the URL and handle "../" + * and will provide support for Active Server Pages. As the handler is the + * last handler to run, it always indicates that it has handled the URL + * by returning 1. + */ + +int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, + char_t *url, char_t *path, char_t *query) +{ + websStatType sbuf; + char_t *lpath, *tmp, *date; + int bytes, flags, nchars; + + a_assert(websValid(wp)); + a_assert(url && *url); + a_assert(path); + a_assert(query); + +/* + * Validate the URL and ensure that ".."s don't give access to unwanted files + */ + flags = websGetRequestFlags(wp); + + if (websValidateUrl(wp, path) < 0) + { + /* + * preventing a cross-site scripting exploit -- you may restore the + * following line of code to revert to the original behavior... + */ + /*websError(wp, 500, T("Invalid URL %s"), url);*/ + websError(wp, 500, T("Invalid URL")); + return 1; + } + lpath = websGetRequestLpath(wp); + nchars = gstrlen(lpath) - 1; + if (lpath[nchars] == '/' || lpath[nchars] == '\\') { + lpath[nchars] = '\0'; + } + +/* + * If the file is a directory, redirect using the nominated default page + */ + if (websPageIsDirectory(lpath)) { + nchars = gstrlen(path); + if (path[nchars-1] == '/' || path[nchars-1] == '\\') { + path[--nchars] = '\0'; + } + nchars += gstrlen(websDefaultPage) + 2; + fmtAlloc(&tmp, nchars, T("%s/%s"), path, websDefaultPage); + websRedirect(wp, tmp); + bfreeSafe(B_L, tmp); + return 1; + } + +/* + * Open the document. Stat for later use. + */ + if (websPageOpen(wp, lpath, path, SOCKET_RDONLY | SOCKET_BINARY, + 0666) < 0) + { + /* 10 Dec 02 BgP -- according to + * , + * the proper code to return here is NOT 400 (old code), which is used + * to indicate a malformed request. Here, the request is good, but the + * error we need to tell the client about is 404 (Not Found). + */ + /* + * 17 Mar 03 BgP -- prevent a cross-site scripting exploit + websError(wp, 404, T("Cannot open URL %s"), url); + */ + + websError(wp, 404, T("Cannot open URL")); + return 1; + } + + if (websPageStat(wp, lpath, path, &sbuf) < 0) { + /* + * 17 Mar 03 BgP + * prevent a cross-site scripting exploit + websError(wp, 400, T("Cannot stat page for URL %s"), url); + */ + websError(wp, 400, T("Cannot stat page for URL")); + return 1; + } + +/* + * If the page has not been modified since the user last received it and it + * is not dynamically generated each time (ASP), then optimize request by + * sending a 304 Use local copy response + */ + websStats.localHits++; +#ifdef WEBS_IF_MODIFIED_SUPPORT + if (flags & WEBS_IF_MODIFIED && !(flags & WEBS_ASP)) { + 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: %s\r\n"), WEBS_NAME); + + if (flags & WEBS_KEEP_ALIVE) { + websWrite(wp, T("Connection: keep-alive\r\n")); + } + websWrite(wp, T("\r\n")); + websSetRequestFlags(wp, flags |= WEBS_HEADER_DONE); + websDone(wp, 304); + return 1; + } + } +#endif + +/* + * Output the normal HTTP response header + */ + if ((date = websGetDateString(NULL)) != NULL) { + websWrite(wp, T("HTTP/1.0 200 OK\r\nDate: %s\r\n"), date); + +/* + * By license terms the following line of code must not be modified. + */ + websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); + bfree(B_L, date); + } + flags |= WEBS_HEADER_DONE; + +/* + * If this is an ASP request, ensure the remote browser doesn't cache it. + * Send back both HTTP/1.0 and HTTP/1.1 cache control directives + */ + if (flags & WEBS_ASP) { + bytes = 0; + websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n")); + + } else { + if ((date = websGetDateString(&sbuf)) != NULL) { + websWrite(wp, T("Last-modified: %s\r\n"), date); + bfree(B_L, date); + } + bytes = sbuf.size; + } + + if (bytes) { + websWrite(wp, T("Content-length: %d\r\n"), bytes); + websSetRequestBytes(wp, bytes); + } + websWrite(wp, T("Content-type: %s\r\n"), websGetRequestType(wp)); + + if ((flags & WEBS_KEEP_ALIVE) && !(flags & WEBS_ASP)) { + websWrite(wp, T("Connection: keep-alive\r\n")); + } + 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) { + if (websAspRequest(wp, lpath) < 0) { + return 1; + } + 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; +} + + +#ifdef WIN32 + +static int badPath(char_t* path, char_t* badPath, int badLen) +{ + int retval = 0; + int len = gstrlen(path); + int i = 0; + + if (len <= badLen +1) + { + for (i = 0; i < badLen; ++i) + { + if (badPath[i] != gtolower(path[i])) + { + return 0; + } + } + /* if we get here, the first 'badLen' characters match. + * If 'path' is 1 character larger than 'badPath' and that extra + * character is NOT a letter or a number, we have a bad path. + */ + retval = 1; + if (badLen + 1 == len) + { + /* e.g. path == "aux:" */ + if (gisalnum(path[len-1])) + { + /* the last character is alphanumeric, so we let this path go + * through. + */ + retval = 0; + } + } + } + + return retval; +} + + +static int isBadWindowsPath(char_t** parts, int partCount) +{ + /* + * If we're running on Windows 95/98/ME, malicious users can crash the + * OS by requesting an URL with any of several reserved DOS device names + * in them (AUX, NUL, etc.). + * If we're running on any of those OS versions, we scan the URL + * for paths with any of these elements before + * trying to access them. If any of the subdirectory names match one + * of our prohibited links, we declare this to be a 'bad' path, and return + * 1 to indicate this. This may be an overly heavy-handed approach, but should + * prevent the DOS attack. + * NOTE that this function is only compiled in when we are running on Win32, + * and only has an effect when we are running on Win95/98, or ME. On all other + * versions of Windows, we check the version info, and return 0 immediately. + * + * According to http://packetstormsecurity.nl/0003-exploits/SCX-SA-01.txt: + + * II. Problem Description + * When the Microsoft Windows operating system is parsing a path that + * is being crafted like "c:\[device]\[device]" it will halt, and crash + * the entire operating system. + * Four device drivers have been found to crash the system. The CON, + * NUL, AUX, CLOCK$ and CONFIG$ are the two device drivers which are + * known to crash. Other devices as LPT[x]:, COM[x]: and PRN have not + * been found to crash the system. + * Making combinations as CON\NUL, NUL\CON, AUX\NUL, ... seems to + * crash Ms Windows as well. + * Calling a path such as "C:\CON\[filename]" won't result in a crash + * but in an error-message. Creating the map "CON", "CLOCK$", "AUX" + * "NUL" or "CONFIG$" will also result in a simple error-message + * saying: ''creating that map isn't allowed''. + * + * returns 1 if it finds a bad path element. + */ + OSVERSIONINFO version; + int i; + version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (GetVersionEx(&version)) + { + if (VER_PLATFORM_WIN32_NT != version.dwPlatformId) + { + /* + * we are currently running on 95/98/ME. + */ + for (i = 0; i < partCount; ++i) + { + /* + * check against the prohibited names. If any of our requested + * subdirectories match any of these, return '1' immediately. + */ + + if ( + (badPath(parts[i], T("con"), 3)) || + (badPath(parts[i], T("nul"), 3)) || + (badPath(parts[i], T("aux"), 3)) || + (badPath(parts[i], T("clock$"), 6)) || + (badPath(parts[i], T("config$"), 7)) ) + { + return 1; + } + } + } + } + /* + * either we're not on one of the bad OS versions, or the request has + * no problems. + */ + return 0; +} +#endif + + +/******************************************************************************/ +/* + * Validate the URL path and process ".." path segments. Return -1 if the URL + * is bad. + */ + +int websValidateUrl(webs_t wp, char_t *path) +{ + /* + Thanks to Dhanwa T (dhanwa@polyserve.com) for this fix -- previously, + if an URL was requested having more than (the hardcoded) 64 parts, + the webServer would experience a hard crash as it attempted to + write past the end of the array 'parts'. + */ + +#define kMaxUrlParts 64 + char_t *parts[kMaxUrlParts]; /* Array of ptr's to URL parts */ + char_t *token, *dir, *lpath; + int i, len, npart; + + a_assert(websValid(wp)); + a_assert(path); + + dir = websGetRequestDir(wp); + if (dir == NULL || *dir == '\0') { + return -1; + } + +/* + * Copy the string so we don't destroy the original + */ + path = bstrdup(B_L, path); + websDecodeUrl(path, path, gstrlen(path)); + + len = npart = 0; + parts[0] = NULL; + + /* + * 22 Jul 02 -- there were reports that a directory traversal exploit was + * possible in the WebServer running under Windows if directory paths + * outside the server's specified root web were given by URL-encoding the + * backslash character, like: + * + * GoAhead is vulnerable to a directory traversal bug. A request such as + * + * GoAhead-server/../../../../../../../ results in an error message + * 'Cannot open URL'. + + * However, by encoding the '/' character, it is possible to break out of + * the + * web root and read arbitrary files from the server. + * Hence a request like: + * + * GoAhead-server/..%5C..%5C..%5C..%5C..%5C..%5C/winnt/win.ini returns the + * contents of the win.ini file. + * (Note that the description uses forward slashes (0x2F), but the example + * uses backslashes (0x5C). In my tests, forward slashes are correctly + * trapped, but backslashes are not. The code below substitutes forward + * slashes for backslashes before attempting to validate that there are no + * unauthorized paths being accessed. + */ + token = gstrchr(path, '\\'); + while (token != NULL) + { + *token = '/'; + token = gstrchr(token, '\\'); + } + + token = gstrtok(path, 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 (npart >= kMaxUrlParts) + { + /* + * malformed URL -- too many parts for us to process. + */ + bfree(B_L, path); + return -1; + } + 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("/")); + } + +#ifdef WIN32 + if (isBadWindowsPath(parts, npart)) + { + bfree(B_L, path); + return -1; + } + +#endif + +/* + * Create local path for document. Need extra space all "/" and null. + */ + if (npart || (gstrcmp(path, T("/")) == 0) || (path[0] == '\0')) + { + lpath = balloc(B_L, (gstrlen(dir) + 1 + len + 1) * sizeof(char_t)); + gstrcpy(lpath, dir); + + for (i = 0; i < npart; i++) + { + gstrcat(lpath, T("/")); + gstrcat(lpath, parts[i]); + } + websSetRequestLpath(wp, lpath); + bfree(B_L, path); + bfree(B_L, lpath); + + } + else + { + bfree(B_L, path); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * Do output back to the browser in the background. This is a socket + * write handler. + */ + +static void websDefaultWriteEvent(webs_t wp) +{ + int len, wrote, flags, bytes, written; + char *buf; + + a_assert(websValid(wp)); + + flags = websGetRequestFlags(wp); + + websSetTimeMark(wp); + + wrote = bytes = 0; + written = websGetRequestWritten(wp); + +/* + * We only do this for non-ASP documents + */ + if ( !(flags & WEBS_ASP)) { + bytes = websGetRequestBytes(wp); +/* + * 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 = websWriteDataNonBlock(wp, buf, len)) < 0) { + break; + } + written += wrote; + if (wrote != len) { + websPageSeek(wp, - (len - wrote)); + break; + } + } +/* + * Safety. If we are at EOF, we must be done + */ + if (len == 0) { + a_assert(written >= bytes); + written = bytes; + } + bfree(B_L, buf); + } + } + +/* + * We're done if an error, or all bytes output + */ + websSetRequestWritten(wp, written); + if (wrote < 0 || written >= bytes) { + websDone(wp, 200); + } +} + +/******************************************************************************/ +/* + * Closing down. Free resources. + */ + +void websDefaultClose() +{ + if (websDefaultPage) { + bfree(B_L, websDefaultPage); + websDefaultPage = NULL; + } + if (websDefaultDir) { + bfree(B_L, websDefaultDir); + websDefaultDir = NULL; + } +} + +/******************************************************************************/ +/* + * Get the default page for URL requests ending in "/" + */ + +char_t *websGetDefaultPage() +{ + return websDefaultPage; +} + +/******************************************************************************/ +/* + * Get the default web directory + */ + +char_t *websGetDefaultDir() +{ + return websDefaultDir; +} + +/******************************************************************************/ +/* + * Set the default page for URL requests ending in "/" + */ + +void websSetDefaultPage(char_t *page) +{ + a_assert(page && *page); + + if (websDefaultPage) { + bfree(B_L, websDefaultPage); + } + websDefaultPage = bstrdup(B_L, page); +} + +/******************************************************************************/ +/* + * Set the default web directory + */ + +void websSetDefaultDir(char_t *dir) +{ + a_assert(dir && *dir); + if (websDefaultDir) { + bfree(B_L, websDefaultDir); + } + websDefaultDir = bstrdup(B_L, dir); +} + +/******************************************************************************/ + + diff --git a/cleopatre/application/spidgoahead/default.css b/cleopatre/application/spidgoahead/default.css new file mode 100644 index 0000000000..9b6f39c035 --- /dev/null +++ b/cleopatre/application/spidgoahead/default.css @@ -0,0 +1,188 @@ +/* +:Author: David Goodger +:Contact: goodger@users.sourceforge.net +:date: $Date: 2003/03/18 21:43:49 $ +:version: $Revision: 1.1 $ +:copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. +*/ + +.first { + margin-top: 0 } + +.last { + margin-bottom: 0 } + +a.toc-backref { + text-decoration: none ; + color: black } + +dd { + margin-bottom: 0.5em } + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.attention, div.caution, div.danger, div.error, div.hint, +div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +div.hint p.admonition-title, div.important p.admonition-title, +div.note p.admonition-title, div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em } + +div.footer, div.header { + font-size: smaller } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr { + width: 75% } + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font-family: serif ; + font-size: 100% } + +pre.line-block { + font-family: serif ; + font-size: 100% } + +pre.literal-block, pre.doctest-block { + margin-left: 2em ; + margin-right: 2em ; + background-color: #eeeeee } + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option-argument { + font-style: italic } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +table { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.citation { + border-left: solid thin gray ; + padding-left: 0.5ex } + +table.docinfo { + margin: 2em 4em } + +table.footnote { + border-left: solid thin black ; + padding-left: 0.5ex } + +td, th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +th.docinfo-name, th.field-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap } + +h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { + font-size: 100% } + +tt { + background-color: #eeeeee } + +ul.auto-toc { + list-style-type: none } diff --git a/cleopatre/application/spidgoahead/ej.h b/cleopatre/application/spidgoahead/ej.h new file mode 100644 index 0000000000..ddcc4633d3 --- /dev/null +++ b/cleopatre/application/spidgoahead/ej.h @@ -0,0 +1,47 @@ +/* + * ej.h -- Ejscript(TM) header + * + * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved. + * + * See the file "license.txt" for information on usage and redistribution + * + * $Id: ej.h,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +#ifndef _h_EJ +#define _h_EJ 1 + +/******************************** Description *********************************/ + +/* + * GoAhead Ejscript(TM) header. This defines the Ejscript API and internal + * structures. + */ + +/********************************* Includes ***********************************/ + +#ifndef UEMF + #include "basic/basic.h" + #include "emf/emf.h" +#else + #include "uemf.h" +#endif + +/********************************** Defines ***********************************/ + +/******************************** 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 ejSetGlobalFunction(int eid, char_t *name, + int (*fn)(int eid, void *handle, int argc, char_t **argv)); +extern void ejSetVar(int eid, char_t *var, char_t *value); +extern int ejGetVar(int eid, char_t *var, char_t **value); +extern char_t *ejEval(int eid, char_t *script, char_t **emsg); + +#endif /* _h_EJ */ + +/*****************************************************************************/ + diff --git a/cleopatre/application/spidgoahead/ejIntrn.h b/cleopatre/application/spidgoahead/ejIntrn.h new file mode 100644 index 0000000000..6459683ee3 --- /dev/null +++ b/cleopatre/application/spidgoahead/ejIntrn.h @@ -0,0 +1,231 @@ +/* + * ejIntrn.h -- Ejscript(TM) header + * + * Copyright (c) GoAhead Software, Inc., 1992-2000 + * + * See the file "license.txt" for information on usage and redistribution + * + * $Id: ejIntrn.h,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +#ifndef _h_EJINTERNAL +#define _h_EJINTERNAL 1 + +/******************************** Description *********************************/ + +/* + * GoAhead Ejscript(TM) header. This defines the Ejscript API and internal + * structures. + */ + +/********************************* Includes ***********************************/ + +#include +#include +#include + +#ifdef CE +#ifndef UEMF + #include +#endif +#endif + +#ifdef LYNX + #include +#endif + +#ifdef QNX4 + #include +#endif + +#ifdef UEMF + #include "uemf.h" +#else + #include + #include + #include "basic/basicInternal.h" + #include "emf/emfInternal.h" +#endif + +#include "ej.h" + +/********************************** Defines ***********************************/ +/* + * Constants + */ +#define EJ_INC 110 /* Growth for tags/tokens */ +#define EJ_SCRIPT_INC 1023 /* Growth for ej scripts */ +#define EJ_OFFSET 1 /* hAlloc doesn't like 0 entries */ +#define EJ_MAX_RECURSE 100 /* Sanity for maximum recursion */ + +/* + * Ejscript Lexical analyser tokens + */ +#define TOK_ERR -1 /* Any error */ +#define TOK_LPAREN 1 /* ( */ +#define TOK_RPAREN 2 /* ) */ +#define TOK_IF 3 /* if */ +#define TOK_ELSE 4 /* else */ +#define TOK_LBRACE 5 /* { */ +#define TOK_RBRACE 6 /* } */ +#define TOK_LOGICAL 7 /* ||, &&, ! */ +#define TOK_EXPR 8 /* +, -, /, % */ +#define TOK_SEMI 9 /* ; */ +#define TOK_LITERAL 10 /* literal string */ +#define TOK_FUNCTION 11 /* function name */ +#define TOK_NEWLINE 12 /* newline white space */ +#define TOK_ID 13 /* function name */ +#define TOK_EOF 14 /* End of script */ +#define TOK_COMMA 15 /* Comma */ +#define TOK_VAR 16 /* var */ +#define TOK_ASSIGNMENT 17 /* = */ +#define TOK_FOR 18 /* for */ +#define TOK_INC_DEC 19 /* ++, -- */ +#define TOK_RETURN 20 /* return */ + +/* + * Expression operators + */ +#define EXPR_LESS 1 /* < */ +#define EXPR_LESSEQ 2 /* <= */ +#define EXPR_GREATER 3 /* > */ +#define EXPR_GREATEREQ 4 /* >= */ +#define EXPR_EQ 5 /* == */ +#define EXPR_NOTEQ 6 /* != */ +#define EXPR_PLUS 7 /* + */ +#define EXPR_MINUS 8 /* - */ +#define EXPR_DIV 9 /* / */ +#define EXPR_MOD 10 /* % */ +#define EXPR_LSHIFT 11 /* << */ +#define EXPR_RSHIFT 12 /* >> */ +#define EXPR_MUL 13 /* * */ +#define EXPR_ASSIGNMENT 14 /* = */ +#define EXPR_INC 15 /* ++ */ +#define EXPR_DEC 16 /* -- */ +#define EXPR_BOOL_COMP 17 /* ! */ +/* + * Conditional operators + */ +#define COND_AND 1 /* && */ +#define COND_OR 2 /* || */ +#define COND_NOT 3 /* ! */ + +/* + * States + */ +#define STATE_ERR -1 /* Error state */ +#define STATE_EOF 1 /* End of file */ +#define STATE_COND 2 /* Parsing a "(conditional)" stmt */ +#define STATE_COND_DONE 3 +#define STATE_RELEXP 4 /* Parsing a relational expr */ +#define STATE_RELEXP_DONE 5 +#define STATE_EXPR 6 /* Parsing an expression */ +#define STATE_EXPR_DONE 7 +#define STATE_STMT 8 /* Parsing General statement */ +#define STATE_STMT_DONE 9 +#define STATE_STMT_BLOCK_DONE 10 /* End of block "}" */ +#define STATE_ARG_LIST 11 /* Function arg list */ +#define STATE_ARG_LIST_DONE 12 +#define STATE_DEC_LIST 16 /* Declaration list */ +#define STATE_DEC_LIST_DONE 17 +#define STATE_DEC 18 +#define STATE_DEC_DONE 19 + +#define STATE_RET 20 /* Return statement */ + +#define STATE_BEGIN STATE_STMT + +/* + * Flags. Used in ej_t and as parameter to parse() + */ +#define FLAGS_EXE 0x1 /* Execute statements */ +#define FLAGS_VARIABLES 0x2 /* Allocated variables store */ +#define FLAGS_FUNCTIONS 0x4 /* Allocated function store */ + +/* + * Function call structure + */ +typedef struct { + char_t *fname; /* Function name */ + char_t **args; /* Args for function (halloc) */ + int nArgs; /* Number of args */ +} ejfunc_t; + +/* + * EJ evaluation block structure + */ +typedef struct ejEval { + ringq_t tokbuf; /* Current token */ + ringq_t script; /* Input script for parsing */ + char_t *putBackToken; /* Putback token string */ + int putBackTokenId; /* Putback token ID */ + char_t *line; /* Current line */ + int lineLength; /* Current line length */ + int lineNumber; /* Parse line number */ + int lineColumn; /* Column in line */ +} ejinput_t; + +/* + * Per Ejscript session structure + */ +typedef struct ej { + ejinput_t *input; /* Input evaluation block */ + sym_fd_t functions; /* Symbol table for functions */ + sym_fd_t *variables; /* hAlloc list of variables */ + int variableMax; /* Number of entries */ + ejfunc_t *func; /* Current function */ + char_t *result; /* Current expression result */ + char_t *error; /* Error message */ + char_t *token; /* Pointer to token string */ + int tid; /* Current token id */ + int eid; /* Halloc handle */ + int flags; /* Flags */ + int userHandle; /* User defined handle */ +} ej_t; + +/******************************** Prototypes **********************************/ + +extern int ejOpenBlock(int eid); +extern int ejCloseBlock(int eid, int vid); +extern char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg); +#ifndef __NO_EJ_FILE +extern char_t *ejEvalFile(int eid, char_t *path, char_t **emsg); +#endif +extern int ejRemoveGlobalFunction(int eid, char_t *name); +extern void *ejGetGlobalFunction(int eid, char_t *name); +extern int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, + int (*fn)(int eid, void *handle, int argc, char_t **argv)); +extern void ejError(ej_t* ep, char_t* fmt, ...); +extern void ejSetUserHandle(int eid, int handle); +extern int ejGetUserHandle(int eid); +extern int ejGetLineNumber(int eid); +extern char_t *ejGetResult(int eid); +extern void ejSetLocalVar(int eid, char_t *var, char_t *value); +extern void ejSetGlobalVar(int eid, char_t *var, char_t *value); + +extern int ejLexOpen(ej_t* ep); +extern void ejLexClose(ej_t* ep); +extern int ejLexOpenScript(ej_t* ep, char_t *script); +extern void ejLexCloseScript(ej_t* ep); +extern void ejLexSaveInputState(ej_t* ep, ejinput_t* state); +extern void ejLexFreeInputState(ej_t* ep, ejinput_t* state); +extern void ejLexRestoreInputState(ej_t* ep, ejinput_t* state); +extern int ejLexGetToken(ej_t* ep, int state); +extern void ejLexPutbackToken(ej_t* ep, int tid, char_t *string); + +extern sym_fd_t ejGetVariableTable(int eid); +extern sym_fd_t ejGetFunctionTable(int eid); + +extern int ejEmfOpen(int eid); +extern void ejEmfClose(int eid); + +extern int ejEmfDbRead(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbReadKeyed(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbTableGetNrow(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbDeleteRow(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfTrace(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbWrite(int eid, void *handle, int argc, char_t **argv); +extern int ejEmfDbCollectTable(int eid, void *handle, int argc, char_t **argv); + +#endif /* _h_EJINTERNAL */ + diff --git a/cleopatre/application/spidgoahead/ejlex.c b/cleopatre/application/spidgoahead/ejlex.c new file mode 100644 index 0000000000..14b0d68b0d --- /dev/null +++ b/cleopatre/application/spidgoahead/ejlex.c @@ -0,0 +1,722 @@ +/* + * ejlex.c -- Ejscript(TM) Lexical Analyser + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: ejlex.c,v 1.4 2002/10/24 14:44:50 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * Ejscript lexical analyser. This implementes a lexical analyser for a + * a subset of the JavaScript language. + */ + +/********************************** Includes **********************************/ + +#include "ejIntrn.h" + +#ifdef UEMF + #include "uemf.h" +#else + #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 ***********************************/ +/* + * Setup the lexical analyser + */ + +int ejLexOpen(ej_t* ep) +{ + return 0; +} + +/******************************************************************************/ +/* + * Close the lexicial analyser + */ + +void ejLexClose(ej_t* ep) +{ +} + +/******************************************************************************/ +/* + * Open a new input script + */ + +int ejLexOpenScript(ej_t* ep, char_t *script) +{ + ejinput_t *ip; + + a_assert(ep); + a_assert(script); + + if ((ep->input = balloc(B_L, sizeof(ejinput_t))) == NULL) { + return -1; + } + ip = ep->input; + memset(ip, 0, sizeof(*ip)); + + a_assert(ip); + a_assert(ip->putBackToken == NULL); + a_assert(ip->putBackTokenId == 0); + +/* + * Create the parse token buffer and script buffer + */ + if (ringqOpen(&ip->tokbuf, EJ_INC, -1) < 0) { + return -1; + } + 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); + + ip->lineNumber = 1; + ip->lineLength = 0; + ip->lineColumn = 0; + ip->line = NULL; + + return 0; +} + +/******************************************************************************/ +/* + * Close the input script + */ + +void ejLexCloseScript(ej_t* ep) +{ + ejinput_t *ip; + + a_assert(ep); + + ip = ep->input; + a_assert(ip); + + if (ip->putBackToken) { + bfree(B_L, ip->putBackToken); + ip->putBackToken = NULL; + } + ip->putBackTokenId = 0; + + if (ip->line) { + bfree(B_L, ip->line); + ip->line = NULL; + } + + ringqClose(&ip->tokbuf); + ringqClose(&ip->script); + + bfree(B_L, ip); +} + +/******************************************************************************/ +/* + * Save the input state + */ + +void ejLexSaveInputState(ej_t* ep, ejinput_t* state) +{ + ejinput_t *ip; + + a_assert(ep); + + ip = ep->input; + a_assert(ip); + + *state = *ip; + if (ip->putBackToken) { + state->putBackToken = bstrdup(B_L, ip->putBackToken); + } +} + +/******************************************************************************/ +/* + * Restore the input state + */ + +void ejLexRestoreInputState(ej_t* ep, ejinput_t* state) +{ + ejinput_t *ip; + + a_assert(ep); + + ip = ep->input; + a_assert(ip); + + ip->tokbuf = state->tokbuf; + ip->script = state->script; + ip->putBackTokenId = state->putBackTokenId; + if (ip->putBackToken) { + bfree(B_L, ip->putBackToken); + } + if (state->putBackToken) { + ip->putBackToken = bstrdup(B_L, state->putBackToken); + } +} + +/******************************************************************************/ +/* + * Free a saved input state + */ + +void ejLexFreeInputState(ej_t* ep, ejinput_t* state) +{ + if (state->putBackToken) { + bfree(B_L, state->putBackToken); + state->putBackToken = NULL; + } +} + +/******************************************************************************/ +/* + * Get the next Ejscript token + */ + +int ejLexGetToken(ej_t* ep, int state) +{ + ep->tid = getLexicalToken(ep, state); + /* + * commented out 04 Apr 02 Bg Porter -- we found a case where very long + * arguments to write() were being corrupted downstream in the trace call + * (the ep->token pointer was being overwritten with the trace message. + * restore this if it's useful for your debugging. + trace(9, T("ejGetToken: %d, \"%s\"\n"), ep->tid, ep->token); + */ + return ep->tid; +} + +/******************************************************************************/ +/* + * Get the next Ejscript token + */ + +static int getLexicalToken(ej_t* ep, int state) +{ + ringq_t *inq, *tokq; + ejinput_t* ip; + int done, tid, c, quote, style; + + a_assert(ep); + ip = ep->input; + a_assert(ip); + + inq = &ip->script; + tokq = &ip->tokbuf; + + ep->tid = -1; + tid = -1; + ep->token = T(""); + + ringqFlush(tokq); + + if (ip->putBackTokenId > 0) { + ringqPutStr(tokq, ip->putBackToken); + tid = ip->putBackTokenId; + ip->putBackTokenId = 0; + ep->token = (char_t*) tokq->servp; + return tid; + } + + if ((c = inputGetc(ep)) < 0) { + return TOK_EOF; + } + + for (done = 0; !done; ) { + switch (c) { + case -1: + return TOK_EOF; + + case ' ': + case '\t': + case '\r': + do { + if ((c = inputGetc(ep)) < 0) + break; + } while (c == ' ' || c == '\t' || c == '\r'); + break; + + case '\n': + return TOK_NEWLINE; + + case '(': + tokenAddChar(ep, c); + return TOK_LPAREN; + + case ')': + tokenAddChar(ep, c); + return TOK_RPAREN; + + case '{': + tokenAddChar(ep, c); + return TOK_LBRACE; + + case '}': + tokenAddChar(ep, c); + return TOK_RBRACE; + + case '+': + if ((c = inputGetc(ep)) < 0) { + ejError(ep, T("Syntax Error")); + return TOK_ERR; + } + if (c != '+' ) { + inputPutback(ep, c); + tokenAddChar(ep, EXPR_PLUS); + return TOK_EXPR; + } + tokenAddChar(ep, EXPR_INC); + return TOK_INC_DEC; + + case '-': + if ((c = inputGetc(ep)) < 0) { + ejError(ep, T("Syntax Error")); + return TOK_ERR; + } + if (c != '-' ) { + inputPutback(ep, c); + tokenAddChar(ep, EXPR_MINUS); + return TOK_EXPR; + } + tokenAddChar(ep, EXPR_DEC); + return TOK_INC_DEC; + + case '*': + tokenAddChar(ep, EXPR_MUL); + return TOK_EXPR; + + case '%': + tokenAddChar(ep, EXPR_MOD); + return TOK_EXPR; + + case '/': +/* + * Handle the division operator and comments + */ + if ((c = inputGetc(ep)) < 0) { + ejError(ep, T("Syntax Error")); + return TOK_ERR; + } + if (c != '*' && c != '/') { + inputPutback(ep, c); + tokenAddChar(ep, EXPR_DIV); + return TOK_EXPR; + } + style = c; +/* + * Eat comments. Both C and C++ comment styles are supported. + */ + while (1) { + if ((c = inputGetc(ep)) < 0) { + ejError(ep, T("Syntax Error")); + return TOK_ERR; + } + if (c == '\n' && style == '/') { + break; + } else if (c == '*') { + c = inputGetc(ep); + if (style == '/') { + if (c == '\n') { + break; + } + } else { + if (c == '/') { + break; + } + } + } + } +/* + * Continue looking for a token, so get the next character + */ + if ((c = inputGetc(ep)) < 0) { + return TOK_EOF; + } + break; + + case '<': /* < and <= */ + if ((c = inputGetc(ep)) < 0) { + ejError(ep, T("Syntax Error")); + return TOK_ERR; + } + if (c == '<') { + tokenAddChar(ep, EXPR_LSHIFT); + return TOK_EXPR; + } else if (c == '=') { + tokenAddChar(ep, EXPR_LESSEQ); + return TOK_EXPR; + } + tokenAddChar(ep, EXPR_LESS); + inputPutback(ep, c); + return TOK_EXPR; + + case '>': /* > and >= */ + if ((c = inputGetc(ep)) < 0) { + ejError(ep, T("Syntax Error")); + return TOK_ERR; + } + if (c == '>') { + tokenAddChar(ep, EXPR_RSHIFT); + return TOK_EXPR; + } else if (c == '=') { + tokenAddChar(ep, EXPR_GREATEREQ); + return TOK_EXPR; + } + tokenAddChar(ep, EXPR_GREATER); + inputPutback(ep, c); + return TOK_EXPR; + + case '=': /* "==" */ + if ((c = inputGetc(ep)) < 0) { + ejError(ep, T("Syntax Error")); + return TOK_ERR; + } + if (c == '=') { + tokenAddChar(ep, EXPR_EQ); + return TOK_EXPR; + } + inputPutback(ep, c); + return TOK_ASSIGNMENT; + + case '!': /* "!=" or "!"*/ + if ((c = inputGetc(ep)) < 0) { + ejError(ep, T("Syntax Error")); + return TOK_ERR; + } + if (c == '=') { + tokenAddChar(ep, EXPR_NOTEQ); + return TOK_EXPR; + } + inputPutback(ep, c); + tokenAddChar(ep, EXPR_BOOL_COMP); + return TOK_EXPR; + + case ';': + tokenAddChar(ep, c); + return TOK_SEMI; + + case ',': + tokenAddChar(ep, c); + return TOK_COMMA; + + case '|': /* "||" */ + if ((c = inputGetc(ep)) < 0 || c != '|') { + ejError(ep, T("Syntax Error")); + return TOK_ERR; + } + tokenAddChar(ep, COND_OR); + return TOK_LOGICAL; + + case '&': /* "&&" */ + if ((c = inputGetc(ep)) < 0 || c != '&') { + ejError(ep, T("Syntax Error")); + return TOK_ERR; + } + tokenAddChar(ep, COND_AND); + return TOK_LOGICAL; + + case '\"': /* String quote */ + case '\'': + quote = c; + if ((c = inputGetc(ep)) < 0) { + ejError(ep, T("Syntax Error")); + return TOK_ERR; + } + + while (c != quote) { +/* + * 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 { + switch (c) { + case 'n': + c = '\n'; break; + case 'b': + c = '\b'; break; + case 'f': + c = '\f'; break; + case 'r': + c = '\r'; break; + case 't': + c = '\t'; break; + case 'x': +/* + * hex support, \x41 maps to 65 = 'A' + */ + c = charConvert(ep, HEX, 2); + break; + case 'u': +/* + * 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; + } + } + if (tokenAddChar(ep, c) < 0) { + return TOK_ERR; + } + } else { + if (tokenAddChar(ep, c) < 0) { + return TOK_ERR; + } + } + if ((c = inputGetc(ep)) < 0) { + ejError(ep, T("Unmatched Quote")); + return TOK_ERR; + } + } + return TOK_LITERAL; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + do { + if (tokenAddChar(ep, c) < 0) { + return TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) + break; + } while (gisdigit(c)); + inputPutback(ep, c); + return TOK_LITERAL; + + default: +/* + * Identifiers or a function names + */ + while (1) { + if (c == '\\') { +/* + * just ignore any \ characters. + */ + } else if (tokenAddChar(ep, c) < 0) { + break; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + if (!gisalnum(c) && c != '$' && c != '_' && + c != '\\') { + break; + } + } + if (! gisalpha(*tokq->servp) && *tokq->servp != '$' && + *tokq->servp != '_') { + ejError(ep, T("Invalid identifier %s"), tokq->servp); + return TOK_ERR; + } +/* + * Check for reserved words (only "if", "else", "var", "for" + * and "return" at the moment) + */ + if (state == STATE_STMT) { + if (gstrcmp(ep->token, T("if")) == 0) { + return TOK_IF; + } else if (gstrcmp(ep->token, T("else")) == 0) { + return TOK_ELSE; + } else if (gstrcmp(ep->token, T("var")) == 0) { + return TOK_VAR; + } 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. + */ + while (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + if ((c = inputGetc(ep)) < 0) + break; + } + + tid = (c == '(') ? TOK_FUNCTION : TOK_ID; + done++; + } + } + +/* + * Putback the last extra character for next time + */ + inputPutback(ep, c); + return tid; +} + +/******************************************************************************/ +/* + * Putback the last token read + */ + +void ejLexPutbackToken(ej_t* ep, int tid, char_t *string) +{ + ejinput_t* ip; + + a_assert(ep); + ip = ep->input; + a_assert(ip); + + if (ip->putBackToken) { + bfree(B_L, ip->putBackToken); + } + ip->putBackTokenId = tid; + ip->putBackToken = bstrdup(B_L, string); +} + +/******************************************************************************/ +/* + * Add a character to the token ringq buffer + */ + +static int tokenAddChar(ej_t *ep, int c) +{ + ejinput_t* ip; + + a_assert(ep); + ip = ep->input; + a_assert(ip); + + if (ringqPutc(&ip->tokbuf, (char_t) c) < 0) { + ejError(ep, T("Token too big")); + return -1; + } + * ((char_t*) ip->tokbuf.endp) = '\0'; + ep->token = (char_t*) ip->tokbuf.servp; + + return 0; +} + +/******************************************************************************/ +/* + * Get another input character + */ + +static int inputGetc(ej_t* ep) +{ + ejinput_t *ip; + int c, len; + + a_assert(ep); + ip = ep->input; + + if ((len = ringqLen(&ip->script)) == 0) { + return -1; + } + + c = ringqGetc(&ip->script); + + if (c == '\n') { + ip->lineNumber++; + ip->lineColumn = 0; + } else { + if ((ip->lineColumn + 2) >= ip->lineLength) { + ip->lineLength += EJ_INC; + ip->line = brealloc(B_L, ip->line, ip->lineLength * sizeof(char_t)); + } + ip->line[ip->lineColumn++] = c; + ip->line[ip->lineColumn] = '\0'; + } + return c; +} + +/******************************************************************************/ +/* + * Putback a character onto the input queue + */ + +static void inputPutback(ej_t* ep, int c) +{ + ejinput_t *ip; + + a_assert(ep); + + ip = ep->input; + ringqInsertc(&ip->script, (char_t) c); + ip->lineColumn--; + ip->line[ip->lineColumn] = '\0'; +} + +/******************************************************************************/ +/* + * 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/cleopatre/application/spidgoahead/ejparse.c b/cleopatre/application/spidgoahead/ejparse.c new file mode 100644 index 0000000000..efe9f76374 --- /dev/null +++ b/cleopatre/application/spidgoahead/ejparse.c @@ -0,0 +1,1805 @@ +/* + * ejparse.c -- Ejscript(TM) Parser + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: ejparse.c,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * Ejscript parser. This implementes a subset of the JavaScript language. + * Multiple Ejscript parsers can be opened at a time. + */ + +/********************************** Includes **********************************/ + +#include "ejIntrn.h" + +#ifdef CE + #include "CE/wincompat.h" +#endif + +/********************************** Local Data ********************************/ + +ej_t **ejHandles; /* List of ej handles */ +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(B_ARGS_DEC, char_t **ptr, char_t *s); +static void appendString(char_t **ptr, char_t *s); +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); +static int parseArgs(ej_t *ep, int state, int flags); +static int parseCond(ej_t *ep, int state, int flags); +static int parseExpr(ej_t *ep, int state, int flags); +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 ***********************************/ +/* + * Initialize a Ejscript engine + */ + +int ejOpenEngine(sym_fd_t variables, sym_fd_t functions) +{ + ej_t *ep; + int eid, vid; + + if ((eid = hAllocEntry((void***) &ejHandles, &ejMax, sizeof(ej_t))) < 0) { + return -1; + } + ep = ejHandles[eid]; + 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 + * declarations so we use hAlloc to manage a list of variable tables. + */ + if ((vid = hAlloc((void***) &ep->variables)) < 0) { + ejMax = hFree((void***) &ejHandles, ep->eid); + return -1; + } + if (vid >= ep->variableMax) { + ep->variableMax = vid + 1; + } + + if (variables == -1) { + ep->variables[vid] = symOpen(64) + EJ_OFFSET; + ep->flags |= FLAGS_VARIABLES; + } else { + ep->variables[vid] = variables + EJ_OFFSET; + } + + if (functions == -1) { + ep->functions = symOpen(64); + ep->flags |= FLAGS_FUNCTIONS; + } else { + ep->functions = functions; + } + + ejLexOpen(ep); + +/* + * Define standard constants + */ + ejSetGlobalVar(ep->eid, T("null"), NULL); + +#ifdef EMF + ejEmfOpen(ep->eid); +#endif + return ep->eid; +} + +/******************************************************************************/ +/* + * Close + */ + +void ejCloseEngine(int eid) +{ + ej_t *ep; + int i; + + if ((ep = ejPtr(eid)) == NULL) { + return; + } + +#ifdef EMF + ejEmfClose(eid); +#endif + + bfreeSafe(B_L, ep->error); + ep->error = NULL; + bfreeSafe(B_L, ep->result); + ep->result = NULL; + + ejLexClose(ep); + + 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); + } + + ejMax = hFree((void***) &ejHandles, ep->eid); + bfree(B_L, ep); +} + +#ifndef __NO_EJ_FILE +/******************************************************************************/ +/* + * Evaluate a Ejscript file + */ + +char_t *ejEvalFile(int eid, char_t *path, char_t **emsg) +{ + gstat_t sbuf; + ej_t *ep; + char_t *script, *rs; + char *fileBuf; + int fd; + + a_assert(path && *path); + + if (emsg) { + *emsg = NULL; + } + + if ((ep = ejPtr(eid)) == NULL) { + return NULL; + } + + if ((fd = gopen(path, O_RDONLY | O_BINARY, 0666)) < 0) { + ejError(ep, T("Bad handle %d"), eid); + return NULL; + } + + if (gstat(path, &sbuf) < 0) { + gclose(fd); + ejError(ep, T("Cant stat %s"), path); + return NULL; + } + + if ((fileBuf = balloc(B_L, sbuf.st_size + 1)) == NULL) { + gclose(fd); + ejError(ep, T("Cant malloc %d"), sbuf.st_size); + return NULL; + } + + 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'; + gclose(fd); + + if ((script = ballocAscToUni(fileBuf, sbuf.st_size)) == NULL) { + bfree(B_L, fileBuf); + ejError(ep, T("Cant malloc %d"), sbuf.st_size + 1); + return NULL; + } + bfree(B_L, fileBuf); + + rs = ejEvalBlock(eid, script, emsg); + + bfree(B_L, script); + return rs; +} +#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. + */ + +int ejOpenBlock(int eid) +{ + ej_t *ep; + 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; + +} + +/******************************************************************************/ +/* + * Close a variable scope block. The vid parameter is the return value from + * the call to ejOpenBlock + */ + +int ejCloseBlock(int eid, int vid) +{ + ej_t *ep; + + if((ep = ejPtr(eid)) == NULL) { + return -1; + } + symClose(ep->variables[vid] - EJ_OFFSET); + ep->variableMax = hFree((void***) &ep->variables, vid); + return 0; + +} + +/******************************************************************************/ +/* + * Create a new variable scope block and evaluate a script. All variables + * created during this context will be automatically deleted when complete. + */ + +char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg) +{ + char_t* returnVal; + int vid; + + a_assert(script); + + vid = ejOpenBlock(eid); + returnVal = ejEval(eid, script, emsg); + ejCloseBlock(eid, vid); + + return returnVal; +} + +/******************************************************************************/ +/* + * 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. + */ + +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(B_L, &ep->result, T("")); + +/* + * Allocate a new evaluation block, and save the old one + */ + oldBlock = ep->input; + ejLexOpenScript(ep, script); + +/* + * 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); + +/* + * Return any error string to the user + */ + if (state == STATE_ERR && emsg) { + *emsg = bstrdup(B_L, ep->error); + } + +/* + * Restore the old evaluation block + */ + ep->input = oldBlock; + + if (state == STATE_EOF) { + return ep->result; + } + + if (state == STATE_ERR) { + return NULL; + } + + return ep->result; +} + +/******************************************************************************/ +/* + * Recursive descent parser for Ejscript + */ + +static int parse(ej_t *ep, int state, int flags) +{ + a_assert(ep); + + switch (state) { +/* + * 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: + if ((state = parseStmt(ep, state, flags)) != STATE_DEC_DONE && + state != STATE_EOF) { + state = STATE_ERR; + } + break; + + case STATE_EXPR: + if ((state = parseStmt(ep, state, flags)) != STATE_EXPR_DONE && + state != STATE_EOF) { + state = STATE_ERR; + } + break; + +/* + * Variable declaration list + */ + case STATE_DEC_LIST: + state = parseDeclaration(ep, state, flags); + break; + +/* + * Function argument string + */ + case STATE_ARG_LIST: + state = parseArgs(ep, state, flags); + break; + +/* + * Logical condition list (relational operations separated by &&, ||) + */ + case STATE_COND: + state = parseCond(ep, state, flags); + break; + +/* + * Expression list + */ + case STATE_RELEXP: + state = parseExpr(ep, state, flags); + break; + } + + if (state == STATE_ERR && ep->error == NULL) { + ejError(ep, T("Syntax error")); + } + return state; +} + +/******************************************************************************/ +/* + * Parse any statement including functions and simple relational operations + */ + +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, *identifier; + int done, expectSemi, thenFlags, elseFlags, tid, cond, forFlags; + int ejVarType; + + a_assert(ep); + +/* + * Set these to NULL, else we try to free them if an error occurs. + */ + endScript.putBackToken = NULL; + bodyScript.putBackToken = NULL; + incrScript.putBackToken = NULL; + condScript.putBackToken = NULL; + + expectSemi = 0; + saveFunc = NULL; + + for (done = 0; !done; ) { + tid = ejLexGetToken(ep, state); + + switch (tid) { + default: + ejLexPutbackToken(ep, TOK_EXPR, ep->token); + done++; + break; + + case TOK_ERR: + state = STATE_ERR; + done++; + break; + + case TOK_EOF: + state = STATE_EOF; + done++; + break; + + case TOK_NEWLINE: + break; + + case TOK_SEMI: +/* + * This case is when we discover no statement and just a lone ';' + */ + if (state != STATE_STMT) { + ejLexPutbackToken(ep, tid, ep->token); + } + done++; + break; + + case TOK_ID: +/* + * This could either be a reference to a variable or an assignment + */ + identifier = NULL; + 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 { + ejVarType = ejGetVar(ep->eid, identifier, &value); + if (ejVarType > 0) { + ejSetLocalVar(ep->eid, identifier, ep->result); + } else { + ejSetGlobalVar(ep->eid, identifier, ep->result); + } + } + } + + } else if (tid == TOK_INC_DEC ) { + value = NULL; + if (flags & FLAGS_EXE) { + ejVarType = ejGetVar(ep->eid, identifier, &value); + if (ejVarType < 0) { + ejError(ep, T("Undefined variable %s\n"), identifier); + goto error; + } + setString(B_L, &ep->result, value); + if (evalExpr(ep, value, (int) *ep->token, T("1")) < 0) { + state = STATE_ERR; + break; + } + + if (ejVarType > 0) { + ejSetLocalVar(ep->eid, identifier, ep->result); + } else { + ejSetGlobalVar(ep->eid, identifier, ep->result); + } + } + + } else { +/* + * If we are processing a declaration, allow undefined vars + */ + value = NULL; + if (state == STATE_DEC) { + if (ejGetVar(ep->eid, identifier, &value) > 0) { + ejError(ep, T("Variable already declared"), + identifier); + clearString(&identifier); + goto error; + } + ejSetLocalVar(ep->eid, identifier, NULL); + } else { + if ( flags & FLAGS_EXE ) { + if (ejGetVar(ep->eid, identifier, &value) < 0) { + ejError(ep, T("Undefined variable %s\n"), + identifier); + clearString(&identifier); + goto error; + } + } + } + setString(B_L, &ep->result, value); + ejLexPutbackToken(ep, tid, ep->token); + } + clearString(&identifier); + + if (state == STATE_STMT) { + expectSemi++; + } + done++; + break; + + case TOK_LITERAL: +/* + * Set the result to the literal (number or string constant) + */ + setString(B_L, &ep->result, ep->token); + if (state == STATE_STMT) { + expectSemi++; + } + done++; + break; + + case TOK_FUNCTION: +/* + * We must save any current ep->func value for the current stack frame + */ + if (ep->func) { + saveFunc = ep->func; + } + memset(&func, 0, sizeof(ejfunc_t)); + setString(B_L, &func.fname, ep->token); + ep->func = &func; + + setString(B_L, &ep->result, T("")); + if (ejLexGetToken(ep, state) != TOK_LPAREN) { + freeFunc(&func); + goto error; + } + + if (parse(ep, STATE_ARG_LIST, flags) != STATE_ARG_LIST_DONE) { + freeFunc(&func); + ep->func = saveFunc; + goto error; + } +/* + * Evaluate the function if required + */ + if (flags & FLAGS_EXE && evalFunction(ep) < 0) { + freeFunc(&func); + ep->func = saveFunc; + goto error; + } + + freeFunc(&func); + ep->func = saveFunc; + + if (ejLexGetToken(ep, state) != TOK_RPAREN) { + goto error; + } + if (state == STATE_STMT) { + expectSemi++; + } + done++; + break; + + case TOK_IF: + if (state != STATE_STMT) { + goto error; + } + if (ejLexGetToken(ep, state) != TOK_LPAREN) { + goto error; + } +/* + * Evaluate the entire condition list "(condition)" + */ + if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) { + goto error; + } + if (ejLexGetToken(ep, state) != TOK_RPAREN) { + goto error; + } +/* + * This is the "then" case. We need to always parse both cases and + * execute only the relevant case. + */ + if (*ep->result == '1') { + thenFlags = flags; + elseFlags = flags & ~FLAGS_EXE; + } else { + thenFlags = flags & ~FLAGS_EXE; + elseFlags = flags; + } +/* + * Process the "then" case. Allow for RETURN statement + */ + 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); + done++; + break; + } +/* + * Process the "else" case. Allow for return. + */ + switch (parse(ep, STATE_STMT, elseFlags)) { + case STATE_RET: + return STATE_RET; + case STATE_STMT_DONE: + break; + default: + goto error; + } + done++; + break; + + case TOK_FOR: +/* + * Format for the expression is: + * + * for (initial; condition; incr) { + * body; + * } + */ + if (state != STATE_STMT) { + goto error; + } + if (ejLexGetToken(ep, state) != TOK_LPAREN) { + goto error; + } + +/* + * Evaluate the for loop initialization statement + */ + if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) { + goto error; + } + if (ejLexGetToken(ep, state) != TOK_SEMI) { + goto error; + } + +/* + * 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. + */ + ejLexSaveInputState(ep, &condScript); + if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) { + goto error; + } + cond = (*ep->result != '0'); + + if (ejLexGetToken(ep, state) != TOK_SEMI) { + goto error; + } + +/* + * Don't execute the loop increment statement or the body first time + */ + forFlags = flags & ~FLAGS_EXE; + ejLexSaveInputState(ep, &incrScript); + if (parse(ep, STATE_EXPR, forFlags) != STATE_EXPR_DONE) { + goto error; + } + if (ejLexGetToken(ep, state) != TOK_RPAREN) { + goto error; + } + +/* + * Parse the body and remember the end of the body script + */ + ejLexSaveInputState(ep, &bodyScript); + if (parse(ep, STATE_STMT, forFlags) != STATE_STMT_DONE) { + goto error; + } + ejLexSaveInputState(ep, &endScript); + +/* + * Now actually do the for loop. Note loop has been rotated + */ + while (cond && (flags & FLAGS_EXE) ) { +/* + * Evaluate the body + */ + ejLexRestoreInputState(ep, &bodyScript); + + switch (parse(ep, STATE_STMT, flags)) { + case STATE_RET: + return STATE_RET; + case STATE_STMT_DONE: + break; + default: + goto error; + } +/* + * Evaluate the increment script + */ + ejLexRestoreInputState(ep, &incrScript); + if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) { + goto error; + } +/* + * Evaluate the condition + */ + ejLexRestoreInputState(ep, &condScript); + if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) { + goto error; + } + cond = (*ep->result != '0'); + } + ejLexRestoreInputState(ep, &endScript); + done++; + break; + + case TOK_VAR: + if (parse(ep, STATE_DEC_LIST, flags) != STATE_DEC_LIST_DONE) { + goto error; + } + done++; + break; + + case TOK_COMMA: + ejLexPutbackToken(ep, TOK_EXPR, ep->token); + done++; + break; + + case TOK_LPAREN: + if (state == STATE_EXPR) { + if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) { + goto error; + } + if (ejLexGetToken(ep, state) != TOK_RPAREN) { + goto error; + } + return STATE_EXPR_DONE; + } + done++; + break; + + case TOK_RPAREN: + ejLexPutbackToken(ep, tid, ep->token); + return STATE_EXPR_DONE; + + case TOK_LBRACE: +/* + * This handles any code in braces except "if () {} else {}" + */ + if (state != STATE_STMT) { + goto error; + } + +/* + * Parse will return STATE_STMT_BLOCK_DONE when the RBRACE is seen + */ + do { + 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; + } + return STATE_STMT_DONE; + + case TOK_RBRACE: + if (state == STATE_STMT) { + ejLexPutbackToken(ep, tid, ep->token); + return STATE_STMT_BLOCK_DONE; + } + goto error; + + case TOK_RETURN: + if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) { + goto error; + } + if (flags & FLAGS_EXE) { + while ( ejLexGetToken(ep, state) != TOK_EOF ); + done++; + return STATE_RET; + } + break; + } + } + + if (expectSemi) { + tid = ejLexGetToken(ep, state); + if (tid != TOK_SEMI && tid != TOK_NEWLINE) { + goto error; + } + +/* + * Skip newline after semi-colon + */ + ejRemoveNewlines(ep, state); + } + +/* + * Free resources and return the correct status + */ +doneParse: + if (tid == TOK_FOR) { + ejLexFreeInputState(ep, &condScript); + ejLexFreeInputState(ep, &incrScript); + ejLexFreeInputState(ep, &endScript); + ejLexFreeInputState(ep, &bodyScript); + } + + if (state == STATE_STMT) { + return STATE_STMT_DONE; + } else if (state == STATE_DEC) { + return STATE_DEC_DONE; + } else if (state == STATE_EXPR) { + return STATE_EXPR_DONE; + } else if (state == STATE_EOF) { + return state; + } else { + return STATE_ERR; + } + +/* + * Common error exit + */ +error: + state = STATE_ERR; + goto doneParse; +} + +/******************************************************************************/ +/* + * Parse variable declaration list + */ + +static int parseDeclaration(ej_t *ep, int state, int flags) +{ + int tid; + + a_assert(ep); + +/* + * Declarations can be of the following forms: + * var x; + * var x, y, z; + * var x = 1 + 2 / 3, y = 2 + 4; + * + * We set the variable to NULL if there is no associated assignment. + */ + + do { + if ((tid = ejLexGetToken(ep, state)) != TOK_ID) { + return STATE_ERR; + } + ejLexPutbackToken(ep, tid, ep->token); + +/* + * Parse the entire assignment or simple identifier declaration + */ + if (parse(ep, STATE_DEC, flags) != STATE_DEC_DONE) { + return STATE_ERR; + } + +/* + * Peek at the next token, continue if comma seen + */ + tid = ejLexGetToken(ep, state); + if (tid == TOK_SEMI) { + return STATE_DEC_LIST_DONE; + } else if (tid != TOK_COMMA) { + return STATE_ERR; + } + } while (tid == TOK_COMMA); + + if (tid != TOK_SEMI) { + return STATE_ERR; + } + return STATE_DEC_LIST_DONE; +} + +/******************************************************************************/ +/* + * Parse function arguments + */ + +static int parseArgs(ej_t *ep, int state, int flags) +{ + int tid, aid; + + a_assert(ep); + + do { + state = parse(ep, STATE_RELEXP, flags); + if (state == STATE_EOF || state == STATE_ERR) { + return state; + } + if (state == STATE_RELEXP_DONE) { + aid = hAlloc((void***) &ep->func->args); + ep->func->args[aid] = bstrdup(B_L, ep->result); + ep->func->nArgs++; + } +/* + * Peek at the next token, continue if more args (ie. comma seen) + */ + tid = ejLexGetToken(ep, state); + if (tid != TOK_COMMA) { + ejLexPutbackToken(ep, tid, ep->token); + } + } while (tid == TOK_COMMA); + + if (tid != TOK_RPAREN && state != STATE_RELEXP_DONE) { + return STATE_ERR; + } + return STATE_ARG_LIST_DONE; +} + +/******************************************************************************/ +/* + * Parse conditional expression (relational ops separated by ||, &&) + */ + +static int parseCond(ej_t *ep, int state, int flags) +{ + char_t *lhs, *rhs; + int tid, operator; + + a_assert(ep); + + setString(B_L, &ep->result, T("")); + rhs = lhs = NULL; + operator = 0; + + do { +/* + * Recurse to handle one side of a conditional. Accumulate the + * left hand side and the final result in ep->result. + */ + state = parse(ep, STATE_RELEXP, flags); + if (state != STATE_RELEXP_DONE) { + state = STATE_ERR; + break; + } + + if (operator > 0) { + setString(B_L, &rhs, ep->result); + if (evalCond(ep, lhs, operator, rhs) < 0) { + state = STATE_ERR; + break; + } + } + setString(B_L, &lhs, ep->result); + + tid = ejLexGetToken(ep, state); + if (tid == TOK_LOGICAL) { + operator = (int) *ep->token; + + } else if (tid == TOK_RPAREN || tid == TOK_SEMI) { + ejLexPutbackToken(ep, tid, ep->token); + state = STATE_COND_DONE; + break; + + } else { + ejLexPutbackToken(ep, tid, ep->token); + } + + } while (state == STATE_RELEXP_DONE); + + if (lhs) { + bfree(B_L, lhs); + } + + if (rhs) { + bfree(B_L, rhs); + } + return state; +} + +/******************************************************************************/ +/* + * Parse expression (leftHandSide operator rightHandSide) + */ + +static int parseExpr(ej_t *ep, int state, int flags) +{ + char_t *lhs, *rhs; + int rel, tid; + + a_assert(ep); + + 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. + */ + 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; + } + } + + 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); + + if ((tid = ejLexGetToken(ep, state)) == TOK_EXPR || + tid == TOK_INC_DEC || tid == TOK_LOGICAL) { + rel = (int) *ep->token; + + } else { + ejLexPutbackToken(ep, tid, ep->token); + state = STATE_RELEXP_DONE; + } + + } while (state == STATE_EXPR_DONE); + + if (rhs) { + bfree(B_L, rhs); + } + + if (lhs) { + bfree(B_L, lhs); + } + + return state; +} + +/******************************************************************************/ +/* + * Evaluate a condition. Implements &&, ||, ! + */ + +static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs) +{ + char_t buf[16]; + int l, r, lval; + + a_assert(lhs); + a_assert(rhs); + a_assert(rel > 0); + + lval = 0; + if (gisdigit((int)*lhs) && gisdigit((int)*rhs)) { + l = gatoi(lhs); + r = gatoi(rhs); + switch (rel) { + case COND_AND: + lval = l && r; + break; + case COND_OR: + lval = l || r; + break; + default: + ejError(ep, T("Bad operator %d"), rel); + return -1; + } + } else { + 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(B_L, &ep->result, buf); + return 0; +} + +/******************************************************************************/ +/* + * Evaluate an operation + */ + +static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs) +{ + char_t *cp, buf[16]; + int numeric, l, r, lval; + + a_assert(lhs); + a_assert(rhs); + a_assert(rel > 0); + +/* + * All of the characters in the lhs and rhs must be numeric + */ + numeric = 1; + for (cp = lhs; *cp; cp++) { + if (!gisdigit((int)*cp)) { + numeric = 0; + break; + } + } + + if (numeric) { + for (cp = rhs; *cp; cp++) { + if (!gisdigit((int)*cp)) { + numeric = 0; + break; + } + } + } + + if (numeric) { + l = gatoi(lhs); + r = gatoi(rhs); + switch (rel) { + case EXPR_PLUS: + lval = l + r; + break; + case EXPR_INC: + lval = l + 1; + break; + case EXPR_MINUS: + lval = l - r; + break; + case EXPR_DEC: + lval = l - 1; + break; + case EXPR_MUL: + lval = l * r; + break; + case EXPR_DIV: + if (r != 0) { + lval = l / r; + } else { + lval = 0; + } + break; + case EXPR_MOD: + if (r != 0) { + lval = l % r; + } else { + lval = 0; + } + break; + case EXPR_LSHIFT: + lval = l << r; + break; + case EXPR_RSHIFT: + lval = l >> r; + break; + case EXPR_EQ: + lval = l == r; + break; + case EXPR_NOTEQ: + lval = l != r; + break; + case EXPR_LESS: + lval = (l < r) ? 1 : 0; + break; + case EXPR_LESSEQ: + lval = (l <= r) ? 1 : 0; + break; + case EXPR_GREATER: + lval = (l > r) ? 1 : 0; + break; + 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; + } + + } else { + switch (rel) { + case EXPR_PLUS: + clearString(&ep->result); + appendString(&ep->result, lhs); + appendString(&ep->result, rhs); + return 0; + case EXPR_LESS: + lval = gstrcmp(lhs, rhs) < 0; + break; + case EXPR_LESSEQ: + lval = gstrcmp(lhs, rhs) <= 0; + break; + case EXPR_GREATER: + lval = gstrcmp(lhs, rhs) > 0; + break; + case EXPR_GREATEREQ: + lval = gstrcmp(lhs, rhs) >= 0; + break; + case EXPR_EQ: + lval = gstrcmp(lhs, rhs) == 0; + break; + case EXPR_NOTEQ: + lval = gstrcmp(lhs, rhs) != 0; + break; + case EXPR_INC: + case EXPR_DEC: + case EXPR_MINUS: + case EXPR_DIV: + case EXPR_MOD: + case EXPR_LSHIFT: + case EXPR_RSHIFT: + default: + ejError(ep, T("Bad operator")); + return -1; + } + } + + stritoa(lval, buf, sizeof(buf)); + setString(B_L, &ep->result, buf); + return 0; +} + +/******************************************************************************/ +/* + * Evaluate a function + */ + +static int evalFunction(ej_t *ep) +{ + sym_t *sp; + int (*fn)(int eid, void *handle, int argc, char_t **argv); + + if ((sp = symLookup(ep->functions, ep->func->fname)) == NULL) { + 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, + ep->func->args); +} + +/******************************************************************************/ +/* + * Output a parse ej_error message + */ + +void ejError(ej_t* ep, char_t* fmt, ...) +{ + va_list args; + ejinput_t *ip; + char_t *errbuf, *msgbuf; + + a_assert(ep); + a_assert(fmt); + ip = ep->input; + + va_start(args, fmt); + msgbuf = NULL; + fmtValloc(&msgbuf, E_MAX_ERROR, fmt, args); + va_end(args); + + if (ep && ip) { + 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; + } + bfreeSafe(B_L, msgbuf); +} + +/******************************************************************************/ +/* + * Clear a string value + */ + +static void clearString(char_t **ptr) +{ + a_assert(ptr); + + if (*ptr) { + bfree(B_L, *ptr); + } + *ptr = NULL; +} + +/******************************************************************************/ +/* + * Set a string value + */ + +static void setString(B_ARGS_DEC, char_t **ptr, char_t *s) +{ + a_assert(ptr); + + if (*ptr) { + bfree(B_ARGS, *ptr); + } + *ptr = bstrdup(B_ARGS, s); +} + +/******************************************************************************/ +/* + * Append to the pointer value + */ + +static void appendString(char_t **ptr, char_t *s) +{ + int len, oldlen; + + a_assert(ptr); + + if (*ptr) { + len = gstrlen(s); + oldlen = gstrlen(*ptr); + *ptr = brealloc(B_L, *ptr, (len + oldlen + 1) * sizeof(char_t)); + gstrcpy(&(*ptr)[oldlen], s); + } else { + *ptr = bstrdup(B_L, s); + } +} + +/******************************************************************************/ +/* + * Define a function + */ + +int ejSetGlobalFunction(int eid, char_t *name, + int (*fn)(int eid, void *handle, int argc, char_t **argv)) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return ejSetGlobalFunctionDirect(ep->functions, name, fn); +} + +/******************************************************************************/ +/* + * Define a function directly into the function symbol table. + */ + +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) { + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * Remove ("undefine") a function + */ + +int ejRemoveGlobalFunction(int eid, char_t *name) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return symDelete(ep->functions, name); +} + +/******************************************************************************/ +/* + * Get a function definition + */ + +void *ejGetGlobalFunction(int eid, char_t *name) +{ + ej_t *ep; + sym_t *sp; + int (*fn)(int eid, void *handle, int argc, char_t **argv); + + 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; + } + return NULL; +} + +/******************************************************************************/ +/* + * Utility routine to crack Ejscript arguments. Return the number of args + * seen. This routine only supports %s and %d type args. + * + * Typical usage: + * + * if (ejArgs(argc, argv, "%s %d", &name, &age) < 2) { + * error("Insufficient args\n"); + * return -1; + * } + */ + +int ejArgs(int argc, char_t **argv, char_t *fmt, ...) +{ + va_list vargs; + char_t *cp, **sp; + int *ip; + int argn; + + va_start(vargs, fmt); + + if (argv == NULL) { + return 0; + } + + for (argn = 0, cp = fmt; cp && *cp && argv[argn]; ) { + if (*cp++ != '%') { + continue; + } + + switch (*cp) { + case 'd': + ip = va_arg(vargs, int*); + *ip = gatoi(argv[argn]); + break; + + case 's': + sp = va_arg(vargs, char_t**); + *sp = argv[argn]; + break; + + default: +/* + * Unsupported + */ + a_assert(0); + } + argn++; + } + + va_end(vargs); + return argn; +} + +/******************************************************************************/ +/* + * Define the user handle + */ + +void ejSetUserHandle(int eid, int handle) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return; + } + ep->userHandle = handle; +} + +/******************************************************************************/ +/* + * Get the user handle + */ + +int ejGetUserHandle(int eid) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return ep->userHandle; +} + +/******************************************************************************/ +/* + * Get the current line number + */ + +int ejGetLineNumber(int eid) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return ep->input->lineNumber; +} + +/******************************************************************************/ +/* + * Set the result + */ + +void ejSetResult(int eid, char_t *s) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return; + } + setString(B_L, &ep->result, s); +} + +/******************************************************************************/ +/* + * Get the result + */ + +char_t *ejGetResult(int eid) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return NULL; + } + return ep->result; +} + +/******************************************************************************/ +/* + * Set a variable. Note: a variable with a value of NULL means declared but + * undefined. The value is defined in the top-most variable frame. + */ + +void ejSetVar(int eid, char_t *var, char_t *value) +{ + ej_t *ep; + value_t v; + + a_assert(var && *var); + + if ((ep = ejPtr(eid)) == NULL) { + return; + } + + if (value == NULL) { + v = valueString(value, 0); + } else { + v = valueString(value, VALUE_ALLOCATE); + } + symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0); +} + +/******************************************************************************/ +/* + * Set a local variable. Note: a variable with a value of NULL means + * declared but undefined. The value is defined in the top-most variable frame. + */ + +void ejSetLocalVar(int eid, char_t *var, char_t *value) +{ + ej_t *ep; + value_t v; + + a_assert(var && *var); + + if ((ep = ejPtr(eid)) == NULL) { + return; + } + + if (value == NULL) { + v = valueString(value, 0); + } else { + v = valueString(value, VALUE_ALLOCATE); + } + symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0); +} + +/******************************************************************************/ +/* + * 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. + */ + +void ejSetGlobalVar(int eid, char_t *var, char_t *value) +{ + ej_t *ep; + value_t v; + + a_assert(var && *var); + + if ((ep = ejPtr(eid)) == NULL) { + return; + } + + if (value == NULL) { + v = valueString(value, 0); + } else { + v = valueString(value, VALUE_ALLOCATE); + } + symEnter(ep->variables[0] - EJ_OFFSET, var, v, 0); +} + +/******************************************************************************/ +/* + * Get a variable + */ + +int ejGetVar(int eid, char_t *var, char_t **value) +{ + ej_t *ep; + sym_t *sp; + int i; + + a_assert(var && *var); + a_assert(value); + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + + 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; +} + +/******************************************************************************/ +/* + * Get the variable symbol table + */ + +sym_fd_t ejGetVariableTable(int eid) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return *ep->variables; +} + +/******************************************************************************/ +/* + * Get the functions symbol table + */ + +sym_fd_t ejGetFunctionTable(int eid) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return ep->functions; +} + +/******************************************************************************/ +/* + * Free an argument list + */ + +static void freeFunc(ejfunc_t *func) +{ + int i; + + for (i = func->nArgs - 1; i >= 0; i--) { + bfree(B_L, func->args[i]); + func->nArgs = hFree((void***) &func->args, i); + } + + if (func->fname) { + bfree(B_L, func->fname); + func->fname = NULL; + } +} + +/******************************************************************************/ +/* + * Get Ejscript pointer + */ + +static ej_t *ejPtr(int eid) +{ + a_assert(0 <= eid && eid < ejMax); + + if (eid < 0 || eid >= ejMax || ejHandles[eid] == NULL) { + ejError(NULL, T("Bad handle %d"), eid); + return NULL; + } + return ejHandles[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/cleopatre/application/spidgoahead/emfdb.c b/cleopatre/application/spidgoahead/emfdb.c new file mode 100644 index 0000000000..ebbd44aa14 --- /dev/null +++ b/cleopatre/application/spidgoahead/emfdb.c @@ -0,0 +1,1064 @@ +/* + * 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: emfdb.c,v 1.4 2003/09/29 19:48:08 bporter Exp $ + */ + +/******************************** 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; + int match = 0; + 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 (NULL != compareVal) + { + if (DB_CASE_INSENSITIVE == flags) + { + match = gstricmp(compareVal, value); + } + else + { + match = gstrcmp(compareVal, value); + } + if (0 == match) + { + 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); +#ifdef 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); +#ifdef 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/cleopatre/application/spidgoahead/emfdb.h b/cleopatre/application/spidgoahead/emfdb.h new file mode 100644 index 0000000000..2866fede97 --- /dev/null +++ b/cleopatre/application/spidgoahead/emfdb.h @@ -0,0 +1,111 @@ +/* + * 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: emfdb.h,v 1.3 2003/09/29 19:48:34 bporter Exp $ + */ + +/******************************** Description *********************************/ +/* + * Emf-like textfile database support for WebServer 2.1. + */ + +/********************************* Includes ***********************************/ + +#ifndef _h_EMFDB +#define _h_EMFDB 1 + +#ifndef 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 + +/* + * 30 Jun 03 BgP -- pass DB_CASE_INSENSITIVE as the "flags" argument to + * dbSearchString() to force a case-insensitive search. + */ +#define DB_CASE_INSENSITIVE 1 + +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 + * 30 Jun 03 BgP: If the value of 'flags' is DB_CASE_INSENSITIVE, use a + * case-insensitive string compare when searching. + */ +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/cleopatre/application/spidgoahead/form.c b/cleopatre/application/spidgoahead/form.c new file mode 100644 index 0000000000..c264871484 --- /dev/null +++ b/cleopatre/application/spidgoahead/form.c @@ -0,0 +1,169 @@ +/* + * form.c -- Form processing (in-memory CGI) for the GoAhead Web server + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: form.c,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +/********************************** Description *******************************/ + +/* + * This module implements the /goform handler. It emulates CGI processing + * but performs this in-process and not as an external process. This enables + * a very high performance implementation with easy parsing and decoding + * of query strings and posted data. + */ + +/*********************************** Includes *********************************/ + +#include "wsIntrn.h" + +/************************************ Locals **********************************/ + +static sym_fd_t formSymtab = -1; /* Symbol table for form handlers */ + +/************************************* Code ***********************************/ +/* + * Process a form request. Returns 1 always to indicate it handled the URL + */ + +int websFormHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, + 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); + + a_assert(websValid(wp)); + a_assert(url && *url); + a_assert(path && *path == '/'); + + websStats.formHits++; + +/* + * Extract the form name + */ + gstrncpy(formBuf, path, TSZ(formBuf)); + if ((formName = gstrchr(&formBuf[1], '/')) == NULL) { + websError(wp, 200, T("Missing form name")); + return 1; + } + formName++; + if ((cp = gstrchr(formName, '/')) != NULL) { + *cp = '\0'; + } + +/* + * Lookup the C form function first and then try tcl (no javascript support + * yet). + */ + sp = symLookup(formSymtab, formName); + 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; + 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; +} + +/******************************************************************************/ +/* + * Define a form function in the "form" map space. + */ + +int websFormDefine(char_t *name, void (*fn)(webs_t wp, char_t *path, + char_t *query)) +{ + a_assert(name && *name); + a_assert(fn); + + if (fn == NULL) { + return -1; + } + + symEnter(formSymtab, name, valueInteger((int) fn), (int) NULL); + return 0; +} + +/******************************************************************************/ +/* + * Open the symbol table for forms. + */ + +void websFormOpen() +{ + formSymtab = symOpen(WEBS_SYM_INIT); +} + +/******************************************************************************/ +/* + * Close the symbol table for forms. + */ + +void websFormClose() +{ + if (formSymtab != -1) { + symClose(formSymtab); + formSymtab = -1; + } +} + +/******************************************************************************/ +/* + * Write a webs header. This is a convenience routine to write a common + * header for a form back to the browser. + */ + +void websHeader(webs_t wp) +{ + a_assert(websValid(wp)); + + websWrite(wp, T("HTTP/1.0 200 OK\n")); + +/* + * By license terms the following line of code must not be modified + */ + websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); + + websWrite(wp, T("Pragma: no-cache\n")); + websWrite(wp, T("Cache-control: no-cache\n")); + websWrite(wp, T("Content-Type: text/html\n")); + websWrite(wp, T("\n")); + websWrite(wp, T("\n")); +} + +/******************************************************************************/ +/* + * Write a webs footer + */ + +void websFooter(webs_t wp) +{ + a_assert(websValid(wp)); + + websWrite(wp, T("\n")); +} + +/******************************************************************************/ + diff --git a/cleopatre/application/spidgoahead/h.c b/cleopatre/application/spidgoahead/h.c new file mode 100644 index 0000000000..3b98a7dd0b --- /dev/null +++ b/cleopatre/application/spidgoahead/h.c @@ -0,0 +1,196 @@ +/* + * h.c -- Handle allocation module + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: h.c,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * 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. + */ + +/********************************* Includes ***********************************/ + +#ifdef UEMF + #include "uemf.h" +#else + #include "basic/basicInternal.h" +#endif + +/********************************** Defines ***********************************/ +/* + * The handle list stores the length of the list and the number of used + * handles in the first two words. These are hidden from the caller by + * returning a pointer to the third word to the caller + */ + +#define H_LEN 0 /* First entry holds length of list */ +#define H_USED 1 /* Second entry holds number of used */ +#define H_OFFSET 2 /* Offset to real start of list */ + +#define H_INCR 16 /* Grow handle list in chunks this size */ + +/*********************************** Code *************************************/ +/* + * 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. + */ + +#ifdef B_STATS +int HALLOC(B_ARGS_DEC, void ***map) +#else +int hAlloc(void ***map) +#endif +{ + int *mp; + int handle, len, memsize, incr; + + a_assert(map); + + if (*map == NULL) { + incr = H_INCR; + memsize = (incr + H_OFFSET) * sizeof(void**); +#ifdef 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); + mp[H_LEN] = incr; + mp[H_USED] = 0; + *map = (void**) &mp[H_OFFSET]; + } else { + mp = &((*(int**)map)[-H_OFFSET]); + } + + len = mp[H_LEN]; + +/* + * Find the first null handle + */ + if (mp[H_USED] < mp[H_LEN]) { + for (handle = 0; handle < len; handle++) { + if (mp[handle+H_OFFSET] == 0) { + mp[H_USED]++; + return handle; + } + } + } else { + handle = len; + } + +/* + * No free handle so grow the handle list. Grow list in chunks of H_INCR. + */ + len += H_INCR; + memsize = (len + H_OFFSET) * sizeof(void**); + if ((mp = (int*) brealloc(B_L, (void*) mp, memsize)) == NULL) { + return -1; + } + *map = (void**) &mp[H_OFFSET]; + mp[H_LEN] = len; + memset(&mp[H_OFFSET + len - H_INCR], 0, sizeof(int*) * H_INCR); + mp[H_USED]++; + return handle; +} + +/******************************************************************************/ +/* + * Free a handle. This function returns the value of the largest + * handle in use plus 1, to be saved as a max value. + */ + +int hFree(void ***map, int handle) +{ + int *mp; + int len; + + a_assert(map); + mp = &((*(int**)map)[-H_OFFSET]); + a_assert(mp[H_LEN] >= H_INCR); + + a_assert(mp[handle + H_OFFSET]); + a_assert(mp[H_USED]); + mp[handle + H_OFFSET] = 0; + if (--(mp[H_USED]) == 0) { + bfree(B_L, (void*) mp); + *map = NULL; + } + +/* + * Find the greatest handle number in use. + */ + if (*map == NULL) { + handle = -1; + } else { + len = mp[H_LEN]; + if (mp[H_USED] < mp[H_LEN]) { + for (handle = len - 1; handle >= 0; handle--) { + if (mp[handle + H_OFFSET]) + break; + } + } else { + handle = len; + } + } + return handle + 1; +} + +/******************************************************************************/ +/* + * Allocate an entry in the halloc array. + */ + +#ifdef 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; + + a_assert(list); + a_assert(max); + +#ifdef B_STATS + if ((id = HALLOC(B_ARGS, (void***) list)) < 0) { +#else + if ((id = hAlloc((void***) list)) < 0) { +#endif + return -1; + } + + if (size > 0) { +#ifdef B_STATS + if ((cp = balloc(B_ARGS, size)) == NULL) { +#else + if ((cp = balloc(B_L, size)) == NULL) { +#endif + hFree(list, id); + return -1; + } + a_assert(cp); + memset(cp, 0, size); + + (*list)[id] = (void*) cp; + } + + if (id >= *max) { + *max = id + 1; + } + return id; +} + +/******************************************************************************/ + + diff --git a/cleopatre/application/spidgoahead/handler.c b/cleopatre/application/spidgoahead/handler.c new file mode 100644 index 0000000000..686a1815ba --- /dev/null +++ b/cleopatre/application/spidgoahead/handler.c @@ -0,0 +1,417 @@ +/* + * handler.c -- URL handler support + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: handler.c,v 1.4 2003/03/17 22:21:41 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * This module implements a URL handler interface and API to permit + * the addition of user definable URL processors. + */ + +/********************************* Includes ***********************************/ + +#include "wsIntrn.h" + +/*********************************** Locals ***********************************/ + +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 websPublishHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, + int sid, char_t *url, char_t *path, char_t *query); +static char_t *websCondenseMultipleChars(char_t *strToCondense, char_t cCondense); + +/*********************************** Code *************************************/ +/* + * Initialize the URL handler module + */ + +int websUrlHandlerOpen() +{ + if (++urlHandlerOpenCount == 1) { + websAspOpen(); + websUrlHandler = NULL; + websUrlHandlerMax = 0; + } + return 0; +} + +/******************************************************************************/ +/* + * Close the URL handler module + */ + +void websUrlHandlerClose() +{ + 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; + } +} + +/******************************************************************************/ +/* + * Define a new URL handler. urlPrefix is the URL prefix to match. webDir is + * an optional root directory path for a web directory. arg is an optional + * arg to pass to the URL handler. flags defines the matching order. Valid + * flags include WEBS_HANDLER_LAST, WEBS_HANDLER_FIRST. If multiple users + * specify last or first, their order is defined alphabetically by the + * urlPrefix. + */ + +int websUrlHandlerDefine(char_t *urlPrefix, char_t *webDir, int arg, + int (*handler)(webs_t wp, char_t *urlPrefix, char_t *webdir, int arg, + char_t *url, char_t *path, char_t *query), int flags) +{ + websUrlHandlerType *sp; + int len; + + a_assert(urlPrefix); + a_assert(handler); + +/* + * Grow the URL handler array to create a new slot + */ + len = (websUrlHandlerMax + 1) * sizeof(websUrlHandlerType); + if ((websUrlHandler = brealloc(B_L, websUrlHandler, len)) == NULL) { + return -1; + } + sp = &websUrlHandler[websUrlHandlerMax++]; + memset(sp, 0, sizeof(websUrlHandlerType)); + + sp->urlPrefix = bstrdup(B_L, urlPrefix); + sp->len = gstrlen(sp->urlPrefix); + if (webDir) { + sp->webDir = bstrdup(B_L, webDir); + } else { + sp->webDir = bstrdup(B_L, T("")); + } + sp->handler = handler; + sp->arg = arg; + sp->flags = flags; + +/* + * Sort in decreasing URL length order observing the flags for first and last + */ + qsort(websUrlHandler, websUrlHandlerMax, sizeof(websUrlHandlerType), + websUrlHandlerSort); + return 0; +} + +/******************************************************************************/ +/* + * Delete an existing URL handler. We don't reclaim the space of the old + * handler, just NULL the entry. Return -1 if handler is not found. + */ + +int websUrlHandlerDelete(int (*handler)(webs_t wp, char_t *urlPrefix, + char_t *webDir, int arg, char_t *url, char_t *path, char_t *query)) +{ + websUrlHandlerType *sp; + int i; + + for (i = 0; i < websUrlHandlerMax; i++) { + sp = &websUrlHandler[i]; + if (sp->handler == handler) { + sp->handler = NULL; + return 0; + } + } + return -1; +} + +/******************************************************************************/ +/* + * Sort in decreasing URL length order observing the flags for first and last + */ + +static int websUrlHandlerSort(const void *p1, const void *p2) +{ + websUrlHandlerType *s1, *s2; + int rc; + + a_assert(p1); + a_assert(p2); + + s1 = (websUrlHandlerType*) p1; + s2 = (websUrlHandlerType*) p2; + + if ((s1->flags & WEBS_HANDLER_FIRST) || (s2->flags & WEBS_HANDLER_LAST)) { + return -1; + } + + if ((s2->flags & WEBS_HANDLER_FIRST) || (s1->flags & WEBS_HANDLER_LAST)) { + return 1; + } + + if ((rc = gstrcmp(s1->urlPrefix, s2->urlPrefix)) == 0) { + if (s1->len < s2->len) { + return 1; + } else if (s1->len > s2->len) { + return -1; + } + } + return -rc; +} + +/******************************************************************************/ +/* + * Publish a new web directory (Use the default URL handler) + */ + +int websPublish(char_t *urlPrefix, char_t *path) +{ + return websUrlHandlerDefine(urlPrefix, path, 0, websPublishHandler, 0); +} + +/******************************************************************************/ +/* + * Return the directory for a given prefix. Ignore empty prefixes + */ + +char_t *websGetPublishDir(char_t *path, char_t **urlPrefix) +{ + websUrlHandlerType *sp; + int i; + + for (i = 0; i < websUrlHandlerMax; i++) { + sp = &websUrlHandler[i]; + if (sp->urlPrefix[0] == '\0') { + continue; + } + if (sp->handler && gstrncmp(sp->urlPrefix, path, sp->len) == 0) { + if (urlPrefix) { + *urlPrefix = sp->urlPrefix; + } + return sp->webDir; + } + } + return NULL; +} + +/******************************************************************************/ +/* + * Publish URL handler. We just patch the web page Directory and let the + * default handler do the rest. + */ + +static int websPublishHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, + int sid, char_t *url, char_t *path, char_t *query) +{ + int len; + + a_assert(websValid(wp)); + a_assert(path); + +/* + * Trim the urlPrefix off the path and set the webdirectory. Add one to step + * over the trailing '/' + */ + len = gstrlen(urlPrefix) + 1; + websSetRequestPath(wp, webDir, &path[len]); + return 0; +} + +/******************************************************************************/ +/* + * See if any valid handlers are defined for this request. If so, call them + * and continue calling valid handlers until one accepts the request. + * Return true if a handler was invoked, else return FALSE. + */ + +int websUrlHandlerRequest(webs_t wp) +{ + websUrlHandlerType *sp; + int i, first; + + a_assert(websValid(wp)); + +/* + * Delete the socket handler as we don't want to start reading any + * data on the connection as it may be for the next pipelined HTTP/1.1 + * request if using Keep Alive + */ + socketDeleteHandler(wp->sid); + wp->state = WEBS_PROCESSING; + websStats.handlerHits++; + + websSetRequestPath(wp, websGetDefaultDir(), NULL); + +/* + * Eliminate security hole + */ + websCondenseMultipleChars(wp->path, '/'); + websCondenseMultipleChars(wp->url, '/'); + +/* + * We loop over each handler in order till one accepts the request. + * The security handler will handle the request if access is NOT allowed. + */ + first = 1; + for (i = 0; i < websUrlHandlerMax; i++) { + sp = &websUrlHandler[i]; + if (sp->handler && gstrncmp(sp->urlPrefix, wp->path, sp->len) == 0) { + if (first) { + websSetEnv(wp); + first = 0; + } + if ((*sp->handler)(wp, sp->urlPrefix, sp->webDir, sp->arg, + wp->url, wp->path, wp->query)) { + return 1; + } + if (!websValid(wp)) { + trace(0, + T("webs: handler %s called websDone, but didn't return 1\n"), + sp->urlPrefix); + return 1; + } + } + } +/* + * If no handler processed the request, then return an error. Note: It is + * the handlers responsibility to call websDone + */ + if (i >= websUrlHandlerMax) { + /* + * 13 Mar 03 BgP + * preventing a cross-site scripting exploit + websError(wp, 200, T("No handler for this URL %s"), wp->url); + */ + websError(wp, 200, T("No handler for this URL")); + } + return 0; +} + +#ifdef OBSOLETE_CODE + +/******************************************************************************/ +/* + * 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; + } +} + +#endif + +/******************************************************************************/ +/* + * Convert multiple adjacent occurrences of a given character to a single + * instance. + */ + +static char_t *websCondenseMultipleChars(char_t *strToCondense, char_t cCondense) +{ + if (strToCondense != NULL) { + char_t *pStr, *pScan; + + pStr = pScan = strToCondense; + + while (*pScan && *pStr) { +/* + * Advance scan pointer over multiple occurences of condense character + */ + while ((*pScan == cCondense) && (*(pScan + 1) == cCondense)) { + pScan++; + } +/* + * Copy character if an advance of the scan pointer has occurred + */ + if (pStr != pScan) { + *pStr = *pScan; + } + + pScan++; + pStr++; + } +/* + * Zero terminate string if multiple adjacent characters were found and condensed + */ + if (pStr != pScan) { + *pStr = 0; + } + } + + return strToCondense; +} + +/******************************************************************************/ + diff --git a/cleopatre/application/spidgoahead/license.txt b/cleopatre/application/spidgoahead/license.txt new file mode 100644 index 0000000000..1fa9e67e79 --- /dev/null +++ b/cleopatre/application/spidgoahead/license.txt @@ -0,0 +1,282 @@ +License Agreement + +THIS LICENSE ALLOWS ONLY THE LIMITED USE OF GO AHEAD SOFTWARE, +INC. PROPRIETARY CODE. PLEASE CAREFULLY READ THIS AGREEMENT AS IT +PERTAINS TO THIS LICENSE, YOU CERTIFY THAT YOU WILL USE THE SOFTWARE +ONLY IN THE MANNER PERMITTED HEREIN. + +1. Definitions. + +1.1 "Documentation" means any documentation GoAhead includes with the +Original Code. + +1.2 "GoAhead" means Go Ahead Software, Inc. + +1.3 "Intellectual Property Rights" means all rights, whether now existing +or hereinafter acquired, in and to trade secrets, patents, copyrights, +trademarks, know-how, as well as moral rights and similar rights of any +type under the laws of any governmental authority, domestic or foreign, +including rights in and to all applications and registrations relating +to any of the foregoing. + +1.4 "License" or "Agreement" means this document. + +1.5 "Modifications" means any addition to or deletion from the substance +or structure of either the Original Code or any previous Modifications. + +1.6 "Original Code" means the Source Code to GoAhead’s proprietary +computer software entitled GoAhead WebServer. + +1.7 "Response Header" means the first portion of the response message +output by the GoAhead WebServer, containing but not limited to, header +fields for date, content-type, server identification and cache control. + +1.8 "Server Identification Field" means the field in the Response Header +which contains the text "Server: GoAhead-Webs". + +1.9 "You" means an individual or a legal entity exercising rights under, +and complying with all of the terms of, this license or a future version +of this license. For legal entities, "You" includes any entity which +controls, is controlled by, or is under common control with You. For +purposes of this definition, "control" means (a) the power, direct or +indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (b) ownership of fifty percent (50%) or +more of the outstanding shares or beneficial ownership of such entity. + +2. Source Code License. + +2.1 Limited Source Code Grant. + +GoAhead hereby grants You a world-wide, royalty-free, non-exclusive +license, subject to third party intellectual property claims, to use, +reproduce, modify, copy and distribute the Original Code. + +2.2 Binary Code. + +GoAhead hereby grants You a world-wide, royalty-free, non-exclusive +license to copy and distribute the binary code versions of the Original +Code together with Your Modifications. + +2.3 License Back to GoAhead. + +You hereby grant in both source code and binary code to GoAhead a +world-wide, royalty-free, non-exclusive license to copy, modify, display, +use and sublicense any Modifications You make that are distributed or +planned for distribution. Within 30 days of either such event, You +agree to ship to GoAhead a file containing the Modifications (in a media +to be determined by the parties), including any programmers’ notes and +other programmers’ materials. Additionally, You will provide to GoAhead +a complete description of the product, the product code or model number, +the date on which the product is initially shipped, and a contact name, +phone number and e-mail address for future correspondence. GoAhead will +keep confidential all data specifically marked as such. + +2.4 Restrictions on Use. + +You may sublicense Modifications to third parties such as subcontractors +or OEM's provided that You enter into license agreements with such third +parties that bind such third parties to all the obligations under this +Agreement applicable to you and that are otherwise substantially similar +in scope and application to this Agreement. + +3. Term. + +This Agreement and license are effective from the time You accept the +terms of this Agreement until this Agreement is terminated. You may +terminate this Agreement at any time by uninstalling or destroying +all copies of the Original Code including any and all binary versions +and removing any Modifications to the Original Code existing in any +products. This Agreement will terminate immediately and without further +notice if You fail to comply with any provision of this Agreement. All +restrictions on use, and all other provisions that may reasonably +be interpreted to survive termination of this Agreement, will survive +termination of this Agreement for any reason. Upon termination, You agree +to uninstall or destroy all copies of the Original Code, Modifications, +and Documentation. + +4. Trademarks and Brand. + +4.1 License and Use. + +GoAhead hereby grants to You a limited world-wide, royalty-free, +non-exclusive license to use the GoAhead trade names, trademarks, logos, +service marks and product designations posted in Exhibit A (collectively, +the "GoAhead Marks") in connection with the activities by You under this +Agreement. Additionally, GoAhead grants You a license under the terms +above to such GoAhead trademarks as shall be identified at a URL (the +"URL") provided by GoAhead. The use by You of GoAhead Marks shall be in +accordance with GoAhead’s trademark policies regarding trademark usage +as established at the web site designated by the URL, or as otherwise +communicated to You by GoAhead at its sole discretion. You understand and +agree that any use of GoAhead Marks in connection with this Agreement +shall not create any right, title or interest in or to such GoAhead +Marks and that all such use and goodwill associated with GoAhead Marks +will inure to the benefit of GoAhead. + +4.2 Promotion by You of GoAhead WebServer Mark. + +In consideration for the licenses granted by GoAhead to You herein, You +agree to notify GoAhead when You incorporate the GoAhead WebServer in +Your product and to inform GoAhead when such product begins to ship. You +agree to promote the Original Code by prominently and visibly displaying +a graphic of the GoAhead WebServer mark on the initial web page of Your +product that is displayed each time a user connects to it. You also agree +that GoAhead may identify your company as a user of the GoAhead WebServer +in conjunction with its own marketing efforts. You may further promote +the Original Code by displaying the GoAhead WebServer mark in marketing +and promotional materials such as the home page of your web site or web +pages promoting the product. + +4.3 Placement of Copyright Notice by You. + +You agree to include copies of the following notice (the "Notice") +regarding proprietary rights in all copies of the products that You +distribute, as follows: (i) embedded in the object code; and (ii) on +the title pages of all documentation. Furthermore, You agree to use +commercially reasonable efforts to cause any licensees of your products +to embed the Notice in object code and on the title pages or relevant +documentation. The Notice is as follows: Copyright (c) 20xx GoAhead +Software, Inc. All Rights Reserved. Unless GoAhead otherwise instructs, +the year 20xx is to be replaced with the year during which the release of +the Original Code containing the notice is issued by GoAhead. If this year +is not supplied with Documentation, GoAhead will supply it upon request. + +4.4 No Modifications to Server Identification Field. + +You agree not to remove or modify the Server identification Field +contained in the Response Header as defined in Section 1.6 and 1.7. + +5. Warranty Disclaimers. + +THE ORIGINAL CODE, THE DOCUMENTATION AND THE MEDIA UPON WHICH THE ORIGINAL +CODE IS RECORDED (IF ANY) ARE PROVIDED "AS IS" AND WITHOUT WARRANTIES OF +ANY KIND, EXPRESS, STATUTORY OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The entire risk as to the quality and performance of the Original Code +(including any Modifications You make) and the Documentation is with +You. Should the Original Code or the Documentation prove defective, +You (and not GoAhead or its distributors, licensors or dealers) assume +the entire cost of all necessary servicing or repair. GoAhead does not +warrant that the functions contained in the Original Code will meet your +requirements or operate in the combination that You may select for use, +that the operation of the Original Code will be uninterrupted or error +free, or that defects in the Original Code will be corrected. No oral +or written statement by GoAhead or by a representative of GoAhead shall +create a warranty or increase the scope of this warranty. + +GOAHEAD DOES NOT WARRANT THE ORIGINAL CODE AGAINST INFRINGEMENT OR THE +LIKE WITH RESPECT TO ANY COPYRIGHT, PATENT, TRADE SECRET, TRADEMARK +OR OTHER PROPRIETARY RIGHT OF ANY THIRD PARTY AND DOES NOT WARRANT +THAT THE ORIGINAL CODE DOES NOT INCLUDE ANY VIRUS, SOFTWARE ROUTINE +OR OTHER SOFTWARE DESIGNED TO PERMIT UNAUTHORIZED ACCESS, TO DISABLE, +ERASE OR OTHERWISE HARM SOFTWARE, HARDWARE OR DATA, OR TO PERFORM ANY +OTHER SUCH ACTIONS. + +Any warranties that by law survive the foregoing disclaimers shall +terminate ninety (90) days from the date You received the Original Code. + +6. Limitation of Liability. + +YOUR SOLE REMEDIES AND GOAHEAD'S ENTIRE LIABILITY ARE SET FORTH ABOVE. IN +NO EVENT WILL GOAHEAD OR ITS DISTRIBUTORS OR DEALERS BE LIABLE FOR +DIRECT, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES RESULTING FROM +THE USE OF THE ORIGINAL CODE, THE INABILITY TO USE THE ORIGINAL CODE, +OR ANY DEFECT IN THE ORIGINAL CODE, INCLUDING ANY LOST PROFITS, EVEN IF +THEY HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +You agree that GoAhead and its distributors and dealers will not be +LIABLE for defense or indemnity with respect to any claim against You +by any third party arising from your possession or use of the Original +Code or the Documentation. + +In no event will GoAhead’s total liability to You for all damages, losses, +and causes of action (whether in contract, tort, including negligence, +or otherwise) exceed the amount You paid for this product. + +SOME STATES DO NOT ALLOW LIMITATIONS ON HOW LONG AN IMPLIED WARRANTY +LASTS, AND SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION +OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THE ABOVE LIMITATIONS OR +EXCLUSIONS MAY NOT APPLY TO YOU. THIS WARRANTY GIVES YOU SPECIFIC LEGAL +RIGHTS AND YOU MAY ALSO HAVE OTHER RIGHTS WHICH VARY FROM STATE TO STATE. + +7. Indemnification by You. + +You agree to indemnify and hold GoAhead harmless against any and all +claims, losses, damages and costs (including legal expenses and reasonable +counsel fees) arising out of any claim of a third party with respect to +the contents of the Your products, and any intellectual property rights +or other rights or interests related thereto. + +8. High Risk Activities. + +The Original Code is not fault-tolerant and is not designed , manufactured +or intended for use or resale as online control equipment in hazardous +environments requiring fail-safe performance, such as in the operation +of nuclear facilities, aircraft navigation or communication systems, +air traffic control, direct life support machines or weapons systems, +in which the failure of the Original Code could lead directly to death, +personal injury, or severe physical or environmental damage. GoAhead and +its suppliers specifically disclaim any express or implied warranty of +fitness for any high risk uses listed above. + +9. Government Restricted Rights. + +For units of the Department of Defense, use, duplication, or disclosure +by the Government is subject to restrictions as set forth in subparagraph +(c)(1)(ii) of the Rights in Technical Data and Computer Software clause +at DFARS 252.227-7013. Contractor/manufacturer is GoAhead Software, +Inc., 10900 N.E. 8th Street, Suite 750, Bellevue, Washington 98004. + +If the Commercial Computer Software Restricted rights clause at FAR +52.227-19 or its successors apply, the Software and Documentation +constitute restricted computer software as defined in that clause and +the Government shall not have the license for published software set +forth in subparagraph (c)(3) of that clause. + +The Original Code (i) was developed at private expense, and no part of it +was developed with governmental funds; (ii) is a trade secret of GoAhead +(or its licensor(s)) for all purposes of the Freedom of Information Act; +(iii) is "restricted computer software" subject to limited utilization as +provided in the contract between the vendor and the governmental entity; +and (iv) in all respects is proprietary data belonging solely to GoAhead +(or its licensor(s)). + +10. Governing Law and Interpretation. + +This Agreement shall be interpreted under and governed by the laws of the +State of Washington, without regard to its rules governing the conflict of +laws. If any provision of this Agreement is held illegal or unenforceable +by a court or tribunal of competent jurisdiction, the remaining provisions +of this Agreement shall remain in effect and the invalid provision deemed +modified to the least degree necessary to remedy such invalidity. + +11. Entire Agreement. + +This Agreement is the complete agreement between GoAhead and You and +supersedes all prior agreements, oral or written, with respect to the +subject matter hereof. + +If You have any questions concerning this Agreement, You may write to +GoAhead Software, Inc., 10900 N.E. 8th Street, Suite 750, Bellevue, +Washington 98004 or send e-mail to info@goahead.com. + +BY CLICKING ON THE "Register" BUTTON ON THE REGISTRATION FORM, YOU +ACCEPT AND AGREE TO BE BOUND BY ALL OF THE TERMS AND CONDITIONS SET +FORTH IN THIS AGREEMENT. IF YOU DO NOT WISH TO ACCEPT THIS LICENSE OR +YOU DO NOT QUALIFY FOR A LICENSE BASED ON THE TERMS SET FORTH ABOVE, +YOU MUST NOT CLICK THE "Register" BUTTON. + +Exhibit A + +GoAhead Trademarks, Logos, and Product Designation Information + + + + +01/28/00 + + + + diff --git a/cleopatre/application/spidgoahead/md5.h b/cleopatre/application/spidgoahead/md5.h new file mode 100644 index 0000000000..c5589206a0 --- /dev/null +++ b/cleopatre/application/spidgoahead/md5.h @@ -0,0 +1,50 @@ +/* MD5.H - header file for MD5C.C + * + * $Id: md5.h,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +/* 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 */ diff --git a/cleopatre/application/spidgoahead/md5c.c b/cleopatre/application/spidgoahead/md5c.c new file mode 100644 index 0000000000..eda94a6628 --- /dev/null +++ b/cleopatre/application/spidgoahead/md5c.c @@ -0,0 +1,338 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + * + * $Id: md5c.c,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +/* 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/cleopatre/application/spidgoahead/mime.c b/cleopatre/application/spidgoahead/mime.c new file mode 100644 index 0000000000..064f7013cc --- /dev/null +++ b/cleopatre/application/spidgoahead/mime.c @@ -0,0 +1,119 @@ +/* + * mime.c -- Web server mime types + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: mime.c,v 1.7 2003/03/17 20:12:33 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * Mime types and file extensions. This module maps URL extensions to + * content types. + */ + +/********************************* Includes ***********************************/ + +#include "wsIntrn.h" + +/******************************** Global Data *********************************/ +/* + * Addd entries to the MimeList as required for your content + */ + + +websMimeType websMimeList[] = { + { T("application/java"), T(".class") }, + { T("application/java"), T(".jar") }, + { T("text/html"), T(".asp") }, + { T("text/html"), T(".htm") }, + { T("text/html"), T(".html") }, + { T("text/xml"), T(".xml") }, + { T("image/gif"), T(".gif") }, + { T("image/jpeg"), T(".jpg") }, + { T("text/css"), T(".css") }, + { T("text/plain"), T(".txt") }, + { T("application/x-javascript"), T(".js") }, + { T("application/x-shockwave-flash"), T(".swf") }, + +#ifdef MORE_MIME_TYPES + { T("application/binary"), T(".exe") }, + { T("application/compress"), T(".z") }, + { T("application/gzip"), T(".gz") }, + { T("application/octet-stream"), T(".bin") }, + { T("application/oda"), T(".oda") }, + { T("application/pdf"), T(".pdf") }, + { T("application/postscript"), T(".ai") }, + { T("application/postscript"), T(".eps") }, + { T("application/postscript"), T(".ps") }, + { T("application/rtf"), T(".rtf") }, + { T("application/x-bcpio"), T(".bcpio") }, + { T("application/x-cpio"), T(".cpio") }, + { T("application/x-csh"), T(".csh") }, + { T("application/x-dvi"), T(".dvi") }, + { T("application/x-gtar"), T(".gtar") }, + { T("application/x-hdf"), T(".hdf") }, + { T("application/x-latex"), T(".latex") }, + { T("application/x-mif"), T(".mif") }, + { T("application/x-netcdf"), T(".nc") }, + { T("application/x-netcdf"), T(".cdf") }, + { T("application/x-ns-proxy-autoconfig"), T(".pac") }, + { T("application/x-patch"), T(".patch") }, + { T("application/x-sh"), T(".sh") }, + { T("application/x-shar"), T(".shar") }, + { T("application/x-sv4cpio"), T(".sv4cpio") }, + { T("application/x-sv4crc"), T(".sv4crc") }, + { T("application/x-tar"), T(".tar") }, + { T("application/x-tcl"), T(".tcl") }, + { T("application/x-tex"), T(".tex") }, + { T("application/x-texinfo"), T(".texinfo") }, + { T("application/x-texinfo"), T(".texi") }, + { T("application/x-troff"), T(".t") }, + { T("application/x-troff"), T(".tr") }, + { T("application/x-troff"), T(".roff") }, + { T("application/x-troff-man"), T(".man") }, + { T("application/x-troff-me"), T(".me") }, + { T("application/x-troff-ms"), T(".ms") }, + { T("application/x-ustar"), T(".ustar") }, + { T("application/x-wais-source"), T(".src") }, + { T("application/zip"), T(".zip") }, + { T("audio/basic"), T(".au snd") }, + { T("audio/x-aiff"), T(".aif") }, + { T("audio/x-aiff"), T(".aiff") }, + { T("audio/x-aiff"), T(".aifc") }, + { T("audio/x-wav"), T(".wav") }, + { T("audio/x-wav"), T(".ram") }, + { T("image/ief"), T(".ief") }, + { T("image/jpeg"), T(".jpeg") }, + { T("image/jpeg"), T(".jpe") }, + { T("image/tiff"), T(".tiff") }, + { T("image/tiff"), T(".tif") }, + { T("image/x-cmu-raster"), T(".ras") }, + { T("image/x-portable-anymap"), T(".pnm") }, + { T("image/x-portable-bitmap"), T(".pbm") }, + { T("image/x-portable-graymap"), T(".pgm") }, + { T("image/x-portable-pixmap"), T(".ppm") }, + { T("image/x-rgb"), T(".rgb") }, + { T("image/x-xbitmap"), T(".xbm") }, + { T("image/x-xpixmap"), T(".xpm") }, + { T("image/x-xwindowdump"), T(".xwd") }, + { T("text/html"), T(".cfm") }, + { T("text/html"), T(".shtm") }, + { T("text/html"), T(".shtml") }, + { T("text/richtext"), T(".rtx") }, + { T("text/tab-separated-values"), T(".tsv") }, + { T("text/x-setext"), T(".etx") }, + { T("video/mpeg"), T(".mpeg mpg mpe") }, + { T("video/quicktime"), T(".qt") }, + { T("video/quicktime"), T(".mov") }, + { T("video/x-msvideo"), T(".avi") }, + { T("video/x-sgi-movie"), T(".movie") }, +#endif + { NULL, NULL}, +}; + +/*****************************************************************************/ + diff --git a/cleopatre/application/spidgoahead/misc.c b/cleopatre/application/spidgoahead/misc.c new file mode 100644 index 0000000000..1e7f8c54df --- /dev/null +++ b/cleopatre/application/spidgoahead/misc.c @@ -0,0 +1,690 @@ +/* + * misc.c -- Miscellaneous routines. + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: misc.c,v 1.6 2003/09/29 19:50:24 bporter Exp $ + */ + +/********************************* Includes ***********************************/ + +#ifdef UEMF + #include "uemf.h" +#else + #include "basic/basicInternal.h" +#endif + +/* + * 16 Sep 03 -- added option to use memcpy() instead of strncpy() in the + * ascToUni and uniToAsc functions. + */ +#define kUseMemcopy + + +/********************************* Defines ************************************/ +/* + * 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 64 /* Growth increment */ + +typedef struct { + char_t *s; /* Pointer to buffer */ + int size; /* Current buffer size */ + int max; /* Maximum buffer size */ + int count; /* Buffer count */ + int flags; /* Allocation flags */ +} strbuf_t; + +/* + * Sprintf formatting flags + */ +enum flag { + flag_none = 0, + flag_minus = 1, + flag_plus = 2, + flag_space = 4, + flag_hash = 8, + flag_zero = 16, + flag_short = 32, + flag_long = 64 +}; + +/************************** Forward Declarations ******************************/ + +static int dsnprintf(char_t **s, int size, char_t *fmt, va_list arg, + int msize); +static int strnlen(char_t *s, unsigned int n); +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); +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); + +/************************************ Code ************************************/ +/* + * "basename" returns a pointer to the last component of a pathname + * LINUX, LynxOS and Mac OS X have their own basename function + */ + +#if (!defined (LINUX) && !defined (LYNX) && !defined (MACOSX)) +char_t *basename(char_t *name) +{ + char_t *cp; + +#if (defined (NW) || defined (WIN)) + if (((cp = gstrrchr(name, '\\')) == NULL) && + ((cp = gstrrchr(name, '/')) == NULL)) { + return name; +#else + if ((cp = gstrrchr(name, '/')) == NULL) { + return name; +#endif + } else if (*(cp + 1) == '\0' && cp == name) { + return name; + } else if (*(cp + 1) == '\0' && cp != name) { + return T(""); + } else { + return ++cp; + } +} +#endif /* ! LINUX & ! LYNX & ! MACOSX */ + +/******************************************************************************/ +/* + * Returns a pointer to the directory component of a pathname. bufsize is + * the size of the buffer in BYTES! + */ + +char_t *dirname(char_t *buf, char_t *name, int bufsize) +{ + char_t *cp; + int len; + + a_assert(name); + a_assert(buf); + a_assert(bufsize > 0); + +#if (defined (WIN) || defined (NW)) + if ((cp = gstrrchr(name, '/')) == NULL && + (cp = gstrrchr(name, '\\')) == NULL) +#else + if ((cp = gstrrchr(name, '/')) == NULL) +#endif + { + gstrcpy(buf, T(".")); + return buf; + } + + if ((*(cp + 1) == '\0' && cp == name)) { + gstrncpy(buf, T("."), TSZ(bufsize)); + gstrcpy(buf, T(".")); + return buf; + } + + len = cp - name; + + if (len < bufsize) { + gstrncpy(buf, name, len); + buf[len] = '\0'; + } else { + gstrncpy(buf, name, TSZ(bufsize)); + buf[bufsize - 1] = '\0'; + } + + return buf; +} + + +/******************************************************************************/ +/* + * sprintf and vsprintf are bad, ok. You can easily clobber memory. Use + * fmtAlloc and fmtValloc instead! These functions do _not_ support floating + * point, like %e, %f, %g... + */ + +int fmtAlloc(char_t **s, int n, char_t *fmt, ...) +{ + va_list ap; + int result; + + a_assert(s); + a_assert(fmt); + + *s = NULL; + va_start(ap, fmt); + 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; +} + +/******************************************************************************/ +/* + * This function appends the formatted string to the supplied string, + * reallocing if required. + */ + +int fmtRealloc(char_t **s, int n, int msize, char_t *fmt, ...) +{ + va_list ap; + int result; + + a_assert(s); + a_assert(fmt); + + if (msize == -1) { + *s = NULL; + } + va_start(ap, fmt); + result = dsnprintf(s, n, fmt, ap, msize); + va_end(ap); + return result; +} + +/******************************************************************************/ +/* + * A vsprintf replacement. + */ + +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. + * 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 + * return the size of the allocated buffer for use with the next call. For + * the first call, msize can be set to -1. + */ + +static int dsnprintf(char_t **s, int size, char_t *fmt, va_list arg, int msize) +{ + strbuf_t buf; + char_t c; + + a_assert(s); + a_assert(fmt); + + memset(&buf, 0, sizeof(buf)); + buf.s = *s; + + if (*s == NULL || msize != 0) { + buf.max = size; + buf.flags |= STR_REALLOC; + if (msize != 0) { + buf.size = max(msize, 0); + } + if (*s != NULL && msize != 0) { + buf.count = gstrlen(*s); + } + } else { + buf.size = size; + } + + while ((c = *fmt++) != '\0') { + if (c != '%' || (c = *fmt++) == '%') { + put_char(&buf, c); + } else { + enum flag f = flag_none; + int width = 0; + int prec = -1; + for ( ; c != '\0'; c = *fmt++) { + if (c == '-') { + f |= flag_minus; + } else if (c == '+') { + f |= flag_plus; + } else if (c == ' ') { + f |= flag_space; + } else if (c == '#') { + f |= flag_hash; + } else if (c == '0') { + f |= flag_zero; + } else { + break; + } + } + if (c == '*') { + width = va_arg(arg, int); + if (width < 0) { + f |= flag_minus; + width = -width; + } + c = *fmt++; + } else { + for ( ; gisdigit((int)c); c = *fmt++) { + width = width * 10 + (c - '0'); + } + } + if (c == '.') { + f &= ~flag_zero; + c = *fmt++; + if (c == '*') { + prec = va_arg(arg, int); + c = *fmt++; + } else { + for (prec = 0; gisdigit((int)c); c = *fmt++) { + prec = prec * 10 + (c - '0'); + } + } + } + if (c == 'h' || c == 'l') { + f |= (c == 'h' ? flag_short : flag_long); + c = *fmt++; + } + if (c == 'd' || c == 'i') { + long int value; + if (f & flag_short) { + value = (short int) va_arg(arg, int); + } else if (f & flag_long) { + value = va_arg(arg, long int); + } else { + value = va_arg(arg, int); + } + if (value >= 0) { + if (f & flag_plus) { + put_ulong(&buf, value, 10, 0, T("+"), width, prec, f); + } else if (f & flag_space) { + put_ulong(&buf, value, 10, 0, T(" "), width, prec, f); + } else { + put_ulong(&buf, value, 10, 0, NULL, width, prec, f); + } + } else { + put_ulong(&buf, -value, 10, 0, T("-"), width, prec, f); + } + } else if (c == 'o' || c == 'u' || c == 'x' || c == 'X') { + unsigned long int value; + if (f & flag_short) { + value = (unsigned short int) va_arg(arg, unsigned int); + } else if (f & flag_long) { + value = va_arg(arg, unsigned long int); + } else { + value = va_arg(arg, unsigned int); + } + if (c == 'o') { + if (f & flag_hash && value != 0) { + put_ulong(&buf, value, 8, 0, T("0"), width, prec, f); + } else { + put_ulong(&buf, value, 8, 0, NULL, width, prec, f); + } + } else if (c == 'u') { + put_ulong(&buf, value, 10, 0, NULL, width, prec, f); + } else { + if (f & flag_hash && value != 0) { + if (c == 'x') { + put_ulong(&buf, value, 16, 0, T("0x"), width, + prec, f); + } else { + put_ulong(&buf, value, 16, 1, T("0X"), width, + prec, f); + } + } else { + /* 04 Apr 02 BgP -- changed so that %X correctly outputs + * uppercase hex digits when requested. + put_ulong(&buf, value, 16, 0, NULL, width, prec, f); + */ + put_ulong(&buf, value, 16, ('X' == c) , NULL, width, prec, f); + } + } + + } else if (c == 'c') { + char_t value = va_arg(arg, int); + put_char(&buf, value); + + } else if (c == 's' || c == 'S') { + char_t *value = va_arg(arg, char_t *); + if (value == NULL) { + put_string(&buf, T("(null)"), -1, width, prec, f); + } else if (f & flag_hash) { + put_string(&buf, + value + 1, (char_t) *value, width, prec, f); + } else { + put_string(&buf, value, -1, width, prec, f); + } + } else if (c == 'p') { + void *value = va_arg(arg, void *); + put_ulong(&buf, + (unsigned long int) value, 16, 0, T("0x"), width, prec, f); + } else if (c == 'n') { + if (f & flag_short) { + short int *value = va_arg(arg, short int *); + *value = buf.count; + } else if (f & flag_long) { + long int *value = va_arg(arg, long int *); + *value = buf.count; + } else { + int *value = va_arg(arg, int *); + *value = buf.count; + } + } else { + put_char(&buf, c); + } + } + } + if (buf.s == NULL) { + put_char(&buf, '\0'); + } + +/* + * If the user requested a dynamic buffer (*s == NULL), ensure it is returned. + */ + if (*s == NULL || msize != 0) { + *s = buf.s; + } + + if (*s != NULL && size > 0) { + if (buf.count < size) { + (*s)[buf.count] = '\0'; + } else { + (*s)[buf.size - 1] = '\0'; + } + } + + if (msize != 0) { + return buf.size; + } + return buf.count; +} + +/******************************************************************************/ +/* + * Return the length of a string limited by a given length + */ + +static int strnlen(char_t *s, unsigned int n) +{ + unsigned int len; + + len = gstrlen(s); + return min(len, n); +} + +/******************************************************************************/ +/* + * Add a character to a string buffer + */ + +static void put_char(strbuf_t *buf, char_t c) +{ + 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) { +/* + * 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)); + } else { + buf->s = brealloc(B_L, buf->s, buf->size * sizeof(char_t)); + } + } + buf->s[buf->count] = c; + if (c != '\0') { + ++buf->count; + } +} + +/******************************************************************************/ +/* + * Add a string to a string buffer + */ + +static void put_string(strbuf_t *buf, char_t *s, int len, int width, + int prec, enum flag f) +{ + int i; + + if (len < 0) { + len = strnlen(s, prec >= 0 ? prec : ULONG_MAX); + } else if (prec >= 0 && prec < len) { + len = prec; + } + if (width > len && !(f & flag_minus)) { + for (i = len; i < width; ++i) { + put_char(buf, ' '); + } + } + for (i = 0; i < len; ++i) { + put_char(buf, s[i]); + } + if (width > len && f & flag_minus) { + for (i = len; i < width; ++i) { + put_char(buf, ' '); + } + } +} + +/******************************************************************************/ +/* + * Add a long to a string buffer + */ + +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) +{ + unsigned long x, x2; + int len, zeros, i; + + for (len = 1, x = 1; x < ULONG_MAX / base; ++len, x = x2) { + x2 = x * base; + if (x2 > value) { + break; + } + } + zeros = (prec > len) ? prec - len : 0; + width -= zeros + len; + if (prefix != NULL) { + width -= strnlen(prefix, ULONG_MAX); + } + if (!(f & flag_minus)) { + 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) { + put_string(buf, prefix, -1, 0, -1, flag_none); + } + for (i = 0; i < zeros; ++i) { + put_char(buf, '0'); + } + for ( ; x > 0; x /= base) { + int digit = (value / x) % base; + put_char(buf, (char) ((digit < 10 ? '0' : (upper ? 'A' : 'a') - 10) + + digit)); + } + if (f & flag_minus) { + for (i = 0; i < width; ++i) { + put_char(buf, ' '); + } + } +} + +/******************************************************************************/ +/* + * Convert an ansi string to a unicode string. On an error, we return the + * original ansi string which is better than returning NULL. nBytes is the + * size of the destination buffer (ubuf) in _bytes_. + */ + +char_t *ascToUni(char_t *ubuf, char *str, int nBytes) +{ +#ifdef UNICODE + if (MultiByteToWideChar(CP_ACP, 0, str, nBytes / sizeof(char_t), ubuf, + nBytes / sizeof(char_t)) < 0) { + return (char_t*) str; + } +#else + +#ifdef kUseMemcopy + memcpy(ubuf, str, nBytes); +#else + strncpy(ubuf, str, nBytes); +#endif /*kUseMemcopy*/ +#endif + return ubuf; +} + +/******************************************************************************/ +/* + * Convert a unicode string to an ansi string. On an error, return the + * original unicode string which is better than returning NULL. + * N.B. nBytes is the number of _bytes_ in the destination buffer, buf. + */ + +char *uniToAsc(char *buf, char_t *ustr, int nBytes) +{ +#ifdef UNICODE + if (WideCharToMultiByte(CP_ACP, 0, ustr, nBytes, buf, nBytes, + NULL, NULL) < 0) + { + return (char*) ustr; + } +#else +#ifdef kUseMemcopy + memcpy(buf, ustr, nBytes); +#else + strncpy(buf, ustr, nBytes); +#endif /* kUseMemcopy */ +#endif + return (char*) buf; +} + +/******************************************************************************/ +/* + * allocate (balloc) a buffer and do ascii to unicode conversion into it. + * 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, int alen) +{ + char_t *unip; + int ulen; + + ulen = (alen + 1) * sizeof(char_t); + if ((unip = balloc(B_L, ulen)) == NULL) { + return NULL; + } + ascToUni(unip, cp, ulen); + unip[alen] = 0; + return unip; +} + +/******************************************************************************/ +/* + * 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 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 * cp; + + 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/cleopatre/application/spidgoahead/mocana_ssl.c b/cleopatre/application/spidgoahead/mocana_ssl.c new file mode 100644 index 0000000000..b77bebe057 --- /dev/null +++ b/cleopatre/application/spidgoahead/mocana_ssl.c @@ -0,0 +1,539 @@ +/* + * mocana_ssl.c + * + * Mocana Embedded SSL Server + * GoAhead integration layer + * + * Copyright Mocana Corp 2003. All Rights Reserved. + * + */ + + +/******************************** Description *********************************/ + +/* + * This module implements a patch into the Mocana Embedded SSL Server. + * + */ + +#ifdef __ENABLE_MOCANA_SSL_SERVER__ + +/********************************* Includes ***********************************/ + +#include "mocana_ssl/common/moptions.h" +#include "mocana_ssl/common/mdefs.h" +#include "mocana_ssl/common/mtypes.h" +#include "mocana_ssl/common/merrors.h" +#include "mocana_ssl/common/mrtos.h" +#include "mocana_ssl/common/mtcp.h" +#include "mocana_ssl/common/mocana.h" +#include "mocana_ssl/common/random.h" +#include "mocana_ssl/ssl/ssl.h" + +#include "wsIntrn.h" +#include "webs.h" +#include "websSSL.h" + +#include +#include +#include + + +/******************************* Definitions **********************************/ + +#ifdef __RTOS_VXWORKS__ +#define SSL_CERTIFICATE_DER_FILE "NVRAM:/ssl.der" +#define SSL_RSA_HOST_KEYS "NVRAM:/sslkey.dat" +#else +#define SSL_CERTIFICATE_DER_FILE "ssl.der" +#define SSL_RSA_HOST_KEYS "sslkey.dat" +#endif + +#define SSL_PORT SSL_DEFAULT_TCPIP_PORT +#define MAX_SSL_CONNECTIONS_ALLOWED (10) +#define SSL_HELLO_TIMEOUT (15000) +#define SSL_RECV_TIMEOUT (300000) + +#define SSL_EXAMPLE_KEY_SIZE (1024 + 256) + +static int sslListenSock = -1; /* Listen socket */ +static char* pCertificate = NULL; +static char* pRsaKeyBlob = NULL; + + +/******************************* Prototypes **********************************/ + +int websSSLAccept(int sid, char *ipaddr, int port, int listenSid); +static void websSSLSocketEvent(int sid, int mask, int iwp); +static int websSSLReadEvent(webs_t wp); +static int mocana_SSL_computeHostKeys(char** ppCertificate, unsigned int *pCertLength, + char** ppRsaKeyBlob, unsigned int *pKeyBlobLength); +static int mocana_SSL_releaseHostKeys(char **ppCertificate, char **ppRsaKeyBlob); + + + +/******************************************************************************/ +/* + * Start up the SSL Context for the application, and start a listen on the + * SSL port (usually 443, and defined by SSL_PORT) + * Return 0 on success, -1 on failure. + */ + +int websSSLOpen() +{ + unsigned int certLength; + unsigned int rsaKeyBlobLength; + + if (0 > MOCANA_initMocana()) + return -1; + + if (0 > mocana_SSL_computeHostKeys(&pCertificate, &certLength, &pRsaKeyBlob, &rsaKeyBlobLength)) + return -1; + + if (0 > SSL_init(pCertificate, certLength, pRsaKeyBlob, rsaKeyBlobLength, MAX_SSL_CONNECTIONS_ALLOWED)) + return -1; + + SSL_sslSettings()->sslTimeOutHello = SSL_HELLO_TIMEOUT; + SSL_sslSettings()->sslTimeOutReceive = SSL_RECV_TIMEOUT; + + sslListenSock = socketOpenConnection(NULL, SSL_PORT, websSSLAccept, SOCKET_BLOCK); + + if (sslListenSock < 0) { + trace(2, T("SSL: Unable to open SSL socket on port <%d>!\n"), + SSL_PORT); + return -1; + } + + return 0; +} + + +/******************************************************************************/ +/* + * Accept a connection + */ + +int websSSLAccept(int sid, char *ipaddr, int port, int listenSid) +{ + webs_t wp; + int wid; + + a_assert(ipaddr && *ipaddr); + a_assert(sid >= 0); + a_assert(port >= 0); + +/* + * Allocate a new handle for this accepted connection. This will allocate + * a webs_t structure in the webs[] list + */ + if ((wid = websAlloc(sid)) < 0) { + return -1; + } + wp = webs[wid]; + a_assert(wp); + wp->listenSid = listenSid; + + ascToUni(wp->ipaddr, ipaddr, min(sizeof(wp->ipaddr), strlen(ipaddr)+1)); + +/* + * Check if this is a request from a browser on this system. This is useful + * to know for permitting administrative operations only for local access + */ + if (gstrcmp(wp->ipaddr, T("127.0.0.1")) == 0 || + gstrcmp(wp->ipaddr, websIpaddr) == 0 || + gstrcmp(wp->ipaddr, websHost) == 0) { + wp->flags |= WEBS_LOCAL_REQUEST; + } +/* + * Since the acceptance came in on this channel, it must be secure + */ + wp->flags |= WEBS_SECURE; + +/* + * Arrange for websSocketEvent to be called when read data is available + */ + socketCreateHandler(sid, SOCKET_READABLE, websSSLSocketEvent, (int) wp); + +/* + * Arrange for a timeout to kill hung requests + */ + wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout, (void *) wp); + trace(8, T("webs: accept request\n")); + return 0; +} + + +/******************************************************************************/ +/* + * Perform a read of the SSL socket + */ + +int websSSLRead(websSSL_t *wsp, char_t *buf, int len) +{ + int numBytesReceived; + int rc; + + a_assert(wsp); + a_assert(buf); + + if ((rc = SSL_recv(wsp->mocanaConnectionInstance, buf, len, &numBytesReceived)) < 0) + { + if (0 > rc) + rc = -1; + + return rc; + } + + return numBytesReceived; +} + + +/******************************************************************************/ +/* + * The webs socket handler. Called in response to I/O. We just pass control + * to the relevant read or write handler. A pointer to the webs structure + * is passed as an (int) in iwp. + */ + +static void websSSLSocketEvent(int sid, int mask, int iwp) +{ + webs_t wp; + + wp = (webs_t) iwp; + a_assert(wp); + + if (! websValid(wp)) { + return; + } + + if (mask & SOCKET_READABLE) { + websSSLReadEvent(wp); + } + if (mask & SOCKET_WRITABLE) { + if (wp->writeSocket) { + (*wp->writeSocket)(wp); + } + } +} + + +/******************************************************************************/ +/* + * Handler for SSL Read Events + */ + +static int websSSLReadEvent (webs_t wp) +{ + int ret = 07, sock; + socket_t *sptr; + int connectionInstance; + + a_assert (wp); + a_assert(websValid(wp)); + + sptr = socketPtr(wp->sid); + a_assert(sptr); + + sock = sptr->sock; + +/* + * Create a new SSL session for this web request + */ + + connectionInstance = SSL_acceptConnection(sock); + + if (0 > connectionInstance) + { +#ifdef __ENABLE_ALL_DEBUGGING__ + printf("websSSLReadEvent: SSL_acceptConnection failed. %d\n", connectionInstance); +#endif + + /* SSL error: cleanup */ + websTimeoutCancel(wp); + socketCloseConnection(wp->sid); + websFree(wp); + + return -1; + } + +/* + * Create the SSL data structure in the wp. + */ + wp->wsp = balloc(B_L, sizeof(websSSL_t)); + a_assert (wp->wsp); + + (wp->wsp)->mocanaConnectionInstance = connectionInstance; + (wp->wsp)->wp = wp; + +/* + * Call the default Read Event + */ + websReadEvent(wp); + + return ret; +} + + +/******************************************************************************/ +/* + * Return TRUE if websSSL has been opened + */ + +int websSSLIsOpen() +{ + return (sslListenSock != -1); +} + + +/******************************************************************************/ +/* + * Perform a gets of the SSL socket, returning an balloc'ed string + * + * 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. If doing non-blocking I/O, it returns -1 for error, EOF or when + * no complete line yet read. If doing blocking I/O, it will block until an + * entire line is read. 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. + */ + +int websSSLGets(websSSL_t *wsp, char_t **buf) +{ + socket_t *sp; + ringq_t *lq; + char c; + int rc, len; + webs_t wp; + int sid; + int mci; + int numBytesReceived; + + a_assert(wsp); + a_assert(buf); + + *buf = NULL; + + wp = wsp->wp; + sid = wp->sid; + mci = wsp->mocanaConnectionInstance; + + if ((sp = socketPtr(sid)) == NULL) { + return -1; + } + lq = &sp->lineBuf; + + while (1) { + + if ((rc = SSL_recv(mci, &c, 1, &numBytesReceived)) < 0) + { + if (0 > rc) + rc = -1; + + return rc; + } + + if (numBytesReceived == 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 { + continue; + } + } +/* + * If a newline is seen, return the data excluding the new line to the + * caller. If carriage return is seen, just eat it. + */ + if (c == '\n') { + len = ringqLen(lq); + if (len > 0) { + *buf = ballocAscToUni((char *)lq->servp, len); + } else { + *buf = NULL; + } + ringqFlush(lq); + return len; + + } else if (c == '\r') { + continue; + } + ringqPutcA(lq, c); + } + return 0; +} + + +/******************************************************************************/ +/* + * Perform a write to the SSL socket + */ + +int websSSLWrite(websSSL_t *wsp, char_t *buf, int len) +{ + int sslBytesSent; + + a_assert(wsp); + a_assert(buf); + + if (wsp == NULL) { + return -1; + } + + sslBytesSent = SSL_send(wsp->mocanaConnectionInstance, buf, len); + + if (0 > sslBytesSent) + sslBytesSent = -1; + + return sslBytesSent; +} + + +/******************************************************************************/ +/* + * Return Eof for the underlying socket + */ + +int websSSLEof(websSSL_t *wsp) +{ + webs_t wp; + int sid; + + a_assert(wsp); + + wp = wsp->wp; + sid = wp->sid; + + return socketEof(sid); +} + + +/******************************************************************************/ +/* + * Flush stub for compatibility + */ +int websSSLFlush(websSSL_t *wsp) +{ + a_assert(wsp); + + /* Autoflush - do nothing */ + return 0; +} + + +/******************************************************************************/ +/* + * Free SSL resources + */ + +int websSSLFree(websSSL_t *wsp) +{ + int status; + if (NULL != wsp) + { + int mci; + + mci = wsp->mocanaConnectionInstance; + + status = SSL_closeConnection(mci); + + if (0 > status) + status = -1; + + /* Free memory here.... */ + bfree(B_L, wsp); + } + + return status; +} + + +/******************************************************************************/ +/* + * Stops the SSL system + */ + +void websSSLClose() +{ + SSL_shutdown(); + mocana_SSL_releaseHostKeys(&pCertificate, &pRsaKeyBlob); + SSL_releaseTables(); + MOCANA_freeMocana(); +} + + +/******************************************************************************/ + +static int +mocana_SSL_testHostKeys(char** ppCertificate, unsigned int *pCertLength, + char** ppRsaKeyBlob, unsigned int *pKeyBlobLength) +{ + int status; + + if (0 > (status = MOCANA_readFile(SSL_CERTIFICATE_DER_FILE, ppCertificate, pCertLength))) + goto exit; + + status = MOCANA_readFile(SSL_RSA_HOST_KEYS, ppRsaKeyBlob, pKeyBlobLength); + +exit: + return status; +} + + +/******************************************************************************/ + +static int +mocana_SSL_releaseHostKeys(char **ppCertificate, char **ppRsaKeyBlob) +{ + MOCANA_freeReadFile(ppCertificate); + MOCANA_freeReadFile(ppRsaKeyBlob); + + return 0; +} + + +/******************************************************************************/ + +static int +mocana_SSL_computeHostKeys(char** ppCertificate, unsigned int *pCertLength, + char** ppRsaKeyBlob, unsigned int *pKeyBlobLength) +{ + int status; + + *ppCertificate = NULL; + *ppRsaKeyBlob = NULL; + + /* check for pre-existing set of host keys */ + if (0 > (status = mocana_SSL_testHostKeys(ppCertificate, pCertLength, ppRsaKeyBlob, pKeyBlobLength))) + { +#ifdef __ENABLE_ALL_DEBUGGING__ + printf("mocana_SSL_computeHostKeys: host keys do not exist, computing new key pair.\n"); +#endif + + /* if not, compute new host keys */ + if (0 > (status = SSL_generateCertificate(ppCertificate, pCertLength, ppRsaKeyBlob, pKeyBlobLength, SSL_EXAMPLE_KEY_SIZE))) + goto exit; + + if (0 > (status = MOCANA_writeFile(SSL_CERTIFICATE_DER_FILE, *ppCertificate, *pCertLength))) + goto exit; + + status = MOCANA_writeFile(SSL_RSA_HOST_KEYS, *ppRsaKeyBlob, *pKeyBlobLength); + +#ifdef __ENABLE_ALL_DEBUGGING__ + printf("mocana_SSL_computeHostKeys: host key computation completed.\n"); +#endif + } + +exit: + if (0 > status) + SSL_freeCertificate(ppCertificate, ppRsaKeyBlob); + + return status; +} + + +/******************************************************************************/ + +#endif diff --git a/cleopatre/application/spidgoahead/page.c b/cleopatre/application/spidgoahead/page.c new file mode 100644 index 0000000000..27a3d77f00 --- /dev/null +++ b/cleopatre/application/spidgoahead/page.c @@ -0,0 +1,143 @@ +/* + * Page.c -- Support for page retrieval. + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: page.c,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * This module provides page retrieval handling. It provides support for + * reading web pages from file systems and has expansion for ROMed web + * pages. + */ + +/********************************* Includes ***********************************/ + +#include "wsIntrn.h" + +/*********************************** Code *************************************/ +/* + * Open a web page. lpath is the local filename. path is the URL path name. + */ + +int websPageOpen(webs_t wp, char_t *lpath, char_t *path, int mode, int perm) +{ + a_assert(websValid(wp)); + +#ifdef WEBS_PAGE_ROM + return websRomPageOpen(wp, path, mode, perm); +#else + return (wp->docfd = gopen(lpath, mode, perm)); +#endif /* WEBS_PAGE_ROM */ +} + +/******************************************************************************/ +/* + * Close a web page + */ + +void websPageClose(webs_t wp) +{ + a_assert(websValid(wp)); + +#ifdef WEBS_PAGE_ROM + websRomPageClose(wp->docfd); +#else + if (wp->docfd >= 0) { + close(wp->docfd); + wp->docfd = -1; + } +#endif +} + +/******************************************************************************/ +/* + * Stat a web page lpath is the local filename. path is the URL path name. + */ + +int websPageStat(webs_t wp, char_t *lpath, char_t *path, websStatType* sbuf) +{ +#ifdef WEBS_PAGE_ROM + return websRomPageStat(path, sbuf); +#else + gstat_t s; + + if (gstat(lpath, &s) < 0) { + return -1; + } + sbuf->size = s.st_size; + sbuf->mtime = s.st_mtime; + sbuf->isDir = s.st_mode & S_IFDIR; + return 0; +#endif +} + +/******************************************************************************/ +/* + * Is this file a directory? + */ + +int websPageIsDirectory(char_t *lpath) +{ +#ifdef WEBS_PAGE_ROM + websStatType sbuf; + + if (websRomPageStat(lpath, &sbuf) >= 0) { + return(sbuf.isDir); + } else { + return 0; + } +#else + gstat_t sbuf; + + if (gstat(lpath, &sbuf) >= 0) { + return(sbuf.st_mode & S_IFDIR); + } else { + return 0; + } +#endif +} + + +/******************************************************************************/ +/* + * Read a web page. Returns the number of _bytes_ read. + * len is the size of buf, in bytes. + */ + +int websPageReadData(webs_t wp, char *buf, int nBytes) +{ + +#ifdef WEBS_PAGE_ROM + a_assert(websValid(wp)); + return websRomPageReadData(wp, buf, nBytes); +#else + a_assert(websValid(wp)); + return read(wp->docfd, buf, nBytes); +#endif +} + +/******************************************************************************/ +/* + * Move file pointer offset bytes. + */ + +void websPageSeek(webs_t wp, long offset) +{ + a_assert(websValid(wp)); + +#ifdef WEBS_PAGE_ROM + websRomPageSeek(wp, offset, SEEK_CUR); +#else + lseek(wp->docfd, offset, SEEK_CUR); +#endif +} + +/******************************************************************************/ + + diff --git a/cleopatre/application/spidgoahead/readme.txt b/cleopatre/application/spidgoahead/readme.txt new file mode 100644 index 0000000000..3c705b88ba --- /dev/null +++ b/cleopatre/application/spidgoahead/readme.txt @@ -0,0 +1,3 @@ +For current WebServer startup instructions and other information +specific to Release 2.1, please refer to the release.htm file +(Release Notes). diff --git a/cleopatre/application/spidgoahead/release.htm b/cleopatre/application/spidgoahead/release.htm new file mode 100644 index 0000000000..775616fe8d --- /dev/null +++ b/cleopatre/application/spidgoahead/release.htm @@ -0,0 +1,637 @@ + + + + + + +GoAhead WebServer 2.1.8 Release Notes + + + +
+

GoAhead WebServer 2.1.8 Release Notes

+ + + + + + + + + + +
+

Contents

+ +
+
+

GoAhead WebServer 2.1.8 Release Notes

+
+
Release Date:
+
02 Dec 2003
+
+
+

Problems with Unicode build

+
+
Description:
+
Missing T() caused trouble in Unicode build.
+
Fix:
+
Added T() macros.
+
+
+
+

Modified for Windows CE .NET

+
+
Description:
+
Modified to work with Windows CE .NET and eMbedded Visual C++ 4.
+
+
+
+

Bug with URLs like "<...>.asp/"

+
+
Description:
+
URLs ending in ".asp/", ".asp\", ".as%70" and other variants made the +WebServer serve Ejscript source code.
+
Fix:
+
Added code to ignore these differences.
+
+
+
+
+

GoAhead WebServer 2.1.7 Release Notes

+
+
Release Date:
+
01 Oct 2003
+
+
+

Added support for the Mocana SSL Toolkit

+
+
Description:
+
Added support for Mocana Corporation's embedded SSL server
+
+
+
+

Changes to dbSearchString()

+

Description:

+
+Pass DB_CASE_INSENSITIVE as the "flags" argument to +dbSearchString() to force a case-insensitive search.
+
+
+

Use memcpy() when converting to/from Unicode

+
+
Description:
+
The functions uniToAsc() and ascToUni() were using the relatively +slow strncpy() runtime library function.
+
Fix:
+
A new preprocessor macro kUseMemcopy was added to misc.c, and both +functions were recoded to use memcpy() when that macro is defined. +Remove the definition to revert to the earlier code, using strncpy().
+
+
+
+

Bug when using UTF-8 encoded text inside ASP/Ejscript blocks

+
+
Description:
+
When reading ASP code containing UTF-8 encoded source text, any characters +encountered having a value > 127 were treated as an error by the parser.
+
Fix:
+
The ring queue code in ringq.c was modified so that it can correctly +handle any character it encounters by casting to unsigned char before +casting back to signed integer.
+
+
+
+

Wrong error code on invalid password

+
+
Description:
+
The WebServer was sending back an inappropriate error code when it received +an incorrect password.
+
Fix:
+
Changed error code returned from 405 to 401. (Thanks to Jay +Chalfant).
+
+
+
+

Windows CE .NET

+
+
Description:
+
Removed "compatibility functions" that are directly supported in Windows +CE .NET.
+
+
+
+

LYNX Makefile

+
+
Description:
+
Corrected problem in LYNX Makefile that prevented OpenSSL from being linked +in correctly.
+
+
+
+
+

GoAhead WebServer 2.1.6 Release Notes

+
+
Release Date:
+
25 Mar 2003
+
+
+

NULL pointer crash in websSafeUrl()

+
+
Description:
+
Passing a NULL pointer into the websSafeUrl() function (as would happen +when the server is processing an invalid URL) crashes the server.
+
Fix:
+
Code modified to check for NULL pointer before performing any string +operations.
+
+
+
+
+

GoAhead WebServer 2.1.5 Release Notes

+
+
Release Date:
+
19 Mar 2003
+
+
+

bopen() failure mode

+
+
Description:
+
New failure behavior for bopen() (see balloc.c)
+
Fix:
+
Changed failure behavior of the bopen() function (suggested by Simon +Byholm). If the malloc() request fails, we reset the bopenCount +variable, and thus allow the client code to reattempt the open with +a smaller memory request.
+
+
+
+

Windows 95/98/ME AUX Denial of Service

+
+
Description:
+

When running on Windows 95/98/ME, URLs requested with path components +matching a set of reserved DOS device names caused the WebServer to crash.

+

The names that cause the crash are:

+
+aux
+con
+nul
+clock$
+config$
+
+
+
Fix:
+

Code added to the websValidateUrl() function to check the contents of +the parsed URL against the list of prohibited names. If any of those names +are present in the parsed URL, the URL is rejected as invalid.

+

The code that checks for these prohibited pathname components checks for +them in the form of either:

+
+http://<<server address>>/aux
+
+

or:

+
+http://<<server address>>/aux:
+
+

where any non-alphanumeric character following one of the prohibited names +will cause the URL request to be rejected.

+
+
+
+
+

404 Cross-site Scripting Exploit

+
+
Description:
+
Malicious users could request an invalid URL containing embedded JavaScript +code that would be executed in the user's browser.
+
Fix:
+
404 (and other error messages) returned by the WebServer no longer display +the invalid URL.
+
+
+
+

Long URL Overflow Crash

+
+
Description:
+
URLs containing more than 64 levels of path components caused the WebServer +to crash, entering a buffer overflow condition.
+
Fix:
+
The WebServer now keeps track of the depth as it validates the URL. URLs +that are too long are now rejected with an error message.
+
+
+
+

Incorrect Error Code in security.c

+
+
Description:
+
Pages assigned an access level of AM_NONE should return an error code +of 404 instead of 400 when an attempt it made to access them.
+
Fix:
+
Error code corrected.
+
+
+
+

Pragma Code for RISC Architectures

+
+
Description:
+
A pragma was not set correctly when compiling for SPARC machines.
+
Fix:
+

Code added to uemf.h:

+
+#ifdef sparc
+# define __NO_PACK
+#endif /* sparc */
+
+
+
+
+
+
+

GoAhead® WebServer 2.1.4 Release Notes

+
+
Release Date:
+
17 Oct 2002
+
+
+

Fixed vulnerability to malicious code in webs.c

+
+
Summary:
+
There were two vulnerabilities in webs.c. Sending a POST message +with a content-length but no data resulted in an attempt to perform +a strlen() on a NULL pointer (thanks to Richard Cullen). Also, +sending an HTTP POST message with a Content-Length header indicating +that the length of the posted data was less than zero would crash +the server (thanks to Auriemma Luigi).
+
Fix:
+
Code errors corrected.
+
+
+
+

https:// bug in security handler

+
+
Summary:
+
The websSecurityHandler() function was performing a logical +OR: (flags | WEBS_SECURE) instead of a logical AND (flags & +WEBS_SECURE), leading to incorrect results (thanks to "Dhanwa T").
+
Fix:
+
Code errors corrected.
+
+
+
+

Fixed vulnerability to malicious code in sockGen.c

+
+
Summary:
+
At line 613 of sockGen.c, the return value of the function +socketInputBuffered() was being discarded, leading to incorrect +behavior in some cases. (Thanks to Richard Cullen)
+
Fix:
+
Code errors corrected.
+
+
+
+
+

Bug Fixes for Version 2.1.3

+
+

Directory Traversal Exploit

+
+
Summary:
+
Several sources have reported that requesting an URL with URL-encoded +backslashes (%5C) allow accessing files located outside the server's +designated web root. This should only have been possible on Windows, as +URL-encoded forward slashes (%2F) were already being handled correctly.
+
Fix:
+
Modified default.c so that any backslash characters created as +part of decoding the URL string are converted (in place) to forward +slashes. The pre-existing directory-traversal detection code then +rejects the bad URL as expected.
+
+
+
+

MIME type for external JavaScript files

+
+
Summary:
+
The WebServer would not serve external JavaScript files (*.js) +correctly.
+
Fix:
+
modified mime.c to associate .js files with the MIME +type application/x-javascript.
+
+
+
+

Bug in If-Modified-Since parsing

+
+
Summary:
+
There was an off-by-one error when converting from Gregorian date to +time_t.
+
Fix:
+
modified function dateToTimet in file webs.c. The month +parameter is numbered from 0 (Jan == 0), but FixedFromGregorian() +takes months numbered from 1 (January == 1). We need to add 1 to +the month before calling FixedFromGregorian.
+
+
+
+
+

Bug Fixes for Version 2.1.2

+
+

Ejscript Error Messages

+

Summary:

+
+Changed ejEval() function so it displays in the browser the error string that is +reported by the Ejscript interpreter (old code discarded it completely).
+
+
Fix:
+
modified websuemf.c
+
+
+
+

Security Handler Response Codes

+
+
Summary:
+
Several places in the websSecurityHandler() function were +returning error code 200 (success) instead of the correct 400-level error code.
+
Fix:
+
Corrected error codes in security.c
+
+
+
+

Security Handler Memory Leak

+
+
Summary:
+
In websSecurityHandler(), if the WebServer was compiled with +WEBS_SSL_SUPPORT enabled, there was a return path that leaked +memory.
+
Fix:
+
Added call to bfree(B_L, accessLimit);
+
+
+
+

Ejscript Write Corruption

+
+
Summary:
+
Very long text strings passed to the Ejscript write() function +were being corrupted before being displayed.
+
Fix:
+
Commented out a trace() statement in ejGetLexToken() that appears to have been the +culprit. The value of ep->token was being corrupted somewhere +in the trace.
+
+
+
+

Error in dsnprintf(): "%X" format

+
+
Summary:
+
The "%X" format specifier did not correctly cause the function to output +uppercase hexadecimal digits.
+
Fix:
+
Added support for the "%X" format specifier.
+
+
+
+

BUG018565 Re-fixed

+
+
Summary:
+
See 2.1.1 release notes (below). This bug fix did not make it into the +2.1.1 release.
+
Fix:
+
Corrected code in sockGen.c.
+
+
+
+

Potential Error in error()

+
+
Summary:
+
If error() is called where the etype parameter is not E_LOG, E_ASSERT, +or E_USER, the call to bfreeSafe(B_L, buf) now at line 71 will fail, +because 'buf' is randomly initialized.
+
Fix:
+
We format a message saying that this is an unknown message type, +and in doing so give buf a valid value.
+
+
+
+

Added Support For Customized Access Control

+
+
Summary:
+
Several users requested a method to control URL access in a hierarchical +fashion. For example, users assigned to an 'admin' group might have +access to all URLs on the WebServer, and users assigned to the group +'technician' would have access to a smaller set of pages, and users +assigned to the group 'users' would perhaps only have access to a set of +read-only pages. The built-in WebServer access control system only +allows users to access URLs that exactly match their group membership.
+
Fix:
+
Added call to a user-supplied function `` bool_t dmfCanAccess(const +char_t* userGroup, const char_t* group)``. This function is called +from inside umUserCanAccessURL() if the macro +qHierarchicalAccess is defined.
+
+
+
+

Memory Leak in websParseRequest()

+
+
Summary:
+
Memory was being leaked in the code now at line 907 of webs.c.
+
Fix:
+
Added a call to bfree().
+
+
+
+
+

Macintosh OS X Support

+

A separate Mac OS X platform directory has been added, and this platform +has been tested on version 10.1.5 of the operating system. +To build the WebServer on OS X:

+
+cd MACOSX
+make
+
+

Note that like all *nix operating systems, only the root user has +permission to open a server port with a lower number than 1024. You must +run the WebServer as root to serve pages over port 80, or change the server +port (in main.c) to a different port (typically port 8080).

+
+
+

Bug Fixes for Version 2.1.1

+
+

Intermittent Access Error for CGI Scripts (BUG01937)

+
+
Summary:
+
Pages were occasionally replaced with the message, Access Error: Data +follows when trying to obtain CGI generated no output.
+
Fix:
+
On multiple CPU systems, it is possible for a CGI application to exit before +its output is flushed to disk. The change for this release locates the code +that collects the output from the CGI application in a separate routine. +In addition to calling that routine from within the CGI application processing +loop, it is also called in a brief loop after the CGI application has exited. +This extra loop runs for only up to one second while the collected output +remains empty. If, after 1 second, the output remains empty, the original +course of action is followed (Access Error is reported).
+
+
+
+

CPU Utilization Hangs at 100% on a Socket Disconnect (BUG01865)

+
+
Summary:
+
This error occurs whenever a user terminates a request before the server +has had ample time to service it. This can be simulated by quickly hitting +the refresh button on the browser twice in a row, causing the first request +to be terminated. The server then enters into a tight loop that utilizes +all of its time.
+
Fix:
+
Always close the socket prior to a disconnect.
+
+
+
+

Security Features can be Bypassed by Adding an Extra Slash in the URL (BUG01518)

+
+
Summary:
+
By adding an extra slash in a URL, password prompting is bypassed, allowing +unrestricted access to secured pages.
+
Fix:
+
Remove multiple adjacent occurrences of slashes in the URL before passing +it to the security handler.
+
+
+
+

Call to websSetVar causes a crash (BUG01938)

+
+
Summary:
+
Whenever a request is not completed while being processed by websGetInput(), +a call to websDone is made, followed by an attempt to process the partial +request data.
+
Fix:
+
Return immediately after closing the socket.
+
+
+
+

Remove stray semicolon in emfdb.c (BUG01820)

+
+
Summary and Fix:
+
A stray semicolon was removed from this file.
+
+
+
+
+

Novell Netware Support

+

With the addition of Novell Netware in this 2.11 release, WebServer now supports these platforms:

+
+
    +
  • LINUX
  • +
  • LynxOS
  • +
  • Novell Netware </font>4.2, 5.1
  • +
  • Mac OS X
  • +
  • UNIX - SCO OpenServer 3.2V5.0.0
  • +
  • VxWorks 5.3.1
  • +
  • Windows 2000
  • +
  • Windows 98
  • +
  • Windows 95
  • +
  • Windows NT
  • +
  • Windows CE
  • +
+
+

To make a Novell Netware target file (webs.nlm):

+
+cd NW
+wmake webs.nlm
+load <path>; \webs.nlm
+webs
+
+

Note: This makefile lacks a valid default rule. In addition, an +environment variable (QMKVER) controls the amount of debug information +that is compiled and linked into the nlm file. If this variable is set +to P, it produces a production version. All other settings +(or the omission of the variable) results in a debug version. For other +platforms supported by WebServer, please refer to your WebServer 2.1 +documentation for appropriate instructions.

+
+ +
+ + diff --git a/cleopatre/application/spidgoahead/release.txt b/cleopatre/application/spidgoahead/release.txt new file mode 100644 index 0000000000..847eff609c --- /dev/null +++ b/cleopatre/application/spidgoahead/release.txt @@ -0,0 +1,625 @@ +===================================== +GoAhead WebServer 2.1.8 Release Notes +===================================== + +.. NOTES: +.. This document is maintained using the reStructuredText markup system. +.. You may download this from . Also note that running +.. the docutils code requires that a version of Python version 2.1 or later +.. be installed on the machine. Since the GoAhead release procedure itself +.. runs in Python, this should not be a problem. +.. +.. To add new entries to the release notes, follow the markup shown below +.. (releases should be underlined with a row of '=' characters, each item +.. noted within a release should be underlined with '-' characters. + + +.. contents:: + +GoAhead WebServer 2.1.8 Release Notes +===================================== + +Release Date: + 02 Dec 2003 + +Problems with Unicode build +---------------------------------------- + +Description: + Missing T() caused trouble in Unicode build. + +Fix: + Added T() macros. + +Modified for Windows CE .NET +---------------------------------------- + +Description: + Modified to work with Windows CE .NET and eMbedded Visual C++ 4. + +Bug with URLs like "<...>.asp/" +---------------------------------------- + +Description: + URLs ending in ".asp/", ".asp\\", ".as%70" and other variants made the + WebServer serve Ejscript source code. + +Fix: + Added code to ignore these differences. + +GoAhead WebServer 2.1.7 Release Notes +===================================== + +Release Date: + 01 Oct 2003 + +Added support for the Mocana SSL Toolkit +---------------------------------------- + +Description: + Added support for Mocana Corporation's embedded SSL server + +Changes to ``dbSearchString()`` +------------------------------- + +Description: + + Pass ``DB_CASE_INSENSITIVE`` as the "flags" argument to + dbSearchString() to force a case-insensitive search. + + +Use ``memcpy()`` when converting to/from Unicode +------------------------------------------------ +Description: + The functions ``uniToAsc()`` and ``ascToUni()`` were using the relatively + slow ``strncpy()`` runtime library function. + +Fix: + A new preprocessor macro ``kUseMemcopy`` was added to ``misc.c``, and both + functions were recoded to use ``memcpy()`` when that macro is defined. + Remove the definition to revert to the earlier code, using ``strncpy()``. + + +Bug when using UTF-8 encoded text inside ASP/Ejscript blocks +--------------------------------------------------------------- + +Description: + When reading ASP code containing UTF-8 encoded source text, any characters + encountered having a value > 127 were treated as an error by the parser. + +Fix: + The ring queue code in ``ringq.c`` was modified so that it can correctly + handle any character it encounters by casting to unsigned char before + casting back to signed integer. + +Wrong error code on invalid password +------------------------------------ + +Description: + The WebServer was sending back an inappropriate error code when it received + an incorrect password. + +Fix: + Changed error code returned from ``405`` to ``401``. (Thanks to Jay + Chalfant). + +Windows CE .NET +----------------- + +Description: + Removed "compatibility functions" that are directly supported in Windows + CE .NET. + +LYNX ``Makefile`` +----------------- + +Description: + Corrected problem in LYNX Makefile that prevented OpenSSL from being linked + in correctly. + +GoAhead WebServer 2.1.6 Release Notes +===================================== +Release Date: + 25 Mar 2003 + +``NULL`` pointer crash in ``websSafeUrl()`` +------------------------------------------- + +Description: + Passing a NULL pointer into the ``websSafeUrl()`` function (as would happen + when the server is processing an invalid URL) crashes the server. + +Fix: + Code modified to check for NULL pointer before performing any string + operations. + + +GoAhead WebServer 2.1.5 Release Notes +===================================== +Release Date: + 19 Mar 2003 + + + +``bopen()`` failure mode +------------------------ + + + +Description: + New failure behavior for ``bopen()`` (see ``balloc.c``) + +Fix: + Changed failure behavior of the bopen() function (suggested by Simon + Byholm). If the malloc() request fails, we reset the bopenCount + variable, and thus allow the client code to reattempt the open with + a smaller memory request. + + +Windows 95/98/ME ``AUX`` Denial of Service +------------------------------------------ + + + +Description: + When running on Windows 95/98/ME, URLs requested with path components + matching a set of reserved DOS device names caused the WebServer to crash. + + The names that cause the crash are:: + + aux + con + nul + clock$ + config$ + + + + +Fix: + Code added to the ``websValidateUrl()`` function to check the contents of + the parsed URL against the list of prohibited names. If any of those names + are present in the parsed URL, the URL is rejected as invalid. + + The code that checks for these prohibited pathname components checks for + them in the form of either:: + + http://<>/aux + + or:: + + http://<>/aux: + + where any non-alphanumeric character following one of the prohibited names + will cause the URL request to be rejected. + + +404 Cross-site Scripting Exploit +----------------------------------- + + +Description: + Malicious users could request an invalid URL containing embedded JavaScript + code that would be executed in the user's browser. + +Fix: + 404 (and other error messages) returned by the WebServer no longer display + the invalid URL. + +Long URL Overflow Crash +----------------------- + + +Description: + URLs containing more than 64 levels of path components caused the WebServer + to crash, entering a buffer overflow condition. + +Fix: + The WebServer now keeps track of the depth as it validates the URL. URLs + that are too long are now rejected with an error message. + +Incorrect Error Code in ``security.c`` +-------------------------------------- + + +Description: + Pages assigned an access level of ``AM_NONE`` should return an error code + of 404 instead of 400 when an attempt it made to access them. + +Fix: + Error code corrected. + +Pragma Code for RISC Architectures +------------------------------------- + + +Description: + A pragma was not set correctly when compiling for SPARC machines. + +Fix: + Code added to ``uemf.h``:: + + #ifdef sparc + # define __NO_PACK + #endif /* sparc */ + + + + + +GoAhead® WebServer 2.1.4 Release Notes +======================================= + +Release Date: + 17 Oct 2002 + + + + +Fixed vulnerability to malicious code in ``webs.c`` +--------------------------------------------------- + + + +Summary: + There were two vulnerabilities in ``webs.c``. Sending a POST message + with a content-length but no data resulted in an attempt to perform + a ``strlen()`` on a NULL pointer (thanks to Richard Cullen). Also, + sending an HTTP POST message with a Content-Length header indicating + that the length of the posted data was less than zero would crash + the server (thanks to Auriemma Luigi). + +Fix: + Code errors corrected. + + + +https:// bug in security handler +-------------------------------- + + +Summary: + The ``websSecurityHandler()`` function was performing a logical + OR: (``flags | WEBS_SECURE``) instead of a logical AND (``flags & + WEBS_SECURE``), leading to incorrect results (thanks to "Dhanwa T"). + +Fix: + Code errors corrected. + + + +Fixed vulnerability to malicious code in sockGen.c +-------------------------------------------------- + + +Summary: + At line 613 of ``sockGen.c``, the return value of the function + ``socketInputBuffered()`` was being discarded, leading to incorrect + behavior in some cases. (Thanks to Richard Cullen) + +Fix: + Code errors corrected. + + +Bug Fixes for Version 2.1.3 +=========================== + + +Directory Traversal Exploit +---------------------------- + + +Summary: + Several sources have reported that requesting an URL with URL-encoded + backslashes (%5C) allow accessing files located outside the server's + designated web root. This should only have been possible on Windows, as + URL-encoded forward slashes (%2F) were already being handled correctly. + +Fix: + Modified ``default.c`` so that any backslash characters created as + part of decoding the URL string are converted (in place) to forward + slashes. The pre-existing directory-traversal detection code then + rejects the bad URL as expected. + + +MIME type for external JavaScript files +---------------------------------------- + + +Summary: + The WebServer would not serve external JavaScript files (``*.js``) + correctly. + + +Fix: + modified ``mime.c`` to associate ``.js`` files with the MIME + type ``application/x-javascript``. + + +Bug in If-Modified-Since parsing +-------------------------------- + + +Summary: + There was an off-by-one error when converting from Gregorian date to + time_t. + +Fix: + modified function ``dateToTimet`` in file ``webs.c``. The ``month`` + parameter is numbered from 0 (Jan == 0), but ``FixedFromGregorian()`` + takes months numbered from 1 (January == 1). We need to add 1 to + the month before calling FixedFromGregorian. + + + + +Bug Fixes for Version 2.1.2 +============================ + + +Ejscript Error Messages +------------------------ + + +Summary: + + Changed ejEval() function so it displays in the browser the error string that is + reported by the Ejscript interpreter (old code discarded it completely). + +Fix: + modified ``websuemf.c`` + + +Security Handler Response Codes +-------------------------------- + + +Summary: + Several places in the ``websSecurityHandler()`` function were + returning error code 200 (success) instead of the correct 400-level error code. +Fix: + Corrected error codes in ``security.c`` + +Security Handler Memory Leak +----------------------------- + + +Summary: + In ``websSecurityHandler()``, if the WebServer was compiled with + ``WEBS_SSL_SUPPORT`` enabled, there was a return path that leaked + memory. + +Fix: + Added call to ``bfree(B_L, accessLimit);`` + +Ejscript Write Corruption +-------------------------- + + +Summary: + Very long text strings passed to the Ejscript ``write()`` function + were being corrupted before being displayed. + +Fix: + Commented out a ``trace()`` statement in ``ejGetLexToken()`` that appears to have been the + culprit. The value of ``ep->token`` was being corrupted somewhere + in the trace. + +Error in dsnprintf(): "%X" format +---------------------------------- + + +Summary: + The "%X" format specifier did not correctly cause the function to output + uppercase hexadecimal digits. + +Fix: + Added support for the "%X" format specifier. + +BUG018565 Re-fixed +------------------- + + +Summary: + See 2.1.1 release notes (below). This bug fix did not make it into the + 2.1.1 release. + +Fix: + Corrected code in ``sockGen.c``. + +Potential Error in ``error()`` +------------------------------- + + + +Summary: + If ``error()`` is called where the etype parameter is not E_LOG, E_ASSERT, + or E_USER, the call to ``bfreeSafe(B_L, buf)`` now at line 71 will fail, + because 'buf' is randomly initialized. + +Fix: + We format a message saying that this is an unknown message type, + and in doing so give buf a valid value. + + +Added Support For Customized Access Control +-------------------------------------------- + + +Summary: + Several users requested a method to control URL access in a hierarchical + fashion. For example, users assigned to an 'admin' group might have + access to all URLs on the WebServer, and users assigned to the group + 'technician' would have access to a smaller set of pages, and users + assigned to the group 'users' would perhaps only have access to a set of + read-only pages. The built-in WebServer access control system only + allows users to access URLs that exactly match their group membership. + +Fix: + Added call to a user-supplied function `` bool_t dmfCanAccess(const + char_t* userGroup, const char_t* group)``. This function is called + from inside ``umUserCanAccessURL()`` if the macro + ``qHierarchicalAccess`` is defined. + + + +Memory Leak in websParseRequest() +---------------------------------- + + +Summary: + Memory was being leaked in the code now at line 907 of ``webs.c``. + +Fix: + Added a call to ``bfree()``. + +Macintosh OS X Support +======================== + + +A separate Mac OS X platform directory has been added, and this platform +has been tested on version 10.1.5 of the operating system. +To build the WebServer on OS X:: + + cd MACOSX + make + + +Note that like all \*nix operating systems, only the root user has +permission to open a server port with a lower number than 1024. You must +run the WebServer as root to serve pages over port 80, or change the server +port (in ``main.c``) to a different port (typically port 8080). + + + + +Bug Fixes for Version 2.1.1 +=========================== + +Intermittent Access Error for CGI Scripts (BUG01937) +---------------------------------------------------- + + +Summary: + Pages were occasionally replaced with the message, ``Access Error: Data + follows when trying to obtain CGI generated no output``. +Fix: + On multiple CPU systems, it is possible for a CGI application to exit before + its output is flushed to disk. The change for this release locates the code + that collects the output from the CGI application in a separate routine. + In addition to calling that routine from within the CGI application processing + loop, it is also called in a brief loop after the CGI application has exited. + This extra loop runs for only up to one second while the collected output + remains empty. If, after 1 second, the output remains empty, the original + course of action is followed (``Access Error`` is reported). + + +CPU Utilization Hangs at 100% on a Socket Disconnect (BUG01865) +--------------------------------------------------------------- + + +Summary: + This error occurs whenever a user terminates a request before the server + has had ample time to service it. This can be simulated by quickly hitting + the refresh button on the browser twice in a row, causing the first request + to be terminated. The server then enters into a tight loop that utilizes + all of its time. + +Fix: + Always close the socket prior to a disconnect. + + +Security Features can be Bypassed by Adding an Extra Slash in the URL (BUG01518) +-------------------------------------------------------------------------------- + + +Summary: + By adding an extra slash in a URL, password prompting is bypassed, allowing + unrestricted access to secured pages. + +Fix: + Remove multiple adjacent occurrences of slashes in the URL before passing + it to the security handler. + + +Call to ``websSetVar`` causes a crash (BUG01938) +------------------------------------------------ + + + +Summary: + Whenever a request is not completed while being processed by websGetInput(), + a call to websDone is made, followed by an attempt to process the partial + request data. + +Fix: + Return immediately after closing the socket. + + +Remove stray semicolon in ``emfdb.c`` (BUG01820) +------------------------------------------------ + + +Summary and Fix: + A stray semicolon was removed from this file. + +Novell Netware Support +====================== + + +With the addition of Novell Netware in this 2.11 release, WebServer now supports these platforms: + + * LINUX + * LynxOS + * Novell Netware 4.2, 5.1 + * Mac OS X + * UNIX - SCO OpenServer 3.2V5.0.0 + * VxWorks 5.3.1 + * Windows 2000 + * Windows 98 + * Windows 95 + * Windows NT + * Windows CE + +To make a Novell Netware target file (``webs.nlm``):: + + cd NW + wmake webs.nlm + load ; \webs.nlm + webs + + +**Note:** This makefile lacks a valid default rule. In addition, an +environment variable (QMKVER) controls the amount of debug information +that is compiled and linked into the nlm file. If this variable is set +to ``P``, it produces a production version. All other settings +(or the omission of the variable) results in a debug version. For other +platforms supported by WebServer, please refer to your WebServer 2.1 +documentation for appropriate instructions. + +Copyright Information +===================== + + +Trademarks + GoAhead and GoAhead WebServer are registered trademarks of GoAhead + Software. All other brand or product names are the trademarks or + registered trademarks of their respective holders. + +Copyright + Copyright © 2000, 2001 GoAhead Software, Inc. All rights reserved. + Product and technical information in this document is subject to + change without notice and does not represent a commitment on the part + of GoAhead Software, Inc. + +Copy Restrictions + The software described in this document may be used and copied only + in accordance with the terms of the accompanying license agreement. + + +GoAhead Software, Inc. + 10900 NE 8th Street Suite 750 Bellevue, WA 98004 +1 ( 425) 453-1900 + www.goahead.com info@goahead.com + +1-53-03 + diff --git a/cleopatre/application/spidgoahead/ringq.c b/cleopatre/application/spidgoahead/ringq.c new file mode 100644 index 0000000000..96ef052716 --- /dev/null +++ b/cleopatre/application/spidgoahead/ringq.c @@ -0,0 +1,595 @@ +/* + * ringq.c -- Ring queue buffering module + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: ringq.c,v 1.4 2003/09/17 14:45:03 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * A ring queue allows maximum utilization of memory for data storage and is + * 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 + * the queue structure and pointers. Change this module very carefully. + * + * This module follows the open/close model. + * + * Operation of a ringq where rq is a pointer to a ringq : + * + * rq->buflen contains the size of the buffer. + * rq->buf will point to the start of the buffer. + * rq->servp will point to the first (un-consumed) data byte. + * rq->endp will point to the next free location to which new data is added + * rq->endbuf will point to one past the end of the buffer. + * + * Eg. If the ringq contains the data "abcdef", it might look like : + * + * +-------------------------------------------------------------------+ + * | | | | | | | | a | b | c | d | e | f | | | | | + * +-------------------------------------------------------------------+ + * ^ ^ ^ ^ + * | | | | + * 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 filler's responsibility to ensure + * the ringq is never filled such that servp == endp. + * + * 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. + */ + +/********************************* Includes ***********************************/ + +#ifdef UEMF + #include "uemf.h" +#else + #include "basic/basicInternal.h" +#endif + +/*********************************** Defines **********************************/ +/* + * Faster than a function call + */ + +#define RINGQ_LEN(rq) \ + ((rq->servp > rq->endp) ? \ + (rq->buflen + (rq->endp - rq->servp)) : \ + (rq->endp - rq->servp)) + +/***************************** Forward Declarations ***************************/ + +static int ringqGrow(ringq_t *rq); +static int getBinBlockSize(int size); + +int ringqGrowCalls = 0; + +/*********************************** Code *************************************/ +/* + * Create a new ringq. "increment" is the amount to increase the size of the + * ringq should it need to grow to accomodate data being added. "maxsize" is + * an upper limit (sanity level) beyond which the q must not grow. Set maxsize + * to -1 to imply no upper limit. The buffer for the ringq is always + * dynamically allocated. Set maxsize + */ + +int ringqOpen(ringq_t *rq, int initSize, int maxsize) +{ + int increment; + + a_assert(rq); + a_assert(initSize >= 0); + + increment = getBinBlockSize(initSize); + if ((rq->buf = balloc(B_L, (increment))) == NULL) { + return -1; + } + rq->maxsize = maxsize; + rq->buflen = increment; + rq->increment = increment; + rq->endbuf = &rq->buf[rq->buflen]; + rq->servp = rq->buf; + rq->endp = rq->buf; + *rq->servp = '\0'; + return 0; +} + +/******************************************************************************/ +/* + * Delete a ringq and free the ringq buffer. + */ + +void ringqClose(ringq_t *rq) +{ + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + if (rq == NULL) { + return; + } + + ringqFlush(rq); + bfree(B_L, (char*) rq->buf); + rq->buf = NULL; +} + +/******************************************************************************/ +/* + * 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) +{ + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + if (rq->servp > rq->endp) { + return rq->buflen + rq->endp - rq->servp; + } else { + return rq->endp - rq->servp; + } +} + +/******************************************************************************/ +/* + * Get a byte from the queue + */ + +int ringqGetc(ringq_t *rq) +{ + char_t c; + char_t* cp; + + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + if (rq->servp == rq->endp) { + return -1; + } + + cp = (char_t*) rq->servp; + c = *cp++; + rq->servp = (unsigned char *) cp; + if (rq->servp >= rq->endbuf) { + rq->servp = rq->buf; + } + /* + * 17 Sep 03 BgP -- using the implicit conversion from signed char to + * signed int in the return below makes this function work incorrectly when + * dealing with UTF-8 encoded text. UTF-8 may include characters that are > + * 127, which a signed char treats as negative. When we return a 'negative' + * value from this function, it gets converted to a negative + * integer, instead of a small positive integer, which is what we want. + * So, we cast to (unsigned char) before returning, and the problem goes + * away... + */ + return (int) ((unsigned char) c); +} + +/******************************************************************************/ +/* + * Add a char to the queue. Note if being used to store wide strings + * this does not add a trailing '\0'. Grow the q as required. + */ + +int ringqPutc(ringq_t *rq, char_t c) +{ + char_t *cp; + + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + if ((ringqPutBlkMax(rq) < (int) sizeof(char_t)) && !ringqGrow(rq)) { + return -1; + } + + cp = (char_t*) rq->endp; + *cp++ = (char_t) c; + rq->endp = (unsigned char *) cp; + if (rq->endp >= rq->endbuf) { + rq->endp = rq->buf; + } + return 0; +} + +/******************************************************************************/ +/* + * Insert a wide character at the front of the queue + */ + +int ringqInsertc(ringq_t *rq, char_t c) +{ + char_t *cp; + + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + if (ringqPutBlkMax(rq) < (int) sizeof(char_t) && !ringqGrow(rq)) { + return -1; + } + if (rq->servp <= rq->buf) { + rq->servp = rq->endbuf; + } + cp = (char_t*) rq->servp; + *--cp = (char_t) c; + rq->servp = (unsigned char *) cp; + return 0; +} + +/******************************************************************************/ +/* + * Add a string to the queue. Add a trailing null (maybe two nulls) + */ + +int ringqPutStr(ringq_t *rq, char_t *str) +{ + int rc; + + a_assert(rq); + a_assert(str); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + rc = ringqPutBlk(rq, (unsigned char*) str, gstrlen(str) * sizeof(char_t)); + *((char_t*) rq->endp) = (char_t) '\0'; + return rc; +} + +/******************************************************************************/ +/* + * Add a null terminator. This does NOT increase the size of the queue + */ + +void ringqAddNull(ringq_t *rq) +{ + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + *((char_t*) rq->endp) = (char_t) '\0'; +} + +/******************************************************************************/ +#ifdef UNICODE +/* + * Get a byte from the queue + */ + +int ringqGetcA(ringq_t *rq) +{ + unsigned char c; + + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + if (rq->servp == rq->endp) { + return -1; + } + + c = *rq->servp++; + if (rq->servp >= rq->endbuf) { + rq->servp = rq->buf; + } + return c; +} + +/******************************************************************************/ +/* + * Add a byte to the queue. Note if being used to store strings this does not + * add a trailing '\0'. Grow the q as required. + */ + +int ringqPutcA(ringq_t *rq, char c) +{ + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + if (ringqPutBlkMax(rq) == 0 && !ringqGrow(rq)) { + return -1; + } + + *rq->endp++ = (unsigned char) c; + if (rq->endp >= rq->endbuf) { + rq->endp = rq->buf; + } + return 0; +} + +/******************************************************************************/ +/* + * Insert a byte at the front of the queue + */ + +int ringqInsertcA(ringq_t *rq, char c) +{ + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + if (ringqPutBlkMax(rq) == 0 && !ringqGrow(rq)) { + return -1; + } + if (rq->servp <= rq->buf) { + rq->servp = rq->endbuf; + } + *--rq->servp = (unsigned char) c; + return 0; +} + +/******************************************************************************/ +/* + * Add a string to the queue. Add a trailing null (not really in the q). + * ie. beyond the last valid byte. + */ + +int ringqPutStrA(ringq_t *rq, char *str) +{ + int rc; + + a_assert(rq); + a_assert(str); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + rc = ringqPutBlk(rq, (unsigned char*) str, strlen(str)); + rq->endp[0] = '\0'; + return rc; +} + +#endif /* UNICODE */ +/******************************************************************************/ +/* + * Add a block of data to the ringq. Return the number of bytes added. + * Grow the q as required. + */ + +int ringqPutBlk(ringq_t *rq, unsigned char *buf, int size) +{ + int this, bytes_put; + + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + a_assert(buf); + a_assert(0 <= size); + +/* + * Loop adding the maximum bytes we can add in a single straight line copy + */ + bytes_put = 0; + while (size > 0) { + this = min(ringqPutBlkMax(rq), size); + if (this <= 0) { + if (! ringqGrow(rq)) { + break; + } + this = min(ringqPutBlkMax(rq), size); + } + + memcpy(rq->endp, buf, this); + buf += this; + rq->endp += this; + size -= this; + bytes_put += this; + + if (rq->endp >= rq->endbuf) { + rq->endp = rq->buf; + } + } + return bytes_put; +} + +/******************************************************************************/ +/* + * Get a block of data from the ringq. Return the number of bytes returned. + */ + +int ringqGetBlk(ringq_t *rq, unsigned char *buf, int size) +{ + int this, bytes_read; + + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + a_assert(buf); + a_assert(0 <= size && size < rq->buflen); + +/* + * Loop getting the maximum bytes we can get in a single straight line copy + */ + bytes_read = 0; + while (size > 0) { + this = ringqGetBlkMax(rq); + this = min(this, size); + if (this <= 0) { + break; + } + + memcpy(buf, rq->servp, this); + buf += this; + rq->servp += this; + size -= this; + bytes_read += this; + + if (rq->servp >= rq->endbuf) { + rq->servp = rq->buf; + } + } + return bytes_read; +} + +/******************************************************************************/ +/* + * Return the maximum number of bytes the ring q can accept via a single + * block copy. Useful if the user is doing their own data insertion. + */ + +int ringqPutBlkMax(ringq_t *rq) +{ + int space, in_a_line; + + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + space = rq->buflen - RINGQ_LEN(rq) - 1; + in_a_line = rq->endbuf - rq->endp; + + return min(in_a_line, space); +} + +/******************************************************************************/ +/* + * Return the maximum number of bytes the ring q can provide via a single + * block copy. Useful if the user is doing their own data retrieval. + */ + +int ringqGetBlkMax(ringq_t *rq) +{ + int len, in_a_line; + + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + + len = RINGQ_LEN(rq); + in_a_line = rq->endbuf - rq->servp; + + return min(in_a_line, len); +} + +/******************************************************************************/ +/* + * Adjust the endp pointer after the user has copied data into the queue. + */ + +void ringqPutBlkAdj(ringq_t *rq, int size) +{ + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + a_assert(0 <= size && size < rq->buflen); + + rq->endp += size; + if (rq->endp >= rq->endbuf) { + rq->endp -= rq->buflen; + } +/* + * Flush the queue if the endp pointer is corrupted via a bad size + */ + if (rq->endp >= rq->endbuf) { + error(E_L, E_LOG, T("Bad end pointer")); + ringqFlush(rq); + } +} + +/******************************************************************************/ +/* + * Adjust the servp pointer after the user has copied data from the queue. + */ + +void ringqGetBlkAdj(ringq_t *rq, int size) +{ + a_assert(rq); + a_assert(rq->buflen == (rq->endbuf - rq->buf)); + a_assert(0 < size && size < rq->buflen); + + rq->servp += size; + if (rq->servp >= rq->endbuf) { + rq->servp -= rq->buflen; + } +/* + * Flush the queue if the servp pointer is corrupted via a bad size + */ + if (rq->servp >= rq->endbuf) { + error(E_L, E_LOG, T("Bad serv pointer")); + ringqFlush(rq); + } +} + +/******************************************************************************/ +/* + * Flush all data in a ring q. Reset the pointers. + */ + +void ringqFlush(ringq_t *rq) +{ + a_assert(rq); + a_assert(rq->servp); + + rq->servp = rq->buf; + rq->endp = rq->buf; + if (rq->servp) { + *rq->servp = '\0'; + } +} + +/******************************************************************************/ +/* + * Grow the buffer. Return true if the buffer can be grown. Grow using + * the increment size specified when opening the ringq. Don't grow beyond + * the maximum possible size. + */ + +static int ringqGrow(ringq_t *rq) +{ + unsigned char *newbuf; + int len; + + a_assert(rq); + + if (rq->maxsize >= 0 && rq->buflen >= rq->maxsize) { + return 0; + } + + len = ringqLen(rq); + + if ((newbuf = balloc(B_L, rq->buflen + rq->increment)) == NULL) { + return 0; + } + ringqGetBlk(rq, newbuf, ringqLen(rq)); + bfree(B_L, (char*) rq->buf); + +#ifdef OLD + rq->endp = &newbuf[endp]; + rq->servp = &newbuf[servp]; + rq->endbuf = &newbuf[rq->buflen]; + rq->buf = newbuf; +#endif + + rq->buflen += rq->increment; + rq->endp = newbuf; + rq->servp = newbuf; + rq->buf = newbuf; + 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/cleopatre/application/spidgoahead/rom.c b/cleopatre/application/spidgoahead/rom.c new file mode 100644 index 0000000000..1dd8463f91 --- /dev/null +++ b/cleopatre/application/spidgoahead/rom.c @@ -0,0 +1,194 @@ +/* + * rom.c -- Support for ROMed page retrieval. + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: rom.c,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * This module provides web page retrieval from compiled web pages. Use the + * webcomp program to compile web pages and link into the GoAhead WebServer. + * This module uses a hashed symbol table for fast page lookup. + * + * Usage: webcomp -f webPageFileList -p Prefix >webrom.c + */ + +/********************************* Includes ***********************************/ + +#include + +#include "wsIntrn.h" + +/******************************** Local Data **********************************/ + +#ifdef WEBS_PAGE_ROM + +sym_fd_t romTab; /* Symbol table for web pages */ + +/*********************************** Code *************************************/ +/* + * Open the ROM module + */ + +int websRomOpen() +{ + websRomPageIndexType *wip; + int nchars; + char_t name[SYM_MAX]; + + romTab = symOpen(WEBS_SYM_INIT); + + for (wip = websRomPageIndex; wip->path; wip++) { + gstrncpy(name, wip->path, SYM_MAX); + nchars = gstrlen(name) - 1; + if (nchars > 0 && + (name[nchars] == '/' || name[nchars] == '\\')) { + name[nchars] = '\0'; + } + symEnter(romTab, name, valueInteger((int) wip), 0); + } + return 0; +} + +/******************************************************************************/ +/* + * Close the ROM module + */ + +void websRomClose() +{ + symClose(romTab); +} + +/******************************************************************************/ +/* + * Open a web page + */ + +int websRomPageOpen(webs_t wp, char_t *path, int mode, int perm) +{ + websRomPageIndexType *wip; + sym_t *sp; + + a_assert(websValid(wp)); + a_assert(path && *path); + + if ((sp = symLookup(romTab, path)) == NULL) { + return -1; + } + wip = (websRomPageIndexType*) sp->content.value.integer; + wip->pos = 0; + return (wp->docfd = wip - websRomPageIndex); +} + +/******************************************************************************/ +/* + * Close a web page + */ + +void websRomPageClose(int fd) +{ +} + +/******************************************************************************/ +/* + * Stat a web page + */ + +int websRomPageStat(char_t *path, websStatType *sbuf) +{ + websRomPageIndexType *wip; + sym_t *sp; + + a_assert(path && *path); + + if ((sp = symLookup(romTab, path)) == NULL) { + return -1; + } + wip = (websRomPageIndexType*) sp->content.value.integer; + + memset(sbuf, 0, sizeof(websStatType)); + sbuf->size = wip->size; + if (wip->page == NULL) { + sbuf->isDir = 1; + } + return 0; +} + +/******************************************************************************/ +/* + * Read a web page + */ + +int websRomPageReadData(webs_t wp, char *buf, int nBytes) +{ + websRomPageIndexType *wip; + int len; + + a_assert(websValid(wp)); + a_assert(buf); + a_assert(wp->docfd >= 0); + + wip = &websRomPageIndex[wp->docfd]; + + len = min(wip->size - wip->pos, nBytes); + memcpy(buf, &wip->page[wip->pos], len); + wip->pos += len; + return len; +} + +/******************************************************************************/ +/* + * Position a web page + */ + +long websRomPageSeek(webs_t wp, long offset, int origin) +{ + websRomPageIndexType *wip; + long pos; + + a_assert(websValid(wp)); + a_assert(origin == SEEK_SET || origin == SEEK_CUR || origin == SEEK_END); + a_assert(wp->docfd >= 0); + + wip = &websRomPageIndex[wp->docfd]; + + if (origin != SEEK_SET && origin != SEEK_CUR && origin != SEEK_END) { + errno = EINVAL; + return -1; + } + + if (wp->docfd < 0) { + errno = EBADF; + return -1; + } + + pos = offset; + switch (origin) { + case SEEK_CUR: + pos = wip->pos + offset; + break; + case SEEK_END: + pos = wip->size + offset; + break; + default: + break; + } + + if (pos < 0) { + errno = EBADF; + return -1; + } + + return (wip->pos = pos); +} + +#endif /* WEBS_PAGE_ROM */ + +/******************************************************************************/ + diff --git a/cleopatre/application/spidgoahead/security.c b/cleopatre/application/spidgoahead/security.c new file mode 100644 index 0000000000..a111e74c46 --- /dev/null +++ b/cleopatre/application/spidgoahead/security.c @@ -0,0 +1,240 @@ +/* + * security.c -- Security handler + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: security.c,v 1.9 2003/09/19 17:04:44 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * 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 *************************************/ +/* + * Determine if this request should be honored + */ + +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, *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, 405, T("Access Denied\nSecure access is required.")); + trace(3, T("SEC: Non-secure access attempted on <%s>\n"), path); + /* bugfix 5/24/02 -- we were leaking the memory pointed to by + * 'accessLimit'. Thanks to Simon Byholm. + */ + bfree(B_L, accessLimit); + 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, 404, T("Page Not Found")); + nRet = 1; + } else if (userid && *userid) { + if (!umUserExists(userid)) { + websStats.access++; + websError(wp, 401, 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, 401, 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; + +/* + * Check digest for equivalence + */ + wp->password = umGetUserPassword(userid); + + 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++; + /* 16 Jun 03 -- error code changed from 405 to 401 -- thanks to + * Jay Chalfant. + */ + websError(wp, 401, T("Access Denied\nWrong Password")); + nRet = 1; + } + + bfree (B_L, digestCalc); +#endif + } else { +/* + * No password has been specified + */ +#ifdef DIGEST_ACCESS_SUPPORT + if (am == AM_DIGEST) { + wp->flags |= WEBS_AUTH_DIGEST; + } +#endif + websStats.errors++; + websError(wp, 401, + T("Access 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; + } + + bfree(B_L, accessLimit); + + return nRet; +} + +/******************************************************************************/ +/* + * Delete the default security handler + */ + +void websSecurityDelete() +{ + websUrlHandlerDelete(websSecurityHandler); +} + +/******************************************************************************/ +/* + * Store the new password, expect a decoded password. Store in websPassword in + * the decoded form. + */ + +void websSetPassword(char_t *password) +{ + a_assert(password); + + gstrncpy(websPassword, password, TSZ(websPassword)); +} + +/******************************************************************************/ +/* + * Get password, return the decoded form + */ + +char_t *websGetPassword() +{ + return bstrdup(B_L, websPassword); +} + +/******************************************************************************/ + + diff --git a/cleopatre/application/spidgoahead/sock.c b/cleopatre/application/spidgoahead/sock.c new file mode 100644 index 0000000000..aaf5276bf8 --- /dev/null +++ b/cleopatre/application/spidgoahead/sock.c @@ -0,0 +1,792 @@ +/* + * sock.c -- Posix Socket upper layer support module for general posix use + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * $Id: sock.c,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * Posix Socket Module. This supports blocking and non-blocking buffered + * socket I/O. + */ + +/********************************** Includes **********************************/ + +#include +#include + +#ifdef UEMF + #include "uemf.h" +#else + #include + #include + #include + #include "emfInternal.h" +#endif + +/************************************ Locals **********************************/ + +socket_t **socketList; /* List of open sockets */ +int socketMax; /* Maximum size of socket */ +int socketHighestFd = -1; /* Highest socket fd opened */ + +/***************************** Forward Declarations ***************************/ + +static int socketDoOutput(socket_t *sp, char *buf, int toWrite, int *errCode); +static int tryAlternateSendTo(int sock, char *buf, int toWrite, int i, + struct sockaddr *server); + +/*********************************** Code *************************************/ +/* + * Write to a socket. Absorb as much data as the socket can buffer. Block if + * the socket is in blocking mode. Returns -1 on error, otherwise the number + * of bytes written. + */ + +int socketWrite(int sid, char *buf, int bufsize) +{ + socket_t *sp; + ringq_t *rq; + int len, bytesWritten, room; + + a_assert(buf); + a_assert(bufsize >= 0); + + if ((sp = socketPtr(sid)) == NULL) { + return -1; + } + +/* + * Loop adding as much data to the output ringq as we can absorb. Initiate a + * flush when the ringq is too full and continue. Block in socketFlush if the + * socket is in blocking mode. + */ + rq = &sp->outBuf; + for (bytesWritten = 0; bufsize > 0; ) { + if ((room = ringqPutBlkMax(rq)) == 0) { + if (socketFlush(sid) < 0) { + return -1; + } + if ((room = ringqPutBlkMax(rq)) == 0) { + if (sp->flags & SOCKET_BLOCK) { +#if (defined (WIN) || defined (CE)) + int errCode; + if (! socketWaitForEvent(sp, FD_WRITE | SOCKET_WRITABLE, + &errCode)) { + return -1; + } +#endif + continue; + } + break; + } + continue; + } + len = min(room, bufsize); + ringqPutBlk(rq, (unsigned char *) buf, len); + bytesWritten += len; + bufsize -= len; + buf += len; + } + return bytesWritten; +} + +/******************************************************************************/ +/* + * Write a string to a socket + */ + +int socketWriteString(int sid, char_t *buf) +{ + #ifdef UNICODE + char *byteBuf; + int r, len; + + len = gstrlen(buf); + byteBuf = ballocUniToAsc(buf, len); + r = socketWrite(sid, byteBuf, len); + bfreeSafe(B_L, byteBuf); + return r; + #else + return socketWrite(sid, buf, strlen(buf)); + #endif /* UNICODE */ +} + +/******************************************************************************/ +/* + * 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. + * If this routine returns zero it indicates an EOF condition. + * which can be verified with socketEof() + + * Note: this ignores the line buffer, so a previous socketGets + * which read a partial line may cause a subsequent socketRead to miss some + * data. This routine may block if the socket is in blocking mode. + * + */ + +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; + } + + rq = &sp->inBuf; + for (bytesRead = 0; bufsize > 0; ) { + len = min(ringqLen(rq), bufsize); + if (len <= 0) { +/* + * if blocking mode and already have data, exit now or it may block + * forever. + */ + if ((sp->flags & SOCKET_BLOCK) && + (bytesRead > 0)) { + break; + } +/* + * This flush is critical for readers of datagram packets. If the + * buffer is not big enough to read the whole datagram in one hit, + * the recvfrom call will fail. + */ + ringqFlush(rq); + room = ringqPutBlkMax(rq); + len = socketGetInput(sid, (char *) rq->endp, room, &errCode); + if (len < 0) { + if (errCode == EWOULDBLOCK) { + if ((sp->flags & SOCKET_BLOCK) && + (bytesRead == 0)) { + continue; + } + if (bytesRead >= 0) { + return bytesRead; + } + } + return -1; + + } else if (len == 0) { +/* + * If bytesRead is 0, this is EOF since socketRead should never + * be called unless there is data yet to be read. Set the flag. + * Then pass back the number of bytes read. + */ + if (bytesRead == 0) { + sp->flags |= SOCKET_EOF; + } + return bytesRead; + } + ringqPutBlkAdj(rq, len); + len = min(len, bufsize); + } + memcpy(&buf[bytesRead], rq->servp, len); + ringqGetBlkAdj(rq, len); + bufsize -= len; + bytesRead += len; + } + 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. If doing non-blocking I/O, it returns -1 for error, EOF or when + * no complete line yet read. If doing blocking I/O, it will block until an + * entire line is read. 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. + */ + +int socketGets(int sid, char_t **buf) +{ + socket_t *sp; + ringq_t *lq; + char c; + int rc, len; + + a_assert(buf); + *buf = NULL; + + if ((sp = socketPtr(sid)) == NULL) { + return -1; + } + lq = &sp->lineBuf; + + while (1) { + + if ((rc = socketRead(sid, &c, 1)) < 0) { + return rc; + } + + 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 (c == '\n') { + len = ringqLen(lq); + if (len > 0) { + *buf = ballocAscToUni((char *)lq->servp, len); + } else { + *buf = NULL; + } + ringqFlush(lq); + return len; + + } else if (c == '\r') { + continue; + } + ringqPutcA(lq, c); + } + return 0; +} + +/******************************************************************************/ +/* + * Flush the socket. Block if the socket is in blocking mode. + * This will return -1 on errors and 0 if successful. + */ + +int socketFlush(int sid) +{ + socket_t *sp; + ringq_t *rq; + int len, bytesWritten, errCode; + + if ((sp = socketPtr(sid)) == NULL) { + return -1; + } + rq = &sp->outBuf; + +/* + * Set the background flushing flag which socketEventProc will check to + * continue the flush. + */ + if (! (sp->flags & SOCKET_BLOCK)) { + sp->flags |= SOCKET_FLUSHING; + } + +/* + * 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 (defined (WIN) || defined (CE)) + if (sp->flags & SOCKET_BLOCK) { + int errCode; + if (! socketWaitForEvent(sp, FD_WRITE | SOCKET_WRITABLE, + &errCode)) { + return -1; + } + continue; + } +#endif +/* + * Ensure we get a FD_WRITE message when the socket can absorb + * more data (non-blocking only.) Store the user's mask if we + * haven't done it already. + */ + if (sp->saveMask < 0 ) { + sp->saveMask = sp->handlerMask; + socketRegisterInterest(sp, + sp->handlerMask | SOCKET_WRITABLE); + } + return 0; + } + return -1; + } + ringqGetBlkAdj(rq, bytesWritten); + } +/* + * If the buffer is empty, reset the ringq pointers to point to the start + * of the buffer. This is essential to ensure that datagrams get written + * in one single I/O operation. + */ + if (ringqLen(rq) == 0) { + ringqFlush(rq); + } +/* + * Restore the users mask if it was saved by the non-blocking code above. + * Note: saveMask = -1 if empty. socketRegisterInterest will set handlerMask + */ + if (sp->saveMask >= 0) { + socketRegisterInterest(sp, sp->saveMask); + sp->saveMask = -1; + } + sp->flags &= ~SOCKET_FLUSHING; + return 0; +} + +/******************************************************************************/ +/* + * 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. + */ + +int socketInputBuffered(int sid) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + return -1; + } + if (socketEof(sid)) { + return -1; + } + return ringqLen(&sp->lineBuf) + ringqLen(&sp->inBuf); +} + +/******************************************************************************/ +/* + * Return true if EOF + */ + +int socketEof(int sid) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + return -1; + } + return sp->flags & SOCKET_EOF; +} + +/******************************************************************************/ +/* + * Return the number of bytes the socket can absorb without blocking + */ + +int socketCanWrite(int sid) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + return -1; + } + return sp->outBuf.buflen - ringqLen(&sp->outBuf) - 1; +} + +/******************************************************************************/ +/* + * Add one to allow the user to write exactly SOCKET_BUFSIZ + */ + +void socketSetBufferSize(int sid, int in, int line, int out) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + return; + } + + if (in >= 0) { + ringqClose(&sp->inBuf); + in++; + ringqOpen(&sp->inBuf, in, in); + } + + if (line >= 0) { + ringqClose(&sp->lineBuf); + line++; + ringqOpen(&sp->lineBuf, line, line); + } + + if (out >= 0) { + ringqClose(&sp->outBuf); + out++; + ringqOpen(&sp->outBuf, out, out); + } +} + +/******************************************************************************/ +/* + * Create a user handler for this socket. The handler called whenever there + * is an event of interest as defined by handlerMask (SOCKET_READABLE, ...) + */ + +void socketCreateHandler(int sid, int handlerMask, socketHandler_t handler, + int data) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + return; + } + sp->handler = handler; + sp->handler_data = data; + socketRegisterInterest(sp, handlerMask); +} + +/******************************************************************************/ +/* + * Delete a handler + */ + +void socketDeleteHandler(int sid) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + return; + } + sp->handler = NULL; + socketRegisterInterest(sp, 0); +} + +/******************************************************************************/ +/* + * Socket output procedure. Return -1 on errors otherwise return the number + * of bytes written. + */ + +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 (defined (WIN) || defined (CE)) + if ((sp->flags & SOCKET_ASYNC) + && ! socketWaitForEvent(sp, FD_CONNECT, errCode)) { + return -1; + } +#endif + +/* + * Write the data + */ + if (sp->flags & SOCKET_BROADCAST) { + server.sin_family = AF_INET; +#if (defined (UEMF) || defined (LITTLEFOOT)) + server.sin_addr.s_addr = INADDR_BROADCAST; +#else + server.sin_addr.s_addr = inet_addr(basicGetBroadcastAddress()); +#endif + server.sin_port = htons((short)(sp->port & 0xFFFF)); + if ((bytes = sendto(sp->sock, buf, toWrite, 0, + (struct sockaddr *) &server, sizeof(server))) < 0) { + bytes = tryAlternateSendTo(sp->sock, buf, toWrite, 0, + (struct sockaddr *) &server); + } + } else if (sp->flags & SOCKET_DATAGRAM) { + server.sin_family = AF_INET; + server.sin_addr.s_addr = inet_addr(sp->host); + 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) { + *errCode = socketGetError(); +#if (defined (WIN) || defined (CE)) + sp->currentEvents &= ~FD_WRITE; +#endif + + return -1; + + } else if (bytes == 0 && bytes != toWrite) { + *errCode = EWOULDBLOCK; +#if (defined (WIN) || defined (CE)) + sp->currentEvents &= ~FD_WRITE; +#endif + return -1; + } + +/* + * Ensure we get to write some more data real soon if the socket can absorb + * more data + */ +#ifndef UEMF +#ifdef WIN + if (sp->interestEvents & FD_WRITE) { + emfTime_t blockTime = { 0, 0 }; + emfSetMaxBlockTime(&blockTime); + } +#endif /* WIN */ +#endif + return bytes; +} + +/******************************************************************************/ +/* + * If the sendto 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 tryAlternateSendTo(int sock, char *buf, int toWrite, int i, + struct sockaddr *server) +{ +#ifdef VXWORKS + char *ptr; + + ptr = (char *)server; + *ptr = *(ptr+1); + *(ptr+1) = 0; + return sendto(sock, buf, toWrite, i, server, sizeof(struct sockaddr)); +#else + return -1; +#endif /* VXWORKS */ +} + +/******************************************************************************/ +/* + * Allocate a new socket structure + */ + +int socketAlloc(char *host, int port, socketAccept_t accept, int flags) +{ + socket_t *sp; + int sid; + + if ((sid = hAllocEntry((void***) &socketList, &socketMax, + sizeof(socket_t))) < 0) { + return -1; + } + sp = socketList[sid]; + + sp->sid = sid; + sp->accept = accept; + sp->port = port; + sp->fileHandle = -1; + sp->saveMask = -1; + + if (host) { + strncpy(sp->host, host, sizeof(sp->host)); + } + +/* + * Preserve only specified flags from the callers open + */ + a_assert((flags & ~(SOCKET_BROADCAST|SOCKET_DATAGRAM|SOCKET_BLOCK| + SOCKET_LISTENING)) == 0); + sp->flags = flags & (SOCKET_BROADCAST | SOCKET_DATAGRAM | SOCKET_BLOCK| + SOCKET_LISTENING); + +/* + * Add one to allow the user to write exactly SOCKET_BUFSIZ + */ + ringqOpen(&sp->inBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ); + ringqOpen(&sp->outBuf, SOCKET_BUFSIZ + 1, SOCKET_BUFSIZ + 1); + ringqOpen(&sp->lineBuf, SOCKET_BUFSIZ, -1); + + return sid; +} + +/******************************************************************************/ +/* + * Free a socket structure + */ + +void socketFree(int sid) +{ + socket_t *sp; + char_t buf[256]; + int i; + + if ((sp = socketPtr(sid)) == NULL) { + return; + } + +/* + * To close a socket, remove any registered interests, set it to + * non-blocking so that the recv which follows won't block, do a + * shutdown on it so peers on the other end will receive a FIN, + * then read any data not yet retrieved from the receive buffer, + * and finally close it. If these steps are not all performed + * RESETs may be sent to the other end causing problems. + */ + socketRegisterInterest(sp, 0); + if (sp->sock >= 0) { + socketSetBlock(sid, 0); + if (shutdown(sp->sock, 1) >= 0) { + recv(sp->sock, buf, sizeof(buf), 0); + } +#if (defined (WIN) || defined (CE)) + closesocket(sp->sock); +#else + close(sp->sock); +#endif + } + + 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); + } +} + +/******************************************************************************/ +/* + * Validate a socket handle + */ + +socket_t *socketPtr(int sid) +{ + if (sid < 0 || sid >= socketMax || socketList[sid] == NULL) { + a_assert(NULL); + errno = EBADF; + return NULL; + } + + a_assert(socketList[sid]); + return socketList[sid]; +} + +/******************************************************************************/ +/* + * Get the operating system error code + */ + +int socketGetError() +{ +#if (defined (WIN) || defined (CE)) + switch (WSAGetLastError()) { + case WSAEWOULDBLOCK: + return EWOULDBLOCK; + case WSAECONNRESET: + return ECONNRESET; + case WSAENETDOWN: + return ENETDOWN; + case WSAEPROCLIM: + return EAGAIN; + case WSAEINTR: + return EINTR; + default: + return EINVAL; + } +#else + return errno; +#endif +} + +/******************************************************************************/ +/* + * Return the underlying socket handle + */ + +int socketGetHandle(int sid) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + return -1; + } + return sp->sock; +} + +/******************************************************************************/ +/* + * Get blocking mode + */ + +int socketGetBlock(int sid) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + a_assert(0); + return 0; + } + return (sp->flags & SOCKET_BLOCK); +} + +/******************************************************************************/ +/* + * Get mode + */ + +int socketGetMode(int sid) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + a_assert(0); + return 0; + } + return sp->flags; +} + +/******************************************************************************/ +/* + * Set mode + */ + +void socketSetMode(int sid, int mode) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + a_assert(0); + return; + } + sp->flags = mode; +} + +/******************************************************************************/ +/* + * Get port. + */ + +int socketGetPort(int sid) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + return -1; + } + return sp->port; +} + +/******************************************************************************/ + diff --git a/cleopatre/application/spidgoahead/sockGen.c b/cleopatre/application/spidgoahead/sockGen.c new file mode 100644 index 0000000000..b72cb4ec79 --- /dev/null +++ b/cleopatre/application/spidgoahead/sockGen.c @@ -0,0 +1,1046 @@ + +/* + * sockGen.c -- Posix Socket support module for general posix use + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * $Id: sockGen.c,v 1.6 2003/04/11 18:00:12 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * Posix Socket Module. This supports blocking and non-blocking buffered + * socket I/O. + */ + +#if (!defined (WIN) || defined (LITTLEFOOT) || defined (WEBS)) + +/********************************** Includes **********************************/ +#ifndef CE +#include +#include +#include +#include +#endif + +#ifdef UEMF + #include "uemf.h" +#else + #include + #include + #include + #include "emfInternal.h" +#endif + +#ifdef VXWORKS + #include +#endif + +/************************************ Locals **********************************/ + +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 void socketAccept(socket_t *sp); +static int socketDoEvent(socket_t *sp); +static int tryAlternateConnect(int sock, struct sockaddr *sockaddr); + +/*********************************** Code *************************************/ +/* + * Open socket module + */ + +int socketOpen() +{ +#if (defined (CE) || defined (WIN)) + WSADATA wsaData; +#endif + + if (++socketOpenCount > 1) { + return 0; + } + +#if (defined (CE) || defined (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; +} + +/******************************************************************************/ +/* + * Close the socket module, by closing all open connections + */ + +void socketClose() +{ + int i; + + if (--socketOpenCount <= 0) { + for (i = socketMax; i >= 0; i--) { + if (socketList && socketList[i]) { + socketCloseConnection(i); + } + } + socketOpenCount = 0; + } +} + +/******************************************************************************/ +/* + * 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) +{ +#if (!defined (NO_GETHOSTBYNAME) && !defined (VXWORKS)) + struct hostent *hostent; /* Host database entry */ +#endif /* ! (NO_GETHOSTBYNAME || VXWORKS) */ + socket_t *sp; + struct sockaddr_in sockaddr; + int sid, bcast, dgram, rc; + + if (port > SOCKET_PORT_MAX) { + return -1; + } +/* + * Allocate a socket structure + */ + if ((sid = socketAlloc(host, port, accept, flags)) < 0) { + return -1; + } + sp = socketList[sid]; + a_assert(sp); + +/* + * Create the socket address structure + */ + memset((char *) &sockaddr, '\0', sizeof(struct sockaddr_in)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons((short) (port & 0xFFFF)); + + if (host == NULL) { + sockaddr.sin_addr.s_addr = INADDR_ANY; + } 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. + */ + +#ifdef 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 (defined (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 { + 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. Support for datagram sockets. Set the close on + * exec flag so children don't inherit the socket. + */ + 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 in blocking mode, then go into + * non-blocking mode if desired. + */ + 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 (defined (WIN) || defined (CE)) && (!defined (LITTLEFOOT) && !defined (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 (defined (WIN) || defined (CE)) + if (socketGetError() != EWOULDBLOCK) { + socketFree(sid); + return -1; + } +#else + socketFree(sid); + return -1; + +#endif /* WIN || CE */ + + } + } + } else { +/* + * 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) { + socketFree(sid); + return -1; + } + + if (! dgram) { + if (listen(sp->sock, SOMAXCONN) < 0) { + socketFree(sid); + return -1; + } +#ifndef UEMF + sp->fileHandle = emfCreateFileHandler(sp->sock, SOCKET_READABLE, + (emfFileProc *) socketAccept, (void *) sp); +#else + sp->flags |= SOCKET_LISTENING; +#endif + } + 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) +{ +#ifdef 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; + + if ((sp = socketPtr(sid)) == NULL) { + return; + } + socketFree(sid); +} + +/******************************************************************************/ +/* + * Accept a connection. Called as a callback on incoming connection. + */ + +static void socketAccept(socket_t *sp) +{ + struct sockaddr_in addr; + socket_t *nsp; + size_t len; + char *pString; + int newSock, nid; + + +#ifdef NW + NETINET_DEFINE_CONTEXT; +#endif + + a_assert(sp); + +/* + * Accept the connection and prevent inheriting by children (F_SETFD) + */ + len = sizeof(struct sockaddr_in); + if ((newSock = accept(sp->sock, (struct sockaddr *) &addr, (int *) &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, sp->flags); + nsp = socketList[nid]; + a_assert(nsp); + nsp->sock = newSock; + nsp->flags &= ~SOCKET_LISTENING; + + if (nsp == NULL) { + return; + } +/* + * 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) { + pString = inet_ntoa(addr.sin_addr); + if ((sp->accept)(nid, pString, ntohs(addr.sin_port), sp->sid) < 0) { + socketFree(nid); + } +#ifdef VXWORKS + free(pString); +#endif + } +} + +/******************************************************************************/ +/* + * 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 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); + + *errCode = 0; + + if ((sp = socketPtr(sid)) == NULL) { + return -1; + } + +/* + * If we have previously seen an EOF condition, then just return + */ + if (sp->flags & SOCKET_EOF) { + return 0; + } +#if ((defined (WIN) || defined (CE)) && (!defined (LITTLEFOOT) && !defined (WEBS))) + if ( !(sp->flags & SOCKET_BLOCK) + && ! socketWaitForEvent(sp, FD_CONNECT, errCode)) { + return -1; + } +#endif + +/* + * Read the data + */ + 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); + } + + /* + * BUG 01865 -- CPU utilization hangs on Windows. The original code used + * the 'errno' global variable, which is not set by the winsock functions + * as it is under *nix platforms. We use the platform independent + * socketGetError() function instead, which does handle Windows correctly. + * Other, *nix compatible platforms should work as well, since on those + * platforms, socketGetError() just returns the value of errno. + * Thanks to Jonathan Burgoyne for the fix. + */ + if (bytesRead < 0) + { + *errCode = socketGetError(); + if (*errCode == ECONNRESET) + { + sp->flags |= SOCKET_CONNRESET; + return 0; + } + return -1; + } + return bytesRead; +} + +/******************************************************************************/ +/* + * Process an event on the event queue + */ + +#ifndef UEMF + +static int socketEventProc(void *data, int mask) +{ + socket_t *sp; + ringq_t *rq; + int sid; + + sid = (int) data; + + a_assert(sid >= 0 && sid < socketMax); + a_assert(socketList[sid]); + + if ((sp = socketPtr(sid)) == NULL) { + return 1; + } + +/* + * If now writable and flushing in the background, continue flushing + */ + if (mask & SOCKET_WRITABLE) { + if (sp->flags & SOCKET_FLUSHING) { + rq = &sp->outBuf; + if (ringqLen(rq) > 0) { + socketFlush(sp->sid); + } else { + sp->flags &= ~SOCKET_FLUSHING; + } + } + } + +/* + * 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->handlerMask & mask)) { + (sp->handler)(sid, mask & sp->handlerMask, sp->handler_data); + } + if (socketList && sid < socketMax && socketList[sid] == sp) { + socketRegisterInterest(sp, sp->handlerMask); + } + return 1; +} +#endif /* ! UEMF */ + +/******************************************************************************/ +/* + * Define the events of interest + */ + +void socketRegisterInterest(socket_t *sp, int handlerMask) +{ + a_assert(sp); + + sp->handlerMask = handlerMask; +#ifndef UEMF + if (handlerMask) { + sp->fileHandle = emfCreateFileHandler(sp->sock, handlerMask, + (emfFileProc *) socketEventProc, (void *) sp->sid); + } else { + emfDeleteFileHandler(sp->fileHandle); + sp->fileHandle = -1; + } +#endif /* ! UEMF */ +} + +/******************************************************************************/ +/* + * Wait until an event occurs on a socket. Return 1 on success, 0 on failure. + * or -1 on exception (UEMF only) + */ + +int socketWaitForEvent(socket_t *sp, int handlerMask, int *errCode) +{ + int mask; + + 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; + } + if (errCode) { + *errCode = errno = EWOULDBLOCK; + } + return 0; +} + +/******************************************************************************/ +/* + * Return TRUE if there is a socket with an event ready to process, + */ + +int socketReady(int sid) +{ + socket_t *sp; + int all; + + all = 0; + if (sid < 0) { + sid = 0; + all = 1; + } + + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (! all) { + break; + } else { + continue; + } + } + if (sp->flags & SOCKET_CONNRESET) { + socketCloseConnection(sid); + return 0; + } + if (sp->currentEvents & sp->handlerMask) { + return 1; + } +/* + * If there is input data, also call select to test for new events + */ + if (sp->handlerMask & SOCKET_READABLE && socketInputBuffered(sid) > 0) { + socketSelect(sid, 0); + return 1; + } + if (! all) { + break; + } + } + return 0; +} + +/******************************************************************************/ +/* + * Wait for a handle to become readable or writable and return a number of + * noticed events. Timeout is in milliseconds. + */ + +#if (defined (WIN) || defined (CE) || defined (NW)) + +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; + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + +/* + * Set the select event masks for events to watch + */ + all = nEvents = 0; + + if (sid < 0) { + all++; + sid = 0; + } + + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + continue; + } + a_assert(sp); +/* + * Set the appropriate bit in the ready masks for the sp->sock. + */ + 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; + } + } + +/* + * Windows select() fails if no descriptors are set, instead of just sleeping + * like other, nice select() calls. So, if WIN, sleep. + */ + if (nEvents == 0) { + Sleep(timeout); + return 0; + } + +/* + * Wait for the event or a timeout. + */ + nEvents = select(socketHighestFd+1, &readFds, &writeFds, &exceptFds, &tv); + + if (all) { + sid = 0; + } + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + continue; + } + + 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 nEvents; +} + +#else /* not WIN || CE || NW */ + +int socketSelect(int sid, int timeout) +{ + 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) / NFDBITS; + len = nwords * sizeof(int); + + readFds = balloc(B_L, len); + memset(readFds, 0, len); + writeFds = balloc(B_L, len); + memset(writeFds, 0, len); + 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 + */ + all = nEvents = 0; + + if (sid < 0) { + all++; + sid = 0; + } + + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (all == 0) { + break; + } else { + continue; + } + } + a_assert(sp); + +/* + * Initialize the ready masks and compute the mask offsets. + */ + index = sp->sock / (NBBY * sizeof(fd_mask)); + bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask))); + +/* + * Set the appropriate bit in the ready masks for the sp->sock. + */ + if (sp->handlerMask & SOCKET_READABLE) { + readFds[index] |= bit; + nEvents++; + if (socketInputBuffered(sid) > 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + if (sp->handlerMask & SOCKET_WRITABLE) { + writeFds[index] |= bit; + nEvents++; + } + if (sp->handlerMask & SOCKET_EXCEPTION) { + exceptFds[index] |= bit; + nEvents++; + } + if (! all) { + break; + } + } + +/* + * 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, &tv); + + if (nEvents > 0) { + if (all) { + sid = 0; + } + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + 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 || socketInputBuffered(sid) > 0) { + sp->currentEvents |= SOCKET_READABLE; + } + if (writeFds[index] & bit) { + sp->currentEvents |= SOCKET_WRITABLE; + } + if (exceptFds[index] & bit) { + sp->currentEvents |= SOCKET_EXCEPTION; + } + if (! all) { + break; + } + } + } + + bfree(B_L, readFds); + bfree(B_L, writeFds); + bfree(B_L, exceptFds); + + return nEvents; +} +#endif /* WIN || CE */ + +/******************************************************************************/ +/* + * Process socket events + */ + +void socketProcess(int sid) +{ + socket_t *sp; + int all; + + all = 0; + if (sid < 0) { + all = 1; + sid = 0; + } +/* + * Process each socket + */ + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (! all) { + break; + } else { + continue; + } + } + if (socketReady(sid)) { + socketDoEvent(sp); + } + if (! all) { + break; + } + } +} + +/******************************************************************************/ +/* + * Process an event on the event queue + */ + +static int socketDoEvent(socket_t *sp) +{ + ringq_t *rq; + int sid; + + a_assert(sp); + + sid = sp->sid; + if (sp->currentEvents & SOCKET_READABLE) { + if (sp->flags & SOCKET_LISTENING) { + socketAccept(sp); + 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->handlerMask & SOCKET_READABLE && socketInputBuffered(sid) > 0) { + sp->currentEvents |= SOCKET_READABLE; + } + } + + +/* + * If now writable and flushing in the background, continue flushing + */ + if (sp->currentEvents & SOCKET_WRITABLE) { + if (sp->flags & SOCKET_FLUSHING) { + rq = &sp->outBuf; + if (ringqLen(rq) > 0) { + socketFlush(sp->sid); + } else { + sp->flags &= ~SOCKET_FLUSHING; + } + } + } + +/* + * 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->handlerMask & sp->currentEvents)) { + (sp->handler)(sid, sp->handlerMask & sp->currentEvents, + sp->handler_data); +/* + * Make sure socket pointer is still valid, then reset the currentEvents. + */ + if (socketList && sid < socketMax && socketList[sid] == sp) { + sp->currentEvents = 0; + } + } + return 1; +} + +/******************************************************************************/ +/* + * Set the socket blocking mode + */ + +int socketSetBlock(int sid, int on) +{ + socket_t *sp; + unsigned long flag; + int iflag; + int oldBlock; + + flag = iflag = !on; + + if ((sp = socketPtr(sid)) == NULL) { + a_assert(0); + return 0; + } + oldBlock = (sp->flags & SOCKET_BLOCK); + sp->flags &= ~(SOCKET_BLOCK); + if (on) { + sp->flags |= SOCKET_BLOCK; + } + +/* + * Put the socket into block / non-blocking mode + */ + if (sp->flags & SOCKET_BLOCK) { +#if (defined (CE) || defined (WIN)) + ioctlsocket(sp->sock, FIONBIO, &flag); +#elif (defined (ECOS)) + int off; + off = 0; + ioctl(sp->sock, FIONBIO, &off); +#elif (defined (VXWORKS) || defined (NW)) + ioctl(sp->sock, FIONBIO, (int)&iflag); +#else + fcntl(sp->sock, F_SETFL, fcntl(sp->sock, F_GETFL) & ~O_NONBLOCK); +#endif + + } else { +#if (defined (CE) || defined (WIN)) + ioctlsocket(sp->sock, FIONBIO, &flag); +#elif (defined (ECOS)) + int on; + on = 1; + ioctl(sp->sock, FIONBIO, &on); +#elif (defined (VXWORKS) || defined (NW)) + ioctl(sp->sock, FIONBIO, (int)&iflag); +#else + fcntl(sp->sock, F_SETFL, fcntl(sp->sock, F_GETFL) | O_NONBLOCK); +#endif + } + return oldBlock; +} + +/******************************************************************************/ +/* + * Return true if a readable socket has buffered data. - not public + */ + +int socketDontBlock() +{ + socket_t *sp; + int i; + + for (i = 0; i < socketMax; i++) { + if ((sp = socketList[i]) == NULL || + (sp->handlerMask & SOCKET_READABLE) == 0) { + continue; + } + if (socketInputBuffered(i) > 0) { + return 1; + } + } + return 0; +} + +/******************************************************************************/ +/* + * Return true if a particular socket buffered data. - not public + */ + +int socketSockBuffered(int sock) +{ + socket_t *sp; + int i; + + for (i = 0; i < socketMax; i++) { + if ((sp = socketList[i]) == NULL || sp->sock != sock) { + continue; + } + return socketInputBuffered(i); + } + return 0; +} + +#endif /* (!WIN) | LITTLEFOOT | WEBS */ + +/******************************************************************************/ + + diff --git a/cleopatre/application/spidgoahead/sym.c b/cleopatre/application/spidgoahead/sym.c new file mode 100644 index 0000000000..88f29c7e8a --- /dev/null +++ b/cleopatre/application/spidgoahead/sym.c @@ -0,0 +1,476 @@ +/* + * sym.c -- Symbol Table module + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: sym.c,v 1.3 2002/10/24 14:44:50 bporter Exp $ + */ + +/******************************** Description *********************************/ +/* + * 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. + */ + +/********************************* Includes ***********************************/ + +#ifdef UEMF + #include "uemf.h" +#else + #include "basic/basicInternal.h" +#endif + +/********************************* Defines ************************************/ + +typedef struct { /* Symbol table descriptor */ + int inuse; /* Is this entry in use */ + int hash_size; /* Size of the table below */ + sym_t **hash_table; /* Allocated at run time */ +} sym_tabent_t; + +/********************************* Globals ************************************/ + +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 */ + +/**************************** 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 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 sd; + sym_tabent_t *tp; + + a_assert(hash_size > 2); + +/* + * Create a new handle for this symbol table + */ + if ((sd = hAlloc((void***) &sym)) < 0) { + return -1; + } + +/* + * Create a new symbol table structure and zero + */ + if ((tp = (sym_tabent_t*) balloc(B_L, sizeof(sym_tabent_t))) == NULL) { + symMax = hFree((void***) &sym, sd); + return -1; + } + memset(tp, 0, sizeof(sym_tabent_t)); + if (sd >= symMax) { + symMax = sd + 1; + } + a_assert(0 <= sd && sd < symMax); + sym[sd] = tp; + +/* + * Now create the hash table for fast indexing. + */ + 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*)); + + return sd; +} + +/******************************************************************************/ +/* + * Close this symbol table. Call a cleanup function to allow the caller + * to free resources associated with each symbol table entry. + */ + +void symClose(sym_fd_t sd) +{ + sym_tabent_t *tp; + sym_t *sp, *forw; + int i; + + a_assert(0 <= sd && sd < symMax); + tp = sym[sd]; + a_assert(tp); + +/* + * Free all symbols in the hash table, then the hash table itself. + */ + for (i = 0; i < tp->hash_size; i++) { + for (sp = tp->hash_table[i]; sp; sp = forw) { + forw = sp->forw; + valueFree(&sp->name); + valueFree(&sp->content); + bfree(B_L, (void*) sp); + sp = forw; + } + } + bfree(B_L, (void*) tp->hash_table); + + symMax = hFree((void***) &sym, sd); + bfree(B_L, (void*) tp); +} + +/******************************************************************************/ +/* + * 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. + */ + +sym_t* symFirst(sym_fd_t sd) +{ + sym_tabent_t *tp; + sym_t *sp, *forw; + int i; + + a_assert(0 <= sd && sd < symMax); + tp = sym[sd]; + a_assert(tp); + +/* + * Find the first symbol in the hashtable and return a pointer to it. + */ + for (i = 0; i < tp->hash_size; i++) { + for (sp = tp->hash_table[i]; sp; sp = forw) { + forw = sp->forw; + + if (forw == NULL) { + htIndex = i + 1; + next = tp->hash_table[htIndex]; + } else { + htIndex = i; + next = forw; + } + return sp; + } + } + return NULL; +} + +/******************************************************************************/ +/* + * Return the next symbol in the hashtable if there is one. See symFirst. + */ + +sym_t* symNext(sym_fd_t sd) +{ + sym_tabent_t *tp; + sym_t *sp, *forw; + int i; + + a_assert(0 <= sd && sd < symMax); + tp = sym[sd]; + a_assert(tp); + +/* + * Find the first symbol in the hashtable and return a pointer to it. + */ + for (i = htIndex; i < tp->hash_size; i++) { + for (sp = next; sp; sp = forw) { + forw = sp->forw; + + if (forw == NULL) { + htIndex = i + 1; + next = tp->hash_table[htIndex]; + } else { + htIndex = i; + next = forw; + } + return sp; + } + next = tp->hash_table[i + 1]; + } + return NULL; +} + +/******************************************************************************/ +/* + * Lookup a symbol and return a pointer to the symbol entry. If not present + * then return a NULL. + */ + +sym_t *symLookup(sym_fd_t sd, char_t *name) +{ + sym_tabent_t *tp; + sym_t *sp; + char_t *cp; + + a_assert(0 <= sd && sd < symMax); + if ((tp = sym[sd]) == NULL) { + return NULL; + } + + if (name == NULL || *name == '\0') { + return NULL; + } + +/* + * Do an initial hash and then follow the link chain to find the right entry + */ + for (sp = hash(tp, name); sp; sp = sp->forw) { + cp = sp->name.value.string; + if (cp[0] == name[0] && gstrcmp(cp, name) == 0) { + break; + } + } + return sp; +} + +/******************************************************************************/ +/* + * 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) +{ + sym_tabent_t *tp; + sym_t *sp, *last; + char_t *cp; + int hindex; + + 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 + * we have daisy-chain, so scan it and look for the symbol. + */ + last = NULL; + hindex = hashIndex(tp, name); + if ((sp = tp->hash_table[hindex]) != NULL) { + for (; sp; sp = sp->forw) { + cp = sp->name.value.string; + if (cp[0] == name[0] && gstrcmp(cp, name) == 0) { + break; + } + last = sp; + } + if (sp) { +/* + * Found, so update the value + * If the caller stores handles which require freeing, they + * will be lost here. It is the callers responsibility to free + * resources before overwriting existing contents. We will here + * free allocated strings which occur due to value_instring(). + * We should consider providing the cleanup function on the open rather + * than the close and then we could call it here and solve the problem. + */ + if (sp->content.valid) { + valueFree(&sp->content); + } + sp->content = v; + sp->arg = arg; + return sp; + } +/* + * Not found so allocate and append to the daisy-chain + */ + sp = (sym_t*) balloc(B_L, sizeof(sym_t)); + if (sp == NULL) { + return NULL; + } + sp->name = valueString(name, VALUE_ALLOCATE); + sp->content = v; + sp->forw = (sym_t*) NULL; + sp->arg = arg; + last->forw = sp; + + } else { +/* + * Daisy chain is empty so we need to start the chain + */ + sp = (sym_t*) balloc(B_L, sizeof(sym_t)); + if (sp == NULL) { + return NULL; + } + tp->hash_table[hindex] = sp; + tp->hash_table[hashIndex(tp, name)] = sp; + + sp->forw = (sym_t*) NULL; + sp->content = v; + sp->arg = arg; + sp->name = valueString(name, VALUE_ALLOCATE); + } + return sp; +} + +/******************************************************************************/ +/* + * Delete a symbol from a table + */ + +int symDelete(sym_fd_t sd, char_t *name) +{ + sym_tabent_t *tp; + sym_t *sp, *last; + char_t *cp; + int hindex; + + a_assert(name && *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 + * we have daisy-chain, so scan it and look for the symbol. + */ + last = NULL; + hindex = hashIndex(tp, name); + if ((sp = tp->hash_table[hindex]) != NULL) { + for ( ; sp; sp = sp->forw) { + cp = sp->name.value.string; + if (cp[0] == name[0] && gstrcmp(cp, name) == 0) { + break; + } + last = sp; + } + } + 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. + */ + if (last) { + last->forw = sp->forw; + } else { + tp->hash_table[hindex] = sp->forw; + } + valueFree(&sp->name); + valueFree(&sp->content); + bfree(B_L, (void*) sp); + + return 0; +} + +/******************************************************************************/ +/* + * Hash a symbol and return a pointer to the hash daisy-chain list + * All symbols reside on the chain (ie. none stored in the hash table itself) + */ + +static sym_t *hash(sym_tabent_t *tp, char_t *name) +{ + a_assert(tp); + + return tp->hash_table[hashIndex(tp, name)]; +} + +/******************************************************************************/ +/* + * Compute the hash function and return an index into the hash table + * We use a basic additive function that is then made modulo the size of the + * table. + */ + +static int hashIndex(sym_tabent_t *tp, char_t *name) +{ + 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 + * new cycle placing character shifted up by one bit. + */ + i = 0; + sum = 0; + while (*name) { + sum += (((int) *name++) << i); + i = (i + 7) % (BITS(int) - BITSPERBYTE); + } + return sum % tp->hash_size; +} + +/******************************************************************************/ +/* + * Check if this number is a prime + */ + +static int isPrime(int n) +{ + int i, max; + + a_assert(n > 0); + + max = n / 2; + for (i = 2; i <= max; i++) { + if (n % i == 0) { + return 0; + } + } + return 1; +} + +/******************************************************************************/ +/* + * Calculate the largest prime smaller than size. + */ + +static int calcPrime(int size) +{ + int count; + + a_assert(size > 0); + + for (count = size; count > 0; count--) { + if (isPrime(count)) { + return count; + } + } + return 1; +} + +/******************************************************************************/ + + diff --git a/cleopatre/application/spidgoahead/uemf.c b/cleopatre/application/spidgoahead/uemf.c new file mode 100644 index 0000000000..598a9b6947 --- /dev/null +++ b/cleopatre/application/spidgoahead/uemf.c @@ -0,0 +1,294 @@ +/* + * uemf.c -- GoAhead Micro Embedded Management Framework + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: uemf.c,v 1.4 2002/10/24 14:44:50 bporter Exp $ + */ + +/********************************** Description *******************************/ + +/* + * This module provides compatibility with the full GoAhead EMF. + * It is a collection of routines which permits the GoAhead WebServer to + * run stand-alone and to also load as a solution pack under the GoAhead EMF. + */ + +/*********************************** Includes *********************************/ + +#include "uemf.h" + +/********************************** Local Data ********************************/ + +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 wherever the developer wishes + */ + +void error(E_ARGS_DEC, int etype, char_t *fmt, ...) +{ + va_list args; + char_t *fmtBuf, *buf; + + va_start(args, fmt); + fmtValloc(&fmtBuf, E_MAX_ERROR, fmt, args); + + if (etype == E_LOG) { + fmtAlloc(&buf, E_MAX_ERROR, T("%s\n"), fmtBuf); +/*#ifdef 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); + } + /* + * bugfix -- if etype is not E_LOG, E_ASSERT, or E_USER, the call to + * bfreeSafe(B_L, buf) below will fail, because 'buf' is randomly + * initialized. To be nice, we format a message saying that this is an + * unknown message type, and in doing so give buf a valid value. Thanks + * to Simon Byholm. + */ + else { + fmtAlloc(&buf, E_MAX_ERROR, T("Unknown error")); + } + va_end(args); + + bfree(B_L, fmtBuf); + + if (errorHandler) { + errorHandler(etype, buf); + } + + 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; +} + +/******************************************************************************/ +/* + * Trace log. Customize this function to log trace output + */ + +void trace(int level, char_t *fmt, ...) +{ + va_list args; + char_t *buf; + + va_start(args, fmt); + fmtValloc(&buf, VALUE_MAX_STRING, fmt, args); + + if (traceHandler) { + traceHandler(level, buf); + } + bfreeSafe(B_L, buf); + va_end(args); +} + +/******************************************************************************/ +/* + * 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; +} + +/******************************************************************************/ +/* + * Save the instance handle + */ + +void emfInstSet(int inst) +{ + emfInst = inst; +} + +/******************************************************************************/ +/* + * Get the instance handle + */ + +int emfInstGet() +{ + return emfInst; +} + +/******************************************************************************/ +/* + * Convert a string to lower case + */ + +char_t *strlower(char_t *string) +{ + char_t *s; + + a_assert(string); + + if (string == NULL) { + return NULL; + } + + s = string; + while (*s) { + if (gisupper(*s)) { + *s = (char_t) gtolower(*s); + } + s++; + } + *s = '\0'; + return string; +} + +/******************************************************************************/ +/* + * Convert a string to upper case + */ + +char_t *strupper(char_t *string) +{ + char_t *s; + + a_assert(string); + if (string == NULL) { + return NULL; + } + + s = string; + while (*s) { + if (gislower(*s)) { + *s = (char_t) gtoupper(*s); + } + s++; + } + *s = '\0'; + return string; +} + +/******************************************************************************/ +/* + * Convert integer to ascii string. Allow a NULL string in which case we + * allocate a dynamic buffer. + */ + +char_t *stritoa(int n, char_t *string, int width) +{ + char_t *cp, *lim, *s; + char_t buf[16]; /* Just temp to hold number */ + int next, minus; + + a_assert(string && width > 0); + + if (string == NULL) { + if (width == 0) { + width = 10; + } + if ((string = balloc(B_L, width + 1)) == NULL) { + return NULL; + } + } + if (n < 0) { + minus = 1; + n = -n; + width--; + } else { + minus = 0; + } + + cp = buf; + lim = &buf[width - 1]; + while (n > 9 && cp < lim) { + next = n; + n /= 10; + *cp++ = (char_t) (next - n * 10 + '0'); + } + if (cp < lim) { + *cp++ = (char_t) (n + '0'); + } + + s = string; + if (minus) { + *s++ = '-'; + } + + while (cp > buf) { + *s++ = *--cp; + } + + *s++ = '\0'; + return string; +} + +/******************************************************************************/ +/* + * 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/cleopatre/application/spidgoahead/uemf.h b/cleopatre/application/spidgoahead/uemf.h new file mode 100644 index 0000000000..4330e6befc --- /dev/null +++ b/cleopatre/application/spidgoahead/uemf.h @@ -0,0 +1,1094 @@ +/* + * uemf.h -- GoAhead Micro Embedded Management Framework Header + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: uemf.h,v 1.8 2003/11/25 21:48:13 hwolff Exp $ + */ + +#ifndef _h_UEMF +#define _h_UEMF 1 + +/******************************** Description *********************************/ + +/* + * GoAhead Web Server header. This defines the Web public APIs + */ + +/******************************* Per O/S Includes *****************************/ + +#ifdef WIN + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif /* WIN */ + +#ifdef CE + /*#include */ + #include + #include + #include + #include + #include + #include "CE/wincompat.h" + #include +#endif /* CE */ + +#ifdef NW + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #define EINTR EINUSE + #define WEBS 1 + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif /* NW */ + +#ifdef SCOV5 + #include + #include + #include "sys/socket.h" + #include "sys/select.h" + #include "netinet/in.h" + #include "arpa/inet.h" + #include "netdb.h" +#endif /* SCOV5 */ + +#ifdef UNIX + #include +#endif /* UNIX */ + +#ifdef LINUX + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif /* LINUX */ + +#ifdef LYNX + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif /* LYNX */ + +#ifdef MACOSX + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif /* MACOSX */ + +#ifdef UW + #include +#endif /* UW */ + +#ifdef VXWORKS + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif /* VXWORKS */ + +#ifdef sparc +# define __NO_PACK +#endif /* sparc */ + +#ifdef SOLARIS + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif /* SOLARIS */ + +#ifdef QNX4 + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif /* QNX4 */ + +#ifdef ECOS + #include + #include + #include + #include + #include + #include +#endif /* ECOS */ + +/********************************** Includes **********************************/ + +#include +#include +#include + +#ifndef WEBS +#include "messages.h" +#endif /* ! WEBS */ + +/******************************* Per O/S Defines *****************************/ + +#ifdef UW + #define __NO_PACK 1 +#endif /* UW */ + +#if (defined (SCOV5) || defined (VXWORKS) || defined (LINUX) || defined (LYNX) || defined (MACOSX)) +#ifndef O_BINARY +#define O_BINARY 0 +#endif /* O_BINARY */ +#define SOCKET_ERROR -1 +#endif /* SCOV5 || VXWORKS || LINUX || LYNX || MACOSX */ + +#if (defined (WIN) || defined (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 (defined (LINUX) && !defined (_STRUCT_TIMEVAL)) +struct timeval +{ + time_t tv_sec; /* Seconds. */ + time_t tv_usec; /* Microseconds. */ +}; +#define _STRUCT_TIMEVAL 1 +#endif /* LINUX && ! _STRUCT_TIMEVAL */ + +#ifdef 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 */ + +#ifdef QNX4 + typedef long fd_mask; + #define NFDBITS (sizeof (fd_mask) * NBBY) /* bits per mask */ +#endif /* QNX4 */ + +#ifdef NW + #define fd_mask fd_set + #define INADDR_NONE -1l + #define Sleep delay + + #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 /* NW */ + +/********************************** Unicode ***********************************/ +/* + * Constants and limits. Also FNAMESIZE and PATHSIZE are currently defined + * in param.h to be 128 and 512 + */ +#define TRACE_MAX (4096 - 48) +#define VALUE_MAX_STRING (4096 - 48) +#define SYM_MAX (512) +#define XML_MAX 4096 /* Maximum size for tags/tokens */ +#define BUF_MAX 4096 /* General sanity check for bufs */ +#define FMT_STATIC_MAX 256 /* Maximum for fmtStatic calls */ + +#if (defined (LITTLEFOOT) || defined (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 +#ifdef 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; + +/* + * Text size of buffer macro. A buffer bytes will hold (size / char size) + * characters. + */ +#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)) + +#else +#define T(s) s +typedef char char_t; +#define TSZ(x) (sizeof(x)) +#define TASTRL(x) (strlen(x) + 1) +#ifdef 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 (defined (LITTLEFOOT) && defined (INMEM)) + #include "lf/inmem.h" +#endif /* LITTLEFOOT && INMEM */ + +/* + * Type for unicode systems + */ +#ifdef UNICODE + +#define gmain wmain + +#define gasctime _wasctime +#define gsprintf swprintf +#define gprintf wprintf +#define gfprintf fwprintf +#define gsscanf swscanf +#define gvsprintf vswprintf + +#define gstrcpy wcscpy +#define gstrncpy wcsncpy +#define gstrncat wcsncat +#define gstrlen wcslen +#define gstrcat wcscat +#define gstrcmp wcscmp +#define gstrncmp wcsncmp +#define gstricmp wcsicmp +#define gstrchr wcschr +#define gstrrchr wcsrchr +#define gstrtok wcstok +#define gstrnset wcsnset +#define gstrrchr wcsrchr +#define gstrspn wcsspn +#define gstrcspn wcscspn +#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 +#define gfinddata_t _wfinddata_t +#define gfindnext _wfindnext +#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 ggetcwd _wgetcwd + +#define gtolower towlower +#define gtoupper towupper +#ifdef 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 gisupper iswupper +#define gislower iswlower +#endif /* if CE */ +#define gisalnum iswalnum +#define gisalpha iswalpha +#define gatoi(s) wcstol(s, NULL, 10) + +#define gctime _wctime +#define ggetenv _wgetenv +#define gexecvp _wexecvp + +#else /* ! UNICODE */ + +#ifdef VXWORKS +#define gchdir vxchdir +#define gmkdir vxmkdir +#define grmdir vxrmdir +#elif (defined (LYNX) || defined (LINUX) || defined (MACOSX) || defined (SOLARIS)) +#define gchdir chdir +#define gmkdir(s) mkdir(s,0755) +#define grmdir rmdir +#else +#define gchdir chdir +#define gmkdir mkdir +#define grmdir rmdir +#endif /* VXWORKS #elif LYNX || LINUX || MACOSX || SOLARIS*/ + +#define gclose close +#define gclosedir closedir +#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 + +#define gasctime asctime +#define gsprintf sprintf +#define gprintf printf +#define gfprintf fprintf +#define gsscanf sscanf +#define gvsprintf vsprintf + +#define gstrcpy strcpy +#define gstrncpy strncpy +#define gstrncat strncat +#define gstrlen strlen +#define gstrcat strcat +#define gstrcmp strcmp +#define gstrncmp strncmp +#define gstricmp strcmpci +#define gstrchr strchr +#define gstrrchr strrchr +#define gstrtok strtok +#define gstrnset strnset +#define gstrrchr strrchr +#define gstrspn strspn +#define gstrcspn strcspn +#define gstrstr strstr +#define gstrtol strtol + +#define gfopen fopen +#define gcreat creat +#define gfgets fgets +#define gfputs fputs +#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 gaccess access + +typedef struct stat gstat_t; + +#define gremove remove + +#define gtolower tolower +#define gtoupper toupper +#define gisspace isspace +#define gisdigit isdigit +#define gisxdigit isxdigit +#define gisalnum isalnum +#define gisalpha isalpha +#define gisupper isupper +#define gislower islower +#define gatoi atoi + +#define gctime ctime +#define ggetenv getenv +#define gexecvp execvp +#ifndef VXWORKS +#define gmain main +#endif /* ! VXWORKS */ +#ifdef VXWORKS +#define fcntl(a, b, c) +#endif /* VXWORKS */ +#endif /* ! UNICODE */ + +/* + * Include inmem.h here because it redefines many of the file access fucntions. + * Otherwise there would be lots more #if-#elif-#else-#endif ugliness. + */ +#ifdef INMEM + #include "lf/inmem.h" +#endif + +/********************************** Defines ***********************************/ + +#ifndef FNAMESIZE +#define FNAMESIZE 254 /* Max length of file names */ +#endif /* FNAMESIZE */ + +#define E_MAX_ERROR 4096 +#define URL_MAX 4096 + +/* + * Error types + */ +#define E_ASSERT 0x1 /* Assertion error */ +#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 + +#if (defined (ASSERT) || defined (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 /* ASSERT || ASSERT_CE */ + +/******************************************************************************/ +/* VALUE */ +/******************************************************************************/ +/* + * These values are not prefixed so as to aid code readability + */ + +typedef enum { + undefined = 0, + 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 */ + +typedef struct { + + union { + char flag; + char byteint; + short shortint; + char percent; + long integer; + long hex; + long octal; + long big[2]; +#ifdef FLOATING_POINT_SUPPORT + double floating; +#endif /* FLOATING_POINT_SUPPORT */ + char_t *string; + char *bytes; + char_t *errmsg; + void *symbol; + } value; + + vtype_t type; + unsigned int valid : 8; + unsigned int allocated : 8; /* String was balloced */ +} value_t; + +#ifndef __NO_PACK +#pragma pack() +#endif /* __NO_PACK */ + +/* + * Allocation flags + */ +#define VALUE_ALLOCATE 0x1 + +#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 } + +/******************************************************************************/ +/* + * A ring queue allows maximum utilization of memory for data storage and is + * ideal for input/output buffering. This module provides a highly effecient + * implementation and a vehicle for dynamic strings. + * + * WARNING: This is a public implementation and callers have full access to + * the queue structure and pointers. Change this module very carefully. + * + * This module follows the open/close model. + * + * Operation of a ringq where rq is a pointer to a ringq : + * + * rq->buflen contains the size of the buffer. + * rq->buf will point to the start of the buffer. + * rq->servp will point to the first (un-consumed) data byte. + * rq->endp will point to the next free location to which new data is added + * rq->endbuf will point to one past the end of the buffer. + * + * Eg. If the ringq contains the data "abcdef", it might look like : + * + * +-------------------------------------------------------------------+ + * | | | | | | | | a | b | c | d | e | f | | | | | + * +-------------------------------------------------------------------+ + * ^ ^ ^ ^ + * | | | | + * 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 + * the ringq is never filled such that servp == endp. + * + * It is the fillers 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. + */ + +/* + * Ring queue buffer structure + */ +typedef struct { + unsigned char *buf; /* Holding buffer for data */ + unsigned char *servp; /* Pointer to start of data */ + unsigned char *endp; /* Pointer to end of data */ + unsigned char *endbuf; /* Pointer to end of buffer */ + int buflen; /* Length of ring queue */ + int maxsize; /* Maximum size */ + int increment; /* Growth increment */ +} ringq_t; + +/* + * Block allocation (balloc) definitions + */ +#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 */ +#define B_MALLOCED 0x80000000 /* Block was malloced */ +#define B_DEFAULT_MEM (64 * 1024) /* Default memory allocation */ +#define B_MAX_FILES (512) /* Maximum number of files */ +#define B_FILL_CHAR (0x77) /* Fill byte for buffers */ +#define B_FILL_WORD (0x77777777) /* Fill word for buffers */ +#define B_MAX_BLOCKS (64 * 1024) /* Maximum allocated blocks */ + +/* + * Flags. The integrity value is used as an arbitrary value to fill the flags. + */ +#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 + */ + +typedef struct sym_t { + struct sym_t *forw; /* Pointer to next hash list */ + value_t name; /* Name of symbol */ + value_t content; /* Value of symbol */ + int arg; /* Parameter value */ +} 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 ((defined (WIN) || defined (CE)) && defined (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_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_CONNRESET 0x400 /* Socket connection was reset */ + +#define SOCKET_PORT_MAX 0xffff /* Max Port size */ + +/* + * Socket error values + */ +#define SOCKET_WOULDBLOCK 1 /* Socket would block on I/O */ +#define SOCKET_RESET 2 /* Socket has been reset */ +#define SOCKET_NETDOWN 3 /* Network is down */ +#define SOCKET_AGAIN 4 /* Issue the request again */ +#define SOCKET_INTR 5 /* Call was interrupted */ +#define SOCKET_INVAL 6 /* Invalid */ + +/* + * Handler event masks + */ +#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) + +#ifdef 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, + 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 *********************************/ +/* + * Balloc module + * + */ + +extern void bclose(); +extern int bopen(void *buf, int bufsize, int flags); + +/* + * Define NO_BALLOC to turn off our balloc module altogether + * #define NO_BALLOC 1 + */ + +#ifdef 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) + +#else /* BALLOC */ + +#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) + +#ifdef UNICODE +#define bstrdupA(B_ARGS, p) bstrdupA(p) +#else /* UNICODE */ +#define bstrdupA bstrdup +#endif /* UNICODE */ + +#endif /* B_STATS */ + +#define gstrdup bstrdup +extern void *balloc(B_ARGS_DEC, int size); +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); + +#ifdef UNICODE +extern char *bstrdupA(B_ARGS_DEC, char *s); +#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 */ + + +#ifndef LINUX +extern char_t *basename(char_t *name); +#endif /* !LINUX */ + +#if (defined (UEMF) && defined (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); + +#ifdef 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 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 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 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, ...); + +#ifdef 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); +#else +#define ringqPutcA ringqPutc +#define ringqInsertcA ringqInsertc +#define ringqPutStrA ringqPutStr +#define ringqGetcA ringqGetc +#endif /* UNICODE */ + +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 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(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); +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 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 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, 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/cleopatre/application/spidgoahead/um.c b/cleopatre/application/spidgoahead/um.c new file mode 100644 index 0000000000..f0688a84c9 --- /dev/null +++ b/cleopatre/application/spidgoahead/um.c @@ -0,0 +1,1436 @@ +/* + * 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: um.c,v 1.5 2002/10/24 14:44:50 bporter Exp $ + */ + +/******************************** 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 qHierarchicalAccess +/* + * user-provided function to allow hierarchical access protection. See below. + * for details. + */ +extern bool_t dmfCanAccess(const char_t* usergroup, const char_t* group); +#endif +#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) { +#ifdef qHierarchicalAccess + /* + * If we are compiling with the hierarchical access extensions, we + * instead call the user-provided function that checks to see whether + * the current user's access level is greater than or equal to the + * access level required for this URL. + */ + return dmfCanAccess(usergroup, group); + +#else + if (usergroup && (gstrcmp(group, usergroup) != 0)) { + return FALSE; + + } +#endif + } + +/* + * 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/cleopatre/application/spidgoahead/um.h b/cleopatre/application/spidgoahead/um.h new file mode 100644 index 0000000000..b4c30175bc --- /dev/null +++ b/cleopatre/application/spidgoahead/um.h @@ -0,0 +1,185 @@ +/* + * 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: um.h,v 1.2 2002/10/24 14:44:50 bporter Exp $ + */ + +#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 ***********************************/ + +#ifndef 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/cleopatre/application/spidgoahead/umui.c b/cleopatre/application/spidgoahead/umui.c new file mode 100644 index 0000000000..411adf35aa --- /dev/null +++ b/cleopatre/application/spidgoahead/umui.c @@ -0,0 +1,642 @@ +/* + * umui.c -- User Management GoForm Processing + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id: umui.c,v 1.2 2002/10/24 14:44:50 bporter Exp $ + */ + +/******************************** Description *********************************/ + +/* + * This module provides GoForm functions for User management + */ + +/********************************* Includes ***********************************/ + +#include "wsIntrn.h" +#include "um.h" + +/********************************* Defines ************************************/ + +#define NONE_OPTION T("") +#define MSG_START T("

") +#define MSG_END T("

") + +/**************************** Forward Declarations ****************************/ + +static void formAddUser(webs_t wp, char_t *path, char_t *query); +static void formDeleteUser(webs_t wp, char_t *path, char_t *query); +static void formDisplayUser(webs_t wp, char_t *path, char_t *query); +static int aspGenerateUserList(int eid, webs_t wp, + int argc, char_t **argv); + +static void formAddGroup(webs_t wp, char_t *path, char_t *query); +static void formDeleteGroup(webs_t wp, char_t *path, char_t *query); +static int aspGenerateGroupList(int eid, webs_t wp, + int argc, char_t **argv); + +static void formAddAccessLimit(webs_t wp, char_t *path, char_t *query); +static void formDeleteAccessLimit(webs_t wp, char_t *path, char_t *query); +static int aspGenerateAccessLimitList(int eid, webs_t wp, + int argc, char_t **argv); + +static int aspGenerateAccessMethodList(int eid, webs_t wp, + int argc, char_t **argv); +static int aspGeneratePrivilegeList(int eid, webs_t wp, + int argc, char_t **argv); + +static void formSaveUserManagement(webs_t wp, char_t *path, char_t *query); +static void formLoadUserManagement(webs_t wp, char_t *path, char_t *query); + +static void websMsgStart(webs_t wp); +static void websMsgEnd(webs_t wp); + +/*********************************** Code *************************************/ +/* + * Set up the User Management form handlers + */ + +void formDefineUserMgmt(void) +{ + websAspDefine(T("MakeGroupList"), aspGenerateGroupList); + websAspDefine(T("MakeUserList"), aspGenerateUserList); + websAspDefine(T("MakeAccessLimitList"), aspGenerateAccessLimitList); + websAspDefine(T("MakeAccessMethodList"), aspGenerateAccessMethodList); + websAspDefine(T("MakePrivilegeList"), aspGeneratePrivilegeList); + + websFormDefine(T("AddUser"), formAddUser); + websFormDefine(T("DeleteUser"), formDeleteUser); + websFormDefine(T("DisplayUser"), formDisplayUser); + websFormDefine(T("AddGroup"), formAddGroup); + websFormDefine(T("DeleteGroup"), formDeleteGroup); + websFormDefine(T("AddAccessLimit"), formAddAccessLimit); + websFormDefine(T("DeleteAccessLimit"), formDeleteAccessLimit); + + websFormDefine(T("SaveUserManagement"), formSaveUserManagement); + websFormDefine(T("LoadUserManagement"), formLoadUserManagement); +} + +/******************************************************************************/ +/* + * Add a user + */ + +static void formAddUser(webs_t wp, char_t *path, char_t *query) +{ + char_t *userid, *pass1, *pass2, *group, *enabled, *ok; + bool_t bDisable; + int nCheck; + + a_assert(wp); + + userid = websGetVar(wp, T("user"), T("")); + pass1 = websGetVar(wp, T("password"), T("")); + pass2 = websGetVar(wp, T("passconf"), T("")); + group = websGetVar(wp, T("group"), T("")); + enabled = websGetVar(wp, T("enabled"), T("")); + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websMsgStart(wp); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Add User Cancelled")); + } else if (gstrcmp(pass1, pass2) != 0) { + websWrite(wp, T("Confirmation Password did not match.")); + } else { + if (enabled && *enabled && (gstrcmp(enabled, T("on")) == 0)) { + bDisable = FALSE; + } else { + bDisable = TRUE; + } + + nCheck = umAddUser(userid, pass1, group, 0, bDisable); + if (nCheck != 0) { + char_t * strError; + + switch (nCheck) { + case UM_ERR_DUPLICATE: + strError = T("User already exists."); + break; + + case UM_ERR_BAD_NAME: + strError = T("Invalid user name."); + break; + + case UM_ERR_BAD_PASSWORD: + strError = T("Invalid password."); + break; + + case UM_ERR_NOT_FOUND: + strError = T("Invalid or unselected group."); + break; + + default: + strError = T("Error writing user record."); + break; + } + + websWrite(wp, T("Unable to add user, \"%s\". %s"), + userid, strError); + } else { + websWrite(wp, T("User, \"%s\" was successfully added."), + userid); + } + } + + websMsgEnd(wp); + websFooter(wp); + websDone(wp, 200); +} + +/******************************************************************************/ +/* + * Delete a user + */ + +static void formDeleteUser(webs_t wp, char_t *path, char_t *query) +{ + char_t *userid, *ok; + + a_assert(wp); + + userid = websGetVar(wp, T("user"), T("")); + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websMsgStart(wp); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Delete User Cancelled")); + } else if (umUserExists(userid) == FALSE) { + websWrite(wp, T("ERROR: User \"%s\" not found"), userid); + } else if (umGetUserProtected(userid)) { + websWrite(wp, T("ERROR: User, \"%s\" is delete-protected."), userid); + } else if (umDeleteUser(userid) != 0) { + websWrite(wp, T("ERROR: Unable to delete user, \"%s\" "), userid); + } else { + websWrite(wp, T("User, \"%s\" was successfully deleted."), userid); + } + + websMsgEnd(wp); + websFooter(wp); + websDone(wp, 200); +} + +/******************************************************************************/ +/* + * Display the user info + */ + +static void formDisplayUser(webs_t wp, char_t *path, char_t *query) +{ + char_t *userid, *ok, *temp; + bool_t enabled; + + a_assert(wp); + + userid = websGetVar(wp, T("user"), T("")); + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websWrite(wp, T("")); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Display User Cancelled")); + } else if (umUserExists(userid) == FALSE) { + websWrite(wp, T("ERROR: User %s not found.\n"), userid); + } else { + websWrite(wp, T("

User ID: %s

\n"), userid); + temp = umGetUserGroup(userid); + websWrite(wp, T("

User Group: %s

\n"), temp); + enabled = umGetUserEnabled(userid); + websWrite(wp, T("

Enabled: %d

\n"), enabled); + } + + websWrite(wp, T("\n")); + websFooter(wp); + websDone(wp, 200); +} + + +/******************************************************************************/ +/* + * Generate HTML to create a list box containing the users + */ + +static int aspGenerateUserList(int eid, webs_t wp, int argc, char_t **argv) +{ + char_t *userid; + int row, nBytesSent, nBytes; + + a_assert(wp); + + nBytes = websWrite(wp, + T("")); + + return nBytesSent; +} + +/******************************************************************************/ +/* + * Add a group + */ + +static void formAddGroup(webs_t wp, char_t *path, char_t *query) +{ + char_t *group, *enabled, *privilege, *method, *ok, *pChar; + int nCheck; + short priv; + accessMeth_t am; + bool_t bDisable; + + a_assert(wp); + + group = websGetVar(wp, T("group"), T("")); + method = websGetVar(wp, T("method"), T("")); + enabled = websGetVar(wp, T("enabled"), T("")); + privilege = websGetVar(wp, T("privilege"), T("")); + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websMsgStart(wp); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Add Group Cancelled.")); + } else if ((group == NULL) || (*group == 0)) { + websWrite(wp, T("No Group Name was entered.")); + } else if (umGroupExists(group)) { + websWrite(wp, T("ERROR: Group, \"%s\" already exists."), group); + } else { + if (privilege && *privilege) { +/* + * privilege is a mulitple ")); +/* + * Add a special "" element to allow de-selection + */ + nBytes = websWrite(wp, T("