summaryrefslogtreecommitdiffstats
path: root/cpukit/httpd
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>2003-04-11 16:34:49 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>2003-04-11 16:34:49 +0000
commit2e7f00fce6696c380a93ea939bf233760f499640 (patch)
tree4054b7b0d7a6722c69691523b63159feb4ce06b0 /cpukit/httpd
parent2002-04-10 Mike Siers <mikes@poliac.com> (diff)
downloadrtems-2e7f00fce6696c380a93ea939bf233760f499640.tar.bz2
2003-04-11 Joel Sherrill <joel@OARcorp.com>
* rtems_webserver/cgi.c, rtems_webserver/sockGen.c, rtems_webserver/umui.c, rtems_webserver/websSSL.c, rtems_webserver/websSSL.h, rtems_webserver/websda.c, rtems_webserver/websda.h: New files. Not included in previous commit.
Diffstat (limited to 'cpukit/httpd')
-rw-r--r--cpukit/httpd/cgi.c331
-rw-r--r--cpukit/httpd/sockGen.c1044
-rw-r--r--cpukit/httpd/umui.c641
-rw-r--r--cpukit/httpd/websSSL.c706
-rw-r--r--cpukit/httpd/websSSL.h67
-rw-r--r--cpukit/httpd/websda.c244
-rw-r--r--cpukit/httpd/websda.h41
7 files changed, 3074 insertions, 0 deletions
diff --git a/cpukit/httpd/cgi.c b/cpukit/httpd/cgi.c
new file mode 100644
index 0000000000..c2328eab6e
--- /dev/null
+++ b/cpukit/httpd/cgi.c
@@ -0,0 +1,331 @@
+/*
+ * 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$
+ */
+
+/********************************** 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/cpukit/httpd/sockGen.c b/cpukit/httpd/sockGen.c
new file mode 100644
index 0000000000..99a53c8919
--- /dev/null
+++ b/cpukit/httpd/sockGen.c
@@ -0,0 +1,1044 @@
+
+/*
+ * sockGen.c -- Posix Socket support module for general posix use
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
+ *
+ * $Id$
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * Posix Socket Module. This supports blocking and non-blocking buffered
+ * socket I/O.
+ */
+
+#if (!defined (WIN) || defined (LITTLEFOOT) || defined (WEBS))
+
+/********************************** Includes **********************************/
+
+#include <errno.h>
+#include <fcntl.h>
+#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
+
+#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/cpukit/httpd/umui.c b/cpukit/httpd/umui.c
new file mode 100644
index 0000000000..971b059bc7
--- /dev/null
+++ b/cpukit/httpd/umui.c
@@ -0,0 +1,641 @@
+/*
+ * 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$
+ */
+
+/******************************** 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/cpukit/httpd/websSSL.c b/cpukit/httpd/websSSL.c
new file mode 100644
index 0000000000..6c2e4ce5da
--- /dev/null
+++ b/cpukit/httpd/websSSL.c
@@ -0,0 +1,706 @@
+/*
+ * 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$
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module implements a patch into SSL implementations for the webs
+ * module.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+#include "webs.h"
+#include "websSSL.h"
+
+/******************************* Definitions **********************************/
+
+#define DEFAULT_CERT_FILE "./server.pem"
+#define DEFAULT_KEY_FILE "./certs/cakey.pem"
+#define DEFAULT_CA_FILE "./certs/cacert.pem"
+#define DEFAULT_CA_PATH "./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);
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/websSSL.h b/cpukit/httpd/websSSL.h
new file mode 100644
index 0000000000..62bcc44930
--- /dev/null
+++ b/cpukit/httpd/websSSL.h
@@ -0,0 +1,67 @@
+/*
+ * 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$
+ */
+
+#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/cpukit/httpd/websda.c b/cpukit/httpd/websda.c
new file mode 100644
index 0000000000..a4a95775b8
--- /dev/null
+++ b/cpukit/httpd/websda.c
@@ -0,0 +1,244 @@
+/*
+ * 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$
+ */
+
+/******************************** 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/cpukit/httpd/websda.h b/cpukit/httpd/websda.h
new file mode 100644
index 0000000000..3852ab8f53
--- /dev/null
+++ b/cpukit/httpd/websda.h
@@ -0,0 +1,41 @@
+/*
+ * 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$
+ */
+
+#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 */
+
+/******************************************************************************/