summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cleopatre/application/libspid/inc/defs.h45
-rw-r--r--cleopatre/application/libspid/inc/label.h10
-rw-r--r--cleopatre/application/libspid/inc/libspid.h26
-rw-r--r--cleopatre/application/libspid/inc/path.h15
-rw-r--r--cleopatre/application/libspid/src/network.c240
-rw-r--r--cleopatre/application/spidgoahead/.gitignore4
-rw-r--r--cleopatre/application/spidgoahead/LINUX/main.c475
-rw-r--r--cleopatre/application/spidgoahead/LINUX/spidcom_asp_functions.c583
-rw-r--r--cleopatre/application/spidgoahead/Makefile98
-rw-r--r--cleopatre/application/spidgoahead/asp.c322
-rw-r--r--cleopatre/application/spidgoahead/balloc.c974
-rw-r--r--cleopatre/application/spidgoahead/base64.c150
-rw-r--r--cleopatre/application/spidgoahead/cgi.c332
-rw-r--r--cleopatre/application/spidgoahead/default.c603
-rw-r--r--cleopatre/application/spidgoahead/default.css188
-rw-r--r--cleopatre/application/spidgoahead/doc/spc300_spidgoahead_archi.odtbin0 -> 404630 bytes
-rw-r--r--cleopatre/application/spidgoahead/ej.h47
-rw-r--r--cleopatre/application/spidgoahead/ejIntrn.h231
-rw-r--r--cleopatre/application/spidgoahead/ejlex.c722
-rw-r--r--cleopatre/application/spidgoahead/ejparse.c1805
-rw-r--r--cleopatre/application/spidgoahead/emfdb.c1064
-rw-r--r--cleopatre/application/spidgoahead/emfdb.h111
-rw-r--r--cleopatre/application/spidgoahead/form.c169
-rw-r--r--cleopatre/application/spidgoahead/h.c196
-rw-r--r--cleopatre/application/spidgoahead/handler.c417
-rw-r--r--cleopatre/application/spidgoahead/license.txt282
-rw-r--r--cleopatre/application/spidgoahead/md5.h50
-rw-r--r--cleopatre/application/spidgoahead/md5c.c338
-rw-r--r--cleopatre/application/spidgoahead/mime.c119
-rw-r--r--cleopatre/application/spidgoahead/misc.c690
-rw-r--r--cleopatre/application/spidgoahead/mocana_ssl.c539
-rw-r--r--cleopatre/application/spidgoahead/page.c143
-rw-r--r--cleopatre/application/spidgoahead/readme.txt3
-rw-r--r--cleopatre/application/spidgoahead/release.htm637
-rw-r--r--cleopatre/application/spidgoahead/release.txt625
-rw-r--r--cleopatre/application/spidgoahead/ringq.c595
-rw-r--r--cleopatre/application/spidgoahead/rom.c194
-rw-r--r--cleopatre/application/spidgoahead/security.c240
-rw-r--r--cleopatre/application/spidgoahead/sock.c792
-rw-r--r--cleopatre/application/spidgoahead/sockGen.c1046
-rw-r--r--cleopatre/application/spidgoahead/sym.c476
-rw-r--r--cleopatre/application/spidgoahead/uemf.c294
-rw-r--r--cleopatre/application/spidgoahead/uemf.h1094
-rw-r--r--cleopatre/application/spidgoahead/um.c1435
-rw-r--r--cleopatre/application/spidgoahead/um.h186
-rw-r--r--cleopatre/application/spidgoahead/um_httpd.conf78
-rw-r--r--cleopatre/application/spidgoahead/umui.c642
-rw-r--r--cleopatre/application/spidgoahead/url.c223
-rw-r--r--cleopatre/application/spidgoahead/value.c1214
-rw-r--r--cleopatre/application/spidgoahead/web/action.asp83
-rw-r--r--cleopatre/application/spidgoahead/web/addgroup.asp43
-rw-r--r--cleopatre/application/spidgoahead/web/addlimit.asp37
-rw-r--r--cleopatre/application/spidgoahead/web/adduser.asp51
-rw-r--r--cleopatre/application/spidgoahead/web/back.pngbin0 -> 488 bytes
-rw-r--r--cleopatre/application/spidgoahead/web/banner.asp9
-rw-r--r--cleopatre/application/spidgoahead/web/br0.asp264
-rw-r--r--cleopatre/application/spidgoahead/web/count.asp5
-rw-r--r--cleopatre/application/spidgoahead/web/delgroup.asp28
-rw-r--r--cleopatre/application/spidgoahead/web/dellimit.asp28
-rw-r--r--cleopatre/application/spidgoahead/web/deluser.asp28
-rw-r--r--cleopatre/application/spidgoahead/web/eth0.asp239
-rw-r--r--cleopatre/application/spidgoahead/web/footer.html7
-rw-r--r--cleopatre/application/spidgoahead/web/index.asp27
-rw-r--r--cleopatre/application/spidgoahead/web/jslib/query.js111
-rw-r--r--cleopatre/application/spidgoahead/web/left.pngbin0 -> 2897 bytes
-rw-r--r--cleopatre/application/spidgoahead/web/loadcfg.asp24
-rw-r--r--cleopatre/application/spidgoahead/web/logo-spidcom.jpgbin0 -> 2271 bytes
-rw-r--r--cleopatre/application/spidgoahead/web/nav.asp70
-rw-r--r--cleopatre/application/spidgoahead/web/none.html40
-rw-r--r--cleopatre/application/spidgoahead/web/pattern.pngbin0 -> 597 bytes
-rw-r--r--cleopatre/application/spidgoahead/web/plc0.asp307
-rw-r--r--cleopatre/application/spidgoahead/web/savecfg.asp24
-rw-r--r--cleopatre/application/spidgoahead/web/soft.asp15
-rw-r--r--cleopatre/application/spidgoahead/web/spidcom.gifbin0 -> 20557 bytes
-rw-r--r--cleopatre/application/spidgoahead/web/stat.asp61
-rw-r--r--cleopatre/application/spidgoahead/web/style/doc.css42
-rw-r--r--cleopatre/application/spidgoahead/web/style/help.htm40
-rw-r--r--cleopatre/application/spidgoahead/web/style/menu.htm55
-rw-r--r--cleopatre/application/spidgoahead/web/style/normal_ws.css48
-rw-r--r--cleopatre/application/spidgoahead/web/style/option.htm28
-rw-r--r--cleopatre/application/spidgoahead/web/um.htm10
-rw-r--r--cleopatre/application/spidgoahead/web/wifi0.asp377
-rw-r--r--cleopatre/application/spidgoahead/web/ws.gifbin0 -> 1373 bytes
-rw-r--r--cleopatre/application/spidgoahead/webcomp.c189
-rw-r--r--cleopatre/application/spidgoahead/webrom.c16
-rw-r--r--cleopatre/application/spidgoahead/webs.c3078
-rw-r--r--cleopatre/application/spidgoahead/webs.h234
-rw-r--r--cleopatre/application/spidgoahead/websSSL.c709
-rw-r--r--cleopatre/application/spidgoahead/websSSL.h66
-rw-r--r--cleopatre/application/spidgoahead/websda.c245
-rw-r--r--cleopatre/application/spidgoahead/websda.h42
-rw-r--r--cleopatre/application/spidgoahead/websuemf.c218
-rw-r--r--cleopatre/application/spidgoahead/wsIntrn.h310
-rw-r--r--cleopatre/buildroot/package/Config.in8
-rw-r--r--cleopatre/buildroot/package/spidgoahead/Config.in9
-rw-r--r--cleopatre/buildroot/package/spidgoahead/spidgoahead.mk113
-rw-r--r--cleopatre/buildroot/target/device/Spidcom/common/defconfig.base1
-rw-r--r--cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/defconfig.part1
-rwxr-xr-xcleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/init.d/S90httpd41
-rw-r--r--cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/save.lst4
-rw-r--r--cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/system.conf40
-rw-r--r--cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/web_reset.info2
102 files changed, 28175 insertions, 14 deletions
diff --git a/cleopatre/application/libspid/inc/defs.h b/cleopatre/application/libspid/inc/defs.h
index c18155466d..43a9ddd86a 100644
--- a/cleopatre/application/libspid/inc/defs.h
+++ b/cleopatre/application/libspid/inc/defs.h
@@ -28,6 +28,46 @@
/** Size of mac address */
#define LIBSPID_MAC_BIN_LEN 6
+#define LIBSPID_WIFI_DATA_ITEMS_COUNT 16
+#define LIBSPID_WIFI_DATA_ITEMS_COMPARE_COUNT 1
+
+#define LIBSPID_WIFI_DATA_LABEL_SSID "SSID"
+#define LIBSPID_WIFI_DATA_LABEL_CHANNEL "Channel"
+#define LIBSPID_WIFI_DATA_LABEL_WIRELESS_MODE "WirelessMode"
+#define LIBSPID_WIFI_DATA_LABEL_AUTH_MODE "AuthMode"
+#define LIBSPID_WIFI_DATA_LABEL_ENCRYP_TYPE "EncrypType"
+#define LIBSPID_WIFI_DATA_LABEL_IEEE8021X "IEEE8021X"
+#define LIBSPID_WIFI_DATA_LABEL_WPAPSK "WPAPSK"
+#define LIBSPID_WIFI_DATA_LABEL_REKEY_INTERVAL "RekeyInterval"
+#define LIBSPID_WIFI_DATA_LABEL_REKEY_METHOD "RekeyMethod"
+#define LIBSPID_WIFI_DATA_LABEL_RADIUS_SERVER "RADIUS_Server"
+#define LIBSPID_WIFI_DATA_LABEL_RADIUS_PORT "RADIUS_Port"
+#define LIBSPID_WIFI_DATA_LABEL_RADIUS_KEY "RADIUS_Key"
+#define LIBSPID_WIFI_DATA_LABEL_DEFAULTKEYID "DefaultKeyID"
+#define LIBSPID_WIFI_DATA_LABEL_KEY1TYPE "Key1Type"
+#define LIBSPID_WIFI_DATA_LABEL_KEY1STR "Key1Str"
+#define LIBSPID_WIFI_DATA_LABEL_OWN_IP_ADDR "own_ip_addr"
+
+
+
+#define LIBSPID_WIFI_DATA_SSID_MAX_LEN 33
+#define LIBSPID_WIFI_DATA_CHANNEL_MAX_LEN 3
+#define LIBSPID_WIFI_DATA_WIRELESS_MODE_MAX_LEN 3
+#define LIBSPID_WIFI_DATA_AUTH_MODE_MAX_LEN 14
+#define LIBSPID_WIFI_DATA_ENCRYP_TYPE_MAX_LEN 8
+#define LIBSPID_WIFI_DATA_IEEE8021X_MAX_LEN 2
+#define LIBSPID_WIFI_DATA_WPAPSK_MAX_LEN 65
+#define LIBSPID_WIFI_DATA_REKEY_INTERVAL_MAX_LEN 8
+#define LIBSPID_WIFI_DATA_REKEY_METHOD_MAX_LEN 8
+#define LIBSPID_WIFI_DATA_RADIUS_SERVER_LEN 16
+#define LIBSPID_WIFI_DATA_RADIUS_PORT_MAX_LEN 6
+#define LIBSPID_WIFI_DATA_RADIUS_KEY_MAX_LEN 65
+#define LIBSPID_WIFI_DATA_DEFAULTKEYID_MAX_LEN 2
+#define LIBSPID_WIFI_DATA_KEY1TYPE_MAX_LEN 2 //0:HEX, 1::ASCII
+#define LIBSPID_WIFI_DATA_KEY1STR_MAX_LEN 27
+#define LIBSPID_WIFI_DATA_OWN_IP_ADDR_MAX_LEN 16
+
+
/* CONFIG ITEM/LINE */
#define LIBSPID_CONFIG_LINE_MAX_LEN 1024
#define LIBSPID_CONFIG_KEY_MAX_LEN 63
@@ -77,6 +117,11 @@
/** Delimiter of "signal.info" file. */
#define LIBSPID_SIGNAL_INFO_DELIMITER " "
+#define LIBSPID_LINE_MAX_LEN LIBSPID_CONFIG_LINE_MAX_LEN
+
+/** web_reset.info*/
+#define LIBSPID_WEB_RESET_INFO_RESET_BY_WEB_YES "yes"
+#define LIBSPID_WEB_RESET_INFO_RESET_BY_WEB_NO "no"
/* Multicast_info */
/** Delimiter of "mcast.info" file */
diff --git a/cleopatre/application/libspid/inc/label.h b/cleopatre/application/libspid/inc/label.h
index a8ecc02404..f36c5534cd 100644
--- a/cleopatre/application/libspid/inc/label.h
+++ b/cleopatre/application/libspid/inc/label.h
@@ -44,6 +44,16 @@
#define LIBSPID_HPAV_INFO_VALUE_CCO_PROXY "proxy"
#define LIBSPID_HPAV_INFO_VALUE_CCO_MAIN "main"
+#define LIBSPID_SYSTEM_CONF_LABEL_HTTP_SERVICE "HTTP_SERVICE"
+
+#define LIBSPID_SYSTEM_CONF_VALUE_YES "yes"
+#define LIBSPID_SYSTEM_CONF_VALUE_NO "no"
+
+/* LIBSPID_WEB_RESET_INFO_PATH */
+#define LIBSPID_WEB_RESET_INFO_LABEL_RESET_BY_WEB "RESET_BY_WEB"
+#define LIBSPID_WEB_RESET_INFO_LABEL_WIFI_CFG_CHANGE "WIFI_CFG_CHANGE"
+
+
/* COMMON VALUES */
#define LIBSPID_VALUE_NONE "none"
#define LIBSPID_VALUE_BOOLEAN_FALSE "no"
diff --git a/cleopatre/application/libspid/inc/libspid.h b/cleopatre/application/libspid/inc/libspid.h
index ea2c6967df..da56e3cdaf 100644
--- a/cleopatre/application/libspid/inc/libspid.h
+++ b/cleopatre/application/libspid/inc/libspid.h
@@ -85,6 +85,27 @@ typedef struct {
unsigned int mtu;
} libspid_ip_t;
+/** structure to manage wireless network data */
+typedef struct {
+ unsigned char ssid[LIBSPID_WIFI_DATA_SSID_MAX_LEN];
+ unsigned char channel[LIBSPID_WIFI_DATA_CHANNEL_MAX_LEN];
+ unsigned char wireless_mode[LIBSPID_WIFI_DATA_WIRELESS_MODE_MAX_LEN];
+ unsigned char auth_mode[LIBSPID_WIFI_DATA_AUTH_MODE_MAX_LEN];
+ unsigned char encryp_type[LIBSPID_WIFI_DATA_ENCRYP_TYPE_MAX_LEN];
+ unsigned char ieee8021x[LIBSPID_WIFI_DATA_IEEE8021X_MAX_LEN];
+ unsigned char wpapsk[LIBSPID_WIFI_DATA_WPAPSK_MAX_LEN];
+ unsigned char rekey_interval[LIBSPID_WIFI_DATA_REKEY_INTERVAL_MAX_LEN];
+ unsigned char rekey_method[LIBSPID_WIFI_DATA_REKEY_METHOD_MAX_LEN];
+ unsigned char radius_server[LIBSPID_WIFI_DATA_RADIUS_SERVER_LEN];
+ unsigned char radius_port[LIBSPID_WIFI_DATA_RADIUS_PORT_MAX_LEN];
+ unsigned char radius_key[LIBSPID_WIFI_DATA_RADIUS_KEY_MAX_LEN];
+ unsigned char default_key_id[LIBSPID_WIFI_DATA_DEFAULTKEYID_MAX_LEN];
+ unsigned char key1_type[LIBSPID_WIFI_DATA_KEY1TYPE_MAX_LEN];
+ unsigned char key1_str[LIBSPID_WIFI_DATA_KEY1STR_MAX_LEN];
+ unsigned char own_ip_addr[LIBSPID_WIFI_DATA_OWN_IP_ADDR_MAX_LEN];
+} libspid_wifi_t;
+
+
typedef enum {
LIBSPID_IMAGE_DESC_TYPE_0,
LIBSPID_IMAGE_DESC_TYPE_1,
@@ -147,6 +168,7 @@ libspid_image_get_index (const char *mtd_path, int *index);
extern libspid_error_t libspid_image_select(libspid_image_select_t select);
extern libspid_error_t libspid_network_get_ip (const char *interface, libspid_ip_t *ip);
extern libspid_error_t libspid_network_set_ip (const char *interface, const libspid_ip_t *ip);
+extern libspid_error_t libspid_network_wifi_data (char *interface, libspid_wifi_t *wifi, libspid_boolean_t isWrite);
extern libspid_error_t libspid_network_get_mac (const char *interface, unsigned char *mac);
extern libspid_error_t libspid_system_get_kernel_version(char *buffer, int buffer_len);
extern libspid_error_t libspid_system_get_plc_version (char *buffer, const int buffer_len);
@@ -175,4 +197,8 @@ extern libspid_error_t
libspid_hpav_info_read_file (libspid_hpav_info_t *hpav_info);
libspid_error_t libspid_image_update_current_index(uint32_t index,
const char *mtd_name);
+
+#define libspid_network_get_wifi(a, b) libspid_network_wifi_data(a, b, FALSE)
+#define libspid_network_set_wifi(a, b) libspid_network_wifi_data(a, b, TRUE)
+
#endif /* LIBSPID_H */
diff --git a/cleopatre/application/libspid/inc/path.h b/cleopatre/application/libspid/inc/path.h
index 0ea32663fd..4d66910612 100644
--- a/cleopatre/application/libspid/inc/path.h
+++ b/cleopatre/application/libspid/inc/path.h
@@ -19,9 +19,9 @@
#ifndef __UTESTS__
- #define LIBSPID_SAVE_LIST_PATH "/etc/save.lst"
- #define LIBSPID_SAVE_DIR_PATH "/usr/local/etc"
- #define LIBSPID_CONF_ROOT_PATH "/etc"
+ #define LIBSPID_SAVE_LIST_PATH "/etc/save.lst"
+ #define LIBSPID_SAVE_DIR_PATH "/usr/local/etc"
+ #define LIBSPID_CONF_ROOT_PATH "/etc"
#define LIBSPID_HPAV_CONF_PATH "/etc/hpav.conf"
#define LIBSPID_HPAV_INFO_PATH "/etc/hpav.info"
#define LIBSPID_PHY_CONF_PATH "/etc/phy.conf"
@@ -36,13 +36,16 @@
#define LIBSPID_FACTORY_PATH "/factory"
#define LIBSPID_DEV_PATH "/dev"
#define LIBSPID_CUR_IMG_PROC_PATH "/proc/spidimg/current_img_slot"
+ #define LIBSPID_WEB_RESET_INFO_PATH "/etc/web_reset.info"
+ #define LIBSPID_SYSTEM_CONF_PATH "/etc/system.conf"
+ #define LIBSPID_WIRELESS_CONF_PATH "/etc/Wireless/RT2870AP/RT2870AP.dat"
#else /* unitary tests paths */
#define UTESTS_TMP_DIR "/tmp/utests"
- #define LIBSPID_SAVE_LIST_PATH "/tmp/utests/etc/save.lst"
- #define LIBSPID_SAVE_DIR_PATH "/tmp/utests/local"
- #define LIBSPID_CONF_ROOT_PATH "/tmp/utests/etc"
+ #define LIBSPID_SAVE_LIST_PATH "/tmp/utests/etc/save.lst"
+ #define LIBSPID_SAVE_DIR_PATH "/tmp/utests/local"
+ #define LIBSPID_CONF_ROOT_PATH "/tmp/utests/etc"
#define LIBSPID_HPAV_CONF_PATH "/tmp/utests/etc/hpav.conf"
#define LIBSPID_HPAV_INFO_PATH "/tmp/utests/etc/hpav.info"
#define LIBSPID_PHY_CONF_PATH "/tmp/utests/etc/phy.conf"
diff --git a/cleopatre/application/libspid/src/network.c b/cleopatre/application/libspid/src/network.c
index e2ac05e53a..13c23a9423 100644
--- a/cleopatre/application/libspid/src/network.c
+++ b/cleopatre/application/libspid/src/network.c
@@ -25,6 +25,8 @@
#include <errno.h>
#include "libspid.h"
+#define DEBUG_NETWORK
+
/* write ip state machine */
enum {
SET_IP_SEARCH_IFACE,
@@ -36,7 +38,7 @@ enum {
* Give the IP setting of a network interface.<br>
* All data are coming from the configuration file content, not from the system status.
*
- * \param interface string containing network interface name (“br0”, “plc0” or “eth0”)
+ * \param interface string containing network interface name (?br0?? ?plc0??or ?eth0??
* \param ip pointer to structure to get all IP data of interface
* \return error type (LIBSPID_SUCCESS if success)
* \return LIBSPID_ERROR_PARAM: bad input parameters
@@ -123,7 +125,7 @@ libspid_error_t libspid_network_get_ip (const char *interface, libspid_ip_t *ip)
/* Suppress '\n' character. */
value[string_size - 1] = '\0';
/* Check for buffer overflow. */
- if (string_size + 1 > LIBSPID_IP_STR_MAX_LEN )
+ if (string_size > LIBSPID_IP_STR_MAX_LEN )
return LIBSPID_ERROR_NO_SPACE;
strcpy (ip_field, value);
}
@@ -143,7 +145,7 @@ libspid_error_t libspid_network_get_ip (const char *interface, libspid_ip_t *ip)
* Change the IP setting of a network interface.<br>
* All data set into the configuration file, not into the running system.
*
- * \param interface string containing network interface name (“br0”, “plc0” or “eth0”)
+ * \param interface string containing network interface name (?br0?? ?plc0??or ?eth0??
* \param ip pointer to structure containing all IP data to set to the interface
* \return error type (LIBSPID_SUCCESS if success)
* \return LIBSPID_ERROR_PARAM: bad input parameters
@@ -279,11 +281,233 @@ libspid_error_t libspid_network_set_ip (const char *interface, const libspid_ip_
return LIBSPID_SUCCESS;
}
+libspid_error_t libspid_network_remove_space (void)
+{
+ char line_buffer[LIBSPID_CONFIG_LINE_MAX_LEN];
+ char out_filename[64];
+ char filename[] = LIBSPID_WIRELESS_CONF_PATH;
+ FILE *fp_in, *fp_out;
+ int fd_in = 0;
+ char label[52], value[72], equal[4];
+
+#ifdef USE_LOCK
+ struct flock lock;
+#endif /* USE_LOCK */
+
+
+ /* open config file */
+ if ( (fd_in = open(filename, O_RDWR | O_CREAT, 0666)) < 0 )
+ {
+ //syslog(LOG_WARNING, "%s: cannot open %s (errno=%d)", __FUNCTION__, filename, errno);
+ return LIBSPID_ERROR_SYSTEM;
+ }
+ if ( (fp_in = fdopen(fd_in, "r")) == NULL )
+ {
+ close(fd_in);
+ return LIBSPID_ERROR_SYSTEM;
+ }
+ /* create the modified config file */
+ sprintf(out_filename, "%sXXXXXX", filename);
+ if ( (fp_out = fdopen(mkstemp(out_filename), "w")) == NULL )
+ {
+ fclose(fp_in);
+ close(fd_in);
+ return LIBSPID_ERROR_SYSTEM;
+ }
+
+#ifdef USE_LOCK
+ /* set the write lock */
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ if (fcntl(fd_in, F_SETLKW, &lock) < 0)
+ {
+ //syslog(LOG_WARNING, "%s: lock set failed (errno=%d)\n)", __FUNCTION__, errno);
+ fclose(fp_out);
+ fclose(fp_in);
+ close(fd_in);
+ return LIBSPID_ERROR_SYSTEM;
+ }
+#endif /* USE_LOCK */
+
+ while ( fgets(line_buffer, LIBSPID_CONFIG_LINE_MAX_LEN - 1, fp_in) )
+ {
+ equal[0] = '\0';
+ value[0] = '\0';
+ if((sscanf(line_buffer, "%s %s %s\n", label, equal, value) > 0) && !strcmp(equal, LIBSPID_CONFIG_DELIMITER))
+ {
+ fprintf(fp_out, "%s=%s\n", label, value);
+ }
+ else
+ fputs (line_buffer, fp_out);
+
+ }
+
+ fclose(fp_out);
+ fclose(fp_in);
+#ifdef USE_LOCK
+ /* close lock */
+ fcntl(fd_in, F_UNLCK, &lock);
+#endif /* USE_LOCK */
+ close(fd_in);
+
+ if ( rename(out_filename, filename) < 0 )
+ {
+ //syslog(LOG_WARNING, "%s : rename (errno=%d)\n", __FUNCTION__, errno);
+ }
+
+ return LIBSPID_SUCCESS;
+}
+
+
+libspid_error_t libspid_network_wifi_data(char *interface, libspid_wifi_t *wifi, libspid_boolean_t isWrite)
+{
+ libspid_error_t ret;
+ libspid_wifi_t org_wifi;
+ int i, k;
+
+ //check parameters
+ if (interface == NULL || wifi == NULL)
+ {
+ return LIBSPID_ERROR_PARAM;
+ }
+
+ const char *labels[LIBSPID_WIFI_DATA_ITEMS_COUNT] =
+ {
+ LIBSPID_WIFI_DATA_LABEL_SSID,
+ LIBSPID_WIFI_DATA_LABEL_CHANNEL,
+ LIBSPID_WIFI_DATA_LABEL_WIRELESS_MODE,
+ LIBSPID_WIFI_DATA_LABEL_AUTH_MODE,
+ LIBSPID_WIFI_DATA_LABEL_ENCRYP_TYPE,
+ LIBSPID_WIFI_DATA_LABEL_IEEE8021X,
+ LIBSPID_WIFI_DATA_LABEL_WPAPSK,
+ LIBSPID_WIFI_DATA_LABEL_REKEY_INTERVAL,
+ LIBSPID_WIFI_DATA_LABEL_REKEY_METHOD,
+ LIBSPID_WIFI_DATA_LABEL_RADIUS_SERVER,
+ LIBSPID_WIFI_DATA_LABEL_RADIUS_PORT,
+ LIBSPID_WIFI_DATA_LABEL_RADIUS_KEY,
+ LIBSPID_WIFI_DATA_LABEL_DEFAULTKEYID,
+ LIBSPID_WIFI_DATA_LABEL_KEY1TYPE,
+ LIBSPID_WIFI_DATA_LABEL_KEY1STR,
+ LIBSPID_WIFI_DATA_LABEL_OWN_IP_ADDR
+ };
+
+ char *values[LIBSPID_WIFI_DATA_ITEMS_COUNT] =
+ {
+ wifi->ssid,
+ wifi->channel,
+ wifi->wireless_mode,
+ wifi->auth_mode,
+ wifi->encryp_type,
+ wifi->ieee8021x,
+ wifi->wpapsk,
+ wifi->rekey_interval,
+ wifi->rekey_method,
+ wifi->radius_server,
+ wifi->radius_port,
+ wifi->radius_key,
+ wifi->default_key_id,
+ wifi->key1_type,
+ wifi->key1_str,
+ wifi->own_ip_addr
+ };
+
+
+
+ int buffers_len[LIBSPID_WIFI_DATA_ITEMS_COUNT]=
+ {
+ LIBSPID_WIFI_DATA_SSID_MAX_LEN,
+ LIBSPID_WIFI_DATA_CHANNEL_MAX_LEN,
+ LIBSPID_WIFI_DATA_WIRELESS_MODE_MAX_LEN,
+ LIBSPID_WIFI_DATA_AUTH_MODE_MAX_LEN,
+ LIBSPID_WIFI_DATA_ENCRYP_TYPE_MAX_LEN,
+ LIBSPID_WIFI_DATA_IEEE8021X_MAX_LEN,
+ LIBSPID_WIFI_DATA_WPAPSK_MAX_LEN,
+ LIBSPID_WIFI_DATA_REKEY_INTERVAL_MAX_LEN,
+ LIBSPID_WIFI_DATA_REKEY_METHOD_MAX_LEN,
+ LIBSPID_WIFI_DATA_RADIUS_SERVER_LEN,
+ LIBSPID_WIFI_DATA_RADIUS_PORT_MAX_LEN,
+ LIBSPID_WIFI_DATA_RADIUS_KEY_MAX_LEN,
+ LIBSPID_WIFI_DATA_DEFAULTKEYID_MAX_LEN,
+ LIBSPID_WIFI_DATA_KEY1TYPE_MAX_LEN,
+ LIBSPID_WIFI_DATA_KEY1STR_MAX_LEN,
+ LIBSPID_WIFI_DATA_OWN_IP_ADDR_MAX_LEN
+ };
+
+ //for the fields need compare.
+ const char *cmp_labels[LIBSPID_WIFI_DATA_ITEMS_COMPARE_COUNT] =
+ {
+ LIBSPID_WIFI_DATA_LABEL_SSID
+ };
+ char *cmp_values[LIBSPID_WIFI_DATA_ITEMS_COMPARE_COUNT] =
+ {
+ org_wifi.ssid
+ };
+
+ int cmp_buffers_len[LIBSPID_WIFI_DATA_ITEMS_COMPARE_COUNT]=
+ {
+ LIBSPID_WIFI_DATA_SSID_MAX_LEN
+ };
+
+
+ if(isWrite)
+ {
+ ret = libspid_config_read_items (LIBSPID_WIRELESS_CONF_PATH, cmp_labels, cmp_values,
+ cmp_buffers_len, LIBSPID_WIFI_DATA_ITEMS_COMPARE_COUNT);
+ if(ret != LIBSPID_SUCCESS)
+ return ret;
+
+ for(i = 0; i < LIBSPID_WIFI_DATA_ITEMS_COMPARE_COUNT; i++)
+ {
+ for(k = 0; k < LIBSPID_WIFI_DATA_ITEMS_COUNT; k++)
+ {
+ if(!strcmp(cmp_labels[i], labels[k])) //label the same
+ {
+ if(strcmp(cmp_values[i], values[k])) //value different
+ {
+ ret = libspid_config_write_item (LIBSPID_WEB_RESET_INFO_PATH, LIBSPID_WEB_RESET_INFO_LABEL_WIFI_CFG_CHANGE,
+ LIBSPID_WEB_RESET_INFO_RESET_BY_WEB_YES);
+ if (LIBSPID_SUCCESS == ret)
+ {
+ libspid_system_save_file (LIBSPID_WEB_RESET_INFO_PATH);
+ }
+ else
+ return ret;
+ }
+ }
+ }
+ }
+
+ ret = libspid_config_write_items (LIBSPID_WIRELESS_CONF_PATH, labels, values,
+ LIBSPID_WIFI_DATA_ITEMS_COUNT);
+
+ //orginal config API uses "label = value", however R wifi driver uses "label=value".
+ libspid_network_remove_space();
+ }
+ else
+ {
+ ret = libspid_config_read_items (LIBSPID_WIRELESS_CONF_PATH, labels, values,
+ buffers_len, LIBSPID_WIFI_DATA_ITEMS_COUNT);
+ }
+
+#ifdef DEBUG_NETWORK
+ printf("[libspid_network_wifi_data]wifi->ssid %s, wifi->ssid len %d\n", wifi->ssid, strlen(wifi->ssid));
+ printf("[libspid_network_wifi_data]wifi->channel %s, wifi->channel len %d\n", wifi->channel, strlen(wifi->channel));
+
+#endif
+
+ return ret;
+
+}
+
+
+
/**
* Get the MAC address of an network interface.<br>
* Result is put into a provided buffer in string format aa:bb:cc:dd:ee:ff hexa address. Buffer must be of LIBSPID_MAC_STRING_MAX_LEN.
*
- * \param interface string containing network interface name (“br0”, “plc0” or “eth0”)
+ * \param interface string containing network interface name (?br0?? ?plc0??or ?eth0??
* \param mac buffer to get the MAC address in hexa string format with ':' inside
* \return error type (LIBSPID_SUCCESS if success)
* \return LIBSPID_ERROR_PARAM: bad input parameters
@@ -305,13 +529,13 @@ libspid_error_t libspid_network_get_mac (const char *interface, unsigned char *m
s = socket( PF_PACKET, SOCK_RAW, htons(ETH_P_ALL) );
if( s < 0 )
- {
+ {
return LIBSPID_ERROR_SYSTEM;
- }
+ }
memset (&ifr, 0, sizeof (struct ifreq));
- strcpy(ifr.ifr_name, interface);
- if ( ioctl(s, SIOCGIFHWADDR, &ifr) != 0 )
+ strcpy(ifr.ifr_name, interface);
+ if ( ioctl(s, SIOCGIFHWADDR, &ifr) != 0 )
{
close (s);
return LIBSPID_ERROR_SYSTEM;
diff --git a/cleopatre/application/spidgoahead/.gitignore b/cleopatre/application/spidgoahead/.gitignore
new file mode 100644
index 0000000000..487f4b5400
--- /dev/null
+++ b/cleopatre/application/spidgoahead/.gitignore
@@ -0,0 +1,4 @@
+obj
+/.compiled
+/web.tar
+/httpd
diff --git a/cleopatre/application/spidgoahead/LINUX/main.c b/cleopatre/application/spidgoahead/LINUX/main.c
new file mode 100644
index 0000000000..fd8442931b
--- /dev/null
+++ b/cleopatre/application/spidgoahead/LINUX/main.c
@@ -0,0 +1,475 @@
+/*
+ * 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 <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#ifdef WEBS_SSL_SUPPORT
+#include "../websSSL.h"
+#endif
+
+#ifdef USER_MANAGEMENT_SUPPORT
+#include "../um.h"
+void formDefineUserMgmt (void);
+#endif
+
+#include "libspid.h"
+
+/*********************************** Locals ***********************************/
+/*
+ * Change configuration here
+ */
+
+static char_t *rootWeb = T("/usr/share/httpd/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 */
+static libspid_ip_t ip;
+
+/****************************** 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
+ */
+
+#define SPC300_RESET_CAUSE_STR_MAX_LEN 17
+
+int
+main (int argc, char** argv)
+{
+ int ret = 0;
+ char enable[LIBSPID_CONFIG_LINE_MAX_LEN] = {0};
+ char sys_reset_cause[SPC300_RESET_CAUSE_STR_MAX_LEN];
+
+ ret = libspid_config_read_item (
+ LIBSPID_WEB_RESET_INFO_PATH,
+ LIBSPID_WEB_RESET_INFO_LABEL_RESET_BY_WEB,
+ sys_reset_cause, LIBSPID_LINE_MAX_LEN);
+ if (LIBSPID_SUCCESS == ret &&
+ 0 == strcmp (sys_reset_cause,
+ LIBSPID_WEB_RESET_INFO_RESET_BY_WEB_YES))
+ {
+ ret = libspid_config_write_item (
+ LIBSPID_WEB_RESET_INFO_PATH,
+ LIBSPID_WEB_RESET_INFO_LABEL_RESET_BY_WEB,
+ LIBSPID_WEB_RESET_INFO_RESET_BY_WEB_NO);
+ if (LIBSPID_SUCCESS == ret)
+ {
+ libspid_system_save_file (LIBSPID_WEB_RESET_INFO_PATH);
+ }
+
+ libspid_config_write_item (
+ LIBSPID_WEB_RESET_INFO_PATH,
+ LIBSPID_WEB_RESET_INFO_LABEL_RESET_BY_WEB,
+ LIBSPID_WEB_RESET_INFO_RESET_BY_WEB_YES);
+ }
+
+ ret = libspid_config_read_item (LIBSPID_SYSTEM_CONF_PATH,
+ LIBSPID_SYSTEM_CONF_LABEL_HTTP_SERVICE,
+ enable, LIBSPID_CONFIG_LINE_MAX_LEN);
+
+ /*
+ * http service is disabled for HTTP_SERVICE = no
+ * in /etc/system.conf
+ * in other case it is enabled
+ */
+ if (ret == LIBSPID_SUCCESS && strcmp (enable, LIBSPID_SYSTEM_CONF_VALUE_NO) == 0)
+ return 0;
+ /*
+ * 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 (UM_TXT_FILENAME);
+#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;
+ }
+ // Spidcom modification
+ if (libspid_network_get_ip ("br0", &ip) != LIBSPID_SUCCESS)
+ strcpy (host, "127.0.0.1");
+ else
+ strcpy (host, ip.address);
+ // End
+ 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
+ */
+ sprintf (webdir, "%s", 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("index.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);
+ spidcom_register_all ();
+
+ /*
+ * 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("<body><h2>Name: %s, Address: %s</h2>\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("index.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/LINUX/spidcom_asp_functions.c b/cleopatre/application/spidgoahead/LINUX/spidcom_asp_functions.c
new file mode 100644
index 0000000000..0d9a2be528
--- /dev/null
+++ b/cleopatre/application/spidgoahead/LINUX/spidcom_asp_functions.c
@@ -0,0 +1,583 @@
+/*-----------------------------------------------------------------------------
+File: spidcom_functions.c
+ Description: Asp functions definition used intenally by GoAhead web server.
+ Project: HTTP server
+ Target: SPiDCOM modem w/ SPC300
+ Version: 0.1
+ Revision list: -
+ Company: SPiDCOM
+ Author: Uros Gardasevic
+ Date: September 2009.
+-----------------------------------------------------------------------------*/
+
+
+
+
+/**
+ *
+ * \brief Function naming:
+ * \brief If it is data-function, it is in format : spidcom_asp_desc_h, where h stands for head, because function will be called in head section of html page.
+ * \brief If it is action-function, it is in format : spidcom_asp_desc_b, where b stands for body, because function will be called in body section of html page.
+ * \brief It is important because of error reporting to the end user.
+ *
+ */
+
+
+/**
+ * For function called from head section, error will be reported during the load of body, and for function called from body section, error will be reported
+ * wright away on the place from where it is actualy called.
+ */
+
+
+#include "../uemf.h"
+#include "../wsIntrn.h"
+#ifdef WEBS_SSL_SUPPORT
+#include "../websSSL.h"
+#endif
+#ifdef USER_MANAGEMENT_SUPPORT
+#include "../um.h"
+void formDefineUserMgmt(void);
+#endif
+
+#define DEBUG_ASP_FUNCTIONS
+
+#include "libspid.h"
+
+typedef char mac_t[6];
+
+
+
+char* human_readable(int return_value)
+{
+ switch(return_value){
+ case LIBSPID_SUCCESS:
+ return "Success";
+ break;
+ case LIBSPID_ERROR_PARAM:
+ return "Bad input parameters";
+ break;
+ case LIBSPID_ERROR_NO_SPACE:
+ return "Not enough aviable space";
+ break;
+ case LIBSPID_ERROR_NOT_FOUND:
+ return "Item / data not found";
+ break;
+ case LIBSPID_ERROR_SYSTEM:
+ return "System error.";
+ default:
+ /*
+ * Not possible but ok...
+ */
+ return "Unknown error";
+ break;
+ }
+}
+
+
+
+/*
+ *
+ * MASTER-only functions
+ *
+ */
+
+
+/*
+ *
+ * MASTER AND SLAVE functions
+ *
+ */
+
+/**
+ * Write the IP setting of a network interface.
+
+ *
+ * \param interface string containing network interface name (?br0?? ?plc0??or ?eth0??
+ * This argument is fetched by ejArgs() function.
+ * \return ip_data separated by ':' on success, or string containing error description on error.
+ */
+
+
+static int spidcom_asp_get_ip_data_h(int eid, webs_t wp, int argc, char_t **argv){
+
+ libspid_ip_t ip_data;
+ libspid_error_t return_value;
+ char *interface;
+
+ if ( ejArgs(argc, argv, T("%s"), &interface) < 1) {
+ websError(wp, 400, T("Insufficient args\n"));
+ return -1;
+ }
+
+ if ((return_value = libspid_network_get_ip(interface, &ip_data)) == LIBSPID_SUCCESS)
+ websWrite(wp, "\"%s:%s:%s:%s:%s\"", ip_data.mode?"DHCP":"STATIC", ip_data.address, ip_data.netmask, ip_data.broadcast, ip_data.gateway);
+ else
+ websWrite(wp, "\"error: Ip data : %s\"", human_readable(return_value));
+
+ return 0;
+}
+
+//[Terry] write API description
+
+static int spidcom_asp_get_wifi_data_h(int eid, webs_t wp, int argc, char_t **argv){
+
+ libspid_wifi_t wifi_data;
+ libspid_error_t return_value;
+ char *interface;
+
+ if ( ejArgs(argc, argv, T("%s"), &interface) < 1) {
+ websError(wp, 400, T("Insufficient args\n"));
+ return -1;
+ }
+
+ if ((return_value = libspid_network_get_wifi(interface, &wifi_data)) == LIBSPID_SUCCESS)
+ {
+ websWrite(wp, "\"%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\"",
+ wifi_data.ssid, wifi_data.channel, wifi_data.wireless_mode,
+ wifi_data.auth_mode, wifi_data.encryp_type, wifi_data.wpapsk,
+ wifi_data.rekey_interval, wifi_data.radius_server,
+ wifi_data.radius_port, wifi_data.radius_key, wifi_data.key1_type, wifi_data.key1_str);
+ }
+ else
+ websWrite(wp, "\"error: wifi data : %s\"", human_readable(return_value));
+
+ return 0;
+}
+
+
+
+/**
+ * Write the MAC address of a network interface.
+
+ *
+ * \param interface string containing network interface name (?br0?? ?plc0??or ?eth0??
+ * This argument is fetched by ejArgs() function.
+ * \return mac string on success, or string containing error description on error.
+ */
+
+
+static int spidcom_asp_get_mac_h(int eid, webs_t wp, int argc, char_t **argv)
+{
+
+ libspid_error_t return_value;
+ char *interface;
+ char mac[24];
+
+ if ( ejArgs(argc, argv, T("%s"), &interface) < 1) {
+ websError(wp, 400, T("Insufficient args\n"));
+ return -1;
+ }
+
+ if ((return_value = libspid_network_get_mac(interface, mac)) == LIBSPID_SUCCESS)
+ websWrite(wp, "\"%s\"", mac);
+ else
+ websWrite(wp, "\"error: MAC data : %s\"", human_readable(return_value));
+
+ return 0;
+}
+
+
+
+/**
+ * Write the local statistics values.
+
+ *
+ * \param mac_address string containing mac address of the device
+ * This argument is fetched by ejArgs() function.
+ * \return Eoc statistics values separated by ':' on success, or string containing error description on error.
+ */
+
+static int spidcom_asp_get_stat_h(int eid, webs_t wp, int argc, char_t **argv)
+{
+
+ FILE *fp;
+ char *interface;
+ char filename[16] = "/proc/net/dev";
+ char word[80];
+ char buffer[LIBSPID_LINE_MAX_LEN];
+ char *delimiter = NULL;
+ int index = 0, range = 0;
+ int stat[20];
+ int found_intefrace = 0, found_delimiter=0; //flag
+ int length = 0;
+
+
+
+ interface = websGetVar(wp,"interface", "0"); // Get intrerface name from QUERY_STRING
+ if ((fp = fopen(filename, "r")) == NULL) // open file for fething data
+ {
+ websWrite(wp, "\"error: Statistics. File not exist.\"");
+ return 0;
+ }
+
+ fgets(buffer, LIBSPID_LINE_MAX_LEN-1, fp ); //Eliminate first two lines
+ fgets(buffer, LIBSPID_LINE_MAX_LEN-1, fp );
+
+
+ while (!feof(fp))
+ {
+ if (found_intefrace && (index<16)) // interface found and not all data is readed yet
+ {
+ fscanf(fp, "%d", &stat[index++] ); // so please read it then...
+ if (index == 16) // dont circle anymore if there is no need...
+ break;
+ }
+ else
+ { // Search for paticular inteface
+ fscanf(fp, "%s", &word);
+ if ( (word[0] < '0') || (word[0] > '9')) // ignore integer values
+ {
+ if ( (delimiter = strchr(word, ':'))){ // extract the interface name
+ length = strlen(word); // remeber size of readed word
+ *(delimiter) = 0;
+ }
+ if (!strcmp(word, interface))
+ {
+ found_intefrace++; // interface found, raise the flag...
+ if ( length > (strlen(word)+1) ) // if there is no white space between interface name and value read it over here
+ stat[index++] = atoi(delimiter + 1);
+ }
+ }
+ }
+ }
+ if(found_intefrace)
+ {
+ range = index;
+ for (index = 0; index < (range/2); index++) // send the data to the browser
+ websWrite(wp, T("%d:%d:"), stat[index], stat[index+8]);
+ }
+ else
+ {
+ websWrite(wp, "\"error: Statistics : Intreface not found\"");
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+ return 0;
+}
+
+
+static int spidcom_asp_eoc_get_topo_h(int eid, webs_t wp, int argc, char_t **argv)
+{
+#if 0
+ mac_t mac_address_list[LIBSPID_ONLINE_INFO_LINE_MAX_NB];
+ int mac_address_count = 0;
+ libspid_error_t return_value;
+ int i = 0, end = 0;
+ char mac[LIBSPID_MAC_STR_LEN];
+
+
+ if ((return_value = libspid_eoc_get_topo(mac_address_list[0], &mac_address_count)) == LIBSPID_SUCCESS)
+ {
+ for (i ; i < mac_address_count; i ++)
+ {
+ libspid_mac_bin_to_str(mac_address_list[i], mac);
+ websWrite(wp, "%s-", mac);
+ }
+ }
+ else
+ {
+ websWrite(wp, "\"error: Online station : %s\"", human_readable(return_value));
+ }
+#endif
+ return 0;
+}
+
+
+
+
+/**
+ * Reboot the modem. ASP page which contain call to this function must have \param doit=yes in its QUERY_STRING.
+ */
+
+static int spidcom_asp_reboot_b (int eid, webs_t wp, int argc, char_t **argv)
+{
+ char *doit, *inter;
+ int ret;
+ doit = websGetVar (wp, T ("doit"), T ("no"));
+ inter = websGetVar (wp, T ("inter"), T ("br0"));
+ if (!(strcmp (doit, "yes")))
+ {
+ websWrite (wp, "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"20; URL=%s.asp\"></head>\n", inter);
+ websWrite (wp, "<h2> System is rebooting. Wait for page to be refreshed.</h2>");
+
+ ret = libspid_config_write_item (
+ LIBSPID_WEB_RESET_INFO_PATH,
+ LIBSPID_WEB_RESET_INFO_LABEL_RESET_BY_WEB,
+ LIBSPID_WEB_RESET_INFO_RESET_BY_WEB_YES);
+ if (LIBSPID_SUCCESS != ret)
+ websWrite (wp, "Write web_reset.info error<br>");
+
+ ret = libspid_system_save_file (LIBSPID_WEB_RESET_INFO_PATH);
+ if (LIBSPID_SUCCESS != ret)
+ websWrite (wp, "save web_reset.info error<br>");
+
+ libspid_system_reboot ();
+ }
+ else
+ websWrite (wp, "Not allowed. Please try again.");
+
+ return 0;
+}
+
+
+
+
+static int spidcom_asp_eoc_action(int eid, webs_t wp, int argc, char_t **argv)
+{
+#if 0
+ char *mac;
+ char *action;
+ libspid_error_t return_value;
+ libspid_eoc_link_quality_t link_quality;
+ libspid_eoc_dev_info_t eoc_dev_info;
+ libspid_eoc_rt_stat_t eoc_rt_stat;
+
+
+ mac = websGetVar(wp, "mac", "00:00:00:00:00:00");
+ action = websGetVar(wp, "action", "none");
+
+
+ memset(&link_quality, 0, sizeof(link_quality));
+ memset(&eoc_dev_info, 0, sizeof(eoc_dev_info));
+ memset(&eoc_rt_stat, 0, sizeof(eoc_rt_stat));
+
+
+ if (!strcmp(action,"get_link"))
+ {
+ if ((return_value = libspid_eoc_get_link(mac, &link_quality)) == LIBSPID_SUCCESS)
+ websWrite(wp, "\"%d:%d:%d:%d\"", link_quality.down_att, link_quality.up_att, link_quality.down_quality, link_quality.up_quality);
+ else
+ websWrite(wp, "\"error: EOC : %s\"", human_readable(return_value));
+ }
+ else
+ if (!strcmp(action,"get_device_info"))
+ {
+ if ((return_value = libspid_eoc_get_device_info(mac, &eoc_dev_info)) == LIBSPID_SUCCESS)
+ websWrite(wp, "\"%s:%d:%s\"", eoc_dev_info.model_no, eoc_dev_info.eth_port_nb, eoc_dev_info.sw_version);
+ else
+ websWrite(wp, "\"error: EOC :%s\"", human_readable(return_value));
+ }
+ else
+ if (!strcmp(action,"get_stat"))
+ {
+ if ((return_value = libspid_eoc_get_stat(mac, &eoc_rt_stat)) == LIBSPID_SUCCESS)
+ websWrite(wp, "\"%d:%d:%d:%d:%d:%d:%d:%d:%d:%d\"", eoc_rt_stat.tx_pkt , eoc_rt_stat.tx_byte, eoc_rt_stat.tx_bcast, eoc_rt_stat.tx_mcast, eoc_rt_stat.tx_dropped, eoc_rt_stat.rx_pkt , eoc_rt_stat.rx_byte, eoc_rt_stat.rx_bcast, eoc_rt_stat.rx_mcast, eoc_rt_stat.rx_dropped);
+ else
+ websWrite(wp, "\"error: EOC : %s\"", human_readable(return_value));
+ }
+ else
+ if (!strcmp(action,"reboot"))
+ {
+ if ((return_value = libspid_eoc_reboot(mac)) == LIBSPID_SUCCESS)
+ websWrite(wp, "\"Station with %s address is rebooted\"", mac);
+ else
+ websWrite(wp, "\"error: EOC : %s\"", human_readable(return_value));
+ }
+ else
+ if (!strcmp(action,"reset_stat"))
+ {
+ if ((return_value = libspid_eoc_reboot(mac)) == LIBSPID_SUCCESS)
+ websWrite(wp, "\"Station with %s address reset statistics\"", mac);
+ else
+ websWrite(wp, "\"error: EOC : %s\"", human_readable(return_value));
+ }
+#endif
+ return 0;
+}
+
+static void spidcom_form_action(webs_t wp, char_t *path, char_t *query)
+{
+ char_t *reboot,
+ *inter,
+ *ipmode;
+
+ libspid_ip_mode_t ip_mode;
+ libspid_ip_t ip_data;
+ libspid_error_t return_value;
+
+
+
+ ipmode = websGetVar(wp, T("dhcp"), T("static"));
+ if (!(strcmp(ipmode,"static")))
+ ip_data.mode = LIBSPID_IP_MODE_STATIC;
+ else
+ if(!(strcmp(ipmode,"dhcp")))
+ ip_data.mode = LIBSPID_IP_MODE_DHCP;
+
+
+ strcpy(ip_data.address, websGetVar(wp, T("ip"), T("0.0.0.0")));
+ strcpy(ip_data.netmask, websGetVar(wp, T("nm"), T("0.0.0.0")));
+ strcpy(ip_data.broadcast, websGetVar(wp, T("bc"), T("0.0.0.0")));
+ strcpy(ip_data.gateway, websGetVar(wp, T("gw"), T("0.0.0.0")));
+
+ inter = websGetVar(wp, T("inter"), T("none"));
+ reboot = websGetVar(wp, T("reboot"), T("no"));
+
+ if (!strcmp(reboot, "yes"))
+ {
+ char line_buffer[LIBSPID_CONFIG_LINE_MAX_LEN];
+ sprintf(line_buffer, "count.asp?doit=yes&inter=%s", inter);
+ websRedirect(wp, line_buffer);
+ return;
+ }
+
+ websHeader(wp);
+
+ websWrite(wp, T("<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"3; URL=../%s.asp\">\n"), inter);
+ websWrite(wp, T("<link rel=\"stylesheet\" href=\"/../style/normal_ws.css\" type=\"text/css\">\n</head>\n<body>\n"), inter);
+
+ websWrite(wp, T("<h3>"));
+
+ if ((return_value = libspid_network_set_ip (inter, &ip_data)) == LIBSPID_SUCCESS)
+ ;//websWrite(wp, "libspid_network_set_ip : ok<br>");
+ else
+ websWrite(wp, "\"error: IP data : %s\" <br>", human_readable(return_value));
+
+
+ if ((return_value = libspid_system_save()) == LIBSPID_SUCCESS)
+ ;//websWrite(wp, "libspid_system_save : ok <br>");
+ else
+ websWrite(wp, "\"error: Save problem : %s\"<br>", human_readable(return_value));
+
+
+ websWrite(wp, T("</h3>"));
+ websWrite(wp, T("</body>\n"));
+ websFooter(wp);
+ websDone(wp, 200);
+
+}
+
+
+static void spidcom_form_action_wifi(webs_t wp, char_t *path, char_t *query)
+{
+ char_t *reboot, *inter, *authmode, *rekey;
+ libspid_wifi_t wifi_data;
+ libspid_error_t return_value;
+
+ memset (&wifi_data, '\0', sizeof (libspid_wifi_t));
+
+ inter = websGetVar(wp, T("inter"), T("none"));
+ reboot = websGetVar(wp, T("reboot"), T("no"));
+
+ if (!strcmp(reboot, "yes"))
+ {
+ char line_buffer[LIBSPID_CONFIG_LINE_MAX_LEN];
+ sprintf(line_buffer, "count.asp?doit=yes&inter=%s", inter);
+ websRedirect(wp, line_buffer);
+ return;
+ }
+
+ websHeader(wp);
+
+ websWrite(wp, T("<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"3; URL=../%s.asp\">\n"), inter);
+ websWrite(wp, T("<link rel=\"stylesheet\" href=\"/../style/normal_ws.css\" type=\"text/css\">\n</head>\n<body>\n"), inter);
+
+ websWrite(wp, T("<h3>"));
+#ifdef DEBUG_ASP_FUNCTIONS
+ websWrite(wp, "\"spidcom_form_action_wifi\" <br>");
+ websWrite(wp, "reboot %s <br>", websGetVar(wp, T("reboot"), T("QQ")));
+ websWrite(wp, "inter %s <br>", websGetVar(wp, T("inter"), T("QQ")));
+ websWrite(wp, "SSID %s <br>", websGetVar(wp, T("SSID"), T("SSID_DEFAULT")));
+ websWrite(wp, "select_channel %s <br>", websGetVar(wp, T("select_channel"), T("QQ")));
+ websWrite(wp, "select_mode %s <br>", websGetVar(wp, T("select_mode"), T("QQ")));
+ websWrite(wp, "security_radiobutton %s <br>", websGetVar(wp, T("security_radiobutton"), T("QQ")));
+#endif
+ //[Terry] Check boundry condition........maxmum length...
+ //copy to ram not flash....copy all...
+ strcpy(wifi_data.ssid, websGetVar(wp, T("SSID"), T("SSID_DEFAULT")));
+ strcpy(wifi_data.channel, websGetVar(wp, T("select_channel"), T("11")));
+ strcpy(wifi_data.wireless_mode, websGetVar(wp, T("select_mode"), T("9")));
+ strcpy(wifi_data.rekey_interval, "0");
+ strcpy(wifi_data.rekey_method, "DISABLE");
+ strcpy(wifi_data.ieee8021x, "0");
+
+ authmode = websGetVar(wp, T("security_radiobutton"), T("NONE"));
+
+ if (!strcmp(authmode, "NONE"))
+ {
+
+ strcpy(wifi_data.auth_mode, "OPEN");
+ strcpy(wifi_data.encryp_type, "NONE");
+
+ }
+ else if(!strcmp(authmode, "WPAPSK"))
+ {
+ strcpy(wifi_data.auth_mode, websGetVar(wp, T("select_wpapsk_auth"), T("WPAPSKWPA2PSK")));
+ strcpy(wifi_data.encryp_type, websGetVar(wp, T("select_wpapsk_encrypt"), T("TKIPAES")));
+ strcpy(wifi_data.wpapsk, websGetVar(wp, T("wpapsk_key"), T("")));
+
+ rekey = websGetVar(wp, T("wpapsk_rekey"), T("1000"));
+ if(strcmp(rekey, "0"))
+ {
+ strcpy(wifi_data.rekey_interval, rekey);
+ strcpy(wifi_data.rekey_method, "TIME");
+ }
+
+ }
+ else if(!strcmp(authmode, "WPA"))
+ {
+ strcpy(wifi_data.auth_mode, websGetVar(wp, T("select_wpa_auth"), T("WPA1WPA2")));
+ strcpy(wifi_data.encryp_type, websGetVar(wp, T("select_wpa_encrypt"), T("TKIPAES")));
+ strcpy(wifi_data.radius_server, websGetVar(wp, T("wpa_radius_server"), T("192.168.1.1")));
+ strcpy(wifi_data.radius_port, websGetVar(wp, T("wpa_radius_port"), T("1812")));
+ strcpy(wifi_data.radius_key, websGetVar(wp, T("wpa_radius_key"), T("")));
+ strcpy(wifi_data.own_ip_addr, websGetVar(wp, T("ip"), T("0.0.0.0")));
+
+
+ rekey = websGetVar(wp, T("wpa_rekey"), T("1000"));
+ if(strcmp(rekey, "0"))
+ {
+ strcpy(wifi_data.rekey_interval, rekey);
+ strcpy(wifi_data.rekey_method, "TIME");
+ }
+ }
+ else if(!strcmp(authmode, "WEP"))
+ {
+ strcpy(wifi_data.auth_mode, websGetVar(wp, T("select_wep_auth"), T("WEPAUTO")));
+ strcpy(wifi_data.encryp_type, "WEP");
+ strcpy(wifi_data.default_key_id, "1");
+ strcpy(wifi_data.key1_str, websGetVar(wp, T("wep_key1"), T("")));
+ strcpy(wifi_data.key1_type, websGetVar(wp, T("select_wep_format"), T("0")));
+
+ }
+ else
+ {
+ //error handle
+ }
+
+ if ((return_value = libspid_network_set_wifi (inter, &wifi_data)) == LIBSPID_SUCCESS)
+ ;//websWrite(wp, "libspid_network_set_ip : ok<br>");
+ else
+ websWrite(wp, "\"error: IP data : %s\" <br>", human_readable(return_value));
+
+ if ((return_value = libspid_system_save()) == LIBSPID_SUCCESS)
+ ;//websWrite(wp, "libspid_system_save : ok <br>");
+ else
+ websWrite(wp, "\"error: Save problem : %s\"<br>", human_readable(return_value));
+
+ websWrite(wp, T("</h3>"));
+ websWrite(wp, T("</body>\n"));
+ websFooter(wp);
+ websDone(wp, 200);
+}
+
+/**
+ *
+ * Bind actual asp name used in HTML to the implemented C function.
+ *
+ */
+
+void spidcom_register_all()
+{
+ websAspDefine(T("spidcomAspEocGetTopo"), spidcom_asp_eoc_get_topo_h);
+ websAspDefine(T("spidcomAspGetStatistics"), spidcom_asp_get_stat_h); // We bind spidcomAspGetStatistic to my function
+ websAspDefine(T("spidcomAspGetIpData"), spidcom_asp_get_ip_data_h);
+ websAspDefine(T("spidcomAspGetWifiData"), spidcom_asp_get_wifi_data_h);
+ websAspDefine(T("spidcomAspGetMac"), spidcom_asp_get_mac_h);
+ websAspDefine(T("spidcomAspReboot"), spidcom_asp_reboot_b);
+
+ websAspDefine(T("spidcomAction"), spidcom_asp_eoc_action);
+ websFormDefine(T("formActionWifi"), spidcom_form_action_wifi);
+ websFormDefine(T("formAction"), spidcom_form_action);
+}
diff --git a/cleopatre/application/spidgoahead/Makefile b/cleopatre/application/spidgoahead/Makefile
new file mode 100644
index 0000000000..23663400bc
--- /dev/null
+++ b/cleopatre/application/spidgoahead/Makefile
@@ -0,0 +1,98 @@
+TARGET := .compiled
+TARGET_DEPS := httpd web.tar
+OBJPATH := obj
+INCPATH := .
+SRCPATH := .
+SUBMOD := LINUX
+
+CLEO_DIR := ../..
+LINUX_DIR := $(CLEO_DIR)/linux-2.6.25.10-spc300
+LIBSPID_DIR := $(CLEO_DIR)/application/libspid
+LIBMME_DIR := $(CLEO_DIR)/application/libmme
+
+
+LIBSPID_SO_BIN := $(LIBSPID_DIR)/libspid.so
+LIBMME_SO_BIN := $(LIBMME_DIR)/libmme.so
+
+ifeq ($(SSL_SUPPORT),y)
+ifeq ($(STAGING_DIR),)
+$(error STAGING_DIR is not set by buildroot.)
+endif
+
+SSL_INC := -I$(STAGING_DIR)/usr/include/openssl
+SSL_LIB := $(STAGING_DIR)/usr/lib/libssl.a $(STAGING_DIR)/usr/lib/libcrypto.a
+SSL_SW := -DWEBS_SSL_SUPPORT -DOPENSSL
+else
+SRC2AVOID := websSSL.c
+endif
+SRC2AVOID := $(SRC2AVOID) webcomp.c
+
+
+ifeq ($(CC_FOR_TARGET),) #direct compile
+CC=arm-linux-gcc
+CC_WITH_CFLAGS=$(CC) -I/opt/spidcom/spc300/usr/include -g -Os
+CC_WITHOUT_CFLAGS=$(CC)
+else #compile from buildroot
+CC_WITH_CFLAGS=$(CC)
+CC_WITHOUT_CFLAGS=$(CC_FOR_TARGET)
+endif
+
+RESPONSE_FILE := extra_flags
+INCLUDES := -I$(INCPATH) \
+ -I$(LINUX_DIR)/include/asm-arm/arch-spc300 \
+ -I$(CLEO_DIR)/include \
+ $(SSL_INC) \
+ $(shell $(LIBSPID_DIR)/libspid-config --I_opts)
+
+EXTRA_CFLAGS := $(INCLUDES) -MMD -Wall \
+ @$(CLEO_DIR)/$(RESPONSE_FILE) \
+ -DWEBS -DUEMF -DOS="LINUX" -DLINUX \
+ -DUSER_MANAGEMENT_SUPPORT \
+ $(SSL_SW)
+
+LIBS := $(SSL_LIB) \
+ $(shell $(LIBSPID_DIR)/libspid-config --L_opts) \
+ $(shell $(LIBSPID_DIR)/libspid-config --libs)
+
+ALL_SRCS := $(wildcard $(SUBMOD)/*.c) $(wildcard *.c)
+SRCS := $(filter-out $(SRC2AVOID),$(ALL_SRCS))
+OBJS := $(addprefix $(OBJPATH)/,$(SRCS:.c=.o))
+DEPS := $(patsubst %o,%d,$(OBJS))
+
+all: $(TARGET)
+
+$(TARGET): $(TARGET_DEPS)
+ @touch $@
+
+httpd: $(OBJS) $(LIBSPID_SO_BIN) $(LIBMME_SO_BIN) $(SSL_LIB)
+ $(CC_WITHOUT_CFLAGS) -o $@ $(OBJS) $(LIBS)
+
+#TODO: 'httpd' dependency is a patch. Should be redo.
+web.tar: httpd
+ tar -cf $@ web
+
+$(OBJPATH)/%.o: %.c
+ $(CC_WITH_CFLAGS) $(EXTRA_CFLAGS) -o $@ -c $<
+
+$(OBJPATH/$(SUBMOD))/%.o: $(SUBMOD)/%.c
+ $(CC_WITH_CFLAGS) $(EXTRA_CFLAGS) -o $@ -c $<
+
+$(OBJS): | $(OBJPATH)
+
+$(OBJPATH):
+ mkdir -p $(OBJPATH)/$(SUBMOD)
+
+$(LIBSPID_SO_BIN) $(LIBMME_SO_BIN):
+ $(error libspid or libmme output files are not found)
+
+$(SSL_LIB):
+ $(error openssl output files are not found)
+
+-include $(DEPS)
+
+.PHONY: all clean
+
+clean:
+ rm -f $(TARGET) $(TARGET_DEPS)
+ rm -f $(OBJS) $(DEPS)
+ rmdir -p $(OBJPATH)/$(SUBMOD)
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("<h2><b>ASP Error: %s</b></h2>\n"),
+ result);
+ websWrite(wp, T("<pre>%s</pre>"), nextp);
+ bfree(B_L, result);
+ } else {
+ websWrite(wp, T("<h2><b>ASP Error</b></h2>\n%s\n"),
+ nextp);
+ }
+ websWrite(wp, T("</body></html>\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 <stdarg.h>
+#include <stdlib.h>
+
+#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
+ * <http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html>,
+ * 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/doc/spc300_spidgoahead_archi.odt b/cleopatre/application/spidgoahead/doc/spc300_spidgoahead_archi.odt
new file mode 100644
index 0000000000..a71c8f7038
--- /dev/null
+++ b/cleopatre/application/spidgoahead/doc/spc300_spidgoahead_archi.odt
Binary files differ
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 <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#ifdef CE
+#ifndef UEMF
+ #include <io.h>
+#endif
+#endif
+
+#ifdef LYNX
+ #include <unistd.h>
+#endif
+
+#ifdef QNX4
+ #include <dirent.h>
+#endif
+
+#ifdef UEMF
+ #include "uemf.h"
+#else
+ #include <param.h>
+ #include <stat.h>
+ #include "basic/basicInternal.h"
+ #include "emf/emfInternal.h"
+#endif
+
+#include "ej.h"
+
+/********************************** Defines ***********************************/
+/*
+ * Constants
+ */
+#define EJ_INC 110 /* Growth for tags/tokens */
+#define EJ_SCRIPT_INC 1023 /* Growth for ej scripts */
+#define EJ_OFFSET 1 /* hAlloc doesn't like 0 entries */
+#define EJ_MAX_RECURSE 100 /* Sanity for maximum recursion */
+
+/*
+ * Ejscript Lexical analyser tokens
+ */
+#define TOK_ERR -1 /* Any error */
+#define TOK_LPAREN 1 /* ( */
+#define TOK_RPAREN 2 /* ) */
+#define TOK_IF 3 /* if */
+#define TOK_ELSE 4 /* else */
+#define TOK_LBRACE 5 /* { */
+#define TOK_RBRACE 6 /* } */
+#define TOK_LOGICAL 7 /* ||, &&, ! */
+#define TOK_EXPR 8 /* +, -, /, % */
+#define TOK_SEMI 9 /* ; */
+#define TOK_LITERAL 10 /* literal string */
+#define TOK_FUNCTION 11 /* function name */
+#define TOK_NEWLINE 12 /* newline white space */
+#define TOK_ID 13 /* function name */
+#define TOK_EOF 14 /* End of script */
+#define TOK_COMMA 15 /* Comma */
+#define TOK_VAR 16 /* var */
+#define TOK_ASSIGNMENT 17 /* = */
+#define TOK_FOR 18 /* for */
+#define TOK_INC_DEC 19 /* ++, -- */
+#define TOK_RETURN 20 /* return */
+
+/*
+ * Expression operators
+ */
+#define EXPR_LESS 1 /* < */
+#define EXPR_LESSEQ 2 /* <= */
+#define EXPR_GREATER 3 /* > */
+#define EXPR_GREATEREQ 4 /* >= */
+#define EXPR_EQ 5 /* == */
+#define EXPR_NOTEQ 6 /* != */
+#define EXPR_PLUS 7 /* + */
+#define EXPR_MINUS 8 /* - */
+#define EXPR_DIV 9 /* / */
+#define EXPR_MOD 10 /* % */
+#define EXPR_LSHIFT 11 /* << */
+#define EXPR_RSHIFT 12 /* >> */
+#define EXPR_MUL 13 /* * */
+#define EXPR_ASSIGNMENT 14 /* = */
+#define EXPR_INC 15 /* ++ */
+#define EXPR_DEC 16 /* -- */
+#define EXPR_BOOL_COMP 17 /* ! */
+/*
+ * Conditional operators
+ */
+#define COND_AND 1 /* && */
+#define COND_OR 2 /* || */
+#define COND_NOT 3 /* ! */
+
+/*
+ * States
+ */
+#define STATE_ERR -1 /* Error state */
+#define STATE_EOF 1 /* End of file */
+#define STATE_COND 2 /* Parsing a "(conditional)" stmt */
+#define STATE_COND_DONE 3
+#define STATE_RELEXP 4 /* Parsing a relational expr */
+#define STATE_RELEXP_DONE 5
+#define STATE_EXPR 6 /* Parsing an expression */
+#define STATE_EXPR_DONE 7
+#define STATE_STMT 8 /* Parsing General statement */
+#define STATE_STMT_DONE 9
+#define STATE_STMT_BLOCK_DONE 10 /* End of block "}" */
+#define STATE_ARG_LIST 11 /* Function arg list */
+#define STATE_ARG_LIST_DONE 12
+#define STATE_DEC_LIST 16 /* Declaration list */
+#define STATE_DEC_LIST_DONE 17
+#define STATE_DEC 18
+#define STATE_DEC_DONE 19
+
+#define STATE_RET 20 /* Return statement */
+
+#define STATE_BEGIN STATE_STMT
+
+/*
+ * Flags. Used in ej_t and as parameter to parse()
+ */
+#define FLAGS_EXE 0x1 /* Execute statements */
+#define FLAGS_VARIABLES 0x2 /* Allocated variables store */
+#define FLAGS_FUNCTIONS 0x4 /* Allocated function store */
+
+/*
+ * Function call structure
+ */
+typedef struct {
+ char_t *fname; /* Function name */
+ char_t **args; /* Args for function (halloc) */
+ int nArgs; /* Number of args */
+} ejfunc_t;
+
+/*
+ * EJ evaluation block structure
+ */
+typedef struct ejEval {
+ ringq_t tokbuf; /* Current token */
+ ringq_t script; /* Input script for parsing */
+ char_t *putBackToken; /* Putback token string */
+ int putBackTokenId; /* Putback token ID */
+ char_t *line; /* Current line */
+ int lineLength; /* Current line length */
+ int lineNumber; /* Parse line number */
+ int lineColumn; /* Column in line */
+} ejinput_t;
+
+/*
+ * Per Ejscript session structure
+ */
+typedef struct ej {
+ ejinput_t *input; /* Input evaluation block */
+ sym_fd_t functions; /* Symbol table for functions */
+ sym_fd_t *variables; /* hAlloc list of variables */
+ int variableMax; /* Number of entries */
+ ejfunc_t *func; /* Current function */
+ char_t *result; /* Current expression result */
+ char_t *error; /* Error message */
+ char_t *token; /* Pointer to token string */
+ int tid; /* Current token id */
+ int eid; /* Halloc handle */
+ int flags; /* Flags */
+ int userHandle; /* User defined handle */
+} ej_t;
+
+/******************************** Prototypes **********************************/
+
+extern int ejOpenBlock(int eid);
+extern int ejCloseBlock(int eid, int vid);
+extern char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg);
+#ifndef __NO_EJ_FILE
+extern char_t *ejEvalFile(int eid, char_t *path, char_t **emsg);
+#endif
+extern int ejRemoveGlobalFunction(int eid, char_t *name);
+extern void *ejGetGlobalFunction(int eid, char_t *name);
+extern int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name,
+ int (*fn)(int eid, void *handle, int argc, char_t **argv));
+extern void ejError(ej_t* ep, char_t* fmt, ...);
+extern void ejSetUserHandle(int eid, int handle);
+extern int ejGetUserHandle(int eid);
+extern int ejGetLineNumber(int eid);
+extern char_t *ejGetResult(int eid);
+extern void ejSetLocalVar(int eid, char_t *var, char_t *value);
+extern void ejSetGlobalVar(int eid, char_t *var, char_t *value);
+
+extern int ejLexOpen(ej_t* ep);
+extern void ejLexClose(ej_t* ep);
+extern int ejLexOpenScript(ej_t* ep, char_t *script);
+extern void ejLexCloseScript(ej_t* ep);
+extern void ejLexSaveInputState(ej_t* ep, ejinput_t* state);
+extern void ejLexFreeInputState(ej_t* ep, ejinput_t* state);
+extern void ejLexRestoreInputState(ej_t* ep, ejinput_t* state);
+extern int ejLexGetToken(ej_t* ep, int state);
+extern void ejLexPutbackToken(ej_t* ep, int tid, char_t *string);
+
+extern sym_fd_t ejGetVariableTable(int eid);
+extern sym_fd_t ejGetFunctionTable(int eid);
+
+extern int ejEmfOpen(int eid);
+extern void ejEmfClose(int eid);
+
+extern int ejEmfDbRead(int eid, void *handle, int argc, char_t **argv);
+extern int ejEmfDbReadKeyed(int eid, void *handle, int argc, char_t **argv);
+extern int ejEmfDbTableGetNrow(int eid, void *handle, int argc, char_t **argv);
+extern int ejEmfDbDeleteRow(int eid, void *handle, int argc, char_t **argv);
+extern int ejEmfTrace(int eid, void *handle, int argc, char_t **argv);
+extern int ejEmfDbWrite(int eid, void *handle, int argc, char_t **argv);
+extern int ejEmfDbCollectTable(int eid, void *handle, int argc, char_t **argv);
+
+#endif /* _h_EJINTERNAL */
+
diff --git a/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..2822349b6b
--- /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("/etc"); /* 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("<html>\n"));
+}
+
+/******************************************************************************/
+/*
+ * Write a webs footer
+ */
+
+void websFooter(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ websWrite(wp, T("</html>\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 GoAheads 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 GoAheads 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 GoAheads 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..77f55c181d
--- /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 *spidgoahead_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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/******************************* 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 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
+<title>GoAhead WebServer 2.1.8 Release Notes</title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document" id="goahead-webserver-2-1-8-release-notes">
+<h1 class="title">GoAhead WebServer 2.1.8 Release Notes</h1>
+<!-- NOTES: -->
+<!-- This document is maintained using the reStructuredText markup system. -->
+<!-- You may download this from <http://docutils.sf.net>. 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. -->
+<div class="contents topic" id="contents">
+<p class="topic-title"><a name="contents">Contents</a></p>
+<ul class="simple">
+<li><a class="reference" href="#id1" id="id2" name="id2">GoAhead WebServer 2.1.8 Release Notes</a><ul>
+<li><a class="reference" href="#problems-with-unicode-build" id="id3" name="id3">Problems with Unicode build</a></li>
+<li><a class="reference" href="#modified-for-windows-ce-net" id="id4" name="id4">Modified for Windows CE .NET</a></li>
+<li><a class="reference" href="#bug-with-urls-like-asp" id="id5" name="id5">Bug with URLs like &quot;&lt;...&gt;.asp/&quot;</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#goahead-webserver-2-1-7-release-notes" id="id6" name="id6">GoAhead WebServer 2.1.7 Release Notes</a><ul>
+<li><a class="reference" href="#added-support-for-the-mocana-ssl-toolkit" id="id7" name="id7">Added support for the Mocana SSL Toolkit</a></li>
+<li><a class="reference" href="#changes-to-dbsearchstring" id="id8" name="id8">Changes to <tt class="literal"><span class="pre">dbSearchString()</span></tt></a></li>
+<li><a class="reference" href="#use-memcpy-when-converting-to-from-unicode" id="id9" name="id9">Use <tt class="literal"><span class="pre">memcpy()</span></tt> when converting to/from Unicode</a></li>
+<li><a class="reference" href="#bug-when-using-utf-8-encoded-text-inside-asp-ejscript-blocks" id="id10" name="id10">Bug when using UTF-8 encoded text inside ASP/Ejscript blocks</a></li>
+<li><a class="reference" href="#wrong-error-code-on-invalid-password" id="id11" name="id11">Wrong error code on invalid password</a></li>
+<li><a class="reference" href="#windows-ce-net" id="id12" name="id12">Windows CE .NET</a></li>
+<li><a class="reference" href="#lynx-makefile" id="id13" name="id13">LYNX <tt class="literal"><span class="pre">Makefile</span></tt></a></li>
+</ul>
+</li>
+<li><a class="reference" href="#goahead-webserver-2-1-6-release-notes" id="id14" name="id14">GoAhead WebServer 2.1.6 Release Notes</a><ul>
+<li><a class="reference" href="#null-pointer-crash-in-webssafeurl" id="id15" name="id15"><tt class="literal"><span class="pre">NULL</span></tt> pointer crash in <tt class="literal"><span class="pre">websSafeUrl()</span></tt></a></li>
+</ul>
+</li>
+<li><a class="reference" href="#goahead-webserver-2-1-5-release-notes" id="id16" name="id16">GoAhead WebServer 2.1.5 Release Notes</a><ul>
+<li><a class="reference" href="#bopen-failure-mode" id="id17" name="id17"><tt class="literal"><span class="pre">bopen()</span></tt> failure mode</a></li>
+<li><a class="reference" href="#windows-95-98-me-aux-denial-of-service" id="id18" name="id18">Windows 95/98/ME <tt class="literal"><span class="pre">AUX</span></tt> Denial of Service</a></li>
+<li><a class="reference" href="#cross-site-scripting-exploit" id="id19" name="id19">404 Cross-site Scripting Exploit</a></li>
+<li><a class="reference" href="#long-url-overflow-crash" id="id20" name="id20">Long URL Overflow Crash</a></li>
+<li><a class="reference" href="#incorrect-error-code-in-security-c" id="id21" name="id21">Incorrect Error Code in <tt class="literal"><span class="pre">security.c</span></tt></a></li>
+<li><a class="reference" href="#pragma-code-for-risc-architectures" id="id22" name="id22">Pragma Code for RISC Architectures</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#goahead-webserver-2-1-4-release-notes" id="id23" name="id23">GoAhead® WebServer 2.1.4 Release Notes</a><ul>
+<li><a class="reference" href="#fixed-vulnerability-to-malicious-code-in-webs-c" id="id24" name="id24">Fixed vulnerability to malicious code in <tt class="literal"><span class="pre">webs.c</span></tt></a></li>
+<li><a class="reference" href="#https-bug-in-security-handler" id="id25" name="id25">https:// bug in security handler</a></li>
+<li><a class="reference" href="#fixed-vulnerability-to-malicious-code-in-sockgen-c" id="id26" name="id26">Fixed vulnerability to malicious code in sockGen.c</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#bug-fixes-for-version-2-1-3" id="id27" name="id27">Bug Fixes for Version 2.1.3</a><ul>
+<li><a class="reference" href="#directory-traversal-exploit" id="id28" name="id28">Directory Traversal Exploit</a></li>
+<li><a class="reference" href="#mime-type-for-external-javascript-files" id="id29" name="id29">MIME type for external JavaScript files</a></li>
+<li><a class="reference" href="#bug-in-if-modified-since-parsing" id="id30" name="id30">Bug in If-Modified-Since parsing</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#bug-fixes-for-version-2-1-2" id="id31" name="id31">Bug Fixes for Version 2.1.2</a><ul>
+<li><a class="reference" href="#ejscript-error-messages" id="id32" name="id32">Ejscript Error Messages</a></li>
+<li><a class="reference" href="#security-handler-response-codes" id="id33" name="id33">Security Handler Response Codes</a></li>
+<li><a class="reference" href="#security-handler-memory-leak" id="id34" name="id34">Security Handler Memory Leak</a></li>
+<li><a class="reference" href="#ejscript-write-corruption" id="id35" name="id35">Ejscript Write Corruption</a></li>
+<li><a class="reference" href="#error-in-dsnprintf-x-format" id="id36" name="id36">Error in dsnprintf(): &quot;%X&quot; format</a></li>
+<li><a class="reference" href="#bug018565-re-fixed" id="id37" name="id37">BUG018565 Re-fixed</a></li>
+<li><a class="reference" href="#potential-error-in-error" id="id38" name="id38">Potential Error in <tt class="literal"><span class="pre">error()</span></tt></a></li>
+<li><a class="reference" href="#added-support-for-customized-access-control" id="id39" name="id39">Added Support For Customized Access Control</a></li>
+<li><a class="reference" href="#memory-leak-in-websparserequest" id="id40" name="id40">Memory Leak in websParseRequest()</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#macintosh-os-x-support" id="id41" name="id41">Macintosh OS X Support</a></li>
+<li><a class="reference" href="#bug-fixes-for-version-2-1-1" id="id42" name="id42">Bug Fixes for Version 2.1.1</a><ul>
+<li><a class="reference" href="#intermittent-access-error-for-cgi-scripts-bug01937" id="id43" name="id43">Intermittent Access Error for CGI Scripts (BUG01937)</a></li>
+<li><a class="reference" href="#cpu-utilization-hangs-at-100-on-a-socket-disconnect-bug01865" id="id44" name="id44">CPU Utilization Hangs at 100% on a Socket Disconnect (BUG01865)</a></li>
+<li><a class="reference" href="#security-features-can-be-bypassed-by-adding-an-extra-slash-in-the-url-bug01518" id="id45" name="id45">Security Features can be Bypassed by Adding an Extra Slash in the URL (BUG01518)</a></li>
+<li><a class="reference" href="#call-to-webssetvar-causes-a-crash-bug01938" id="id46" name="id46">Call to <tt class="literal"><span class="pre">websSetVar</span></tt> causes a crash (BUG01938)</a></li>
+<li><a class="reference" href="#remove-stray-semicolon-in-emfdb-c-bug01820" id="id47" name="id47">Remove stray semicolon in <tt class="literal"><span class="pre">emfdb.c</span></tt> (BUG01820)</a></li>
+</ul>
+</li>
+<li><a class="reference" href="#novell-netware-support" id="id48" name="id48">Novell Netware Support</a></li>
+<li><a class="reference" href="#copyright-information" id="id49" name="id49">Copyright Information</a></li>
+</ul>
+</div>
+<div class="section" id="id1">
+<h1><a class="toc-backref" href="#id2" name="id1">GoAhead WebServer 2.1.8 Release Notes</a></h1>
+<dl>
+<dt>Release Date:</dt>
+<dd>02 Dec 2003</dd>
+</dl>
+<div class="section" id="problems-with-unicode-build">
+<h2><a class="toc-backref" href="#id3" name="problems-with-unicode-build">Problems with Unicode build</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>Missing T() caused trouble in Unicode build.</dd>
+<dt>Fix:</dt>
+<dd>Added T() macros.</dd>
+</dl>
+</div>
+<div class="section" id="modified-for-windows-ce-net">
+<h2><a class="toc-backref" href="#id4" name="modified-for-windows-ce-net">Modified for Windows CE .NET</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>Modified to work with Windows CE .NET and eMbedded Visual C++ 4.</dd>
+</dl>
+</div>
+<div class="section" id="bug-with-urls-like-asp">
+<h2><a class="toc-backref" href="#id5" name="bug-with-urls-like-asp">Bug with URLs like &quot;&lt;...&gt;.asp/&quot;</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>URLs ending in &quot;.asp/&quot;, &quot;.asp\&quot;, &quot;.as%70&quot; and other variants made the
+WebServer serve Ejscript source code.</dd>
+<dt>Fix: </dt>
+<dd>Added code to ignore these differences.</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="goahead-webserver-2-1-7-release-notes">
+<h1><a class="toc-backref" href="#id6" name="goahead-webserver-2-1-7-release-notes">GoAhead WebServer 2.1.7 Release Notes</a></h1>
+<dl>
+<dt>Release Date:</dt>
+<dd>01 Oct 2003</dd>
+</dl>
+<div class="section" id="added-support-for-the-mocana-ssl-toolkit">
+<h2><a class="toc-backref" href="#id7" name="added-support-for-the-mocana-ssl-toolkit">Added support for the Mocana SSL Toolkit</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>Added support for Mocana Corporation's embedded SSL server</dd>
+</dl>
+</div>
+<div class="section" id="changes-to-dbsearchstring">
+<h2><a class="toc-backref" href="#id8" name="changes-to-dbsearchstring">Changes to <tt class="literal"><span class="pre">dbSearchString()</span></tt></a></h2>
+<p>Description:</p>
+<blockquote>
+Pass <tt class="literal"><span class="pre">DB_CASE_INSENSITIVE</span></tt> as the &quot;flags&quot; argument to
+dbSearchString() to force a case-insensitive search.</blockquote>
+</div>
+<div class="section" id="use-memcpy-when-converting-to-from-unicode">
+<h2><a class="toc-backref" href="#id9" name="use-memcpy-when-converting-to-from-unicode">Use <tt class="literal"><span class="pre">memcpy()</span></tt> when converting to/from Unicode</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>The functions <tt class="literal"><span class="pre">uniToAsc()</span></tt> and <tt class="literal"><span class="pre">ascToUni()</span></tt> were using the relatively
+slow <tt class="literal"><span class="pre">strncpy()</span></tt> runtime library function.</dd>
+<dt>Fix:</dt>
+<dd>A new preprocessor macro <tt class="literal"><span class="pre">kUseMemcopy</span></tt> was added to <tt class="literal"><span class="pre">misc.c</span></tt>, and both
+functions were recoded to use <tt class="literal"><span class="pre">memcpy()</span></tt> when that macro is defined.
+Remove the definition to revert to the earlier code, using <tt class="literal"><span class="pre">strncpy()</span></tt>.</dd>
+</dl>
+</div>
+<div class="section" id="bug-when-using-utf-8-encoded-text-inside-asp-ejscript-blocks">
+<h2><a class="toc-backref" href="#id10" name="bug-when-using-utf-8-encoded-text-inside-asp-ejscript-blocks">Bug when using UTF-8 encoded text inside ASP/Ejscript blocks</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>When reading ASP code containing UTF-8 encoded source text, any characters
+encountered having a value &gt; 127 were treated as an error by the parser.</dd>
+<dt>Fix:</dt>
+<dd>The ring queue code in <tt class="literal"><span class="pre">ringq.c</span></tt> was modified so that it can correctly
+handle any character it encounters by casting to unsigned char before
+casting back to signed integer.</dd>
+</dl>
+</div>
+<div class="section" id="wrong-error-code-on-invalid-password">
+<h2><a class="toc-backref" href="#id11" name="wrong-error-code-on-invalid-password">Wrong error code on invalid password</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>The WebServer was sending back an inappropriate error code when it received
+an incorrect password.</dd>
+<dt>Fix:</dt>
+<dd>Changed error code returned from <tt class="literal"><span class="pre">405</span></tt> to <tt class="literal"><span class="pre">401</span></tt>. (Thanks to Jay
+Chalfant).</dd>
+</dl>
+</div>
+<div class="section" id="windows-ce-net">
+<h2><a class="toc-backref" href="#id12" name="windows-ce-net">Windows CE .NET</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>Removed &quot;compatibility functions&quot; that are directly supported in Windows
+CE .NET.</dd>
+</dl>
+</div>
+<div class="section" id="lynx-makefile">
+<h2><a class="toc-backref" href="#id13" name="lynx-makefile">LYNX <tt class="literal"><span class="pre">Makefile</span></tt></a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>Corrected problem in LYNX Makefile that prevented OpenSSL from being linked
+in correctly.</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="goahead-webserver-2-1-6-release-notes">
+<h1><a class="toc-backref" href="#id14" name="goahead-webserver-2-1-6-release-notes">GoAhead WebServer 2.1.6 Release Notes</a></h1>
+<dl>
+<dt>Release Date:</dt>
+<dd>25 Mar 2003</dd>
+</dl>
+<div class="section" id="null-pointer-crash-in-webssafeurl">
+<h2><a class="toc-backref" href="#id15" name="null-pointer-crash-in-webssafeurl"><tt class="literal"><span class="pre">NULL</span></tt> pointer crash in <tt class="literal"><span class="pre">websSafeUrl()</span></tt></a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>Passing a NULL pointer into the <tt class="literal"><span class="pre">websSafeUrl()</span></tt> function (as would happen
+when the server is processing an invalid URL) crashes the server.</dd>
+<dt>Fix:</dt>
+<dd>Code modified to check for NULL pointer before performing any string
+operations.</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="goahead-webserver-2-1-5-release-notes">
+<h1><a class="toc-backref" href="#id16" name="goahead-webserver-2-1-5-release-notes">GoAhead WebServer 2.1.5 Release Notes</a></h1>
+<dl>
+<dt>Release Date:</dt>
+<dd>19 Mar 2003</dd>
+</dl>
+<div class="section" id="bopen-failure-mode">
+<h2><a class="toc-backref" href="#id17" name="bopen-failure-mode"><tt class="literal"><span class="pre">bopen()</span></tt> failure mode</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>New failure behavior for <tt class="literal"><span class="pre">bopen()</span></tt> (see <tt class="literal"><span class="pre">balloc.c</span></tt>)</dd>
+<dt>Fix:</dt>
+<dd>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.</dd>
+</dl>
+</div>
+<div class="section" id="windows-95-98-me-aux-denial-of-service">
+<h2><a class="toc-backref" href="#id18" name="windows-95-98-me-aux-denial-of-service">Windows 95/98/ME <tt class="literal"><span class="pre">AUX</span></tt> Denial of Service</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd><p class="first">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.</p>
+<p>The names that cause the crash are:</p>
+<pre class="last literal-block">
+aux
+con
+nul
+clock$
+config$
+</pre>
+</dd>
+<dt>Fix:</dt>
+<dd><p class="first">Code added to the <tt class="literal"><span class="pre">websValidateUrl()</span></tt> 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.</p>
+<p>The code that checks for these prohibited pathname components checks for
+them in the form of either:</p>
+<pre class="literal-block">
+http://&lt;&lt;server address&gt;&gt;/aux
+</pre>
+<p>or:</p>
+<pre class="literal-block">
+http://&lt;&lt;server address&gt;&gt;/aux:
+</pre>
+<p class="last">where any non-alphanumeric character following one of the prohibited names
+will cause the URL request to be rejected.</p>
+</dd>
+</dl>
+</div>
+<div class="section" id="cross-site-scripting-exploit">
+<h2><a class="toc-backref" href="#id19" name="cross-site-scripting-exploit">404 Cross-site Scripting Exploit</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>Malicious users could request an invalid URL containing embedded JavaScript
+code that would be executed in the user's browser.</dd>
+<dt>Fix:</dt>
+<dd>404 (and other error messages) returned by the WebServer no longer display
+the invalid URL.</dd>
+</dl>
+</div>
+<div class="section" id="long-url-overflow-crash">
+<h2><a class="toc-backref" href="#id20" name="long-url-overflow-crash">Long URL Overflow Crash</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>URLs containing more than 64 levels of path components caused the WebServer
+to crash, entering a buffer overflow condition.</dd>
+<dt>Fix:</dt>
+<dd>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.</dd>
+</dl>
+</div>
+<div class="section" id="incorrect-error-code-in-security-c">
+<h2><a class="toc-backref" href="#id21" name="incorrect-error-code-in-security-c">Incorrect Error Code in <tt class="literal"><span class="pre">security.c</span></tt></a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>Pages assigned an access level of <tt class="literal"><span class="pre">AM_NONE</span></tt> should return an error code
+of 404 instead of 400 when an attempt it made to access them.</dd>
+<dt>Fix:</dt>
+<dd>Error code corrected.</dd>
+</dl>
+</div>
+<div class="section" id="pragma-code-for-risc-architectures">
+<h2><a class="toc-backref" href="#id22" name="pragma-code-for-risc-architectures">Pragma Code for RISC Architectures</a></h2>
+<dl>
+<dt>Description:</dt>
+<dd>A pragma was not set correctly when compiling for SPARC machines.</dd>
+<dt>Fix:</dt>
+<dd><p class="first">Code added to <tt class="literal"><span class="pre">uemf.h</span></tt>:</p>
+<pre class="last literal-block">
+#ifdef sparc
+# define __NO_PACK
+#endif /* sparc */
+</pre>
+</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="goahead-webserver-2-1-4-release-notes">
+<h1><a class="toc-backref" href="#id23" name="goahead-webserver-2-1-4-release-notes">GoAhead® WebServer 2.1.4 Release Notes</a></h1>
+<dl>
+<dt>Release Date:</dt>
+<dd>17 Oct 2002</dd>
+</dl>
+<div class="section" id="fixed-vulnerability-to-malicious-code-in-webs-c">
+<h2><a class="toc-backref" href="#id24" name="fixed-vulnerability-to-malicious-code-in-webs-c">Fixed vulnerability to malicious code in <tt class="literal"><span class="pre">webs.c</span></tt></a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>There were two vulnerabilities in <tt class="literal"><span class="pre">webs.c</span></tt>. Sending a POST message
+with a content-length but no data resulted in an attempt to perform
+a <tt class="literal"><span class="pre">strlen()</span></tt> 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).</dd>
+<dt>Fix:</dt>
+<dd>Code errors corrected.</dd>
+</dl>
+</div>
+<div class="section" id="https-bug-in-security-handler">
+<h2><a class="toc-backref" href="#id25" name="https-bug-in-security-handler"><a class="reference" href="https://">https://</a> bug in security handler</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>The <tt class="literal"><span class="pre">websSecurityHandler()</span></tt> function was performing a logical
+OR: (<tt class="literal"><span class="pre">flags</span> <span class="pre">|</span> <span class="pre">WEBS_SECURE</span></tt>) instead of a logical AND (<tt class="literal"><span class="pre">flags</span> <span class="pre">&amp;</span>
+<span class="pre">WEBS_SECURE</span></tt>), leading to incorrect results (thanks to &quot;Dhanwa T&quot;).</dd>
+<dt>Fix:</dt>
+<dd>Code errors corrected.</dd>
+</dl>
+</div>
+<div class="section" id="fixed-vulnerability-to-malicious-code-in-sockgen-c">
+<h2><a class="toc-backref" href="#id26" name="fixed-vulnerability-to-malicious-code-in-sockgen-c">Fixed vulnerability to malicious code in sockGen.c</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>At line 613 of <tt class="literal"><span class="pre">sockGen.c</span></tt>, the return value of the function
+<tt class="literal"><span class="pre">socketInputBuffered()</span></tt> was being discarded, leading to incorrect
+behavior in some cases. (Thanks to Richard Cullen)</dd>
+<dt>Fix:</dt>
+<dd>Code errors corrected.</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="bug-fixes-for-version-2-1-3">
+<h1><a class="toc-backref" href="#id27" name="bug-fixes-for-version-2-1-3">Bug Fixes for Version 2.1.3</a></h1>
+<div class="section" id="directory-traversal-exploit">
+<h2><a class="toc-backref" href="#id28" name="directory-traversal-exploit">Directory Traversal Exploit</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>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.</dd>
+<dt>Fix:</dt>
+<dd>Modified <tt class="literal"><span class="pre">default.c</span></tt> 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.</dd>
+</dl>
+</div>
+<div class="section" id="mime-type-for-external-javascript-files">
+<h2><a class="toc-backref" href="#id29" name="mime-type-for-external-javascript-files">MIME type for external JavaScript files</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>The WebServer would not serve external JavaScript files (<tt class="literal"><span class="pre">*.js</span></tt>)
+correctly.</dd>
+<dt>Fix:</dt>
+<dd>modified <tt class="literal"><span class="pre">mime.c</span></tt> to associate <tt class="literal"><span class="pre">.js</span></tt> files with the MIME
+type <tt class="literal"><span class="pre">application/x-javascript</span></tt>.</dd>
+</dl>
+</div>
+<div class="section" id="bug-in-if-modified-since-parsing">
+<h2><a class="toc-backref" href="#id30" name="bug-in-if-modified-since-parsing">Bug in If-Modified-Since parsing</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>There was an off-by-one error when converting from Gregorian date to
+time_t.</dd>
+<dt>Fix:</dt>
+<dd>modified function <tt class="literal"><span class="pre">dateToTimet</span></tt> in file <tt class="literal"><span class="pre">webs.c</span></tt>. The <tt class="literal"><span class="pre">month</span></tt>
+parameter is numbered from 0 (Jan == 0), but <tt class="literal"><span class="pre">FixedFromGregorian()</span></tt>
+takes months numbered from 1 (January == 1). We need to add 1 to
+the month before calling FixedFromGregorian.</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="bug-fixes-for-version-2-1-2">
+<h1><a class="toc-backref" href="#id31" name="bug-fixes-for-version-2-1-2">Bug Fixes for Version 2.1.2</a></h1>
+<div class="section" id="ejscript-error-messages">
+<h2><a class="toc-backref" href="#id32" name="ejscript-error-messages">Ejscript Error Messages</a></h2>
+<p>Summary:</p>
+<blockquote>
+Changed ejEval() function so it displays in the browser the error string that is
+reported by the Ejscript interpreter (old code discarded it completely).</blockquote>
+<dl>
+<dt>Fix:</dt>
+<dd>modified <tt class="literal"><span class="pre">websuemf.c</span></tt></dd>
+</dl>
+</div>
+<div class="section" id="security-handler-response-codes">
+<h2><a class="toc-backref" href="#id33" name="security-handler-response-codes">Security Handler Response Codes</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>Several places in the <tt class="literal"><span class="pre">websSecurityHandler()</span></tt> function were
+returning error code 200 (success) instead of the correct 400-level error code.</dd>
+<dt>Fix:</dt>
+<dd>Corrected error codes in <tt class="literal"><span class="pre">security.c</span></tt></dd>
+</dl>
+</div>
+<div class="section" id="security-handler-memory-leak">
+<h2><a class="toc-backref" href="#id34" name="security-handler-memory-leak">Security Handler Memory Leak</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>In <tt class="literal"><span class="pre">websSecurityHandler()</span></tt>, if the WebServer was compiled with
+<tt class="literal"><span class="pre">WEBS_SSL_SUPPORT</span></tt> enabled, there was a return path that leaked
+memory.</dd>
+<dt>Fix:</dt>
+<dd>Added call to <tt class="literal"><span class="pre">bfree(B_L,</span> <span class="pre">accessLimit);</span></tt></dd>
+</dl>
+</div>
+<div class="section" id="ejscript-write-corruption">
+<h2><a class="toc-backref" href="#id35" name="ejscript-write-corruption">Ejscript Write Corruption</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>Very long text strings passed to the Ejscript <tt class="literal"><span class="pre">write()</span></tt> function
+were being corrupted before being displayed.</dd>
+<dt>Fix:</dt>
+<dd>Commented out a <tt class="literal"><span class="pre">trace()</span></tt> statement in <tt class="literal"><span class="pre">ejGetLexToken()</span></tt> that appears to have been the
+culprit. The value of <tt class="literal"><span class="pre">ep-&gt;token</span></tt> was being corrupted somewhere
+in the trace.</dd>
+</dl>
+</div>
+<div class="section" id="error-in-dsnprintf-x-format">
+<h2><a class="toc-backref" href="#id36" name="error-in-dsnprintf-x-format">Error in dsnprintf(): &quot;%X&quot; format</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>The &quot;%X&quot; format specifier did not correctly cause the function to output
+uppercase hexadecimal digits.</dd>
+<dt>Fix:</dt>
+<dd>Added support for the &quot;%X&quot; format specifier.</dd>
+</dl>
+</div>
+<div class="section" id="bug018565-re-fixed">
+<h2><a class="toc-backref" href="#id37" name="bug018565-re-fixed">BUG018565 Re-fixed</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>See 2.1.1 release notes (below). This bug fix did not make it into the
+2.1.1 release.</dd>
+<dt>Fix:</dt>
+<dd>Corrected code in <tt class="literal"><span class="pre">sockGen.c</span></tt>.</dd>
+</dl>
+</div>
+<div class="section" id="potential-error-in-error">
+<h2><a class="toc-backref" href="#id38" name="potential-error-in-error">Potential Error in <tt class="literal"><span class="pre">error()</span></tt></a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>If <tt class="literal"><span class="pre">error()</span></tt> is called where the etype parameter is not E_LOG, E_ASSERT,
+or E_USER, the call to <tt class="literal"><span class="pre">bfreeSafe(B_L,</span> <span class="pre">buf)</span></tt> now at line 71 will fail,
+because 'buf' is randomly initialized.</dd>
+<dt>Fix:</dt>
+<dd>We format a message saying that this is an unknown message type,
+and in doing so give buf a valid value.</dd>
+</dl>
+</div>
+<div class="section" id="added-support-for-customized-access-control">
+<h2><a class="toc-backref" href="#id39" name="added-support-for-customized-access-control">Added Support For Customized Access Control</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>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.</dd>
+<dt>Fix:</dt>
+<dd>Added call to a user-supplied function `` bool_t dmfCanAccess(const
+char_t* userGroup, const char_t* group)``. This function is called
+from inside <tt class="literal"><span class="pre">umUserCanAccessURL()</span></tt> if the macro
+<tt class="literal"><span class="pre">qHierarchicalAccess</span></tt> is defined.</dd>
+</dl>
+</div>
+<div class="section" id="memory-leak-in-websparserequest">
+<h2><a class="toc-backref" href="#id40" name="memory-leak-in-websparserequest">Memory Leak in websParseRequest()</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>Memory was being leaked in the code now at line 907 of <tt class="literal"><span class="pre">webs.c</span></tt>.</dd>
+<dt>Fix:</dt>
+<dd>Added a call to <tt class="literal"><span class="pre">bfree()</span></tt>.</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="macintosh-os-x-support">
+<h1><a class="toc-backref" href="#id41" name="macintosh-os-x-support">Macintosh OS X Support</a></h1>
+<p>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:</p>
+<pre class="literal-block">
+cd MACOSX
+make
+</pre>
+<p>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 <tt class="literal"><span class="pre">main.c</span></tt>) to a different port (typically port 8080).</p>
+</div>
+<div class="section" id="bug-fixes-for-version-2-1-1">
+<h1><a class="toc-backref" href="#id42" name="bug-fixes-for-version-2-1-1">Bug Fixes for Version 2.1.1</a></h1>
+<div class="section" id="intermittent-access-error-for-cgi-scripts-bug01937">
+<h2><a class="toc-backref" href="#id43" name="intermittent-access-error-for-cgi-scripts-bug01937">Intermittent Access Error for CGI Scripts (BUG01937)</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>Pages were occasionally replaced with the message, <tt class="literal"><span class="pre">Access</span> <span class="pre">Error:</span> <span class="pre">Data</span>
+<span class="pre">follows</span> <span class="pre">when</span> <span class="pre">trying</span> <span class="pre">to</span> <span class="pre">obtain</span> <span class="pre">CGI</span> <span class="pre">generated</span> <span class="pre">no</span> <span class="pre">output</span></tt>.</dd>
+<dt>Fix:</dt>
+<dd>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 (<tt class="literal"><span class="pre">Access</span> <span class="pre">Error</span></tt> is reported).</dd>
+</dl>
+</div>
+<div class="section" id="cpu-utilization-hangs-at-100-on-a-socket-disconnect-bug01865">
+<h2><a class="toc-backref" href="#id44" name="cpu-utilization-hangs-at-100-on-a-socket-disconnect-bug01865">CPU Utilization Hangs at 100% on a Socket Disconnect (BUG01865)</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>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.</dd>
+<dt>Fix:</dt>
+<dd>Always close the socket prior to a disconnect.</dd>
+</dl>
+</div>
+<div class="section" id="security-features-can-be-bypassed-by-adding-an-extra-slash-in-the-url-bug01518">
+<h2><a class="toc-backref" href="#id45" name="security-features-can-be-bypassed-by-adding-an-extra-slash-in-the-url-bug01518">Security Features can be Bypassed by Adding an Extra Slash in the URL (BUG01518)</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>By adding an extra slash in a URL, password prompting is bypassed, allowing
+unrestricted access to secured pages.</dd>
+<dt>Fix:</dt>
+<dd>Remove multiple adjacent occurrences of slashes in the URL before passing
+it to the security handler.</dd>
+</dl>
+</div>
+<div class="section" id="call-to-webssetvar-causes-a-crash-bug01938">
+<h2><a class="toc-backref" href="#id46" name="call-to-webssetvar-causes-a-crash-bug01938">Call to <tt class="literal"><span class="pre">websSetVar</span></tt> causes a crash (BUG01938)</a></h2>
+<dl>
+<dt>Summary:</dt>
+<dd>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.</dd>
+<dt>Fix:</dt>
+<dd>Return immediately after closing the socket.</dd>
+</dl>
+</div>
+<div class="section" id="remove-stray-semicolon-in-emfdb-c-bug01820">
+<h2><a class="toc-backref" href="#id47" name="remove-stray-semicolon-in-emfdb-c-bug01820">Remove stray semicolon in <tt class="literal"><span class="pre">emfdb.c</span></tt> (BUG01820)</a></h2>
+<dl>
+<dt>Summary and Fix:</dt>
+<dd>A stray semicolon was removed from this file.</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="novell-netware-support">
+<h1><a class="toc-backref" href="#id48" name="novell-netware-support">Novell Netware Support</a></h1>
+<p>With the addition of Novell Netware in this 2.11 release, WebServer now supports these platforms:</p>
+<blockquote>
+<ul class="simple">
+<li>LINUX</li>
+<li>LynxOS</li>
+<li>Novell Netware &lt;/font&gt;4.2, 5.1</li>
+<li>Mac OS X</li>
+<li>UNIX - SCO OpenServer 3.2V5.0.0</li>
+<li>VxWorks 5.3.1</li>
+<li>Windows 2000</li>
+<li>Windows 98</li>
+<li>Windows 95</li>
+<li>Windows NT</li>
+<li>Windows CE</li>
+</ul>
+</blockquote>
+<p>To make a Novell Netware target file (<tt class="literal"><span class="pre">webs.nlm</span></tt>):</p>
+<pre class="literal-block">
+cd NW
+wmake webs.nlm
+load &lt;path&gt;; \webs.nlm
+webs
+</pre>
+<p><strong>Note:</strong> 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 <tt class="literal"><span class="pre">P</span></tt>, 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.</p>
+</div>
+<div class="section" id="copyright-information">
+<h1><a class="toc-backref" href="#id49" name="copyright-information">Copyright Information</a></h1>
+<dl>
+<dt>Trademarks</dt>
+<dd>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.</dd>
+<dt>Copyright </dt>
+<dd>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.</dd>
+<dt>Copy Restrictions </dt>
+<dd>The software described in this document may be used and copied only
+in accordance with the terms of the accompanying license agreement.</dd>
+<dt>GoAhead Software, Inc.</dt>
+<dd>10900 NE 8th Street Suite 750 Bellevue, WA 98004 +1 ( 425) 453-1900
+www.goahead.com <a class="reference" href="mailto:info&#64;goahead.com">info&#64;goahead.com</a></dd>
+</dl>
+<p>1-53-03</p>
+</div>
+</div>
+</body>
+</html>
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 <http://docutils.sf.net>. 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://<<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.
+
+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 <stdlib.h>
+
+#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 <string.h>
+#include <stdlib.h>
+
+#ifdef UEMF
+ #include "uemf.h"
+#else
+ #include <socket.h>
+ #include <types.h>
+ #include <unistd.h>
+ #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 <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#endif
+
+#ifdef UEMF
+ #include "uemf.h"
+#else
+ #include <socket.h>
+ #include <types.h>
+ #include <unistd.h>
+ #include "emfInternal.h"
+#endif
+
+#ifdef VXWORKS
+ #include <hostLib.h>
+#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..aafffbd4f3
--- /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 <direct.h>
+ #include <io.h>
+ #include <sys/stat.h>
+ #include <limits.h>
+ #include <tchar.h>
+ #include <windows.h>
+ #include <winnls.h>
+ #include <time.h>
+ #include <sys/types.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* WIN */
+
+#ifdef CE
+ /*#include <errno.h>*/
+ #include <limits.h>
+ #include <tchar.h>
+ #include <windows.h>
+ #include <winsock.h>
+ #include <winnls.h>
+ #include "CE/wincompat.h"
+ #include <winsock.h>
+#endif /* CE */
+
+#ifdef NW
+ #include <direct.h>
+ #include <io.h>
+ #include <sys/stat.h>
+ #include <time.h>
+ #include <sys/types.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <fcntl.h>
+ #include <errno.h>
+ #include <niterror.h>
+ #define EINTR EINUSE
+ #define WEBS 1
+ #include <limits.h>
+ #include <netdb.h>
+ #include <process.h>
+ #include <tiuser.h>
+ #include <sys/time.h>
+ #include <arpa/inet.h>
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/filio.h>
+ #include <netinet/in.h>
+#endif /* NW */
+
+#ifdef SCOV5
+ #include <sys/types.h>
+ #include <stdio.h>
+ #include "sys/socket.h"
+ #include "sys/select.h"
+ #include "netinet/in.h"
+ #include "arpa/inet.h"
+ #include "netdb.h"
+#endif /* SCOV5 */
+
+#ifdef UNIX
+ #include <stdio.h>
+#endif /* UNIX */
+
+#ifdef LINUX
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/param.h>
+ #include <limits.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <sys/socket.h>
+ #include <sys/select.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <time.h>
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* LINUX */
+
+#ifdef LYNX
+ #include <limits.h>
+ #include <stdarg.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <time.h>
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* LYNX */
+
+#ifdef MACOSX
+ #include <sys/stat.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* MACOSX */
+
+#ifdef UW
+ #include <stdio.h>
+#endif /* UW */
+
+#ifdef VXWORKS
+ #include <vxWorks.h>
+ #include <sockLib.h>
+ #include <selectLib.h>
+ #include <inetLib.h>
+ #include <ioLib.h>
+ #include <stdio.h>
+ #include <stat.h>
+ #include <time.h>
+ #include <usrLib.h>
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* VXWORKS */
+
+#ifdef sparc
+# define __NO_PACK
+#endif /* sparc */
+
+#ifdef SOLARIS
+ #include <sys/types.h>
+ #include <limits.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <socket.h>
+ #include <sys/select.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <time.h>
+ #include <fcntl.h>
+ #include <errno.h>
+#endif /* SOLARIS */
+
+#ifdef QNX4
+ #include <sys/types.h>
+ #include <stdio.h>
+ #include <sys/socket.h>
+ #include <sys/select.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <sys/uio.h>
+ #include <sys/wait.h>
+#endif /* QNX4 */
+
+#ifdef ECOS
+ #include <limits.h>
+ #include <cyg/infra/cyg_type.h>
+ #include <cyg/kernel/kapi.h>
+ #include <time.h>
+ #include <network.h>
+ #include <errno.h>
+#endif /* ECOS */
+
+/********************************** Includes **********************************/
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <string.h>
+
+#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 *spidgoahead_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..4ae0aa41f8
--- /dev/null
+++ b/cleopatre/application/spidgoahead/um.c
@@ -0,0 +1,1435 @@
+/*
+ * 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")
+
+/*
+ * 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..8052bed6d7
--- /dev/null
+++ b/cleopatre/application/spidgoahead/um.h
@@ -0,0 +1,186 @@
+/*
+ * 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
+
+#define UM_TXT_FILENAME T("um_httpd.conf")
+/*
+ * 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/um_httpd.conf b/cleopatre/application/spidgoahead/um_httpd.conf
new file mode 100644
index 0000000000..f35bf56c17
--- /dev/null
+++ b/cleopatre/application/spidgoahead/um_httpd.conf
@@ -0,0 +1,78 @@
+TABLE=users
+ROW=0
+name=admin
+password=KZF
+group=admin
+prot=0
+disable=0
+ROW=1
+name=spidcom
+password=Y^K#N
+group=user
+prot=0
+disable=0
+ROW=2
+name=root
+password=XX
+group=admin
+prot=0
+disable=0
+TABLE=groups
+ROW=0
+name=admin
+priv=4
+method=2
+prot=0
+disable=0
+ROW=1
+name=user
+priv=1
+method=2
+prot=0
+disable=0
+TABLE=access
+ROW=0
+name=/adduser.asp
+method=2
+secure=0
+group=admin
+ROW=1
+name=/addgroup.asp
+method=2
+secure=0
+group=admin
+ROW=2
+name=/addlimit.asp
+method=2
+secure=0
+group=admin
+ROW=3
+name=/dellimit.asp
+method=2
+secure=0
+group=admin
+ROW=4
+name=/deluser.asp
+method=2
+secure=0
+group=admin
+ROW=5
+name=/delgroup.asp
+method=2
+secure=0
+group=admin
+ROW=6
+name=/loadcfg.asp
+method=2
+secure=0
+group=admin
+ROW=7
+name=/savecfg.asp
+method=2
+secure=0
+group=admin
+ROW=8
+name=/
+method=2
+secure=0
+group=
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("<NONE>")
+#define MSG_START T("<body><h2>")
+#define MSG_END T("</h2></body>")
+
+/**************************** 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("<body>"));
+
+ if (gstricmp(ok, T("ok")) != 0) {
+ websWrite(wp, T("Display User Cancelled"));
+ } else if (umUserExists(userid) == FALSE) {
+ websWrite(wp, T("ERROR: User <b>%s</b> not found.\n"), userid);
+ } else {
+ websWrite(wp, T("<h2>User ID: <b>%s</b></h2>\n"), userid);
+ temp = umGetUserGroup(userid);
+ websWrite(wp, T("<h3>User Group: <b>%s</b></h3>\n"), temp);
+ enabled = umGetUserEnabled(userid);
+ websWrite(wp, T("<h3>Enabled: <b>%d</b></h3>\n"), enabled);
+ }
+
+ websWrite(wp, T("</body>\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("<SELECT NAME=\"user\" SIZE=\"3\" TITLE=\"Select a User\">"));
+ row = 0;
+ userid = umGetFirstUser();
+ nBytesSent = 0;
+
+ while (userid && (nBytes > 0)) {
+ nBytes = websWrite(wp, T("<OPTION VALUE=\"%s\">%s\n"),
+ userid, userid);
+ userid = umGetNextUser(userid);
+ nBytesSent += nBytes;
+ }
+
+ nBytesSent += websWrite(wp, T("</SELECT>"));
+
+ 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 <SELECT> var, and must be parsed.
+ * Values for these variables are space delimited.
+ */
+ priv = 0;
+ for (pChar = privilege; *pChar; pChar++) {
+ if (*pChar == ' ') {
+ *pChar = '\0';
+ priv |= gatoi(privilege);
+ *pChar = ' ';
+ privilege = pChar + 1;
+ }
+ }
+ priv |= gatoi(privilege);
+ } else {
+ priv = 0;
+ }
+
+ if (method && *method) {
+ am = (accessMeth_t) gatoi(method);
+ } else {
+ am = AM_FULL;
+ }
+
+ if (enabled && *enabled && (gstrcmp(enabled, T("on")) == 0)) {
+ bDisable = FALSE;
+ } else {
+ bDisable = TRUE;
+ }
+
+ nCheck = umAddGroup(group, priv, am, 0, bDisable);
+ if (nCheck != 0) {
+ websWrite(wp, T("Unable to add group, \"%s\", code: %d "),
+ group, nCheck);
+ } else {
+ websWrite(wp, T("Group, \"%s\" was successfully added."),
+ group);
+ }
+ }
+
+ websMsgEnd(wp);
+ websFooter(wp);
+ websDone(wp, 200);
+}
+
+/******************************************************************************/
+/*
+ * Delete a group
+ */
+
+static void formDeleteGroup(webs_t wp, char_t *path, char_t *query)
+{
+ char_t *group, *ok;
+
+ a_assert(wp);
+
+ group = websGetVar(wp, T("group"), T(""));
+ ok = websGetVar(wp, T("ok"), T(""));
+
+ websHeader(wp);
+ websMsgStart(wp);
+
+ if (gstricmp(ok, T("ok")) != 0) {
+ websWrite(wp, T("Delete Group Cancelled."));
+ } else if ((group == NULL) || (*group == '\0')) {
+ websWrite(wp, T("ERROR: No group was selected."));
+ } else if (umGetGroupProtected(group)) {
+ websWrite(wp, T("ERROR: Group, \"%s\" is delete-protected."), group);
+ } else if (umGetGroupInUse(group)) {
+ websWrite(wp, T("ERROR: Group, \"%s\" is being used."), group);
+ } else if (umDeleteGroup(group) != 0) {
+ websWrite(wp, T("ERROR: Unable to delete group, \"%s\" "), group);
+ } else {
+ websWrite(wp, T("Group, \"%s\" was successfully deleted."), group);
+ }
+
+ websMsgEnd(wp);
+ websFooter(wp);
+ websDone(wp, 200);
+}
+
+/******************************************************************************/
+/*
+ * Generate HTML to create a list box containing the groups
+ */
+
+static int aspGenerateGroupList(int eid, webs_t wp, int argc, char_t **argv)
+{
+ char_t *group;
+ int row, nBytesSent, nBytes;
+
+ a_assert(wp);
+
+ row = 0;
+ nBytesSent = 0;
+ nBytes = websWrite(wp,
+ T("<SELECT NAME=\"group\" SIZE=\"3\" TITLE=\"Select a Group\">"));
+/*
+ * Add a special "<NONE>" element to allow de-selection
+ */
+ nBytes = websWrite(wp, T("<OPTION VALUE=\"\">[NONE]\n"));
+
+ group = umGetFirstGroup();
+ while (group && (nBytes > 0)) {
+ nBytes = websWrite(wp, T("<OPTION VALUE=\"%s\">%s\n"), group, group);
+ group = umGetNextGroup(group);
+ nBytesSent += nBytes;
+ }
+
+ nBytesSent += websWrite(wp, T("</SELECT>"));
+
+ return nBytesSent;
+}
+
+/******************************************************************************/
+/*
+ * Add an access limit
+ */
+
+static void formAddAccessLimit(webs_t wp, char_t *path, char_t *query)
+{
+ char_t *url, *method, *group, *secure, *ok;
+ int nCheck;
+ accessMeth_t am;
+ short nSecure;
+
+ a_assert(wp);
+
+ url = websGetVar(wp, T("url"), T(""));
+ group = websGetVar(wp, T("group"), T(""));
+ method = websGetVar(wp, T("method"), T(""));
+ secure = websGetVar(wp, T("secure"), T(""));
+ ok = websGetVar(wp, T("ok"), T(""));
+
+ websHeader(wp);
+ websMsgStart(wp);
+
+ if (gstricmp(ok, T("ok")) != 0) {
+ websWrite(wp, T("Add Access Limit Cancelled."));
+ } else if ((url == NULL) || (*url == 0)) {
+ websWrite(wp, T("ERROR: No URL was entered."));
+ } else if (umAccessLimitExists(url)) {
+ websWrite(wp, T("ERROR: An Access Limit for [%s] already exists."),
+ url);
+ } else {
+ if (method && *method) {
+ am = (accessMeth_t) gatoi(method);
+ } else {
+ am = AM_FULL;
+ }
+
+ if (secure && *secure) {
+ nSecure = (short) gatoi(secure);
+ } else {
+ nSecure = 0;
+ }
+
+ nCheck = umAddAccessLimit(url, am, nSecure, group);
+ if (nCheck != 0) {
+ websWrite(wp, T("Unable to add Access Limit for [%s]"), url);
+ } else {
+ websWrite(wp, T("Access limit for [%s], was successfully added."),
+ url);
+ }
+ }
+
+ websMsgEnd(wp);
+ websFooter(wp);
+ websDone(wp, 200);
+}
+
+/******************************************************************************/
+/*
+ * Delete an Access Limit
+ */
+
+static void formDeleteAccessLimit(webs_t wp, char_t *path, char_t *query)
+{
+ char_t *url, *ok;
+
+ a_assert(wp);
+
+ url = websGetVar(wp, T("url"), T(""));
+ ok = websGetVar(wp, T("ok"), T(""));
+
+ websHeader(wp);
+ websMsgStart(wp);
+
+ if (gstricmp(ok, T("ok")) != 0) {
+ websWrite(wp, T("Delete Access Limit Cancelled"));
+ } else if (umDeleteAccessLimit(url) != 0) {
+ websWrite(wp, T("ERROR: Unable to delete Access Limit for [%s]"),
+ url);
+ } else {
+ websWrite(wp, T("Access Limit for [%s], was successfully deleted."),
+ url);
+ }
+
+ websMsgEnd(wp);
+ websFooter(wp);
+ websDone(wp, 200);
+}
+
+/******************************************************************************/
+/*
+ * Generate HTML to create a list box containing the access limits
+ */
+
+static int aspGenerateAccessLimitList(int eid, webs_t wp,
+ int argc, char_t **argv)
+{
+ char_t *url;
+ int row, nBytesSent, nBytes;
+
+ a_assert(wp);
+
+ row = nBytesSent = 0;
+ url = umGetFirstAccessLimit();
+ nBytes = websWrite(wp,
+ T("<SELECT NAME=\"url\" SIZE=\"3\" TITLE=\"Select a URL\">"));
+
+ while (url && (nBytes > 0)) {
+ nBytes = websWrite(wp, T("<OPTION VALUE=\"%s\">%s\n"), url, url);
+ url = umGetNextAccessLimit(url);
+ nBytesSent += nBytes;
+ }
+
+ nBytesSent += websWrite(wp, T("</SELECT>"));
+
+ return nBytesSent;
+}
+
+/******************************************************************************/
+/*
+ * Generate HTML to create a list box containing the access methods
+ */
+
+static int aspGenerateAccessMethodList(int eid, webs_t wp,
+ int argc, char_t **argv)
+{
+ int nBytes;
+
+ a_assert(wp);
+
+ nBytes = websWrite(wp,
+ T("<SELECT NAME=\"method\" SIZE=\"3\" TITLE=\"Select a Method\">"));
+ nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\">FULL ACCESS\n"),
+ AM_FULL);
+ nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\">BASIC ACCESS\n"),
+ AM_BASIC);
+ nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\" SELECTED>DIGEST ACCESS\n"),
+ AM_DIGEST);
+ nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\">NO ACCESS\n"),
+ AM_NONE);
+ nBytes += websWrite(wp, T("</SELECT>"));
+
+ return nBytes;
+}
+/******************************************************************************/
+/*
+ * Generate HTML to create a list box containing privileges
+ */
+
+static int aspGeneratePrivilegeList(int eid, webs_t wp,
+ int argc, char_t **argv)
+{
+ int nBytes;
+
+ a_assert(wp);
+
+ nBytes = websWrite(wp, T("<SELECT NAME=\"privilege\" SIZE=\"3\" "));
+ nBytes += websWrite(wp, T("MULTIPLE TITLE=\"Choose Privileges\">"));
+ nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\">READ\n"), PRIV_READ);
+ nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\">EXECUTE\n"), PRIV_WRITE);
+ nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\">ADMINISTRATE\n"),
+ PRIV_ADMIN);
+ nBytes += websWrite(wp, T("</SELECT>"));
+
+ return nBytes;
+}
+
+/******************************************************************************/
+/*
+ * Save the user management configuration to a file
+ */
+
+static void formSaveUserManagement(webs_t wp, char_t *path, char_t *query)
+{
+ char_t *ok;
+
+ a_assert(wp);
+
+ ok = websGetVar(wp, T("ok"), T(""));
+
+ websHeader(wp);
+ websMsgStart(wp);
+
+ if (gstricmp(ok, T("ok")) != 0) {
+ websWrite(wp, T("Save Cancelled."));
+ } else if (umCommit(NULL) != 0) {
+ websWrite(wp, T("ERROR: Unable to save user configuration."));
+ } else {
+ websWrite(wp, T("User configuration was saved successfully."));
+ }
+
+ websMsgEnd(wp);
+ websFooter(wp);
+ websDone(wp, 200);
+}
+
+/******************************************************************************/
+/*
+ * Load the user management configuration from a file
+ */
+
+static void formLoadUserManagement(webs_t wp, char_t *path, char_t *query)
+{
+ char_t *ok;
+
+ a_assert(wp);
+
+ ok = websGetVar(wp, T("ok"), T(""));
+
+ websHeader(wp);
+ websMsgStart(wp);
+
+ if (gstricmp(ok, T("ok")) != 0) {
+ websWrite(wp, T("Load Cancelled."));
+ } else if (umRestore(NULL) != 0) {
+ websWrite(wp, T("ERROR: Unable to load user configuration."));
+ } else {
+ websWrite(wp, T("User configuration was re-loaded successfully."));
+ }
+
+ websMsgEnd(wp);
+ websFooter(wp);
+ websDone(wp, 200);
+}
+
+/******************************************************************************/
+/*
+ * Message start and end convenience functions
+ */
+
+static void websMsgStart(webs_t wp)
+{
+ websWrite(wp, MSG_START);
+}
+
+static void websMsgEnd(webs_t wp)
+{
+ websWrite(wp, MSG_END);
+}
+
+/******************************************************************************/
+
diff --git a/cleopatre/application/spidgoahead/url.c b/cleopatre/application/spidgoahead/url.c
new file mode 100644
index 0000000000..557758bedf
--- /dev/null
+++ b/cleopatre/application/spidgoahead/url.c
@@ -0,0 +1,223 @@
+/*
+ * url.c -- Parse URLs
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ *
+ * $Id: url.c,v 1.4 2003/11/25 21:48:13 hwolff Exp $
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module parses URLs into their components.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+
+/********************************* Statics ************************************/
+/*
+ * htmExt is declared in this way to avoid a Linux and Solaris segmentation
+ * fault when a constant string is passed to strlower which could change its
+ * argument.
+ */
+
+char_t htmExt[] = T(".htm");
+
+
+/*********************************** Code *************************************/
+/*
+ * Return the mime type for the given URL given a URL.
+ * The caller supplies the buffer to hold the result.
+ * charCnt is the number of characters the buffer will hold, ascii or UNICODE.
+ */
+
+char_t *websUrlType(char_t *url, char_t *buf, int charCnt)
+{
+ sym_t *sp;
+ char_t *ext, *parsebuf;
+
+ a_assert(url && *url);
+ a_assert(buf && charCnt > 0);
+
+ if (url == NULL || *url == '\0') {
+ gstrcpy(buf, T("text/plain"));
+ return buf;
+ }
+ if (websUrlParse(url, &parsebuf, NULL, NULL, NULL, NULL, NULL,
+ NULL, &ext) < 0) {
+ gstrcpy(buf, T("text/plain"));
+ return buf;
+ }
+ strlower(ext);
+
+/*
+ * Lookup the mime type symbol table to find the relevant content type
+ */
+ if ((sp = symLookup(websMime, ext)) != NULL) {
+ gstrncpy(buf, sp->content.value.string, charCnt);
+ } else {
+ gstrcpy(buf, T("text/plain"));
+ }
+ bfree(B_L, parsebuf);
+ return buf;
+}
+
+/******************************************************************************/
+/*
+ * Parse the URL. A buffer is allocated to store the parsed URL in *pbuf.
+ * This must be freed by the caller. NOTE: tag is not yet fully supported.
+ */
+
+int websUrlParse(char_t *url, char_t **pbuf, char_t **phost, char_t **ppath,
+ char_t **pport, char_t **pquery, char_t **pproto, char_t **ptag,
+ char_t **pext)
+{
+ char_t *tok, *cp, *host, *path, *port, *proto, *tag, *query, *ext;
+ char_t *hostbuf, *portbuf, *buf;
+ int c, len, ulen;
+
+ a_assert(url);
+ a_assert(pbuf);
+
+ ulen = gstrlen(url);
+/*
+ * We allocate enough to store separate hostname and port number fields.
+ * As there are 3 strings in the one buffer, we need room for 3 null chars.
+ * We allocate MAX_PORT_LEN char_t's for the port number.
+ */
+ len = ulen * 2 + MAX_PORT_LEN + 3;
+ if ((buf = balloc(B_L, len * sizeof(char_t))) == NULL) {
+ return -1;
+ }
+ portbuf = &buf[len - MAX_PORT_LEN - 1];
+ hostbuf = &buf[ulen+1];
+ /*
+ Handle any URL encoding.
+ Otherwise a URL ending in ".as%70", for example, causes trouble.
+ */
+ websDecodeUrl(buf, url, ulen);
+
+ url = buf;
+
+/*
+ * Convert the current listen port to a string. We use this if the URL has
+ * no explicit port setting
+ */
+ stritoa(websGetPort(), portbuf, MAX_PORT_LEN);
+ port = portbuf;
+ path = T("/");
+ proto = T("http");
+ host = T("localhost");
+ query = T("");
+ ext = htmExt;
+ tag = T("");
+
+ if (gstrncmp(url, T("http://"), 7) == 0) {
+ tok = &url[7];
+ tok[-3] = '\0';
+ proto = url;
+ host = tok;
+ for (cp = tok; *cp; cp++) {
+ if (*cp == '/') {
+ break;
+ }
+ if (*cp == ':') {
+ *cp++ = '\0';
+ port = cp;
+ tok = cp;
+ }
+ }
+ if ((cp = gstrchr(tok, '/')) != NULL) {
+/*
+ * If a full URL is supplied, we need to copy the host and port
+ * portions into static buffers.
+ */
+ c = *cp;
+ *cp = '\0';
+ gstrncpy(hostbuf, host, ulen);
+ gstrncpy(portbuf, port, MAX_PORT_LEN);
+ *cp = c;
+ host = hostbuf;
+ port = portbuf;
+ path = cp;
+ tok = cp;
+ }
+
+ } else {
+ path = url;
+ tok = url;
+ }
+
+/*
+ * Parse the query string
+ */
+ if ((cp = gstrchr(tok, '?')) != NULL) {
+ *cp++ = '\0';
+ query = cp;
+ path = tok;
+ tok = query;
+ }
+
+/*
+ * Parse the fragment identifier
+ */
+ if ((cp = gstrchr(tok, '#')) != NULL) {
+ *cp++ = '\0';
+ if (*query == 0) {
+ path = tok;
+ }
+ }
+
+/*
+ * Only do the following if asked for the extension
+ */
+ if (pext) {
+ /*
+ Later the path will be cleaned up for trailing slashes and so on.
+ To be ready, we need to clean up here, much as in websValidateUrl.
+ Otherwise a URL ending in "asp/" or "asP" sends Ejscript source
+ to the browser.
+ */
+ if ((cp = gstrrchr(path, '.')) != NULL) {
+ const char_t* garbage = T("/\\");
+ int length = gstrcspn(cp, garbage);
+ int garbageLength = gstrspn(cp + length, garbage);
+ int ok = (length + garbageLength == (int) gstrlen(cp));
+
+ if (ok) {
+ cp[length] = '\0';
+#ifdef WIN32
+ strlower(cp);
+#endif
+ ext = cp;
+ }
+ }
+ }
+
+/*
+ * Pass back the fields requested (if not NULL)
+ */
+ if (phost)
+ *phost = host;
+ if (ppath)
+ *ppath = path;
+ if (pport)
+ *pport = port;
+ if (pproto)
+ *pproto = proto;
+ if (pquery)
+ *pquery = query;
+ if (ptag)
+ *ptag = tag;
+ if (pext)
+ *pext = ext;
+ *pbuf = buf;
+ return 0;
+}
+
+/******************************************************************************/
+
diff --git a/cleopatre/application/spidgoahead/value.c b/cleopatre/application/spidgoahead/value.c
new file mode 100644
index 0000000000..8bad4e71e5
--- /dev/null
+++ b/cleopatre/application/spidgoahead/value.c
@@ -0,0 +1,1214 @@
+/*
+ * value.c -- Generic type (holds all types)
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
+ *
+ * $Id: value.c,v 1.3 2002/10/24 14:44:50 bporter Exp $
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module provides a generic type that can hold all possible types.
+ * It is designed to provide maximum effeciency.
+ */
+
+/********************************* Includes ***********************************/
+
+#ifdef UEMF
+ #include "uemf.h"
+#else
+ #include "basic/basicInternal.h"
+#endif
+
+/*********************************** Locals ***********************************/
+#ifndef UEMF
+static value_t value_null; /* All zeros */
+
+/***************************** Forward Declarations ***************************/
+
+static void coerce_types(value_t* v1, value_t* v2);
+static int value_to_integer(value_t* vp);
+#endif /*!UEMF*/
+/*********************************** Code *************************************/
+/*
+ * Initialize a integer value.
+ */
+
+value_t valueInteger(long value)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+ v.type = integer;
+ v.value.integer = value;
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Initialize a string value.
+ */
+
+value_t valueString(char_t* value, int flags)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+ v.type = string;
+ if (flags & VALUE_ALLOCATE) {
+ v.allocated = 1;
+ v.value.string = gstrdup(B_L, value);
+ } else {
+ v.allocated = 0;
+ v.value.string = value;
+ }
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Free any storage allocated for a value.
+ */
+
+void valueFree(value_t* v)
+{
+ if (v->valid && v->allocated && v->type == string &&
+ v->value.string != NULL) {
+ bfree(B_L, v->value.string);
+ }
+#ifndef UEMF
+ if (v->valid && v->type == symbol && v->value.symbol.data != NULL &&
+ v->value.symbol.freeCb !=NULL) {
+ v->value.symbol.freeCb(v->value.symbol.data);
+ }
+#endif
+ v->type = undefined;
+ v->valid = 0;
+ v->allocated = 0;
+}
+
+#ifndef UEMF
+
+/******************************************************************************/
+/*
+ * Initialize an invalid value.
+ */
+
+value_t valueInvalid()
+{
+ value_t v;
+ v.valid = 0;
+ v.type = undefined;
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Initialize a flag value.
+ */
+
+value_t valueBool(int value)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.type = flag;
+ v.valid = 1;
+ v.value.flag = (char) value;
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Initialize a byteint value.
+ */
+
+value_t valueByteint(char value)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+ v.type = byteint;
+ v.value.byteint = value;
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Initialize a shortint value.
+ */
+
+value_t valueShortint(short value)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+ v.type = shortint;
+ v.value.shortint = value;
+ return v;
+}
+
+#ifdef FLOATING_POINT_SUPPORT
+/******************************************************************************/
+/*
+ * Initialize a floating value.
+ */
+
+value_t valueFloating(double value)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+ v.type = floating;
+ v.value.floating = value;
+ return v;
+}
+#endif /* FLOATING_POINT_SUPPORT */
+
+/******************************************************************************/
+/*
+ * Initialize a big value.
+ */
+
+value_t valueBig(long high_word, long low_word)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+ v.type = big;
+ v.value.big[BLOW] = low_word;
+ v.value.big[BHIGH] = high_word;
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Initialize a hex value.
+ */
+
+value_t valueHex(int value)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+ v.type = hex;
+ v.value.integer = value;
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Initialize a octal value.
+ */
+
+value_t valueOctal(int value)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+ v.type = octal;
+ v.value.integer = value;
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Initialize a percent value.
+ */
+
+value_t valuePercent(int value)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+ v.type = percent;
+ v.value.percent = (char) value;
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Initialize an byte array. Note: no allocation, just store the ptr
+ */
+
+value_t valueBytes(char* value, int flags)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+ v.type = bytes;
+ if (flags & VALUE_ALLOCATE) {
+ v.allocated = 1;
+ v.value.bytes = bstrdupA(B_L, value);
+ } else {
+ v.allocated = 0;
+ v.value.bytes = value;
+ }
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Initialize a symbol value.
+ * Value parameter can hold a pointer to any type of value
+ * Free parameter can be NULL, or a function pointer to a function that will
+ * free the value
+ */
+
+value_t valueSymbol(void *value, freeCallback freeCb)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+ v.type = symbol;
+ v.value.symbol.data = value;
+ v.value.symbol.freeCb = freeCb;
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Initialize an error message value.
+ */
+
+value_t valueErrmsg(char_t* value)
+{
+ value_t v;
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+ v.type = errmsg;
+ v.value.errmsg = value;
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Copy a value. If the type is 'string' then allocate another string.
+ * Note: we allow the copy of a null value.
+ */
+
+value_t valueCopy(value_t v2)
+{
+ value_t v1;
+
+ v1 = v2;
+ if (v2.valid && v2.type == string && v2.value.string != NULL) {
+ v1.value.string = gstrdup(B_L, v2.value.string);
+ v1.allocated = 1;
+ }
+ return v1;
+}
+
+
+/******************************************************************************/
+/*
+ * Add a value.
+ */
+
+value_t valueAdd(value_t v1, value_t v2)
+{
+ value_t v;
+
+ a_assert(v1.valid);
+ a_assert(v2.valid);
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+
+ if (v1.type != v2.type)
+ coerce_types(&v1, &v2);
+
+ switch (v1.type) {
+ default:
+ case string:
+ case bytes:
+ a_assert(0);
+ break;
+
+#ifdef FLOATING_POINT_SUPPORT
+ case floating:
+ v1.value.floating += v2.value.floating;
+ return v1;
+#endif
+
+ case flag:
+ v1.value.bool |= v2.value.flag;
+ return v1;
+
+ case byteint:
+ case percent:
+ v1.value.byteint += v2.value.byteint;
+ return v1;
+
+ case shortint:
+ v1.value.shortint += v2.value.shortint;
+ return v1;
+
+ case hex:
+ case integer:
+ case octal:
+ v1.value.integer += v2.value.integer;
+ return v1;
+
+ case big:
+ v.type = big;
+ badd(v.value.big, v1.value.big, v2.value.big);
+ return v;
+ }
+
+ return v1;
+}
+
+
+/******************************************************************************/
+/*
+ * Subtract a value.
+ */
+
+value_t valueSub(value_t v1, value_t v2)
+{
+ value_t v;
+
+ a_assert(v1.valid);
+ a_assert(v2.valid);
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+
+ if (v1.type != v2.type)
+ coerce_types(&v1, &v2);
+ switch (v1.type) {
+ default:
+ a_assert(0);
+ break;
+
+#ifdef FLOATING_POINT_SUPPORT
+ case floating:
+ v1.value.floating -= v2.value.floating;
+ return v1;
+#endif
+
+ case flag:
+ v1.value.flag &= v2.value.flag;
+ return v1;
+
+ case byteint:
+ case percent:
+ v1.value.byteint -= v2.value.byteint;
+ return v1;
+
+ case shortint:
+ v1.value.shortint -= v2.value.shortint;
+ return v1;
+
+ case hex:
+ case integer:
+ case octal:
+ v1.value.integer -= v2.value.integer;
+ return v1;
+
+ case big:
+ v.type = big;
+ bsub(v.value.big, v1.value.big, v2.value.big);
+ return v;
+ }
+
+ return v1;
+}
+
+
+/******************************************************************************/
+/*
+ * Multiply a value.
+ */
+
+value_t valueMul(value_t v1, value_t v2)
+{
+ value_t v;
+
+ a_assert(v1.valid);
+ a_assert(v2.valid);
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+
+ if (v1.type != v2.type)
+ coerce_types(&v1, &v2);
+ switch (v1.type) {
+ default:
+ a_assert(0);
+ break;
+
+ case flag:
+ a_assert(v1.type != flag);
+ break;
+
+#ifdef FLOATING_POINT_SUPPORT
+ case floating:
+ v1.value.floating *= v2.value.floating;
+ return v1;
+#endif
+
+ case byteint:
+ case percent:
+ v1.value.byteint *= v2.value.byteint;
+ return v1;
+
+ case shortint:
+ v1.value.shortint *= v2.value.shortint;
+ return v1;
+
+ case hex:
+ case integer:
+ case octal:
+ v1.value.integer *= v2.value.integer;
+ return v1;
+
+ case big:
+ v.type = big;
+ bmul(v.value.big, v1.value.big, v2.value.big);
+ return v;
+ }
+
+ return v1;
+}
+
+
+/******************************************************************************/
+/*
+ * Divide a value.
+ */
+
+value_t valueDiv(value_t v1, value_t v2)
+{
+ value_t v;
+
+ a_assert(v1.valid);
+ a_assert(v2.valid);
+
+ memset(&v, 0x0, sizeof(v));
+ v.valid = 1;
+
+ if (v1.type != v2.type)
+ coerce_types(&v1, &v2);
+ switch (v1.type) {
+ default:
+ a_assert(0);
+ break;
+
+ case flag:
+ a_assert(v1.type != flag);
+ break;
+
+#ifdef FLOATING_POINT_SUPPORT
+ case floating:
+ v1.value.floating /= v2.value.floating;
+ return v1;
+#endif
+
+ case byteint:
+ case percent:
+ v1.value.byteint /= v2.value.byteint;
+ return v1;
+
+ case shortint:
+ v1.value.shortint /= v2.value.shortint;
+ return v1;
+
+ case hex:
+ case integer:
+ case octal:
+ v1.value.integer /= v2.value.integer;
+ return v1;
+
+ case big:
+ v.type = big;
+ bdiv(v.value.big, v1.value.big, v2.value.big);
+ return v;
+ }
+
+ return v1;
+}
+
+
+/******************************************************************************/
+/*
+ * Compare a value.
+ */
+
+int valueCmp(value_t v1, value_t v2)
+{
+ a_assert(v1.valid);
+ a_assert(v2.valid);
+
+ if (v1.type != v2.type)
+ coerce_types(&v1, &v2);
+ if (v1.type != v2.type) {
+/*
+ * Make v2 == v1
+ */
+ a_assert(v1.type == v2.type);
+ v2 = v1;
+ return 0;
+ }
+ switch (v1.type) {
+ case string:
+ if (v1.value.string == NULL && v2.value.string == NULL) {
+ return 0;
+ } else if (v1.value.string == NULL) {
+ return -1;
+ } else if (v2.value.string == NULL) {
+ return 1;
+ } else {
+ return gstrcmp(v1.value.string, v2.value.string);
+ }
+ /* Nobody here */
+
+ case flag:
+ if (v1.value.flag < v2.value.flag)
+ return -1;
+ else if (v1.value.flag == v2.value.flag)
+ return 0;
+ else return 1;
+
+#ifdef FLOATING_POINT_SUPPORT
+ case floating:
+ if (v1.value.floating < v2.value.floating)
+ return -1;
+ else if (v1.value.floating == v2.value.floating)
+ return 0;
+ else return 1;
+#endif
+
+ case byteint:
+ case percent:
+ if (v1.value.byteint < v2.value.byteint)
+ return -1;
+ else if (v1.value.byteint == v2.value.byteint)
+ return 0;
+ else return 1;
+
+ case shortint:
+ if (v1.value.shortint < v2.value.shortint)
+ return -1;
+ else if (v1.value.shortint == v2.value.shortint)
+ return 0;
+ else return 1;
+
+ case hex:
+ case integer:
+ case octal:
+ if (v1.value.integer < v2.value.integer)
+ return -1;
+ else if (v1.value.integer == v2.value.integer)
+ return 0;
+ else return 1;
+
+ case big:
+ return bcompare(v1.value.big, v2.value.big);
+
+ default:
+ a_assert(0);
+ return 0;
+ }
+}
+
+
+/******************************************************************************/
+/*
+ * If type mismatch, then coerce types to big.
+ * Note: Known bug, casting of negative bigs to floats doesn't work.
+ */
+
+static void coerce_types(register value_t* v1, register value_t* v2)
+{
+#ifdef FLOATING_POINT_SUPPORT
+ if (v1->type == floating) {
+ v2->type = floating;
+ v2->value.floating = (double) v2->value.integer;
+ if (v2->type == big)
+ v2->value.floating = (double) v2->value.big[BLOW] +
+ (double) v2->value.big[BHIGH] * (double) MAXINT;
+
+ } else if (v2->type == floating) {
+ v1->type = floating;
+ v1->value.floating = (double) v1->value.integer;
+ if (v1->type == big)
+ v1->value.floating = (double) v1->value.big[BLOW] +
+ (double) v1->value.big[BHIGH] * (double) MAXINT;
+
+ } else if (v1->type == big) {
+#else
+ if (v1->type == big) {
+#endif /* FLOATING_POINT_SUPPORT */
+ v2->value.big[BLOW] = value_to_integer(v2);
+ if (valueNegative(v2))
+ v2->value.big[BHIGH] = -1;
+ else
+ v2->value.big[BHIGH] = 0;
+ v2->type = big;
+
+ } else if (v2->type == big) {
+ if (valueNegative(v1))
+ v1->value.big[BHIGH] = -1;
+ else
+ v1->value.big[BHIGH] = 0;
+ v1->value.big[BLOW] = value_to_integer(v1);
+ v1->type = big;
+
+
+ } else if (v1->type == integer) {
+ v2->value.integer = value_to_integer(v2);
+ v2->type = integer;
+
+ } else if (v2->type == integer) {
+ v1->value.integer = value_to_integer(v1);
+ v1->type = integer;
+
+ } else if (v1->type != integer) {
+ v2->type = v1->type;
+
+ } else if (v2->type != integer) {
+ v1->type = v2->type;
+
+ }
+ a_assert(v1->type == v2->type);
+}
+
+
+/******************************************************************************/
+/*
+ * Return true if the value is numeric and negative. Otherwise return 0.
+ */
+
+int valueNegative(value_t* vp)
+{
+ switch (vp->type) {
+ default:
+ case string:
+ case bytes:
+ return 0;
+
+#ifdef FLOATING_POINT_SUPPORT
+ case floating:
+ if (vp->value.floating < 0)
+ return 1;
+ return 0;
+#endif
+
+ case flag:
+ if ((signed char)vp->value.flag < 0)
+ return 1;
+ return 0;
+
+ case byteint:
+ case percent:
+ if ((signed char)vp->value.byteint < 0)
+ return 1;
+ return 0;
+
+ case shortint:
+ if (vp->value.shortint < 0)
+ return 1;
+ return 0;
+
+ case hex:
+ case integer:
+ case octal:
+ if (vp->value.integer < 0)
+ return 1;
+ return 0;
+
+ case big:
+ if (vp->value.big[BHIGH] < 0)
+ return 1;
+ return 0;
+ }
+}
+
+/******************************************************************************/
+/*
+ * Return true if the value is numeric and zero. Otherwise return 0.
+ */
+
+int valueZero(value_t* vp)
+{
+ switch (vp->type) {
+ default:
+ case string:
+ case bytes:
+ return 0;
+
+#ifdef FLOATING_POINT_SUPPORT
+ case floating:
+ if (vp->value.floating == 0)
+ return 1;
+ return 0;
+#endif
+
+ case flag:
+ if (vp->value.flag == 0)
+ return 1;
+ return 0;
+
+ case byteint:
+ case percent:
+ if (vp->value.byteint == 0)
+ return 1;
+ return 0;
+
+ case shortint:
+ if (vp->value.shortint == 0)
+ return 1;
+ return 0;
+
+ case hex:
+ case integer:
+ case octal:
+ if (vp->value.integer == 0)
+ return 1;
+ return 0;
+
+ case big:
+ if (vp->value.big[BHIGH] == 0 && vp->value.big[BLOW] == 0)
+ return 1;
+ return 0;
+ }
+}
+
+
+/******************************************************************************/
+/*
+ * Cast a value to an integer. Cannot be called for floating, non-numerics
+ * or bigs.
+ */
+
+static int value_to_integer(value_t* vp)
+{
+ switch (vp->type) {
+ default:
+ case string:
+ case bytes:
+ case big:
+#ifdef FLOATING_POINT_SUPPORT
+ case floating:
+ a_assert(0);
+ return -1;
+#endif
+
+ case flag:
+ return (int) vp->value.flag;
+
+ case byteint:
+ case percent:
+ return (int) vp->value.byteint;
+
+ case shortint:
+ return (int) vp->value.shortint;
+
+ case hex:
+ case integer:
+ case octal:
+ return (int) vp->value.integer;
+ }
+}
+
+
+/******************************************************************************/
+/*
+ * Convert a value to a text based representation of its value
+ */
+
+void valueSprintf(char_t** out, int size, char_t* fmt, value_t vp)
+{
+ char_t *src, *dst, *tmp, *dst_start;
+
+ a_assert(out);
+
+ *out = NULL;
+
+ if (! vp.valid) {
+ *out = bstrdup(B_L, T("Invalid"));
+ return;
+ }
+
+ switch (vp.type) {
+ case flag:
+ if (fmt == NULL || *fmt == '\0') {
+ *out = bstrdup(B_L, (vp.value.flag) ? T("true") : T("false"));
+ } else {
+ fmtAlloc(out, size, fmt, (vp.value.flag) ? T("true") : T("false"));
+ }
+ break;
+
+#ifdef FLOATING_POINT_SUPPORT
+ case floating:
+ if (fmt == NULL || *fmt == '\0') {
+ fmtAlloc(out, size, T("%f"), vp.value.floating);
+ } else {
+ fmtAlloc(out, size, fmt, vp.value.floating);
+ }
+ break;
+#endif
+
+ case hex:
+ if (fmt == NULL || *fmt == '\0') {
+ fmtAlloc(out, size, T("0x%lx"), vp.value.hex);
+ } else {
+ fmtAlloc(out, size, fmt, vp.value.hex);
+ }
+ break;
+
+ case big:
+ if (*out == NULL) {
+ *out = btoa(vp.value.big, NULL, 0);
+ } else {
+ btoa(vp.value.big, *out, size);
+ }
+ break;
+
+ case integer:
+ if (fmt == NULL || *fmt == '\0') {
+ fmtAlloc(out, size, T("%ld"), vp.value.integer);
+ } else {
+ fmtAlloc(out, size, fmt, vp.value.integer);
+ }
+ break;
+
+ case octal:
+ if (fmt == NULL || *fmt == '\0') {
+ fmtAlloc(out, size, T("0%lo"), vp.value.octal);
+ } else {
+ fmtAlloc(out, size, fmt, vp.value.octal);
+ }
+ break;
+
+ case percent:
+ if (fmt == NULL || *fmt == '\0') {
+ fmtAlloc(out, size, T("%d%%"), vp.value.percent);
+ } else {
+ fmtAlloc(out, size, fmt, vp.value.percent);
+ }
+ break;
+
+ case byteint:
+ if (fmt == NULL || *fmt == '\0') {
+ fmtAlloc(out, size, T("%d"), (int) vp.value.byteint);
+ } else {
+ fmtAlloc(out, size, fmt, (int) vp.value.byteint);
+ }
+ break;
+
+ case shortint:
+ if (fmt == NULL || *fmt == '\0') {
+ fmtAlloc(out, size, T("%d"), (int) vp.value.shortint);
+ } else {
+ fmtAlloc(out, size, fmt, (int) vp.value.shortint);
+ }
+ break;
+
+ case string:
+ case errmsg:
+ src = vp.value.string;
+
+ if (src == NULL) {
+ *out = bstrdup(B_L, T("NULL"));
+ } else if (fmt && *fmt) {
+ fmtAlloc(out, size, fmt, src);
+
+ } else {
+
+ *out = balloc(B_L, size);
+ dst_start = dst = *out;
+ for (; *src != '\0'; src++) {
+ if (dst >= &dst_start[VALUE_MAX_STRING - 5])
+ break;
+ switch (*src) {
+ case '\a': *dst++ = '\\'; *dst++ = 'a'; break;
+ case '\b': *dst++ = '\\'; *dst++ = 'b'; break;
+ case '\f': *dst++ = '\\'; *dst++ = 'f'; break;
+ case '\n': *dst++ = '\\'; *dst++ = 'n'; break;
+ case '\r': *dst++ = '\\'; *dst++ = 'r'; break;
+ case '\t': *dst++ = '\\'; *dst++ = 't'; break;
+ case '\v': *dst++ = '\\'; *dst++ = 'v'; break;
+ case '\\': *dst++ = '\\'; *dst++ = '\\'; break;
+ case '"': *dst++ = '\\'; *dst++ = '\"'; break;
+ default:
+ if (gisprint(*src)) {
+ *dst++ = *src;
+ } else {
+ fmtAlloc(&tmp, size, T("\\x%02x"),
+ (unsigned int) *src);
+ gstrcpy(dst, tmp);
+ bfreeSafe(B_L, tmp);
+ dst += 4;
+ }
+ break;
+ }
+ }
+ *dst++ = '\0';
+ }
+ break;
+
+#ifdef UNUSED
+ case bytes:
+ asrc = vp.value.bytes;
+
+ if (asrc == NULL) {
+ *out = bstrdup(B_L, T("NULL"));
+
+ } else if (fmt && *fmt) {
+ fmtAlloc(out, size, fmt, asrc);
+
+ } else {
+
+ dst_start = dst;
+ for (; *asrc != '\0'; asrc++) {
+ if (dst >= &dst_start[VALUE_MAX_STRING - 5])
+ break;
+ switch (*asrc) {
+ case '\a': *dst++ = '\\'; *dst++ = 'a'; break;
+ case '\b': *dst++ = '\\'; *dst++ = 'b'; break;
+ case '\f': *dst++ = '\\'; *dst++ = 'f'; break;
+ case '\n': *dst++ = '\\'; *dst++ = 'n'; break;
+ case '\r': *dst++ = '\\'; *dst++ = 'r'; break;
+ case '\t': *dst++ = '\\'; *dst++ = 't'; break;
+ case '\v': *dst++ = '\\'; *dst++ = 'v'; break;
+ case '\\': *dst++ = '\\'; *dst++ = '\\'; break;
+ case '"': *dst++ = '\\'; *dst++ = '\"'; break;
+ default:
+ if (gisprint(*asrc)) {
+ *dst++ = *asrc;
+ } else {
+ fmtAlloc(dst, size,
+ T("\\x%02x"), (unsigned int) *asrc);
+ dst += 4;
+ }
+ break;
+ }
+ }
+ *dst++ = '\0';
+ }
+ break;
+#endif
+
+ default:
+ a_assert(0);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Print a value to the named file descriptor
+ */
+
+void valueFprintf(FILE* fp, char_t* fmt, value_t vp)
+{
+ char_t *buf;
+
+ buf = NULL;
+ valueSprintf(&buf, VALUE_MAX_STRING, fmt, vp);
+ gfputs(buf, fp);
+ bfreeSafe(B_L, buf);
+ fflush(fp);
+}
+
+/******************************************************************************/
+/*
+ * Ascii to value conversion
+ */
+
+value_t valueAtov(char_t* s, int pref_type)
+{
+ vtype_t type;
+ value_t v;
+ long tmp[2], tmp2[2], base[2];
+ int i, len, num;
+
+ a_assert(0 <= pref_type && pref_type < 99); /* Sanity check */
+ a_assert(s);
+
+ v = value_null;
+ if (s == NULL) {
+ return value_null;
+ }
+
+ base[BLOW] = 10;
+ base[BHIGH] = 0;
+ len = gstrlen(s);
+
+/*
+ * Determine the value type
+ */
+ type = undefined;
+ if (pref_type <= 0) {
+ if (gisdigit(*s)) {
+ base[BHIGH] = 0;
+ if (s[len - 1] == '%') {
+ type = percent;
+ len --;
+ base[BLOW] = 10;
+ } else if (*s == '0') {
+ if (s[1] == 'x') {
+ type = hex;
+ s += 2;
+ len -= 2;
+ base[BLOW] = 16;
+ } else if (s[1] == '\0') {
+ type = integer;
+ base[BLOW] = 10;
+ } else {
+ type = octal;
+ s++;
+ len--;
+ base[BLOW] = 8;
+ }
+ } else {
+ type = integer;
+ base[BLOW] = 10;
+ }
+
+ } else {
+ if (gstrcmp(s, T("true")) == 0 || gstrcmp(s, T("false")) == 0) {
+ type = flag;
+ } else if (*s == '\'' && s[len - 1] == '\'') {
+ type = string;
+ s++;
+ len -= 2;
+ } else if (*s == '\"' && s[len - 1] == '\"') {
+ type = string;
+ s++;
+ len -= 2;
+ } else {
+ type = string;
+ }
+ }
+ v.type = type;
+
+ } else
+ v.type = pref_type;
+ v.valid = 1;
+
+/*
+ * Do the conversion. Always use big arithmetic
+ */
+ switch (v.type) {
+ case hex:
+ if (!isdigit(s[0])) {
+ if (gtolower(s[0]) >= 'a' || gtolower(s[0]) <= 'f') {
+ v.value.big[BLOW] = 10 + gtolower(s[0]) - 'a';
+ } else {
+ v.value.big[BLOW] = 0;
+ }
+ } else {
+ v.value.big[BLOW] = s[0] - '0';
+ }
+ v.value.big[BHIGH] = 0;
+ for (i = 1; i < len; i++) {
+ if (!isdigit(s[i])) {
+ if (gtolower(s[i]) < 'a' || gtolower(s[i]) > 'f') {
+ break;
+ }
+ num = 10 + gtolower(s[i]) - 'a';
+ } else {
+ num = s[i] - '0';
+ }
+ bmul(tmp, v.value.big, base);
+ binit(tmp2, 0, num);
+ badd(v.value.big, tmp, tmp2);
+ }
+ v.value.hex = v.value.big[BLOW];
+ break;
+
+ case shortint:
+ case byteint:
+ case integer:
+ case percent:
+ case octal:
+ case big:
+ v.value.big[BHIGH] = 0;
+ if (gisdigit(s[0]))
+ v.value.big[BLOW] = s[0] - '0';
+ else
+ v.value.big[BLOW] = 0;
+ for (i = 1; i < len && gisdigit(s[i]); i++) {
+ bmul(tmp, v.value.big, base);
+ binit(tmp2, 0, s[i] - '0');
+ badd(v.value.big, tmp, tmp2);
+ }
+ switch (v.type) {
+ case shortint:
+ v.value.shortint = (short) v.value.big[BLOW];
+ break;
+ case byteint:
+ v.value.byteint = (char) v.value.big[BLOW];
+ break;
+ case integer:
+ v.value.integer = (int) v.value.big[BLOW];
+ break;
+ case percent:
+ v.value.percent = (char) v.value.big[BLOW];
+ break;
+ case octal:
+ v.value.octal = (int) v.value.big[BLOW];
+ break;
+ default:
+ break;
+ }
+ break;
+
+#ifdef FLOATING_POINT_SUPPORT
+ case floating:
+ gsscanf(s, T("%f"), &v.value.floating);
+ break;
+#endif
+
+ case flag:
+ if (*s == 't')
+ v.value.flag = 1;
+ else v.value.flag = 0;
+ break;
+
+ case string:
+/*
+ * Note this always ballocs a string
+ */
+ v = valueString(s, VALUE_ALLOCATE);
+ break;
+
+ case bytes:
+ v = valueBytes((char*) s, VALUE_ALLOCATE);
+ break;
+
+#ifdef UNUSED
+ case literal:
+ v = value_literal(bstrdup(B_L, s));
+ v.value.literal[len] = '\0';
+ break;
+#endif
+
+ case undefined:
+ case symbol:
+ default:
+ v.valid = 0;
+ a_assert(0);
+ }
+ return v;
+}
+
+#endif /* !UEMF */
+/******************************************************************************/
+
diff --git a/cleopatre/application/spidgoahead/web/action.asp b/cleopatre/application/spidgoahead/web/action.asp
new file mode 100644
index 0000000000..b3d07ce70f
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/action.asp
@@ -0,0 +1,83 @@
+<html>
+<head>
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+<script type="text/javascript" src="jslib/query.js"></script>
+
+<script type='text/javascript'>
+
+var qs = new Querystring();
+var action = qs.get("action","none");
+var mac = qs.get("mac","00:00:00:00:00:00");
+
+var result = <% spidcomAction(); %>;
+
+var data_array;
+var data;
+
+
+function generateLink()
+{
+ document.getElementById('stat_table').innerHTML += "<tr><td>Downlink attenuation</td><td>Uplink attenuation</td><td>Downlink quality</td><td>Uplink quality</td></tr>";
+ document.getElementById('stat_table').innerHTML += "<tr><td>"+data_array[0]+"</td><td>"+data_array[1]+"</td><td>"+data_array[2]+"</td><td>"+data_array[3]+"</td></tr>";
+}
+
+function generateDeviceInfo()
+{
+ document.getElementById('stat_table').innerHTML += "<tr><td>Model number</td><td>Port amount</td><td>Sofware version</td></tr>";
+ document.getElementById('stat_table').innerHTML += "<tr><td>" +data_array[0]+ "</td><td>" +data_array[1]+ "</td><td>" +data_array[2]+ "</td><td></tr>";
+}
+
+function generateStatistics()
+{
+
+ table_rows = new Array("Packets","Bytes","Broadcast","Multicast","Dropped");
+ document.getElementById('stat_table').innerHTML += "<tr><td colspan='2'>TX</td><td colspan='2'>RX</td></tr>";
+ for(i=0;i<data_array.length/2;i++)
+ document.getElementById('stat_table').innerHTML += "<tr> <td>"+table_rows[i]+"</td><td>" + data_array[i] + "</td><td>"+ table_rows[i] +"</td><td>" + data_array[i+5] + "</td></tr>";
+}
+
+function start(){
+ if ((action == "reboot") || (action == "reset_stat"))
+ document.getElementById("customer_message").innerHTML = "<code>" + result +"</code>";
+ else
+ if (action == "get_stat")
+ {
+ data = result;
+ data_array = data.split(':');
+ generateStatistics();
+ }
+ else
+ if (action == "get_device_info")
+ {
+ data = result;
+ data_array = data.split(':');
+ generateDeviceInfo();
+ }
+ else
+ if (action == "get_link")
+ {
+ data = result;
+ data_array = data.split(':');
+ generateLink();
+ }
+
+
+}
+
+</script>
+
+</head>
+
+
+<body onload='javascript:start();'>
+
+<table id='stat_table' style='width:100%'></table>
+
+<div id='customer_message' style='width:100%'>
+</div>
+<div align='right'>
+<input type='button' onclick='JavaScript:window.close()' value='Close'/>
+</div>
+</body>
+
+</html> \ No newline at end of file
diff --git a/cleopatre/application/spidgoahead/web/addgroup.asp b/cleopatre/application/spidgoahead/web/addgroup.asp
new file mode 100644
index 0000000000..53c8ec80f9
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/addgroup.asp
@@ -0,0 +1,43 @@
+<html>
+<!- Copyright (c) Go Ahead Software Inc., 2000-2000. All Rights Reserved. ->
+<head>
+<title>Add a User Group</title>
+<meta http-equiv="Pragma" content="no-cache">
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+<% language=javascript %>
+</head>
+
+<body>
+<h1>Add a User Group</h1>
+<form action=/goform/AddGroup method=POST>
+
+<table>
+<tr>
+ <td>Group Name:</td>
+<td>
+ <input type=text name=group title="Group Name" size=40 value="">
+</td>
+</tr>
+<tr>
+ <td>Privilege:</td><td><% MakePrivilegeList(); %></td>
+</tr>
+<tr>
+ <td>Access Method:</td><td><% MakeAccessMethodList(); %></td>
+</tr>
+<tr>
+ <td>Enabled:</td>
+<td>
+ <INPUT TYPE=checkbox CHECKED name=enabled title="Enabled">
+</td>
+</tr>
+<tr>
+ <td></td>
+ <td ALIGN="CENTER">
+ <input type=submit name=ok value="OK"> <input type=submit name=ok value="Cancel"></td>
+</tr>
+</table>
+
+</form>
+
+</body>
+</html>
diff --git a/cleopatre/application/spidgoahead/web/addlimit.asp b/cleopatre/application/spidgoahead/web/addlimit.asp
new file mode 100644
index 0000000000..9c2bb613e7
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/addlimit.asp
@@ -0,0 +1,37 @@
+<html>
+<!- Copyright (c) Go Ahead Software Inc., 2000-2000. All Rights Reserved. ->
+<head>
+<title>Add an Access Limit</title>
+<meta http-equiv="Pragma" content="no-cache">
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+<% language=javascript %>
+</head>
+
+<body>
+<h1>Add an Access Limit</h1>
+<form action=/goform/AddAccessLimit method=POST>
+
+<table>
+<tr>
+ <td>URL:</td><td><input type=text name=url title="URL" size=40 value=""></td>
+</tr>
+<tr>
+ <td>Group:</td><td><% MakeGroupList(); %></td>
+</tr>
+<tr>
+ <td>Access Method:</td><td><% MakeAccessMethodList(); %></td>
+</tr>
+<tr>
+ <td>Secure:</td><td><INPUT TYPE=checkbox name=secure" title="Secure"></td>
+</tr>
+<tr>
+ <td></td>
+ <td ALIGN="CENTER">
+ <input type=submit name=ok value="OK"> <input type=submit name=ok value="Cancel"></td>
+</tr>
+</table>
+
+</form>
+
+</body>
+</html>
diff --git a/cleopatre/application/spidgoahead/web/adduser.asp b/cleopatre/application/spidgoahead/web/adduser.asp
new file mode 100644
index 0000000000..91f33c931b
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/adduser.asp
@@ -0,0 +1,51 @@
+<html>
+<!- Copyright (c) Go Ahead Software Inc., 2000-2000. All Rights Reserved. ->
+<head>
+<title>Add a User</title>
+<meta http-equiv="Pragma" content="no-cache">
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+<% language=javascript %>
+</head>
+
+<body>
+<h1>Add a User</h1>
+<form action=/goform/AddUser method=POST>
+<table>
+<tr>
+ <td>User ID:</td>
+<td>
+ <input type=text name=user title="User ID" size=40 value="">
+</td>
+</tr>
+<tr>
+ <td>Group:</td><td><% MakeGroupList(); %></td>
+</tr>
+<tr>
+ <td>Enabled:</td>
+<td>
+ <INPUT TYPE=checkbox CHECKED name=enabled title="Enabled">
+</td>
+</tr>
+<tr>
+ <td>Password:</td>
+<td>
+ <input type=password name=password size=40 title="Enter Password" value="">
+</td>
+</tr>
+<tr>
+ <td>Confirm:</td>
+<td>
+ <input type=password name=passconf size=40 title="Confirm Password" value="">
+</td>
+</tr>
+<tr>
+ <td></td>
+ <td ALIGN="CENTER">
+ <input type=submit name=ok value="OK"> <input type=submit name=ok value="Cancel"></td>
+</tr>
+</table>
+
+</form>
+
+</body>
+</html>
diff --git a/cleopatre/application/spidgoahead/web/back.png b/cleopatre/application/spidgoahead/web/back.png
new file mode 100644
index 0000000000..a0dd8422b0
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/back.png
Binary files differ
diff --git a/cleopatre/application/spidgoahead/web/banner.asp b/cleopatre/application/spidgoahead/web/banner.asp
new file mode 100644
index 0000000000..9f50c4513b
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/banner.asp
@@ -0,0 +1,9 @@
+<HTML>
+<body bgcolor="#F5F5F5">
+<p>
+<table bgcolor="#ffaaaa" width=100%><TR><TD align="center">&nbsp;</TD>
+</TR></table>
+</p>
+<div style='position:absolute;left:50%;margin-bottom:50%' id='naslov'></div>
+</body>
+</HTML>
diff --git a/cleopatre/application/spidgoahead/web/br0.asp b/cleopatre/application/spidgoahead/web/br0.asp
new file mode 100644
index 0000000000..3fdf176b5a
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/br0.asp
@@ -0,0 +1,264 @@
+<html>
+
+<head>
+ <title>SPC 300 - SPiDCOM</title>
+
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+
+<style type = 'text/css'>
+div.margina,table.margina{
+ margin-left:5%;
+ width:500px;
+ background-color:#F5F5F5;
+}
+td{
+ padding-left:5px;
+}
+
+input[type='text']{
+ margin-left:auto;
+ margin-right:auto;
+ text-align:center;
+}
+input[disabled='disabled'] {
+ background-color:BBBBBB;
+ color:white;
+ cursor:default;
+}
+body{
+ background-image: url("/spidcom.gif");
+ background-repeat: no-repeat;
+ background-attachment: fixed;
+ background-position: center center;
+}
+
+</style>
+
+<script type='text/javascript'>
+
+var mac = <% spidcomAspGetMac('br0'); %>
+var ip_data = <% spidcomAspGetIpData('br0'); %>
+
+
+//var online_station = '< % spidcomAspEocGetTopo(); % >';
+//var station_array = online_station.split('-');
+
+
+var ip_data_array = ip_data.split(':');
+
+var ip_array;
+var nm_array;
+var gw_array;
+
+if(ip_data_array[1].length == 0)
+ ip_array = new Array("0","0","0","0")
+else
+ ip_array = ip_data_array[1].split('.');
+
+if(ip_data_array[2].length == 0)
+ nm_array = new Array("0","0","0","0")
+else
+ nm_array = ip_data_array[2].split('.');
+
+if(ip_data_array[4].length == 0)
+ gw_array = new Array("0","0","0","0")
+else
+ gw_array = ip_data_array[4].split('.');
+
+
+
+var dhcp = ip_data_array[0];
+var mac_array = mac.split(':');
+
+
+
+function validate()
+{
+ parent.footer2.document.getElementById('error_console').innerHTML = "";
+
+ if(ip_data.indexOf("error") >= 0) // error
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>IP data : error condition: " + ip_data.toString() + "</small><br>";
+ else
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>IP data : OK</small><br>";
+
+ if(mac.indexOf("error") >= 0) // error
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>MAC : error condition: " + mac.toString() + "</small><br>";
+ else
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>MAC : OK</small><br>";
+
+ /*
+ if(online_station.indexOf("error") >= 0) // error
+ parent.footer2.document.getElementById('error_console').innerHTML += "<code>Online station : error condition: " + mac.toString(); + "</code><br>";
+ else
+ parent.footer2.document.getElementById('error_console').innerHTML += "<code>Online station : OK</code><br>";
+ */
+}
+
+
+function showTip(){
+document.getElementById('tooltip').innerHTML = "You must restart modem for the changes to take effect";
+}
+
+function clearTip(){
+document.getElementById('tooltip').innerHTML = "";
+}
+
+function reboot_flag()
+{
+ document.getElementById('reboot').value = 'yes';
+}
+
+function refresh(){
+parent.content.location.reload();
+}
+
+
+function joinadd(){
+document.getElementById('ip').value = document.getElementById('ipe1').value + '.' + document.getElementById('ipe2').value + '.' + document.getElementById('ipe3').value + '.' + document.getElementById('ipe4').value;
+document.getElementById('nm').value = document.getElementById('nete1').value + '.' + document.getElementById('nete2').value + '.' + document.getElementById('nete3').value + '.' + document.getElementById('nete4').value;
+document.getElementById('gw').value = document.getElementById('gwe1').value + '.' + document.getElementById('gwe2').value + '.' + document.getElementById('gwe3').value + '.' + document.getElementById('gwe4').value;
+}
+
+function set_refresh(){
+
+if (document.getElementById('refresh_or_not').checked)
+ document.getElementById("stat_iframe").contentWindow.RestartRefresh();
+else
+ document.getElementById('stat_iframe').contentWindow.StopRefresh();
+}
+
+function checkMainForm(){
+ return true;
+}
+
+function fill_ip_data()
+{
+
+var idip = 'ipe';
+var idnet = 'nete';
+var idgw = 'gwe';
+var idmac = 'mace'
+
+for (var i=1;i<=4;i++)
+{
+document.getElementById(idip+i).value = ip_array[i-1];
+document.getElementById(idnet+i).value = nm_array[i-1];
+document.getElementById(idgw+i).value = gw_array[i-1];
+document.getElementById(idmac+i).value = mac_array[i-1];
+}
+document.getElementById(idmac+5).value = mac_array[4];
+document.getElementById(idmac+6).value = mac_array[5];
+
+
+if (dhcp == 'DHCP')
+ document.mainForm.dhcp[0].checked = 'checked';
+
+
+ joinadd();
+}
+</script>
+</head>
+
+
+<body onload='javascript:validate();fill_ip_data();'>
+<!-- Settings name -->
+<div class='margina' > <h2>Bridge configuration</h2></div>
+
+<!-- Main form -->
+
+<form name='mainForm' action='/goform/formAction' target='_self' method='GET' onSubmit='return checkMainForm();'>
+
+<input type='hidden' name='reboot' id='reboot' value='no'/> <!-- This will be passed by form -->
+<input type='hidden' name='inter' id='inter' value='br0'/> <!-- This will be passed by form -->
+
+
+<table name = 'ipData' class='margina' style='background-color:#FFFFFF' border='0'>
+<tr>
+<td><h3>Network configuration</h3></td>
+</tr>
+<tr>
+<td>Current IP address</td>
+<td></td><td></td>
+<td><input id='ipe1' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='ipe2' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='ipe3' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='ipe4' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input type='hidden' name='ip' id='ip' value='0'/> </td> <!-- This will be passed by form -->
+</tr>
+
+<tr>
+<td>Current netmask</td>
+<td></td><td></td>
+<td><input id='nete1' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='nete2' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='nete3' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='nete4' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input type='hidden' name='nm' id='nm' value='0'/> </td> <!-- This will be passed by form -->
+</tr>
+<tr>
+<td>Current gateway address</td>
+<td></td><td></td>
+<td><input id='gwe1' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='gwe2' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='gwe3' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='gwe4' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input type='hidden' id='gw' name='gw' value='0'/> </td> <!-- This will be passed by form -->
+</tr>
+<tr align='center'>
+<td align='left'>MAC address</td>
+
+<td><input disabled='disabled' id='mace1' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace2' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace3' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace4' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace5' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace6' type='text' size=2 maxlength=2 value='0'/> </td>
+</tr>
+<tr>
+<td>DHCP Mode</td>
+<td></td><td></td><td></td><td></td>
+<td><input type='radio' name='dhcp' value='dhcp'/>ON</td>
+<td><input type='radio' name='dhcp' value='static' checked='checked'/>OFF</td>
+</tr>
+
+</table>
+
+<div class='margina'><h2>Activity statistics</h2></div>
+<table id='background-image' class='d25' style='margin-left:5%;width:500px' border='0'> <tr align='center'><td colspan='2'>RX:</td><td colspan='2'>TX:</td></tr>
+<tr><td class='d25' >Bytes</td><td class='d25' id='rb' bgcolor='lavender' align='center'></td><td>Bytes</td> <td class='d25' id = 'tb' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Packets</td><td class='d25' id='rp' bgcolor='lavender' align='center'></td><td>Packets</td> <td class='d25' id = 'tp' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Errors</td><td class='d25' id='re' bgcolor='lavender' align='center'></td><td>Errors</td> <td class='d25' id = 'te' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Drops</td><td class='d25' id='rd' bgcolor='lavender' align='center'></td><td>Drops</td> <td class='d25' id = 'td' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Fifo</td><td class='d25' id='rf' bgcolor='lavender' align='center'></td><td>Fifo</td> <td class='d25' id = 'tf' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Frame</td><td class='d25' id='rfr' bgcolor='lavender' align='center'></td><td>Colls</td> <td class='d25' id = 'tc' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Compressed</td><td class='d25' id='rc' bgcolor='lavender' align='center'></td><td>Carrier</td> <td class='d25' id = 'tcar' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Multicast</td><td class='d25' id='rm' bgcolor='lavender' align='center'></td><td>Compressed</td> <td class='d25' id = 'tcom' bgcolor='lavender' align='center'></td></tr>
+
+
+</table>
+<div class='margina' align='right'>
+Auto refresh<input type='checkbox' name='refresh_or_not' id='refresh_or_not' onchange='set_refresh();' checked='checked' >
+</div>
+
+<div class='margina' style='margin-top:5px' align='right'>
+<pre ></pre>
+<input type='button' value='Refresh' onclick='javascript:refresh();'>
+<input type='submit' onclick='javascript:joinadd();' value='Save configuration' onmouseover='javascript:showTip();' onmouseout='javascript:clearTip();'/>
+<input type='submit' value='Restart the modem' onclick='javascript:reboot_flag();'/>
+<pre id ='tooltip'></pre>
+</div>
+
+
+</form>
+
+
+
+
+
+
+
+<iframe id='stat_iframe' name='stat_iframe' src ="stat.asp?interface=br0" frameborder='0' height='5'>
+ <p>Your browser does not support iframes.</p>
+</iframe>
+</body>
+</html> \ No newline at end of file
diff --git a/cleopatre/application/spidgoahead/web/count.asp b/cleopatre/application/spidgoahead/web/count.asp
new file mode 100644
index 0000000000..f5e08e65e4
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/count.asp
@@ -0,0 +1,5 @@
+<html>
+
+<% spidcomAspReboot(); %>
+
+</html>
diff --git a/cleopatre/application/spidgoahead/web/delgroup.asp b/cleopatre/application/spidgoahead/web/delgroup.asp
new file mode 100644
index 0000000000..e0eab7b58b
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/delgroup.asp
@@ -0,0 +1,28 @@
+<html>
+<!- Copyright (c) Go Ahead Software Inc., 2000-2000. All Rights Reserved. ->
+<head>
+<title>Delete a User Group</title>
+<meta http-equiv="Pragma" content="no-cache">
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+<% language=javascript %>
+</head>
+
+<body>
+<h1>Delete a User Group</h1>
+<form action=/goform/DeleteGroup method=POST>
+
+<table>
+<tr>
+<% MakeGroupList(); %>
+</tr>
+<tr>
+ <td></td>
+ <td ALIGN="CENTER">
+ <input type=submit name=ok value="OK"> <input type=submit name=ok value="Cancel"></td>
+</tr>
+</table>
+
+</form>
+
+</body>
+</html>
diff --git a/cleopatre/application/spidgoahead/web/dellimit.asp b/cleopatre/application/spidgoahead/web/dellimit.asp
new file mode 100644
index 0000000000..f613b60c44
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/dellimit.asp
@@ -0,0 +1,28 @@
+<html>
+<!- Copyright (c) Go Ahead Software Inc., 2000-2000. All Rights Reserved. ->
+<head>
+<title>Delete an Access Limit</title>
+<meta http-equiv="Pragma" content="no-cache">
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+<% language=javascript %>
+</head>
+
+<body>
+<h1>Delete an Access Limit</h1>
+<form action=/goform/DeleteAccessLimit method=POST>
+
+<table>
+<tr>
+<% MakeAccessLimitList(); %>
+</tr>
+<tr>
+ <td></td>
+ <td ALIGN="CENTER">
+ <input type=submit name=ok value="OK"> <input type=submit name=ok value="Cancel"></td>
+</tr>
+</table>
+
+</form>
+
+</body>
+</html>
diff --git a/cleopatre/application/spidgoahead/web/deluser.asp b/cleopatre/application/spidgoahead/web/deluser.asp
new file mode 100644
index 0000000000..3157b3513c
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/deluser.asp
@@ -0,0 +1,28 @@
+<html>
+<!- Copyright (c) Go Ahead Software Inc., 2000-2000. All Rights Reserved. ->
+<head>
+<title>Delete a User</title>
+<meta http-equiv="Pragma" content="no-cache">
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+<% language=javascript %>
+</head>
+
+<body>
+<h1>Delete a User</h1>
+<form action=/goform/DeleteUser method=POST>
+
+<table>
+<tr>
+<% MakeUserList(); %>
+</tr>
+<tr>
+ <td></td>
+ <td ALIGN="CENTER">
+ <input type=submit name=ok value="OK" title="Delete the User"> <input type=submit name=ok value="Cancel"></td>
+</tr>
+</table>
+
+</form>
+
+</body>
+</html>
diff --git a/cleopatre/application/spidgoahead/web/eth0.asp b/cleopatre/application/spidgoahead/web/eth0.asp
new file mode 100644
index 0000000000..55a65f4d62
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/eth0.asp
@@ -0,0 +1,239 @@
+<html>
+
+<head>
+ <title>SPC 300 - SPiDCOM</title>
+
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+
+<style type = 'text/css'>
+div.margina,table.margina{
+ margin-left:5%;
+ width:500px;
+ background-color:#F5F5F5;
+}
+td{
+ padding-left:5px;
+}
+
+input[type='text']{
+ margin-left:auto;
+ margin-right:auto;
+ text-align:center;
+}
+input[disabled='disabled'] {
+ background-color:BBBBBB;
+ color:white;
+ cursor:default;
+}
+
+body{
+ background-image: url("/spidcom.gif");
+ background-repeat: no-repeat;
+ background-attachment: fixed;
+ background-position: center center;
+}
+</style>
+
+<script type='text/javascript'>
+
+var mac = <% spidcomAspGetMac('eth0'); %>
+var ip_data = <% spidcomAspGetIpData('eth0'); %>
+
+
+var ip_data_array = ip_data.split(':');
+
+var ip_array;
+var nm_array;
+var gw_array;
+
+if(ip_data_array[1].length == 0)
+ ip_array = new Array("0","0","0","0")
+else
+ ip_array = ip_data_array[1].split('.');
+
+if(ip_data_array[2].length == 0)
+ nm_array = new Array("0","0","0","0")
+else
+ nm_array = ip_data_array[2].split('.');
+
+if(ip_data_array[4].length == 0)
+ gw_array = new Array("0","0","0","0")
+else
+ gw_array = ip_data_array[4].split('.');
+var mac_array = mac.split(':');
+
+var dhcp = ip_data_array[0];
+
+
+function validate()
+{
+ parent.footer2.document.getElementById('error_console').innerHTML = "";
+
+ if(ip_data.indexOf("error") >= 0) // error
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>IP data : error condition: " + ip_data.toString() + "</small><br>";
+ else
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>IP data : OK</small><br>";
+
+ if(mac.indexOf("error") >= 0) // error
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>MAC : error condition: " + mac.toString() + "</small><br>";
+ else
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>MAC : OK</small><br>";
+
+}
+
+
+function showTip(){
+document.getElementById('tooltip').innerHTML = "You must restart modem for the changes to take effect";
+}
+
+function clearTip(){
+document.getElementById('tooltip').innerHTML = "";
+}
+
+function reboot_flag()
+{
+ document.getElementById('reboot').value = 'yes';
+}
+
+function refresh(){
+parent.content.location.reload();
+}
+
+
+function joinadd(){
+document.getElementById('ip').value = document.getElementById('ipe1').value + '.' + document.getElementById('ipe2').value + '.' + document.getElementById('ipe3').value + '.' + document.getElementById('ipe4').value;
+document.getElementById('nm').value = document.getElementById('nete1').value + '.' + document.getElementById('nete2').value + '.' + document.getElementById('nete3').value + '.' + document.getElementById('nete4').value;
+document.getElementById('gw').value = document.getElementById('gwe1').value + '.' + document.getElementById('gwe2').value + '.' + document.getElementById('gwe3').value + '.' + document.getElementById('gwe4').value;
+}
+
+function set_refresh(){
+
+if (document.getElementById('refresh_or_not').checked)
+ document.getElementById("stat_iframe").contentWindow.RestartRefresh();
+else
+ document.getElementById('stat_iframe').contentWindow.StopRefresh();
+}
+
+function checkMainForm(){
+ return true;
+}
+
+function fill_ip_data()
+{
+
+var idip = 'ipe';
+var idnet = 'nete';
+var idgw = 'gwe';
+var idmac = 'mace'
+
+for (var i=1;i<=4;i++)
+{
+document.getElementById(idip+i).value = ip_array[i-1];
+document.getElementById(idnet+i).value = nm_array[i-1];
+document.getElementById(idgw+i).value = gw_array[i-1];
+document.getElementById(idmac+i).value = mac_array[i-1];
+}
+document.getElementById(idmac+5).value = mac_array[4];
+document.getElementById(idmac+6).value = mac_array[5];
+
+
+if (dhcp == 'DHCP')
+ document.mainForm.dhcp.value = 'dhcp';
+
+ joinadd();
+}
+</script>
+</head>
+
+
+<body onload='javascript:validate();fill_ip_data();'>
+<!-- Settings name -->
+<div class='margina' > <h2>Ethernet configuration</h2></div>
+
+<!-- Main form -->
+
+<form name='mainForm' action='/goform/formAction' target='_self' method = 'GET' onSubmit='return checkMainForm();'>
+
+<table name = 'ipData' class='margina' style='background-color:#FFFFFF' border='0'>
+<tr>
+<td><h3>Network configuration</h3></td>
+</tr>
+<tr>
+<td>Current IP address</td>
+<td></td><td></td>
+<td><input id='ipe1' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='ipe2' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='ipe3' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='ipe4' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input type='hidden' name='ip' id='ip' value='0'/> </td> <!-- This will be passed by form -->
+</tr>
+
+<tr>
+<td>Current netmask</td>
+<td></td><td></td>
+<td><input id='nete1' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='nete2' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='nete3' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='nete4' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input type='hidden' name='nm' id='nm' value='0'/> </td> <!-- This will be passed by form -->
+</tr>
+<tr>
+<td>Current gateway address</td>
+<td></td><td></td>
+<td><input id='gwe1' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='gwe2' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='gwe3' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='gwe4' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input type='hidden' id='gw' name='gw' value='0'/> </td> <!-- This will be passed by form -->
+</tr>
+<tr align='center'>
+<td align='left'>MAC address</td>
+
+<td><input disabled='disabled' id='mace1' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace2' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace3' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace4' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace5' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace6' type='text' size=2 maxlength=2 value='0'/> </td>
+</tr>
+
+<input type='hidden' name='dhcp' id='dhcp' value='static'/> <!-- This will be passed by form -->
+<input type='hidden' name='inter' id='inter' value='eth0'/> <!-- This will be passed by form -->
+</table>
+
+<div class='margina'><h2>Activity statistics</h2></div>
+<table id='background-image' class='d25' style='margin-left:5%;width:500px' border='0'> <tr align='center'><td colspan='2'>RX:</td><td colspan='2'>TX:</td></tr>
+<tr><td class='d25' >Bytes</td><td class='d25' id='rb' bgcolor='lavender' align='center'></td><td>Bytes</td> <td class='d25' id = 'tb' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Packets</td><td class='d25' id='rp' bgcolor='lavender' align='center'></td><td>Packets</td> <td class='d25' id = 'tp' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Errors</td><td class='d25' id='re' bgcolor='lavender' align='center'></td><td>Errors</td> <td class='d25' id = 'te' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Drops</td><td class='d25' id='rd' bgcolor='lavender' align='center'></td><td>Drops</td> <td class='d25' id = 'td' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Fifo</td><td class='d25' id='rf' bgcolor='lavender' align='center'></td><td>Fifo</td> <td class='d25' id = 'tf' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Frame</td><td class='d25' id='rfr' bgcolor='lavender' align='center'></td><td>Colls</td> <td class='d25' id = 'tc' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Compressed</td><td class='d25' id='rc' bgcolor='lavender' align='center'></td><td>Carrier</td> <td class='d25' id = 'tcar' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Multicast</td><td class='d25' id='rm' bgcolor='lavender' align='center'></td><td>Compressed</td> <td class='d25' id = 'tcom' bgcolor='lavender' align='center'></td></tr>
+
+</table>
+<div class='margina' align='right'>
+Auto refresh<input type='checkbox' name='refresh_or_not' id='refresh_or_not' onchange='set_refresh();' checked='checked'>
+</div>
+
+
+<div class='margina' align='right'>
+<pre ></pre>
+<input type='button' value='Refresh' onclick='javascript:refresh();'>
+<input type='submit' onclick='javascript:joinadd();' value='Save configuration' onmouseover='javascript:showTip();' onmouseout='javascript:clearTip();'/>
+<input type='submit' value='Restart the modem' onclick='javascript:reboot_flag();'/>
+<pre id ='tooltip'></pre>
+</div>
+
+
+</form>
+
+
+
+
+<iframe id='stat_iframe' name='stat_iframe' src ="stat.asp?interface=eth0" frameborder='0' height='5'>
+ <p>Your browser does not support iframes.</p>
+</iframe>
+</body>
+</html> \ No newline at end of file
diff --git a/cleopatre/application/spidgoahead/web/footer.html b/cleopatre/application/spidgoahead/web/footer.html
new file mode 100644
index 0000000000..25862cea00
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/footer.html
@@ -0,0 +1,7 @@
+<HTML>
+<body bgcolor="#F5F5F5">
+<table height='25' bgcolor="#ffaaaa" width=100%><TR><TD align="center">
+ <i><b></b></i>
+</TD></TR></table>
+</body>
+</HTML>
diff --git a/cleopatre/application/spidgoahead/web/index.asp b/cleopatre/application/spidgoahead/web/index.asp
new file mode 100644
index 0000000000..ca9d07be9b
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/index.asp
@@ -0,0 +1,27 @@
+<html>
+ <head>
+ <title>SPiDCOM - SPC 300</title>
+ <link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+ <script language="JavaScript">
+
+
+ function restore() {
+ self.content.location.href = "/index.asp"
+ }
+ </script>
+ </head>
+
+ <FRAMESET border="0" frameBorder="1" frameSpacing="0" ROWS="60,*,30" onResize="if (document.layers) {restore();}">
+ <FRAME marginHeight="0" marginWidth="0" noResize name='banner' id='banner' scrolling="no" src="banner.asp">
+ <FRAMESET border="0" COLS="150,*" frameBorder="no" frameSpacing="0">
+ <FRAME marginHeight="0" marginWidth="0" name="nav" noResize scrolling="auto" src="nav.asp">
+ <FRAMESET border="0" ROWS="*,100" frameBorder="no" frameSpacing="0">
+ <FRAME marginHeight="0" marginWidth="0" name="content" noResize scrolling='auto' src="br0.asp">
+ <FRAME marginHeight="0" marginWidth="0" name="footer2" noResize src="none.html">
+ </FRAMESET>
+ </FRAMESET>
+ <FRAME marginHeight="0" marginWidth="0" name="footer" noResize src="footer.html">
+ </FRAMESET>
+
+</html>
+
diff --git a/cleopatre/application/spidgoahead/web/jslib/query.js b/cleopatre/application/spidgoahead/web/jslib/query.js
new file mode 100644
index 0000000000..059bd89460
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/jslib/query.js
@@ -0,0 +1,111 @@
+//<script type='text/javascript'>
+function Querystring()
+
+
+{
+
+
+// get the query string, ignore the ? at the front.
+
+
+ var querystring=location.search.substring(1,location.search.length);
+
+
+
+
+
+// parse out name/value pairs separated via &
+
+
+ var args = querystring.split('&');
+
+
+
+
+
+// split out each name = value pair
+
+
+ for (var i=0;i<args.length;i++)
+
+
+ {
+
+
+ var pair = args[i].split('=');
+
+
+
+
+
+ // Fix broken unescaping
+
+
+ temp = unescape(pair[0]).split('+');
+
+
+ name = temp.join(' ');
+
+
+
+
+
+ temp = unescape(pair[1]).split('+');
+
+
+ value = temp.join(' ');
+
+
+
+
+
+ this[name]=value;
+
+
+ }
+
+
+
+
+
+ this.get=Querystring_get;
+
+
+}
+
+
+
+
+
+
+
+
+function Querystring_get(strKey,strDefault)
+
+
+{
+
+
+ var value=this[strKey];
+
+
+ if (value==null)
+
+
+ {
+
+
+ value=strDefault;
+
+
+ }
+
+
+
+
+
+ return value;
+
+
+}
+//<script> \ No newline at end of file
diff --git a/cleopatre/application/spidgoahead/web/left.png b/cleopatre/application/spidgoahead/web/left.png
new file mode 100644
index 0000000000..b3733eed59
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/left.png
Binary files differ
diff --git a/cleopatre/application/spidgoahead/web/loadcfg.asp b/cleopatre/application/spidgoahead/web/loadcfg.asp
new file mode 100644
index 0000000000..8a1fe46952
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/loadcfg.asp
@@ -0,0 +1,24 @@
+<html>
+<!- Copyright (c) Go Ahead Software Inc., 2000-2000. All Rights Reserved. ->
+<head>
+<title>Load the User Configuration</title>
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+<% language=javascript %>
+</head>
+
+<body>
+<h1>Re-load the permanently stored user configuration</h1>
+<form action=/goform/LoadUserManagement method=POST>
+
+<table>
+<tr>
+ <td></td>
+ <td ALIGN="CENTER">
+ <input type=submit name=ok value="OK" title="Load Configuration"> <input type=submit name=ok value="Cancel"></td>
+</tr>
+</table>
+
+</form>
+
+</body>
+</html>
diff --git a/cleopatre/application/spidgoahead/web/logo-spidcom.jpg b/cleopatre/application/spidgoahead/web/logo-spidcom.jpg
new file mode 100644
index 0000000000..8d8b7f77c9
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/logo-spidcom.jpg
Binary files differ
diff --git a/cleopatre/application/spidgoahead/web/nav.asp b/cleopatre/application/spidgoahead/web/nav.asp
new file mode 100644
index 0000000000..f2b84a395f
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/nav.asp
@@ -0,0 +1,70 @@
+<html>
+
+<head>
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+
+<style type='text/css'>
+
+#one-column-emphasis
+{
+ font-family: "Lucida Sans Unicode", "Lucida Grande", Sans-Serif;
+ font-size: 11px;
+ width:100%;
+ text-align: center;
+ border-collapse: separate;
+ background:#F5F5F5;
+}
+#one-column-emphasis th
+{
+ font-size: 14px;
+ font-weight: normal;
+ padding: 12px 15px;
+ color: #039;
+}
+#one-column-emphasis td
+{
+ padding: 10px 15px;
+ color: #669;
+ border-top: 1px solid #e8edff;
+}
+
+#one-column-emphasis tr:hover td
+{
+ color: white;
+ background: #E0E0E0;
+}
+
+a:link, a:visited {
+color: black;
+
+}
+
+a:hover {
+color:red;
+}
+</style>
+
+
+</head>
+
+<body bgcolor="#F5F5F5">
+
+<div style='position:relative;margin-top:15%'></div>
+
+<div style='position:relative;margin-left:5%;margin-right:5%;background-color:#CCCCCC;text-align:center;color:black'><code>SPC 300</code>
+<table id='one-column-emphasis'>
+<tr><td><a href = 'plc0.asp' target='content'>Plc </a></td></tr>
+<tr><td><a href = 'eth0.asp' target='content'>Ethernet </a></td></tr>
+<tr><td><a href = 'br0.asp' target='content'>Bridge </a></td></tr>
+<tr><td><a href = 'wifi0.asp' target='content'>Wifi </a></td></tr>
+<tr><td><a href = 'soft.asp' target='content'>Software </a></td></tr>
+</table>
+</div>
+
+<div style='position:relative;margin-left:5%;margin-right:5%;background-color:#CCCCCC;text-align:center;color:black'><code>SPC 300</code></div>
+
+
+
+</body>
+
+<html> \ No newline at end of file
diff --git a/cleopatre/application/spidgoahead/web/none.html b/cleopatre/application/spidgoahead/web/none.html
new file mode 100644
index 0000000000..6f8fd62e8c
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/none.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+<style type='text/css'>
+a:link, a:visited {
+color: black;
+
+}
+a { text-decoration: none;}
+
+a:focus { outline: none; }
+
+a:hover {
+color:red;
+}
+</style>
+<script type='text/javascript'>
+function showConsole()
+{
+ if(document.getElementById("error_console").style.visibility == "visible")
+ {
+ document.getElementById("error_console").style.visibility = "hidden";
+ }
+ else
+ {
+ document.getElementById("error_console").style.visibility = "visible";
+ }
+}
+
+</script>
+
+</head>
+<body bgcolor="#FFFFFF" style='background-image:url(/logo-spidcom.jpg);background-repeat:no-repeat;background-position: center right'>
+<!--<div id='title' style='line-height:45px;width:500px;height:45px;margin-left:5%;margin-top:5px;background-image:url(/logo-spidcom.jpg);background-repeat:no-repeat;background-position: center right'><a href='javascript:showConsole();'>Show/Hide ASP console :</a></div><br>-->
+<div id='title' style='width:500px;margin-left:5%;margin-top:5px;'><a href='javascript:showConsole();'>Show/Hide ASP console :</a></div><br>
+<div style='width:500px;border-style:dotted;border-width:thin;border-color:red;margin-left:5%;margin-bottom:15px;visibility:hidden' id='error_console'> </div>
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/cleopatre/application/spidgoahead/web/pattern.png b/cleopatre/application/spidgoahead/web/pattern.png
new file mode 100644
index 0000000000..5159b3bbf1
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/pattern.png
Binary files differ
diff --git a/cleopatre/application/spidgoahead/web/plc0.asp b/cleopatre/application/spidgoahead/web/plc0.asp
new file mode 100644
index 0000000000..ee4e588eb6
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/plc0.asp
@@ -0,0 +1,307 @@
+<html>
+
+<head>
+ <title>SPC 300 - SPiDCOM</title>
+
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+
+<style type = 'text/css'>
+div.margina,table.margina{
+ margin-left:5%;
+ width:500px;
+ background-color:#F5F5F5;
+}
+td{
+ padding-left:5px;
+}
+
+input[type='text']{
+ margin-left:auto;
+ margin-right:auto;
+ text-align:center;
+}
+input[disabled='disabled'] {
+ background-color:BBBBBB;
+ color:white;
+ cursor:default;
+}
+body{
+ background-image: url("/spidcom.gif");
+ background-repeat: no-repeat;
+ background-attachment: fixed;
+ background-position: center center;
+}
+
+</style>
+
+<script type='text/javascript'>
+
+var mac = <% spidcomAspGetMac('plc0'); %>
+var ip_data = <% spidcomAspGetIpData('plc0'); %>
+
+var online_station = '<% spidcomAspEocGetTopo(); %>';
+var station_array = online_station.split('-');
+
+
+var ip_data_array = ip_data.split(':');
+
+var ip_array;
+var nm_array;
+var gw_array;
+
+if(ip_data_array[1].length == 0)
+ ip_array = new Array("0","0","0","0")
+else
+ ip_array = ip_data_array[1].split('.');
+
+if(ip_data_array[2].length == 0)
+ nm_array = new Array("0","0","0","0")
+else
+ nm_array = ip_data_array[2].split('.');
+
+if(ip_data_array[4].length == 0)
+ gw_array = new Array("0","0","0","0")
+else
+ gw_array = ip_data_array[4].split('.');
+
+
+
+var dhcp = ip_data_array[0];
+var mac_array = mac.split(':');
+
+
+
+function validate()
+{
+ parent.footer2.document.getElementById('error_console').innerHTML = "";
+
+ if(ip_data.indexOf("error") >= 0) // error
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>IP data : error condition: " + ip_data.toString() + "</small><br>";
+ else
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>IP data : OK</small><br>";
+
+ if(mac.indexOf("error") >= 0) // error
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>MAC : error condition: " + mac.toString() + "</small><br>";
+ else
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>MAC : OK</small><br>";
+
+
+ if(online_station.indexOf("error") >= 0) // error
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>Online station : error condition: " + mac.toString() + "</small><br>";
+ else
+ parent.footer2.document.getElementById('error_console').innerHTML += "<small style='margin-left:3px'>Online station : OK</small><br>";
+
+}
+
+
+function showTip(){
+document.getElementById('tooltip').innerHTML = "You must restart modem for the changes to take effect";
+}
+
+function clearTip(){
+document.getElementById('tooltip').innerHTML = "";
+}
+
+function reboot_flag()
+{
+ document.getElementById('reboot').value = 'yes';
+}
+
+function refresh(){
+parent.content.location.reload();
+}
+
+
+function perform_eoc_action()
+{
+
+temp1 = document.getElementById('station').value;
+temp2 = document.getElementById('action').value;
+
+if ( (temp1 == "-1") || (temp2 == "none") )
+{
+ alert("Please check your request.");
+ return;
+}
+
+action = document.getElementById('action')[document.getElementById('action').selectedIndex].value.toString();
+station = document.getElementById('station')[document.getElementById('station').selectedIndex].innerHTML.toString();
+mojProzor = window.open("","prozor","toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=yes, width=600, height=200, left=100, top=100");
+mojProzor.focus();
+mojProzor.location.href = "action.asp?mac=" + station + "&action=" + action;
+}
+
+
+function joinadd(){
+document.getElementById('ip').value = document.getElementById('ipe1').value + '.' + document.getElementById('ipe2').value + '.' + document.getElementById('ipe3').value + '.' + document.getElementById('ipe4').value;
+document.getElementById('nm').value = document.getElementById('nete1').value + '.' + document.getElementById('nete2').value + '.' + document.getElementById('nete3').value + '.' + document.getElementById('nete4').value;
+document.getElementById('gw').value = document.getElementById('gwe1').value + '.' + document.getElementById('gwe2').value + '.' + document.getElementById('gwe3').value + '.' + document.getElementById('gwe4').value;
+}
+
+function set_refresh(){
+
+if (document.getElementById('refresh_or_not').checked)
+ document.getElementById("stat_iframe").contentWindow.RestartRefresh();
+else
+ document.getElementById('stat_iframe').contentWindow.StopRefresh();
+}
+
+function checkMainForm(){
+ return true;
+}
+
+function fill_ip_data()
+{
+
+var idip = 'ipe';
+var idnet = 'nete';
+var idgw = 'gwe';
+var idmac = 'mace'
+
+for (var i=1;i<=4;i++)
+{
+document.getElementById(idip+i).value = ip_array[i-1];
+document.getElementById(idnet+i).value = nm_array[i-1];
+document.getElementById(idgw+i).value = gw_array[i-1];
+document.getElementById(idmac+i).value = mac_array[i-1];
+}
+document.getElementById(idmac+5).value = mac_array[4];
+document.getElementById(idmac+6).value = mac_array[5];
+
+
+if (dhcp == 'DHCP')
+ document.mainForm.dhcp.value = 'dhcp';
+
+
+for (i=0;i<station_array.length-1;i++)
+ document.getElementById('station').innerHTML += "<option value='" + i.toString() + "'>" + station_array[i].toString(); + "</option>";
+
+joinadd();
+}
+</script>
+</head>
+
+
+<body onload='javascript:validate();fill_ip_data();'>
+<!-- Settings name -->
+<div class='margina' > <h2>PLC configuration</h2></div>
+
+<!-- Main form -->
+
+<form name='mainForm' action='/goform/formAction' target='_self' method = 'GET' onSubmit='return checkMainForm();'>
+
+<table name = 'ipData' class='margina' style='background-color:#FFFFFF' border='0'>
+<tr>
+<td><h3>Network configuration</h3></td>
+</tr>
+<tr>
+<td>Current IP address</td>
+<td></td><td></td>
+<td><input id='ipe1' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='ipe2' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='ipe3' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='ipe4' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input type='hidden' name='ip' id='ip' value='0'/> </td> <!-- This will be passed by form -->
+</tr>
+
+<tr>
+<td>Current netmask</td>
+<td></td><td></td>
+<td><input id='nete1' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='nete2' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='nete3' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='nete4' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input type='hidden' name='nm' id='nm' value='0'/> </td> <!-- This will be passed by form -->
+</tr>
+<tr>
+<td>Current gateway address</td>
+<td></td><td></td>
+<td><input id='gwe1' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='gwe2' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='gwe3' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input id='gwe4' type='text' size=3 maxlength=3 value='0'/> </td>
+<td><input type='hidden' id='gw' name='gw' value='0'/> </td> <!-- This will be passed by form -->
+</tr>
+<tr align='center'>
+<td align='left'>MAC address</td>
+
+<td><input disabled='disabled' id='mace1' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace2' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace3' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace4' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace5' type='text' size=2 maxlength=2 value='0'/> </td>
+<td><input disabled='disabled' id='mace6' type='text' size=2 maxlength=2 value='0'/> </td>
+</tr>
+<tr>
+
+<input type='hidden' name='dhcp' id='dhcp' value='static'/> <!-- This will be passed by form -->
+<input type='hidden' name='inter' id='inter' value='plc0'/> <!-- This will be passed by form -->
+</table>
+
+<div class='margina'><h2>Activity statistics</h2></div>
+<table id='background-image' class='d25' style='margin-left:5%;width:500px' border='0'> <tr align='center'><td colspan='2'>RX:</td><td colspan='2'>TX:</td></tr>
+<tr><td class='d25' >Bytes</td><td class='d25' id='rb' bgcolor='lavender' align='center'></td><td>Bytes</td> <td class='d25' id = 'tb' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Packets</td><td class='d25' id='rp' bgcolor='lavender' align='center'></td><td>Packets</td> <td class='d25' id = 'tp' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Errors</td><td class='d25' id='re' bgcolor='lavender' align='center'></td><td>Errors</td> <td class='d25' id = 'te' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Drops</td><td class='d25' id='rd' bgcolor='lavender' align='center'></td><td>Drops</td> <td class='d25' id = 'td' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Fifo</td><td class='d25' id='rf' bgcolor='lavender' align='center'></td><td>Fifo</td> <td class='d25' id = 'tf' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Frame</td><td class='d25' id='rfr' bgcolor='lavender' align='center'></td><td>Colls</td> <td class='d25' id = 'tc' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Compressed</td><td class='d25' id='rc' bgcolor='lavender' align='center'></td><td>Carrier</td> <td class='d25' id = 'tcar' bgcolor='lavender' align='center'></td></tr>
+<tr><td class='d25' >Multicast</td><td class='d25' id='rm' bgcolor='lavender' align='center'></td><td>Compressed</td> <td class='d25' id = 'tcom' bgcolor='lavender' align='center'></td></tr>
+
+</table>
+<div class='margina' align='right'>
+Auto refresh<input type='checkbox' name='refresh_or_not' id='refresh_or_not' onchange='set_refresh();' checked='checked'>
+</div>
+
+
+
+
+<div class='margina'><h2>Station managment</h2></div>
+
+<div class='margina' style='background-color:#FFFFFF' align='right'>
+<table border = '0' style='margin-top:5px;margin-right:5px'>
+<tr>
+<td class='margina' style ='width:500px' align='right'><select style='width:200px' name='station' id='station'><option value='-1'>Select station</option></select></td> </tr>
+<tr>
+<td align='right'>
+<select style='width:200px' name='action' id='action'>
+<option id='-1' value='none'>Select action</option>
+<option style='color:red' id='0' value='reboot'>Reboot</option>
+<option id='1' value='get_stat'>Retrieve statistics</option>
+<option id='2' value='reset_stat'>Reset statistics</option>
+<option id='3' value='get_device_info'>Retrieve device info</option>
+<option id='4' value='get_link'>Retrieve link quality</option>
+</select>
+</td>
+</tr>
+<tr>
+<td colspan='2' align = 'right'><input type='button' value='Proceed' onclick='javascript:perform_eoc_action();'/></td>
+</tr>
+</table>
+</div>
+<div class='margina' align='right'>
+<pre ></pre>
+<input type='button' value='Refresh' onclick='javascript:refresh();'>
+<input type='submit' onclick='javascript:joinadd();' value='Save configuration' onmouseover='javascript:showTip();' onmouseout='javascript:clearTip();'/>
+<input type='submit' value='Restart the modem' onclick='javascript:reboot_flag();'/>
+<pre id ='tooltip'></pre>
+</div>
+
+
+
+
+
+
+</form>
+
+
+
+
+
+<iframe id='stat_iframe' name='stat_iframe' src ="stat.asp?interface=plc0" frameborder='0' height='5'>
+ <p>Your browser does not support iframes.</p>
+</iframe>
+
+</body>
+</html> \ No newline at end of file
diff --git a/cleopatre/application/spidgoahead/web/savecfg.asp b/cleopatre/application/spidgoahead/web/savecfg.asp
new file mode 100644
index 0000000000..0947eb58dc
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/savecfg.asp
@@ -0,0 +1,24 @@
+<html>
+<!- Copyright (c) Go Ahead Software Inc., 2000-2000. All Rights Reserved. ->
+<head>
+<title>Save the User Configuration</title>
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+<% language=javascript %>
+</head>
+
+<body>
+<h1>Permanently save the user configuration</h1>
+<form action=/goform/SaveUserManagement method=POST>
+
+<table>
+<tr>
+ <td></td>
+ <td ALIGN="CENTER">
+ <input type=submit name=ok value="OK" title="Save Configuration"> <input type=submit name=ok value="Cancel"></td>
+</tr>
+</table>
+
+</form>
+
+</body>
+</html>
diff --git a/cleopatre/application/spidgoahead/web/soft.asp b/cleopatre/application/spidgoahead/web/soft.asp
new file mode 100644
index 0000000000..25ccefc945
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/soft.asp
@@ -0,0 +1,15 @@
+<html>
+
+<head>
+<script type='text/javascript'>
+function clearConsole()
+{
+ parent.footer2.document.getElementById('error_console').innerHTML = "";
+}
+
+</script>
+</head>
+<body onload='javascript:clearConsole();'>
+
+</body>
+</html> \ No newline at end of file
diff --git a/cleopatre/application/spidgoahead/web/spidcom.gif b/cleopatre/application/spidgoahead/web/spidcom.gif
new file mode 100644
index 0000000000..6bf1629a29
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/spidcom.gif
Binary files differ
diff --git a/cleopatre/application/spidgoahead/web/stat.asp b/cleopatre/application/spidgoahead/web/stat.asp
new file mode 100644
index 0000000000..f1919f3dd2
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/stat.asp
@@ -0,0 +1,61 @@
+<html>
+<head>
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+<script type = 'text/javascript'>
+
+var RefreshID = setInterval("window.location.reload()",5000);
+
+
+
+
+function StopRefresh(){
+clearInterval(RefreshID);
+}
+
+function RestartRefresh(){
+RefreshID = setInterval("window.location.reload()",5000);
+}
+
+var stats = '<% spidcomAspGetStatistics(); %>';
+stats_array = stats.split(':');
+
+
+function validate()
+{
+
+ if(stats.indexOf("error") >= 0) // error
+ parent.parent.footer2.document.getElementById('error_console').innerHTML += "<code>Statistics : error condition</code><br>";
+ //else
+ //parent.parent.footer2.document.getElementById('error_console').innerHTML += "<code>Statistics : OK</code><br>";
+
+}
+
+
+function fill_it(){
+
+parent.document.getElementById('rb').innerHTML = stats_array[0];
+parent.document.getElementById('tb').innerHTML = stats_array[1];
+parent.document.getElementById('rp').innerHTML = stats_array[2];
+parent.document.getElementById('tp').innerHTML = stats_array[3];
+parent.document.getElementById('re').innerHTML = stats_array[4];
+parent.document.getElementById('te').innerHTML = stats_array[5];
+parent.document.getElementById('rd').innerHTML = stats_array[6];
+parent.document.getElementById('td').innerHTML = stats_array[7];
+parent.document.getElementById('rf').innerHTML = stats_array[8];
+parent.document.getElementById('tf').innerHTML = stats_array[9];
+parent.document.getElementById('rfr').innerHTML = stats_array[10];
+parent.document.getElementById('tc').innerHTML = stats_array[11];
+parent.document.getElementById('rc').innerHTML = stats_array[12];
+parent.document.getElementById('tcar').innerHTML = stats_array[13];
+parent.document.getElementById('rm').innerHTML = stats_array[14];
+parent.document.getElementById('tcom').innerHTML = stats_array[15];
+
+}
+
+</script>
+
+</head>
+<body onload='javascript:validate();fill_it();'>
+</body>
+
+</html> \ No newline at end of file
diff --git a/cleopatre/application/spidgoahead/web/style/doc.css b/cleopatre/application/spidgoahead/web/style/doc.css
new file mode 100644
index 0000000000..94147aac97
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/style/doc.css
@@ -0,0 +1,42 @@
+body {
+ font-family: Arial, Helvetica, sans-serif; font-size: 9pt;
+; margin-right: 5px; margin-left: 10px
+}
+p {
+; margin-left: 30px
+; font-family: Arial, Helvetica, sans-serif; color: #333333
+; font-size: 9pt
+}
+h3 {
+ font-size: 10pt; font-weight: bold;
+; margin-top: 8pt
+; font-family: Arial, Helvetica, sans-serif; color: #0066CC
+}
+h2 {
+ font-size: 11pt; font-weight: bold;
+; margin-top: 8pt
+; font-family: Arial, Helvetica, sans-serif; color: #0066CC
+}
+h1 {
+ font-size: 16pt; font-weight: bold;
+; font-family: Arial, Helvetica, sans-serif; color: #0066CC
+}
+pre {
+ background-color: #EEEEEE;
+; color: #0A0A8C; font-family: "Courier New", Courier, mono; font-size: 9pt
+; padding-left: 31px
+; padding-top: 5px; padding-right: 20px; padding-bottom: 5px
+}
+li {
+; font-family: Arial, Helvetica, sans-serif; color: #333333
+; margin-left: 30px
+; font-size: 9pt
+}
+td { font-family: Arial, Helvetica, sans-serif; font-size: 9pt; color: #333333; vertical-align: top}
+
+ul { ; font-family: Arial, Helvetica, sans-serif; color: #333333; margin-left: 30px; font-size: 9pt }
+.titleLeft { font-family: Arial, Helvetica, sans-serif; font-size: 10pt; font-weight: bold; width: 33%; color: #0066CC; text-align: left}
+.titleCenter { font-family: Arial, Helvetica, sans-serif; font-size: 10pt; font-weight: bold; width: 33%; color: #0066CC; text-align: center}
+.titleRight { font-family: Arial, Helvetica, sans-serif; font-size: 10pt; font-weight: bold; width: 33%; color: #0066CC; text-align: right}
+table { padding-left: 30px}
+.apiTitle { padding-left: 0px}
diff --git a/cleopatre/application/spidgoahead/web/style/help.htm b/cleopatre/application/spidgoahead/web/style/help.htm
new file mode 100644
index 0000000000..7b1e028131
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/style/help.htm
@@ -0,0 +1,40 @@
+<style>
+<!- Copyright (c) Go Ahead Software Inc., 1999-2000. All Rights Reserved. ->
+<!--
+ body {
+ font-family: Veranda, Arial, Helvetica;
+ font-size: 12pt;
+ color: #0000A0;
+ }
+ p {
+ font-family: Arial, Helvetica;
+ font-size: 12pt;
+ color: #0000A0;
+ }
+ h2 {
+ font-family: Arial, Helvetica;
+ font-size: 14pt;
+ font-weight: bold;
+ color: #0000A0;
+ margin: 8pt 0pt 0pt 0pt;
+ padding: 0 0 0 0;
+ }
+ h1 {
+ font-family: Arial, Helvetica;
+ font-size: 16pt;
+ font-weight: bold;
+ color: #0000A0;
+ margin: 8pt 0pt 0pt 0pt;
+ padding: 0 0 0 0;
+ }
+ td {
+ font-family: Arial, Helvetica;
+ font-size: 9pt;
+ color: #0000A0;
+ }
+ a:link {
+ text-decoration: underline;
+ color: #0000A0;
+ }
+-->
+</style>
diff --git a/cleopatre/application/spidgoahead/web/style/menu.htm b/cleopatre/application/spidgoahead/web/style/menu.htm
new file mode 100644
index 0000000000..63e64fe96c
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/style/menu.htm
@@ -0,0 +1,55 @@
+<style>
+<!- Copyright (c) Go Ahead Software Inc., 1999-2000. All Rights Reserved. ->
+<!--
+ body {
+ font-family: Veranda, Arial, Helvetica;
+ font-size: 9pt;
+ color: #ffffff;
+ background-graphic: graphics/stripe.jpg;
+ }
+ p {
+ font-family: Arial, Helvetica;
+ font-size: 9pt;
+ color: #ffffff;
+ }
+ h1 {
+ font-family: Arial, Helvetica;
+ font-size: 11pt;
+ font-weight: bold;
+ color: #ffffff;
+ margin-top: 8pt;
+ margin-bottom: 4pt
+ }
+ h2 {
+ font-family: Arial, Helvetica;
+ font-size: 9pt;
+ font-weight: bold;
+ color: #ffffff;
+ margin-top: 8pt;
+ margin-bottom: 4pt;
+ padding-left: 12
+ }
+ h3 {
+ font-family: Arial, Helvetica;
+ font-size: 9pt;
+ font-weight: normal;
+ color: #ffffff;
+ margin: 0pt 0pt 0pt 0pt;
+ padding: 0 0 0 30;
+ }
+h4 { font-family: Arial, Helvetica; font-size: 9pt; font-weight: normal; color: #ffffff; ; margin-top: 0pt; margin-right: 0pt; margin-bottom: 0pt; margin-left: 15px; padding-top: 0; padding-right: 0; padding-bottom: 0; padding-left: 30}
+ a:link {
+ text-decoration: underline;
+ color: #33ccff;
+ }
+ a:visited {
+ text-decoration: underline;
+ color: #3399ff;
+ }
+ a:active {
+ text-decoration: underline;
+ color: #ccffff;
+ }
+-->
+</style>
+
diff --git a/cleopatre/application/spidgoahead/web/style/normal_ws.css b/cleopatre/application/spidgoahead/web/style/normal_ws.css
new file mode 100644
index 0000000000..3c30e6bf22
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/style/normal_ws.css
@@ -0,0 +1,48 @@
+body {
+ font-family: Arial, Helvetica, sans-serif; font-size: 9pt;
+}
+p {
+ font-size: 9pt;
+; margin-left: 10px
+; font-family: Arial, Helvetica, sans-serif; color: #333333
+}
+h1 {
+ font-size: 14pt; font-weight: bold;
+; margin-left: 10px
+; font-family: Arial, Helvetica, sans-serif; color: #0066CC
+}
+h2 {
+ font-size: 12pt; font-weight: bold;
+; margin-left: 10px
+; font-family: Arial, Helvetica, sans-serif; color: #0066CC
+}
+h3 {
+ font-size: 10pt; font-weight: bold;
+; margin-left: 10px
+; font-family: Arial, Helvetica, sans-serif; color: #0066CC
+}
+pre {
+line-height: normal;
+; color: #0A0A8C
+; font-family: "Courier New", Courier, mono; font-size: 9pt; margin-left: 10px; background-color: #EEEEEE; padding-left: 20px
+; padding-top: 5px; padding-bottom: 5px; padding-right: 20px
+}
+td {
+ font-size: 9pt;
+ font-family: Arial, Helvetica, sans-serif; color: #333333;
+ padding-top:2px;
+ padding-bottom:2px;
+ padding-left:3px;
+ padding-right:3px;
+}
+a { text-decoration: none;}
+
+a:focus { outline: none; }
+
+table { margin-left: 0px;
+ background: url('/pattern.png');
+}
+ol { margin-left: 5px; color: #0066CC; font-family: Arial, Helvetica, sans-serif; font-size: 9pt}
+li a:hover { color: #0066C0;}
+ul { font-size: 9pt; margin-left: 5px; font-family: Arial, Helvetica, sans-serif; color: #0066CC}
+h4 { font-size: 9pt; font-weight: bold; margin-left: 10px ; font-family: Arial, Helvetica, sans-serif; color: #0066CC}
diff --git a/cleopatre/application/spidgoahead/web/style/option.htm b/cleopatre/application/spidgoahead/web/style/option.htm
new file mode 100644
index 0000000000..7b1406af59
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/style/option.htm
@@ -0,0 +1,28 @@
+<style>
+<!- Copyright (c) Go Ahead Software Inc., 1999-2000. All Rights Reserved. ->
+<!--
+ body {
+ font-family: MS Sans Serif, Arial, Helvetica;
+ font-size: 9pt;
+ color: #0000A0;
+ }
+ p {
+ font-family: MS Sans Serif, Arial, Helvetica;
+ font-size: 9pt;
+ color: #0000A0;
+ }
+ h2 {
+ font-family: Arial, Helvetica;
+ font-size: 9pt;
+ font-weight: bold;
+ color: #0000A0;
+ margin: 0pt 0pt 0pt 0pt;
+ padding: 0 0 0 0;
+ }
+ td {
+ font-family: MS Sans Serif, Arial, Helvetica;
+ font-size: 9pt;
+ color: #0000A0;
+ }
+-->
+</style>
diff --git a/cleopatre/application/spidgoahead/web/um.htm b/cleopatre/application/spidgoahead/web/um.htm
new file mode 100644
index 0000000000..e4d5108b5b
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/um.htm
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>Company Overview</title>
+<link rel="stylesheet" href="style/normal_ws.css">
+</head>
+
+<BODY>
+<table Width="550"><tr><td>
+<H1>User Management Functions</H1><H2>Add User </H2><P>The Add a User option calls up the URL, &quot;<A HREF="adduser.asp">adduser.asp</A>&quot;. This Add a User data entry pane contains these fields:</P><BLOCKQUOTE><P> <B>User ID</B> - enter the ID of the user in this text field<BR><B>Group</B> - select a user group from this list for the user<BR><B>Enabled</B> -ensure that the Check box is checked to enable the user<BR><B>Password</B> - secure text field (displays asterisks)<BR><B>Confirm </B>- secure text field </P></BLOCKQUOTE><H2>Delete User </H2><P>The Delete a User option calls up the URL, <A HREF="deluser.asp">&quot;deluser.asp</A>&quot;. The Delete a User data entry pane provides a list of all added users. Use this list to select the user you wish to delete.</P><P>Clicking OK deletes the selected user and updates the user list accordingly.</P><H2>Add Group </H2><P>The Add a User Group option calls up the URL, &quot;<A HREF="addgroup.asp">addgroup.asp</A>&quot;. The Add Group data entry pane contains these fields:</P><BLOCKQUOTE><P> <B>Group Name</B> - enter the name of the group in this text field.<BR> <B>Privilege</B> - select privileges from this list to assign to the group (holding down the control key allows you to select multiple privileges)<BR> <B>Access Method</B> - select the default access method from the provided list that will be assigned to the group<BR> <B>Enabled</B> - ensure that the check box is selected to enable the user group</P></BLOCKQUOTE><H2>Delete Group </H2><P>The Delete a User Group option calls up the URL, &quot;<A HREF="delgroup.asp">delgroup.asp</A>&quot;. The Delete Group data-entry pane provides a list of all added user groups. Use this list to select the user group you wish to delete. </P><P> Clicking the OK button deletes the selected user group then updates the user group list accordingly.</P><H2> Add Access Limit </H2><P>The Add an Access Limit option calls up the URL, &quot;<A HREF="addlimit.asp">addlimit.asp</A>&quot;. The Add Access Limit data entry pane box provides the following fields:</P><BLOCKQUOTE><P> <B>URL</B> - enter the URL path to the page or the directory in this text field<BR><B>Group</B> - select the group (optional) to assign to the URL<BR> <B>Access Method</B> - select the access method from this list to assign to the URL. If a group is selected, this item is ignored. <BR> <B>Secure</B> - select this check box if you require data encryption for this URL. This item will be ignored unless SSL is installed. </P></BLOCKQUOTE><H2>Delete Access Limit </H2><P>The Delete an Access Limit option calls up the URL, &quot;<A HREF="dellimit.asp">dellimit.asp</A>&quot;. The Delete Group data entry pane provides a list of all access limit that have been assigned to this URL. Use this list to select the access limit you wish to delete <B> </B></P><P>Clicking OK deletes the selected access limits then updates the list of existing access limits.</P><H2>Save the User Configuration</H2><P>This option calls up the URL, <A HREF="savecfg.asp">savecfg.asp</A>. The Permanently Save the User Configuration data entry pane contains no data fields. Clicking OK saves the current User Management configuration. If the user configuration data had been saved previously, it will be overwritten. Data is saved in the file umconfig.txt. </P><H2>Restore the User Configuration</H2><P>This option calls up the URL, &quot;<A HREF="loadcfg.asp">loadcfg.asp</A>&quot;. The Re-load the Permanently Stored User Configuration data entry pane contains no data fields. Clicking OK replaces the current User Management configuration with the user configuration data previously saved. If no data has been saved, then the user configuration data will be deleted. Data is saved in the file &quot;umconfig.txt&quot;.</P><H2>&nbsp; </H2></td></tr></table>
+</BODY></HTML>
diff --git a/cleopatre/application/spidgoahead/web/wifi0.asp b/cleopatre/application/spidgoahead/web/wifi0.asp
new file mode 100644
index 0000000000..bd14498d4c
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/wifi0.asp
@@ -0,0 +1,377 @@
+<html>
+
+<head>
+ <title>SPC 300 - SPiDCOM</title>
+
+<link rel="stylesheet" href="style/normal_ws.css" type="text/css">
+
+<style type = 'text/css'>
+div.margina,table.margina{
+ margin-left:5%;
+ width:500px;
+ background-color:#F5F5F5;
+}
+td{
+ padding-left:5px;
+}
+
+input[type='text']{
+ margin-left:auto;
+ margin-right:auto;
+ text-align:center;
+}
+input[disabled='disabled'] {
+ background-color:BBBBBB;
+ color:white;
+ cursor:default;
+}
+body{
+ background-image: url("/spidcom.gif");
+ background-repeat: no-repeat;
+ background-attachment: fixed;
+ background-position: center center;
+}
+
+</style>
+
+<script type='text/javascript'>
+
+var wifi_data = <% spidcomAspGetWifiData('wifi0'); %>
+var ip_data = <% spidcomAspGetIpData('br0'); %>
+
+var wifi_data_array = wifi_data.split(':');
+var ip_data_array = ip_data.split(':');
+
+function validate()
+{
+ parent.footer2.document.getElementById('error_console').innerHTML = "";
+
+}
+
+
+function showTip(){
+document.getElementById('tooltip').innerHTML = "You must restart modem for the changes to take effect";
+}
+
+function clearTip(){
+document.getElementById('tooltip').innerHTML = "";
+}
+
+function reboot_flag()
+{
+ document.getElementById('reboot').value = 'yes';
+}
+
+function refresh(){
+ parent.content.location.reload();
+}
+
+function set_refresh(){
+
+if (document.getElementById('refresh_or_not').checked)
+ document.getElementById("stat_iframe").contentWindow.RestartRefresh();
+else
+ document.getElementById('stat_iframe').contentWindow.StopRefresh();
+}
+
+function change_maxlength(){
+
+var format = document.getElementById('select_wep_format').options[document.getElementById('select_wep_format').selectedIndex].value;
+var bit = document.getElementById('select_key1_bit').options[document.getElementById('select_key1_bit').selectedIndex].value;
+
+if(format == '1' && bit == '64'){
+ document.getElementById('wep_key1').value = document.getElementById('wep_key1').value.substr(0, 5);
+ document.getElementById('wep_key1').maxLength = 5;
+ document.getElementById('wep_key1_format').innerHTML = "5 ASCII characters";
+}else if(format == '0' && bit == '64'){
+ document.getElementById('wep_key1').value = document.getElementById('wep_key1').value.substr(0, 10);
+ document.getElementById('wep_key1').maxLength = 10;
+ document.getElementById('wep_key1_format').innerHTML = "10 hexadecimal characters";
+}else if(format == '1' && bit == '128'){
+ document.getElementById('wep_key1').value = document.getElementById('wep_key1').value.substr(0, 13);
+ document.getElementById('wep_key1').maxLength = 13;
+ document.getElementById('wep_key1_format').innerHTML = "13 ASCII characters";
+}else if(format == '0' && bit == '128'){
+ document.getElementById('wep_key1').value = document.getElementById('wep_key1').value.substr(0, 26);
+ document.getElementById('wep_key1').maxLength = 26;
+ document.getElementById('wep_key1_format').innerHTML = "26 hexadecimal characters";
+}
+
+}
+
+function checkMainForm(){
+ return true;
+}
+
+
+function joinadd(){
+
+}
+
+function fill_ip_data(){
+document.getElementById('ip').value = ip_data_array[1];
+
+document.getElementById('SSID').value = wifi_data_array[0];
+document.getElementById('select_channel').value = wifi_data_array[1];
+document.getElementById('select_mode').value = wifi_data_array[2];
+
+document.getElementById('wpapsk_key').value = wifi_data_array[5];
+document.getElementById('wpapsk_rekey').value = '0';
+document.getElementById('wpa_rekey').value = '0';
+document.getElementById('select_wpapsk_auth').value = 'WPAPSKWPA2PSK';
+document.getElementById('select_wpapsk_encrypt').value = 'TKIPAES';
+document.getElementById('select_wpa_auth').value = 'WPA1WPA2';
+document.getElementById('select_wpa_encrypt').value = 'TKIPAES';
+
+if(wifi_data_array[4] == 'NONE'){
+ document.mainForm.security_radiobutton[0].checked = 'checked';
+}
+
+if(wifi_data_array[3] == 'WPAPSK' || wifi_data_array[3] == 'WPA2PSK' || wifi_data_array[3] == 'WPAPSKWPA2PSK'){
+ document.mainForm.security_radiobutton[1].checked = 'checked';
+ document.getElementById('select_wpapsk_auth').value = wifi_data_array[3];
+ document.getElementById('select_wpapsk_encrypt').value = wifi_data_array[4];
+ document.getElementById('wpapsk_rekey').value = wifi_data_array[6];
+}
+
+if(wifi_data_array[3] == 'WPA' || wifi_data_array[3] == 'WPA2' || wifi_data_array[3] == 'WPA1WPA2'){
+ document.mainForm.security_radiobutton[2].checked = 'checked';
+ document.getElementById('select_wpa_auth').value = wifi_data_array[3];
+ document.getElementById('select_wpa_encrypt').value = wifi_data_array[4];
+ document.getElementById('wpa_rekey').value = wifi_data_array[6];
+ document.getElementById('wpa_radius_server').value = wifi_data_array[7];
+ document.getElementById('wpa_radius_port').value = wifi_data_array[8];
+ document.getElementById('wpa_radius_key').value = wifi_data_array[9];
+}
+
+if(wifi_data_array[4] == 'WEP'){
+ document.mainForm.security_radiobutton[3].checked = 'checked';
+ document.getElementById('wep_key1').value = wifi_data_array[11];
+ document.getElementById('select_wep_auth').value = wifi_data_array[3];
+ if(wifi_data_array[11].length==5){
+ document.getElementById('select_wep_format').value = '1';
+ document.getElementById('select_key1_bit').value = '64';
+ }else if(wifi_data_array[11].length==10){
+ document.getElementById('select_wep_format').value = '0';
+ document.getElementById('select_key1_bit').value = '64';
+ }else if(wifi_data_array[11].length==13){
+ document.getElementById('select_wep_format').value = '1';
+ document.getElementById('select_key1_bit').value = '128';
+ }else if(wifi_data_array[11].length==26){
+ document.getElementById('select_wep_format').value = '0';
+ document.getElementById('select_key1_bit').value = '128';
+ }
+
+ change_maxlength();
+}
+}
+
+</script>
+</head>
+
+
+<body onload='javascript:validate();fill_ip_data();'>
+<!-- Settings name -->
+<div class='margina' > <h2>Wifi configuration</h2>
+</div>
+
+<!-- Main form -->
+
+<form name='mainForm' action='/goform/formActionWifi' target='_self' method='GET' onSubmit='return checkMainForm();'>
+
+<input type='hidden' name='reboot' id='reboot' value='no'/> <!-- This will be passed by form -->
+<input type='hidden' name='inter' id='inter' value='wifi0'/> <!-- This will be passed by form -->
+
+
+<table width="564" border='0' class='margina' style='background-color:#FFFFFF' name = 'WifiConfig'>
+<tr>
+<td width="142"><h3>Network configuration</h3></td><td width="23"><input type='hidden' name='ip' id='ip' value='0'/> </td> <!-- This will be passed by form -->
+</tr>
+<tr>
+<td>SSID</td>
+<td colspan="5"><input name='SSID' id='SSID' type='text' size=32 maxlength=32 value='SSID_DEFAULT'/></td>
+</tr>
+<tr>
+<td>Wireless Mode</td>
+<td colspan="5">
+ <select name='select_mode' id='select_mode'>
+ <option value="0">B/G mixed</option>
+ <option value="1">B only</option>
+ <option value="2">A only</option>
+ <option value="4">G only</option>
+ <option value="6">N only</option>
+ <option value="7">G/N mixed</option>
+ <option value="8">A/N mixed</option>
+ <option value="9" selected>B/G/N mixed</option>
+ <option value="10">A/G/N mixed</option>
+ <option value="11">N in 5G band only</option>
+ </select></td>
+</tr>
+<tr>
+<td>Channel</td>
+<td colspan="5">
+ <select name='select_channel' id='select_channel'>
+ <option value="0">auto</option>
+ <option value="1">1</option>
+ <option value="2">2</option>
+ <option value="3">3</option>
+ <option value="4">4</option>
+ <option value="5">5</option>
+ <option value="6">6</option>
+ <option value="7">7</option>
+ <option value="8">8</option>
+ <option value="9">9</option>
+ <option value="10">10</option>
+ <option value="11" selected>11</option>
+ <option value="12">12</option>
+ <option value="13">13</option>
+ <option value="14">14</option>
+ </select></td>
+</tr>
+<tr>
+<td width="142"><h3>Security configuration</h3></td>
+</tr>
+<tr>
+<td><label>
+ <input name="security_radiobutton" type="radio" value="NONE" checked='checked'>
+ No security</label></td>
+<td width="304">&nbsp;</td>
+</tr>
+<tr>
+<td><label>
+ <input name="security_radiobutton" type="radio" value="WPAPSK">
+ WPA-PSK/WPA2-PSK</label></td>
+<td>&nbsp;</td>
+</tr>
+<tr>
+<td>Authentication type </td>
+<td><label>
+ <select name="select_wpapsk_auth" id="select_wpapsk_auth">
+ <option value="WPAPSKWPA2PSK" selected>AUTO</option>
+ <option value="WPAPSK">WPA-PSK</option>
+ <option value="WPA2PSK">WPA2-PSK</option>
+ </select>
+</label></td>
+</tr>
+<tr>
+<td>Encryption type </td>
+<td><label>
+ <select name="select_wpapsk_encrypt" id="select_wpapsk_encrypt">
+ <option value="TKIPAES" selected>AUTO</option>
+ <option value="TKIP">TKIP</option>
+ <option value="AES">AES</option>
+ </select>
+</label></td>
+</tr>
+<tr>
+<td>Key</td>
+<td colspan="5"><input name='wpapsk_key' id='wpapsk_key' type='password' size=50 maxlength=63 /></td>
+</tr>
+
+<tr>
+<td></td>
+<td><div>8 ~ 63 ASCII characters</div></td>
+</tr>
+
+<tr>
+<td>Rekey interval</td>
+<td colspan="5"><input name='wpapsk_rekey' id='wpapsk_rekey' type='text' size=10 maxlength=10 value="0" />
+(sec)</td>
+</tr>
+<tr>
+<td><label>
+ <input name="security_radiobutton" type="radio" value="WPA">
+ WPA/WPA2 </label></td>
+<td>&nbsp;</td>
+</tr>
+<tr>
+<td>Authentication type </td>
+<td><label>
+ <select name="select_wpa_auth" id="select_wpa_auth">
+ <option value="WPA1WPA2" selected>AUTO</option>
+ <option value="WPA">WPA</option>
+ <option value="WPA2">WPA2</option>
+ </select>
+</label></td>
+</tr>
+<tr>
+<td>Encryption type </td>
+<td><label>
+ <select name="select_wpa_encrypt" id="select_wpa_encrypt">
+ <option value="TKIPAES" selected>AUTO</option>
+ <option value="TKIP">TKIP</option>
+ <option value="AES">AES</option>
+ </select>
+</label></td>
+</tr>
+<tr>
+<td>Radius ip</td>
+<td colspan="5"><input name='wpa_radius_server' id='wpa_radius_server' type='text' size=15 maxlength=15 /></td>
+</tr>
+<tr>
+<td>Radius port</td>
+<td colspan="5"><input name='wpa_radius_port' id='wpa_radius_port' type='text' size=5 maxlength=5 /></td>
+</tr>
+<tr>
+<td>Radius key</td>
+<td colspan="5"><input name='wpa_radius_key' id='wpa_radius_key' type='password' size=50 maxlength=64 /></td>
+</tr>
+<tr>
+<td>Rekey interval</td>
+<td colspan="5"><input name='wpa_rekey' id='wpa_rekey' type='text' size=10 maxlength=10 value="0" />
+(sec) </td>
+</tr>
+<tr>
+<td><label>
+ <input name="security_radiobutton" type="radio" value="WEP" onclick='change_maxlength();'>
+ WEP </label></td>
+<td>&nbsp;</td>
+</tr>
+<tr>
+<td>Authentication type </td>
+<td><label>
+ <select name="select_wep_auth" id="select_wep_auth">
+ <option value="WEPAUTO" selected>AUTO</option>
+ <option value="OPEN">OPEN</option>
+ <option value="SHARED">SHARED</option>
+ </select>
+</label></td>
+</tr>
+<tr>
+<td>Key type </td>
+<td><label>
+ <select name="select_wep_format" id="select_wep_format" onchange='change_maxlength();'>
+ <option value="0" selected>HEX</option>
+ <option value="1">ASCII</option>
+ </select>
+</label></td>
+</tr>
+<tr>
+<td><label>Key1:</label></td>
+<td colspan="5">
+ <input name='wep_key1' id='wep_key1' type='password' size=30 maxlength=26 />
+ <select name="select_key1_bit" id="select_key1_bit" onchange='change_maxlength();'>
+ <option value="64">64 bits</option>
+ <option value="128" selected>128 bits</option>
+ </select>
+</td>
+</tr>
+<tr>
+<td></td>
+<td><div id='wep_key1_format'></div></td>
+</tr>
+</table>
+
+<div class='margina' style='margin-top:5px' align='right'>
+<pre ></pre>
+<input type='button' value='Refresh' onclick='javascript:refresh();'>
+<input type='submit' onclick='javascript:joinadd();' value='Save configuration' onmouseover='javascript:showTip();' onmouseout='javascript:clearTip();'/>
+<input type='submit' value='Restart the modem' onclick='javascript:reboot_flag();'/>
+<pre id ='tooltip'></pre>
+</div>
+
+
+</form>
+
+</body>
+</html> \ No newline at end of file
diff --git a/cleopatre/application/spidgoahead/web/ws.gif b/cleopatre/application/spidgoahead/web/ws.gif
new file mode 100644
index 0000000000..8dd2d1dc60
--- /dev/null
+++ b/cleopatre/application/spidgoahead/web/ws.gif
Binary files differ
diff --git a/cleopatre/application/spidgoahead/webcomp.c b/cleopatre/application/spidgoahead/webcomp.c
new file mode 100644
index 0000000000..eed3795e97
--- /dev/null
+++ b/cleopatre/application/spidgoahead/webcomp.c
@@ -0,0 +1,189 @@
+/*
+ * webcomp -- Compile web pages into C source
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ *
+ * $Id: webcomp.c,v 1.3 2002/10/24 14:44:50 bporter Exp $
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * Usage: webcomp prefix filelist >webrom.c
+ *
+ * filelist is a file containing the pathnames of all web pages
+ * prefix is a path prefix to remove from all the web page pathnames
+ * webrom.c is the resulting C source file to compile and link.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+
+/**************************** Forward Declarations ****************************/
+
+static int compile(char_t *fileList, char_t *prefix);
+static void usage();
+
+/*********************************** Code *************************************/
+/*
+ * Main program for webpack test harness
+ */
+
+int gmain(int argc, char_t* argv[])
+{
+ char_t *fileList, *prefix;
+
+ fileList = NULL;
+
+ if (argc != 3) {
+ usage();
+ }
+
+ prefix = argv[1];
+ fileList = argv[2];
+
+ if (compile(fileList, prefix) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Output usage message
+ */
+
+static void usage()
+{
+ fprintf(stderr, "usage: webcomp prefix filelist >output.c\n");
+ exit(2);
+}
+
+/******************************************************************************/
+/*
+ * Compile the web pages
+ */
+
+static int compile(char_t *fileList, char_t *prefix)
+{
+ gstat_t sbuf;
+ FILE *lp;
+ time_t now;
+ char_t file[FNAMESIZE];
+ char_t *cp, *sl;
+ char buf[512];
+ unsigned char *p;
+ int j, i, len, fd, nFile;
+
+/*
+ * Open list of files
+ */
+ if ((lp = fopen(fileList, "r")) == NULL) {
+ fprintf(stderr, "Can't open file list %s\n", fileList);
+ return -1;
+ }
+
+ time(&now);
+ fprintf(stdout, "/*\n * webrom.c -- Compiled Web Pages\n *\n");
+ fprintf(stdout, " * Compiled by GoAhead WebCompile: %s */\n\n",
+ gctime(&now));
+ fprintf(stdout, "#include \"wsIntrn.h\"\n\n");
+ fprintf(stdout, "#ifndef WEBS_PAGE_ROM\n");
+ fprintf(stdout, "websRomPageIndexType websRomPageIndex[] = {\n");
+ fprintf(stdout, " { 0, 0, 0 },\n};\n");
+ fprintf(stdout, "#else\n");
+
+/*
+ * Open each input file and compile each web page
+ */
+ nFile = 0;
+ while (fgets(file, sizeof(file), lp) != NULL) {
+ if ((p = strchr(file, '\n')) || (p = strchr(file, '\r'))) {
+ *p = '\0';
+ }
+ if (*file == '\0') {
+ continue;
+ }
+ if (gstat(file, &sbuf) == 0 && sbuf.st_mode & S_IFDIR) {
+ continue;
+ }
+ if ((fd = gopen(file, O_RDONLY | O_BINARY)) < 0) {
+ fprintf(stderr, "Can't open file %s\n", file);
+ return -1;
+ }
+ fprintf(stdout, "static unsigned char page_%d[] = {\n", nFile);
+
+ while ((len = read(fd, buf, sizeof(buf))) > 0) {
+ p = buf;
+ for (i = 0; i < len; ) {
+ fprintf(stdout, " ");
+ for (j = 0; p < &buf[len] && j < 16; j++, p++) {
+ fprintf(stdout, "%3d,", *p);
+ }
+ i += j;
+ fprintf(stdout, "\n");
+ }
+ }
+ fprintf(stdout, " 0 };\n\n");
+
+ close(fd);
+ nFile++;
+ }
+ fclose(lp);
+
+/*
+ * Now output the page index
+ */
+ fprintf(stdout, "websRomPageIndexType websRomPageIndex[] = {\n");
+
+ if ((lp = fopen(fileList, "r")) == NULL) {
+ fprintf(stderr, "Can't open file list %s\n", fileList);
+ return -1;
+ }
+ nFile = 0;
+ while (fgets(file, sizeof(file), lp) != NULL) {
+ if ((p = strchr(file, '\n')) || (p = strchr(file, '\r'))) {
+ *p = '\0';
+ }
+ if (*file == '\0') {
+ continue;
+ }
+/*
+ * Remove the prefix and add a leading "/" when we print the path
+ */
+ if (strncmp(file, prefix, gstrlen(prefix)) == 0) {
+ cp = &file[gstrlen(prefix)];
+ } else {
+ cp = file;
+ }
+ while((sl = strchr(file, '\\')) != NULL) {
+ *sl = '/';
+ }
+ if (*cp == '/') {
+ cp++;
+ }
+
+ if (gstat(file, &sbuf) == 0 && sbuf.st_mode & S_IFDIR) {
+ fprintf(stdout, " { T(\"/%s\"), 0, 0 },\n", cp);
+ continue;
+ }
+ fprintf(stdout, " { T(\"/%s\"), page_%d, %d },\n", cp, nFile,
+ sbuf.st_size);
+ nFile++;
+ }
+ fclose(lp);
+
+ fprintf(stdout, " { 0, 0, 0 },\n");
+ fprintf(stdout, "};\n");
+ fprintf(stdout, "#endif /* WEBS_PAGE_ROM */\n");
+
+ fclose(lp);
+ fflush(stdout);
+ return 0;
+}
+
+/******************************************************************************/
+
diff --git a/cleopatre/application/spidgoahead/webrom.c b/cleopatre/application/spidgoahead/webrom.c
new file mode 100644
index 0000000000..66b77b2582
--- /dev/null
+++ b/cleopatre/application/spidgoahead/webrom.c
@@ -0,0 +1,16 @@
+/*
+ * webrom.c -- Compiled Web Pages
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ *
+ * $Id: webrom.c,v 1.3 2002/10/24 14:44:50 bporter Exp $
+ */
+
+#include "wsIntrn.h"
+
+websRomPageIndexType websRomPageIndex[] = {
+ { 0, 0, 0 },
+};
+
diff --git a/cleopatre/application/spidgoahead/webs.c b/cleopatre/application/spidgoahead/webs.c
new file mode 100644
index 0000000000..f21f456109
--- /dev/null
+++ b/cleopatre/application/spidgoahead/webs.c
@@ -0,0 +1,3078 @@
+/*
+ * webs.c -- GoAhead Embedded HTTP webs server
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ *
+ * $Id: webs.c,v 1.16 2003/04/11 18:02:02 bporter Exp $
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module implements an embedded HTTP/1.1 web server. It supports
+ * loadable URL handlers that define the nature of URL processing performed.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+#ifdef DIGEST_ACCESS_SUPPORT
+ #include "websda.h"
+#endif
+
+/******************************** Global Data *********************************/
+
+websStatsType websStats; /* Web access stats */
+webs_t *webs; /* Open connection list head */
+sym_fd_t websMime; /* Set of mime types */
+int websMax; /* List size */
+int websPort; /* Listen port for server */
+char_t websHost[64]; /* Host name for the server */
+char_t websIpaddr[64]; /* IP address for the server */
+char_t *websHostUrl = NULL; /* URL to access server */
+char_t *websIpaddrUrl = NULL; /* URL to access server */
+
+/*********************************** Locals ***********************************/
+/*
+ * Standard HTTP error codes
+ */
+
+websErrorType websErrors[] = {
+ { 200, T("Data follows") },
+ { 204, T("No Content") },
+ { 301, T("Redirect") },
+ { 302, T("Redirect") },
+ { 304, T("Use local copy") },
+ { 400, T("Page not found") },
+ { 401, T("Unauthorized") },
+ { 403, T("Forbidden") },
+ { 404, T("Site or Page Not Found") },
+ { 405, T("Access Denied") },
+ { 500, T("Web Error") },
+ { 501, T("Not Implemented") },
+ { 503, T("Site Temporarily Unavailable. Try again.") },
+ { 0, NULL }
+};
+
+#ifdef WEBS_LOG_SUPPORT
+static char_t websLogname[64] = T("log.txt"); /* Log filename */
+static int websLogFd; /* Log file handle */
+#endif
+
+static int websListenSock; /* Listen socket */
+static char_t websRealm[64] = T("GoAhead"); /* Realm name */
+
+static int websOpenCount = 0; /* count of apps using this module */
+
+/**************************** Forward Declarations ****************************/
+
+
+/*static char_t *websErrorMsg(int code);*/
+static int websGetInput(webs_t wp, char_t **ptext, int *nbytes);
+static int websParseFirst(webs_t wp, char_t *text);
+static void websParseRequest(webs_t wp);
+static void websSocketEvent(int sid, int mask, int data);
+static int websGetTimeSinceMark(webs_t wp);
+
+#ifdef WEBS_LOG_SUPPORT
+static void websLog(webs_t wp, int code);
+#endif
+#ifdef WEBS_IF_MODIFIED_SUPPORT
+static time_t dateParse(time_t tip, char_t *cmd);
+#endif
+
+/*********************************** Code *************************************/
+/*
+ * Open the GoAhead WebServer
+ */
+
+int websOpenServer(int port, int retries)
+{
+ websMimeType *mt;
+
+ if (++websOpenCount != 1) {
+ return websPort;
+ }
+
+ a_assert(port > 0);
+ a_assert(retries >= 0);
+
+#ifdef WEBS_PAGE_ROM
+ websRomOpen();
+#endif
+
+ webs = NULL;
+ websMax = 0;
+/*
+ * Create a mime type lookup table for quickly determining the content type
+ */
+ websMime = symOpen(WEBS_SYM_INIT * 4);
+ a_assert(websMime >= 0);
+ for (mt = websMimeList; mt->type; mt++) {
+ symEnter(websMime, mt->ext, valueString(mt->type, 0), 0);
+ }
+
+/*
+ * Open the URL handler module. The caller should create the required
+ * URL handlers after calling this function.
+ */
+ if (websUrlHandlerOpen() < 0) {
+ return -1;
+ }
+ websFormOpen();
+
+#ifdef WEBS_LOG_SUPPORT
+/*
+ * Optional request log support
+ */
+ websLogFd = gopen(websLogname, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY,
+ 0666);
+ a_assert(websLogFd >= 0);
+#endif
+
+ return websOpenListen(port, retries);
+}
+
+/******************************************************************************/
+/*
+ * Close the GoAhead WebServer
+ */
+
+void websCloseServer()
+{
+ webs_t wp;
+ int wid;
+
+ if (--websOpenCount > 0) {
+ return;
+ }
+
+/*
+ * Close the listen handle first then all open connections.
+ */
+ websCloseListen();
+
+/*
+ * Close each open browser connection and free all resources
+ */
+ for (wid = websMax; webs && wid >= 0; wid--) {
+ if ((wp = webs[wid]) == NULL) {
+ continue;
+ }
+ socketCloseConnection(wp->sid);
+ websFree(wp);
+ }
+
+#ifdef WEBS_LOG_SUPPORT
+ if (websLogFd >= 0) {
+ close(websLogFd);
+ websLogFd = -1;
+ }
+#endif
+
+#ifdef WEBS_PAGE_ROM
+ websRomClose();
+#endif
+ symClose(websMime);
+ websFormClose();
+ websUrlHandlerClose();
+}
+
+/******************************************************************************/
+/*
+ * Open the GoAhead WebServer listen port
+ */
+
+int websOpenListen(int port, int retries)
+{
+ int i, orig;
+
+ a_assert(port > 0);
+ a_assert(retries >= 0);
+
+ orig = port;
+/*
+ * Open the webs webs listen port. If we fail, try the next port.
+ */
+ for (i = 0; i <= retries; i++) {
+ websListenSock = socketOpenConnection(NULL, port, websAccept, 0);
+ if (websListenSock >= 0) {
+ break;
+ }
+ port++;
+ }
+ if (i > retries) {
+ error(E_L, E_USER, T("Couldn't open a socket on ports %d - %d"),
+ orig, port - 1);
+ return -1;
+ }
+
+/*
+ * Determine the full URL address to access the home page for this web server
+ */
+ websPort = port;
+ bfreeSafe(B_L, websHostUrl);
+ bfreeSafe(B_L, websIpaddrUrl);
+ websIpaddrUrl = websHostUrl = NULL;
+
+ if (port == 80) {
+ websHostUrl = bstrdup(B_L, websHost);
+ websIpaddrUrl = bstrdup(B_L, websIpaddr);
+ } else {
+ fmtAlloc(&websHostUrl, WEBS_MAX_URL + 80, T("%s:%d"), websHost, port);
+ fmtAlloc(&websIpaddrUrl, WEBS_MAX_URL + 80, T("%s:%d"),
+ websIpaddr, port);
+ }
+ trace(0, T("webs: Listening for HTTP requests at address %s\n"),
+ websIpaddrUrl);
+
+ return port;
+}
+
+/******************************************************************************/
+/*
+ * Close webs listen port
+ */
+
+void websCloseListen()
+{
+ if (websListenSock >= 0) {
+ socketCloseConnection(websListenSock);
+ websListenSock = -1;
+ }
+ bfreeSafe(B_L, websHostUrl);
+ bfreeSafe(B_L, websIpaddrUrl);
+ websIpaddrUrl = websHostUrl = NULL;
+}
+
+/******************************************************************************/
+/*
+ * Accept a connection
+ */
+
+int websAccept(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;
+ }*/
+
+/*
+ * Arrange for websSocketEvent to be called when read data is available
+ */
+ socketCreateHandler(sid, SOCKET_READABLE, websSocketEvent, (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;
+}
+
+/******************************************************************************/
+/*
+ * 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 websSocketEvent(int sid, int mask, int iwp)
+{
+ webs_t wp;
+
+ wp = (webs_t) iwp;
+ a_assert(wp);
+
+ if (! websValid(wp)) {
+ return;
+ }
+
+ if (mask & SOCKET_READABLE) {
+ websReadEvent(wp);
+ }
+ if (mask & SOCKET_WRITABLE) {
+ if (websValid(wp) && wp->writeSocket) {
+ (*wp->writeSocket)(wp);
+ }
+ }
+}
+
+/******************************************************************************/
+/*
+ * The webs read handler. This is the primary read event loop. It uses a
+ * state machine to track progress while parsing the HTTP request.
+ * Note: we never block as the socket is always in non-blocking mode.
+ */
+
+void websReadEvent(webs_t wp)
+{
+ char_t *text;
+ int rc, nbytes, len, done, fd;
+
+ a_assert(wp);
+ a_assert(websValid(wp));
+
+ websSetTimeMark(wp);
+
+/*
+ * Read as many lines as possible. socketGets is called to read the header
+ * and socketRead is called to read posted data.
+ */
+ text = NULL;
+ fd = -1;
+ for (done = 0; !done; ) {
+ if (text) {
+ bfree(B_L, text);
+ text = NULL;
+ }
+
+/*
+ * Get more input into "text". Returns 0, if more data is needed
+ * to continue, -1 if finished with the request, or 1 if all
+ * required data is available for current state.
+ */
+ while ((rc = websGetInput(wp, &text, &nbytes)) == 0) {
+ ;
+ }
+
+/*
+ * websGetInput returns -1 if it finishes with the request
+ */
+ if (rc < 0) {
+ break;
+ }
+
+/*
+ * This is the state machine for the web server.
+ */
+ switch(wp->state) {
+ case WEBS_BEGIN:
+/*
+ * Parse the first line of the Http header
+ */
+ if (websParseFirst(wp, text) < 0) {
+ done++;
+ break;
+ }
+ wp->state = WEBS_HEADER;
+ break;
+
+ case WEBS_HEADER:
+/*
+ * Store more of the HTTP header. As we are doing line reads, we
+ * need to separate the lines with '\n'
+ */
+ if (ringqLen(&wp->header) > 0) {
+ ringqPutStr(&wp->header, T("\n"));
+ }
+ ringqPutStr(&wp->header, text);
+ break;
+
+ case WEBS_POST_CLEN:
+/*
+ * POST request with content specified by a content length.
+ * If this is a CGI request, write the data to the cgi stdin.
+ * socketGets was used to get the data and it strips \n's so
+ * add them back in here.
+ */
+#ifndef __NO_CGI_BIN
+ if (wp->flags & WEBS_CGI_REQUEST) {
+ if (fd == -1) {
+ fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY,
+ 0666);
+ }
+ gwrite(fd, text, gstrlen(text));
+ /*
+ * NOTE that the above comment is wrong -- if the content length
+ * is set, websGetInput() does NOT use socketGets(), it uses
+ * socketRead(), so the line below that adds an additional newline
+ * is destructive.
+ */
+ /*gwrite(fd, T("\n"), sizeof(char_t));*/
+/*
+ * Line removed as per BUG02488
+ *
+ nbytes += 1;
+ */
+ } else
+#endif
+ if (wp->query) {
+ if (wp->query[0] && !(wp->flags & WEBS_POST_DATA)) {
+/*
+ * Special case where the POST request also had query data
+ * specified in the URL, ie. url?query_data. In this case
+ * the URL query data is separated by a '&' from the posted
+ * query data.
+ */
+ len = gstrlen(wp->query);
+ wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
+ 2) * sizeof(char_t));
+ wp->query[len++] = '&';
+ gstrcpy(&wp->query[len], text);
+
+ } else {
+/*
+ * The existing query data came from the POST request so just
+ * append it.
+ */
+ if (text != NULL)
+ {
+ len = gstrlen(wp->query);
+ wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
+ 1) * sizeof(char_t));
+ if (wp->query) {
+ gstrcpy(&wp->query[len], text);
+ }
+ }
+ }
+
+ } else {
+ wp->query = bstrdup(B_L, text);
+ }
+/*
+ * Calculate how much more post data is to be read.
+ */
+ wp->flags |= WEBS_POST_DATA;
+ wp->clen -= nbytes;
+ if (wp->clen > 0) {
+ if (nbytes > 0) {
+ break;
+ }
+ done++;
+ break;
+ }
+/*
+ * No more data so process the request, (but be sure to close
+ * the input file first!).
+ */
+ if (fd != -1) {
+ gclose (fd);
+ fd = -1;
+ }
+ websUrlHandlerRequest(wp);
+ done++;
+ break;
+
+ case WEBS_POST:
+/*
+ * POST without content-length specification
+ * If this is a CGI request, write the data to the cgi stdin.
+ * socketGets was used to get the data and it strips \n's so
+ * add them back in here.
+ */
+
+#ifndef __NO_CGI_BIN
+ if (wp->flags & WEBS_CGI_REQUEST) {
+ if (fd == -1) {
+ fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY,
+ 0666);
+ }
+ gwrite(fd, text, gstrlen(text));
+ gwrite(fd, T("\n"), sizeof(char_t));
+ } else
+#endif
+ if (wp->query && *wp->query && !(wp->flags & WEBS_POST_DATA)) {
+ len = gstrlen(wp->query);
+ wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
+ 2) * sizeof(char_t));
+ if (wp->query) {
+ wp->query[len++] = '&';
+ gstrcpy(&wp->query[len], text);
+ }
+
+ } else {
+ wp->query = bstrdup(B_L, text);
+ }
+ wp->flags |= WEBS_POST_DATA;
+ done++;
+ break;
+
+ default:
+ websError(wp, 404, T("Bad state"));
+ done++;
+ break;
+ }
+ }
+
+ if (fd != -1) {
+ fd = gclose (fd);
+ }
+
+ if (text) {
+ bfree(B_L, text);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Get input from the browser. Return TRUE (!0) if the request has been
+ * handled. Return -1 on errors or if the request has been processed,
+ * 1 if input read, and 0 to instruct the caller to call again for more input.
+ *
+ * Note: socketRead will Return the number of bytes read if successful. This
+ * may be less than the requested "bufsize" and may be zero. It returns -1 for
+ * errors. It returns 0 for EOF. Otherwise it returns the number of bytes
+ * read. Since this may be zero, callers should use socketEof() to
+ * distinguish between this and EOF.
+ */
+
+static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes)
+{
+ char_t *text;
+ char buf[WEBS_SOCKET_BUFSIZ+1];
+ int nbytes, len, clen;
+
+ a_assert(websValid(wp));
+ a_assert(ptext);
+ a_assert(pnbytes);
+
+ *ptext = text = NULL;
+ *pnbytes = 0;
+
+/*
+ * If this request is a POST with a content length, we know the number
+ * of bytes to read so we use socketRead().
+ */
+ if (wp->state == WEBS_POST_CLEN) {
+ len = (wp->clen > WEBS_SOCKET_BUFSIZ) ? WEBS_SOCKET_BUFSIZ : wp->clen;
+ } else {
+ len = 0;
+ }
+
+ if (len > 0) {
+
+#ifdef WEBS_SSL_SUPPORT
+ if (wp->flags & WEBS_SECURE) {
+ nbytes = websSSLRead(wp->wsp, buf, len);
+ } else {
+ nbytes = socketRead(wp->sid, buf, len);
+ }
+#else
+ nbytes = socketRead(wp->sid, buf, len);
+#endif
+ if (nbytes < 0) { /* Error */
+ websDone(wp, 0);
+ return -1;
+
+ } else if (nbytes == 0) { /* EOF or No data available */
+ return -1;
+
+ } else { /* Valid data */
+/*
+ * Convert to UNICODE if necessary. First be sure the string
+ * is NULL terminated.
+ */
+ buf[nbytes] = '\0';
+ if ((text = ballocAscToUni(buf, nbytes)) == NULL) {
+ websError(wp, 503, T("Insufficient memory"));
+ return -1;
+ }
+ }
+
+ } else {
+#ifdef WEBS_SSL_SUPPORT
+ if (wp->flags & WEBS_SECURE) {
+ nbytes = websSSLGets(wp->wsp, &text);
+ } else {
+ nbytes = socketGets(wp->sid, &text);
+ }
+#else
+ nbytes = socketGets(wp->sid, &text);
+#endif
+
+ if (nbytes < 0) {
+ int eof;
+/*
+ * Error, EOF or incomplete
+ */
+#ifdef WEBS_SSL_SUPPORT
+ if (wp->flags & WEBS_SECURE) {
+/*
+ * If state is WEBS_BEGIN and the request is secure, a -1 will
+ * usually indicate SSL negotiation
+ */
+ if (wp->state == WEBS_BEGIN) {
+ eof = 1;
+ } else {
+ eof = websSSLEof(wp->wsp);
+ }
+ } else {
+ eof = socketEof(wp->sid);
+ }
+#else
+ eof = socketEof(wp->sid);
+#endif
+
+ if (eof) {
+/*
+ * If this is a post request without content length, process
+ * the request as we now have all the data. Otherwise just
+ * close the connection.
+ */
+ if (wp->state == WEBS_POST) {
+ websUrlHandlerRequest(wp);
+ } else {
+ websDone(wp, 0);
+ }
+ } else {
+/*
+ * If an error occurred and it wasn't an eof, close the connection
+ */
+#ifdef HP_FIX
+ websDone(wp, 0);
+#endif /*HP_FIX*/
+
+ }
+/*
+ * If state is WEBS_HEADER and the ringq is empty, then this is a
+ * simple request with no additional header fields to process and
+ * no empty line terminator.
+ */
+/*
+ * NOTE: this fix for earlier versions of browsers is troublesome
+ * because if we don't receive the entire header in the first pass
+ * this code assumes we were only expecting a one line header, which
+ * is not necessarily the case. So we weren't processing the whole
+ * header and weren't fufilling requests properly.
+ */
+#ifdef UNUSED
+ if (wp->state == WEBS_HEADER && ringqLen(&wp->header) <= 0) {
+ websParseRequest(wp);
+ websUrlHandlerRequest(wp);
+ }
+#endif
+ return -1;
+
+ } else if (nbytes == 0) {
+ if (wp->state == WEBS_HEADER) {
+/*
+ * Valid empty line, now finished with header
+ */
+ websParseRequest(wp);
+ if (wp->flags & WEBS_POST_REQUEST) {
+ if (wp->flags & WEBS_CLEN) {
+ wp->state = WEBS_POST_CLEN;
+ clen = wp->clen;
+ } else {
+ wp->state = WEBS_POST;
+ clen = 1;
+ }
+ if (clen > 0) {
+/*
+ * Return 0 to get more data.
+ */
+ return 0;
+ }
+ return 1;
+ }
+/*
+ * We've read the header so go and handle the request
+ */
+ websUrlHandlerRequest(wp);
+ }
+ return -1;
+ }
+ }
+ a_assert(text);
+ a_assert(nbytes > 0);
+ *ptext = text;
+ *pnbytes = nbytes;
+ return 1;
+}
+
+/******************************************************************************/
+/*
+ * Parse the first line of a HTTP request
+ */
+
+static int websParseFirst(webs_t wp, char_t *text)
+{
+ char_t *op, *proto, *protoVer, *url, *host, *query, *path, *port, *ext;
+ char_t *buf;
+ int testPort;
+
+ a_assert(websValid(wp));
+ a_assert(text && *text);
+
+/*
+ * Determine the request type: GET, HEAD or POST
+ */
+ op = gstrtok(text, T(" \t"));
+ if (op == NULL || *op == '\0') {
+ websError(wp, 400, T("Bad HTTP request"));
+ return -1;
+ }
+ if (gstrcmp(op, T("GET")) != 0) {
+ if (gstrcmp(op, T("POST")) == 0) {
+ wp->flags |= WEBS_POST_REQUEST;
+ } else if (gstrcmp(op, T("HEAD")) == 0) {
+ wp->flags |= WEBS_HEAD_REQUEST;
+ } else {
+ websError(wp, 400, T("Bad request type"));
+ return -1;
+ }
+ }
+
+/*
+ * Store result in the form (CGI) variable store
+ */
+ websSetVar(wp, T("REQUEST_METHOD"), op);
+
+ url = gstrtok(NULL, T(" \t\n"));
+ if (url == NULL || *url == '\0') {
+ websError(wp, 400, T("Bad HTTP request"));
+ return -1;
+ }
+ protoVer = gstrtok(NULL, T(" \t\n"));
+
+/*
+ * Parse the URL and store all the various URL components. websUrlParse
+ * returns an allocated buffer in buf which we must free. We support both
+ * proxied and non-proxied requests. Proxied requests will have http://host/
+ * at the start of the URL. Non-proxied will just be local path names.
+ */
+ host = path = port = proto = query = ext = NULL;
+ if (websUrlParse(url, &buf, &host, &path, &port, &query, &proto,
+ NULL, &ext) < 0) {
+ websError(wp, 400, T("Bad URL format"));
+ return -1;
+ }
+
+ wp->url = bstrdup(B_L, url);
+
+#ifndef __NO_CGI_BIN
+ if (gstrstr(url, CGI_BIN) != NULL) {
+ wp->flags |= WEBS_CGI_REQUEST;
+ if (wp->flags & WEBS_POST_REQUEST) {
+ wp->cgiStdin = websGetCgiCommName();
+ }
+ }
+#endif
+
+ wp->query = bstrdup(B_L, query);
+ wp->host = bstrdup(B_L, host);
+ wp->path = bstrdup(B_L, path);
+ wp->protocol = bstrdup(B_L, proto);
+ wp->protoVersion = bstrdup(B_L, protoVer);
+
+ if ((testPort = socketGetPort(wp->listenSid)) >= 0) {
+ wp->port = testPort;
+ } else {
+ wp->port = gatoi(port);
+ }
+
+ if (gstrcmp(ext, T(".asp")) == 0) {
+ wp->flags |= WEBS_ASP;
+ }
+ bfree(B_L, buf);
+
+ websUrlType(url, wp->type, TSZ(wp->type));
+
+#ifdef WEBS_PROXY_SUPPORT
+/*
+ * Determine if this is a request for local webs data. If it is not a proxied
+ * request from the browser, we won't see the "http://" or the system name, so
+ * we assume it must be talking to us directly for local webs data.
+ * Note: not fully implemented yet.
+ */
+ if (gstrstr(wp->url, T("http://")) == NULL ||
+ ((gstrcmp(wp->host, T("localhost")) == 0 ||
+ gstrcmp(wp->host, websHost) == 0) && (wp->port == websPort))) {
+ wp->flags |= WEBS_LOCAL_PAGE;
+ if (gstrcmp(wp->path, T("/")) == 0) {
+ wp->flags |= WEBS_HOME_PAGE;
+ }
+ }
+#endif
+
+ ringqFlush(&wp->header);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Parse a full request
+ */
+
+#define isgoodchar(s) (gisalnum((s)) || ((s) == '/') || ((s) == '_') || \
+ ((s) == '.') || ((s) == '-') )
+
+static void websParseRequest(webs_t wp)
+{
+ char_t *authType, *upperKey, *cp, *browser, *lp, *key, *value;
+
+ a_assert(websValid(wp));
+
+/*
+ * Define default CGI values
+ */
+ websSetVar(wp, T("HTTP_AUTHORIZATION"), T(""));
+
+/*
+ * Parse the header and create the Http header keyword variables
+ * We rewrite the header as we go for non-local requests. NOTE: this
+ * modifies the header string directly and tokenizes each line with '\0'.
+ */
+ browser = NULL;
+ for (lp = (char_t*) wp->header.servp; lp && *lp; ) {
+ cp = lp;
+ if ((lp = gstrchr(lp, '\n')) != NULL) {
+ lp++;
+ }
+
+ if ((key = gstrtok(cp, T(": \t\n"))) == NULL) {
+ continue;
+ }
+
+ if ((value = gstrtok(NULL, T("\n"))) == NULL) {
+ value = T("");
+ }
+
+ while (gisspace(*value)) {
+ value++;
+ }
+ strlower(key);
+
+/*
+ * Create a variable (CGI) for each line in the header
+ */
+ fmtAlloc(&upperKey, (gstrlen(key) + 6), T("HTTP_%s"), key);
+ for (cp = upperKey; *cp; cp++) {
+ if (*cp == '-')
+ *cp = '_';
+ }
+ strupper(upperKey);
+ websSetVar(wp, upperKey, value);
+ bfree(B_L, upperKey);
+
+/*
+ * Track the requesting agent (browser) type
+ */
+ if (gstrcmp(key, T("user-agent")) == 0) {
+ wp->userAgent = bstrdup(B_L, value);
+
+/*
+ * Parse the user authorization. ie. password
+ */
+ } else if (gstricmp(key, T("authorization")) == 0) {
+/*
+ * Determine the type of Authorization Request
+ */
+ authType = bstrdup (B_L, value);
+ a_assert (authType);
+/*
+ * Truncate authType at the next non-alpha character
+ */
+ cp = authType;
+ while (gisalpha(*cp)) {
+ cp++;
+ }
+ *cp = '\0';
+
+ wp->authType = bstrdup(B_L, authType);
+ bfree(B_L, authType);
+
+ if (gstricmp(wp->authType, T("basic")) == 0) {
+ char_t userAuth[FNAMESIZE];
+/*
+ * The incoming value is username:password (Basic authentication)
+ */
+ if ((cp = gstrchr(value, ' ')) != NULL) {
+ *cp = '\0';
+ /*
+ * bugfix 5/24/02 -- we were leaking the memory pointed to by
+ * wp->authType that was allocated just before the if()
+ * statement that we are currently in. Thanks to Simon Byholm.
+ */
+ bfree(B_L, wp->authType);
+ wp->authType = bstrdup(B_L, value);
+ websDecode64(userAuth, ++cp, sizeof(userAuth));
+ } else {
+ websDecode64(userAuth, value, sizeof(userAuth));
+ }
+/*
+ * Split userAuth into userid and password
+ */
+ if ((cp = gstrchr(userAuth, ':')) != NULL) {
+ *cp++ = '\0';
+ }
+ if (cp) {
+ wp->userName = bstrdup(B_L, userAuth);
+ wp->password = bstrdup(B_L, cp);
+ } else {
+ wp->userName = bstrdup(B_L, T(""));
+ wp->password = bstrdup(B_L, T(""));
+ }
+/*
+ * Set the flags to indicate digest authentication
+ */
+ wp->flags |= WEBS_AUTH_BASIC;
+ } else {
+#ifdef DIGEST_ACCESS_SUPPORT
+/*
+ * The incoming value is slightly more complicated (Digest)
+ */
+ char_t *np; /* pointer to end of tag name */
+ char_t tp; /* temporary character holding space */
+ char_t *vp; /* pointer to value */
+ char_t *npv; /* pointer to end of value, "next" pointer */
+ char_t tpv; /* temporary character holding space */
+/*
+ * Set the flags to indicate digest authentication
+ */
+ wp->flags |= WEBS_AUTH_DIGEST;
+/*
+ * Move cp to Next word beyond "Digest",
+ * vp to first char after '='.
+ */
+ cp = value;
+ while (isgoodchar(*cp)) {
+ cp++;
+ }
+ while (!isgoodchar(*cp)) {
+ cp++;
+ }
+
+/*
+ * Find beginning of value
+ */
+ vp = gstrchr(cp, '=');
+ while (vp) {
+/*
+ * Zero-terminate tag name
+ */
+ np = cp;
+ while (isgoodchar(*np)) {
+ np++;
+ }
+ tp = *np;
+ *np = 0;
+/*
+ * Advance value pointer to first legit character
+ */
+ vp++;
+ while (!isgoodchar(*vp)) {
+ vp++;
+ }
+/*
+ * Zero-terminate value
+ */
+ npv = vp;
+ while (isgoodchar(*npv)) {
+ npv++;
+ }
+ tpv = *npv;
+ *npv = 0;
+/*
+ * Extract the fields
+ */
+ if (gstricmp(cp, T("username")) == 0) {
+ wp->userName = bstrdup(B_L, vp);
+ } else if (gstricmp(cp, T("response")) == 0) {
+ wp->digest = bstrdup(B_L, vp);
+ } else if (gstricmp(cp, T("opaque")) == 0) {
+ wp->opaque = bstrdup(B_L, vp);
+ } else if (gstricmp(cp, T("uri")) == 0) {
+ wp->uri = bstrdup(B_L, vp);
+ } else if (gstricmp(cp, T("realm")) == 0) {
+ wp->realm = bstrdup(B_L, vp);
+ } else if (gstricmp(cp, T("nonce")) == 0) {
+ wp->nonce = bstrdup(B_L, vp);
+ } else if (gstricmp(cp, T("nc")) == 0) {
+ wp->nc = bstrdup(B_L, vp);
+ } else if (gstricmp(cp, T("cnonce")) == 0) {
+ wp->cnonce = bstrdup(B_L, vp);
+ } else if (gstricmp(cp, T("qop")) == 0) {
+ wp->qop = bstrdup(B_L, vp);
+ }
+/*
+ * Restore tag name and value zero-terminations
+ */
+ *np = tp;
+ *npv = tpv;
+/*
+ * Advance tag name and value pointers
+ */
+ cp = npv;
+ while (*cp && isgoodchar(*cp)) {
+ cp++;
+ }
+ while (*cp && !isgoodchar(*cp)) {
+ cp++;
+ }
+
+ if (*cp) {
+ vp = gstrchr(cp, '=');
+ } else {
+ vp = NULL;
+ }
+ }
+#endif /* DIGEST_ACCESS_SUPPORT */
+ } /* if (gstrcmp(wp->authType)) */
+/*
+ * Parse the content length
+ */
+ } else if (gstrcmp(key, T("content-length")) == 0) {
+ /*
+ * 11 Oct 02 BgP -- The server would crash if an attacker sent a POST
+ * message with a content-length value <= 0. We assume that anyone
+ * sending this is malicious, and the POST is read from the socket,
+ * but it is ignored, and the socket is closed.
+ */
+ wp->clen = gatoi(value);
+ if (wp->clen > 0)
+ {
+ wp->flags |= WEBS_CLEN;
+ websSetVar(wp, T("CONTENT_LENGTH"), value);
+ }
+ else
+ {
+ wp->clen = 0;
+ }
+
+/*
+ * Parse the content type
+ */
+ } else if (gstrcmp(key, T("content-type")) == 0) {
+ websSetVar(wp, T("CONTENT_TYPE"), value);
+
+#ifdef WEBS_KEEP_ALIVE_SUPPORT
+ } else if (gstrcmp(key, T("connection")) == 0) {
+ strlower(value);
+ if (gstrcmp(value, T("keep-alive")) == 0) {
+ wp->flags |= WEBS_KEEP_ALIVE;
+ }
+#endif
+
+#ifdef WEBS_PROXY_SUPPORT
+/*
+ * This may be useful if you wish to keep a local cache of web pages
+ * for proxied requests.
+ */
+ } else if (gstrcmp(key, T("pragma")) == 0) {
+ char_t tmp[256];
+ gstrncpy(tmp, value, TSZ(tmp));
+ strlower(tmp);
+ if (gstrstr(tmp, T("no-cache"))) {
+ wp->flags |= WEBS_DONT_USE_CACHE;
+ }
+#endif /* WEBS_PROXY_SUPPORT */
+
+/*
+ * Store the cookie
+ */
+ } else if (gstrcmp(key, T("cookie")) == 0) {
+ wp->flags |= WEBS_COOKIE;
+ wp->cookie = bstrdup(B_L, value);
+
+#ifdef WEBS_IF_MODIFIED_SUPPORT
+/*
+ * See if the local page has been modified since the browser last
+ * requested this document. If not, just return a 302
+ */
+ } else if (gstrcmp(key, T("if-modified-since")) == 0) {
+ char_t *cmd;
+ time_t tip = 0;
+
+ if ((cp = gstrchr(value, ';')) != NULL) {
+ *cp = '\0';
+ }
+
+ fmtAlloc(&cmd, 64, T("%s"), value);
+
+ if ((wp->since = dateParse(tip, cmd)) != 0) {
+ wp->flags |= WEBS_IF_MODIFIED;
+ }
+
+ bfreeSafe(B_L, cmd);
+#endif /* WEBS_IF_MODIFIED_SUPPORT */
+ }
+ }
+}
+
+/******************************************************************************/
+/*
+ * Set the variable (CGI) environment for this request. Create variables
+ * for all standard CGI variables. Also decode the query string and create
+ * a variable for each name=value pair.
+ */
+
+void websSetEnv(webs_t wp)
+{
+ char_t portBuf[8];
+ char_t *keyword, *value, *valCheck, *valNew;
+
+ a_assert(websValid(wp));
+
+ websSetVar(wp, T("QUERY_STRING"), wp->query);
+ websSetVar(wp, T("GATEWAY_INTERFACE"), T("CGI/1.1"));
+ websSetVar(wp, T("SERVER_HOST"), websHost);
+ websSetVar(wp, T("SERVER_NAME"), websHost);
+ websSetVar(wp, T("SERVER_URL"), websHostUrl);
+ websSetVar(wp, T("REMOTE_HOST"), wp->ipaddr);
+ websSetVar(wp, T("REMOTE_ADDR"), wp->ipaddr);
+ websSetVar(wp, T("PATH_INFO"), wp->path);
+ stritoa(websPort, portBuf, sizeof(portBuf));
+ websSetVar(wp, T("SERVER_PORT"), portBuf);
+ websSetVar(wp, T("SERVER_ADDR"), websIpaddr);
+ fmtAlloc(&value, FNAMESIZE, T("%s/%s"), WEBS_NAME, WEBS_VERSION);
+ websSetVar(wp, T("SERVER_SOFTWARE"), value);
+ bfreeSafe(B_L, value);
+ websSetVar(wp, T("SERVER_PROTOCOL"), wp->protoVersion);
+
+/*
+ * Decode and create an environment query variable for each query keyword.
+ * We split into pairs at each '&', then split pairs at the '='.
+ * Note: we rely on wp->decodedQuery preserving the decoded values in the
+ * symbol table.
+ */
+ wp->decodedQuery = bstrdup(B_L, wp->query);
+ keyword = gstrtok(wp->decodedQuery, T("&"));
+ while (keyword != NULL) {
+ if ((value = gstrchr(keyword, '=')) != NULL) {
+ *value++ = '\0';
+ websDecodeUrl(keyword, keyword, gstrlen(keyword));
+ websDecodeUrl(value, value, gstrlen(value));
+ } else {
+ value = T("");
+ }
+
+ if (*keyword) {
+/*
+ * If keyword has already been set, append the new value to what has
+ * been stored.
+ */
+ if ((valCheck = websGetVar(wp, keyword, NULL)) != 0) {
+ fmtAlloc(&valNew, 256, T("%s %s"), valCheck, value);
+ websSetVar(wp, keyword, valNew);
+ bfreeSafe(B_L, valNew);
+ } else {
+ websSetVar(wp, keyword, value);
+ }
+ }
+ keyword = gstrtok(NULL, T("&"));
+ }
+
+#ifdef EMF
+/*
+ * Add GoAhead Embedded Management Framework defines
+ */
+ websSetEmfEnvironment(wp);
+#endif
+}
+
+/******************************************************************************/
+/*
+ * Define a webs (CGI) variable for this connection. Also create in relevant
+ * scripting engines. Note: the incoming value may be volatile.
+ */
+
+void websSetVar(webs_t wp, char_t *var, char_t *value)
+{
+ value_t v;
+
+ a_assert(websValid(wp));
+
+/*
+ * value_instring will allocate the string if required.
+ */
+ if (value) {
+ v = valueString(value, VALUE_ALLOCATE);
+ } else {
+ v = valueString(T(""), VALUE_ALLOCATE);
+ }
+ symEnter(wp->cgiVars, var, v, 0);
+}
+
+/******************************************************************************/
+/*
+ * Return TRUE if a webs variable exists for this connection.
+ */
+
+int websTestVar(webs_t wp, char_t *var)
+{
+ sym_t *sp;
+
+ a_assert(websValid(wp));
+
+ if (var == NULL || *var == '\0') {
+ return 0;
+ }
+
+ if ((sp = symLookup(wp->cgiVars, var)) == NULL) {
+ return 0;
+ }
+ return 1;
+}
+
+/******************************************************************************/
+/*
+ * Get a webs variable but return a default value if string not found.
+ * Note, defaultGetValue can be NULL to permit testing existence.
+ */
+
+char_t *websGetVar(webs_t wp, char_t *var, char_t *defaultGetValue)
+{
+ sym_t *sp;
+
+ a_assert(websValid(wp));
+ a_assert(var && *var);
+
+ if ((sp = symLookup(wp->cgiVars, var)) != NULL) {
+ a_assert(sp->content.type == string);
+ if (sp->content.value.string) {
+ return sp->content.value.string;
+ } else {
+ return T("");
+ }
+ }
+ return defaultGetValue;
+}
+
+/******************************************************************************/
+/*
+ * Return TRUE if a webs variable is set to a given value
+ */
+
+int websCompareVar(webs_t wp, char_t *var, char_t *value)
+{
+ a_assert(websValid(wp));
+ a_assert(var && *var);
+
+ if (gstrcmp(value, websGetVar(wp, var, T(" __UNDEF__ "))) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Cancel the request timeout. Note may be called multiple times.
+ */
+
+void websTimeoutCancel(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ if (wp->timeout >= 0) {
+ emfUnschedCallback(wp->timeout);
+ wp->timeout = -1;
+ }
+}
+
+/******************************************************************************/
+/*
+ * Output a HTTP response back to the browser. If redirect is set to a
+ * URL, the browser will be sent to this location.
+ */
+
+void websResponse(webs_t wp, int code, char_t *message, char_t *redirect)
+{
+ char_t *date;
+
+ a_assert(websValid(wp));
+
+/*
+ * IE3.0 needs no Keep Alive for some return codes.
+ */
+ wp->flags &= ~WEBS_KEEP_ALIVE;
+
+/*
+ * Only output the header if a header has not already been output.
+ */
+ if ( !(wp->flags & WEBS_HEADER_DONE)) {
+ wp->flags |= WEBS_HEADER_DONE;
+/*
+ * Redirect behaves much better when sent with HTTP/1.0
+ */
+ if (redirect != NULL) {
+ websWrite(wp, T("HTTP/1.0 %d %s\r\n"), code, websErrorMsg(code));
+ } else {
+ websWrite(wp, T("HTTP/1.1 %d %s\r\n"), code, websErrorMsg(code));
+ }
+
+/*
+ * By license terms the following line of code must not be modified.
+ */
+ websWrite(wp, T("Server: %s\r\n"), WEBS_NAME);
+
+/*
+ * Timestamp/Date is usually the next to go
+ */
+ if ((date = websGetDateString(NULL)) != NULL) {
+ websWrite(wp, T("Date: %s\r\n"), date);
+ bfree(B_L, date);
+ }
+/*
+ * If authentication is required, send the auth header info
+ */
+ if (code == 401) {
+ if (!(wp->flags & WEBS_AUTH_DIGEST)) {
+ websWrite(wp, T("WWW-Authenticate: Basic realm=\"%s\"\r\n"),
+ websGetRealm());
+#ifdef DIGEST_ACCESS_SUPPORT
+ } else {
+ char_t *nonce, *opaque;
+
+ /* $$$ before... (note commas instead of semicolons...)
+ nonce = websCalcNonce(wp),
+ opaque = websCalcOpaque(wp),
+ $$$ after */
+ nonce = websCalcNonce(wp);
+ opaque = websCalcOpaque(wp);
+ /* ...$$$ end */
+ websWrite(wp,
+ T("WWW-Authenticate: Digest realm=\"%s\", domain=\"%s\",")
+ T("qop=\"%s\", nonce=\"%s\", opaque=\"%s\",")
+ T("algorithm=\"%s\", stale=\"%s\"\r\n"),
+ websGetRealm(),
+ websGetHostUrl(),
+ T("auth"),
+ nonce,
+ opaque, T("MD5"), T("FALSE"));
+ bfree(B_L, nonce);
+ bfree(B_L, opaque);
+#endif
+ }
+ }
+
+ if (wp->flags & WEBS_KEEP_ALIVE) {
+ websWrite(wp, T("Connection: keep-alive\r\n"));
+ }
+
+ websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n"));
+ websWrite(wp, T("Content-Type: text/html\r\n"));
+/*
+ * We don't do a string length here as the message may be multi-line.
+ * Ie. <CR><LF> will count as only one and we will have a content-length
+ * that is too short.
+ *
+ * websWrite(wp, T("Content-Length: %s\r\n"), message);
+ */
+ if (redirect) {
+ websWrite(wp, T("Location: %s\r\n"), redirect);
+ }
+ websWrite(wp, T("\r\n"));
+ }
+
+/*
+ * If the browser didn't do a HEAD only request, send the message as well.
+ */
+ if ((wp->flags & WEBS_HEAD_REQUEST) == 0 && message && *message) {
+ websWrite(wp, T("%s\r\n"), message);
+ }
+ websDone(wp, code);
+}
+
+/******************************************************************************/
+/*
+ * Redirect the user to another webs page
+ */
+
+void websRedirect(webs_t wp, char_t *url)
+{
+ char_t *msgbuf, *urlbuf, *redirectFmt;
+
+ a_assert(websValid(wp));
+ a_assert(url);
+
+ websStats.redirects++;
+ msgbuf = urlbuf = NULL;
+
+/*
+ * Some browsers require a http://host qualified URL for redirection
+ */
+ if (gstrstr(url, T("http://")) == NULL) {
+ if (*url == '/') {
+ url++;
+ }
+
+ redirectFmt = T("http://%s/%s");
+
+#ifdef WEBS_SSL_SUPPORT
+ if (wp->flags & WEBS_SECURE) {
+ redirectFmt = T("https://%s/%s");
+ }
+#endif
+
+ fmtAlloc(&urlbuf, WEBS_MAX_URL + 80, redirectFmt,
+ websGetVar(wp, T("HTTP_HOST"), websHostUrl), url);
+ url = urlbuf;
+ }
+
+/*
+ * Add human readable message for completeness. Should not be required.
+ */
+ fmtAlloc(&msgbuf, WEBS_MAX_URL + 80,
+ T("<html><head></head><body>\r\n\
+ This document has moved to a new <a href=\"%s\">location</a>.\r\n\
+ Please update your documents to reflect the new location.\r\n\
+ </body></html>\r\n"), url);
+
+ websResponse(wp, 302, msgbuf, url);
+
+ bfreeSafe(B_L, msgbuf);
+ bfreeSafe(B_L, urlbuf);
+}
+
+
+/*
+ * websSafeUrl -- utility function to clean up URLs that will be printed by
+ * the websError() function, below. To prevent problems with the 'cross-site
+ * scripting exploit', where attackers request an URL containing embedded
+ * JavaScript code, we replace all '<' and '>' characters with HTML entities
+ * so that the user's browser will not interpret the URL as JavaScript.
+ */
+
+#define kLt '<'
+#define kLessThan T("&lt;")
+#define kGt '>'
+#define kGreaterThan T("&gt;")
+
+
+
+
+static int charCount(const char_t* str, char_t ch)
+{
+ int count = 0;
+ char_t* p = (char_t*) str;
+
+ if (NULL == str)
+ {
+ return 0;
+ }
+
+ while (1)
+ {
+ p = gstrchr(p, ch);
+ if (NULL == p)
+ {
+ break;
+ }
+ /*
+ * increment the count, and begin looking at the next character
+ */
+ ++count;
+ ++p;
+ }
+ return count;
+}
+
+
+
+static char_t* websSafeUrl(const char_t* url)
+{
+
+ int ltCount = charCount(url, kLt);
+ int gtCount = charCount(url, kGt);
+ int safeLen = 0;
+ char_t* safeUrl = NULL;
+ char_t* src = NULL;
+ char_t* dest = NULL;
+
+ if (NULL != url)
+ {
+ safeLen = gstrlen(url);
+ if (ltCount == 0 && gtCount == 0)
+ {
+ safeUrl = bstrdup(B_L, (char_t*) url);
+ }
+ else
+ {
+ safeLen += (ltCount * 4);
+ safeLen += (gtCount * 4);
+
+ safeUrl = balloc(B_L, safeLen);
+ if (safeUrl != NULL)
+ {
+ src = (char_t*) url;
+ dest = safeUrl;
+ while (*src)
+ {
+ if (*src == kLt)
+ {
+ gstrcpy(dest, kLessThan);
+ dest += gstrlen(kLessThan);
+ }
+ else if (*src == kGt)
+ {
+ gstrcpy(dest, kGreaterThan);
+ dest += gstrlen(kGreaterThan);
+ }
+ else
+ {
+ *dest++ = *src;
+ }
+ ++src;
+ }
+ /* don't forget to terminate the string...*/
+ *dest = '\0';
+ }
+ }
+ }
+ return safeUrl;
+}
+
+
+/******************************************************************************/
+/*
+ * Output an error message and cleanup
+ */
+
+#ifdef qRichErrorPage
+extern int dmfRichError(webs_t wp, int code, char_t* userMsg);
+#endif
+void websError(webs_t wp, int code, char_t *fmt, ...)
+{
+ va_list args;
+ char_t *msg, *userMsg, *buf;
+ char_t* safeUrl = NULL;
+ char_t* safeMsg = NULL;
+#ifdef qRichErrorPage
+ static int reEntry = 0;
+ int errorOk;
+#endif
+
+ a_assert(websValid(wp));
+ a_assert(fmt);
+
+ websStats.errors++;
+
+ /* remove any dangerous characters in the url, and replace the string in the
+ * wp structure. The webs_t cleanup code will free this memory for us.
+ */
+ safeUrl = websSafeUrl(wp->url);
+ bfreeSafe(B_L, wp->url);
+ wp->url = safeUrl;
+
+ va_start(args, fmt);
+ userMsg = NULL;
+ fmtValloc(&userMsg, WEBS_BUFSIZE, fmt, args);
+ va_end(args);
+ safeMsg = websSafeUrl(userMsg);
+ bfreeSafe(B_L, userMsg);
+ userMsg = safeMsg;
+ safeMsg = NULL;
+
+
+
+#ifdef qRichErrorPage
+ if (!reEntry)
+ {
+ /*
+ * The dmfRichError function that we're about to call may very well call
+ * websError() as part of its work. If that happens, we do NOT want to
+ * get into a never-ending recursive call chain. When we get back here
+ * in a call from inside dmfRichError(), we check to see if we're
+ * already trying to call dmfRichError. If we are, we just revert to the
+ * old non-rich behavior and display a black on white error page.
+ */
+
+ reEntry = 1;
+ errorOk = dmfRichError(wp, code, userMsg);
+ reEntry = 0;
+ if (errorOk)
+ {
+ bfreeSafe(B_L, userMsg);
+ return;
+ }
+ /* ...else we need to fall through and execute the simple error page. */
+ }
+ /* implicit else... */
+#endif
+
+ msg = T("<html><head><title>Document Error: %s</title></head>\r\n\
+ <body><h2>Access Error: %s</h2>\r\n\
+ <p>%s</p></body></html>\r\n");
+/*
+ * Ensure we have plenty of room
+ */
+
+ buf = NULL;
+ fmtAlloc(&buf, WEBS_BUFSIZE, msg, websErrorMsg(code),
+ websErrorMsg(code), userMsg);
+
+ websResponse(wp, code, buf, NULL);
+ bfreeSafe(B_L, buf);
+ bfreeSafe(B_L, userMsg);
+}
+
+/******************************************************************************/
+/*
+ * Return the error message for a given code
+ */
+
+/*static char_t *websErrorMsg(int code)*/
+char_t *websErrorMsg(int code)
+{
+ websErrorType *ep;
+
+ for (ep = websErrors; ep->code; ep++) {
+ if (code == ep->code) {
+ return ep->msg;
+ }
+ }
+ a_assert(0);
+ return T("");
+}
+
+/******************************************************************************/
+/*
+ * Do formatted output to the browser. This is the public ASP and form
+ * write procedure.
+ */
+
+int websWrite(webs_t wp, char_t *fmt, ...)
+{
+ va_list vargs;
+ char_t *buf;
+ int rc;
+
+ a_assert(websValid(wp));
+
+ va_start(vargs, fmt);
+
+ buf = NULL;
+ rc = 0;
+
+ if (fmtValloc(&buf, WEBS_BUFSIZE, fmt, vargs) >= WEBS_BUFSIZE) {
+ trace(0, T("webs: websWrite lost data, buffer overflow\n"));
+ }
+
+ va_end(vargs);
+ a_assert(buf);
+ if (buf) {
+ rc = websWriteBlock(wp, buf, gstrlen(buf));
+ bfree(B_L, buf);
+ }
+ return rc;
+}
+
+/******************************************************************************/
+/*
+ * Write a block of data of length "nChars" to the user's browser. Public
+ * write block procedure. If unicode is turned on this function expects
+ * buf to be a unicode string and it converts it to ASCII before writing.
+ * See websWriteDataNonBlock to always write binary or ASCII data with no
+ * unicode conversion. This returns the number of char_t's processed.
+ * It spins until nChars are flushed to the socket. For non-blocking
+ * behavior, use websWriteDataNonBlock.
+ */
+
+int websWriteBlock(webs_t wp, char_t *buf, int nChars)
+{
+ int len, done;
+ char *asciiBuf, *pBuf;
+
+ a_assert(wp);
+ a_assert(websValid(wp));
+ a_assert(buf);
+ a_assert(nChars >= 0);
+
+ done = len = 0;
+
+/*
+ * ballocUniToAsc will convert Unicode to strings to Ascii. If Unicode is
+ * not turned on then ballocUniToAsc will not do the conversion.
+ */
+ pBuf = asciiBuf = ballocUniToAsc(buf, nChars);
+
+ while (nChars > 0) {
+#ifdef WEBS_SSL_SUPPORT
+ if (wp->flags & WEBS_SECURE) {
+ if ((len = websSSLWrite(wp->wsp, pBuf, nChars)) < 0) {
+ bfree(B_L, asciiBuf);
+ return -1;
+ }
+ websSSLFlush(wp->wsp);
+ } else {
+ if ((len = socketWrite(wp->sid, pBuf, nChars)) < 0) {
+ bfree(B_L, asciiBuf);
+ return -1;
+ }
+ socketFlush(wp->sid);
+ }
+#else /* ! WEBS_SSL_SUPPORT */
+ if ((len = socketWrite(wp->sid, pBuf, nChars)) < 0) {
+ bfree(B_L, asciiBuf);
+ return -1;
+ }
+ socketFlush(wp->sid);
+#endif /* WEBS_SSL_SUPPORT */
+ nChars -= len;
+ pBuf += len;
+ done += len;
+ }
+
+ bfree(B_L, asciiBuf);
+ return done;
+}
+
+/******************************************************************************/
+/*
+ * Write a block of data of length "nChars" to the user's browser. Same as
+ * websWriteBlock except that it expects straight ASCII or binary and does no
+ * unicode conversion before writing the data. If the socket cannot hold all
+ * the data, it will return the number of bytes flushed to the socket before
+ * it would have blocked. This returns the number of chars processed or -1
+ * if socketWrite fails.
+ */
+
+int websWriteDataNonBlock(webs_t wp, char *buf, int nChars)
+{
+ int r;
+
+ a_assert(wp);
+ a_assert(websValid(wp));
+ a_assert(buf);
+ a_assert(nChars >= 0);
+
+#ifdef WEBS_SSL_SUPPORT
+ if (wp->flags & WEBS_SECURE) {
+ r = websSSLWrite(wp->wsp, buf, nChars);
+ websSSLFlush(wp->wsp);
+ } else {
+ r = socketWrite(wp->sid, buf, nChars);
+ socketFlush(wp->sid);
+ }
+#else
+ r = socketWrite(wp->sid, buf, nChars);
+ socketFlush(wp->sid);
+#endif
+
+ return r;
+}
+
+/******************************************************************************/
+/*
+ * Decode a URL (or part thereof). Allows insitu decoding.
+ */
+
+void websDecodeUrl(char_t *decoded, char_t *token, int len)
+{
+ char_t *ip, *op;
+ int num, i, c;
+
+ a_assert(decoded);
+ a_assert(token);
+
+ op = decoded;
+ for (ip = token; *ip && len > 0; ip++, op++) {
+ if (*ip == '+') {
+ *op = ' ';
+ } else if (*ip == '%' && gisxdigit(ip[1]) && gisxdigit(ip[2])) {
+
+/*
+ * Convert %nn to a single character
+ */
+ ip++;
+ for (i = 0, num = 0; i < 2; i++, ip++) {
+ c = tolower(*ip);
+ if (c >= 'a' && c <= 'f') {
+ num = (num * 16) + 10 + c - 'a';
+ } else {
+ num = (num * 16) + c - '0';
+ }
+ }
+ *op = (char_t) num;
+ ip--;
+
+ } else {
+ *op = *ip;
+ }
+ len--;
+ }
+ *op = '\0';
+}
+
+/******************************************************************************/
+#ifdef WEBS_LOG_SUPPORT
+/*
+ * Output a log message
+ */
+
+static void websLog(webs_t wp, int code)
+{
+ char_t *buf;
+ char *abuf;
+ int len;
+#define qAnlLog 1
+#if (defined(qAnlLog) && !defined(CE))
+ time_t timer;
+ char_t* newLine = NULL;
+ char_t* timeStr = NULL;
+#endif
+ a_assert(websValid(wp));
+
+ buf = NULL;
+
+#if (defined(qAnlLog) && !defined(CE))
+ time(&timer);
+ timeStr = ctime(&timer);
+ newLine = gstrchr(timeStr, '\n');
+ if (newLine)
+ {
+ *newLine = '\0';
+ }
+ fmtAlloc(&buf, WEBS_MAX_URL + 80, T("%s\t%s\t%s\tcode = %d\n"),
+ timeStr, wp->ipaddr, wp->url, code);
+#else
+ fmtAlloc(&buf, WEBS_MAX_URL + 80, T("%d %s %d %d\n"), time(0),
+ wp->url, code, wp->written);
+#endif
+ len = gstrlen(buf);
+ abuf = ballocUniToAsc(buf, len+1);
+ write(websLogFd, abuf, len);
+ bfreeSafe(B_L, buf);
+ bfreeSafe(B_L, abuf);
+}
+
+#endif /* WEBS_LOG_SUPPORT */
+
+/******************************************************************************/
+/*
+ * Request timeout. The timeout triggers if we have not read any data from
+ * the users browser in the last WEBS_TIMEOUT period. If we have heard from
+ * the browser, simply re-issue the timeout.
+ */
+
+void websTimeout(void *arg, int id)
+{
+ webs_t wp;
+ int delay, tm;
+
+ wp = (webs_t) arg;
+ a_assert(websValid(wp));
+
+ tm = websGetTimeSinceMark(wp) * 1000;
+ if (tm >= WEBS_TIMEOUT) {
+ websStats.timeouts++;
+ emfUnschedCallback(id);
+
+/*
+ * Clear the timeout id
+ */
+ wp->timeout = -1;
+ websDone(wp, 404);
+ } else {
+ delay = WEBS_TIMEOUT - tm;
+ a_assert(delay > 0);
+ emfReschedCallback(id, delay);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Called when the request is done.
+ */
+
+void websDone(webs_t wp, int code)
+{
+ a_assert(websValid(wp));
+
+/*
+ * Disable socket handler in case keep alive set.
+ */
+ socketDeleteHandler(wp->sid);
+
+ if (code != 200) {
+ wp->flags &= ~WEBS_KEEP_ALIVE;
+ }
+
+#ifdef WEBS_PROXY_SUPPORT
+ if (! (wp->flags & WEBS_LOCAL_PAGE)) {
+ websStats.activeNetRequests--;
+ }
+#endif
+
+#ifdef WEBS_LOG_SUPPORT
+ if (! (wp->flags & WEBS_REQUEST_DONE)) {
+ websLog(wp, code);
+ }
+#endif
+
+/*
+ * Close any opened document by a handler
+ */
+ websPageClose(wp);
+
+/*
+ * Exit if secure.
+ */
+#ifdef WEBS_SSL_SUPPORT
+ if (wp->flags & WEBS_SECURE) {
+ websTimeoutCancel(wp);
+ websSSLFlush(wp->wsp);
+ socketCloseConnection(wp->sid);
+ websFree(wp);
+ return;
+ }
+#endif
+
+/*
+ * If using Keep Alive (HTTP/1.1) we keep the socket open for a period
+ * while waiting for another request on the socket.
+ */
+ if (wp->flags & WEBS_KEEP_ALIVE) {
+ if (socketFlush(wp->sid) == 0) {
+ wp->state = WEBS_BEGIN;
+ wp->flags |= WEBS_REQUEST_DONE;
+ if (wp->header.buf) {
+ ringqFlush(&wp->header);
+ }
+ socketCreateHandler(wp->sid, SOCKET_READABLE, websSocketEvent,
+ (int) wp);
+ websTimeoutCancel(wp);
+ wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout,
+ (void *) wp);
+ return;
+ }
+ } else {
+ websTimeoutCancel(wp);
+ socketSetBlock(wp->sid, 1);
+ socketFlush(wp->sid);
+ socketCloseConnection(wp->sid);
+ }
+ websFree(wp);
+}
+
+/******************************************************************************/
+/*
+ * Allocate a new webs structure
+ */
+
+int websAlloc(int sid)
+{
+ webs_t wp;
+ int wid;
+
+/*
+ * Allocate a new handle for this connection
+ */
+ if ((wid = hAllocEntry((void***) &webs, &websMax,
+ sizeof(struct websRec))) < 0) {
+ return -1;
+ }
+ wp = webs[wid];
+
+ wp->wid = wid;
+ wp->sid = sid;
+ wp->state = WEBS_BEGIN;
+ wp->docfd = -1;
+ wp->timeout = -1;
+ wp->dir = NULL;
+ wp->authType = NULL;
+ wp->protocol = NULL;
+ wp->protoVersion = NULL;
+ wp->password = NULL;
+ wp->userName = NULL;
+#ifdef DIGEST_ACCESS_SUPPORT
+ wp->realm = NULL;
+ wp->nonce = NULL;
+ wp->digest = NULL;
+ wp->uri = NULL;
+ wp->opaque = NULL;
+ wp->nc = NULL;
+ wp->cnonce = NULL;
+ wp->qop = NULL;
+#endif
+#ifdef WEBS_SSL_SUPPORT
+ wp->wsp = NULL;
+#endif
+
+ ringqOpen(&wp->header, WEBS_HEADER_BUFINC, WEBS_MAX_HEADER);
+
+/*
+ * Create storage for the CGI variables. We supply the symbol tables for
+ * both the CGI variables and for the global functions. The function table
+ * is common to all webs instances (ie. all browsers)
+ */
+ wp->cgiVars = symOpen(WEBS_SYM_INIT);
+
+ return wid;
+}
+
+/******************************************************************************/
+/*
+ * Free a webs structure
+ */
+
+void websFree(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ if (wp->path)
+ bfree(B_L, wp->path);
+ if (wp->url)
+ bfree(B_L, wp->url);
+ if (wp->host)
+ bfree(B_L, wp->host);
+ if (wp->lpath)
+ bfree(B_L, wp->lpath);
+ if (wp->query)
+ bfree(B_L, wp->query);
+ if (wp->decodedQuery)
+ bfree(B_L, wp->decodedQuery);
+ if (wp->authType)
+ bfree(B_L, wp->authType);
+ if (wp->password)
+ bfree(B_L, wp->password);
+ if (wp->userName)
+ bfree(B_L, wp->userName);
+ if (wp->cookie)
+ bfree(B_L, wp->cookie);
+ if (wp->userAgent)
+ bfree(B_L, wp->userAgent);
+ if (wp->dir)
+ bfree(B_L, wp->dir);
+ if (wp->protocol)
+ bfree(B_L, wp->protocol);
+ if (wp->protoVersion)
+ bfree(B_L, wp->protoVersion);
+ if (wp->cgiStdin)
+ bfree(B_L, wp->cgiStdin);
+
+
+#ifdef DIGEST_ACCESS_SUPPORT
+ if (wp->realm)
+ bfree(B_L, wp->realm);
+ if (wp->uri)
+ bfree(B_L, wp->uri);
+ if (wp->digest)
+ bfree(B_L, wp->digest);
+ if (wp->opaque)
+ bfree(B_L, wp->opaque);
+ if (wp->nonce)
+ bfree(B_L, wp->nonce);
+ if (wp->nc)
+ bfree(B_L, wp->nc);
+ if (wp->cnonce)
+ bfree(B_L, wp->cnonce);
+ if (wp->qop)
+ bfree(B_L, wp->qop);
+#endif
+#ifdef WEBS_SSL_SUPPORT
+ websSSLFree(wp->wsp);
+#endif
+ symClose(wp->cgiVars);
+
+ if (wp->header.buf) {
+ ringqClose(&wp->header);
+ }
+
+ websMax = hFree((void***) &webs, wp->wid);
+ bfree(B_L, wp);
+ a_assert(websMax >= 0);
+}
+
+/******************************************************************************/
+/*
+ * Return the server address
+ */
+
+char_t *websGetHost()
+{
+ return websHost;
+}
+
+/******************************************************************************/
+/*
+ * Return the the url to access the server. (ip address)
+ */
+
+char_t *websGetIpaddrUrl()
+{
+ return websIpaddrUrl;
+}
+
+/******************************************************************************/
+/*
+ * Return the server address
+ */
+
+char_t *websGetHostUrl()
+{
+ return websHostUrl;
+}
+
+/******************************************************************************/
+/*
+ * Return the listen port
+ */
+
+int websGetPort()
+{
+ return websPort;
+}
+
+/******************************************************************************/
+/*
+ * Get the number of bytes to write
+ */
+
+int websGetRequestBytes(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->numbytes;
+}
+
+/******************************************************************************/
+/*
+ * Get the directory for this request
+ */
+
+char_t *websGetRequestDir(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ if (wp->dir == NULL) {
+ return T("");
+ }
+
+ return wp->dir;
+}
+
+/******************************************************************************/
+/*
+ * Get the flags for this request
+ */
+
+int websGetRequestFlags(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->flags;
+}
+
+/******************************************************************************/
+/*
+ * Return the IP address
+ */
+
+char_t *websGetRequestIpaddr(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->ipaddr;
+}
+
+/******************************************************************************/
+/*
+ * Set the local path for the request
+ */
+
+char_t *websGetRequestLpath(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+#ifdef WEBS_PAGE_ROM
+ return wp->path;
+#else
+ return wp->lpath;
+#endif
+}
+
+/******************************************************************************/
+/*
+ * Get the path for this request
+ */
+
+char_t *websGetRequestPath(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ if (wp->path == NULL) {
+ return T("");
+ }
+
+ return wp->path;
+}
+
+/******************************************************************************/
+/*
+ * Return the password
+ */
+
+char_t *websGetRequestPassword(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->password;
+}
+
+/******************************************************************************/
+/*
+ * Return the request type
+ */
+
+char_t *websGetRequestType(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->type;
+}
+
+/******************************************************************************/
+/*
+ * Return the username
+ */
+
+char_t *websGetRequestUserName(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->userName;
+}
+
+/******************************************************************************/
+/*
+ * Get the number of bytes written
+ */
+
+int websGetRequestWritten(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->written;
+}
+
+/******************************************************************************/
+/*
+ * Set the hostname
+ */
+
+void websSetHost(char_t *host)
+{
+ gstrncpy(websHost, host, TSZ(websHost));
+}
+
+/******************************************************************************/
+/*
+ * Set the host URL
+ */
+
+void websSetHostUrl(char_t *url)
+{
+ a_assert(url && *url);
+
+ bfreeSafe(B_L, websHostUrl);
+ websHostUrl = gstrdup(B_L, url);
+}
+
+/******************************************************************************/
+/*
+ * Set the IP address
+ */
+
+void websSetIpaddr(char_t *ipaddr)
+{
+ a_assert(ipaddr && *ipaddr);
+
+ gstrncpy(websIpaddr, ipaddr, TSZ(websIpaddr));
+}
+
+/******************************************************************************/
+/*
+ * Set the number of bytes to write
+ */
+
+void websSetRequestBytes(webs_t wp, int bytes)
+{
+ a_assert(websValid(wp));
+ a_assert(bytes >= 0);
+
+ wp->numbytes = bytes;
+}
+
+/******************************************************************************/
+/*
+ * Set the flags for this request
+ */
+
+void websSetRequestFlags(webs_t wp, int flags)
+{
+ a_assert(websValid(wp));
+
+ wp->flags = flags;
+}
+
+/******************************************************************************/
+/*
+ * Set the local path for the request
+ */
+
+void websSetRequestLpath(webs_t wp, char_t *lpath)
+{
+ a_assert(websValid(wp));
+ a_assert(lpath && *lpath);
+
+ if (wp->lpath) {
+ bfree(B_L, wp->lpath);
+ }
+ wp->lpath = bstrdup(B_L, lpath);
+ websSetVar(wp, T("PATH_TRANSLATED"), wp->lpath);
+}
+
+/******************************************************************************/
+/*
+ * Update the URL path and the directory containing the web page
+ */
+
+void websSetRequestPath(webs_t wp, char_t *dir, char_t *path)
+{
+ char_t *tmp;
+
+ a_assert(websValid(wp));
+
+ if (dir) {
+ tmp = wp->dir;
+ wp->dir = bstrdup(B_L, dir);
+ if (tmp) {
+ bfree(B_L, tmp);
+ }
+ }
+ if (path) {
+ tmp = wp->path;
+ wp->path = bstrdup(B_L, path);
+ websSetVar(wp, T("PATH_INFO"), wp->path);
+ if (tmp) {
+ bfree(B_L, tmp);
+ }
+ }
+}
+
+/******************************************************************************/
+/*
+ * Set the Write handler for this socket
+ */
+
+void websSetRequestSocketHandler(webs_t wp, int mask, void (*fn)(webs_t wp))
+{
+ a_assert(websValid(wp));
+
+ wp->writeSocket = fn;
+ socketCreateHandler(wp->sid, SOCKET_WRITABLE, websSocketEvent, (int) wp);
+}
+
+/******************************************************************************/
+/*
+ * Set the number of bytes written
+ */
+
+void websSetRequestWritten(webs_t wp, int written)
+{
+ a_assert(websValid(wp));
+
+ wp->written = written;
+}
+
+/******************************************************************************/
+/*
+ * Reurn true if the webs handle is valid
+ */
+
+int websValid(webs_t wp)
+{
+ int wid;
+
+ for (wid = 0; wid < websMax; wid++) {
+ if (wp == webs[wid]) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Build an ASCII time string. If sbuf is NULL we use the current time,
+ * else we use the last modified time of sbuf;
+ */
+
+char_t *websGetDateString(websStatType *sbuf)
+{
+ char_t* cp, *r;
+ time_t now;
+
+ if (sbuf == NULL) {
+ time(&now);
+ } else {
+ now = sbuf->mtime;
+ }
+ if ((cp = gctime(&now)) != NULL) {
+ cp[gstrlen(cp) - 1] = '\0';
+ r = bstrdup(B_L, cp);
+ return r;
+ }
+ return NULL;
+}
+
+/******************************************************************************/
+/*
+ * Mark time. Set a timestamp so that, later, we can return the number of
+ * seconds since we made the mark. Note that the mark my not be a
+ * "real" time, but rather a relative marker.
+ */
+
+void websSetTimeMark(webs_t wp)
+{
+ wp->timestamp = time(0);
+}
+
+/******************************************************************************/
+/*
+ * Get the number of seconds since the last mark.
+ */
+
+static int websGetTimeSinceMark(webs_t wp)
+{
+ return time(0) - wp->timestamp;
+}
+
+/******************************************************************************/
+/*
+ * Store the new realm name
+ */
+
+void websSetRealm(char_t *realmName)
+{
+ a_assert(realmName);
+
+ gstrncpy(websRealm, realmName, TSZ(websRealm));
+}
+
+/******************************************************************************/
+/*
+ * Return the realm name (used for authorization)
+ */
+
+char_t *websGetRealm()
+{
+ return websRealm;
+}
+
+
+#ifdef WEBS_IF_MODIFIED_SUPPORT
+/******************************************************************************/
+/*
+ * These functions are intended to closely mirror the syntax for HTTP-date
+ * from RFC 2616 (HTTP/1.1 spec). This code was submitted by Pete Bergstrom.
+ */
+
+/*
+ * RFC1123Date = wkday "," SP date1 SP time SP "GMT"
+ * RFC850Date = weekday "," SP date2 SP time SP "GMT"
+ * ASCTimeDate = wkday SP date3 SP time SP 4DIGIT
+ *
+ * Each of these functions tries to parse the value and update the index to
+ * the point it leaves off parsing.
+ */
+
+typedef enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC } MonthEnumeration;
+typedef enum { SUN, MON, TUE, WED, THU, FRI, SAT } WeekdayEnumeration;
+
+/******************************************************************************/
+/*
+ * Parse an N-digit value
+ */
+
+static int parseNDIGIT(char_t *buf, int digits, int *index)
+{
+ int tmpIndex, returnValue;
+
+ returnValue = 0;
+
+ for (tmpIndex = *index; tmpIndex < *index+digits; tmpIndex++) {
+ if (gisdigit(buf[tmpIndex])) {
+ returnValue = returnValue * 10 + (buf[tmpIndex] - T('0'));
+ }
+ }
+ *index = tmpIndex;
+
+ return returnValue;
+}
+
+/******************************************************************************/
+/*
+ * Return an index into the month array
+ */
+
+static int parseMonth(char_t *buf, int *index)
+{
+/*
+ * "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" |
+ * "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"
+ */
+ int tmpIndex, returnValue;
+
+ returnValue = -1;
+ tmpIndex = *index;
+
+ switch (buf[tmpIndex]) {
+ case 'A':
+ switch (buf[tmpIndex+1]) {
+ case 'p':
+ returnValue = APR;
+ break;
+ case 'u':
+ returnValue = AUG;
+ break;
+ }
+ break;
+ case 'D':
+ returnValue = DEC;
+ break;
+ case 'F':
+ returnValue = FEB;
+ break;
+ case 'J':
+ switch (buf[tmpIndex+1]) {
+ case 'a':
+ returnValue = JAN;
+ break;
+ case 'u':
+ switch (buf[tmpIndex+2]) {
+ case 'l':
+ returnValue = JUL;
+ break;
+ case 'n':
+ returnValue = JUN;
+ break;
+ }
+ break;
+ }
+ break;
+ case 'M':
+ switch (buf[tmpIndex+1]) {
+ case 'a':
+ switch (buf[tmpIndex+2]) {
+ case 'r':
+ returnValue = MAR;
+ break;
+ case 'y':
+ returnValue = MAY;
+ break;
+ }
+ break;
+ }
+ break;
+ case 'N':
+ returnValue = NOV;
+ break;
+ case 'O':
+ returnValue = OCT;
+ break;
+ case 'S':
+ returnValue = SEP;
+ break;
+ }
+
+ if (returnValue >= 0) {
+ *index += 3;
+ }
+
+ return returnValue;
+}
+
+/******************************************************************************/
+/*
+ * Parse a year value (either 2 or 4 digits)
+ */
+
+static int parseYear(char_t *buf, int *index)
+{
+ int tmpIndex, returnValue;
+
+ tmpIndex = *index;
+ returnValue = parseNDIGIT(buf, 4, &tmpIndex);
+
+ if (returnValue >= 0) {
+ *index = tmpIndex;
+ } else {
+ returnValue = parseNDIGIT(buf, 2, &tmpIndex);
+ if (returnValue >= 0) {
+/*
+ * Assume that any year earlier than the start of the
+ * epoch for time_t (1970) specifies 20xx
+ */
+ if (returnValue < 70) {
+ returnValue += 2000;
+ } else {
+ returnValue += 1900;
+ }
+
+ *index = tmpIndex;
+ }
+ }
+
+ return returnValue;
+}
+
+/******************************************************************************/
+/*
+ * The formulas used to build these functions are from "Calendrical Calculations",
+ * by Nachum Dershowitz, Edward M. Reingold, Cambridge University Press, 1997.
+ */
+
+#include <math.h>
+
+const int GregorianEpoch = 1;
+
+/******************************************************************************/
+/*
+ * Determine if year is a leap year
+ */
+
+int GregorianLeapYearP(long year)
+{
+ int result;
+ long tmp;
+
+ tmp = year % 400;
+
+ if ((year % 4 == 0) &&
+ (tmp != 100) &&
+ (tmp != 200) &&
+ (tmp != 300)) {
+ result = TRUE;
+ } else {
+ result = FALSE;
+ }
+
+ return result;
+}
+
+/******************************************************************************/
+/*
+ * Return the fixed date from the gregorian date
+ */
+
+long FixedFromGregorian(long month, long day, long year)
+{
+ long fixedDate;
+
+ fixedDate = (long)(GregorianEpoch - 1 + 365 * (year - 1) +
+ floor((year - 1) / 4.0) -
+ floor((double)(year - 1) / 100.0) +
+ floor((double)(year - 1) / 400.0) +
+ floor((367.0 * ((double)month) - 362.0) / 12.0));
+
+ if (month <= 2) {
+ fixedDate += 0;
+ } else if (TRUE == GregorianLeapYearP(year)) {
+ fixedDate += -1;
+ } else {
+ fixedDate += -2;
+ }
+
+ fixedDate += day;
+
+ return fixedDate;
+}
+
+/******************************************************************************/
+/*
+ * Return the gregorian year from a fixed date
+ */
+
+long GregorianYearFromFixed(long fixedDate)
+{
+ long result, d0, n400, d1, n100, d2, n4, d3, n1, d4, year;
+
+ d0 = fixedDate - GregorianEpoch;
+ n400 = (long)(floor((double)d0 / (double)146097));
+ d1 = d0 % 146097;
+ n100 = (long)(floor((double)d1 / (double)36524));
+ d2 = d1 % 36524;
+ n4 = (long)(floor((double)d2 / (double)1461));
+ d3 = d2 % 1461;
+ n1 = (long)(floor((double)d3 / (double)365));
+ d4 = (d3 % 365) + 1;
+ year = 400 * n400 + 100 * n100 + 4 * n4 + n1;
+
+ if ((n100 == 4) || (n1 == 4)) {
+ result = year;
+ } else {
+ result = year + 1;
+ }
+
+ return result;
+}
+
+/******************************************************************************/
+/*
+ * Returns the Gregorian date from a fixed date
+ * (not needed for this use, but included for completeness
+ */
+
+#if 0
+GregorianFromFixed(long fixedDate, long *month, long *day, long *year)
+{
+ long priorDays, correction;
+
+ *year = GregorianYearFromFixed(fixedDate);
+ priorDays = fixedDate - FixedFromGregorian(1, 1, *year);
+
+ if (fixedDate < FixedFromGregorian(3,1,*year)) {
+ correction = 0;
+ } else if (true == GregorianLeapYearP(*year)) {
+ correction = 1;
+ } else {
+ correction = 2;
+ }
+
+ *month = (long)(floor((12.0 * (double)(priorDays + correction) + 373.0) / 367.0));
+ *day = fixedDate - FixedFromGregorian(*month, 1, *year);
+}
+#endif
+
+/******************************************************************************/
+/*
+ * Returns the difference between two Gregorian dates
+ */
+
+long GregorianDateDifference( long month1, long day1, long year1,
+ long month2, long day2, long year2)
+{
+ return FixedFromGregorian(month2, day2, year2) -
+ FixedFromGregorian(month1, day1, year1);
+}
+
+
+/******************************************************************************/
+/*
+ * Return the number of seconds into the current day
+ */
+
+#define SECONDS_PER_DAY 24*60*60
+
+static int parseTime(char_t *buf, int *index)
+{
+/*
+ * Format of buf is - 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ */
+ int returnValue, tmpIndex, hourValue, minuteValue, secondValue;
+
+ hourValue = minuteValue = secondValue = -1;
+ returnValue = -1;
+ tmpIndex = *index;
+
+ hourValue = parseNDIGIT(buf, 2, &tmpIndex);
+
+ if (hourValue >= 0) {
+ tmpIndex++;
+ minuteValue = parseNDIGIT(buf, 2, &tmpIndex);
+ if (minuteValue >= 0) {
+ tmpIndex++;
+ secondValue = parseNDIGIT(buf, 2, &tmpIndex);
+ }
+ }
+
+ if ((hourValue >= 0) &&
+ (minuteValue >= 0) &&
+ (secondValue >= 0)) {
+ returnValue = (((hourValue * 60) + minuteValue) * 60) + secondValue;
+ *index = tmpIndex;
+ }
+
+ return returnValue;
+}
+
+/******************************************************************************/
+/*
+ * Return the equivalent of time() given a gregorian date
+ */
+
+static time_t dateToTimet(int year, int month, int day)
+{
+ long dayDifference;
+
+ /*
+ * Bug fix by Jeff Reeder (Jun 14, 2002): 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
+ */
+ dayDifference = FixedFromGregorian(month + 1, day, year) -
+ FixedFromGregorian(1, 1, 1970);
+
+ return dayDifference * SECONDS_PER_DAY;
+}
+
+/******************************************************************************/
+/*
+ * Return the number of seconds between Jan 1, 1970 and the parsed date
+ * (corresponds to documentation for time() function)
+ */
+
+static time_t parseDate1or2(char_t *buf, int *index)
+{
+/*
+ * Format of buf is either
+ * 2DIGIT SP month SP 4DIGIT
+ * or
+ * 2DIGIT "-" month "-" 2DIGIT
+ */
+ int dayValue, monthValue, yearValue, tmpIndex;
+ time_t returnValue;
+
+ returnValue = (time_t) -1;
+ tmpIndex = *index;
+
+ dayValue = monthValue = yearValue = -1;
+
+ if (buf[tmpIndex] == T(',')) {
+/*
+ * Skip over the ", "
+ */
+ tmpIndex += 2;
+
+ dayValue = parseNDIGIT(buf, 2, &tmpIndex);
+ if (dayValue >= 0) {
+/*
+ * Skip over the space or hyphen
+ */
+ tmpIndex++;
+ monthValue = parseMonth(buf, &tmpIndex);
+ if (monthValue >= 0) {
+/*
+ * Skip over the space or hyphen
+ */
+ tmpIndex++;
+ yearValue = parseYear(buf, &tmpIndex);
+ }
+ }
+
+ if ((dayValue >= 0) &&
+ (monthValue >= 0) &&
+ (yearValue >= 0)) {
+ if (yearValue < 1970) {
+/*
+ * Allow for Microsoft IE's year 1601 dates
+ */
+ returnValue = 0;
+ } else {
+ returnValue = dateToTimet(yearValue, monthValue, dayValue);
+ }
+ *index = tmpIndex;
+ }
+ }
+
+ return returnValue;
+}
+
+/******************************************************************************/
+/*
+ * Return the number of seconds between Jan 1, 1970 and the parsed date
+ */
+
+static time_t parseDate3Time(char_t *buf, int *index)
+{
+/*
+ * Format of buf is month SP ( 2DIGIT | ( SP 1DIGIT ))
+ */
+ int dayValue, monthValue, yearValue, timeValue, tmpIndex;
+ time_t returnValue;
+
+ returnValue = (time_t) -1;
+ tmpIndex = *index;
+
+ dayValue = monthValue = yearValue = timeValue = -1;
+
+ monthValue = parseMonth(buf, &tmpIndex);
+ if (monthValue >= 0) {
+/*
+ * Skip over the space
+ */
+ tmpIndex++;
+ if (buf[tmpIndex] == T(' ')) {
+/*
+ * Skip over this space too
+ */
+ tmpIndex++;
+ dayValue = parseNDIGIT(buf, 1, &tmpIndex);
+ } else {
+ dayValue = parseNDIGIT(buf, 2, &tmpIndex);
+ }
+/*
+ * Now get the time and time SP 4DIGIT
+ */
+ timeValue = parseTime(buf, &tmpIndex);
+ if (timeValue >= 0) {
+/*
+ * Now grab the 4DIGIT year value
+ */
+ yearValue = parseYear(buf, &tmpIndex);
+ }
+ }
+
+ if ((dayValue >= 0) &&
+ (monthValue >= 0) &&
+ (yearValue >= 0)) {
+ returnValue = dateToTimet(yearValue, monthValue, dayValue);
+ returnValue += timeValue;
+ *index = tmpIndex;
+ }
+
+ return returnValue;
+}
+
+
+/******************************************************************************/
+/*
+ * Although this looks like a trivial function, I found I was replicating the implementation
+ * seven times in the parseWeekday function. In the interests of minimizing code size
+ * and redundancy, it is broken out into a separate function. The cost of an extra
+ * function call I can live with given that it should only be called once per HTTP request.
+ */
+
+static int bufferIndexIncrementGivenNTest(char_t *buf, int testIndex, char_t testChar,
+ int foundIncrement, int notfoundIncrement)
+{
+ if (buf[testIndex] == testChar) {
+ return foundIncrement;
+ }
+
+ return notfoundIncrement;
+}
+
+/******************************************************************************/
+/*
+ * Return an index into a logical weekday array
+ */
+
+static int parseWeekday(char_t *buf, int *index)
+{
+/*
+ * Format of buf is either
+ * "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun"
+ * or
+ * "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday"
+ */
+ int tmpIndex, returnValue;
+
+ returnValue = -1;
+ tmpIndex = *index;
+
+ switch (buf[tmpIndex]) {
+ case 'F':
+ returnValue = FRI;
+ *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'd', sizeof("Friday"), 3);
+ break;
+ case 'M':
+ returnValue = MON;
+ *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'd', sizeof("Monday"), 3);
+ break;
+ case 'S':
+ switch (buf[tmpIndex+1]) {
+ case 'a':
+ returnValue = SAT;
+ *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'u', sizeof("Saturday"), 3);
+ break;
+ case 'u':
+ returnValue = SUN;
+ *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'd', sizeof("Sunday"), 3);
+ break;
+ }
+ break;
+ case 'T':
+ switch (buf[tmpIndex+1]) {
+ case 'h':
+ returnValue = THU;
+ *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'r', sizeof("Thursday"), 3);
+ break;
+ case 'u':
+ returnValue = TUE;
+ *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 's', sizeof("Tuesday"), 3);
+ break;
+ }
+ break;
+ case 'W':
+ returnValue = WED;
+ *index += bufferIndexIncrementGivenNTest(buf, tmpIndex+3, 'n', sizeof("Wednesday"), 3);
+ break;
+ }
+ return returnValue;
+}
+
+/******************************************************************************/
+/*
+ * Parse the date and time string.
+ */
+
+static time_t dateParse(time_t tip, char_t *cmd)
+{
+ int index, tmpIndex, weekday, timeValue;
+ time_t parsedValue, dateValue;
+
+ parsedValue = (time_t) 0;
+ index = timeValue = 0;
+ weekday = parseWeekday(cmd, &index);
+
+ if (weekday >= 0) {
+ tmpIndex = index;
+ dateValue = parseDate1or2(cmd, &tmpIndex);
+ if (dateValue >= 0) {
+ index = tmpIndex + 1;
+/*
+ * One of these two forms is being used
+ * wkday "," SP date1 SP time SP "GMT"
+ * weekday "," SP date2 SP time SP "GMT"
+ */
+ timeValue = parseTime(cmd, &index);
+ if (timeValue >= 0) {
+/*
+ * Now match up that "GMT" string for completeness
+ * Compute the final value if there were no problems in the parse
+ */
+ if ((weekday >= 0) &&
+ (dateValue >= 0) &&
+ (timeValue >= 0)) {
+ parsedValue = dateValue + timeValue;
+ }
+ }
+ } else {
+/*
+ * Try the other form - wkday SP date3 SP time SP 4DIGIT
+ */
+ tmpIndex = index;
+ parsedValue = parseDate3Time(cmd, &tmpIndex);
+ }
+ }
+
+ return parsedValue;
+}
+
+#endif /* WEBS_IF_MODIFIED_SUPPORT */
+
+/******************************************************************************/
+
diff --git a/cleopatre/application/spidgoahead/webs.h b/cleopatre/application/spidgoahead/webs.h
new file mode 100644
index 0000000000..4548d5b090
--- /dev/null
+++ b/cleopatre/application/spidgoahead/webs.h
@@ -0,0 +1,234 @@
+/*
+ * webs.h -- GoAhead Web public header
+ *
+ * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved.
+ *
+ * See the file "license.txt" for information on usage and redistribution
+ *
+ * $Id: webs.h,v 1.10 2003/12/01 23:52:30 hwolff Exp $
+ */
+
+#ifndef _h_WEBS
+#define _h_WEBS 1
+
+/******************************** Description *********************************/
+
+/*
+ * GoAhead Web Server header. This defines the Web public APIs.
+ * Include this header for files that contain ASP or Form procedures.
+ * Include wsIntrn.h when creating URL handlers.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "ej.h"
+#ifdef WEBS_SSL_SUPPORT
+ #include "websSSL.h"
+#endif
+
+/********************************** Defines ***********************************/
+/*
+ * By license terms the server software name defined in the following line of
+ * code must not be modified.
+ */
+#define WEBS_NAME T("GoAhead-Webs")
+#define WEBS_VERSION T("2.1.8")
+
+#define WEBS_HEADER_BUFINC 512 /* Header buffer size */
+#define WEBS_ASP_BUFINC 512 /* Asp expansion increment */
+#define WEBS_MAX_PASS 32 /* Size of password */
+#define WEBS_BUFSIZE 1000 /* websWrite max output string */
+#define WEBS_MAX_HEADER (5 * 1024) /* Sanity check header */
+#define WEBS_MAX_URL 4096 /* Maximum URL size for sanity */
+#define WEBS_SOCKET_BUFSIZ 256 /* Bytes read from socket */
+
+#define WEBS_HTTP_PORT T("httpPort")
+#define CGI_BIN T("cgi-bin")
+
+/*
+ * Request flags. Also returned by websGetRequestFlags().
+ */
+#define WEBS_LOCAL_PAGE 0x1 /* Request for local webs page */
+#define WEBS_KEEP_ALIVE 0x2 /* HTTP/1.1 keep alive */
+#define WEBS_DONT_USE_CACHE 0x4 /* Not implemented cache support */
+#define WEBS_COOKIE 0x8 /* Cookie supplied in request */
+#define WEBS_IF_MODIFIED 0x10 /* If-modified-since in request */
+#define WEBS_POST_REQUEST 0x20 /* Post request operation */
+#define WEBS_LOCAL_REQUEST 0x40 /* Request from this system */
+#define WEBS_HOME_PAGE 0x80 /* Request for the home page */
+#define WEBS_ASP 0x100 /* ASP request */
+#define WEBS_HEAD_REQUEST 0x200 /* Head request */
+#define WEBS_CLEN 0x400 /* Request had a content length */
+#define WEBS_FORM 0x800 /* Request is a form */
+#define WEBS_REQUEST_DONE 0x1000 /* Request complete */
+#define WEBS_POST_DATA 0x2000 /* Already appended post data */
+#define WEBS_CGI_REQUEST 0x4000 /* cgi-bin request */
+#define WEBS_SECURE 0x8000 /* connection uses SSL */
+#define WEBS_AUTH_BASIC 0x10000 /* Basic authentication request */
+#define WEBS_AUTH_DIGEST 0x20000 /* Digest authentication request */
+#define WEBS_HEADER_DONE 0x40000 /* Already output the HTTP header */
+
+/*
+ * URL handler flags
+ */
+#define WEBS_HANDLER_FIRST 0x1 /* Process this handler first */
+#define WEBS_HANDLER_LAST 0x2 /* Process this handler last */
+
+/*
+ * Per socket connection webs structure
+ */
+typedef struct websRec {
+ ringq_t header; /* Header dynamic string */
+ time_t since; /* Parsed if-modified-since time */
+ sym_fd_t cgiVars; /* CGI standard variables */
+ sym_fd_t cgiQuery; /* CGI decoded query string */
+ time_t timestamp; /* Last transaction with browser */
+ int timeout; /* Timeout handle */
+ char_t ipaddr[32]; /* Connecting ipaddress */
+ char_t type[64]; /* Mime type */
+ char_t *dir; /* Directory containing the page */
+ char_t *path; /* Path name without query */
+ char_t *url; /* Full request url */
+ char_t *host; /* Requested host */
+ char_t *lpath; /* Cache local path name */
+ char_t *query; /* Request query */
+ char_t *decodedQuery; /* Decoded request query */
+ char_t *authType; /* Authorization type (Basic/DAA) */
+ char_t *password; /* Authorization password */
+ char_t *userName; /* Authorization username */
+ char_t *cookie; /* Cookie string */
+ char_t *userAgent; /* User agent (browser) */
+ char_t *protocol; /* Protocol (normally HTTP) */
+ char_t *protoVersion; /* Protocol version */
+ int sid; /* Socket id (handler) */
+ int listenSid; /* Listen Socket id */
+ int port; /* Request port number */
+ int state; /* Current state */
+ int flags; /* Current flags -- see above */
+ int code; /* Request result code */
+ int clen; /* Content length */
+ int wid; /* Index into webs */
+ char_t *cgiStdin; /* filename for CGI stdin */
+ int docfd; /* Document file descriptor */
+ int numbytes; /* Bytes to transfer to browser */
+ int written; /* Bytes actually transferred */
+ void (*writeSocket)(struct websRec *wp);
+#ifdef DIGEST_ACCESS_SUPPORT
+ char_t *realm; /* usually the same as "host" from websRec */
+ char_t *nonce; /* opaque-to-client string sent by server */
+ char_t *digest; /* digest form of user password */
+ char_t *uri; /* URI found in DAA header */
+ char_t *opaque; /* opaque value passed from server */
+ char_t *nc; /* nonce count */
+ char_t *cnonce; /* check nonce */
+ char_t *qop; /* quality operator */
+#endif
+#ifdef WEBS_SSL_SUPPORT
+ websSSL_t *wsp; /* SSL data structure */
+#endif
+} websRec;
+
+typedef websRec *webs_t;
+typedef websRec websType;
+
+/******************************** Prototypes **********************************/
+extern int websAccept(int sid, char *ipaddr, int port, int listenSid);
+extern int websAspDefine(char_t *name,
+ int (*fn)(int ejid, webs_t wp, int argc, char_t **argv));
+extern int websAspRequest(webs_t wp, char_t *lpath);
+extern void websCloseListen();
+extern int websDecode64(char_t *outbuf, char_t *string, int buflen);
+extern void websDecodeUrl(char_t *token, char_t *decoded, int len);
+extern void websDone(webs_t wp, int code);
+extern void websEncode64(char_t *outbuf, char_t *string, int buflen);
+extern void websError(webs_t wp, int code, char_t *msg, ...);
+/* function websErrorMsg() made extern 03 Jun 02 BgP */
+extern char_t *websErrorMsg(int code);
+extern void websFooter(webs_t wp);
+extern int websFormDefine(char_t *name, void (*fn)(webs_t wp,
+ char_t *path, char_t *query));
+extern char_t *websGetDefaultDir();
+extern char_t *websGetDefaultPage();
+extern char_t *websGetHostUrl();
+extern char_t *websGetIpaddrUrl();
+extern char_t *websGetPassword();
+extern int websGetPort();
+extern char_t *websGetPublishDir(char_t *path, char_t **urlPrefix);
+extern char_t *websGetRealm();
+extern int websGetRequestBytes(webs_t wp);
+extern char_t *websGetRequestDir(webs_t wp);
+extern int websGetRequestFlags(webs_t wp);
+extern char_t *websGetRequestIpaddr(webs_t wp);
+extern char_t *websGetRequestLpath(webs_t wp);
+extern char_t *websGetRequestPath(webs_t wp);
+extern char_t *websGetRequestPassword(webs_t wp);
+extern char_t *websGetRequestType(webs_t wp);
+extern int websGetRequestWritten(webs_t wp);
+extern char_t *websGetVar(webs_t wp, char_t *var, char_t *def);
+extern int websCompareVar(webs_t wp, char_t *var, char_t *value);
+extern void websHeader(webs_t wp);
+extern int websOpenListen(int port, int retries);
+extern int websPageOpen(webs_t wp, char_t *lpath, char_t *path,
+ int mode, int perm);
+extern void websPageClose(webs_t wp);
+extern int websPublish(char_t *urlPrefix, char_t *path);
+extern void websRedirect(webs_t wp, char_t *url);
+extern void websSecurityDelete();
+extern int websSecurityHandler(webs_t wp, char_t *urlPrefix,
+ char_t *webDir, int arg, char_t *url, char_t *path,
+ char_t *query);
+extern void websSetDefaultDir(char_t *dir);
+extern void websSetDefaultPage(char_t *page);
+extern void websSetEnv(webs_t wp);
+extern void websSetHost(char_t *host);
+extern void websSetIpaddr(char_t *ipaddr);
+extern void websSetPassword(char_t *password);
+extern void websSetRealm(char_t *realmName);
+extern void websSetRequestBytes(webs_t wp, int bytes);
+extern void websSetRequestFlags(webs_t wp, int flags);
+extern void websSetRequestLpath(webs_t wp, char_t *lpath);
+extern void websSetRequestPath(webs_t wp, char_t *dir, char_t *path);
+extern char_t *websGetRequestUserName(webs_t wp);
+extern void websSetRequestWritten(webs_t wp, int written);
+extern void websSetVar(webs_t wp, char_t *var, char_t *value);
+extern int websTestVar(webs_t wp, char_t *var);
+extern void websTimeoutCancel(webs_t wp);
+extern int websUrlHandlerDefine(char_t *urlPrefix, char_t *webDir,
+ int arg, int (*fn)(webs_t wp, char_t *urlPrefix,
+ char_t *webDir, int arg, char_t *url, char_t *path,
+ char_t *query), int flags);
+extern int websUrlHandlerDelete(int (*fn)(webs_t wp, char_t *urlPrefix,
+ char_t *webDir, int arg, char_t *url, char_t *path,
+ char_t *query));
+extern int websUrlHandlerRequest(webs_t wp);
+extern int websUrlParse(char_t *url, char_t **buf, char_t **host,
+ char_t **path, char_t **port, char_t **query,
+ char_t **proto, char_t **tag, char_t **ext);
+extern char_t *websUrlType(char_t *webs, char_t *buf, int charCnt);
+extern int websWrite(webs_t wp, char_t* fmt, ...);
+extern int websWriteBlock(webs_t wp, char_t *buf, int nChars);
+extern int websWriteDataNonBlock(webs_t wp, char *buf, int nChars);
+extern int websValid(webs_t wp);
+extern int websValidateUrl(webs_t wp, char_t *path);
+extern void websSetTimeMark(webs_t wp);
+
+/*
+ * The following prototypes are used by the SSL patch found in websSSL.c
+ */
+extern int websAlloc(int sid);
+extern void websFree(webs_t wp);
+extern void websTimeout(void *arg, int id);
+extern void websReadEvent(webs_t wp);
+
+/*
+ * Prototypes for functions available when running as part of the
+ * GoAhead Embedded Management Framework (EMF)
+ */
+#ifdef EMF
+extern void websFormExplain(webs_t wp, char_t *path, char_t *query);
+#endif
+
+#endif /* _h_WEBS */
+
+/******************************************************************************/
+
diff --git a/cleopatre/application/spidgoahead/websSSL.c b/cleopatre/application/spidgoahead/websSSL.c
new file mode 100644
index 0000000000..5937418395
--- /dev/null
+++ b/cleopatre/application/spidgoahead/websSSL.c
@@ -0,0 +1,709 @@
+/*
+ * websSSL.c -- SSL envrionment creation
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ *
+ * $Id: websSSL.c,v 1.3 2003/09/29 20:30:48 bporter Exp $
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module implements a patch into SSL implementations for the webs
+ * module.
+ */
+
+#ifndef __ENABLE_MOCANA_SSL_SERVER__
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+#include "webs.h"
+#include "websSSL.h"
+
+/******************************* Definitions **********************************/
+
+#define DEFAULT_CERT_FILE "/etc/server.pem"
+#define DEFAULT_KEY_FILE "/etc/certs/cakey.pem"
+#define DEFAULT_CA_FILE "/etc/certs/cacert.pem"
+#define DEFAULT_CA_PATH "/etc/certs/"
+#define SSL_PORT 443
+
+/*
+ * Define the components of the apps_startup() macro
+ */
+
+#ifdef SIGPIPE
+#define do_pipe_sig() signal(SIGPIPE,SIG_IGN)
+#else
+#define do_pipe_sig()
+#endif
+
+#ifdef OPENSSL
+#define SSLC_add_all_algorithms() SSLeay_add_all_algorithms()
+#else
+extern void SSLC_add_all_algorithms(void);
+#endif
+
+/*
+ * Define the apps_startup() macro
+ */
+
+# if defined(MSDOS) || defined(WIN16) || defined(WIN32)
+# ifdef _O_BINARY
+# define apps_startup() \
+ _fmode=_O_BINARY; do_pipe_sig(); CRYPTO_malloc_init(); \
+ SSLC_add_all_algorithms()
+# else
+# define apps_startup() \
+ _fmode=O_BINARY; do_pipe_sig(); CRYPTO_malloc_init(); \
+ SSLC_add_all_algorithms()
+# endif
+# else
+# define apps_startup() do_pipe_sig(); SSLC_add_all_algorithms();
+# endif
+
+/*************************** Forward Declarations *****************************/
+
+static int websSSLSetCertStuff(SSL_CTX *ctx,
+ char *cert_file,
+ char *key_file);
+static int websSSLVerifyCallback(int ok, X509_STORE_CTX *ctx);
+static RSA *websSSLTempRSACallback(SSL *s, int is_export, int keylength);
+
+static int websSSLReadEvent (webs_t wp);
+static int websSSLAccept(int sid, char *ipaddr, int port, int listenSid);
+static void websSSLSocketEvent(int sid, int mask, int data);
+
+/*********************************** Locals ***********************************/
+
+static int sslListenSock = -1; /* Listen socket */
+static SSL_CTX *sslctx = NULL;
+
+/******************************************************************************/
+/*
+ * 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()
+{
+ char *certFile, *keyFile, *CApath, *CAfile;
+ SSL_METHOD *meth;
+
+/*
+ * Install and initialize the SSL library
+ */
+ apps_startup();
+ trace(7, T("SSL: Initializing SSL\n"));
+
+#ifdef SSLC
+ SSL_library_init();
+#endif
+
+ SSL_load_error_strings();
+
+#ifdef OPENSSL
+ SSLeay_add_ssl_algorithms();
+#endif
+
+/*
+ * Important! Enable both SSL versions 2 and 3
+ */
+ meth = SSLv23_server_method();
+ sslctx = SSL_CTX_new(meth);
+
+ a_assert(sslctx);
+
+ if (sslctx == NULL) {
+ trace(2, T("SSL: Unable to create SSL context!\n"));
+ return -1;
+ }
+
+/*
+ * Adjust some SSL Context variables
+ */
+ SSL_CTX_set_quiet_shutdown(sslctx, 1);
+ SSL_CTX_set_options(sslctx, 0);
+ SSL_CTX_sess_set_cache_size(sslctx, 128);
+
+/*
+ * Set the certificate verification locations
+ */
+ CApath = DEFAULT_CA_PATH;
+ CAfile = DEFAULT_CA_FILE;
+ if ((!SSL_CTX_load_verify_locations(sslctx, CAfile, CApath)) ||
+ (!SSL_CTX_set_default_verify_paths(sslctx))) {
+ trace(2, T("SSL: Unable to set cert verification locations!\n"));
+ websSSLClose();
+ return -1;
+ }
+
+/*
+ * Set the certificate and key files for the SSL context
+ */
+ certFile = DEFAULT_CERT_FILE;
+ keyFile = NULL;
+ if (websSSLSetCertStuff(sslctx, certFile, keyFile) != 0) {
+ websSSLClose();
+ return -1;
+ }
+
+/*
+ * Set the RSA callback for the SSL context
+ */
+ SSL_CTX_set_tmp_rsa_callback(sslctx, websSSLTempRSACallback);
+
+/*
+ * Set the verification callback for the SSL context
+ */
+ SSL_CTX_set_verify(sslctx, SSL_VERIFY_NONE, websSSLVerifyCallback);
+
+/*
+ * Set the certificate authority list for the client
+ */
+ SSL_CTX_set_client_CA_list(sslctx, SSL_load_client_CA_file(CAfile));
+
+/*
+ * Open the socket
+ */
+ 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;
+}
+
+/******************************************************************************/
+/*
+ * Return TRUE if websSSL has been opened
+ */
+
+int websSSLIsOpen()
+{
+ return (sslListenSock != -1);
+}
+
+/******************************************************************************/
+/*
+ * Stops the SSL
+ */
+
+void websSSLClose()
+{
+ trace(7, T("SSL: Closing SSL\n"));
+
+ if (sslctx != NULL) {
+ SSL_CTX_free(sslctx);
+ sslctx = NULL;
+ }
+
+ if (sslListenSock != -1) {
+ socketCloseConnection(sslListenSock);
+ sslListenSock = -1;
+ }
+
+#ifdef SSLC
+ SSL_library_cleanup();
+#endif
+}
+
+/******************************************************************************/
+/*
+ * 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;
+}
+
+/******************************************************************************/
+/*
+ * 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, sock;
+ socket_t *sptr;
+ SSL *ssl;
+ BIO *bio, *bioSSL, *bioSock;
+#ifdef DEV
+ const char *ciphers;
+#endif
+
+ a_assert (wp);
+ a_assert(websValid(wp));
+
+ sptr = socketPtr(wp->sid);
+ a_assert(sptr);
+
+ sock = sptr->sock;
+
+/*
+ * Create a new BIO and SSL session for this web request
+ */
+ bio = BIO_new(BIO_f_buffer());
+ a_assert(bio);
+
+ if (!BIO_set_write_buffer_size(bio, 128)) {
+ return -1;
+ }
+
+ ssl = (SSL *) SSL_new(sslctx);
+ a_assert(ssl);
+
+ if (ssl == NULL) {
+ return -1;
+ }
+
+ SSL_set_session(ssl, NULL);
+
+ bioSSL = BIO_new(BIO_f_ssl());
+ a_assert(bioSSL);
+
+ bioSock = BIO_new_socket(sock, BIO_NOCLOSE);
+ a_assert(bioSock);
+
+ SSL_set_bio(ssl, bioSock, bioSock);
+ SSL_set_accept_state(ssl);
+
+ ret = BIO_set_ssl(bioSSL, ssl, BIO_CLOSE);
+ BIO_push(bio, bioSSL);
+
+#ifdef DEV
+ ciphers = SSL_get_cipher_list(ssl, 10);
+#endif
+
+/*
+ * Create the SSL data structure in the wp.
+ */
+#ifdef WEBS_SSL_SUPPORT
+ wp->wsp = balloc(B_L, sizeof(websSSL_t));
+ a_assert (wp->wsp);
+ (wp->wsp)->bio = bio;
+ (wp->wsp)->ssl = ssl;
+#endif
+
+/*
+ * Call the default Read Event
+ */
+ websReadEvent(wp);
+
+ return ret;
+}
+
+
+/******************************************************************************/
+/*
+ * SSL Verification Callback
+ */
+
+static int sslVerifyDepth = 0;
+static int sslVerifyError = X509_V_OK;
+
+int websSSLVerifyCallback(int ok, X509_STORE_CTX *ctx)
+{
+ char buf[256];
+ X509 *errCert;
+ int err;
+ int depth;
+
+ errCert = X509_STORE_CTX_get_current_cert(ctx);
+ err = X509_STORE_CTX_get_error(ctx);
+ depth = X509_STORE_CTX_get_error_depth(ctx);
+
+ X509_NAME_oneline(X509_get_subject_name(errCert), buf, 256);
+
+ if (!ok) {
+ if (sslVerifyDepth >= depth) {
+ ok = 1;
+ sslVerifyError = X509_V_OK;
+ } else {
+ ok=0;
+ sslVerifyError = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+ }
+ }
+
+ switch (err) {
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+#ifdef OPENSSL
+ X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
+#endif
+ break;
+
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ break;
+ }
+
+ return ok;
+}
+
+/******************************************************************************/
+/*
+ * Set the SSL certificate and key for the SSL context
+ */
+
+int websSSLSetCertStuff(SSL_CTX *ctx, char *certFile, char *keyFile)
+{
+ a_assert (ctx);
+ a_assert (certFile);
+
+ if (certFile != NULL) {
+ if (SSL_CTX_use_certificate_file(ctx, certFile,
+ SSL_FILETYPE_PEM) <= 0) {
+ trace(2, T("SSL: Unable to set certificate file <%s>\n"),
+ certFile);
+ return -1;
+ }
+
+ if (keyFile == NULL) {
+ keyFile = certFile;
+ }
+
+ if (SSL_CTX_use_PrivateKey_file(ctx, keyFile, SSL_FILETYPE_PEM) <= 0) {
+ trace(2, T("SSL: Unable to set private key file <%s>\n"),
+ keyFile);
+ return -1;
+ }
+
+/*
+ * Now we know that a key and cert have been set against
+ * the SSL context
+ */
+ if (!SSL_CTX_check_private_key(ctx)) {
+ trace(2, T("SSL: Check of private key file <%s> FAILED!\n"),
+ keyFile);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Set certificate file for SSL context
+ */
+
+int websSSLSetCertFile(char_t *certFile)
+{
+ a_assert (sslctx);
+ a_assert (certFile);
+
+ if (sslctx == NULL) {
+ return -1;
+ }
+
+ if (SSL_CTX_use_certificate_file(sslctx, certFile,
+ SSL_FILETYPE_PEM) <= 0) {
+ return -1;
+ }
+/*
+ * Confirm that the certificate and the private key jive.
+ */
+ if (!SSL_CTX_check_private_key(sslctx)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Set key file for SSL context
+ */
+
+int websSSLSetKeyFile(char_t *keyFile)
+{
+ a_assert (sslctx);
+ a_assert (keyFile);
+
+ if (sslctx == NULL) {
+ return -1;
+ }
+
+ if (SSL_CTX_use_PrivateKey_file(sslctx, keyFile, SSL_FILETYPE_PEM) <= 0) {
+ return -1;
+ }
+/*
+ * Confirm that the certificate and the private key jive.
+ */
+ if (!SSL_CTX_check_private_key(sslctx)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef SSLC
+extern RSA *RSA_new(void);
+#endif
+
+/******************************************************************************/
+/*
+ * the Temporary RSA callback
+ */
+
+static RSA *websSSLTempRSACallback(SSL *ssl, int isExport, int keyLength)
+{
+ static RSA *rsaTemp = NULL;
+
+ if (rsaTemp == NULL) {
+
+#ifdef OPENSSL
+ rsaTemp = RSA_generate_key(keyLength, RSA_F4, NULL, NULL);
+#endif
+
+#ifdef SSLC
+ rsaTemp = RSA_new();
+#endif
+
+ }
+
+ return rsaTemp;
+}
+
+/******************************************************************************/
+/*
+ * Free SSL resources
+ */
+
+int websSSLFree(websSSL_t *wsp)
+{
+ if (wsp == NULL) {
+ return -1;
+ }
+
+/*
+ * Make sure we re-use sessions
+ */
+ if (wsp->ssl != NULL) {
+ SSL_set_shutdown(wsp->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
+ }
+
+ if (wsp->bio != NULL) {
+ BIO_free_all(wsp->bio);
+ }
+
+ bfree(B_L, wsp);
+
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Return Eof for the SSL BIO
+ */
+
+int websSSLEof(websSSL_t *wsp)
+{
+ a_assert(wsp);
+
+ if ((wsp == NULL) || (wsp->bio == NULL)) {
+ return -1;
+ }
+
+ return BIO_eof(wsp->bio);
+}
+
+/******************************************************************************/
+/*
+ * Perform a read of the SSL BIO
+ */
+
+int websSSLRead(websSSL_t *wsp, char_t *buf, int len)
+{
+ a_assert(wsp);
+ a_assert(buf);
+
+ if ((wsp == NULL) || (wsp->bio == NULL)) {
+ return -1;
+ }
+
+ return BIO_read(wsp->bio, buf, len);
+}
+
+/******************************************************************************/
+/*
+ * Perform a gets of the SSL BIO, returning an balloc'ed string
+ */
+
+#define BUF_BLOCK 256
+
+int websSSLGets(websSSL_t *wsp, char_t **buf)
+{
+ int rc, len, lenBuf;
+ char c;
+
+ a_assert(wsp);
+ a_assert(buf);
+
+ lenBuf = 0;
+ len = 0;
+
+ if ((wsp == NULL) || (wsp->bio == NULL)) {
+ return -1;
+ }
+
+ while (1) {
+
+ if ((rc = BIO_read(wsp->bio, &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 (len > 0 && BIO_eof(wsp->bio)) {
+ 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') {
+ if ((len > 0) && (len < lenBuf)) {
+ (*buf)[len] = 0;
+ }
+ return len;
+ } else if (c == '\r') {
+ continue;
+ }
+/*
+ * Append character to buf
+ */
+ if (len >= lenBuf) {
+ lenBuf += BUF_BLOCK;
+ *buf = brealloc(B_L, *buf, lenBuf);
+ }
+
+ a_assert(*buf);
+ (*buf)[len] = c;
+ len++;
+ }
+}
+
+/******************************************************************************/
+/*
+ * Perform a write to the SSL BIO
+ */
+
+int websSSLWrite(websSSL_t *wsp, char_t *buf, int len)
+{
+ a_assert(wsp);
+ a_assert(buf);
+
+ if ((wsp == NULL) || (wsp->bio == NULL)) {
+ return -1;
+ }
+
+ return BIO_write(wsp->bio, buf, len);
+}
+
+/******************************************************************************/
+/*
+ * Perform a flush of the SSL BIO
+ */
+
+int websSSLFlush(websSSL_t *wsp)
+{
+ a_assert(wsp);
+
+ if ((wsp == NULL) || (wsp->bio == NULL)) {
+ return -1;
+ }
+
+ return BIO_flush(wsp->bio);
+}
+
+/******************************************************************************/
+
+#endif
diff --git a/cleopatre/application/spidgoahead/websSSL.h b/cleopatre/application/spidgoahead/websSSL.h
new file mode 100644
index 0000000000..1afec14310
--- /dev/null
+++ b/cleopatre/application/spidgoahead/websSSL.h
@@ -0,0 +1,66 @@
+/*
+ * websSSL.h -- SSL Patch header
+ *
+ * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved.
+ *
+ * See the file "license.txt" for information on usage and redistribution
+ *
+ * $Id: websSSL.h,v 1.3 2003/09/29 19:50:50 bporter Exp $
+ */
+
+#ifndef _h_websSSL
+#define _h_websSSL 1
+
+/******************************** Description *********************************/
+
+/*
+ * Header file for the GoAhead Patch for SSL. This defines the interface to
+ * integrate SSL into the GoAhead Webserver.
+ */
+
+/********************************* Includes ***********************************/
+
+
+#ifdef OPENSSL
+#define SSLEAY /* turn off a few special case MONOLITH macros */
+#define USE_SOCKETS /* needed for the _O_BINARY defs in the MS world */
+#include <openssl/ssl.h>
+#else
+#include <sslc.h>
+#endif
+
+#ifndef UEMF
+ #include "basic/basic.h"
+ #include "emf/emf.h"
+#else
+ #include "uemf.h"
+#endif
+
+/********************************** Defines ***********************************/
+typedef struct {
+ SSL *ssl;
+ BIO *bio;
+} websSSL_t;
+
+/******************************** Prototypes **********************************/
+
+extern int websSSLOpen();
+extern int websSSLIsOpen();
+extern void websSSLClose();
+
+extern int websSSLWrite(websSSL_t *wsp, char_t *buf, int nChars);
+extern int websSSLGets(websSSL_t *wsp, char_t **buf);
+extern int websSSLRead(websSSL_t *wsp, char_t *buf, int nChars);
+extern int websSSLEof(websSSL_t *wsp);
+
+extern int websSSLFree(websSSL_t *wsp);
+extern int websSSLFlush(websSSL_t *wsp);
+
+extern int websSSLSetKeyFile(char_t *keyFile);
+extern int websSSLSetCertFile(char_t *certFile);
+
+
+#endif /* _h_websSSL */
+
+/*****************************************************************************/
+
diff --git a/cleopatre/application/spidgoahead/websda.c b/cleopatre/application/spidgoahead/websda.c
new file mode 100644
index 0000000000..979c89abd7
--- /dev/null
+++ b/cleopatre/application/spidgoahead/websda.c
@@ -0,0 +1,245 @@
+/*
+ * websda.c -- Digest Access Authentication routines
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ *
+ * $Id: websda.c,v 1.2 2002/10/24 14:44:50 bporter Exp $
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * Routines for generating DAA data. The module uses the
+ * "RSA Data Security, Inc. MD5 Message-Digest Algorithm" found in md5c.c
+ */
+
+/********************************* Includes ***********************************/
+
+#ifndef CE
+#include <time.h>
+#endif
+#include "websda.h"
+#include "md5.h"
+
+/******************************** Local Data **********************************/
+
+#define RANDOMKEY T("onceuponatimeinparadise")
+#define NONCE_SIZE 34
+#define HASH_SIZE 16
+
+/*********************************** Code *************************************/
+/*
+ * websMD5binary returns the MD5 hash
+ */
+
+char *websMD5binary(unsigned char *buf, int length)
+{
+ const char *hex = "0123456789abcdef";
+ MD5_CONTEXT md5ctx;
+ unsigned char hash[HASH_SIZE];
+ char *r, *strReturn;
+ char result[(HASH_SIZE * 2) + 1];
+ int i;
+
+/*
+ * Take the MD5 hash of the string argument.
+ */
+ MD5Init(&md5ctx);
+ MD5Update(&md5ctx, buf, (unsigned int)length);
+ MD5Final(hash, &md5ctx);
+
+/*
+ * Prepare the resulting hash string
+ */
+ for (i = 0, r = result; i < 16; i++) {
+ *r++ = hex[hash[i] >> 4];
+ *r++ = hex[hash[i] & 0xF];
+ }
+
+/*
+ * Zero terminate the hash string
+ */
+ *r = '\0';
+
+/*
+ * Allocate a new copy of the hash string
+ */
+ strReturn = balloc(B_L, sizeof(result));
+ strcpy(strReturn, result);
+
+ return strReturn;
+}
+
+/*****************************************************************************/
+/*
+ * Convenience call to websMD5binary
+ * (Performs char_t to char conversion and back)
+ */
+
+char_t *websMD5(char_t *string)
+{
+ char_t *strReturn;
+
+ a_assert(string && *string);
+
+ if (string && *string) {
+ char *strTemp, *strHash;
+ int nLen;
+/*
+ * Convert input char_t string to char string
+ */
+ nLen = gstrlen(string);
+ strTemp = ballocUniToAsc(string, nLen + 1);
+/*
+ * Execute the digest calculation
+ */
+ strHash = websMD5binary((unsigned char *)strTemp, nLen);
+/*
+ * Convert the returned char string digest to a char_t string
+ */
+ nLen = strlen(strHash);
+ strReturn = ballocAscToUni(strHash, nLen);
+/*
+ * Free up the temporary allocated resources
+ */
+ bfree(B_L, strTemp);
+ bfree(B_L, strHash);
+ } else {
+ strReturn = NULL;
+ }
+
+ return strReturn;
+}
+
+/******************************************************************************/
+/*
+ * Get a Nonce value for passing along to the client. This function
+ * composes the string "RANDOMKEY:timestamp:myrealm" and
+ * calculates the MD5 digest placing it in output.
+ */
+
+char_t *websCalcNonce(webs_t wp)
+{
+ char_t *nonce, *prenonce;
+ struct tm *newtime;
+ time_t longTime;
+
+ a_assert(wp);
+/*
+ * Get time as long integer.
+ */
+ time(&longTime);
+/*
+ * Convert to local time.
+ */
+ newtime = localtime(&longTime);
+/*
+ * Create prenonce string.
+ */
+ prenonce = NULL;
+#ifdef DIGEST_ACCESS_SUPPORT
+ fmtAlloc(&prenonce, 256, T("%s:%s:%s"), RANDOMKEY, gasctime(newtime),
+ wp->realm);
+#else
+ fmtAlloc(&prenonce, 256, T("%s:%s:%s"), RANDOMKEY, gasctime(newtime),
+ RANDOMKEY);
+#endif
+ a_assert(prenonce);
+/*
+ * Create the nonce
+ */
+ nonce = websMD5(prenonce);
+/*
+ * Cleanup
+ */
+ bfreeSafe(B_L, prenonce);
+
+ return nonce;
+}
+
+/******************************************************************************/
+/*
+ * Get an Opaque value for passing along to the client
+ */
+
+char_t *websCalcOpaque(webs_t wp)
+{
+ char_t *opaque;
+ a_assert(wp);
+/*
+ * Temporary stub!
+ */
+ opaque = bstrdup(B_L, T("5ccc069c403ebaf9f0171e9517f40e41"));
+
+ return opaque;
+}
+
+/******************************************************************************/
+/*
+ * Get a Digest value using the MD5 algorithm
+ */
+
+char_t *websCalcDigest(webs_t wp)
+{
+#ifdef DIGEST_ACCESS_SUPPORT
+ char_t *digest, *a1, *a1prime, *a2, *a2prime, *preDigest, *method;
+
+ a_assert(wp);
+ digest = NULL;
+
+/*
+ * Calculate first portion of digest H(A1)
+ */
+ a1 = NULL;
+ fmtAlloc(&a1, 255, T("%s:%s:%s"), wp->userName, wp->realm, wp->password);
+ a_assert(a1);
+ a1prime = websMD5(a1);
+ bfreeSafe(B_L, a1);
+/*
+ * Calculate second portion of digest H(A2)
+ */
+ method = websGetVar(wp, T("REQUEST_METHOD"), NULL);
+ a_assert(method);
+ a2 = NULL;
+ fmtAlloc(&a2, 255, T("%s:%s"), method, wp->uri);
+ a_assert(a2);
+ a2prime = websMD5(a2);
+ bfreeSafe(B_L, a2);
+/*
+ * Construct final digest KD(H(A1):nonce:H(A2))
+ */
+ a_assert(a1prime);
+ a_assert(a2prime);
+ a_assert(wp->nonce);
+
+ preDigest = NULL;
+ if (!wp->qop) {
+ fmtAlloc(&preDigest, 255, T("%s:%s:%s"), a1prime, wp->nonce, a2prime);
+ } else {
+ fmtAlloc(&preDigest, 255, T("%s:%s:%s:%s:%s:%s"),
+ a1prime,
+ wp->nonce,
+ wp->nc,
+ wp->cnonce,
+ wp->qop,
+ a2prime);
+ }
+
+ a_assert(preDigest);
+ digest = websMD5(preDigest);
+/*
+ * Now clean up
+ */
+ bfreeSafe(B_L, a1prime);
+ bfreeSafe(B_L, a2prime);
+ bfreeSafe(B_L, preDigest);
+ return digest;
+#else
+ return NULL;
+#endif /* DIGEST_ACCESS_SUPPORT */
+}
+
+/******************************************************************************/
+
diff --git a/cleopatre/application/spidgoahead/websda.h b/cleopatre/application/spidgoahead/websda.h
new file mode 100644
index 0000000000..e19392c048
--- /dev/null
+++ b/cleopatre/application/spidgoahead/websda.h
@@ -0,0 +1,42 @@
+/*
+ * websda.h -- GoAhead Digest Access Authentication public header
+ *
+ * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved.
+ *
+ * See the file "license.txt" for information on usage and redistribution
+ *
+ * $Id: websda.h,v 1.3 2002/10/24 14:44:50 bporter Exp $
+ */
+
+#ifndef _h_WEBSDA
+#define _h_WEBSDA 1
+
+/******************************** Description *********************************/
+
+/*
+ * GoAhead Digest Access Authentication header. This defines the Digest
+ * access authentication public APIs. Include this header for files that
+ * use DAA functions
+ */
+
+/********************************* Includes ***********************************/
+
+#ifndef UEMF
+ #include "basic/basic.h"
+ #include "emf/emf.h"
+#else
+ #include "uemf.h"
+#endif
+
+#include "webs.h"
+
+/****************************** Definitions ***********************************/
+
+extern char_t *websCalcNonce(webs_t wp);
+extern char_t *websCalcOpaque(webs_t wp);
+extern char_t *websCalcDigest(webs_t wp);
+
+#endif /* _h_WEBSDA */
+
+/******************************************************************************/
+
diff --git a/cleopatre/application/spidgoahead/websuemf.c b/cleopatre/application/spidgoahead/websuemf.c
new file mode 100644
index 0000000000..63a6926271
--- /dev/null
+++ b/cleopatre/application/spidgoahead/websuemf.c
@@ -0,0 +1,218 @@
+/*
+ * websuemf.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: websuemf.c,v 1.4 2002/10/24 14:44:50 bporter Exp $
+ */
+
+/********************************** Description *******************************/
+
+/*
+ * This modules provides compatibility with the full GoAhead EMF.
+ */
+
+/*********************************** Includes *********************************/
+
+#include "ejIntrn.h"
+#include "wsIntrn.h"
+
+/*********************************** Defines **********************************/
+
+/*
+ * This structure stores scheduled events.
+ */
+typedef struct {
+ void (*routine)(void *arg, int id);
+ void *arg;
+ time_t at;
+ int schedid;
+} sched_t;
+
+/*********************************** Locals ***********************************/
+
+static sched_t **sched;
+static int schedMax;
+
+/************************************* Code ***********************************/
+/*
+ * Evaluate a script
+ */
+
+int scriptEval(int engine, char_t *cmd, char_t **result, int chan)
+{
+ int ejid;
+
+ if (engine == EMF_SCRIPT_EJSCRIPT) {
+ ejid = (int) chan;
+ /*
+ * NOTE -- to disable better reporting of ASP errors, change the
+ * following line of code to
+ * if (ejEval(ejid, cmd, NULL) ) {
+ */
+ if (ejEval(ejid, cmd, result) ) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ return -1;
+}
+
+/******************************************************************************/
+/*
+ * Compare strings, ignoring case: normal strcmp return codes.
+ *
+ * WARNING: It is not good form to increment or decrement pointers inside a
+ * "call" to tolower et al. These can be MACROS, and have undesired side
+ * effects.
+ */
+
+int strcmpci(char_t *s1, char_t *s2)
+{
+ int rc;
+
+ a_assert(s1 && s2);
+ if (s1 == NULL || s2 == NULL) {
+ return 0;
+ }
+
+ if (s1 == s2) {
+ return 0;
+ }
+
+ do {
+ rc = gtolower(*s1) - gtolower(*s2);
+ if (*s1 == '\0') {
+ break;
+ }
+ s1++;
+ s2++;
+ } while (rc == 0);
+ return rc;
+}
+
+/******************************************************************************/
+/*
+ * This function is called when a scheduled process time has come.
+ */
+
+void TimerProc(int schedid)
+{
+ sched_t *s;
+
+ a_assert(0 <= schedid && schedid < schedMax);
+ s = sched[schedid];
+ a_assert(s);
+
+ (s->routine)(s->arg, s->schedid);
+}
+
+/******************************************************************************/
+/*
+ * Schedule an event in delay milliseconds time. We will use 1 second
+ * granularity for webServer.
+ */
+
+int emfSchedCallback(int delay, emfSchedProc *proc, void *arg)
+{
+ sched_t *s;
+ int schedid;
+
+ if ((schedid = hAllocEntry((void***) &sched, &schedMax,
+ sizeof(sched_t))) < 0) {
+ return -1;
+ }
+ s = sched[schedid];
+ s->routine = proc;
+ s->arg = arg;
+ s->schedid = schedid;
+
+/*
+ * Round the delay up to seconds.
+ */
+ s->at = ((delay + 500) / 1000) + time(0);
+
+ return schedid;
+}
+
+/******************************************************************************/
+/*
+ * Reschedule to a new delay.
+ */
+
+void emfReschedCallback(int schedid, int delay)
+{
+ sched_t *s;
+
+ if (sched == NULL || schedid == -1 || schedid >= schedMax ||
+ (s = sched[schedid]) == NULL) {
+ return;
+ }
+ s->at = ((delay + 500) / 1000) + time(0);
+}
+
+/******************************************************************************/
+
+void emfUnschedCallback(int schedid)
+{
+ sched_t *s;
+
+ if (sched == NULL || schedid == -1 || schedid >= schedMax ||
+ (s = sched[schedid]) == NULL) {
+ return;
+ }
+ bfree(B_L, s);
+ schedMax = hFree((void***) &sched, schedid);
+}
+
+/******************************************************************************/
+/*
+ * Take the tasks off the queue in a round robin fashion.
+ */
+
+void emfSchedProcess()
+{
+ sched_t *s;
+ int schedid;
+ static int next = 0;
+
+/*
+ * If schedMax is 0, there are no tasks scheduled, so just return.
+ */
+ if (schedMax <= 0) {
+ return;
+ }
+
+/*
+ * If next >= schedMax, the schedule queue was reduced in our absence
+ * so reset next to 0 to start from the begining of the queue again.
+ */
+ if (next >= schedMax) {
+ next = 0;
+ }
+
+ schedid = next;
+ for (;;) {
+ if ((s = sched[schedid]) != NULL && (int)s->at <= (int)time(0)) {
+ TimerProc(schedid);
+ next = schedid + 1;
+ return;
+ }
+ if (++schedid >= schedMax) {
+ schedid = 0;
+ }
+ if (schedid == next) {
+/*
+ * We've gone all the way through the queue without finding
+ * anything to do so just return.
+ */
+ return;
+ }
+ }
+}
+
+/******************************************************************************/
+
diff --git a/cleopatre/application/spidgoahead/wsIntrn.h b/cleopatre/application/spidgoahead/wsIntrn.h
new file mode 100644
index 0000000000..d3b1e7da7e
--- /dev/null
+++ b/cleopatre/application/spidgoahead/wsIntrn.h
@@ -0,0 +1,310 @@
+/*
+ * wsIntrn.h -- Internal GoAhead Web server header
+ *
+ * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved.
+ *
+ * See the file "license.txt" for information on usage and redistribution
+ *
+ * $Id: wsIntrn.h,v 1.4 2002/10/24 14:44:50 bporter Exp $
+ */
+
+#ifndef _h_WEBS_INTERNAL
+#define _h_WEBS_INTERNAL 1
+
+/******************************** Description *********************************/
+
+/*
+ * Internal GoAhead Web Server header. This defines the Web private APIs
+ * Include this header when you want to create URL handlers.
+ */
+
+/*********************************** Defines **********************************/
+
+/*
+ * Define this to enable logging of web accesses to a file
+ * #define WEBS_LOG_SUPPORT 1
+ *
+ * Define this to enable HTTP/1.1 keep alive support
+ * #define WEBS_KEEP_ALIVE_SUPPORT 1
+ *
+ * Define this to enable if-modified-since support
+ * #define WEBS_IF_MODIFIED_SUPPORT 1
+ *
+ * Define this to support proxy capability and track local vs remote request
+ * Note: this is not yet fully implemented.
+ * #define WEBS_PROXY_SUPPORT 1
+ *
+ * Define this to support reading pages from ROM
+ * #define WEBS_PAGE_ROM 1
+ *
+ * Define this to enable memory allocation and stack usage tracking
+ * #define B_STATS 1
+ */
+
+/********************************** Includes **********************************/
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#ifdef NETWARE
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <signal.h>
+ #include <io.h>
+#endif
+
+#ifdef WIN
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <io.h>
+#endif
+
+#ifdef CE
+#ifndef UEMF
+ #include <io.h>
+#endif
+#endif
+
+#ifdef NW
+ #include <fcntl.h>
+ #include <sys/stat.h>
+#endif
+
+#ifdef SCOV5
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <signal.h>
+ #include <unistd.h>
+#endif
+
+#ifdef LYNX
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <signal.h>
+ #include <unistd.h>
+#endif
+
+#ifdef UNIX
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <signal.h>
+ #include <unistd.h>
+#endif
+
+#ifdef QNX4
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <signal.h>
+ #include <unistd.h>
+ #include <unix.h>
+#endif
+
+#ifdef UW
+ #include <fcntl.h>
+ #include <sys/stat.h>
+#endif
+
+#ifdef VXWORKS
+ #include <vxWorks.h>
+ #include <fcntl.h>
+ #include <sys/stat.h>
+#endif
+
+#ifdef SOLARIS
+ #include <macros.h>
+ #include <fcntl.h>
+ #include <sys/stat.h>
+#endif
+
+#ifdef UEMF
+ #include "uemf.h"
+ #include "ejIntrn.h"
+#else
+ #include "emf/emfInternal.h"
+ #include "ej/ejIntrn.h"
+#endif
+
+#include "webs.h"
+
+/********************************** Defines ***********************************/
+/*
+ * Read handler flags and state
+ */
+#define WEBS_BEGIN 0x1 /* Beginning state */
+#define WEBS_HEADER 0x2 /* Ready to read first line */
+#define WEBS_POST 0x4 /* POST without content */
+#define WEBS_POST_CLEN 0x8 /* Ready to read content for POST */
+#define WEBS_PROCESSING 0x10 /* Processing request */
+#define WEBS_KEEP_TIMEOUT 15000 /* Keep-alive timeout (15 secs) */
+#define WEBS_TIMEOUT 60000 /* General request timeout (60) */
+
+#define PAGE_READ_BUFSIZE 512 /* bytes read from page files */
+#define MAX_PORT_LEN 10 /* max digits in port number */
+#define WEBS_SYM_INIT 64 /* initial # of sym table entries */
+
+/*
+ * URL handler structure. Stores the leading URL path and the handler
+ * function to call when the URL path is seen.
+ */
+typedef struct {
+ int (*handler)(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
+ char_t *url, char_t *path,
+ char_t *query); /* Callback URL handler function */
+ char_t *webDir; /* Web directory if required */
+ char_t *urlPrefix; /* URL leading prefix */
+ int len; /* Length of urlPrefix for speed */
+ int arg; /* Argument to provide to handler */
+ int flags; /* Flags */
+} websUrlHandlerType;
+
+/*
+ * Webs statistics
+ */
+typedef struct {
+ long errors; /* General errors */
+ long redirects;
+ long net_requests;
+ long activeNetRequests;
+ long activeBrowserRequests;
+ long timeouts;
+ long access; /* Access violations */
+ long localHits;
+ long remoteHits;
+ long formHits;
+ long cgiHits;
+ long handlerHits;
+} websStatsType;
+
+extern websStatsType websStats; /* Web access stats */
+
+/*
+ * Error code list
+ */
+typedef struct {
+ int code; /* HTTP error code */
+ char_t *msg; /* HTTP error message */
+} websErrorType;
+
+/*
+ * Mime type list
+ */
+typedef struct {
+ char_t *type; /* Mime type */
+ char_t *ext; /* File extension */
+} websMimeType;
+
+/*
+ * File information structure.
+ */
+typedef struct {
+ unsigned long size; /* File length */
+ int isDir; /* Set if directory */
+ time_t mtime; /* Modified time */
+} websStatType;
+
+/*
+ * Compiled Rom Page Index
+ */
+typedef struct {
+ char_t *path; /* Web page URL path */
+ unsigned char *page; /* Web page data */
+ int size; /* Size of web page in bytes */
+ int pos; /* Current read position */
+} websRomPageIndexType;
+
+/*
+ * Defines for file open.
+ */
+#ifndef CE
+#define SOCKET_RDONLY O_RDONLY
+#define SOCKET_BINARY O_BINARY
+#else /* CE */
+#define SOCKET_RDONLY 0x1
+#define SOCKET_BINARY 0x2
+#endif /* CE */
+
+extern websRomPageIndexType websRomPageIndex[];
+extern websMimeType websMimeList[]; /* List of mime types */
+extern sym_fd_t websMime; /* Set of mime types */
+extern webs_t* webs; /* Session list head */
+extern int websMax; /* List size */
+extern char_t websHost[64]; /* Name of this host */
+extern char_t websIpaddr[64]; /* IP address of this host */
+extern char_t *websHostUrl; /* URL for this host */
+extern char_t *websIpaddrUrl; /* URL for this host */
+extern int websPort; /* Port number */
+
+/******************************** Prototypes **********************************/
+
+extern int websAspOpen();
+extern void websAspClose();
+extern void websFormOpen();
+extern void websFormClose();
+extern int websAspWrite(int ejid, webs_t wp, int argc, char_t **argv);
+extern void websDefaultClose();
+extern int websDefaultHandler(webs_t wp, char_t *urlPrefix,
+ char_t *webDir, int arg, char_t *url, char_t *path,
+ char_t *query);
+extern int websFormHandler(webs_t wp, char_t *urlPrefix, char_t *webDir,
+ int arg, char_t *url, char_t *path, char_t *query);
+extern int websCgiHandler(webs_t wp, char_t *urlPrefix, char_t *webDir,
+ int arg, char_t *url, char_t *path, char_t *query);
+extern void websCgiCleanup();
+extern int websCheckCgiProc(int handle);
+extern char_t *websGetCgiCommName();
+
+extern int websLaunchCgiProc(char_t *cgiPath, char_t **argp,
+ char_t **envp, char_t *stdIn, char_t *stdOut);
+extern int websOpen(int sid);
+extern void websResponse(webs_t wp, int code, char_t *msg,
+ char_t *redirect);
+extern int websJavaScriptEval(webs_t wp, char_t *script);
+extern int websPageReadData(webs_t wp, char *buf, int nBytes);
+extern int websPageOpen(webs_t wp, char_t *lpath, char_t *path, int mode,
+ int perm);
+extern void websPageClose(webs_t wp);
+extern void websPageSeek(webs_t wp, long offset);
+extern int websPageStat(webs_t wp, char_t *lpath, char_t *path,
+ websStatType *sbuf);
+extern int websPageIsDirectory(char_t *lpath);
+extern int websRomOpen();
+extern void websRomClose();
+extern int websRomPageOpen(webs_t wp, char_t *path, int mode, int perm);
+extern void websRomPageClose(int fd);
+extern int websRomPageReadData(webs_t wp, char *buf, int len);
+extern int websRomPageStat(char_t *path, websStatType *sbuf);
+extern long websRomPageSeek(webs_t wp, long offset, int origin);
+extern void websSetRequestSocketHandler(webs_t wp, int mask,
+ void (*fn)(webs_t wp));
+extern int websSolutionHandler(webs_t wp, char_t *urlPrefix,
+ char_t *webDir, int arg, char_t *url, char_t *path,
+ char_t *query);
+extern void websUrlHandlerClose();
+extern int websUrlHandlerOpen();
+extern int websOpenServer(int port, int retries);
+extern void websCloseServer();
+extern char_t* websGetDateString(websStatType* sbuf);
+
+extern int strcmpci(char_t* s1, char_t* s2);
+
+/*
+ * Prototypes for functions available when running as part of the
+ * GoAhead Embedded Management Framework (EMF)
+ */
+#ifdef EMF
+extern int websEmfOpen();
+extern void websEmfClose();
+extern void websSetEmfEnvironment(webs_t wp);
+#endif
+
+#ifdef CE
+extern int writeUniToAsc(int fid, void *buf, unsigned int len);
+extern int readAscToUni(int fid, void **buf, unsigned int len);
+#endif
+
+#endif /* _h_WEBS_INTERNAL */
+
+/******************************************************************************/
+
diff --git a/cleopatre/buildroot/package/Config.in b/cleopatre/buildroot/package/Config.in
index 77f74b4850..7eaca7887b 100644
--- a/cleopatre/buildroot/package/Config.in
+++ b/cleopatre/buildroot/package/Config.in
@@ -246,6 +246,14 @@ source "package/quagga/Config.in"
source "package/rsync/Config.in"
source "package/samba/Config.in"
source "package/socat/Config.in"
+if BR2_TARGET_SPIDCOM
+source "package/spidgoahead/Config.in"
+config BR2_SPIDGOAHEAD_SSL_SUPPORT
+ bool "Support for HTTPS protocol."
+ depends on BR2_PACKAGE_OPENSSL
+ depends on BR2_PACKAGE_GOA
+ default n
+endif
source "package/stunnel/Config.in"
source "package/tcpdump/Config.in"
source "package/tftpd/Config.in"
diff --git a/cleopatre/buildroot/package/spidgoahead/Config.in b/cleopatre/buildroot/package/spidgoahead/Config.in
new file mode 100644
index 0000000000..6e3c11a4af
--- /dev/null
+++ b/cleopatre/buildroot/package/spidgoahead/Config.in
@@ -0,0 +1,9 @@
+config BR2_PACKAGE_SPIDGOAHEAD
+ bool "spidgoahead"
+ depends on BR2_PACKAGE_LIBSPID
+ help
+ GoAhead WebServer 2.1 is a fully featured, open source web server designed specifically for the needs of embedded device developers. It delivers a full range of features.
+
+ http://www.goahead.com/
+comment "spidgoahead disabled (requires package libspid)"
+ depends on !BR2_PACKAGE_LIBSPID \ No newline at end of file
diff --git a/cleopatre/buildroot/package/spidgoahead/spidgoahead.mk b/cleopatre/buildroot/package/spidgoahead/spidgoahead.mk
new file mode 100644
index 0000000000..7c28ba4c3d
--- /dev/null
+++ b/cleopatre/buildroot/package/spidgoahead/spidgoahead.mk
@@ -0,0 +1,113 @@
+#############################################################
+#
+# spidgoahead
+# Fully featured HTTP server customized for SPC300 board
+#
+#############################################################
+
+SPIDGOAHEAD_VERSION:=0.0.1
+SPIDGOAHEAD_SITE:=$(BASE_DIR)/../application/spidgoahead
+SPIDGOAHEAD_NAME:=spidgoahead-$(SPIDGOAHEAD_VERSION)
+SPIDGOAHEAD_DIR:=$(BUILD_DIR)/spidgoahead-$(SPIDGOAHEAD_VERSION)
+
+SPIDGOAHEAD_LIBSPID_SITE:=$(BASE_DIR)/../application/libspid
+
+TARGET_ETC_CERTS_DIR = $(TARGET_DIR)/etc/certs
+
+TARGET_LIST_BASE := \
+ $(TARGET_DIR)/usr/bin/httpd \
+ $(TARGET_DIR)/etc/http/um_httpd.conf
+
+TARGET_LIST_WITH_SSH := \
+ $(TARGET_DIR)/etc/server.pem \
+ $(TARGET_DIR)/etc/certs/cacert.pem \
+ $(TARGET_DIR)/etc/certs/cakey.pem
+
+# Update target if SSL switch is on
+ifeq ($(BR2_SPIDGOAHEAD_SSL_SUPPORT),y)
+TARGET_LIST := $(TARGET_LIST_BASE) $(TARGET_LIST_WITH_SSH)
+else
+TARGET_LIST := $(TARGET_LIST_BASE)
+endif
+
+spidgoahead-source:
+
+$(TARGET_ETC_CERTS_DIR):
+ mkdir -p $(TARGET_DIR)/etc/certs
+
+$(SPIDGOAHEAD_DIR)/.unpacked:
+ ln -s $(SPIDGOAHEAD_SITE) $(SPIDGOAHEAD_DIR)
+ touch $(SPIDGOAHEAD_DIR)/.unpacked
+
+$(SPIDGOAHEAD_DIR)/.configured: $(SPIDGOAHEAD_DIR)/.unpacked
+ touch $(SPIDGOAHEAD_DIR)/.configured
+
+$(SPIDGOAHEAD_DIR)/.compiled: $(SPIDGOAHEAD_DIR)/.configured \
+ $(SPIDGOAHEAD_LIBSPID_SITE)/libspid.so \
+ FORCE_SPIDGOAHEAD_MAKE
+ @$(TARGET_CONFIGURE_OPTS) \
+ $(MAKE) -C $(SPIDGOAHEAD_DIR) \
+ SSL_SUPPORT=$(BR2_SPIDGOAHEAD_SSL_SUPPORT) \
+ STAGING_DIR="$(STAGING_DIR)"
+
+# httpd file is expected through .compiled rule.
+# 'web.tar' is always rebuild with httpd. See spidgoahead makefile.
+$(TARGET_DIR)/usr/bin/httpd: $(SPIDGOAHEAD_DIR)/.compiled
+ mkdir -p $(TARGET_DIR)/usr/bin
+ cp $(SPIDGOAHEAD_DIR)/httpd $@
+ mkdir -p $(TARGET_DIR)/usr/share/httpd/web/cgi-bin
+ tar -xvf $(SPIDGOAHEAD_DIR)/web.tar -C $(TARGET_DIR)/usr/share/httpd/
+
+# '$(SPIDGOAHEAD_DIR)/server.pem' is expected through .compiled rule.
+$(TARGET_DIR)/etc/server.pem: $(SPIDGOAHEAD_DIR)/.compiled \
+ | $(TARGET_ETC_CERTS_DIR)
+ cp $(SPIDGOAHEAD_DIR)/server.pem $@
+
+# '$(SPIDGOAHEAD_DIR)/um_httpd.conf' is expected through .compiled rule.
+$(TARGET_DIR)/etc/http/um_httpd.conf: $(SPIDGOAHEAD_DIR)/.compiled
+ mkdir -p $(TARGET_DIR)/etc/http
+ cp $(SPIDGOAHEAD_DIR)/um_httpd.conf $@
+
+# This rule should be in spidgoahead makefile.
+# because package's makefile prepare TARGET only.
+$(SPIDGOAHEAD_DIR)/server-cert.pem $(SPIDGOAHEAD_DIR)/server.pem: $(SPIDGOAHEAD_DIR)/server-key.pem
+ openssl req -x509 -newkey rsa:2048 -days 1024 -keyout \
+ $(SPIDGOAHEAD_DIR)/server-key.pem \
+ -out $(SPIDGOAHEAD_DIR)/server-cert.pem
+ openssl rsa \
+ -in $(SPIDGOAHEAD_DIR)/server-key.pem \
+ -out $(SPIDGOAHEAD_DIR)/server-key-nopassword.pem
+ cat $(SPIDGOAHEAD_DIR)/server-key-nopassword.pem $(SPIDGOAHEAD_DIR)/server-cert.pem \
+ > $(SPIDGOAHEAD_DIR)/server.pem
+
+$(TARGET_DIR)/etc/certs/cacert.pem: $(SPIDGOAHEAD_DIR)/server-cert.pem \
+ | $(TARGET_ETC_CERTS_DIR)
+ cp $< $@
+
+$(TARGET_DIR)/etc/certs/cakey.pem: $(SPIDGOAHEAD_DIR)/server-key.pem \
+ | $(TARGET_ETC_CERTS_DIR)
+ cp $< $@
+
+spidgoahead: uclibc $(TARGET_LIST)
+
+spidgoahead-clean:
+ rm -f $(TARGET_LIST_BASE)
+ rm -f $(TARGET_LIST_WITH_SSH)
+ -$(MAKE) -C $(SPIDGOAHEAD_DIR) clean
+
+https-clean: spidgoahead-clean
+
+spidgoahead-dirclean:
+ rm -f $(SPIDGOAHEAD_DIR)
+
+# We declare FORCE_SPIDGOAHEAD_MAKE rule as PHONY to force compilation
+.PHONY: FORCE_SPIDGOAHEAD_MAKE
+
+#############################################################
+#
+# Toplevel Makefile options
+#
+#############################################################
+ifeq ($(strip $(BR2_PACKAGE_SPIDGOAHEAD)),y)
+TARGETS+=spidgoahead
+endif
diff --git a/cleopatre/buildroot/target/device/Spidcom/common/defconfig.base b/cleopatre/buildroot/target/device/Spidcom/common/defconfig.base
index f0a0510d79..95dd8b8402 100644
--- a/cleopatre/buildroot/target/device/Spidcom/common/defconfig.base
+++ b/cleopatre/buildroot/target/device/Spidcom/common/defconfig.base
@@ -365,6 +365,7 @@ BR2_PACKAGE_PLCDRV=y
# BR2_PACKAGE_RT5572DRV is not set
# BR2_PACKAGE_DEBUG_UTILS is not set
# BR2_PACKAGE_MT7601UDRV is not set
+# BR2_PACKAGE_SPIDGOAHEAD is not set
#
# portmap requires a toolchain with 'Enable RPC' selected
diff --git a/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/defconfig.part b/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/defconfig.part
index 8651067852..29c6cdccf9 100644
--- a/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/defconfig.part
+++ b/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/defconfig.part
@@ -3,3 +3,4 @@ BR2_HOSTNAME="mpr520e"
BR2_TARGET_SPIDCOM_MPR520E=y
BR2_PACKAGE_PMD=y
BR2_PACKAGE_MT7601UDRV=y
+BR2_PACKAGE_SPIDGOAHEAD=y \ No newline at end of file
diff --git a/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/init.d/S90httpd b/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/init.d/S90httpd
new file mode 100755
index 0000000000..8cd512a514
--- /dev/null
+++ b/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/init.d/S90httpd
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# Start the httpd daemon....
+#
+
+start() {
+ echo "Starting HTTP Daemon..."
+ if [ -f /usr/bin/httpd ]
+ then
+ /usr/bin/httpd &
+ fi
+}
+stop() {
+ echo "Stopping HTTP Daemon..."
+ if [ -f /usr/bin/httpd ]
+ then
+ killall httpd
+ fi
+}
+restart() {
+ stop
+ start
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart|reload)
+ restart
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|restart}"
+ exit 1
+esac
+
+exit $?
+
diff --git a/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/save.lst b/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/save.lst
new file mode 100644
index 0000000000..048412f71d
--- /dev/null
+++ b/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/save.lst
@@ -0,0 +1,4 @@
+Wireless/RT2870AP/RT2870AP.dat
+network/interfaces
+hostname
+save.lst
diff --git a/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/system.conf b/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/system.conf
new file mode 100644
index 0000000000..9ce02d74b2
--- /dev/null
+++ b/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/system.conf
@@ -0,0 +1,40 @@
+ADMIN_LOGICAL_ID = SPiDCOM Technologies
+ADMIN_TECHPROJECT = Mstarsemi
+ADMIN_EMS_IP_ADDRESS = 0.0.0.0
+UPGRADE_SERVER_IP = 127.0.0.1
+UPGRADE_SERVER_PORT = 21
+UPGRADE_SERVER_LOGIN = spidcom
+UPGRADE_SERVER_PASSWORD = spidcom
+UPGRADE_MASTER_FILENAME = master_filename
+UPGRADE_SLAVE_FILENAME = slave_filename
+UPGRADE_BOARD_TYPE_CHECK = yes
+CONFIG_SERVER_IP = 127.0.0.1
+CONFIG_SERVER_PORT = 21
+CONFIG_SERVER_LOGIN = spidcom
+CONFIG_SERVER_PASSWORD = spidcom
+CONFIG_SERVER_FILENAME = filename
+AUTOUPDATE = yes
+REMOTE_UPDATE = yes
+AUTOCONF = yes
+FTP_SERVICE = yes
+SNMP_SERVICE = yes
+SERIAL_SERVICE = yes
+TELNET_SERVICE = yes
+TELNET_TIMEOUT_SEC = 60
+TELNET_SESSIONS_MAX_NB = 4
+8021X_SUPPLICANT_SERVICE = no
+8021X_AUTH_SERVICE = no
+HTTP_SERVICE = yes
+AUTODISCOVERY_ENABLE = yes
+AUTODISCOVERY_TIME_SEC = 60
+AUTODISCOVERY_TIME_SEC2 = 1200
+AUTODISCOVERY_INTERFACE = eth0
+PLC_FORWARDING_ENABLE = yes
+STP_ENABLE = no
+SSH_ENABLE = no
+WL_CONTROL = 1
+NSCRTV_EPONEOC_MOD_EOC = no
+VLAN_VERSION = 1
+VLAN_MAX_ID = 4095
+VLAN_MAX_SUPPORTED_VLANS = 4095
+BCMP_ENABLE = no
diff --git a/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/web_reset.info b/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/web_reset.info
new file mode 100644
index 0000000000..c02e7ce932
--- /dev/null
+++ b/cleopatre/buildroot/target/device/Spidcom/targets/mpr520e/target_skeleton_add/etc/web_reset.info
@@ -0,0 +1,2 @@
+RESET_BY_WEB = no
+WIFI_CFG_CHANGE = no