From 2e7f00fce6696c380a93ea939bf233760f499640 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Fri, 11 Apr 2003 16:34:49 +0000 Subject: 2003-04-11 Joel Sherrill * 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. --- c/src/libnetworking/ChangeLog | 7 + c/src/libnetworking/rtems_webserver/cgi.c | 331 ++++++++ c/src/libnetworking/rtems_webserver/sockGen.c | 1044 +++++++++++++++++++++++++ c/src/libnetworking/rtems_webserver/umui.c | 641 +++++++++++++++ c/src/libnetworking/rtems_webserver/websSSL.c | 706 +++++++++++++++++ c/src/libnetworking/rtems_webserver/websSSL.h | 67 ++ c/src/libnetworking/rtems_webserver/websda.c | 244 ++++++ c/src/libnetworking/rtems_webserver/websda.h | 41 + 8 files changed, 3081 insertions(+) create mode 100644 c/src/libnetworking/rtems_webserver/cgi.c create mode 100644 c/src/libnetworking/rtems_webserver/sockGen.c create mode 100644 c/src/libnetworking/rtems_webserver/umui.c create mode 100644 c/src/libnetworking/rtems_webserver/websSSL.c create mode 100644 c/src/libnetworking/rtems_webserver/websSSL.h create mode 100644 c/src/libnetworking/rtems_webserver/websda.c create mode 100644 c/src/libnetworking/rtems_webserver/websda.h (limited to 'c/src/libnetworking') diff --git a/c/src/libnetworking/ChangeLog b/c/src/libnetworking/ChangeLog index ea2d0bf722..a65bdccbd9 100644 --- a/c/src/libnetworking/ChangeLog +++ b/c/src/libnetworking/ChangeLog @@ -1,3 +1,10 @@ +2003-04-11 Joel Sherrill + + * 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. + 2002-04-10 Mike Siers * rtems_webserver/NOTES, rtems_webserver/asp.c, diff --git a/c/src/libnetworking/rtems_webserver/cgi.c b/c/src/libnetworking/rtems_webserver/cgi.c new file mode 100644 index 0000000000..c2328eab6e --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/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/c/src/libnetworking/rtems_webserver/sockGen.c b/c/src/libnetworking/rtems_webserver/sockGen.c new file mode 100644 index 0000000000..99a53c8919 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/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 +#include +#include +#include + +#ifdef UEMF + #include "uemf.h" +#else + #include + #include + #include + #include "emfInternal.h" +#endif + +#ifdef VXWORKS + #include +#endif + +/************************************ Locals **********************************/ + +extern socket_t **socketList; /* List of open sockets */ +extern int socketMax; /* Maximum size of socket */ +extern int socketHighestFd; /* Highest socket fd opened */ +static int socketOpenCount = 0; /* Number of task using sockets */ + +/***************************** Forward Declarations ***************************/ + +static void socketAccept(socket_t *sp); +static int socketDoEvent(socket_t *sp); +static int tryAlternateConnect(int sock, struct sockaddr *sockaddr); + +/*********************************** Code *************************************/ +/* + * Open socket module + */ + +int socketOpen() +{ +#if (defined (CE) || defined (WIN)) + WSADATA wsaData; +#endif + + if (++socketOpenCount > 1) { + return 0; + } + +#if (defined (CE) || defined (WIN)) + if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) { + return -1; + } + if (wsaData.wVersion != MAKEWORD(1,1)) { + WSACleanup(); + return -1; + } +#endif + + socketList = NULL; + socketMax = 0; + socketHighestFd = -1; + + return 0; +} + +/******************************************************************************/ +/* + * Close the socket module, by closing all open connections + */ + +void socketClose() +{ + int i; + + if (--socketOpenCount <= 0) { + for (i = socketMax; i >= 0; i--) { + if (socketList && socketList[i]) { + socketCloseConnection(i); + } + } + socketOpenCount = 0; + } +} + +/******************************************************************************/ +/* + * Open a client or server socket. Host is NULL if we want server capability. + */ + +int socketOpenConnection(char *host, int port, socketAccept_t accept, int flags) +{ +#if (!defined (NO_GETHOSTBYNAME) && !defined (VXWORKS)) + struct hostent *hostent; /* Host database entry */ +#endif /* ! (NO_GETHOSTBYNAME || VXWORKS) */ + socket_t *sp; + struct sockaddr_in sockaddr; + int sid, bcast, dgram, rc; + + if (port > SOCKET_PORT_MAX) { + return -1; + } +/* + * Allocate a socket structure + */ + if ((sid = socketAlloc(host, port, accept, flags)) < 0) { + return -1; + } + sp = socketList[sid]; + a_assert(sp); + +/* + * Create the socket address structure + */ + memset((char *) &sockaddr, '\0', sizeof(struct sockaddr_in)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons((short) (port & 0xFFFF)); + + if (host == NULL) { + sockaddr.sin_addr.s_addr = INADDR_ANY; + } else { + sockaddr.sin_addr.s_addr = inet_addr(host); + if (sockaddr.sin_addr.s_addr == INADDR_NONE) { +/* + * If the OS does not support gethostbyname functionality, the macro: + * NO_GETHOSTBYNAME should be defined to skip the use of gethostbyname. + * Unfortunatly there is no easy way to recover, the following code + * simply uses the basicGetHost IP for the sockaddr. + */ + +#ifdef NO_GETHOSTBYNAME + if (strcmp(host, basicGetHost()) == 0) { + sockaddr.sin_addr.s_addr = inet_addr(basicGetAddress()); + } + if (sockaddr.sin_addr.s_addr == INADDR_NONE) { + socketFree(sid); + return -1; + } +#elif (defined (VXWORKS)) + sockaddr.sin_addr.s_addr = (unsigned long) hostGetByName(host); + if (sockaddr.sin_addr.s_addr == NULL) { + errno = ENXIO; + socketFree(sid); + return -1; + } +#else + hostent = gethostbyname(host); + if (hostent != NULL) { + memcpy((char *) &sockaddr.sin_addr, + (char *) hostent->h_addr_list[0], + (size_t) hostent->h_length); + } else { + char *asciiAddress; + char_t *address; + + address = basicGetAddress(); + asciiAddress = ballocUniToAsc(address, gstrlen(address)); + sockaddr.sin_addr.s_addr = inet_addr(asciiAddress); + bfree(B_L, asciiAddress); + if (sockaddr.sin_addr.s_addr == INADDR_NONE) { + errno = ENXIO; + socketFree(sid); + return -1; + } + } +#endif /* (NO_GETHOSTBYNAME || VXWORKS) */ + } + } + + bcast = sp->flags & SOCKET_BROADCAST; + if (bcast) { + sp->flags |= SOCKET_DATAGRAM; + } + dgram = sp->flags & SOCKET_DATAGRAM; + +/* + * Create the socket. Support for datagram sockets. Set the close on + * exec flag so children don't inherit the socket. + */ + sp->sock = socket(AF_INET, dgram ? SOCK_DGRAM: SOCK_STREAM, 0); + if (sp->sock < 0) { + socketFree(sid); + return -1; + } +#ifndef __NO_FCNTL + fcntl(sp->sock, F_SETFD, FD_CLOEXEC); +#endif + socketHighestFd = max(socketHighestFd, sp->sock); + +/* + * If broadcast, we need to turn on broadcast capability. + */ + if (bcast) { + int broadcastFlag = 1; + if (setsockopt(sp->sock, SOL_SOCKET, SO_BROADCAST, + (char *) &broadcastFlag, sizeof(broadcastFlag)) < 0) { + socketFree(sid); + return -1; + } + } + +/* + * Host is set if we are the client + */ + if (host) { +/* + * Connect to the remote server in blocking mode, then go into + * non-blocking mode if desired. + */ + if (!dgram) { + if (! (sp->flags & SOCKET_BLOCK)) { +/* + * sockGen.c is only used for Windows products when blocking + * connects are expected. This applies to FieldUpgrader + * agents and open source webserver connectws. Therefore the + * asynchronous connect code here is not compiled. + */ +#if (defined (WIN) || defined (CE)) && (!defined (LITTLEFOOT) && !defined (WEBS)) + int flag; + + sp->flags |= SOCKET_ASYNC; +/* + * Set to non-blocking for an async connect + */ + flag = 1; + if (ioctlsocket(sp->sock, FIONBIO, &flag) == SOCKET_ERROR) { + socketFree(sid); + return -1; + } +#else + socketSetBlock(sid, 1); +#endif /* #if (WIN || CE) && !(LITTLEFOOT || WEBS) */ + + } + if ((rc = connect(sp->sock, (struct sockaddr *) &sockaddr, + sizeof(sockaddr))) < 0 && + (rc = tryAlternateConnect(sp->sock, + (struct sockaddr *) &sockaddr)) < 0) { +#if (defined (WIN) || defined (CE)) + if (socketGetError() != EWOULDBLOCK) { + socketFree(sid); + return -1; + } +#else + socketFree(sid); + return -1; + +#endif /* WIN || CE */ + + } + } + } else { +/* + * Bind to the socket endpoint and the call listen() to start listening + */ + rc = 1; + setsockopt(sp->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc)); + if (bind(sp->sock, (struct sockaddr *) &sockaddr, + sizeof(sockaddr)) < 0) { + socketFree(sid); + return -1; + } + + if (! dgram) { + if (listen(sp->sock, SOMAXCONN) < 0) { + socketFree(sid); + return -1; + } +#ifndef UEMF + sp->fileHandle = emfCreateFileHandler(sp->sock, SOCKET_READABLE, + (emfFileProc *) socketAccept, (void *) sp); +#else + sp->flags |= SOCKET_LISTENING; +#endif + } + sp->handlerMask |= SOCKET_READABLE; + } + +/* + * Set the blocking mode + */ + + if (flags & SOCKET_BLOCK) { + socketSetBlock(sid, 1); + } else { + socketSetBlock(sid, 0); + } + return sid; +} + + +/******************************************************************************/ +/* + * If the connection failed, swap the first two bytes in the + * sockaddr structure. This is a kludge due to a change in + * VxWorks between versions 5.3 and 5.4, but we want the + * product to run on either. + */ + +static int tryAlternateConnect(int sock, struct sockaddr *sockaddr) +{ +#ifdef VXWORKS + char *ptr; + + ptr = (char *)sockaddr; + *ptr = *(ptr+1); + *(ptr+1) = 0; + return connect(sock, sockaddr, sizeof(struct sockaddr)); +#else + return -1; +#endif /* VXWORKS */ +} + +/******************************************************************************/ +/* + * Close a socket + */ + +void socketCloseConnection(int sid) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + return; + } + socketFree(sid); +} + +/******************************************************************************/ +/* + * Accept a connection. Called as a callback on incoming connection. + */ + +static void socketAccept(socket_t *sp) +{ + struct sockaddr_in addr; + socket_t *nsp; + size_t len; + char *pString; + int newSock, nid; + + +#ifdef NW + NETINET_DEFINE_CONTEXT; +#endif + + a_assert(sp); + +/* + * Accept the connection and prevent inheriting by children (F_SETFD) + */ + len = sizeof(struct sockaddr_in); + if ((newSock = accept(sp->sock, (struct sockaddr *) &addr, (int *) &len)) < 0) { + return; + } +#ifndef __NO_FCNTL + fcntl(newSock, F_SETFD, FD_CLOEXEC); +#endif + socketHighestFd = max(socketHighestFd, newSock); + +/* + * Create a socket structure and insert into the socket list + */ + nid = socketAlloc(sp->host, sp->port, sp->accept, sp->flags); + nsp = socketList[nid]; + a_assert(nsp); + nsp->sock = newSock; + nsp->flags &= ~SOCKET_LISTENING; + + if (nsp == NULL) { + return; + } +/* + * Set the blocking mode before calling the accept callback. + */ + + socketSetBlock(nid, (nsp->flags & SOCKET_BLOCK) ? 1: 0); +/* + * Call the user accept callback. The user must call socketCreateHandler + * to register for further events of interest. + */ + if (sp->accept != NULL) { + pString = inet_ntoa(addr.sin_addr); + if ((sp->accept)(nid, pString, ntohs(addr.sin_port), sp->sid) < 0) { + socketFree(nid); + } +#ifdef VXWORKS + free(pString); +#endif + } +} + +/******************************************************************************/ +/* + * Get more input from the socket and return in buf. + * Returns 0 for EOF, -1 for errors and otherwise the number of bytes read. + */ + +int socketGetInput(int sid, char *buf, int toRead, int *errCode) +{ + struct sockaddr_in server; + socket_t *sp; + int len, bytesRead; + + a_assert(buf); + a_assert(errCode); + + *errCode = 0; + + if ((sp = socketPtr(sid)) == NULL) { + return -1; + } + +/* + * If we have previously seen an EOF condition, then just return + */ + if (sp->flags & SOCKET_EOF) { + return 0; + } +#if ((defined (WIN) || defined (CE)) && (!defined (LITTLEFOOT) && !defined (WEBS))) + if ( !(sp->flags & SOCKET_BLOCK) + && ! socketWaitForEvent(sp, FD_CONNECT, errCode)) { + return -1; + } +#endif + +/* + * Read the data + */ + if (sp->flags & SOCKET_DATAGRAM) { + len = sizeof(server); + bytesRead = recvfrom(sp->sock, buf, toRead, 0, + (struct sockaddr *) &server, &len); + } else { + bytesRead = recv(sp->sock, buf, toRead, 0); + } + + /* + * BUG 01865 -- CPU utilization hangs on Windows. The original code used + * the 'errno' global variable, which is not set by the winsock functions + * as it is under *nix platforms. We use the platform independent + * socketGetError() function instead, which does handle Windows correctly. + * Other, *nix compatible platforms should work as well, since on those + * platforms, socketGetError() just returns the value of errno. + * Thanks to Jonathan Burgoyne for the fix. + */ + if (bytesRead < 0) + { + *errCode = socketGetError(); + if (*errCode == ECONNRESET) + { + sp->flags |= SOCKET_CONNRESET; + return 0; + } + return -1; + } + return bytesRead; +} + +/******************************************************************************/ +/* + * Process an event on the event queue + */ + +#ifndef UEMF + +static int socketEventProc(void *data, int mask) +{ + socket_t *sp; + ringq_t *rq; + int sid; + + sid = (int) data; + + a_assert(sid >= 0 && sid < socketMax); + a_assert(socketList[sid]); + + if ((sp = socketPtr(sid)) == NULL) { + return 1; + } + +/* + * If now writable and flushing in the background, continue flushing + */ + if (mask & SOCKET_WRITABLE) { + if (sp->flags & SOCKET_FLUSHING) { + rq = &sp->outBuf; + if (ringqLen(rq) > 0) { + socketFlush(sp->sid); + } else { + sp->flags &= ~SOCKET_FLUSHING; + } + } + } + +/* + * Now invoke the users socket handler. NOTE: the handler may delete the + * socket, so we must be very careful after calling the handler. + */ + if (sp->handler && (sp->handlerMask & mask)) { + (sp->handler)(sid, mask & sp->handlerMask, sp->handler_data); + } + if (socketList && sid < socketMax && socketList[sid] == sp) { + socketRegisterInterest(sp, sp->handlerMask); + } + return 1; +} +#endif /* ! UEMF */ + +/******************************************************************************/ +/* + * Define the events of interest + */ + +void socketRegisterInterest(socket_t *sp, int handlerMask) +{ + a_assert(sp); + + sp->handlerMask = handlerMask; +#ifndef UEMF + if (handlerMask) { + sp->fileHandle = emfCreateFileHandler(sp->sock, handlerMask, + (emfFileProc *) socketEventProc, (void *) sp->sid); + } else { + emfDeleteFileHandler(sp->fileHandle); + sp->fileHandle = -1; + } +#endif /* ! UEMF */ +} + +/******************************************************************************/ +/* + * Wait until an event occurs on a socket. Return 1 on success, 0 on failure. + * or -1 on exception (UEMF only) + */ + +int socketWaitForEvent(socket_t *sp, int handlerMask, int *errCode) +{ + int mask; + + a_assert(sp); + + mask = sp->handlerMask; + sp->handlerMask |= handlerMask; + while (socketSelect(sp->sid, 1000)) { + if (sp->currentEvents & (handlerMask | SOCKET_EXCEPTION)) { + break; + } + } + sp->handlerMask = mask; + if (sp->currentEvents & SOCKET_EXCEPTION) { + return -1; + } else if (sp->currentEvents & handlerMask) { + return 1; + } + if (errCode) { + *errCode = errno = EWOULDBLOCK; + } + return 0; +} + +/******************************************************************************/ +/* + * Return TRUE if there is a socket with an event ready to process, + */ + +int socketReady(int sid) +{ + socket_t *sp; + int all; + + all = 0; + if (sid < 0) { + sid = 0; + all = 1; + } + + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (! all) { + break; + } else { + continue; + } + } + if (sp->flags & SOCKET_CONNRESET) { + socketCloseConnection(sid); + return 0; + } + if (sp->currentEvents & sp->handlerMask) { + return 1; + } +/* + * If there is input data, also call select to test for new events + */ + if (sp->handlerMask & SOCKET_READABLE && socketInputBuffered(sid) > 0) { + socketSelect(sid, 0); + return 1; + } + if (! all) { + break; + } + } + return 0; +} + +/******************************************************************************/ +/* + * Wait for a handle to become readable or writable and return a number of + * noticed events. Timeout is in milliseconds. + */ + +#if (defined (WIN) || defined (CE) || defined (NW)) + +int socketSelect(int sid, int timeout) +{ + struct timeval tv; + socket_t *sp; + fd_set readFds, writeFds, exceptFds; + int nEvents; + int all, socketHighestFd; /* Highest socket fd opened */ + + FD_ZERO(&readFds); + FD_ZERO(&writeFds); + FD_ZERO(&exceptFds); + socketHighestFd = -1; + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + +/* + * Set the select event masks for events to watch + */ + all = nEvents = 0; + + if (sid < 0) { + all++; + sid = 0; + } + + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + continue; + } + a_assert(sp); +/* + * Set the appropriate bit in the ready masks for the sp->sock. + */ + if (sp->handlerMask & SOCKET_READABLE) { + FD_SET(sp->sock, &readFds); + nEvents++; + if (socketInputBuffered(sid) > 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + if (sp->handlerMask & SOCKET_WRITABLE) { + FD_SET(sp->sock, &writeFds); + nEvents++; + } + if (sp->handlerMask & SOCKET_EXCEPTION) { + FD_SET(sp->sock, &exceptFds); + nEvents++; + } + if (! all) { + break; + } + } + +/* + * Windows select() fails if no descriptors are set, instead of just sleeping + * like other, nice select() calls. So, if WIN, sleep. + */ + if (nEvents == 0) { + Sleep(timeout); + return 0; + } + +/* + * Wait for the event or a timeout. + */ + nEvents = select(socketHighestFd+1, &readFds, &writeFds, &exceptFds, &tv); + + if (all) { + sid = 0; + } + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + continue; + } + + if (FD_ISSET(sp->sock, &readFds) || socketInputBuffered(sid) > 0) { + sp->currentEvents |= SOCKET_READABLE; + } + if (FD_ISSET(sp->sock, &writeFds)) { + sp->currentEvents |= SOCKET_WRITABLE; + } + if (FD_ISSET(sp->sock, &exceptFds)) { + sp->currentEvents |= SOCKET_EXCEPTION; + } + if (! all) { + break; + } + } + + return nEvents; +} + +#else /* not WIN || CE || NW */ + +int socketSelect(int sid, int timeout) +{ + socket_t *sp; + struct timeval tv; + fd_mask *readFds, *writeFds, *exceptFds; + int all, len, nwords, index, bit, nEvents; + +/* + * Allocate and zero the select masks + */ + nwords = (socketHighestFd + NFDBITS) / NFDBITS; + len = nwords * sizeof(int); + + readFds = balloc(B_L, len); + memset(readFds, 0, len); + writeFds = balloc(B_L, len); + memset(writeFds, 0, len); + exceptFds = balloc(B_L, len); + memset(exceptFds, 0, len); + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + +/* + * Set the select event masks for events to watch + */ + all = nEvents = 0; + + if (sid < 0) { + all++; + sid = 0; + } + + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (all == 0) { + break; + } else { + continue; + } + } + a_assert(sp); + +/* + * Initialize the ready masks and compute the mask offsets. + */ + index = sp->sock / (NBBY * sizeof(fd_mask)); + bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask))); + +/* + * Set the appropriate bit in the ready masks for the sp->sock. + */ + if (sp->handlerMask & SOCKET_READABLE) { + readFds[index] |= bit; + nEvents++; + if (socketInputBuffered(sid) > 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + if (sp->handlerMask & SOCKET_WRITABLE) { + writeFds[index] |= bit; + nEvents++; + } + if (sp->handlerMask & SOCKET_EXCEPTION) { + exceptFds[index] |= bit; + nEvents++; + } + if (! all) { + break; + } + } + +/* + * Wait for the event or a timeout. Reset nEvents to be the number of actual + * events now. + */ + nEvents = select(socketHighestFd + 1, (fd_set *) readFds, + (fd_set *) writeFds, (fd_set *) exceptFds, &tv); + + if (nEvents > 0) { + if (all) { + sid = 0; + } + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (all == 0) { + break; + } else { + continue; + } + } + + index = sp->sock / (NBBY * sizeof(fd_mask)); + bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask))); + + if (readFds[index] & bit || socketInputBuffered(sid) > 0) { + sp->currentEvents |= SOCKET_READABLE; + } + if (writeFds[index] & bit) { + sp->currentEvents |= SOCKET_WRITABLE; + } + if (exceptFds[index] & bit) { + sp->currentEvents |= SOCKET_EXCEPTION; + } + if (! all) { + break; + } + } + } + + bfree(B_L, readFds); + bfree(B_L, writeFds); + bfree(B_L, exceptFds); + + return nEvents; +} +#endif /* WIN || CE */ + +/******************************************************************************/ +/* + * Process socket events + */ + +void socketProcess(int sid) +{ + socket_t *sp; + int all; + + all = 0; + if (sid < 0) { + all = 1; + sid = 0; + } +/* + * Process each socket + */ + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (! all) { + break; + } else { + continue; + } + } + if (socketReady(sid)) { + socketDoEvent(sp); + } + if (! all) { + break; + } + } +} + +/******************************************************************************/ +/* + * Process an event on the event queue + */ + +static int socketDoEvent(socket_t *sp) +{ + ringq_t *rq; + int sid; + + a_assert(sp); + + sid = sp->sid; + if (sp->currentEvents & SOCKET_READABLE) { + if (sp->flags & SOCKET_LISTENING) { + socketAccept(sp); + sp->currentEvents = 0; + return 1; + } + + } else { +/* + * If there is still read data in the buffers, trigger the read handler + * NOTE: this may busy spin if the read handler doesn't read the data + */ + if (sp->handlerMask & SOCKET_READABLE && socketInputBuffered(sid) > 0) { + sp->currentEvents |= SOCKET_READABLE; + } + } + + +/* + * If now writable and flushing in the background, continue flushing + */ + if (sp->currentEvents & SOCKET_WRITABLE) { + if (sp->flags & SOCKET_FLUSHING) { + rq = &sp->outBuf; + if (ringqLen(rq) > 0) { + socketFlush(sp->sid); + } else { + sp->flags &= ~SOCKET_FLUSHING; + } + } + } + +/* + * Now invoke the users socket handler. NOTE: the handler may delete the + * socket, so we must be very careful after calling the handler. + */ + if (sp->handler && (sp->handlerMask & sp->currentEvents)) { + (sp->handler)(sid, sp->handlerMask & sp->currentEvents, + sp->handler_data); +/* + * Make sure socket pointer is still valid, then reset the currentEvents. + */ + if (socketList && sid < socketMax && socketList[sid] == sp) { + sp->currentEvents = 0; + } + } + return 1; +} + +/******************************************************************************/ +/* + * Set the socket blocking mode + */ + +int socketSetBlock(int sid, int on) +{ + socket_t *sp; + unsigned long flag; + int iflag; + int oldBlock; + + flag = iflag = !on; + + if ((sp = socketPtr(sid)) == NULL) { + a_assert(0); + return 0; + } + oldBlock = (sp->flags & SOCKET_BLOCK); + sp->flags &= ~(SOCKET_BLOCK); + if (on) { + sp->flags |= SOCKET_BLOCK; + } + +/* + * Put the socket into block / non-blocking mode + */ + if (sp->flags & SOCKET_BLOCK) { +#if (defined (CE) || defined (WIN)) + ioctlsocket(sp->sock, FIONBIO, &flag); +#elif (defined (ECOS)) + int off; + off = 0; + ioctl(sp->sock, FIONBIO, &off); +#elif (defined (VXWORKS) || defined (NW)) + ioctl(sp->sock, FIONBIO, (int)&iflag); +#else + fcntl(sp->sock, F_SETFL, fcntl(sp->sock, F_GETFL) & ~O_NONBLOCK); +#endif + + } else { +#if (defined (CE) || defined (WIN)) + ioctlsocket(sp->sock, FIONBIO, &flag); +#elif (defined (ECOS)) + int on; + on = 1; + ioctl(sp->sock, FIONBIO, &on); +#elif (defined (VXWORKS) || defined (NW)) + ioctl(sp->sock, FIONBIO, (int)&iflag); +#else + fcntl(sp->sock, F_SETFL, fcntl(sp->sock, F_GETFL) | O_NONBLOCK); +#endif + } + return oldBlock; +} + +/******************************************************************************/ +/* + * Return true if a readable socket has buffered data. - not public + */ + +int socketDontBlock() +{ + socket_t *sp; + int i; + + for (i = 0; i < socketMax; i++) { + if ((sp = socketList[i]) == NULL || + (sp->handlerMask & SOCKET_READABLE) == 0) { + continue; + } + if (socketInputBuffered(i) > 0) { + return 1; + } + } + return 0; +} + +/******************************************************************************/ +/* + * Return true if a particular socket buffered data. - not public + */ + +int socketSockBuffered(int sock) +{ + socket_t *sp; + int i; + + for (i = 0; i < socketMax; i++) { + if ((sp = socketList[i]) == NULL || sp->sock != sock) { + continue; + } + return socketInputBuffered(i); + } + return 0; +} + +#endif /* (!WIN) | LITTLEFOOT | WEBS */ + +/******************************************************************************/ + diff --git a/c/src/libnetworking/rtems_webserver/umui.c b/c/src/libnetworking/rtems_webserver/umui.c new file mode 100644 index 0000000000..971b059bc7 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/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("") +#define MSG_START T("

") +#define MSG_END T("

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

User ID: %s

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

User Group: %s

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

Enabled: %d

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