diff options
author | tenox <as@tenoware.com> | 2016-01-11 23:57:03 -0800 |
---|---|---|
committer | tenox <as@tenoware.com> | 2016-01-11 23:57:03 -0800 |
commit | 245eaad379742cc0ba9992c858523664b02102fb (patch) | |
tree | 47eb4faed0a498e42aaf927ed24eb66bac601a9e | |
download | wfm-1.0.0.tar.gz |
initial commit1.0.0
-rw-r--r-- | Makefile | 25 | ||||
-rw-r--r-- | README | 57 | ||||
-rw-r--r-- | bin2c.c | 123 | ||||
-rw-r--r-- | cgic.c | 2559 | ||||
-rw-r--r-- | cgic.diff | 102 | ||||
-rw-r--r-- | cgic.h | 232 | ||||
-rw-r--r-- | dialogs.c | 435 | ||||
-rw-r--r-- | dir.c | 607 | ||||
-rw-r--r-- | fileio.c | 518 | ||||
-rwxr-xr-x | icons/adn.gif | bin | 0 -> 49 bytes | |||
-rwxr-xr-x | icons/arr.gif | bin | 0 -> 345 bytes | |||
-rwxr-xr-x | icons/aup.gif | bin | 0 -> 48 bytes | |||
-rwxr-xr-x | icons/clr.gif | bin | 0 -> 55 bytes | |||
-rwxr-xr-x | icons/delete.gif | bin | 0 -> 345 bytes | |||
-rwxr-xr-x | icons/dir.gif | bin | 0 -> 354 bytes | |||
-rwxr-xr-x | icons/dir_up.gif | bin | 0 -> 554 bytes | |||
-rw-r--r-- | icons/disk.gif | bin | 0 -> 386 bytes | |||
-rwxr-xr-x | icons/edit.gif | bin | 0 -> 578 bytes | |||
-rwxr-xr-x | icons/exe.gif | bin | 0 -> 354 bytes | |||
-rwxr-xr-x | icons/ext.gif | bin | 0 -> 564 bytes | |||
-rwxr-xr-x | icons/gen.gif | bin | 0 -> 607 bytes | |||
-rwxr-xr-x | icons/home.gif | bin | 0 -> 627 bytes | |||
-rwxr-xr-x | icons/img.gif | bin | 0 -> 572 bytes | |||
-rwxr-xr-x | icons/iso.gif | bin | 0 -> 617 bytes | |||
-rwxr-xr-x | icons/lck.gif | bin | 0 -> 364 bytes | |||
-rwxr-xr-x | icons/med.gif | bin | 0 -> 586 bytes | |||
-rwxr-xr-x | icons/mkdir.gif | bin | 0 -> 601 bytes | |||
-rwxr-xr-x | icons/mkfile.gif | bin | 0 -> 604 bytes | |||
-rwxr-xr-x | icons/move.gif | bin | 0 -> 340 bytes | |||
-rwxr-xr-x | icons/net.gif | bin | 0 -> 352 bytes | |||
-rwxr-xr-x | icons/off.gif | bin | 0 -> 598 bytes | |||
-rwxr-xr-x | icons/pdf.gif | bin | 0 -> 605 bytes | |||
-rwxr-xr-x | icons/readonly.gif | bin | 0 -> 588 bytes | |||
-rwxr-xr-x | icons/readwrite.gif | bin | 0 -> 567 bytes | |||
-rwxr-xr-x | icons/reload.gif | bin | 0 -> 604 bytes | |||
-rwxr-xr-x | icons/rename.gif | bin | 0 -> 335 bytes | |||
-rwxr-xr-x | icons/time.gif | bin | 0 -> 381 bytes | |||
-rwxr-xr-x | icons/txt.gif | bin | 0 -> 376 bytes | |||
-rwxr-xr-x | icons/user.gif | bin | 0 -> 373 bytes | |||
-rwxr-xr-x | icons/ver.gif | bin | 0 -> 591 bytes | |||
-rwxr-xr-x | icons/wfmicon.gif | bin | 0 -> 622 bytes | |||
-rwxr-xr-x | icons/zip.gif | bin | 0 -> 382 bytes | |||
-rw-r--r-- | md5.c | 381 | ||||
-rw-r--r-- | md5.h | 91 | ||||
-rwxr-xr-x | mkicons.sh | 11 | ||||
-rw-r--r-- | wfm.c | 462 | ||||
-rw-r--r-- | wfm.cfg | 28 | ||||
-rw-r--r-- | wfm.h | 149 |
48 files changed, 5780 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3753a13 --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +CC=gcc -O3 -Wall +OB=wfm.o dir.o dialogs.o fileio.o cgic.o md5.o + +all: wfm + +wfm: ${OB} + ${CC} ${OB} -o wfm + @strip wfm + @du -h wfm + cp wfm ../web/tenox/app + +wfm.o: wfm.c wfmiconres.h wfm.h + +wfmiconres.h: bin2c + bash ./mkicons.sh + +bin2c: bin2c.c + ${CC} -o bin2c bin2c.c + +.c.o: + ${CC} -c $< + +clean: + rm -f *.o wfm wfmicon*.h bin2c + @@ -0,0 +1,57 @@ +WFM - Web File Manager +====================== +WFM is a web file management application. It allows to upload, download, +rename, delete files and organize directory / folder structure using a +standard web browser with CGI. + +This program is written using portable C code and compiles natively +for many flavors of Unix. The output is a pure W3C certified HTML 4.01 +and doesn't require JavaScript support in the browser. WFM will work in +most HTTP 1.1 web servers with CGI support. Due to compact size and +lightning fast execution time it's ideal for small and embedded systems. + + +History +======= +WFM has been conceived around 1994 as a perl cgi script for CERN httpd +server to allow uploading data files by field support engineers. Later +rewritten in C language, when CGIC library and Apache httpd were released +around 1996. Up to 2015 WFM has been a closed source commercial application, +supported by a few large enterprise and smaller corporation users. WFM is +now released as open source. + + +Installation +============ +WFM binary is self contained including all icons/images. You only need +to copy the compiled wfm binary (with any name) to your cgi execution +directory. Include a config file of the same name as the binary file plus +.cfg extension. Example: + +/u/foo/web/cgi-bin + wfm + wfm.cfg + +Edit the cfg file according to your needs. + +Point your browser to http://x.x.x.x/cgi-bin/wfm and you are done. + +Configuration +============= + +Copyrights +========== +Implemented by Antoni Sawicki +CGIC Library by Thomas Boutell +Server Side RFC 1321 implementation by L. Peter Deutsch +Client Side RFC 1321 implementation by Paul Johnston +Icons by Yusuke Kamiyamane +Copyright (c) 1994-2015 by Antoni Sawicki +Copyright (c) 1996-2011 by Thomas Boutell and Boutell.Com, Inc. +Copyright (c) 2002 by Aladdin Enterprises +Copyright (c) 1999-2009 by Paul Johnston +Copyright (c) 2010 by Yusuke Kamiyamane + +License +======= +BSD @@ -0,0 +1,123 @@ +// bin2c.c +// +// convert a binary file into a C source vector +// +// THE "BEER-WARE LICENSE" (Revision 3.1415): +// sandro AT sigala DOT it wrote this file. As long as you retain this notice you can do +// whatever you want with this stuff. If we meet some day, and you think this stuff is +// worth it, you can buy me a beer in return. Sandro Sigala +// +// syntax: bin2c [-c] [-z] <input_file> <output_file> +// +// -c add the "const" keyword to definition +// -z terminate the array with a zero (useful for embedded C strings) +// +// examples: +// bin2c -c myimage.png myimage_png.cpp +// bin2c -z sometext.txt sometext_txt.cpp + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +int useconst = 0; +int zeroterminated = 0; + +int myfgetc(FILE *f) +{ + int c = fgetc(f); + if (c == EOF && zeroterminated) + { + zeroterminated = 0; + return 0; + } + return c; +} + +void process(const char *ifname, const char *ofname) +{ + FILE *ifile, *ofile; + ifile = fopen(ifname, "rb"); + if (ifile == NULL) + { + fprintf(stderr, "cannot open %s for reading\n", ifname); + exit(1); + } + ofile = fopen(ofname, "ab"); + if (ofile == NULL) + { + fprintf(stderr, "cannot open %s for writing\n", ofname); + exit(1); + } + char buf[PATH_MAX], *p; + const char *cp; + if ((cp = strrchr(ifname, '/')) != NULL) + { + ++cp; + } else { + if ((cp = strrchr(ifname, '\\')) != NULL) + ++cp; + else + cp = ifname; + } + strcpy(buf, cp); + for (p = buf; *p != '\0'; ++p) + { + if (!isalnum((int)*p)) + *p = '_'; + } + fprintf(ofile, "static %sunsigned char %s[] = {\n", useconst ? "const " : "", buf); + int c, col = 1; + while ((c = myfgetc(ifile)) != EOF) + { + if (col >= 78 - 6) + { + fputc('\n', ofile); + col = 1; + } + fprintf(ofile, "0x%.2x, ", c); + col += 6; + } + fprintf(ofile, "\n};\n"); + + fclose(ifile); + fclose(ofile); +} + +void usage(void) +{ + fprintf(stderr, "usage: bin2c [-cz] <input_file> <output_file>\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + while (argc > 3) + { + if (!strcmp(argv[1], "-c")) + { + useconst = 1; + --argc; + ++argv; + } else if (!strcmp(argv[1], "-z")) + { + zeroterminated = 1; + --argc; + ++argv; + } else { + usage(); + } + } + if (argc != 3) + { + usage(); + } + process(argv[1], argv[2]); + return 0; +} + @@ -0,0 +1,2559 @@ +/* cgicTempDir is the only setting you are likely to need + to change in this file. */ + +/* Used only in Unix environments, in conjunction with mkstemp(). + Elsewhere (Windows), temporary files go where the tmpnam() + function suggests. If this behavior does not work for you, + modify the getTempFileName() function to suit your needs. */ + +#define cgicTempDir "/tmp" + +#if CGICDEBUG +#define CGICDEBUGSTART \ + { \ + FILE *dout; \ + dout = fopen("/home/boutell/public_html/debug", "a"); \ + +#define CGICDEBUGEND \ + fclose(dout); \ + } +#else /* CGICDEBUG */ +#define CGICDEBUGSTART +#define CGICDEBUGEND +#endif /* CGICDEBUG */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> +#include <sys/shm.h> + +#ifdef WIN32 +#include <io.h> + +/* cgic 2.01 */ +#include <fcntl.h> + +#else +#include <unistd.h> +#endif /* WIN32 */ +#include "cgic.h" + +#define cgiStrEq(a, b) (!strcmp((a), (b))) + +char *cgiServerSoftware; +char *cgiServerName; +char *cgiGatewayInterface; +char *cgiServerProtocol; +char *cgiServerPort; +char *cgiRequestMethod; +char *cgiPathInfo; +char *cgiPathTranslated; +char *cgiScriptName; +char *cgiQueryString; +char *cgiRemoteHost; +char *cgiRemoteAddr; +char *cgiAuthType; +char *cgiRemoteUser; +char *cgiRemoteIdent; +char cgiContentTypeData[1024]; +char *cgiContentType = cgiContentTypeData; +char *cgiMultipartBoundary; +char *cgiCookie; +int cgiContentLength; +char *cgiAccept; +char *cgiUserAgent; +char *cgiReferrer; + +char *shm_addr='\0'; +int shm_key=0; +int shm_id=0; +#define SHM_SIZE 16 + + +FILE *cgiIn; +FILE *cgiOut; + +/* True if CGI environment was restored from a file. */ +static int cgiRestored = 0; + +static void cgiGetenv(char **s, char *var); + +typedef enum { + cgiParseSuccess, + cgiParseMemory, + cgiParseIO +} cgiParseResultType; + +/* One form entry, consisting of an attribute-value pair, + and an optional filename and content type. All of + these are guaranteed to be valid null-terminated strings, + which will be of length zero in the event that the + field is not present, with the exception of tfileName + which will be null when 'in' is null. DO NOT MODIFY THESE + VALUES. Make local copies if modifications are desired. */ + +typedef struct cgiFormEntryStruct { + char *attr; + /* value is populated for regular form fields only. + For file uploads, it points to an empty string, and file + upload data should be read from the file tfileName. */ + char *value; + /* When fileName is not an empty string, tfileName is not null, + and 'value' points to an empty string. */ + /* Valid for both files and regular fields; does not include + terminating null of regular fields. */ + int valueLength; + char *fileName; + char *contentType; + /* Temporary file name for working storage of file uploads. */ + char *tfileName; + struct cgiFormEntryStruct *next; +} cgiFormEntry; + +/* The first form entry. */ +static cgiFormEntry *cgiFormEntryFirst; + +static cgiParseResultType cgiParseGetFormInput(); +static cgiParseResultType cgiParsePostFormInput(); +static cgiParseResultType cgiParsePostMultipartInput(); +static cgiParseResultType cgiParseFormInput(char *data, int length); +static void cgiSetupConstants(); +static void cgiFreeResources(); +static int cgiStrEqNc(char *s1, char *s2); +static int cgiStrBeginsNc(char *s1, char *s2); + +/* Dirty little hack to get file upload progress - Part 4: SHM Cleanup */ +void shmcleanup(void) { + if(shm_id) + shmctl(shm_id, IPC_RMID, NULL); +} + +int main(int argc, char *argv[]) { + int result; + char *cgiContentLengthString; + char *e; + cgiSetupConstants(); + cgiGetenv(&cgiServerSoftware, "SERVER_SOFTWARE"); + cgiGetenv(&cgiServerName, "SERVER_NAME"); + cgiGetenv(&cgiGatewayInterface, "GATEWAY_INTERFACE"); + cgiGetenv(&cgiServerProtocol, "SERVER_PROTOCOL"); + cgiGetenv(&cgiServerPort, "SERVER_PORT"); + cgiGetenv(&cgiRequestMethod, "REQUEST_METHOD"); + cgiGetenv(&cgiPathInfo, "PATH_INFO"); + cgiGetenv(&cgiPathTranslated, "PATH_TRANSLATED"); + cgiGetenv(&cgiScriptName, "SCRIPT_NAME"); + cgiGetenv(&cgiQueryString, "QUERY_STRING"); + cgiGetenv(&cgiRemoteHost, "REMOTE_HOST"); + cgiGetenv(&cgiRemoteAddr, "REMOTE_ADDR"); + cgiGetenv(&cgiAuthType, "AUTH_TYPE"); + cgiGetenv(&cgiRemoteUser, "REMOTE_USER"); + cgiGetenv(&cgiRemoteIdent, "REMOTE_IDENT"); + /* 2.0: the content type string needs to be parsed and modified, so + copy it to a buffer. */ + e = getenv("CONTENT_TYPE"); + if (e) { + if (strlen(e) < sizeof(cgiContentTypeData)) { + strcpy(cgiContentType, e); + } else { + /* Truncate safely in the event of what is almost certainly + a hack attempt */ + strncpy(cgiContentType, e, sizeof(cgiContentTypeData)); + cgiContentType[sizeof(cgiContentTypeData) - 1] = '\0'; + } + } else { + cgiContentType[0] = '\0'; + } + /* Never null */ + cgiMultipartBoundary = ""; + /* 2.0: parse semicolon-separated additional parameters of the + content type. The one we're interested in is 'boundary'. + We discard the rest to make cgiContentType more useful + to the typical programmer. */ + if (strchr(cgiContentType, ';')) { + char *sat = strchr(cgiContentType, ';'); + while (sat) { + *sat = '\0'; + sat++; + while (isspace((int)*sat)) { + sat++; + } + if (cgiStrBeginsNc(sat, "boundary=")) { + char *s; + cgiMultipartBoundary = sat + strlen("boundary="); + s = cgiMultipartBoundary; + while ((*s) && (!isspace((int)*s))) { + s++; + } + *s = '\0'; + break; + } else { + sat = strchr(sat, ';'); + } + } + } + cgiGetenv(&cgiContentLengthString, "CONTENT_LENGTH"); + cgiContentLength = atoi(cgiContentLengthString); + cgiGetenv(&cgiAccept, "HTTP_ACCEPT"); + cgiGetenv(&cgiUserAgent, "HTTP_USER_AGENT"); + cgiGetenv(&cgiReferrer, "HTTP_REFERER"); + cgiGetenv(&cgiCookie, "HTTP_COOKIE"); +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "%d\n", cgiContentLength); + fprintf(dout, "%s\n", cgiRequestMethod); + fprintf(dout, "%s\n", cgiContentType); + CGICDEBUGEND +#endif /* CGICDEBUG */ +#ifdef WIN32 + /* 1.07: Must set stdin and stdout to binary mode */ + /* 2.0: this is particularly crucial now and must not be removed */ + _setmode( _fileno( stdin ), _O_BINARY ); + _setmode( _fileno( stdout ), _O_BINARY ); +#endif /* WIN32 */ + cgiFormEntryFirst = 0; + cgiIn = stdin; + cgiOut = stdout; + cgiRestored = 0; + + + /* These five lines keep compilers from + producing warnings that argc and argv + are unused. They have no actual function. */ + if (argc) { + if (argv[0]) { + cgiRestored = 0; + } + } + + + if (cgiStrEqNc(cgiRequestMethod, "post")) { +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "POST recognized\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + if (cgiStrEqNc(cgiContentType, "application/x-www-form-urlencoded")) { +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "Calling PostFormInput\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + if (cgiParsePostFormInput() != cgiParseSuccess) { +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "PostFormInput failed\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + cgiFreeResources(); + return -1; + } +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "PostFormInput succeeded\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + } else if (cgiStrEqNc(cgiContentType, "multipart/form-data")) { +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "Calling PostMultipartInput\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + if (cgiParsePostMultipartInput() != cgiParseSuccess) { +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "PostMultipartInput failed\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + cgiFreeResources(); + return -1; + } +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "PostMultipartInput succeeded\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + } + } else if (cgiStrEqNc(cgiRequestMethod, "get")) { + /* The spec says this should be taken care of by + the server, but... it isn't */ + cgiContentLength = strlen(cgiQueryString); + if (cgiParseGetFormInput() != cgiParseSuccess) { +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "GetFormInput failed\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + cgiFreeResources(); + return -1; + } else { +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "GetFormInput succeeded\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + } + } + result = cgiMain(); + cgiFreeResources(); + return result; +} + +static void cgiGetenv(char **s, char *var){ + *s = getenv(var); + if (!(*s)) { + *s = ""; + } +} + +static cgiParseResultType cgiParsePostFormInput() { + char *input; + cgiParseResultType result; + if (!cgiContentLength) { + return cgiParseSuccess; + } + input = (char *) malloc(cgiContentLength); + if (!input) { + return cgiParseMemory; + } + if (((int) fread(input, 1, cgiContentLength, cgiIn)) + != cgiContentLength) + { + return cgiParseIO; + } + result = cgiParseFormInput(input, cgiContentLength); + free(input); + return result; +} + +/* 2.0: A virtual datastream supporting putback of + enough characters to handle multipart boundaries easily. + A simple memset(&mp, 0, sizeof(mp)) is suitable initialization. */ + +typedef struct { + /* Buffer for putting characters back */ + char putback[1024]; + /* Position in putback from which next character will be read. + If readPos == writePos, then next character should + come from cgiIn. */ + int readPos; + /* Position in putback to which next character will be put back. + If writePos catches up to readPos, as opposed to the other + way around, the stream no longer functions properly. + Calling code must guarantee that no more than + sizeof(putback) bytes are put back at any given time. */ + int writePos; + /* Offset in the virtual datastream; can be compared + to cgiContentLength */ + int offset; +} mpStream, *mpStreamPtr; + +int mpRead(mpStreamPtr mpp, char *buffer, int len) +{ + int ilen = len; + int got = 0; + while (len) { + if (mpp->readPos != mpp->writePos) { + *buffer++ = mpp->putback[mpp->readPos++]; + mpp->readPos %= sizeof(mpp->putback); + got++; + len--; + } else { + break; + } + } + /* Refuse to read past the declared length in order to + avoid deadlock */ + if (len > (cgiContentLength - mpp->offset)) { + len = cgiContentLength - mpp->offset; + } + if (len) { + int fgot = fread(buffer, 1, len, cgiIn); + if (fgot >= 0) { + mpp->offset += (got + fgot); + return got + fgot; + } else if (got > 0) { + mpp->offset += got; + return got; + } else { + /* EOF or error */ + return fgot; + } + } else if (got) { + return got; + } else if (ilen) { + return EOF; + } else { + /* 2.01 */ + return 0; + } +} + +void mpPutBack(mpStreamPtr mpp, char *data, int len) +{ + mpp->offset -= len; + while (len) { + mpp->putback[mpp->writePos++] = *data++; + mpp->writePos %= sizeof(mpp->putback); + len--; + } +} + +/* This function copies the body to outf if it is not null, otherwise to + a newly allocated character buffer at *outP, which will be null + terminated; if both outf and outP are null the body is not stored. + If bodyLengthP is not null, the size of the body in bytes is stored + to *bodyLengthP, not including any terminating null added to *outP. + If 'first' is nonzero, a preceding newline is not expected before + the boundary. If 'first' is zero, a preceding newline is expected. + Upon return mpp is positioned after the boundary and its trailing + newline, if any; if the boundary is followed by -- the next two + characters read after this function returns will be --. Upon error, + if outP is not null, *outP is a null pointer; *bodyLengthP + is set to zero. Returns cgiParseSuccess, cgiParseMemory + or cgiParseIO. */ + +static cgiParseResultType afterNextBoundary(mpStreamPtr mpp, + FILE *outf, + char **outP, + int *bodyLengthP, + int first + ); + +static int readHeaderLine( + mpStreamPtr mpp, + char *attr, + int attrSpace, + char *value, + int valueSpace); + +static void decomposeValue(char *value, + char *mvalue, int mvalueSpace, + char **argNames, + char **argValues, + int argValueSpace); + +/* tfileName must be 1024 bytes to ensure adequacy on + win32 (1024 exceeds the maximum path length and + certainly exceeds observed behavior of _tmpnam). + May as well also be 1024 bytes on Unix, although actual + length is strlen(cgiTempDir) + a short unique pattern. */ + +static cgiParseResultType getTempFileName(char *tfileName); + +static cgiParseResultType cgiParsePostMultipartInput() { + cgiParseResultType result; + cgiFormEntry *n = 0, *l = 0; + int got; + int sig; + FILE *outf = 0; + char *out = 0; + char tfileName[1024]; + mpStream mp; + mpStreamPtr mpp = ∓ + memset(&mp, 0, sizeof(mp)); + if (!cgiContentLength) { + return cgiParseSuccess; + } + /* Read first boundary, including trailing newline */ + result = afterNextBoundary(mpp, 0, 0, 0, 1); + if (result == cgiParseIO) { + /* An empty submission is not necessarily an error */ + return cgiParseSuccess; + } else if (result != cgiParseSuccess) { + return result; + } + while (1) { + char d[1024]; + char fvalue[1024]; + char fname[1024]; + int bodyLength = 0; + char ffileName[1024]; + char fcontentType[1024]; + char attr[1024]; + char value[1024]; + fvalue[0] = 0; + fname[0] = 0; + ffileName[0] = 0; + fcontentType[0] = 0; + out = 0; + outf = 0; + /* Check for EOF */ + got = mpRead(mpp, d, 2); + if (got < 2) { + /* Crude EOF */ + break; + } + if ((d[0] == '-') && (d[1] == '-')) { + /* Graceful EOF */ + break; + } + mpPutBack(mpp, d, 2); + /* Read header lines until end of header */ + while (readHeaderLine( + mpp, attr, sizeof(attr), value, sizeof(value))) + { + char *argNames[3]; + char *argValues[2]; + /* Content-Disposition: form-data; + name="test"; filename="googley.gif" */ + if (cgiStrEqNc(attr, "Content-Disposition")) { + argNames[0] = "name"; + argNames[1] = "filename"; + argNames[2] = 0; + argValues[0] = fname; + argValues[1] = ffileName; + decomposeValue(value, + fvalue, sizeof(fvalue), + argNames, + argValues, + 1024); + } else if (cgiStrEqNc(attr, "Content-Type")) { + argNames[0] = 0; + decomposeValue(value, + fcontentType, sizeof(fcontentType), + argNames, + 0, + 0); + } + } + if (!cgiStrEqNc(fvalue, "form-data")) { + /* Not form data */ + continue; + } + /* Body is everything from here until the next + boundary. So, set it aside and move past boundary. + If a filename was submitted as part of the + disposition header, store to a temporary file. + Otherwise, store to a memory buffer (it is + presumably a regular form field). */ + if (strlen(ffileName)) { + if (getTempFileName(tfileName) != cgiParseSuccess) { + return cgiParseIO; + } + outf = fopen(tfileName, "w+b"); + /* Dirty little hack to get file upload progress - Part 1: Initialize */ + if (cgiFormInteger("upload_id", &shm_key, 0) == cgiFormSuccess && shm_key) { + if ((shm_id = shmget(shm_key, 16, IPC_CREAT | 0666)) > 0) { + for(sig=1; sig<=31; sig++) + signal(sig, (void*)shmcleanup); + if ((shm_addr = shmat(shm_id, NULL, 0)) > 0) + *shm_addr='\0'; + } + } + } else { + outf = 0; + tfileName[0] = '\0'; + } + result = afterNextBoundary(mpp, outf, &out, &bodyLength, 0); + /* Dirty little hack to get file upload progress - Part 2: Clean up */ + if(shm_addr) + shmdt(shm_addr); + if(shm_id) + shmctl(shm_id, IPC_RMID, NULL); + if (result != cgiParseSuccess) { + /* Lack of a boundary here is an error. */ + if (outf) { + fclose(outf); + unlink(tfileName); + } + if (out) { + free(out); + } + return result; + } + /* OK, we have a new pair, add it to the list. */ + n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry)); + if (!n) { + goto outOfMemory; + } + memset(n, 0, sizeof(cgiFormEntry)); + /* 2.01: one of numerous new casts required + to please C++ compilers */ + n->attr = (char *) malloc(strlen(fname) + 1); + if (!n->attr) { + goto outOfMemory; + } + strcpy(n->attr, fname); + if (out) { + n->value = out; + out = 0; + } else if (outf) { + n->value = (char *) malloc(1); + if (!n->value) { + goto outOfMemory; + } + n->value[0] = '\0'; + fclose(outf); + } + n->valueLength = bodyLength; + n->next = 0; + if (!l) { + cgiFormEntryFirst = n; + } else { + l->next = n; + } + n->fileName = (char *) malloc(strlen(ffileName) + 1); + if (!n->fileName) { + goto outOfMemory; + } + strcpy(n->fileName, ffileName); + n->contentType = (char *) malloc(strlen(fcontentType) + 1); + if (!n->contentType) { + goto outOfMemory; + } + strcpy(n->contentType, fcontentType); + n->tfileName = (char *) malloc(strlen(tfileName) + 1); + if (!n->tfileName) { + goto outOfMemory; + } + strcpy(n->tfileName, tfileName); + + l = n; + } + return cgiParseSuccess; +outOfMemory: + if (n) { + if (n->attr) { + free(n->attr); + } + if (n->value) { + free(n->value); + } + if (n->fileName) { + free(n->fileName); + } + if (n->tfileName) { + free(n->tfileName); + } + if (n->contentType) { + free(n->contentType); + } + free(n); + } + if (out) { + free(out); + } + if (outf) { + fclose(outf); + unlink(tfileName); + } + return cgiParseMemory; +} + +static cgiParseResultType getTempFileName(char *tfileName) +{ +#ifndef WIN32 + /* Unix. Use the robust 'mkstemp' function to create + a temporary file that is truly unique, with + permissions that are truly safe. The + fopen-for-write destroys any bogus information + written by potential hackers during the brief + window between the file's creation and the + chmod call (glibc 2.0.6 and lower might + otherwise have allowed this). */ + int outfd; + strcpy(tfileName, cgicTempDir "/cgicXXXXXX"); + outfd = mkstemp(tfileName); + if (outfd == -1) { + return cgiParseIO; + } + close(outfd); + /* Fix the permissions */ + if (chmod(tfileName, 0600) != 0) { + unlink(tfileName); + return cgiParseIO; + } +#else + /* Non-Unix. Do what we can. */ + if (!tmpnam(tfileName)) { + return cgiParseIO; + } +#endif + return cgiParseSuccess; +} + + +#define APPEND(string, char) \ + { \ + if ((string##Len + 1) < string##Space) { \ + string[string##Len++] = (char); \ + } \ + } + +#define RAPPEND(string, ch) \ + { \ + if ((string##Len + 1) == string##Space) { \ + char *sold = string; \ + string##Space *= 2; \ + string = (char *) realloc(string, string##Space); \ + if (!string) { \ + string = sold; \ + goto outOfMemory; \ + } \ + } \ + string[string##Len++] = (ch); \ + } + +#define BAPPEND(ch) \ + { \ + if (outf) { \ + putc(ch, outf); \ + outLen++; \ + } else if (out) { \ + RAPPEND(out, ch); \ + } \ + } + +cgiParseResultType afterNextBoundary(mpStreamPtr mpp, FILE *outf, char **outP, + int *bodyLengthP, int first) +{ + int outLen = 0; + int outSpace = 256; + char *out = 0; + cgiParseResultType result; + int boffset; + int got; + off_t tot=0; + char d[2]; + /* This is large enough, because the buffer into which the + original boundary string is fetched is shorter by more + than four characters due to the space required for + the attribute name */ + char workingBoundaryData[1024]; + char *workingBoundary = workingBoundaryData; + int workingBoundaryLength; + if ((!outf) && (outP)) { + out = (char *) malloc(outSpace); + if (!out) { + goto outOfMemory; + } + } + boffset = 0; + sprintf(workingBoundaryData, "\r\n--%s", cgiMultipartBoundary); + if (first) { + workingBoundary = workingBoundaryData + 2; + } + workingBoundaryLength = strlen(workingBoundary); + while (1) { + got = mpRead(mpp, d, 1); + /* Dirty little hack to get file upload progress - Part 3: Set value */ + if(outf && shm_addr) + snprintf(shm_addr, SHM_SIZE, "%.1f M", (float)(tot+=got)/1024/1024); + if (got != 1) { + /* 2.01: cgiParseIO, not cgiFormIO */ + result = cgiParseIO; + goto error; + } + if (d[0] == workingBoundary[boffset]) { + /* We matched the next byte of the boundary. + Keep track of our progress into the + boundary and don't emit anything. */ + boffset++; + if (boffset == workingBoundaryLength) { + break; + } + } else if (boffset > 0) { + /* We matched part, but not all, of the + boundary. Now we have to be careful: + put back all except the first + character and try again. The + real boundary could begin in the + middle of a false match. We can + emit the first character only so far. */ + BAPPEND(workingBoundary[0]); + mpPutBack(mpp, + workingBoundary + 1, boffset - 1); + mpPutBack(mpp, d, 1); + boffset = 0; + } else { + /* Not presently in the middle of a boundary + match; just emit the character. */ + BAPPEND(d[0]); + } + } + /* Read trailing newline or -- EOF marker. A literal EOF here + would be an error in the input stream. */ + got = mpRead(mpp, d, 2); + if (got != 2) { + result = cgiParseIO; + goto error; + } + if ((d[0] == '\r') && (d[1] == '\n')) { + /* OK, EOL */ + } else if (d[0] == '-') { + /* Probably EOF, but we check for + that later */ + mpPutBack(mpp, d, 2); + } + if (out && outSpace) { + char *oout = out; + out[outLen] = '\0'; + out = (char *) realloc(out, outLen + 1); + if (!out) { + /* Surprising if it happens; and not fatal! We were + just trying to give some space back. We can + keep it if we have to. */ + out = oout; + } + *outP = out; + } + if (bodyLengthP) { + *bodyLengthP = outLen; + } + return cgiParseSuccess; +outOfMemory: + result = cgiParseMemory; + if (outP) { + if (out) { + free(out); + } + *outP = '\0'; + } +error: + if (bodyLengthP) { + *bodyLengthP = 0; + } + if (out) { + free(out); + } + if (outP) { + *outP = 0; + } + return result; +} + +static void decomposeValue(char *value, + char *mvalue, int mvalueSpace, + char **argNames, + char **argValues, + int argValueSpace) +{ + char argName[1024]; + int argNameSpace = sizeof(argName); + int argNameLen = 0; + int mvalueLen = 0; + char *argValue; + int argNum = 0; + while (argNames[argNum]) { + if (argValueSpace) { + argValues[argNum][0] = '\0'; + } + argNum++; + } + while (isspace((int)*value)) { + value++; + } + /* Quoted mvalue */ + if (*value == '\"') { + value++; + while ((*value) && (*value != '\"')) { + APPEND(mvalue, *value); + value++; + } + while ((*value) && (*value != ';')) { + value++; + } + } else { + /* Unquoted mvalue */ + while ((*value) && (*value != ';')) { + APPEND(mvalue, *value); + value++; + } + } + if (mvalueSpace) { + mvalue[mvalueLen] = '\0'; + } + while (*value == ';') { + int argNum; + int argValueLen = 0; + /* Skip the ; between parameters */ + value++; + /* Now skip leading whitespace */ + while ((*value) && (isspace((int)*value))) { + value++; + } + /* Now read the parameter name */ + argNameLen = 0; + while ((*value) && (isalnum((int)*value))) { + APPEND(argName, *value); + value++; + } + if (argNameSpace) { + argName[argNameLen] = '\0'; + } + while ((*value) && isspace((int)*value)) { + value++; + } + if (*value != '=') { + /* Malformed line */ + return; + } + value++; + while ((*value) && isspace((int)*value)) { + value++; + } + /* Find the parameter in the argument list, if present */ + argNum = 0; + argValue = 0; + while (argNames[argNum]) { + if (cgiStrEqNc(argName, argNames[argNum])) { + argValue = argValues[argNum]; + break; + } + argNum++; + } + /* Finally, read the parameter value */ + if (*value == '\"') { + value++; + while ((*value) && (*value != '\"')) { + if (argValue) { + APPEND(argValue, *value); + } + value++; + } + while ((*value) && (*value != ';')) { + value++; + } + } else { + /* Unquoted value */ + while ((*value) && (*value != ';')) { + if (argNames[argNum]) { + APPEND(argValue, *value); + } + value++; + } + } + if (argValueSpace) { + argValue[argValueLen] = '\0'; + } + } +} + +static int readHeaderLine( + mpStreamPtr mpp, + char *attr, + int attrSpace, + char *value, + int valueSpace) +{ + int attrLen = 0; + int valueLen = 0; + int valueFound = 0; + while (1) { + char d[1]; + int got = mpRead(mpp, d, 1); + if (got != 1) { + return 0; + } + if (d[0] == '\r') { + got = mpRead(mpp, d, 1); + if (got == 1) { + if (d[0] == '\n') { + /* OK */ + } else { + mpPutBack(mpp, d, 1); + } + } + break; + } else if (d[0] == '\n') { + break; + } else if ((d[0] == ':') && attrLen) { + valueFound = 1; + while (mpRead(mpp, d, 1) == 1) { + if (!isspace((int)d[0])) { + mpPutBack(mpp, d, 1); + break; + } + } + } else if (!valueFound) { + if (!isspace((int)*d)) { + if (attrLen < (attrSpace - 1)) { + attr[attrLen++] = *d; + } + } + } else if (valueFound) { + if (valueLen < (valueSpace - 1)) { + value[valueLen++] = *d; + } + } + } + if (attrSpace) { + attr[attrLen] = '\0'; + } + if (valueSpace) { + value[valueLen] = '\0'; + } + if (attrLen && valueLen) { + return 1; + } else { + return 0; + } +} + +static cgiParseResultType cgiParseGetFormInput() { + return cgiParseFormInput(cgiQueryString, cgiContentLength); +} + +typedef enum { + cgiEscapeRest, + cgiEscapeFirst, + cgiEscapeSecond +} cgiEscapeState; + +typedef enum { + cgiUnescapeSuccess, + cgiUnescapeMemory +} cgiUnescapeResultType; + +static cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len); + +static cgiParseResultType cgiParseFormInput(char *data, int length) { + /* Scan for pairs, unescaping and storing them as they are found. */ + int pos = 0; + cgiFormEntry *n; + cgiFormEntry *l = 0; + while (pos != length) { + int foundEq = 0; + int foundAmp = 0; + int start = pos; + int len = 0; + char *attr; + char *value; + while (pos != length) { + if (data[pos] == '=') { + foundEq = 1; + pos++; + break; + } + pos++; + len++; + } + if (!foundEq) { + break; + } + if (cgiUnescapeChars(&attr, data+start, len) + != cgiUnescapeSuccess) { + return cgiParseMemory; + } + start = pos; + len = 0; + while (pos != length) { + if (data[pos] == '&') { + foundAmp = 1; + pos++; + break; + } + pos++; + len++; + } + /* The last pair probably won't be followed by a &, but + that's fine, so check for that after accepting it */ + if (cgiUnescapeChars(&value, data+start, len) + != cgiUnescapeSuccess) { + free(attr); + return cgiParseMemory; + } + /* OK, we have a new pair, add it to the list. */ + n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry)); + if (!n) { + free(attr); + free(value); + return cgiParseMemory; + } + n->attr = attr; + n->value = value; + n->valueLength = strlen(n->value); + n->fileName = (char *) malloc(1); + if (!n->fileName) { + free(attr); + free(value); + free(n); + return cgiParseMemory; + } + n->fileName[0] = '\0'; + n->contentType = (char *) malloc(1); + if (!n->contentType) { + free(attr); + free(value); + free(n->fileName); + free(n); + return cgiParseMemory; + } + n->contentType[0] = '\0'; + n->tfileName = (char *) malloc(1); + if (!n->tfileName) { + free(attr); + free(value); + free(n->fileName); + free(n->contentType); + free(n); + return cgiParseMemory; + } + n->tfileName[0] = '\0'; + n->next = 0; + if (!l) { + cgiFormEntryFirst = n; + } else { + l->next = n; + } + l = n; + if (!foundAmp) { + break; + } + } + return cgiParseSuccess; +} + +static int cgiHexValue[256]; + +cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len) { + char *s; + cgiEscapeState escapeState = cgiEscapeRest; + int escapedValue = 0; + int srcPos = 0; + int dstPos = 0; + s = (char *) malloc(len + 1); + if (!s) { + return cgiUnescapeMemory; + } + while (srcPos < len) { + int ch = cp[srcPos]; + switch (escapeState) { + case cgiEscapeRest: + if (ch == '%') { + escapeState = cgiEscapeFirst; + } else if (ch == '+') { + s[dstPos++] = ' '; + } else { + s[dstPos++] = ch; + } + break; + case cgiEscapeFirst: + escapedValue = cgiHexValue[ch] << 4; + escapeState = cgiEscapeSecond; + break; + case cgiEscapeSecond: + escapedValue += cgiHexValue[ch]; + s[dstPos++] = escapedValue; + escapeState = cgiEscapeRest; + break; + } + srcPos++; + } + s[dstPos] = '\0'; + *sp = s; + return cgiUnescapeSuccess; +} + +static void cgiSetupConstants() { + int i; + for (i=0; (i < 256); i++) { + cgiHexValue[i] = 0; + } + cgiHexValue['0'] = 0; + cgiHexValue['1'] = 1; + cgiHexValue['2'] = 2; + cgiHexValue['3'] = 3; + cgiHexValue['4'] = 4; + cgiHexValue['5'] = 5; + cgiHexValue['6'] = 6; + cgiHexValue['7'] = 7; + cgiHexValue['8'] = 8; + cgiHexValue['9'] = 9; + cgiHexValue['A'] = 10; + cgiHexValue['B'] = 11; + cgiHexValue['C'] = 12; + cgiHexValue['D'] = 13; + cgiHexValue['E'] = 14; + cgiHexValue['F'] = 15; + cgiHexValue['a'] = 10; + cgiHexValue['b'] = 11; + cgiHexValue['c'] = 12; + cgiHexValue['d'] = 13; + cgiHexValue['e'] = 14; + cgiHexValue['f'] = 15; +} + +static void cgiFreeResources() { + cgiFormEntry *c = cgiFormEntryFirst; + cgiFormEntry *n; + while (c) { + n = c->next; + free(c->attr); + free(c->value); + free(c->fileName); + free(c->contentType); + if (strlen(c->tfileName)) { + unlink(c->tfileName); + } + free(c->tfileName); + free(c); + c = n; + } + /* If the cgi environment was restored from a saved environment, + then these are in allocated space and must also be freed */ + if (cgiRestored) { + free(cgiServerSoftware); + free(cgiServerName); + free(cgiGatewayInterface); + free(cgiServerProtocol); + free(cgiServerPort); + free(cgiRequestMethod); + free(cgiPathInfo); + free(cgiPathTranslated); + free(cgiScriptName); + free(cgiQueryString); + free(cgiRemoteHost); + free(cgiRemoteAddr); + free(cgiAuthType); + free(cgiRemoteUser); + free(cgiRemoteIdent); + free(cgiContentType); + free(cgiAccept); + free(cgiUserAgent); + free(cgiReferrer); + } + /* 2.0: to clean up the environment for cgiReadEnvironment, + we must set these correctly */ + cgiFormEntryFirst = 0; + cgiRestored = 0; +} + +static cgiFormResultType cgiFormEntryString( + cgiFormEntry *e, char *result, int max, int newlines); + +static cgiFormEntry *cgiFormEntryFindFirst(char *name); +static cgiFormEntry *cgiFormEntryFindNext(); + +cgiFormResultType cgiFormString( + char *name, char *result, int max) { + cgiFormEntry *e; + e = cgiFormEntryFindFirst(name); + if (!e) { + strcpy(result, ""); + return cgiFormNotFound; + } + return cgiFormEntryString(e, result, max, 1); +} + +cgiFormResultType cgiFormFileName( + char *name, char *result, int resultSpace) +{ + cgiFormEntry *e; + int resultLen = 0; + char *s; + e = cgiFormEntryFindFirst(name); + if (!e) { + strcpy(result, ""); + return cgiFormNotFound; + } + s = e->fileName; + while (*s) { + APPEND(result, *s); + s++; + } + if (resultSpace) { + result[resultLen] = '\0'; + } + if (!strlen(e->fileName)) { + return cgiFormNoFileName; + } else if (((int) strlen(e->fileName)) > (resultSpace - 1)) { + return cgiFormTruncated; + } else { + return cgiFormSuccess; + } +} + +cgiFormResultType cgiFormFileContentType( + char *name, char *result, int resultSpace) +{ + cgiFormEntry *e; + int resultLen = 0; + char *s; + e = cgiFormEntryFindFirst(name); + if (!e) { + if (resultSpace) { + result[0] = '\0'; + } + return cgiFormNotFound; + } + s = e->contentType; + while (*s) { + APPEND(result, *s); + s++; + } + if (resultSpace) { + result[resultLen] = '\0'; + } + if (!strlen(e->contentType)) { + return cgiFormNoContentType; + } else if (((int) strlen(e->contentType)) > (resultSpace - 1)) { + return cgiFormTruncated; + } else { + return cgiFormSuccess; + } +} + +cgiFormResultType cgiFormFileSize( + char *name, int *sizeP) +{ + cgiFormEntry *e; + e = cgiFormEntryFindFirst(name); + if (!e) { + if (sizeP) { + *sizeP = 0; + } + return cgiFormNotFound; + } else if (!strlen(e->tfileName)) { + if (sizeP) { + *sizeP = 0; + } + return cgiFormNotAFile; + } else { + if (sizeP) { + *sizeP = e->valueLength; + } + return cgiFormSuccess; + } +} + +typedef struct cgiFileStruct { + FILE *in; +} cgiFile; + +cgiFormResultType cgiFormFileOpen( + char *name, cgiFilePtr *cfpp) +{ + cgiFormEntry *e; + cgiFilePtr cfp; + e = cgiFormEntryFindFirst(name); + if (!e) { + *cfpp = 0; + return cgiFormNotFound; + } + if (!strlen(e->tfileName)) { + *cfpp = 0; + return cgiFormNotAFile; + } + cfp = (cgiFilePtr) malloc(sizeof(cgiFile)); + if (!cfp) { + *cfpp = 0; + return cgiFormMemory; + } + cfp->in = fopen(e->tfileName, "rb"); + if (!cfp->in) { + free(cfp); + return cgiFormIO; + } + *cfpp = cfp; + return cgiFormSuccess; +} + +cgiFormResultType cgiFormFileRead( + cgiFilePtr cfp, char *buffer, + int bufferSize, int *gotP) +{ + int got = 0; + if (!cfp) { + return cgiFormOpenFailed; + } + got = fread(buffer, 1, bufferSize, cfp->in); + if (got <= 0) { + return cgiFormEOF; + } + *gotP = got; + return cgiFormSuccess; +} + +cgiFormResultType cgiFormFileClose(cgiFilePtr cfp) +{ + if (!cfp) { + return cgiFormOpenFailed; + } + fclose(cfp->in); + free(cfp); + return cgiFormSuccess; +} + +cgiFormResultType cgiFormStringNoNewlines( + char *name, char *result, int max) { + cgiFormEntry *e; + e = cgiFormEntryFindFirst(name); + if (!e) { + strcpy(result, ""); + return cgiFormNotFound; + } + return cgiFormEntryString(e, result, max, 0); +} + +cgiFormResultType cgiFormStringMultiple( + char *name, char ***result) { + char **stringArray; + cgiFormEntry *e; + int i; + int total = 0; + /* Make two passes. One would be more efficient, but this + function is not commonly used. The select menu and + radio box functions are faster. */ + e = cgiFormEntryFindFirst(name); + if (e != 0) { + do { + total++; + } while ((e = cgiFormEntryFindNext()) != 0); + } + stringArray = (char **) malloc(sizeof(char *) * (total + 1)); + if (!stringArray) { + *result = 0; + return cgiFormMemory; + } + /* initialize all entries to null; the last will stay that way */ + for (i=0; (i <= total); i++) { + stringArray[i] = 0; + } + /* Now go get the entries */ + e = cgiFormEntryFindFirst(name); +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "StringMultiple Beginning\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + if (e) { + i = 0; + do { + int max = (int) (strlen(e->value) + 1); + stringArray[i] = (char *) malloc(max); + if (stringArray[i] == 0) { + /* Memory problems */ + cgiStringArrayFree(stringArray); + *result = 0; + return cgiFormMemory; + } + strcpy(stringArray[i], e->value); + cgiFormEntryString(e, stringArray[i], max, 1); + i++; + } while ((e = cgiFormEntryFindNext()) != 0); + *result = stringArray; +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "StringMultiple Succeeding\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + return cgiFormSuccess; + } else { + *result = stringArray; +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "StringMultiple found nothing\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + return cgiFormNotFound; + } +} + +cgiFormResultType cgiFormStringSpaceNeeded( + char *name, int *result) { + cgiFormEntry *e; + e = cgiFormEntryFindFirst(name); + if (!e) { + *result = 1; + return cgiFormNotFound; + } + *result = ((int) strlen(e->value)) + 1; + return cgiFormSuccess; +} + +static cgiFormResultType cgiFormEntryString( + cgiFormEntry *e, char *result, int max, int newlines) { + char *dp, *sp; + int truncated = 0; + int len = 0; + int avail = max-1; + int crCount = 0; + int lfCount = 0; + dp = result; + sp = e->value; + while (1) { + int ch; + /* 1.07: don't check for available space now. + We check for it immediately before adding + an actual character. 1.06 handled the + trailing null of the source string improperly, + resulting in a cgiFormTruncated error. */ + ch = *sp; + /* Fix the CR/LF, LF, CR nightmare: watch for + consecutive bursts of CRs and LFs in whatever + pattern, then actually output the larger number + of LFs. Consistently sane, yet it still allows + consecutive blank lines when the user + actually intends them. */ + if ((ch == 13) || (ch == 10)) { + if (ch == 13) { + crCount++; + } else { + lfCount++; + } + } else { + if (crCount || lfCount) { + int lfsAdd = crCount; + if (lfCount > crCount) { + lfsAdd = lfCount; + } + /* Stomp all newlines if desired */ + if (!newlines) { + lfsAdd = 0; + } + while (lfsAdd) { + if (len >= avail) { + truncated = 1; + break; + } + *dp = 10; + dp++; + lfsAdd--; + len++; + } + crCount = 0; + lfCount = 0; + } + if (ch == '\0') { + /* The end of the source string */ + break; + } + /* 1.06: check available space before adding + the character, because a previously added + LF may have brought us to the limit */ + if (len >= avail) { + truncated = 1; + break; + } + *dp = ch; + dp++; + len++; + } + sp++; + } + *dp = '\0'; + if (truncated) { + return cgiFormTruncated; + } else if (!len) { + return cgiFormEmpty; + } else { + return cgiFormSuccess; + } +} + +static int cgiFirstNonspaceChar(char *s); + +cgiFormResultType cgiFormInteger( + char *name, int *result, int defaultV) { + cgiFormEntry *e; + int ch; + e = cgiFormEntryFindFirst(name); + if (!e) { + *result = defaultV; + return cgiFormNotFound; + } + if (!strlen(e->value)) { + *result = defaultV; + return cgiFormEmpty; + } + ch = cgiFirstNonspaceChar(e->value); + if (!(isdigit(ch)) && (ch != '-') && (ch != '+')) { + *result = defaultV; + return cgiFormBadType; + } else { + *result = atoi(e->value); + return cgiFormSuccess; + } +} + +cgiFormResultType cgiFormIntegerBounded( + char *name, int *result, int min, int max, int defaultV) { + cgiFormResultType error = cgiFormInteger(name, result, defaultV); + if (error != cgiFormSuccess) { + return error; + } + if (*result < min) { + *result = min; + return cgiFormConstrained; + } + if (*result > max) { + *result = max; + return cgiFormConstrained; + } + return cgiFormSuccess; +} + +cgiFormResultType cgiFormDouble( + char *name, double *result, double defaultV) { + cgiFormEntry *e; + int ch; + e = cgiFormEntryFindFirst(name); + if (!e) { + *result = defaultV; + return cgiFormNotFound; + } + if (!strlen(e->value)) { + *result = defaultV; + return cgiFormEmpty; + } + ch = cgiFirstNonspaceChar(e->value); + if (!(isdigit(ch)) && (ch != '.') && (ch != '-') && (ch != '+')) { + *result = defaultV; + return cgiFormBadType; + } else { + *result = atof(e->value); + return cgiFormSuccess; + } +} + +cgiFormResultType cgiFormDoubleBounded( + char *name, double *result, double min, double max, double defaultV) { + cgiFormResultType error = cgiFormDouble(name, result, defaultV); + if (error != cgiFormSuccess) { + return error; + } + if (*result < min) { + *result = min; + return cgiFormConstrained; + } + if (*result > max) { + *result = max; + return cgiFormConstrained; + } + return cgiFormSuccess; +} + +cgiFormResultType cgiFormSelectSingle( + char *name, char **choicesText, int choicesTotal, + int *result, int defaultV) +{ + cgiFormEntry *e; + int i; + e = cgiFormEntryFindFirst(name); +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "%d\n", (int) e); + CGICDEBUGEND +#endif /* CGICDEBUG */ + if (!e) { + *result = defaultV; + return cgiFormNotFound; + } + for (i=0; (i < choicesTotal); i++) { +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "%s %s\n", choicesText[i], e->value); + CGICDEBUGEND +#endif /* CGICDEBUG */ + if (cgiStrEq(choicesText[i], e->value)) { +#ifdef CGICDEBUG + CGICDEBUGSTART + fprintf(dout, "MATCH\n"); + CGICDEBUGEND +#endif /* CGICDEBUG */ + *result = i; + return cgiFormSuccess; + } + } + *result = defaultV; + return cgiFormNoSuchChoice; +} + +cgiFormResultType cgiFormSelectMultiple( + char *name, char **choicesText, int choicesTotal, + int *result, int *invalid) +{ + cgiFormEntry *e; + int i; + int hits = 0; + int invalidE = 0; + for (i=0; (i < choicesTotal); i++) { + result[i] = 0; + } + e = cgiFormEntryFindFirst(name); + if (!e) { + *invalid = invalidE; + return cgiFormNotFound; + } + do { + int hit = 0; + for (i=0; (i < choicesTotal); i++) { + if (cgiStrEq(choicesText[i], e->value)) { + result[i] = 1; + hits++; + hit = 1; + break; + } + } + if (!(hit)) { + invalidE++; + } + } while ((e = cgiFormEntryFindNext()) != 0); + + *invalid = invalidE; + + if (hits) { + return cgiFormSuccess; + } else { + return cgiFormNotFound; + } +} + +cgiFormResultType cgiFormCheckboxSingle( + char *name) +{ + cgiFormEntry *e; + e = cgiFormEntryFindFirst(name); + if (!e) { + return cgiFormNotFound; + } + return cgiFormSuccess; +} + +extern cgiFormResultType cgiFormCheckboxMultiple( + char *name, char **valuesText, int valuesTotal, + int *result, int *invalid) +{ + /* Implementation is identical to cgiFormSelectMultiple. */ + return cgiFormSelectMultiple(name, valuesText, + valuesTotal, result, invalid); +} + +cgiFormResultType cgiFormRadio( + char *name, + char **valuesText, int valuesTotal, int *result, int defaultV) +{ + /* Implementation is identical to cgiFormSelectSingle. */ + return cgiFormSelectSingle(name, valuesText, valuesTotal, + result, defaultV); +} + +cgiFormResultType cgiCookieString( + char *name, + char *value, + int space) +{ + char *p = cgiCookie; + while (*p) { + char *n = name; + /* 2.02: if cgiCookie is exactly equal to name, this + can cause an overrun. The server probably wouldn't + allow it, since a name without values makes no sense + -- but then again it might not check, so this is a + genuine security concern. Thanks to Nicolas + Tomadakis. */ + while (*p == *n) { + if ((p == '\0') && (n == '\0')) { + /* Malformed cookie header from client */ + return cgiFormNotFound; + } + p++; + n++; + } + if ((!*n) && (*p == '=')) { + p++; + while ((*p != ';') && (*p != '\0') && + (space > 1)) + { + *value = *p; + value++; + p++; + space--; + } + if (space > 0) { + *value = '\0'; + } + /* Correct parens: 2.02. Thanks to + Mathieu Villeneuve-Belair. */ + if (!(((*p) == ';') || ((*p) == '\0'))) + { + return cgiFormTruncated; + } else { + return cgiFormSuccess; + } + } else { + /* Skip to next cookie */ + while (*p) { + if (*p == ';') { + break; + } + p++; + } + if (!*p) { + /* 2.01: default to empty */ + if (space) { + *value = '\0'; + } + return cgiFormNotFound; + } + p++; + /* Allow whitespace after semicolon */ + while ((*p) && isspace((int)*p)) { + p++; + } + } + } + /* 2.01: actually the above loop never terminates except + with a return, but do this to placate gcc */ + if (space) { + *value = '\0'; + } + return cgiFormNotFound; +} + +cgiFormResultType cgiCookieInteger( + char *name, + int *result, + int defaultV) +{ + char buffer[256]; + cgiFormResultType r = + cgiCookieString(name, buffer, sizeof(buffer)); + if (r != cgiFormSuccess) { + *result = defaultV; + } else { + *result = atoi(buffer); + } + return r; +} + +void cgiHeaderCookieSetInteger(char *name, int value, int secondsToLive, + char *path, char *domain) +{ + char svalue[256]; + sprintf(svalue, "%d", value); + cgiHeaderCookieSetString(name, svalue, secondsToLive, path, domain); +} + +char *days[] = { + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat" +}; + +char *months[] = { + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" +}; + +void cgiHeaderCookieSetString(char *name, char *value, int secondsToLive, + char *path, char *domain) +{ + /* cgic 2.02: simpler and more widely compatible implementation. + Thanks to Chunfu Lai. + cgic 2.03: yes, but it didn't work. Reimplemented by + Thomas Boutell. ; after last element was a bug. + Examples of real world cookies that really work: + Set-Cookie: MSNADS=UM=; domain=.slate.com; + expires=Tue, 26-Apr-2022 19:00:00 GMT; path=/ + Set-Cookie: MC1=V=3&ID=b5bc08af2b8a43ff85fcb5efd8b238f0; + domain=.slate.com; expires=Mon, 04-Oct-2021 19:00:00 GMT; path=/ + */ + time_t now; + time_t then; + struct tm *gt; + time(&now); + then = now + secondsToLive; + gt = gmtime(&then); + fprintf(cgiOut, + "Set-Cookie: %s=%s; domain=%s; expires=%s, %02d-%s-%04d %02d:%02d:%02d GMT; path=%s\r\n", + name, value, domain, + days[gt->tm_wday], + gt->tm_mday, + months[gt->tm_mon], + gt->tm_year + 1900, + gt->tm_hour, + gt->tm_min, + gt->tm_sec, + path); +} + +void cgiHeaderLocation(char *redirectUrl) { + fprintf(cgiOut, "Location: %s\r\n\r\n", redirectUrl); +} + +void cgiHeaderStatus(int status, char *statusMessage) { + fprintf(cgiOut, "Status: %d %s\r\n\r\n", status, statusMessage); +} + +void cgiHeaderContentType(char *mimeType) { + fprintf(cgiOut, "Content-type: %s\r\n\r\n", mimeType); +} + +static int cgiWriteString(FILE *out, char *s); + +static int cgiWriteInt(FILE *out, int i); + +#define CGIC_VERSION "2.0" + +cgiEnvironmentResultType cgiWriteEnvironment(char *filename) { + FILE *out; + cgiFormEntry *e; + /* Be sure to open in binary mode */ + out = fopen(filename, "wb"); + if (!out) { + /* Can't create file */ + return cgiEnvironmentIO; + } + if (!cgiWriteString(out, "CGIC2.0")) { + goto error; + } + if (!cgiWriteString(out, cgiServerSoftware)) { + goto error; + } + if (!cgiWriteString(out, cgiServerName)) { + goto error; + } + if (!cgiWriteString(out, cgiGatewayInterface)) { + goto error; + } + if (!cgiWriteString(out, cgiServerProtocol)) { + goto error; + } + if (!cgiWriteString(out, cgiServerPort)) { + goto error; + } + if (!cgiWriteString(out, cgiRequestMethod)) { + goto error; + } + if (!cgiWriteString(out, cgiPathInfo)) { + goto error; + } + if (!cgiWriteString(out, cgiPathTranslated)) { + goto error; + } + if (!cgiWriteString(out, cgiScriptName)) { + goto error; + } + if (!cgiWriteString(out, cgiQueryString)) { + goto error; + } + if (!cgiWriteString(out, cgiRemoteHost)) { + goto error; + } + if (!cgiWriteString(out, cgiRemoteAddr)) { + goto error; + } + if (!cgiWriteString(out, cgiAuthType)) { + goto error; + } + if (!cgiWriteString(out, cgiRemoteUser)) { + goto error; + } + if (!cgiWriteString(out, cgiRemoteIdent)) { + goto error; + } + if (!cgiWriteString(out, cgiContentType)) { + goto error; + } + if (!cgiWriteString(out, cgiAccept)) { + goto error; + } + if (!cgiWriteString(out, cgiUserAgent)) { + goto error; + } + if (!cgiWriteString(out, cgiReferrer)) { + goto error; + } + if (!cgiWriteString(out, cgiCookie)) { + goto error; + } + if (!cgiWriteInt(out, cgiContentLength)) { + goto error; + } + e = cgiFormEntryFirst; + while (e) { + cgiFilePtr fp; + if (!cgiWriteString(out, e->attr)) { + goto error; + } + if (!cgiWriteString(out, e->value)) { + goto error; + } + /* New 2.0 fields and file uploads */ + if (!cgiWriteString(out, e->fileName)) { + goto error; + } + if (!cgiWriteString(out, e->contentType)) { + goto error; + } + if (!cgiWriteInt(out, e->valueLength)) { + goto error; + } + if (cgiFormFileOpen(e->attr, &fp) == cgiFormSuccess) { + char buffer[1024]; + int got; + if (!cgiWriteInt(out, 1)) { + cgiFormFileClose(fp); + goto error; + } + while (cgiFormFileRead(fp, buffer, + sizeof(buffer), &got) == cgiFormSuccess) + { + if (((int) fwrite(buffer, 1, got, out)) != got) { + cgiFormFileClose(fp); + goto error; + } + } + if (cgiFormFileClose(fp) != cgiFormSuccess) { + goto error; + } + } else { + if (!cgiWriteInt(out, 0)) { + goto error; + } + } + e = e->next; + } + fclose(out); + return cgiEnvironmentSuccess; +error: + fclose(out); + /* If this function is not defined in your system, + you must substitute the appropriate + file-deletion function. */ + unlink(filename); + return cgiEnvironmentIO; +} + +static int cgiWriteString(FILE *out, char *s) { + int len = (int) strlen(s); + cgiWriteInt(out, len); + if (((int) fwrite(s, 1, len, out)) != len) { + return 0; + } + return 1; +} + +static int cgiWriteInt(FILE *out, int i) { + if (!fwrite(&i, sizeof(int), 1, out)) { + return 0; + } + return 1; +} + +static int cgiReadString(FILE *out, char **s); + +static int cgiReadInt(FILE *out, int *i); + +cgiEnvironmentResultType cgiReadEnvironment(char *filename) { + FILE *in; + cgiFormEntry *e = 0, *p; + char *version; + /* Prevent compiler warnings */ + cgiEnvironmentResultType result = cgiEnvironmentIO; + /* Free any existing data first */ + cgiFreeResources(); + /* Be sure to open in binary mode */ + in = fopen(filename, "rb"); + if (!in) { + /* Can't access file */ + return cgiEnvironmentIO; + } + if (!cgiReadString(in, &version)) { + goto error; + } + if (strcmp(version, "CGIC" CGIC_VERSION)) { + /* 2.02: Merezko Oleg */ + free(version); + return cgiEnvironmentWrongVersion; + } + /* 2.02: Merezko Oleg */ + free(version); + if (!cgiReadString(in, &cgiServerSoftware)) { + goto error; + } + if (!cgiReadString(in, &cgiServerName)) { + goto error; + } + if (!cgiReadString(in, &cgiGatewayInterface)) { + goto error; + } + if (!cgiReadString(in, &cgiServerProtocol)) { + goto error; + } + if (!cgiReadString(in, &cgiServerPort)) { + goto error; + } + if (!cgiReadString(in, &cgiRequestMethod)) { + goto error; + } + if (!cgiReadString(in, &cgiPathInfo)) { + goto error; + } + if (!cgiReadString(in, &cgiPathTranslated)) { + goto error; + } + if (!cgiReadString(in, &cgiScriptName)) { + goto error; + } + if (!cgiReadString(in, &cgiQueryString)) { + goto error; + } + if (!cgiReadString(in, &cgiRemoteHost)) { + goto error; + } + if (!cgiReadString(in, &cgiRemoteAddr)) { + goto error; + } + if (!cgiReadString(in, &cgiAuthType)) { + goto error; + } + if (!cgiReadString(in, &cgiRemoteUser)) { + goto error; + } + if (!cgiReadString(in, &cgiRemoteIdent)) { + goto error; + } + if (!cgiReadString(in, &cgiContentType)) { + goto error; + } + if (!cgiReadString(in, &cgiAccept)) { + goto error; + } + if (!cgiReadString(in, &cgiUserAgent)) { + goto error; + } + if (!cgiReadString(in, &cgiReferrer)) { + goto error; + } + /* 2.0 */ + if (!cgiReadString(in, &cgiCookie)) { + goto error; + } + if (!cgiReadInt(in, &cgiContentLength)) { + goto error; + } + p = 0; + while (1) { + int fileFlag; + e = (cgiFormEntry *) calloc(1, sizeof(cgiFormEntry)); + if (!e) { + cgiFreeResources(); + fclose(in); + return cgiEnvironmentMemory; + } + memset(e, 0, sizeof(cgiFormEntry)); + if (!cgiReadString(in, &e->attr)) { + /* This means we've reached the end of the list. */ + /* 2.02: thanks to Merezko Oleg */ + free(e); + break; + } + if (!cgiReadString(in, &e->value)) { + goto outOfMemory; + } + if (!cgiReadString(in, &e->fileName)) { + goto outOfMemory; + } + if (!cgiReadString(in, &e->contentType)) { + goto outOfMemory; + } + if (!cgiReadInt(in, &e->valueLength)) { + goto outOfMemory; + } + if (!cgiReadInt(in, &fileFlag)) { + goto outOfMemory; + } + if (fileFlag) { + char buffer[1024]; + FILE *out; + char tfileName[1024]; + int got; + int len = e->valueLength; + if (getTempFileName(tfileName) + != cgiParseSuccess) + { + result = cgiEnvironmentIO; + goto error; + } + out = fopen(tfileName, "w+b"); + if (!out) { + result = cgiEnvironmentIO; + goto error; + } + while (len > 0) { + /* 2.01: try is a bad variable name in + C++, and it wasn't being used + properly either */ + int tryr = len; + if (tryr > ((int) sizeof(buffer))) { + tryr = sizeof(buffer); + } + got = fread(buffer, 1, tryr, in); + if (got <= 0) { + result = cgiEnvironmentIO; + fclose(out); + unlink(tfileName); + goto error; + } + if (((int) fwrite(buffer, 1, got, out)) != got) { + result = cgiEnvironmentIO; + fclose(out); + unlink(tfileName); + goto error; + } + len -= got; + } + /* cgic 2.05: should be fclose not rewind */ + fclose(out); + e->tfileName = (char *) malloc((int) strlen(tfileName) + 1); + if (!e->tfileName) { + result = cgiEnvironmentMemory; + unlink(tfileName); + goto error; + } + strcpy(e->tfileName, tfileName); + } else { + e->tfileName = (char *) malloc(1); + if (!e->tfileName) { + result = cgiEnvironmentMemory; + goto error; + } + } + e->next = 0; + if (p) { + p->next = e; + } else { + cgiFormEntryFirst = e; + } + p = e; + } + fclose(in); + cgiRestored = 1; + return cgiEnvironmentSuccess; +outOfMemory: + result = cgiEnvironmentMemory; +error: + cgiFreeResources(); + fclose(in); + if (e) { + if (e->attr) { + free(e->attr); + } + if (e->value) { + free(e->value); + } + if (e->fileName) { + free(e->fileName); + } + if (e->contentType) { + free(e->contentType); + } + if (e->tfileName) { + free(e->tfileName); + } + free(e); + } + return result; +} + +static int cgiReadString(FILE *in, char **s) { + int len; + /* 2.0 fix: test cgiReadInt for failure! */ + if (!cgiReadInt(in, &len)) { + return 0; + } + *s = (char *) malloc(len + 1); + if (!(*s)) { + return 0; + } + if (((int) fread(*s, 1, len, in)) != len) { + return 0; + } + (*s)[len] = '\0'; + return 1; +} + +static int cgiReadInt(FILE *out, int *i) { + if (!fread(i, sizeof(int), 1, out)) { + return 0; + } + return 1; +} + +static int cgiStrEqNc(char *s1, char *s2) { + while(1) { + if (!(*s1)) { + if (!(*s2)) { + return 1; + } else { + return 0; + } + } else if (!(*s2)) { + return 0; + } + if (isalpha((int)*s1)) { + if (tolower(*s1) != tolower(*s2)) { + return 0; + } + } else if ((*s1) != (*s2)) { + return 0; + } + s1++; + s2++; + } +} + +static int cgiStrBeginsNc(char *s1, char *s2) { + while(1) { + if (!(*s2)) { + return 1; + } else if (!(*s1)) { + return 0; + } + if (isalpha((int)*s1)) { + if (tolower(*s1) != tolower(*s2)) { + return 0; + } + } else if ((*s1) != (*s2)) { + return 0; + } + s1++; + s2++; + } +} + +static char *cgiFindTarget = 0; +static cgiFormEntry *cgiFindPos = 0; + +static cgiFormEntry *cgiFormEntryFindFirst(char *name) { + cgiFindTarget = name; + cgiFindPos = cgiFormEntryFirst; + return cgiFormEntryFindNext(); +} + +static cgiFormEntry *cgiFormEntryFindNext() { + while (cgiFindPos) { + cgiFormEntry *c = cgiFindPos; + cgiFindPos = c->next; + if (!strcmp(c -> attr, cgiFindTarget)) { + return c; + } + } + return 0; +} + +static int cgiFirstNonspaceChar(char *s) { + int len = strspn(s, " \n\r\t"); + return s[len]; +} + +void cgiStringArrayFree(char **stringArray) { + char *p; + char **arrayItself = stringArray; + p = *stringArray; + while (p) { + free(p); + stringArray++; + p = *stringArray; + } + /* 2.0: free the array itself! */ + free(arrayItself); +} + +cgiFormResultType cgiCookies(char ***result) { + char **stringArray; + int i; + int total = 0; + char *p; + char *n; + p = cgiCookie; + while (*p) { + if (*p == '=') { + total++; + } + p++; + } + stringArray = (char **) malloc(sizeof(char *) * (total + 1)); + if (!stringArray) { + *result = 0; + return cgiFormMemory; + } + /* initialize all entries to null; the last will stay that way */ + for (i=0; (i <= total); i++) { + stringArray[i] = 0; + } + i = 0; + p = cgiCookie; + while (*p) { + while (*p && isspace((int)*p)) { + p++; + } + n = p; + while (*p && (*p != '=')) { + p++; + } + if (p != n) { + stringArray[i] = (char *) malloc((p - n) + 1); + if (!stringArray[i]) { + cgiStringArrayFree(stringArray); + *result = 0; + return cgiFormMemory; + } + memcpy(stringArray[i], n, p - n); + stringArray[i][p - n] = '\0'; + i++; + } + while (*p && (*p != ';')) { + p++; + } + if (!*p) { + break; + } + if (*p == ';') { + p++; + } + } + *result = stringArray; + return cgiFormSuccess; +} + +cgiFormResultType cgiFormEntries(char ***result) { + char **stringArray; + cgiFormEntry *e, *pe; + int i; + int total = 0; + e = cgiFormEntryFirst; + while (e) { + /* Don't count a field name more than once if + multiple values happen to be present for it */ + pe = cgiFormEntryFirst; + while (pe != e) { + if (!strcmp(e->attr, pe->attr)) { + goto skipSecondValue; + } + pe = pe->next; + } + total++; +skipSecondValue: + e = e->next; + } + stringArray = (char **) malloc(sizeof(char *) * (total + 1)); + if (!stringArray) { + *result = 0; + return cgiFormMemory; + } + /* initialize all entries to null; the last will stay that way */ + for (i=0; (i <= total); i++) { + stringArray[i] = 0; + } + /* Now go get the entries */ + e = cgiFormEntryFirst; + i = 0; + while (e) { + int space; + /* Don't return a field name more than once if + multiple values happen to be present for it */ + pe = cgiFormEntryFirst; + while (pe != e) { + if (!strcmp(e->attr, pe->attr)) { + goto skipSecondValue2; + } + pe = pe->next; + } + space = (int) strlen(e->attr) + 1; + stringArray[i] = (char *) malloc(space); + if (stringArray[i] == 0) { + /* Memory problems */ + cgiStringArrayFree(stringArray); + *result = 0; + return cgiFormMemory; + } + strcpy(stringArray[i], e->attr); + i++; +skipSecondValue2: + e = e->next; + } + *result = stringArray; + return cgiFormSuccess; +} + +#define TRYPUTC(ch) \ + { \ + if (putc((ch), cgiOut) == EOF) { \ + return cgiFormIO; \ + } \ + } + +cgiFormResultType cgiHtmlEscapeData(char *data, int len) +{ + while (len--) { + if (*data == '<') { + TRYPUTC('&'); + TRYPUTC('l'); + TRYPUTC('t'); + TRYPUTC(';'); + } else if (*data == '&') { + TRYPUTC('&'); + TRYPUTC('a'); + TRYPUTC('m'); + TRYPUTC('p'); + TRYPUTC(';'); + } else if (*data == '>') { + TRYPUTC('&'); + TRYPUTC('g'); + TRYPUTC('t'); + TRYPUTC(';'); + } else { + TRYPUTC(*data); + } + data++; + } + return cgiFormSuccess; +} + +cgiFormResultType cgiHtmlEscape(char *s) +{ + return cgiHtmlEscapeData(s, (int) strlen(s)); +} + +/* Output data with the " character HTML-escaped, and no + other characters escaped. This is useful when outputting + the contents of a tag attribute such as 'href' or 'src'. + 'data' is not null-terminated; 'len' is the number of + bytes in 'data'. Returns cgiFormIO in the event + of error, cgiFormSuccess otherwise. */ +cgiFormResultType cgiValueEscapeData(char *data, int len) +{ + while (len--) { + if (*data == '\"') { + TRYPUTC('&'); + TRYPUTC('#'); + TRYPUTC('3'); + TRYPUTC('4'); + TRYPUTC(';'); + } else { + TRYPUTC(*data); + } + data++; + } + return cgiFormSuccess; +} + +cgiFormResultType cgiValueEscape(char *s) +{ + return cgiValueEscapeData(s, (int) strlen(s)); +} + + diff --git a/cgic.diff b/cgic.diff new file mode 100644 index 0000000..08ec75d --- /dev/null +++ b/cgic.diff @@ -0,0 +1,102 @@ +*** cgic.c 2004-11-15 08:57:59.000000000 -0800 +--- ../wfr33/cgic.c 2011-11-24 14:14:52.000000000 -0800 +*************** +*** 29,34 **** +--- 29,36 ---- + #include <time.h> + #include <sys/types.h> + #include <sys/stat.h> ++ #include <signal.h> ++ #include <sys/shm.h> + + #ifdef WIN32 + #include <io.h> +*************** char *cgiAccept; +*** 67,72 **** +--- 69,79 ---- + char *cgiUserAgent; + char *cgiReferrer; + ++ off_t *shm; ++ int shm_key; ++ int shmid; ++ ++ + FILE *cgiIn; + FILE *cgiOut; + +*************** static void cgiFreeResources(); +*** 119,124 **** +--- 126,137 ---- + static int cgiStrEqNc(char *s1, char *s2); + static int cgiStrBeginsNc(char *s1, char *s2); + ++ /* Dirty little hack to get file upload progress - Part 4: SHM Cleanup */ ++ void shmcleanup(void) { ++ if(shmid>0) ++ shmctl(shmid, IPC_RMID, NULL); ++ } ++ + int main(int argc, char *argv[]) { + int result; + char *cgiContentLengthString; +*************** static cgiParseResultType cgiParsePostMu +*** 435,440 **** +--- 448,454 ---- + cgiParseResultType result; + cgiFormEntry *n = 0, *l = 0; + int got; ++ int sig; + FILE *outf = 0; + char *out = 0; + char tfileName[1024]; +*************** static cgiParseResultType cgiParsePostMu +*** 521,531 **** +--- 535,559 ---- + return cgiParseIO; + } + outf = fopen(tfileName, "w+b"); ++ /* Dirty little hack to get file upload progress - Part 1: Initialize */ ++ if (cgiFormInteger("upload_id", &shm_key, 0) == cgiFormSuccess && shm_key) { ++ if ((shmid = shmget(shm_key, sizeof(int), IPC_CREAT | 0666)) > 0) { ++ for(sig=1; sig<=31; sig++) ++ signal(sig, (void*)shmcleanup); ++ if ((shm = shmat(shmid, NULL, 0)) > 0) ++ *shm=0; ++ } ++ } + } else { + outf = 0; + tfileName[0] = '\0'; + } + result = afterNextBoundary(mpp, outf, &out, &bodyLength, 0); ++ /* Dirty little hack to get file upload progress - Part 2: Clean up */ ++ if(shm>0) ++ shmdt(shm); ++ if(shmid>0) ++ shmctl(shmid, IPC_RMID, NULL); + if (result != cgiParseSuccess) { + /* Lack of a boundary here is an error. */ + if (outf) { +*************** cgiParseResultType afterNextBoundary(mpS +*** 689,694 **** +--- 717,723 ---- + cgiParseResultType result; + int boffset; + int got; ++ off_t tot=0; + char d[2]; + /* This is large enough, because the buffer into which the + original boundary string is fetched is shorter by more +*************** cgiParseResultType afterNextBoundary(mpS +*** 711,716 **** +--- 740,748 ---- + workingBoundaryLength = strlen(workingBoundary); + while (1) { + got = mpRead(mpp, d, 1); ++ /* Dirty little hack to get file upload progress - Part 3: Set value */ ++ if(outf && shm>0) ++ *shm=tot+=got; + if (got != 1) { + /* 2.01: cgiParseIO, not cgiFormIO */ + result = cgiParseIO; @@ -0,0 +1,232 @@ +/* The CGI_C library, by Thomas Boutell, version 2.01. CGI_C is intended + to be a high-quality API to simplify CGI programming tasks. */ + +/* Make sure this is only included once. */ + +#ifndef CGI_C +#define CGI_C 1 + +/* Bring in standard I/O since some of the functions refer to + types defined by it, such as FILE *. */ + +#include <stdio.h> + +/* The various CGI environment variables. Instead of using getenv(), + the programmer should refer to these, which are always + valid null-terminated strings (they may be empty, but they + will never be null). If these variables are used instead + of calling getenv(), then it will be possible to save + and restore CGI environments, which is highly convenient + for debugging. */ + +extern char *cgiServerSoftware; +extern char *cgiServerName; +extern char *cgiGatewayInterface; +extern char *cgiServerProtocol; +extern char *cgiServerPort; +extern char *cgiRequestMethod; +extern char *cgiPathInfo; +extern char *cgiPathTranslated; +extern char *cgiScriptName; +extern char *cgiQueryString; +extern char *cgiRemoteHost; +extern char *cgiRemoteAddr; +extern char *cgiAuthType; +extern char *cgiRemoteUser; +extern char *cgiRemoteIdent; +extern char *cgiContentType; +extern char *cgiAccept; +extern char *cgiUserAgent; +extern char *cgiReferrer; + +/* Cookies as sent to the server. You can also get them + individually, or as a string array; see the documentation. */ +extern char *cgiCookie; + +/* A macro providing the same incorrect spelling that is + found in the HTTP/CGI specifications */ +#define cgiReferer cgiReferrer + +/* The number of bytes of data received. + Note that if the submission is a form submission + the library will read and parse all the information + directly from cgiIn; the programmer need not do so. */ + +extern int cgiContentLength; + +/* Pointer to CGI output. The cgiHeader functions should be used + first to output the mime headers; the output HTML + page, GIF image or other web document should then be written + to cgiOut by the programmer. In the standard CGIC library, + cgiOut is always equivalent to stdout. */ + +extern FILE *cgiOut; + +/* Pointer to CGI input. The programmer does not read from this. + We have continued to export it for backwards compatibility + so that cgic 1.x applications link properly. */ + +extern FILE *cgiIn; + +/* Possible return codes from the cgiForm family of functions (see below). */ + +typedef enum { + cgiFormSuccess, + cgiFormTruncated, + cgiFormBadType, + cgiFormEmpty, + cgiFormNotFound, + cgiFormConstrained, + cgiFormNoSuchChoice, + cgiFormMemory, + cgiFormNoFileName, + cgiFormNoContentType, + cgiFormNotAFile, + cgiFormOpenFailed, + cgiFormIO, + cgiFormEOF +} cgiFormResultType; + +/* These functions are used to retrieve form data. See + cgic.html for documentation. */ + +extern cgiFormResultType cgiFormString( + char *name, char *result, int max); + +extern cgiFormResultType cgiFormStringNoNewlines( + char *name, char *result, int max); + + +extern cgiFormResultType cgiFormStringSpaceNeeded( + char *name, int *length); + + +extern cgiFormResultType cgiFormStringMultiple( + char *name, char ***ptrToStringArray); + +extern void cgiStringArrayFree(char **stringArray); + +extern cgiFormResultType cgiFormInteger( + char *name, int *result, int defaultV); + +extern cgiFormResultType cgiFormIntegerBounded( + char *name, int *result, int min, int max, int defaultV); + +extern cgiFormResultType cgiFormDouble( + char *name, double *result, double defaultV); + +extern cgiFormResultType cgiFormDoubleBounded( + char *name, double *result, double min, double max, double defaultV); + +extern cgiFormResultType cgiFormSelectSingle( + char *name, char **choicesText, int choicesTotal, + int *result, int defaultV); + + +extern cgiFormResultType cgiFormSelectMultiple( + char *name, char **choicesText, int choicesTotal, + int *result, int *invalid); + +/* Just an alias; users have asked for this */ +#define cgiFormSubmitClicked cgiFormCheckboxSingle + +extern cgiFormResultType cgiFormCheckboxSingle( + char *name); + +extern cgiFormResultType cgiFormCheckboxMultiple( + char *name, char **valuesText, int valuesTotal, + int *result, int *invalid); + +extern cgiFormResultType cgiFormRadio( + char *name, char **valuesText, int valuesTotal, + int *result, int defaultV); + +/* The paths returned by this function are the original names of files + as reported by the uploading web browser and shoult NOT be + blindly assumed to be "safe" names for server-side use! */ +extern cgiFormResultType cgiFormFileName( + char *name, char *result, int max); + +/* The content type of the uploaded file, as reported by the browser. + It should NOT be assumed that browsers will never falsify + such information. */ +extern cgiFormResultType cgiFormFileContentType( + char *name, char *result, int max); + +extern cgiFormResultType cgiFormFileSize( + char *name, int *sizeP); + +typedef struct cgiFileStruct *cgiFilePtr; + +extern cgiFormResultType cgiFormFileOpen( + char *name, cgiFilePtr *cfpp); + +extern cgiFormResultType cgiFormFileRead( + cgiFilePtr cfp, char *buffer, int bufferSize, int *gotP); + +extern cgiFormResultType cgiFormFileClose( + cgiFilePtr cfp); + +extern cgiFormResultType cgiCookieString( + char *name, char *result, int max); + +extern cgiFormResultType cgiCookieInteger( + char *name, int *result, int defaultV); + +cgiFormResultType cgiCookies( + char ***ptrToStringArray); + +/* path can be null or empty in which case a path of / (entire site) is set. + domain can be a single web site; if it is an entire domain, such as + 'boutell.com', it should begin with a dot: '.boutell.com' */ +extern void cgiHeaderCookieSetString(char *name, char *value, + int secondsToLive, char *path, char *domain); +extern void cgiHeaderCookieSetInteger(char *name, int value, + int secondsToLive, char *path, char *domain); +extern void cgiHeaderLocation(char *redirectUrl); +extern void cgiHeaderStatus(int status, char *statusMessage); +extern void cgiHeaderContentType(char *mimeType); + +typedef enum { + cgiEnvironmentIO, + cgiEnvironmentMemory, + cgiEnvironmentSuccess, + cgiEnvironmentWrongVersion +} cgiEnvironmentResultType; + +extern cgiEnvironmentResultType cgiWriteEnvironment(char *filename); +extern cgiEnvironmentResultType cgiReadEnvironment(char *filename); + +extern int cgiMain(); + +extern cgiFormResultType cgiFormEntries( + char ***ptrToStringArray); + +/* Output string with the <, &, and > characters HTML-escaped. + 's' is null-terminated. Returns cgiFormIO in the event + of error, cgiFormSuccess otherwise. */ +cgiFormResultType cgiHtmlEscape(char *s); + +/* Output data with the <, &, and > characters HTML-escaped. + 'data' is not null-terminated; 'len' is the number of + bytes in 'data'. Returns cgiFormIO in the event + of error, cgiFormSuccess otherwise. */ +cgiFormResultType cgiHtmlEscapeData(char *data, int len); + +/* Output string with the " character HTML-escaped, and no + other characters escaped. This is useful when outputting + the contents of a tag attribute such as 'href' or 'src'. + 's' is null-terminated. Returns cgiFormIO in the event + of error, cgiFormSuccess otherwise. */ +cgiFormResultType cgiValueEscape(char *s); + +/* Output data with the " character HTML-escaped, and no + other characters escaped. This is useful when outputting + the contents of a tag attribute such as 'href' or 'src'. + 'data' is not null-terminated; 'len' is the number of + bytes in 'data'. Returns cgiFormIO in the event + of error, cgiFormSuccess otherwise. */ +cgiFormResultType cgiValueEscapeData(char *data, int len); + +#endif /* CGI_C */ + diff --git a/dialogs.c b/dialogs.c new file mode 100644 index 0000000..abe61cb --- /dev/null +++ b/dialogs.c @@ -0,0 +1,435 @@ +// WFM HTML Dialog Routines + +#include "wfm.h" + +// +// Prompt for delete / move operation +// +void multiprompt_ui(char *m_action) { + int i; + int res; + char **responses; + struct stat fileinfo; + char M_action[64]; + + res=cgiFormStringMultiple("multiselect_filename", &responses); + + // pre-check for filenames so, that if there is an error, HTML is not yet out, allowing error dialog to be rendered + if(res == cgiFormNotFound) { + checkfilename(NULL); + } else { + for(i=0; responses[i]; i++) + checkfilename(responses[i]); + } + + cgiHeaderContentType("text/html"); + snprintf(M_action, sizeof(M_action), "%c%s Confirmation", toupper(m_action[0]), m_action+1); + html_title(M_action); + + fprintf(cgiOut, + "</HEAD>\n" + "<!-- Multi Prompt -->\n" + "<BODY BGCOLOR=\"#FFFFFF\">\n" + "<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 CLASS=\"twh\"><TR><TD VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n" + "<FORM NAME=\"wfm\" ACTION=\"%s\" METHOD=\"POST\" ENCTYPE=\"multipart/form-data\">\n\n", cgiScriptName); + + fprintf(cgiOut, + "<TABLE WIDTH=\"500\" BGCOLOR=\"#F0F0F0\" BORDER=0 CELLSPACING=0 CELLPADDING=1 CLASS=\"tbr\">\n" + " <TR><TD COLSPAN=2 BGCOLOR=\"#004080\"><FONT COLOR=\"#FFFFFF\"> %s</FONT></TD></TR>\n" + " <TR><TD WIDTH=30> </TD><TD>\n" + " <BR>\n" + " About to %s following items:<P><UL>\n", + M_action, m_action); + + + if(res == cgiFormNotFound) { + checkfilename(NULL); + if(stat(phys_filename, &fileinfo)==0) { + fprintf(cgiOut, "<INPUT TYPE=\"HIDDEN\" NAME=\"filename\" VALUE=\"%s\">\n", virt_filename); + fprintf(cgiOut, "<LI TYPE=\"square\"><B>%s/</B>", virt_filename); + if(S_ISDIR(fileinfo.st_mode)) + fprintf(cgiOut, " [directory %s]\n", buprintf(du(phys_filename), FALSE)); + else + fprintf(cgiOut, " [file %s]\n", buprintf(fileinfo.st_size, FALSE)); + } + } else { + for(i=0; responses[i]; i++) { + checkfilename(responses[i]); + if(stat(phys_filename, &fileinfo)==0) { + fprintf(cgiOut, "<INPUT TYPE=\"HIDDEN\" NAME=\"multiselect_filename\" VALUE=\"%s\">\n", virt_filename); + fprintf(cgiOut, "<LI TYPE=\"square\"><B>%s</B>", virt_filename); + if(S_ISDIR(fileinfo.st_mode)) + fprintf(cgiOut, "/ [directory %s]\n", buprintf(du(phys_filename), FALSE)); + else + fprintf(cgiOut, " [file %s]\n", buprintf(fileinfo.st_size, FALSE)); + } + } + } + + fprintf(cgiOut, "</UL>"); + + // move needs a destination... + if(strcmp(m_action, "move")==0) { + fprintf(cgiOut, "<P>Destination: <SELECT NAME=\"destination\"><OPTION VALUE=\"/\">/ - Root Directory</OPTION>\n"); + re_dir_ui("/", 1); + fprintf(cgiOut, "</SELECT>\n<INPUT TYPE=\"HIDDEN\" NAME=\"absdst\" VALUE=\"1\">\n<P>\n"); + } + + fprintf(cgiOut, + " </TD></TR>\n" + " <TR><TD COLSPAN=2>\n" + " <P><CENTER>\n" + " <INPUT TYPE=\"HIDDEN\" NAME=\"action\" VALUE=\"%s\">\n" + " <INPUT TYPE=\"HIDDEN\" NAME=\"directory\" VALUE=\"%s\">\n" + " <INPUT TYPE=\"HIDDEN\" NAME=\"token\" VALUE=\"%s\">\n" + " <INPUT TYPE=\"SUBMIT\" VALUE=\" OK \" NAME=\"OK\">\n" + " <INPUT TYPE=\"BUTTON\" VALUE=\" Cancel \" onClick=\"self.location='?directory=%s&token=%s'; return true;\">\n" + " </CENTER><BR>\n" + " </TD></TR>\n" + "</TABLE></FORM>\n\n" + "</TD></TR></TABLE>\n" + "</BODY>\n</HTML>\n", m_action, virt_dirname, token, virt_dirname, token); + + cgiStringArrayFree(responses); + +} + +// +// Single Prompt +// Used for rename, mkfile, mkdir +// +void singleprompt_ui(char *m_action) { + char M_action[64]; + + snprintf(M_action, sizeof(M_action), "%c%s", toupper(m_action[0]), m_action+1); + + if(strcmp(m_action, "move")==0) { + checkfilename(NULL); + snprintf(M_action, sizeof(M_action), "Rename"); + } + + cgiHeaderContentType("text/html"); + html_title(M_action); + + fprintf(cgiOut, + "</HEAD>\n" + "<!-- Single Prompt -->\n" + "<BODY ONLOAD=\"document.wfm.inp1.focus();\" BGCOLOR=\"#FFFFFF\">\n" + "<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 CLASS=\"twh\"><TR><TD VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n" + "<FORM NAME=\"wfm\" ACTION=\"%s\" METHOD=\"POST\" ENCTYPE=\"multipart/form-data\">\n\n" + "<TABLE WIDTH=\"400\" BGCOLOR=\"#F0F0F0\" BORDER=0 CELLSPACING=0 CELLPADDING=1 CLASS=\"tbr\">\n" + " <TR><TD COLSPAN=2 BGCOLOR=\"#004080\"><FONT COLOR=\"#FFFFFF\"> %s</FONT></TD></TR>\n" + " <TR><TD WIDTH=30> </TD><TD>\n" + " <BR>\n", cgiScriptName, M_action); + + + if(strcmp(m_action, "move")==0) + fprintf(cgiOut, + " Current Name: <B>%s</B><P>\n" + " Enter new name:<P>\n" + " <INPUT TYPE=\"TEXT\" ID=\"inp1\" NAME=\"destination\" SIZE=\"40\" VALUE=\"%s\">\n" + " <INPUT TYPE=\"HIDDEN\" NAME=\"filename\" VALUE=\"%s\">\n", + virt_filename, virt_filename, virt_filename); + + else if(strcmp(m_action, "mkfile")==0) + fprintf(cgiOut, + " Enter name of the new text file:<P>\n" + " <INPUT TYPE=\"TEXT\" ID=\"inp1\" NAME=\"filename\" SIZE=\"40\" VALUE=\"\">\n"); + + else if(strcmp(m_action, "mkdir")==0) + fprintf(cgiOut, + " <BR>Enter name of the new directory:<P>\n" + " <INPUT TYPE=\"TEXT\" ID=\"inp1\" NAME=\"filename\" SIZE=\"40\" VALUE=\"\">\n"); + + fprintf(cgiOut, + " </TD></TR>\n" + " <TR><TD COLSPAN=2>\n" + " <P><CENTER>\n" + " <INPUT TYPE=\"HIDDEN\" NAME=\"action\" VALUE=\"%s\">\n" + " <INPUT TYPE=\"HIDDEN\" NAME=\"directory\" VALUE=\"%s\">\n" + " <INPUT TYPE=\"HIDDEN\" NAME=\"token\" VALUE=\"%s\">\n" + " <INPUT TYPE=\"SUBMIT\" VALUE=\" OK \" NAME=\"OK\">\n" + " <INPUT TYPE=\"BUTTON\" VALUE=\" Cancel \" onClick=\"self.location='?directory=%s&token=%s'; return true;\">\n" + " </CENTER><BR>\n" + " </TD></TR>\n" + "</TABLE></FORM>\n\n" + "</TD></TR></TABLE>\n" + "</BODY>\n</HTML>\n", m_action, virt_dirname, token, virt_dirname, token); + +} + + +// +// Error message - note that strerror() is already passed by the caller +// +void error(char *msg, ...) { + va_list ap; + char buff[1024]={0}; + + if(msg) { + va_start(ap, msg); + vsnprintf(buff, sizeof(buff), msg, ap); + va_end(ap); + + cgiHeaderContentType("text/html"); + html_title("ERROR"); + fprintf(cgiOut, + "</HEAD>\n"\ + "<BODY BGCOLOR=\"#FFFFFF\">\n"\ + "<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 CLASS=\"twh\"><TR><TD VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n" + "<TABLE WIDTH=\"400\" BGCOLOR=\"#D4D0C8\" BORDER=0 CELLSPACING=0 CELLPADDING=1 CLASS=\"tbr\">\n" + "<TR BGCOLOR=\"#FF0000\">\n" + "<TD COLSPAN=3 ALIGN=\"LEFT\">\n" + "<SPAN STYLE=\"color:#FFFFFF; font-weight:bold;\"> ERROR:</SPAN>\n" + "</TD>\n" + "</TR>\n" + "<TR BGCOLOR=\"#EEEEEE\">\n" + "<TD WIDTH=\"50\" VALIGN=\"top\">\n" + " <BR>\n" + "</TD>\n" + "<TD ALIGN=\"LEFT\">\n" + " <BR>\n" + "%s<BR>\n" + " <P>\n" + " <P>\n" + "</TD>\n" + "<TD WIDTH=\"20\">\n" + " \n" + "</TD>\n" + "</TR>\n" + "<TR><TD COLSPAN=3 ALIGN=\"CENTER\" BGCOLOR=\"#EEEEEE\">\n" + "<INPUT TYPE=\"BUTTON\" VALUE=\" OK \" onClick=\"self.location='?directory=%s&token=%s'; return true;\">\n" + "</TD></TR>\n" + "<TR><TD COLSPAN=3 BGCOLOR=\"#EEEEEE\"> </TD></TR>\n" + "</TABLE>\n" + "</TD></TR></TABLE>\n</BODY></HTML>\n", + buff, virt_dirname, token); + } + else { + cgiHeaderContentType("text/plain"); + fprintf(cgiOut, "FATAL ERROR\n"); + } + + exit(0); +} + + +// +// About message +// +void about(void) { + cgiHeaderContentType("text/html"); + html_title("About"); + fprintf(cgiOut, + "</HEAD>\n" + "<BODY BGCOLOR=\"#FFFFFF\">\n" + "<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 CLASS=\"twh\"><TR><TD VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n" + "<TABLE WIDTH=\"500\" BGCOLOR=\"#D4D0C8\" BORDER=0 CELLSPACING=0 CELLPADDING=1 CLASS=\"tbr\">\n" + "<TR BGCOLOR=\"#0040A0\">\n" + "<TD COLSPAN=3 ALIGN=\"LEFT\">\n" + "<IMG SRC=\"%swfmicon.gif\" ALT=\"wfm Icon\"><SPAN STYLE=\"color:#FFFFFF\"> About:</SPAN>\n" + "</TD>\n" + "</TR>\n" + "<TR BGCOLOR=\"#EEEEEE\">\n" + "<TD WIDTH=\"50\" VALIGN=\"top\">\n" + " <BR>\n" + "</TD>\n" + "<TD ALIGN=\"LEFT\">\n" + " <BR>\n" + "<U>%s</U><BR>\n" + "WFM Implemented by Antoni Sawicki<BR>\n" + "CGIC Library by Thomas Boutell<BR>\n" + "Server Side RFC 1321 implementation by L. Peter Deutsch<BR>\n" + "Client Side RFC 1321 implementation by Paul Johnston<BR>\n" + "Icons by Yusuke Kamiyamane<BR>\n" + "Copyright © 1994-2016 by Antoni Sawicki<BR>\n" + "Copyright © 1996-2011 by Thomas Boutell and Boutell.Com, Inc.<BR>\n" + "Copyright © 2002 by Aladdin Enterprises<BR>\n" + "Copyright © 1999-2009 by Paul Johnston<BR>\n" + "Copyright © 2010 by Yusuke Kamiyamane<BR>\n" + "<HR>\n" + "WFM: %s (build %s / %s)<BR>\n" + "GCC: %s<BR>\n" + "Server: %s<BR>\n" + "<img src=\"http://www.w3.org/Icons/valid-html401\" alt=\"Valid HTML 4.01 Transitional\">\n" + " <P>\n" + " <P>\n" + "</TD>\n" + "<TD WIDTH=\"20\">\n" + " \n" + "</TD>\n" + "</TR>\n" + "<TR><TD COLSPAN=3 ALIGN=\"CENTER\" BGCOLOR=\"#EEEEEE\">\n" + "<INPUT TYPE=\"BUTTON\" VALUE=\" OK \" onClick=\"self.location='?directory=%s&token=%s'; return true;\">\n" + "</TD></TR>\n" + "<TR><TD COLSPAN=3 BGCOLOR=\"#EEEEEE\"> </TD></TR>\n" + "</TABLE>\n" + "</TD></TR></TABLE>\n</BODY></HTML>\n", + ICONSURL, TAGLINE, VERSION, __DATE__, __TIME__, __VERSION__, cgiServerSoftware, virt_dirname, token); + +} + + +// +// Prompt for username and password +// +void login_ui(void) { + cgiHeaderContentType("text/html"); + html_title("Login"); + fprintf(cgiOut, + "<SCRIPT LANGUAGE=\"JavaScript\" TYPE=\"text/javascript\">\n<!--\n" + "var hexcase=0;function hex_md5(a){return rstr2hex(rstr_md5(str2rstr_utf8(a)))}function hex_hmac_md5(a,b){return rstr2hex(rstr_hmac_md5(str2rstr_utf8(a),str2rstr_utf8(b)))}function md5_vm_test(){return hex_md5(\"abc\").toLowerCase()==\"900150983cd24fb0d6963f7d28e17f72\"}function rstr_md5(a){return binl2rstr(binl_md5(rstr2binl(a),a.length*8))}function rstr_hmac_md5(c,f){var e=rstr2binl(c);if(e.length>16){e=binl_md5(e,c.length*8)}var a=Array(16),d=Array(16);for(var b=0;b<16;b++){a[b]=e[b]^909522486;d[b]=e[b]^1549556828}var g=binl_md5(a.concat(rstr2binl(f)),512+f.length*8);return binl2rstr(binl_md5(d.concat(g),512+128))}function rstr2hex(c){try{hexcase}catch(g){hexcase=0}var f=hexcase?\"0123456789ABCDEF\":\"0123456789abcdef\";var b=\"\";var a;for(var d=0;d<c.length;d++){a=c.charCodeAt(d);b+=f.charAt((a>>>4)&15)+f.charAt(a&15)}return b}function str2rstr_utf8(c){var b=\"\";var d=-1;var a,e;while(++d<c.length){a=c.charCodeAt(d);e=d+1<c.length?c.charCodeAt(d+1):0;if(55296<=a&&a<=56319&&56320<=e&&e<=57343){a=65536+((a&1023)<<10)+(e&1023);d++}if(a<=127){b+=String.fromCharCode(a)}else{if(a<=2047){b+=String.fromCharCode(192|((a>>>6)&31),128|(a&63))}else{if(a<=65535){b+=String.fromCharCode(224|((a>>>12)&15),128|((a>>>6)&63),128|(a&63))}else{if(a<=2097151){b+=String.fromCharCode(240|((a>>>18)&7),128|((a>>>12)&63),128|((a>>>6)&63),128|(a&63))}}}}}return b}function rstr2binl(b){var a=Array(b.length>>2);for(var c=0;c<a.length;c++){a[c]=0}for(var c=0;c<b.length*8;c+=8){a[c>>5]|=(b.charCodeAt(c/8)&255)<<(c%%32)}return a}function binl2rstr(b){var a=\"\";for(var c=0;c<b.length*32;c+=8){a+=String.fromCharCode((b[c>>5]>>>(c%%32))&255)}return a}function binl_md5(p,k){p[k>>5]|=128<<((k)%%32);p[(((k+64)>>>9)<<4)+14]=k;var o=1732584193;var n=-271733879;var m=-1732584194;var l=271733878;for(var g=0;g<p.length;g+=16){var j=o;var h=n;var f=m;var e=l;o=md5_ff(o,n,m,l,p[g+0],7,-680876936);l=md5_ff(l,o,n,m,p[g+1],12,-389564586);m=md5_ff(m,l,o,n,p[g+2],17,606105819);n=md5_ff(n,m,l,o,p[g+3],22,-1044525330);o=md5_ff(o,n,m,l,p[g+4],7,-176418897);l=md5_ff(l,o,n,m,p[g+5],12,1200080426);m=md5_ff(m,l,o,n,p[g+6],17,-1473231341);n=md5_ff(n,m,l,o,p[g+7],22,-45705983);o=md5_ff(o,n,m,l,p[g+8],7,1770035416);l=md5_ff(l,o,n,m,p[g+9],12,-1958414417);m=md5_ff(m,l,o,n,p[g+10],17,-42063);n=md5_ff(n,m,l,o,p[g+11],22,-1990404162);o=md5_ff(o,n,m,l,p[g+12],7,1804603682);l=md5_ff(l,o,n,m,p[g+13],12,-40341101);m=md5_ff(m,l,o,n,p[g+14],17,-1502002290);n=md5_ff(n,m,l,o,p[g+15],22,1236535329);o=md5_gg(o,n,m,l,p[g+1],5,-165796510);l=md5_gg(l,o,n,m,p[g+6],9,-1069501632);m=md5_gg(m,l,o,n,p[g+11],14,643717713);n=md5_gg(n,m,l,o,p[g+0],20,-373897302);o=md5_gg(o,n,m,l,p[g+5],5,-701558691);l=md5_gg(l,o,n,m,p[g+10],9,38016083);m=md5_gg(m,l,o,n,p[g+15],14,-660478335);n=md5_gg(n,m,l,o,p[g+4],20,-405537848);o=md5_gg(o,n,m,l,p[g+9],5,568446438);l=md5_gg(l,o,n,m,p[g+14],9,-1019803690);m=md5_gg(m,l,o,n,p[g+3],14,-187363961);n=md5_gg(n,m,l,o,p[g+8],20,1163531501);o=md5_gg(o,n,m,l,p[g+13],5,-1444681467);l=md5_gg(l,o,n,m,p[g+2],9,-51403784);m=md5_gg(m,l,o,n,p[g+7],14,1735328473);n=md5_gg(n,m,l,o,p[g+12],20,-1926607734);o=md5_hh(o,n,m,l,p[g+5],4,-378558);l=md5_hh(l,o,n,m,p[g+8],11,-2022574463);m=md5_hh(m,l,o,n,p[g+11],16,1839030562);n=md5_hh(n,m,l,o,p[g+14],23,-35309556);o=md5_hh(o,n,m,l,p[g+1],4,-1530992060);l=md5_hh(l,o,n,m,p[g+4],11,1272893353);m=md5_hh(m,l,o,n,p[g+7],16,-155497632);n=md5_hh(n,m,l,o,p[g+10],23,-1094730640);o=md5_hh(o,n,m,l,p[g+13],4,681279174);l=md5_hh(l,o,n,m,p[g+0],11,-358537222);m=md5_hh(m,l,o,n,p[g+3],16,-722521979);n=md5_hh(n,m,l,o,p[g+6],23,76029189);o=md5_hh(o,n,m,l,p[g+9],4,-640364487);l=md5_hh(l,o,n,m,p[g+12],11,-421815835);m=md5_hh(m,l,o,n,p[g+15],16,530742520);n=md5_hh(n,m,l,o,p[g+2],23,-995338651);o=md5_ii(o,n,m,l,p[g+0],6,-198630844);l=md5_ii(l,o,n,m,p[g+7],10,1126891415);m=md5_ii(m,l,o,n,p[g+14],15,-1416354905);n=md5_ii(n,m,l,o,p[g+5],21,-57434055);o=md5_ii(o,n,m,l,p[g+12],6,1700485571);l=md5_ii(l,o,n,m,p[g+3],10,-1894986606);m=md5_ii(m,l,o,n,p[g+10],15,-1051523);n=md5_ii(n,m,l,o,p[g+1],21,-2054922799);o=md5_ii(o,n,m,l,p[g+8],6,1873313359);l=md5_ii(l,o,n,m,p[g+15],10,-30611744);m=md5_ii(m,l,o,n,p[g+6],15,-1560198380);n=md5_ii(n,m,l,o,p[g+13],21,1309151649);o=md5_ii(o,n,m,l,p[g+4],6,-145523070);l=md5_ii(l,o,n,m,p[g+11],10,-1120210379);m=md5_ii(m,l,o,n,p[g+2],15,718787259);n=md5_ii(n,m,l,o,p[g+9],21,-343485551);o=safe_add(o,j);n=safe_add(n,h);m=safe_add(m,f);l=safe_add(l,e)}return Array(o,n,m,l)}function md5_cmn(h,e,d,c,g,f){return safe_add(bit_rol(safe_add(safe_add(e,h),safe_add(c,f)),g),d)}function md5_ff(g,f,k,j,e,i,h){return md5_cmn((f&k)|((~f)&j),g,f,e,i,h)}function md5_gg(g,f,k,j,e,i,h){return md5_cmn((f&j)|(k&(~j)),g,f,e,i,h)}function md5_hh(g,f,k,j,e,i,h){return md5_cmn(f^k^j,g,f,e,i,h)}function md5_ii(g,f,k,j,e,i,h){return md5_cmn(k^(f|(~j)),g,f,e,i,h)}function safe_add(a,d){var c=(a&65535)+(d&65535);var b=(a>>16)+(d>>16)+(c>>16);return(b<<16)|(c&65535)}function bit_rol(a,b){return(a<<b)|(a>>>(32-b))};" + "\n//-->\n</SCRIPT>\n"); + + fprintf(cgiOut, + "</HEAD>\n" + "<BODY ONLOAD=\"document.wfm.username.focus(); document.wfm.Login.value='MD5 Login';\" BGCOLOR=\"#FFFFFF\">\n" + "<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 CLASS=\"twh\"><TR><TD VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n" + "<FORM NAME=\"wfm\" ACTION=\"%s\" METHOD=\"POST\" ENCTYPE=\"multipart/form-data\">\n\n", cgiScriptName); + + fprintf(cgiOut, + "<TABLE WIDTH=\"400\" BGCOLOR=\"#F0F0F0\" BORDER=0 CELLSPACING=0 CELLPADDING=1 CLASS=\"tbr\">\n" + " <TR><TD COLSPAN=2 BGCOLOR=\"#004080\"><FONT COLOR=\"#FFFFFF\"> Authentication Required</FONT></TD></TR>\n" + " <TR><TD WIDTH=30> </TD><TD>\n" + " <BR>Enter your login credentials:<P>\n" + " <TABLE>\n" + " <TR><TD>Username:</TD><TD><INPUT TYPE=\"TEXT\" NAME=\"username\" SIZE=\"32\" VALUE=\"\"></TD></TR>\n" + " <TR><TD>Password:</TD><TD><INPUT TYPE=\"PASSWORD\" NAME=\"password\" SIZE=\"32\" VALUE=\"\"></TD></TR>\n" + " </TABLE><P>\n" + " </TD></TR>\n" + " <TR><TD COLSPAN=2>\n" + " <P><CENTER>\n" + " <INPUT TYPE=\"HIDDEN\" VALUE=\"login\" NAME=\"action\">\n" + " <INPUT TYPE=\"HIDDEN\" VALUE=\"%s\" NAME=\"directory\">\n" + " <INPUT TYPE=\"SUBMIT\" VALUE=\" %s Login \" NAME=\"Login\" " + "onClick=\"self.location='?directory=%s&login=client&token=' + hex_md5('%s:' + document.wfm.username.value + ':' + document.wfm.password.value); return false;\" >\n" + " </CENTER><BR>\n" + " </TD></TR>\n" + "</TABLE></FORM>\n\n" + "</TD></TR></TABLE>\n" + "</BODY>\n</HTML>\n", virt_dirname, (getenv("HTTPS")) ? "SSL" : "UNSECURE", virt_dirname, cgiRemoteAddr); + +} + + +// +// Text Area File Editor +// +// size info: the file is read as raw text with size in bytes +// the output is escaped in textarea resulting in more length +// however the output from textarea is saved unescaped so the +// resulting file size is same as before escaping +// +// size variable is used by edit_save() to read the specified +// lenght from cgiFormString() however fwrite() uses strlen() +// +void edit_ui(void) { + FILE *input; + char *buff; + char backup[4]; + char *bkcolor; + int size; + + checkfilename(NULL); + + cgiFormString("backup", backup, sizeof(backup)); + + if(strcmp("yes", backup)==0) + bkcolor="background-color:#404040; color:#FFFFFF;"; + else + bkcolor="background-color:#EEEEEE; color:#000000;"; + + input=fopen(phys_filename, "r"); + if(input==NULL) + error("Unable to open file.<BR>%s", strerror(errno)); + + fseek(input, 0, SEEK_END); + size=ftell(input); + fseek(input, 0, SEEK_SET); + + buff=(char *) malloc(size+1); + if(buff==NULL) + error("Unable to allocate memory."); + + memset(buff, 0, size+1); + + fread(buff, size, 1, input); + fclose(input); + + cgiHeaderContentType("text/html"); + html_title("Editor"); + + fprintf(cgiOut, + "<SCRIPT LANGUAGE=\"JavaScript\" TYPE=\"text/javascript\">\n" + "<!--\n" + "function chwrap() { \n" + " if(document.EDITOR.content.wrap=='off') { \n" + " document.EDITOR.content.wrap='soft';\n" + " document.EDITOR.wrapbtn.style.backgroundColor='#404040';\n" + " document.EDITOR.wrapbtn.style.color='#FFFFFF';\n" + " } else { \n" + " document.EDITOR.content.wrap='off';\n" + " document.EDITOR.wrapbtn.style.backgroundColor='#EEEEEE';\n" + " document.EDITOR.wrapbtn.style.color='#000000';\n" + " } \n" + "} \n" + "function chbak() { \n" + " if(document.EDITOR.backup.value=='yes') { \n" + " document.EDITOR.backup.value='no'; \n" + " document.EDITOR.bakbtn.style.backgroundColor='#EEEEEE';\n" + " document.EDITOR.bakbtn.style.color='#000000';\n" + " } else { \n" + " document.EDITOR.backup.value='yes'; \n" + " document.EDITOR.bakbtn.style.backgroundColor='#404040';\n" + " document.EDITOR.bakbtn.style.color='#FFFFFF';\n" + " } \n" + "} \n" + "//-->\n" + "</SCRIPT>\n" + "<STYLE TYPE=\"text/css\"><!-- \n" + "html, body, table, textarea, form { box-sizing: border-box; width:100%%; height:100%%; margin:0px; padding:0px; } \n" + "--></STYLE>\n" + "</HEAD>\n" + "<BODY>\n" + "<FORM NAME=\"EDITOR\" ACTION=\"?action=edit_save\" METHOD=\"POST\" ENCTYPE=\"multipart/form-data\" STYLE=\"height:100%%;\">\n" + "<TABLE BGCOLOR=\"#EEEEEE\" BORDER=0 CELLSPACING=0 CELLPADDING=5 STYLE=\"height:100%%;\">\n" + "<TR STYLE=\"height:1%%;\">\n" + "<TD ALIGN=\"LEFT\" VALIGN=\"MIDDLE\" BGCOLOR=\"#CCCCCC\">\n" + "<IMG SRC=\"%sedit.gif\" BORDER=0 ALIGN=\"MIDDLE\" ALT=\"EDIT\">\n" + "File Editor: %s\n" + "</TD>\n" + "<TD BGCOLOR=\"#CCCCCC\" ALIGN=\"RIGHT\">" + "<INPUT TYPE=\"button\" ID=\"bakbtn\" onClick=\"chbak()\" VALUE=\"Backup\" STYLE=\"border:none; %s \"> \n" + "<INPUT TYPE=\"button\" ID=\"wrapbtn\" onClick=\"chwrap()\" VALUE=\"Wrap\" STYLE=\"border:none; background-color:#404040; color:#FFFFFF;\">\n" + "</TD>\n" + "</TR>\n" + "<TR STYLE=\"height:99%%;\">\n" + "<TD COLSPAN=2 ALIGN=\"CENTER\" VALIGN=\"MIDDLE\" STYLE=\"height:100%%;\">\n" + "<TEXTAREA COLS=\"80\" ROWS=\"24\" NAME=\"content\" STYLE=\"resize:none;\">", + ICONSURL, virt_filename, bkcolor); + + + cgiHtmlEscapeData(buff, size); + + fprintf(cgiOut, "</TEXTAREA>\n" + "</TD>\n" + "</TR>\n" + "<TR>\n" + "<TD COLSPAN=2 ALIGN=\"RIGHT\" VALIGN=\"MIDDLE\">\n" + "<INPUT TYPE=\"SUBMIT\" VALUE=\"Save\" onClick=\"document.EDITOR.size.value=document.EDITOR.content.value.length+1; return true;\"> \n" + "<INPUT TYPE=\"BUTTON\" VALUE=\"Cancel\" onClick=\"self.location='?directory=%s&token=%s'; return true;\">\n" + "</TD>\n" + "</TR>\n" + "</TABLE>\n" + "<INPUT TYPE=\"hidden\" NAME=\"action\" VALUE=\"edit_save\">\n" + "<INPUT TYPE=\"hidden\" NAME=\"filename\" VALUE=\"%s\">\n" + "<INPUT TYPE=\"hidden\" NAME=\"size\" VALUE=\"%d\">\n" + "<INPUT TYPE=\"hidden\" NAME=\"directory\" VALUE=\"%s\">\n" + "<INPUT TYPE=\"hidden\" NAME=\"token\" VALUE=\"%s\">\n" + "<INPUT TYPE=\"hidden\" NAME=\"backup\" VALUE=\"%s\">\n" + "</FORM></BODY></HTML>\n", + virt_dirname, token, virt_filename, size, virt_dirname, token, backup); + + free(buff); + +}
\ No newline at end of file @@ -0,0 +1,607 @@ +#include "wfm.h" + +char DIRIMG[256], AUPIMG[256], ADNIMG[256], GENIMG[256], NEWIMG[256], ZIPIMG[256]; +char IMGIMG[256], OFFIMG[256], PDFIMG[256]; +char TXTIMG[256], EXEIMG[256], MEDIMG[257], ISOIMG[256], LNKIMG[256]; +regex_t reg_zip, reg_img, reg_pdf, reg_exe, reg_txt, reg_off, reg_med, reg_iso; + +char M_HR[]="<FONT COLOR=\"#000000\" STYLE=\"font-weight:bold;\">(Last Hour)"; +char M_DAY[]="<FONT COLOR=\"#505050\" STYLE=\"font-weight:bold;\">(Last Day)"; +char M_WK[]="<FONT COLOR=\"#000000\">(Last Week)"; +//char M_2WK[]="<FONT COLOR=\"#000000\">(Last 2 Weeks)"; +char M_MO[]="<FONT COLOR=\"#505050\">(Last Month)"; +//char M_2MO[]="<FONT COLOR=\"#505050\">(Last 2 Months)"; +//char M_6MO[]="<FONT COLOR=\"#707070\">(Last 6 Months)"; +char M_YR[]="<FONT COLOR=\"#909090\">(Last Year)"; +char M_OLD[]="<FONT COLOR=\"#C0C0C0\">(Old)"; + +char tNORMAL_COLOR[]="FFFFFF"; +char tHIGH_COLOR[]="33CC33"; +char tHL_COLOR[]="FFD700"; + +static const char *access_string[]={ "none", "readonly", "readwrite" }; + +void dir_icoinita(void) { + snprintf(DIRIMG, sizeof(DIRIMG), "<IMG SRC=\"%sdir.gif\" ALT=\"Dir\" ALIGN=\"MIDDLE\" BORDER=\"0\">", ICONSURL); + snprintf(LNKIMG, sizeof(LNKIMG), "<IMG SRC=\"%slnk.gif\" ALT=\"Symlink\" ALIGN=\"MIDDLE\" BORDER=\"0\">", ICONSURL); + snprintf(AUPIMG, sizeof(AUPIMG), "<IMG SRC=\"%saup.gif\" ALT=\"Up\" ALIGN=\"MIDDLE\" BORDER=\"0\" WIDTH=\"7\" HEIGHT=\"4\">", ICONSURL); + snprintf(ADNIMG, sizeof(ADNIMG), "<IMG SRC=\"%sadn.gif\" ALT=\"Down\" ALIGN=\"MIDDLE\" BORDER=\"0\" WIDTH=\"7\" HEIGHT=\"4\">", ICONSURL); + snprintf(GENIMG, sizeof(GENIMG), "<IMG SRC=\"%sgen.gif\" ALT=\"Unknown\" ALIGN=\"MIDDLE\" BORDER=\"0\" WIDTH=\"16\" HEIGHT=\"16\">", ICONSURL); + snprintf(NEWIMG, sizeof(NEWIMG), "<IMG SRC=\"%sarr.gif\" ALT=\"New\" ALIGN=\"MIDDLE\" BORDER=\"0\" WIDTH=\"16\" HEIGHT=\"16\">", ICONSURL); + snprintf(ZIPIMG, sizeof(ZIPIMG), "<IMG SRC=\"%szip.gif\" ALT=\"Archive\" ALIGN=\"MIDDLE\" BORDER=\"0\" WIDTH=\"16\" HEIGHT=\"16\">", ICONSURL); + snprintf(IMGIMG, sizeof(IMGIMG), "<IMG SRC=\"%simg.gif\" ALT=\"Image\" ALIGN=\"MIDDLE\" BORDER=\"0\" WIDTH=\"16\" HEIGHT=\"16\">", ICONSURL); + snprintf(OFFIMG, sizeof(OFFIMG), "<IMG SRC=\"%soff.gif\" ALT=\"Office File\" ALIGN=\"MIDDLE\" BORDER=\"0\" WIDTH=\"16\" HEIGHT=\"16\">", ICONSURL); + snprintf(PDFIMG, sizeof(PDFIMG), "<IMG SRC=\"%spdf.gif\" ALT=\"PDF\" ALIGN=\"MIDDLE\" BORDER=\"0\" WIDTH=\"16\" HEIGHT=\"16\">", ICONSURL); + snprintf(TXTIMG, sizeof(TXTIMG), "<IMG SRC=\"%stxt.gif\" ALT=\"Text\" ALIGN=\"MIDDLE\" BORDER=\"0\" WIDTH=\"16\" HEIGHT=\"16\">", ICONSURL); + snprintf(EXEIMG, sizeof(EXEIMG), "<IMG SRC=\"%sexe.gif\" ALT=\"Exec\" ALIGN=\"MIDDLE\" BORDER=\"0\" WIDTH=\"16\" HEIGHT=\"16\">", ICONSURL); + snprintf(MEDIMG, sizeof(MEDIMG), "<IMG SRC=\"%smed.gif\" ALT=\"Multimedia\" ALIGN=\"MIDDLE\" BORDER=\"0\" WIDTH=\"16\" HEIGHT=\"16\">", ICONSURL); + snprintf(ISOIMG, sizeof(ISOIMG), "<IMG SRC=\"%siso.gif\" ALT=\"Disk Image\" ALIGN=\"MIDDLE\" BORDER=\"0\" WIDTH=\"16\" HEIGHT=\"16\">", ICONSURL); + + if( + regcomp(®_zip, "\\.(zip|rar|tar|gz|tgz|z|arj|bz|tbz|7z|xz)$", REG_EXTENDED | REG_ICASE)!=0 || + regcomp(®_img, "\\.(gif|png|tif|tiff|jpg|jpeg)$", REG_EXTENDED | REG_ICASE)!=0 || + regcomp(®_off, "\\.(doc|docx|rtf|dot|xls|xlsx|ppt|pptx|off)$", REG_EXTENDED | REG_ICASE)!=0 || + regcomp(®_iso, "\\.(iso|flp|img|nrg|dmg)$", REG_EXTENDED | REG_ICASE)!=0 || + regcomp(®_med, "\\.(mp3|mp4|vaw|mov|avi|ivr|mkv)$", REG_EXTENDED | REG_ICASE)!=0 || + regcomp(®_pdf, "\\.(pdf|ps|eps|ai)$", REG_EXTENDED | REG_ICASE)!=0 || + regcomp(®_txt, "\\.(txt|asc|nfo|me|log|htm|html|shtml|js|jsp|php|xml|dtd|css|bas|c|h|cpp|cmd|bat|sh|ksh|awk|reg|log|bak|cfg|py)$", REG_EXTENDED | REG_ICASE)!=0 || + regcomp(®_exe, "\\.(exe|cmd|vbs|bat|com|pif)$", REG_EXTENDED | REG_ICASE)!=0 + ) error("Unable to compile regex."); + +} + + +// +// Display directory list main panel +// +void dirlist(void) { + ASDIR *direntry; + off_t size, totalsize=0; + char highlight[VIRT_FILENAME_SIZE]; + char namepfx[1024], sizepfx[1024], datepfx[1024]; + char rtime[64], mtime[64], atime[64]; + char *stime; + char sortby[64]; + char *name, *icon, *linecolor; + int nentr=0, e; + int editable; + time_t now; + + time(&now); + + cgiFormStringNoNewlines("highlight", highlight, VIRT_FILENAME_SIZE-1); + cgiFormStringNoNewlines("sortby", sortby, 63); + if(strlen(sortby)<4) + snprintf(sortby, 63, "name"); + + // + // Get and Print Directory Entries + // + if(strcmp(sortby, "name")==0) + nentr=asscandir(phys_dirname, &direntry, namesort); + else if(strcmp(sortby, "rname")==0) + nentr=asscandir(phys_dirname, &direntry, rnamesort); + else if(strcmp(sortby, "size")==0) + nentr=asscandir(phys_dirname, &direntry, sizesort); + else if(strcmp(sortby, "rsize")==0) + nentr=asscandir(phys_dirname, &direntry, rsizesort); + else if(strcmp(sortby, "date")==0) + nentr=asscandir(phys_dirname, &direntry, timesort); + else if(strcmp(sortby, "rdate")==0) + nentr=asscandir(phys_dirname, &direntry, rtimesort); + else + nentr=asscandir(phys_dirname, &direntry, namesort); + + dir_icoinita(); + + cgiHeaderContentType("text/html"); + + // + // HTML HEADER + // + fprintf(cgiOut, + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n\"http://www.w3.org/TR/html4/loose.dtd\">\n" + "\n%s" + "<HTML>" + "<HEAD>\n" + "<TITLE>%s : %c%s</TITLE>\n" + "<SCRIPT LANGUAGE=\"JavaScript\" TYPE=\"text/javascript\">\n" + "<!--\n" + " function checkUncheckAll(checkAllState, cbGroup) {\n" + " if(cbGroup.length > 0) {\n" + " for (i = 0; i < cbGroup.length; i++) {\n" + " cbGroup[i].checked = checkAllState.checked;\n" + " }\n" + " }\n" + " else {\n" + " cbGroup.checked = checkAllState.checked;\n" + " }\n" + " }\n" + "\n" + " function xmlhttpPost(strURL) {\n" + " var xmlHttpReq = false;\n" + " var self = this;\n" + " var method = \"GET\";\n" + "\n" + " if (window.XMLHttpRequest) {\n" + " self.xmlHttpReq = new XMLHttpRequest();\n" + " }\n" + " else if (window.ActiveXObject) {\n" + " self.xmlHttpReq = new ActiveXObject(\"Microsoft.XMLHTTP\");\n" + " }\n" + " if (self.xmlHttpReq != null) {\n" + " self.xmlHttpReq.open(method, strURL, true);\n" + " self.xmlHttpReq.onreadystatechange = function () {\n" + " if (self.xmlHttpReq.readyState == 4) {\n" + " var result = document.getElementById(\"Upload_Status\");\n" + " result.value = self.xmlHttpReq.responseText;\n" + " }\n" + " }\n" + " self.xmlHttpReq.send(null);\n" + " }\n" + " }\n" + "\n" + " function start() {\n" + " setInterval('xmlhttpPost(\"%s?ea=upstat&upload_id=%s\");', \"100\");\n" + " }\n" + "\n" + "//-->\n" + "</SCRIPT>\n" + "<STYLE TYPE=\"text/css\">\n" + "<!--\n" + "A:link {text-decoration: none; color:#0000CE; } \n" + "A:visited {text-decoration: none; color:#0000CE; } \n" + "A:active {text-decoration: none; color:#FF0000; } \n" + "A:hover {text-decoration: none; color:#FF0000; } \n" + "html, body, table { width:100%%; margin:0px; padding:0px; border:none; } \n" + "img { vertical-align:middle; } \n" + "td, th { font-family: Tahoma, Arial, Geneva, sans-serif; font-size:11px; margin:0px; padding:2px; border:none; } \n" + "input { border-color:#000000;border-style:solid; \n" + "font-family: Tahoma, Arial, Geneva, sans-serif; font-size:11px; }\n" + ".hovout { border: none; padding: 0px; background-color: transparent; color: #0000CE; }\n" + ".hovin { border: none; padding: 0px; background-color: transparent; color: #FF0000; }\n" + "//-->\n" + "</STYLE>\n" + "<META HTTP-EQUIV=\"Content-type\" CONTENT=\"text/html;charset=UTF-8\">\n" + "<LINK REL=\"icon\" TYPE=\"image/gif\" HREF=\"%swfmicon.gif\">\n" + "</HEAD>\n" + "<BODY BGCOLOR=\"#FFFFFF\">\n" + "<FORM ACTION=\"%s\" METHOD=\"POST\" ENCTYPE=\"multipart/form-data\" onsubmit=\"start()\">\n", + copyright, TAGLINE, (strlen(virt_dirname)>0) ? ' ' : '/', virt_dirname, cgiScriptName, "1234", ICONSURL, cgiScriptName); + + + + + // + // TITLE + // + fprintf(cgiOut, + "<!-- TITLE --> \n" + "<TABLE WIDTH=\"100%%\" BGCOLOR=\"#FFFFFF\" CELLPADDING=\"0\" CELLSPACING=\"0\" BORDER=\"0\" STYLE=\"height:28px;\">\n" + "<TR>\n" + "<TD WIDTH=\"100%%\" BGCOLOR=\"#0072c6\" VALIGN=\"MIDDLE\" ALIGN=\"LEFT\" STYLE=\"color:#FFFFFF; font-weight:bold;\">\n" + " <IMG SRC=\"%swfmicon.gif\" ALIGN=\"MIDDLE\" ALT=\"WFM\">\n" + "%s : %c%s \n" + "<TD BGCOLOR=\"#F1F1F1\" VALIGN=\"MIDDLE\" ALIGN=\"RIGHT\" STYLE=\"color:#000000; font-weight:bold; white-space:nowrap\">\n" + , + ICONSURL, TAGLINE, (strlen(virt_dirname)>0) ? ' ' : '/', virt_dirname + ); + + + // lock / unlock + if(!access_as_user && users_defined) + fprintf(cgiOut, + "<A HREF=\"%s?action=login&directory=%s\">"\ + " <IMG SRC=\"%s%s.gif\" ALIGN=\"MIDDLE\" BORDER=\"0\" ALT=\"Access\"></A> %s\n", + cgiScriptName, virt_dirname, ICONSURL, access_string[access_level], access_string[access_level]); + else + fprintf(cgiOut, + "<A HREF=\"%s?directory=%s\"><IMG SRC=\"%s%s.gif\" BORDER=\"0\" ALIGN=\"MIDDLE\" ALT=\"Access\">" + "</A> %s <IMG SRC=\"%suser.gif\" ALIGN=\"MIDDLE\" ALT=\"User\"> %s \n", + cgiScriptName, virt_dirname, ICONSURL, access_string[access_level], access_string[access_level], ICONSURL, loggedinuser); + + // about / version + fprintf(cgiOut, + "</TD><TD BGCOLOR=\"#F1F1F1\" VALIGN=\"MIDDLE\" ALIGN=\"RIGHT\" STYLE=\"color:#000000; font-weight:bold; white-space:nowrap\">" + " <IMG SRC=\"%snet.gif\" ALIGN=\"MIDDLE\" ALT=\"Client IP\"> %s </TD>" + "<TD BGCOLOR=\"#F1F1F1\" VALIGN=\"MIDDLE\" ALIGN=\"RIGHT\" STYLE=\"color:#000000; font-weight:bold; white-space:nowrap\">" + "<A HREF=\"%s?action=about&directory=%s&token=%s\"><IMG BORDER=\"0\" SRC=\"%sver.gif\" ALIGN=\"MIDDLE\" ALT=\"Version\"></A> v%s " + "</TD>\n"\ + "</TR>\n"\ + "</TABLE>\n", + ICONSURL, cgiRemoteAddr, cgiScriptName, virt_dirname, token, ICONSURL, VERSION + ); + + + + // + // TOOLBAR + // + fprintf(cgiOut, + "<!-- TOOLBAR -->\n"\ + "<TABLE WIDTH=\"100%%\" BGCOLOR=\"#FFFFFF\" CELLPADDING=\"0\" CELLSPACING=\"0\" BORDER=\"0\" STYLE=\"height:28px;\">\n"\ + "<TR>\n"\ + "<!-- DIR-UP -->\n"\ + "<TD BGCOLOR=\"#F1F1F1\" VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n"\ + "<A HREF=\"%s?sortby=%s&directory=%s&token=%s\">\n"\ + "<IMG SRC=\"%sdir_up.gif\" BORDER=0 ALIGN=\"MIDDLE\" WIDTH=\"16\" HEIGHT=\"16\" ALT=\"Dir Up\"> Up\n"\ + "</A>\n"\ + "</TD>\n", + cgiScriptName, sortby, virt_parent, token, ICONSURL); + + fprintf(cgiOut, + "<!-- HOME -->\n"\ + "<TD BGCOLOR=\"#F1F1F1\" VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n"\ + "<A HREF=\"%s?sortby=%s&directory=/&token=%s\">\n"\ + "<IMG SRC=\"%shome.gif\" BORDER=0 ALIGN=\"MIDDLE\" WIDTH=\"16\" HEIGHT=\"16\" ALT=\"Home\"> Home\n"\ + "</A>\n"\ + "</TD>\n", + cgiScriptName, sortby, token, ICONSURL); + + fprintf(cgiOut, + "<!-- RELOAD -->\n"\ + "<TD BGCOLOR=\"#F1F1F1\" VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n"\ + "<A HREF=\"%s?sortby=%s&directory=%s&token=%s\">\n"\ + "<IMG SRC=\"%sreload.gif\" BORDER=0 ALIGN=\"MIDDLE\" ALT=\"Reload\"> Refresh\n"\ + "</A>\n"\ + "</TD>\n", + cgiScriptName, sortby, virt_dirname, token, ICONSURL); + + fprintf(cgiOut, + "<!-- MULTI DELETE -->\n"\ + "<TD BGCOLOR=\"#F1F1F1\" VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n"\ + "<INPUT TYPE=\"IMAGE\" SRC=\"%sdelete.gif\" STYLE=\"border: none; padding: 0px; vertical-align:middle;\" ALT=\"Delete\" ALIGN=\"MIDDLE\" NAME=\"multi_delete_prompt\" VALUE=\"Delete\">\n" + "<INPUT TYPE=\"SUBMIT\" CLASS=\"hovout\" NAME=\"multi_delete_prompt\" VALUE=\"Delete\" onMouseOver=\"this.className='hovin';\" onMouseOut=\"this.className='hovout';\">\n" + "</TD>\n", + ICONSURL); + + fprintf(cgiOut, + "<!-- MULTI MOVE -->\n"\ + "<TD BGCOLOR=\"#F1F1F1\" VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n"\ + "<INPUT TYPE=\"IMAGE\" SRC=\"%smove.gif\" STYLE=\"border: none; padding: 0px; vertical-align:middle; \" ALT=\"Move\" ALIGN=\"MIDDLE\" NAME=\"multi_move_prompt\" VALUE=\"Move\">\n" + "<INPUT TYPE=\"SUBMIT\" CLASS=\"hovout\" NAME=\"multi_move_prompt\" VALUE=\"Move\" onMouseOver=\"this.className='hovin';\" onMouseOut=\"this.className='hovout';\">\n" + "</TD>\n", + ICONSURL); + + fprintf(cgiOut, + "<!-- NEWDIR -->\n"\ + "<TD BGCOLOR=\"#F1F1F1\" VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n"\ + "<A HREF=\"%s?action=mkdir_prompt&directory=%s&token=%s\" >\n"\ + "<IMG SRC=\"%smkdir.gif\" BORDER=0 ALIGN=\"MIDDLE\" ALT=\"New Folder\"> New Folder\n"\ + "</A>\n"\ + "</TD>\n", + cgiScriptName, virt_dirname, token, ICONSURL); + + + fprintf(cgiOut, + "<!-- NEWFILE -->\n"\ + "<TD BGCOLOR=\"#F1F1F1\" VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n"\ + "<A HREF=\"%s?action=mkfile_prompt&directory=%s&token=%s\" >\n"\ + "<IMG SRC=\"%smkfile.gif\" BORDER=0 ALIGN=\"MIDDLE\" ALT=\"New File\"> New File\n"\ + "</A>\n"\ + "</TD>\n", + cgiScriptName, virt_dirname, token, ICONSURL); + + + + fprintf(cgiOut, + "<!-- UPLOAD -->\n"\ + "<TD BGCOLOR=\"#F1F1F1\" VALIGN=\"MIDDLE\" ALIGN=\"CENTER\">\n" + "<INPUT TYPE=\"hidden\" NAME=\"directory\" VALUE=\"%s\">\n" + "<INPUT TYPE=\"hidden\" NAME=\"token\" VALUE=\"%s\">\n" + "<INPUT TYPE=\"hidden\" NAME=\"upload_id\" VALUE=\"%s\">\n" + "<INPUT TYPE=\"file\" NAME=\"filename\"> \n" + "<INPUT TYPE=\"submit\" NAME=\"upload\" ID=\"Upload_Status\" VALUE=\"Upload\" >\n" + "</TD>\n" + "</TR>\n" + "</TABLE>\n", + virt_dirname, token, "1234"); + + // + // SORT BY + // + if(strcmp(sortby, "size")==0) { + snprintf(namepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=name\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Filename</A>", cgiScriptName, virt_dirname, token); + snprintf(sizepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=rsize\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Size</A> %s", cgiScriptName, virt_dirname, token, ADNIMG); + snprintf(datepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=date\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Modified</A>", cgiScriptName, virt_dirname, token); + } else if(strcmp(sortby, "rsize")==0) { + snprintf(namepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=name\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Filename</A>", cgiScriptName, virt_dirname, token); + snprintf(sizepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=size\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Size</A> %s", cgiScriptName, virt_dirname, token, AUPIMG); + snprintf(datepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=date\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Modified</A>", cgiScriptName, virt_dirname, token); + } else if(strcmp(sortby, "date")==0) { + snprintf(namepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=name\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Filename</A>", cgiScriptName, virt_dirname, token); + snprintf(sizepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=size\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Size</A>", cgiScriptName, virt_dirname, token); + snprintf(datepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=rdate\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Modified</A> %s", cgiScriptName, virt_dirname, token, ADNIMG); + } else if(strcmp(sortby, "rdate")==0) { + snprintf(namepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=name\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Filename</A>", cgiScriptName, virt_dirname, token); + snprintf(sizepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=size\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Size</A>", cgiScriptName, virt_dirname, token); + snprintf(datepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=date\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Modified</A> %s", cgiScriptName, virt_dirname, token, AUPIMG); + } else if(strcmp(sortby, "name")==0) { + snprintf(namepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=rname\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Filename</A> %s", cgiScriptName, virt_dirname, token, ADNIMG); + snprintf(sizepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=size\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Size</A>", cgiScriptName, virt_dirname, token); + snprintf(datepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=date\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Modified</A>", cgiScriptName, virt_dirname, token); + } else if(strcmp(sortby, "rname")==0) { + snprintf(namepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=name\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Filename</A> %s", cgiScriptName, virt_dirname, token, AUPIMG); + snprintf(sizepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=size\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Size</A>", cgiScriptName, virt_dirname, token); + snprintf(datepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=date\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Modified</A>", cgiScriptName, virt_dirname, token); + } else { + snprintf(namepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=name\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Filename</A>", cgiScriptName, virt_dirname, token); + snprintf(sizepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=size\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Size</A>", cgiScriptName, virt_dirname, token); + snprintf(datepfx, 1024, " <A HREF=\"%s?directory=%s&token=%s&sortby=date\" STYLE=\"text-decoration: none; color:#FFFFFF;\">Modified</A>", cgiScriptName, virt_dirname, token); + } + + + // SORTBY ROW + dir files display + fprintf(cgiOut, + "<!-- MAIN FILE TABLE --> \n"\ + "<TABLE WIDTH=\"100%%\" BGCOLOR=\"#FFFFFF\" CELLPADDING=0 CELLSPACING=0 BORDER=0 >\n"\ + "<!-- SORTBY LINE -->\n"\ + "<TR BGCOLOR=\"#FFFFFF\" >\n"\ + "<TD ALIGN=\"left\" WIDTH=\"50%%\" BGCOLOR=\"#A0A0A0\">\n"\ + "<FONT COLOR=\"#FFFFFF\">\n"\ + "<INPUT TYPE=\"CHECKBOX\" NAME=\"CHECKALL\" STYLE=\"padding: 0px; border: none;\" ONCLICK=\"checkUncheckAll(this, multiselect_filename);\">\n" + "%s\n"\ + "</FONT>\n"\ + "</TD>\n"\ + "<TD ALIGN=\"right\" BGCOLOR=\"#A0A0A0\">\n"\ + "<FONT COLOR=\"#FFFFFF\">\n"\ + "%s\n"\ + "</FONT>\n"\ + "</TD>\n"\ + "<TD ALIGN=\"right\" BGCOLOR=\"#A0A0A0\">\n"\ + "<FONT COLOR=\"#FFFFFF\">\n"\ + "%s\n"\ + "</FONT>\n"\ + "</TD>\n"\ + "<TD ALIGN=\"right\" BGCOLOR=\"#A0A0A0\">\n"\ + " "\ + "</TD>"\ + "<TD ALIGN=\"left\" BGCOLOR=\"#A0A0A0\">\n"\ + "<FONT COLOR=\"#FFFFFF\">\n"\ + " \n"\ + "</FONT>\n"\ + "</TD>\n"\ + "</TR>\n" + "<!-- End of Header -->\n\n", + namepfx, sizepfx, datepfx); + + + // Directories + for(e=0; e<nentr; e++) { + if(!S_ISDIR(direntry[e].type)) + continue; + if(direntry[e].name[0]=='.') + continue; + + name=direntry[e].name; + if(recursive_du) { + snprintf(phys_filename, PHYS_FILENAME_SIZE, "%s/%s", phys_dirname, direntry[e].name); + size=du(phys_filename); + } + else { + size=-1; + } + + ctime_r(&direntry[e].atime, atime); + ctime_r(&direntry[e].mtime, mtime); + ctime_r(&direntry[e].rtime, rtime); + + if(now-direntry[e].mtime < 3600) stime=M_HR; + else if(now-direntry[e].mtime < 24*3600) stime=M_DAY; + else if(now-direntry[e].mtime < 7*24*3600) stime=M_WK; +// else if(now-direntry[e].mtime < 14*24*3600) stime=M_2WK; + else if(now-direntry[e].mtime < 31*24*3600) stime=M_MO; +// else if(now-direntry[e].mtime < 62*24*3600) stime=M_2MO; +// else if(now-direntry[e].mtime < 182*24*3600) stime=M_6MO; + else if(now-direntry[e].mtime < 365*24*3600) stime=M_YR; + else stime=M_OLD; + + if(strcmp(highlight, name)==0) { + icon=NEWIMG; + linecolor=tHIGH_COLOR; + } + else { + icon=DIRIMG; + linecolor=tNORMAL_COLOR; + } + + + // directory name / date + fprintf(cgiOut, + "<!-- Directory Entry -->\n"\ + "<TR BGCOLOR=\"#%s\" onMouseOver=\"this.bgColor='#%s';\" onMouseOut=\"this.bgColor='#%s';\">\n"\ + "<TD ALIGN=\"LEFT\">\n" + "<INPUT TYPE=\"CHECKBOX\" NAME=\"multiselect_filename\" STYLE=\"border: none;\" VALUE=\"%s\">", + linecolor, HL_COLOR, linecolor, name); + + fprintf(cgiOut, + "<A HREF=\"%s?sortby=%s&directory=%s/%s&token=%s\">%s %s</A></TD> \n"\ + "<TD ALIGN=\"RIGHT\">%s</TD>\n"\ + "<TD ALIGN=\"RIGHT\"><SPAN TITLE=\"Created:%s\n Modified:%s\n Accessed:%s\n\">%s %s</FONT></SPAN></TD>\n"\ + "<TD> </TD>"\ + "<TD ALIGN=\"LEFT\">", + cgiScriptName, sortby, (strcmp(virt_dirname, "/")==0) ? "" : virt_dirname, name, token, icon, name, + buprintf(size, TRUE), rtime, mtime, atime, stime, mtime); + + // rename + fprintf(cgiOut, "\n"\ + "<A HREF=\"%s?action=rename_prompt&directory=%s&filename=%s&token=%s\" TITLE=\"Rename '%s'\">\n"\ + "<IMG SRC=\"%srename.gif\" BORDER=0 WIDTH=16 HEIGHT=16 ALT=\"Rename File\">\n"\ + "</A>\n", + cgiScriptName, virt_dirname, name, token, name, ICONSURL); + + // move + fprintf(cgiOut, "\n"\ + "<A HREF=\"%s?action=move_prompt&directory=%s&filename=%s&token=%s\" TITLE=\"Move '%s'\">\n"\ + "<IMG SRC=\"%smove.gif\" BORDER=0 WIDTH=16 HEIGHT=16 ALT=\"Move File\">\n"\ + "</A>\n", + cgiScriptName, virt_dirname, name, token, name, ICONSURL); + + // delete + fprintf(cgiOut, "\n"\ + "<A HREF=\"%s?action=delete_prompt&directory=%s&filename=%s&token=%s\" TITLE=\"Delete '%s'\">\n"\ + "<IMG SRC=\"%sdelete.gif\" BORDER=0 WIDTH=16 HEIGHT=16 ALT=\"Delete Directory\">\n"\ + "</A>\n"\ + "</TD>\n"\ + "</TR>\n\n\n", + cgiScriptName, virt_dirname, name, token, name, ICONSURL); + + totalsize+=size; + } + + + // regular files + for(e=0; e<nentr; e++) { + if(S_ISDIR(direntry[e].type)) + continue; + if(direntry[e].name[0]=='.') + continue; + + name=direntry[e].name; + size=direntry[e].size; + + ctime_r(&direntry[e].atime, atime); + ctime_r(&direntry[e].mtime, mtime); + ctime_r(&direntry[e].rtime, rtime); + + if(now-direntry[e].mtime < 3600) stime=M_HR; + else if(now-direntry[e].mtime < 24*3600) stime=M_DAY; + else if(now-direntry[e].mtime < 7*24*3600) stime=M_WK; +// else if(now-direntry[e].mtime < 14*24*3600) stime=M_2WK; + else if(now-direntry[e].mtime < 31*24*3600) stime=M_MO; +// else if(now-direntry[e].mtime < 62*24*3600) stime=M_2MO; +// else if(now-direntry[e].mtime < 182*24*3600) stime=M_6MO; + else if(now-direntry[e].mtime < 365*24*3600) stime=M_YR; + else stime=M_OLD; + + if(regexec(®_zip, name, 0, 0, 0)==0) { icon=ZIPIMG; editable=0; } + else if(regexec(®_img, name, 0, 0, 0)==0) { icon=IMGIMG; editable=0; } + else if(regexec(®_off, name, 0, 0, 0)==0) { icon=OFFIMG; editable=0; } + else if(regexec(®_pdf, name, 0, 0, 0)==0) { icon=PDFIMG; editable=0; } + else if(regexec(®_txt, name, 0, 0, 0)==0) { icon=TXTIMG; editable=1; } + else if(regexec(®_exe, name, 0, 0, 0)==0) { icon=EXEIMG; editable=0; } + else if(regexec(®_med, name, 0, 0, 0)==0) { icon=MEDIMG; editable=0; } + else if(regexec(®_iso, name, 0, 0, 0)==0) { icon=ISOIMG; editable=0; } + else { icon=GENIMG; editable=0; } + + if(edit_any_file) { editable=1; } + + if(strcmp(highlight, name)==0) { + icon=NEWIMG; + linecolor=tHIGH_COLOR; + } + else { + linecolor=tNORMAL_COLOR; + } + + // filename + fprintf(cgiOut, + "<!-- File Entry -->\n" + "<TR BGCOLOR=\"#%s\" onMouseOver=\"this.bgColor='#%s';\" onMouseOut=\"this.bgColor='#%s';\">\n" + "<TD ALIGN=\"LEFT\"><INPUT TYPE=\"CHECKBOX\" NAME=\"multiselect_filename\" STYLE=\"border: none;\" VALUE=\"%s\">" + "<A HREF=\"%s?action=%s&directory=%s&filename=%s&token=%s\" TITLE=\"Open '%s'\">%s %s</A></TD>\n", + linecolor, HL_COLOR, linecolor, name, cgiScriptName, (edit_by_default && editable) ? "edit" : "sendfile", + virt_dirname, name, token, name, icon, name); + + + // size / date + fprintf(cgiOut, + "\n" + "<TD ALIGN=\"RIGHT\" >%s</TD>\n" + "<TD ALIGN=\"RIGHT\" ><SPAN TITLE=\"Created:%s\n Modified:%s\n Accessed:%s\n\">%s %s</FONT></SPAN></TD>\n", + buprintf(size, TRUE), rtime, mtime, atime, stime, mtime); + + + // file tools + fprintf(cgiOut, "\n<TD> </TD><TD ALIGN=\"LEFT\">\n"); + + + // rename + fprintf(cgiOut, + "<A HREF=\"%s?action=rename_prompt&directory=%s&filename=%s&token=%s\" TITLE=\"Rename '%s'\">\n" + "<IMG SRC=\"%srename.gif\" BORDER=0 WIDTH=16 HEIGHT=16 ALT=\"Rename File\">\n" + "</A>\n", + cgiScriptName, virt_dirname, name, token, name, ICONSURL); + + // move + fprintf(cgiOut, + "\n" + "<A HREF=\"%s?action=move_prompt&directory=%s&filename=%s&token=%s\" TITLE=\"Move '%s'\">" + "<IMG SRC=\"%smove.gif\" BORDER=0 WIDTH=16 HEIGHT=16 ALT=\"Move '%s'\">\n" + "</A>\n", + cgiScriptName, virt_dirname, name, token, name, ICONSURL, name); + + // delete + fprintf(cgiOut, + "\n" + "<A HREF=\"%s?action=delete_prompt&directory=%s&filename=%s&token=%s\" " + "TITLE=\"Remove '%s'\"> \n" + "<IMG SRC=\"%sdelete.gif\" BORDER=0 WIDTH=16 HEIGHT=16 ALT=\"Delete File\">\n" + "</A>\n", + cgiScriptName, virt_dirname, name, token, name, ICONSURL); + + + // view + if(strlen(HOMEURL)>4) + fprintf(cgiOut, + "\n" + "<A HREF=\"%s%s%s/%s\" TITLE=\"Preview '%s' In Browser\">\n" + "<IMG SRC=\"%sext.gif\" BORDER=0 WIDTH=16 HEIGHT=16 ALT=\"Preview '%s' In Browser\" >\n" + "</A>\n", + HOMEURL, (virt_dirname[0]!='/') ? "/" : "", (strcmp(virt_dirname, "/")==0) ? "" : virt_dirname, name, name, ICONSURL, name); + + + // edit for text files.. + if(editable) { + if(edit_by_default) + fprintf(cgiOut, + "\n" + "<A HREF=\"%s?action=sendfile&directory=%s&filename=%s&token=%s\" TITLE=\"Download '%s'\">\n" + "<IMG SRC=\"%sdisk.gif\" BORDER=0 WIDTH=16 HEIGHT=16 ALT=\"Download File\">\n" + "</A>\n" + "</TD>\n" + "</TR>\n\n", + cgiScriptName, virt_dirname, name, token, name, ICONSURL); + else + fprintf(cgiOut, + "\n" + "<A HREF=\"%s?action=edit&directory=%s&filename=%s&token=%s\" TITLE=\"Edit '%s'\">\n" + "<IMG SRC=\"%sedit.gif\" BORDER=0 WIDTH=16 HEIGHT=16 ALT=\"Edit File\">\n" + "</A>\n" + "</TD>\n" + "</TR>\n\n", + cgiScriptName, virt_dirname, name, token, name, ICONSURL); + } + else { + fprintf(cgiOut, + "\n" + " \n" + "</TD>\n" + "</TR>\n\n" + ); + } + + totalsize+=size; + + } + + tstop(); + + // + // footer line + // + fprintf(cgiOut, + "<!-- FOOTER -->\n" + "<TR>\n" + "<TD BGCOLOR=\"#%s\"> </TD>\n" + "<TD BGCOLOR=\"#%s\" ALIGN=\"right\" STYLE=\"border-top:1px solid grey\">total %s </TD>\n" + "<TD BGCOLOR=\"#%s\" ALIGN=\"right\" STYLE=\"color:#D0D0D0;\">%.1f ms</TD>\n" + "<TD BGCOLOR=\"#%s\"> </TD>\n" + "<TD BGCOLOR=\"#%s\"> </TD>\n" + "</TR>\n" + "</TABLE>\n</FORM>\n</BODY>\n<!-- Page generated in %f seconds -->\n</HTML>\n\n", + NORMAL_COLOR, NORMAL_COLOR, buprintf(totalsize, TRUE), NORMAL_COLOR, (t2-t1)*1000, NORMAL_COLOR, NORMAL_COLOR, t2-t1 + ); + +} + diff --git a/fileio.c b/fileio.c new file mode 100644 index 0000000..a6c24c9 --- /dev/null +++ b/fileio.c @@ -0,0 +1,518 @@ +// WFM File I/O Routines + +#include "wfm.h" + +// +// Send file to client browser +// Called by cgiMain action=sendfile +// +void sendfile(void) { + char buff[1024]; + FILE *in; + int rd, tot, size, pos, blk; + + checkfilename(NULL); + + // TODO: 2gb file limit? + in=fopen(phys_filename, "rb"); + if(!in) + error("Unable to open file.<BR>%s", strerror(errno)); + + fseek(in, 0, SEEK_END); + size=ftell(in); + fseek(in, 0, SEEK_SET); + + fprintf(cgiOut, + "Content-Type: application/octet-stream\r\n" + "Content-Disposition: attachment; filename=\"%s\"; size=%d\r\n" + "Content-Length: %d\r\n\r\n", + virt_filename, size, size + ); + + blk=sizeof(buff); + + reread: + pos=ftell(in); + rd=fread(buff, blk, 1, in); + + if(rd) { + tot=tot+rd*blk; +// printf("rw=%u size=%u total=%u remaining=%u\n", rd*blk, size, tot, size-pos-(rd*blk)); + fwrite(buff, blk, 1, cgiOut); + goto reread; + } + + if(pos<size) { + blk=size-pos; + fseek(in, pos, SEEK_SET); + goto reread; + } + + fclose(in); + +} + +// +// Receive file from client browser via upload form +// Called by cgiMain action=upload +// +void receivefile(void) { + cgiFilePtr input; + FILE *output; + char *buff; + int size=0, got=0; + + if(cgiFormFileSize("filename", &size) != cgiFormSuccess) + error("No file size specified."); + + if(cgiFormFileOpen("filename", &input) != cgiFormSuccess) + error("Unable to access uploaded file."); + + buff=(char *) malloc(size+1); + if(buff==NULL) + error("Unable to allocate memory."); + + // TODO: check for freeze - return value? + if(cgiFormFileRead(input, buff, size, &got) != cgiFormSuccess) + error("Reading file."); + + cgiFormFileClose(input); + + if(got != size) + error("Wrong file size. Size=%d Received=%d.", size, got); + + checkfilename(NULL); + + output=fopen(phys_filename, "wb"); + if(!output) + error("Unable to open file %s for writing.<BR>%s", virt_filename, strerror(errno)); + + if(fwrite(buff, size, 1, output) != 1) + error("While writing file.<BR>%s", strerror(errno)); + + fclose(output); + free(buff); + + redirect("?highlight=%s&directory=%s&token=%s", virt_filename, virt_dirname, token); + +} + +// +// Create a new new empty file +// Called by cgiMain action=mkfile +// +void mkfile(void) { + FILE *output; + + checkfilename(NULL); + + output=fopen(phys_filename, "a"); + + if(!output) + error("Unable to create file.<BR>%s", strerror(errno)); + + fclose(output); + + redirect("?highlight=%s&directory=%s&token=%s", virt_filename, virt_dirname, token); + +} + +// +// Create a new empty folder +// Called by cgiMain action=newdir +// +void newdir(void) { + + checkfilename(NULL); + + if(mkdir(phys_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH )!=0) + error("Unable to create directory.<BR>%s", strerror(errno)); + + redirect("?highlight=%s&directory=%s&token=%s", virt_filename, virt_dirname, token); + +} + +// +// Save file from textarea editor +// Called by cgiMain action=edit_save +// +void edit_save(void) { + int size=0; + int tmpfd; + char *buff; + char tempname[64]; + //FILE *output; + FILE *tempf; + char backup[4]; + char backup_filename[PHYS_DESTINATION_SIZE]; + regex_t re; + regmatch_t pmatch; + struct stat tmpstat; + + + checkfilename(NULL); + + // the size should be updated by onclick from content.value.lenght just before submission + // it's used to verify that received data length is consistent with editor contents + cgiFormInteger("size", &size, 0); + + if(size>100*1024*1024) + error("Input size too large."); + + buff=(char *) malloc(size); + if(buff==NULL) + error("Unable to allocate memory."); + + memset(buff, 0, size); + + cgiFormString("content", buff, size); + + if(strlen(buff)+1 != size) // +1 because size was also given +1 via front end + error("Received wrong size. <BR>ContentLen=%d DataLen=%d. <BR> The file was not changed.", size, strlen(buff)); + + // rename to .bak if requested + cgiFormStringNoNewlines("backup", backup, sizeof(backup)); + + if(strcmp(backup, "yes")==0) { + regcomp(&re, "\\.(.+)$", REG_EXTENDED|REG_ICASE); + if(regexec(&re, phys_filename, 1, &pmatch, 0)==0) { + if(pmatch.rm_so+4 < PHYS_DESTINATION_SIZE) { + strcpy(backup_filename, phys_filename); + strcpy(backup_filename+pmatch.rm_so+1, "bak\0"); + if(rename(phys_filename, backup_filename)!=0) + error("Unable to create .bak file.<BR>%s was not modified.<BR>%s", virt_filename, strerror(errno)); + } + } + } + + // write to temporary file + snprintf(tempname, sizeof(tempname), "%s/.wfmXXXXXX", phys_dirname); + + tmpfd=mkstemp(tempname); + + if(!tmpfd) + error("Unable to create temporary file %s.<BR>%s", basename(tempname), strerror(errno)); + + tempf=fdopen(tmpfd, "w"); + + if(!tempf) + error("Unable to open temporary file %s.<BR>%s", basename(tempname), strerror(errno)); + + if(fwrite(buff, strlen(buff), 1, tempf) != 1) + error("Unable to write to temporary file %s.<BR>%s", basename(tempname), strerror(errno)); + + fclose(tempf); + + if(stat(tempname, &tmpstat)!=0) + error("Unable to check temporary file size.<BR>%s<BR>%s", basename(tempname), strerror(errno)); + + if(tmpstat.st_size != strlen(buff)) + error("Temprary file has a wrong length. Giving up.<BR>%s size=%d, buff len=%d", virt_filename, tmpstat.st_size); + + if(chmod(tempname, 00644)!=0) + error("Unable to set file permissions.<BR>%s", strerror(errno)); + + // finally rename to desination file + if(rename(tempname, phys_filename)!=0) + error("Unable to rename temp file.<BR>%s - %s<BR>%s<BR>", basename(tempname), virt_filename, strerror(errno)); + + + free(buff); + + redirect("?highlight=%s&directory=%s&token=%s", virt_filename, virt_dirname, token); + +} + +// +// Recursively Delete Folders - Internal Routine +// Called by fileio_delete when directory is encountered +// +void fileio_re_rmdir(char *dirname) { + DIR *dir; + struct dirent *direntry; + struct stat fileinfo; + char tempfullpath[PHYS_FILENAME_SIZE]; + + dir=opendir(dirname); + if(!dir) + error("Unable to remove directory.."); + + + direntry=readdir(dir); + while(direntry!=0) { + if(strncmp(direntry->d_name, ".", 1) && strncmp(direntry->d_name, "..", 2)) { + snprintf(tempfullpath, PHYS_FILENAME_SIZE, "%s/%s", dirname, direntry->d_name); + + if(lstat(tempfullpath, &fileinfo)!=0) + error("Unable to get file status.<BR>%s", strerror(errno)); + + if(S_ISDIR(fileinfo.st_mode)) { + fileio_re_rmdir(tempfullpath); + if(rmdir(tempfullpath)!=0) + error("Unable to remove directory...<BR>%s", strerror(errno)); + } else { + if(unlink(tempfullpath)!=0) + error("Unable to remove directory....<BR>%s", strerror(errno)); + } + + } + direntry=readdir(dir); + } + + closedir(dir); + +} + +// +// Delete Files Internal Routine +// Called by delete() +// +void fileio_delete(void) { + struct stat fileinfo; + + if(lstat(phys_filename, &fileinfo)==0) { + if(S_ISDIR(fileinfo.st_mode)) { + fileio_re_rmdir(phys_filename); + if(rmdir(phys_filename)!=0) + error("Unable to remove directory.<BR>%s", strerror(errno)); + } + else { + if(unlink(phys_filename)!=0) + error("Unable to remove file.<BR>%s", strerror(errno)); + } + } + +} + +// +// Delete File or Directory Handler - Allows Multiselect +// Called by cgiMain action=delete +// +void delete(void) { + int i; + char **responses; + + // Single + if(cgiFormStringMultiple("multiselect_filename", &responses) == cgiFormNotFound) { + checkfilename(NULL); + fileio_delete(); + } + // Multi + else { + for(i=0; responses[i]; i++) { + checkfilename(responses[i]); + fileio_delete(); + } + } + + redirect("?directory=%s&token=%s", virt_dirname, token); + +} + +// +// Move File/Directory Internal Routine +// Called by move() +// +void fileio_move(void) { + char final_destination[PHYS_DESTINATION_SIZE]; + struct stat fileinfo; + + // If moving file to a different directory we need to append the original file name + if( stat(phys_destination, &fileinfo)==0 && S_ISDIR(fileinfo.st_mode) ) + snprintf(final_destination, sizeof(final_destination), "%s/%s", phys_destination, virt_filename); + else + strncpy(final_destination, phys_destination, sizeof(final_destination)); + + if(rename(phys_filename, final_destination)!=0) + error("Unable to move file. <BR>[%d: %s]<BR>[SRC=%s] [DST=%s]", errno, strerror(errno), phys_filename, final_destination); +} + +// +// Move File/Directory - Handler +// Called by cgiMain action=move +// +void move(void) { + int i; + char **responses; + + + checkdestination(); + + // Single + if(cgiFormStringMultiple("multiselect_filename", &responses) == cgiFormNotFound) { + checkfilename(NULL); + fileio_move(); + } + // Multi + else { + for(i=0; responses[i]; i++) { + checkfilename(responses[i]); + fileio_move(); + } + } + + redirect("?highlight=%s&directory=%s&token=%s", virt_destination, virt_dirname, token); +} + + +// +// Recursive Dir Size +// +// WARNING: will not count directories starting with . +off_t du(char *pdir) { + DIR *dir; + struct dirent *direntry; + struct stat fileinfo; + char child[PHYS_DIRNAME_SIZE]; + off_t tot=0; + + if(lstat(pdir, &fileinfo)==0) + if(S_ISLNK(fileinfo.st_mode)) + return -1; + + dir=opendir(pdir); + if(dir) { + direntry=readdir(dir); + while(direntry) { + snprintf(child, PHYS_DIRNAME_SIZE, "%s/%s", pdir, direntry->d_name); + if(lstat(child, &fileinfo)==0) { + if(S_ISDIR(fileinfo.st_mode) && (direntry->d_name[0]!='.')) //TODO: count other ".files" except . & .. + tot+=du(child); + else + tot+=fileinfo.st_size; + } + direntry=readdir(dir); + } + closedir(dir); + } + + return tot; +} + +// +// Recursive folder list +// Called by for move_ui() +// +void re_dir_ui(char *vdir, int level) { + struct dirent **direntry; + struct stat fileinfo; + char child[VIRT_DIRNAME_SIZE]; + char phy_child[PHYS_DIRNAME_SIZE]; + char re_phys_dirname[PHYS_DIRNAME_SIZE]; + int n; + int nentr, e; + + snprintf(re_phys_dirname, PHYS_DIRNAME_SIZE, "%s/%s", HOMEDIR, vdir); + + if(strlen(re_phys_dirname)<2 || strlen(re_phys_dirname)>(PHYS_DIRNAME_SIZE-2)) + error("Invalid directory name."); + + if(regexec(&dotdot, re_phys_dirname, 0, 0, 0)==0) error("Invalid directory name."); + if(strlen(re_phys_dirname) < strlen(HOMEDIR)) error("Invalid directory name."); + + nentr=scandir(re_phys_dirname, &direntry, 0, alphasort); + + for(e=0; e<nentr; e++) { + snprintf(phy_child, PHYS_DIRNAME_SIZE, "%s/%s/%s", HOMEDIR, vdir, direntry[e]->d_name); + if((direntry[e]->d_name[0]!='.') && (lstat(phy_child, &fileinfo)==0) && S_ISDIR(fileinfo.st_mode)) { + + + snprintf(child, VIRT_DIRNAME_SIZE, "%s/%s", vdir, direntry[e]->d_name); + + fprintf(cgiOut, "<OPTION VALUE=\"%s\">", child); + + for (n=0; n<(level-1); n++) + fprintf(cgiOut, " "); + + fprintf(cgiOut, "⌊ %s</OPTION>\n", direntry[e]->d_name); + + // recurse + re_dir_ui(child,level+1); + } + free(direntry[e]); + } + +} + +// +// Scandir replacement function +// +int namesort(const void *d1, const void *d2) { + return(strcmp( ((ASDIR*)d1)->name, ((ASDIR*)d2)->name)); +} + +int rnamesort(const void *d1, const void *d2) { + return(strcmp( ((ASDIR*)d2)->name, ((ASDIR*)d1)->name)); +} + +int sizesort(const void *d1, const void *d2) { + if(((ASDIR*)d1)->size < ((ASDIR*)d2)->size) return -1; + else if(((ASDIR*)d1)->size > ((ASDIR*)d2)->size) return 1; + else return 0; +} + +int rsizesort(const void *d1, const void *d2) { + if(((ASDIR*)d1)->size > ((ASDIR*)d2)->size) return -1; + else if(((ASDIR*)d1)->size < ((ASDIR*)d2)->size) return 1; + else return 0; +} + +int timesort(const void *d1, const void *d2) { + if(((ASDIR*)d1)->mtime < ((ASDIR*)d2)->mtime) return -1; + else if(((ASDIR*)d1)->mtime > ((ASDIR*)d2)->mtime) return 1; + else return 0; +} + +int rtimesort(const void *d1, const void *d2) { + if(((ASDIR*)d1)->mtime > ((ASDIR*)d2)->mtime) return -1; + else if(((ASDIR*)d1)->mtime < ((ASDIR*)d2)->mtime) return 1; + else return 0; +} + +int asscandir(const char *dir, ASDIR **namelist, int (*compar)(const void *, const void *)) { + DIR *dirh; + ASDIR *names; + struct dirent *entry; + struct stat fileinfo; + char filename[PATH_MAX]; + int entries=0; + + dirh=opendir(dir); + if(dirh==NULL) + return -1; + + names=(ASDIR*)malloc(sizeof(ASDIR)); + if(names==NULL) + return -1; + + entry=readdir(dirh); + while(entry!=NULL) { + snprintf(filename, sizeof(filename), "%s/%s", dir, entry->d_name); + if(stat(filename, &fileinfo)!=0) + return -1; + + memset(&names[entries], 0, sizeof(ASDIR)); + strcpy(names[entries].name, entry->d_name); + names[entries].type=fileinfo.st_mode; + if(S_ISDIR(fileinfo.st_mode) && recursive_du) + names[entries].size=du(filename); + else + names[entries].size=fileinfo.st_size; + names[entries].atime=fileinfo.st_atime; + names[entries].mtime=fileinfo.st_mtime; + names[entries].rtime=fileinfo.st_ctime; + + names=(ASDIR*)realloc((ASDIR*)names, sizeof(ASDIR)*(entries+2)); + if(names==NULL) + return -1; + entries++; + entry=readdir(dirh); + } + closedir(dirh); + + if(entries) + qsort(&names[0], entries, sizeof(ASDIR), compar); + + *namelist=names; + return entries; +}
\ No newline at end of file diff --git a/icons/adn.gif b/icons/adn.gif Binary files differnew file mode 100755 index 0000000..46dc0b7 --- /dev/null +++ b/icons/adn.gif diff --git a/icons/arr.gif b/icons/arr.gif Binary files differnew file mode 100755 index 0000000..1c964c7 --- /dev/null +++ b/icons/arr.gif diff --git a/icons/aup.gif b/icons/aup.gif Binary files differnew file mode 100755 index 0000000..35c106d --- /dev/null +++ b/icons/aup.gif diff --git a/icons/clr.gif b/icons/clr.gif Binary files differnew file mode 100755 index 0000000..a5ef053 --- /dev/null +++ b/icons/clr.gif diff --git a/icons/delete.gif b/icons/delete.gif Binary files differnew file mode 100755 index 0000000..02327ed --- /dev/null +++ b/icons/delete.gif diff --git a/icons/dir.gif b/icons/dir.gif Binary files differnew file mode 100755 index 0000000..9b11305 --- /dev/null +++ b/icons/dir.gif diff --git a/icons/dir_up.gif b/icons/dir_up.gif Binary files differnew file mode 100755 index 0000000..3f8af1f --- /dev/null +++ b/icons/dir_up.gif diff --git a/icons/disk.gif b/icons/disk.gif Binary files differnew file mode 100644 index 0000000..c4fcab2 --- /dev/null +++ b/icons/disk.gif diff --git a/icons/edit.gif b/icons/edit.gif Binary files differnew file mode 100755 index 0000000..b610312 --- /dev/null +++ b/icons/edit.gif diff --git a/icons/exe.gif b/icons/exe.gif Binary files differnew file mode 100755 index 0000000..bf41d1a --- /dev/null +++ b/icons/exe.gif diff --git a/icons/ext.gif b/icons/ext.gif Binary files differnew file mode 100755 index 0000000..3da6e7e --- /dev/null +++ b/icons/ext.gif diff --git a/icons/gen.gif b/icons/gen.gif Binary files differnew file mode 100755 index 0000000..529d299 --- /dev/null +++ b/icons/gen.gif diff --git a/icons/home.gif b/icons/home.gif Binary files differnew file mode 100755 index 0000000..278dba7 --- /dev/null +++ b/icons/home.gif diff --git a/icons/img.gif b/icons/img.gif Binary files differnew file mode 100755 index 0000000..6940784 --- /dev/null +++ b/icons/img.gif diff --git a/icons/iso.gif b/icons/iso.gif Binary files differnew file mode 100755 index 0000000..4a43b2e --- /dev/null +++ b/icons/iso.gif diff --git a/icons/lck.gif b/icons/lck.gif Binary files differnew file mode 100755 index 0000000..0e54b95 --- /dev/null +++ b/icons/lck.gif diff --git a/icons/med.gif b/icons/med.gif Binary files differnew file mode 100755 index 0000000..e156fae --- /dev/null +++ b/icons/med.gif diff --git a/icons/mkdir.gif b/icons/mkdir.gif Binary files differnew file mode 100755 index 0000000..694b0af --- /dev/null +++ b/icons/mkdir.gif diff --git a/icons/mkfile.gif b/icons/mkfile.gif Binary files differnew file mode 100755 index 0000000..0565051 --- /dev/null +++ b/icons/mkfile.gif diff --git a/icons/move.gif b/icons/move.gif Binary files differnew file mode 100755 index 0000000..368f4db --- /dev/null +++ b/icons/move.gif diff --git a/icons/net.gif b/icons/net.gif Binary files differnew file mode 100755 index 0000000..321d83c --- /dev/null +++ b/icons/net.gif diff --git a/icons/off.gif b/icons/off.gif Binary files differnew file mode 100755 index 0000000..9010f2d --- /dev/null +++ b/icons/off.gif diff --git a/icons/pdf.gif b/icons/pdf.gif Binary files differnew file mode 100755 index 0000000..add1dc4 --- /dev/null +++ b/icons/pdf.gif diff --git a/icons/readonly.gif b/icons/readonly.gif Binary files differnew file mode 100755 index 0000000..2ac4e9e --- /dev/null +++ b/icons/readonly.gif diff --git a/icons/readwrite.gif b/icons/readwrite.gif Binary files differnew file mode 100755 index 0000000..0da0a44 --- /dev/null +++ b/icons/readwrite.gif diff --git a/icons/reload.gif b/icons/reload.gif Binary files differnew file mode 100755 index 0000000..1828b5c --- /dev/null +++ b/icons/reload.gif diff --git a/icons/rename.gif b/icons/rename.gif Binary files differnew file mode 100755 index 0000000..440a726 --- /dev/null +++ b/icons/rename.gif diff --git a/icons/time.gif b/icons/time.gif Binary files differnew file mode 100755 index 0000000..9dcdd12 --- /dev/null +++ b/icons/time.gif diff --git a/icons/txt.gif b/icons/txt.gif Binary files differnew file mode 100755 index 0000000..3a624ea --- /dev/null +++ b/icons/txt.gif diff --git a/icons/user.gif b/icons/user.gif Binary files differnew file mode 100755 index 0000000..6896994 --- /dev/null +++ b/icons/user.gif diff --git a/icons/ver.gif b/icons/ver.gif Binary files differnew file mode 100755 index 0000000..d3729de --- /dev/null +++ b/icons/ver.gif diff --git a/icons/wfmicon.gif b/icons/wfmicon.gif Binary files differnew file mode 100755 index 0000000..6314cd4 --- /dev/null +++ b/icons/wfmicon.gif diff --git a/icons/zip.gif b/icons/zip.gif Binary files differnew file mode 100755 index 0000000..c0252dd --- /dev/null +++ b/icons/zip.gif @@ -0,0 +1,381 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include <string.h> + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include <stdio.h> in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include <string.h> + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke <purschke@bnl.gov>. + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/mkicons.sh b/mkicons.sh new file mode 100755 index 0000000..1a594b8 --- /dev/null +++ b/mkicons.sh @@ -0,0 +1,11 @@ + +cat /dev/null > wfmiconres.h +echo "if (0) ;" > wfmicondis.h + +for file in icons/*.gif +do + ./bin2c -c $file wfmiconres.h + filename=$(basename $file) + filedef=$(echo $filename | tr "." "_") + echo "else if(strcmp(icon_name, \"${filename}\")==0) fwrite(${filedef}, sizeof(${filedef}), 1, cgiOut);" >> wfmicondis.h +done @@ -0,0 +1,462 @@ +#include "wfm.h" + +// +// Dispense Common HTML Header +// Used by all (?) functions that display HTML pages +// +void html_title(char *msg) { + fprintf(cgiOut, + HTML_HEADER + "<LINK REL=\"icon\" TYPE=\"image/gif\" HREF=\"%swfmicon.gif\">\n" + "<TITLE>%s : %s</TITLE>\n", + ICONSURL, TAGLINE, msg); // (strlen(virt_dirname)>0) ? ' ' : '/', TAGLINE, virt_dirname +} + + +// +// Dispense embedded icons +// Called on early action=icon +// +int icon(void) { + char icon_name[32]; + + cgiFormStringNoNewlines("name", icon_name, sizeof(icon_name)); + + fprintf(cgiOut, "Cache-Control: max-age=29030400, public\r\n"); + cgiHeaderContentType("image/gif"); + +#include "wfmicondis.h" + + exit(0); +} + + +// +// Display upload status in SHM via key_id +// Called by early action=upstat +// +void upload_status(void) { + int shm_key=0; + int shm_id=0; + char *shm_addr='\0'; + + if(cgiFormInteger("upload_id", &shm_key, 0) == cgiFormSuccess && shm_key) { + shm_id = shmget(shm_key, SHM_SIZE, 0666); + if(shm_id > 0) + shm_addr = shmat(shm_id, NULL, 0); + } + + fprintf(cgiOut, "Cache-Control: no-cache\r\n"); + cgiHeaderContentType("text/plain"); + + if(shm_id && shm_addr && *shm_addr) + fprintf(cgiOut, "%s\r\n", shm_addr); + else + fprintf(cgiOut, "-------\r\n"); + + if (shm_id && shm_addr) + shmdt(shm_addr); + + exit(0); +} + + +// +// Generate auth token +// Used by access_check() to compare tokens and login() to generate token from a web form +// +char *mktoken(char *str) { + md5_state_t state; + md5_byte_t digest[16]; + char *outstr; + int i; + + outstr=(char*) malloc((sizeof(digest)*2)+2); + memset(outstr, 0, (sizeof(digest)*2)+2); + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)str, strlen(str)); + md5_finish(&state, digest); + + for (i = 0; i < sizeof(digest); i++) + sprintf(outstr + i * 2, "%02x", digest[i]); + + return outstr; +} + +// +// WFM Login Procedure +// Called from WFM main procedure if no sufficient access permission available +// +void login(void) { + char username[64]; + char password[64]; + char token_inp[256]; + + cgiFormStringNoNewlines("username", username, sizeof(username)); // only used if JavaScript not + cgiFormStringNoNewlines("password", password, sizeof(password)); // available in the browser + + if(strlen(username)) { + snprintf(token_inp, sizeof(token_inp), "%s:%s:%s", cgiRemoteAddr, username, password); + redirect("?directory=%s&login=server&token=%s", virt_dirname, mktoken(token_inp)); // generate MD5 as if it was the client + } + else + login_ui(); // display actual login page, which normally generates token in JavaScript + +} + +// +// Access_check +// Called by cfg read routine during initialization +// +void access_check(char *access_string) { + char ipaddr[32]; + char user[32]; + char pass[32]; + char type[4]; + char token_inp[64]; + + memset(ipaddr, 0, sizeof(ipaddr)); + memset(user, 0, sizeof(user)); + memset(pass, 0, sizeof(pass)); + memset(type, 0, sizeof(type)); + + if(sscanf(access_string, "access-ip=%2s:%30s", type, ipaddr)==2) { + + if(ipaddr[0]=='*') { + if(strcmp(type, "ro")==0) + access_level=PERM_RO; + else if(strcmp(type, "rw")==0) + access_level=PERM_RW; + } + else if(strcmp(cgiRemoteAddr, ipaddr)==0) { + if(strcmp(type, "ro")==0) + access_level=PERM_RO; + else if(strcmp(type, "rw")==0) + access_level=PERM_RW; + } + + } + else if(sscanf(access_string, "access-user=%2[^':']:%30[^':']:%30s", type, user, pass)==3) { + users_defined=1; + + snprintf(token_inp, sizeof(token_inp), "%s:%s:%s", cgiRemoteAddr, user, pass); + // perform user auth by comparing user supplied token with system generated token + if(strcmp(mktoken(token_inp), token)==0) { + if(strcmp(type, "ro")==0) { + access_level=PERM_RO; + access_as_user=1; + strncpy(loggedinuser, user, sizeof(loggedinuser)); + } + else if(strcmp(type, "rw")==0) { + access_level=PERM_RW; + access_as_user=1; + strncpy(loggedinuser, user, sizeof(loggedinuser)); + } + } + } +} + + +// +// Check filename +// Should be used by every function that uses filename +// +void checkfilename(char *inp_filename) { + char temp_dirname[PHYS_FILENAME_SIZE]; + char *bname; + + if(inp_filename && strlen(inp_filename)) { + strncpy(virt_filename, inp_filename, VIRT_FILENAME_SIZE); + } + else if(cgiFormFileName("filename", virt_filename, VIRT_FILENAME_SIZE) == cgiFormSuccess) { + + } + else if(cgiFormStringNoNewlines("filename", virt_filename, VIRT_FILENAME_SIZE) == cgiFormSuccess) { + + } + else + error("No filename specified."); + + // We only want basename from the client! + bname=strrchr(virt_filename, '/'); + if(!bname) + bname=strrchr(virt_filename, '\\'); + + if(!bname) + bname=virt_filename; + else + (void) *bname++; + + strip(bname, VIRT_FILENAME_SIZE, VALIDCHRS); + strncpy(virt_filename, bname, VIRT_FILENAME_SIZE); + snprintf(phys_filename, PHYS_FILENAME_SIZE, "%s/%s", phys_dirname, virt_filename); + + if(!strlen(phys_filename) || strlen(phys_filename)>(PHYS_FILENAME_SIZE-2)) error("Invalid pfilename lenght"); + if(!strlen(virt_filename) || strlen(virt_filename)>(VIRT_FILENAME_SIZE-2)) error("Invalid vfilename lenght"); + if(regexec(&dotdot, phys_filename, 0, 0, 0)==0) error("Double dots in pfilename"); + if(regexec(&dotdot, virt_filename, 0, 0, 0)==0) error("Double dots in vfilename"); + + strncpy(temp_dirname, phys_filename, PHYS_FILENAME_SIZE); + if(strlen(dirname(temp_dirname)) < strlen(HOMEDIR)) error("Invalid directory name."); +} + +// +// Check destination +// Only used by move() +// +void checkdestination(void) { + int absolute_destination; + + cgiFormStringNoNewlines("destination", virt_destination, VIRT_DESTINATION_SIZE); + strip(virt_destination, VIRT_DESTINATION_SIZE, VALIDCHRS_DST); + cgiFormInteger("absdst", &absolute_destination, 0); // move operation relies on absolute paths + if(absolute_destination) + snprintf(phys_destination, PHYS_DESTINATION_SIZE, "%s/%s", HOMEDIR, virt_destination); + else + snprintf(phys_destination, PHYS_DESTINATION_SIZE, "%s/%s", phys_dirname, virt_destination); + + if(strlen(phys_destination)<1 || strlen(phys_destination)>(PHYS_DESTINATION_SIZE-2)) error("Invalid vfilename lenght"); + if(strlen(virt_destination)<1 || strlen(virt_destination)>(VIRT_DESTINATION_SIZE-2)) error("Invalid pfilename lenght"); + if(regexec(&dotdot, phys_destination, 0, 0, 0)==0) error("Double dots in pfilename"); + if(regexec(&dotdot, virt_destination, 0, 0, 0)==0) error("Double dots in vfilename"); +} + +// +// Check directory +// Only used by cgiMain during initialization +// +void checkdirectory(void) { + char temp[VIRT_DIRNAME_SIZE]; + + cgiFormStringNoNewlines("directory", virt_dirname, VIRT_DIRNAME_SIZE); + strip(virt_dirname, VIRT_DIRNAME_SIZE, VALIDCHRS_DST); + snprintf(phys_dirname, PHYS_DIRNAME_SIZE, "%s/%s", HOMEDIR, virt_dirname); + + if(strlen(phys_dirname)<2 || strlen(phys_dirname)>(PHYS_DIRNAME_SIZE-2)) + error("Invalid directory name."); + + if(regexec(&dotdot, phys_dirname, 0, 0, 0)==0) error("Invalid directory name."); + if(strlen(phys_dirname) < strlen(HOMEDIR)) error("Invalid directory name."); + + if(!strlen(virt_dirname)) strcpy(virt_dirname, "/"); + + // parent + strncpy(temp, virt_dirname, VIRT_DIRNAME_SIZE); + strncpy(virt_parent, dirname(temp), VIRT_DIRNAME_SIZE); +} + + +void tstart(void) { + gettimeofday(&mt, 0); + t1=mt.tv_sec+(mt.tv_usec/1000000.0); +} + +void tstop(void) { + gettimeofday(&mt, 0); + t2=mt.tv_sec+(mt.tv_usec/1000000.0); +} + +// strip unwanted characters from string +// deny all by default +// allow a=alpha n=numeric u=spaces as underscore +// use: allow="an.-@" for email +// use: allow="anu.-_!" for filenames +// ! is a cmd termination char +// use !! to allow only ! char +// use !a to allow only letter a +int strip(char *str, int len, char *allow) { + int n,a; + int alpha=0, number=0, spctou=0; + char *dst; + + if(!str || !strlen(str) || !allow || !strlen(allow)) + return -1; + + if(*allow == 'a') { + alpha=1; + allow++; + } + if(*allow == 'n') { + number=1; + allow++; + } + if(*allow == 'u') { + spctou=1; + allow++; + } + if(*allow == '!') { + allow++; + } + + dst=str; + + for(n=0; n<len && *str!='\0'; n++, str++) { + if(alpha && isalpha(*str)) + *(dst++)=*str; + else if(number && isdigit(*str)) + *(dst++)=*str; + else if(spctou && *str==' ') + *(dst++)='_'; + else if(strlen(allow)) + for(a=0; a<strlen(allow); a++) + if(*str==allow[a]) + *(dst++)=*str; + } + + *dst='\0'; + + return 0; //strlen(dst); +} + +// +// Byte unit printf +// +char *buprintf(float v, int bold) { + float size; + char unit; + char *buffer; + + if(v == -1) + return " "; + + if(v >= P1024_1 && v < P1024_2 ) { size = v / P1024_1; unit = 'K'; } + else if(v >= P1024_2 && v < P1024_3 ) { size = v / P1024_2; unit = 'M'; } + else if(v >= P1024_3 && v < P1024_4 ) { size = v / P1024_3; unit = 'G'; } + else { size = v; unit = ' '; } + + buffer=(char *)calloc(128, sizeof(char)); + + if(unit == 'G' && bold) + snprintf(buffer, 128, "<B> %5.1f %cB </B>", size, unit); + else if(unit == 'M' && bold) + snprintf(buffer, 128, "%5.1f %cB", size, unit); + else if(bold) + snprintf(buffer, 128, "<FONT COLOR=\"#909090\"> %5.1f %cB </FONT>", size, unit); + else + snprintf(buffer, 128, " %5.1f %cB", size, unit); + + return (char *)buffer; +} + +// +// redirect browser +// +void redirect(char *location, ...) { + va_list ap; + char buff[1024]; + + va_start(ap, location); + vsnprintf(buff, sizeof(buff), location, ap); + va_end(ap); + + cgiHeaderLocation(buff); +} + +// +// CGI entry +// +int cgiMain(void) { + char action[32]; + char ea[8]; + FILE *cfgfile; + char cfgname[128]; + char cfgline[256]; + char c_tagline[]="tagline="; + char c_homeurl[]="browser-url="; + char c_homedir[]="directory="; + char c_editdef[]="txt-default-edit=true"; + char c_editany[]="edit-any-file=true"; + char c_du[]="recursive-du=true"; + char c_access[]="access"; + + // early action - simple actions before cfg is read or access check performed + cgiFormStringNoNewlines("ea", ea, sizeof(ea)); + if(strcmp(ea, "icon")==0) icon(); + if(strcmp(ea, "upstat")==0) upload_status(); + + // normal initialization + tstart(); + + if(regcomp(&dotdot, "\\.\\.", REG_EXTENDED | REG_ICASE)!=0) + error("Regex compilation problem.<BR>%s", strerror(errno)); + + fprintf(cgiOut, "Cache-Control: max-age=0, private\r\nExpires: -1\r\n"); + + snprintf(ICONSURL, sizeof(ICONSURL), "%s?ea=icon&name=", cgiScriptName); + + + // read config file and authenticate + access_level=PERM_NO; // no access by default + access_as_user=0; + users_defined=0; + edit_by_default=0; // for .txt files + edit_any_file=0; + recursive_du=0; + + memset(HOMEDIR, 0, sizeof(HOMEDIR)); + memset(HOMEURL, 0, sizeof(HOMEURL)); + memset(TAGLINE, 0, sizeof(TAGLINE)); + + cgiFormStringNoNewlines("token", token, sizeof(token)); + snprintf(cfgname, sizeof(cfgname), "%s.cfg", basename(cgiScriptName)); + cfgfile=fopen(cfgname, "r"); + if(!cfgfile) + error("Unable to open configuration file %s.<BR>%s", cfgname, strerror(errno)); + + while(fgets(cfgline, sizeof(cfgline), cfgfile)) { + if((*cfgline==';')||(*cfgline=='/')||(*cfgline=='#')||(*cfgline=='\n')) continue; + else if(strncmp(cfgline, c_homedir, strlen(c_homedir))==0) strncpy(HOMEDIR, cfgline+strlen(c_homedir), sizeof(HOMEDIR)); + else if(strncmp(cfgline, c_homeurl, strlen(c_homeurl))==0) strncpy(HOMEURL, cfgline+strlen(c_homeurl), sizeof(HOMEURL)); + else if(strncmp(cfgline, c_tagline, strlen(c_tagline))==0) strncpy(TAGLINE, cfgline+strlen(c_tagline), sizeof(TAGLINE)); + else if(strncmp(cfgline, c_editdef, strlen(c_editdef))==0) edit_by_default=1; + else if(strncmp(cfgline, c_editany, strlen(c_editany))==0) edit_any_file=1; + else if(strncmp(cfgline, c_du, strlen(c_du))==0) recursive_du=1; + else if(strncmp(cfgline, c_access, strlen(c_access))==0) access_check(cfgline); + } + fclose(cfgfile); + + // remove newlines + if(strlen(HOMEDIR)>2) HOMEDIR[strlen(HOMEDIR)-1]='\0'; + if(strlen(HOMEURL)>2) HOMEURL[strlen(HOMEURL)-1]='\0'; + if(strlen(TAGLINE)>2) TAGLINE[strlen(TAGLINE)-1]='\0'; + + // do checks + if(strlen(HOMEDIR) < 4 || *HOMEDIR!='/') + error("Home directory not defined."); + + if(!strlen(TAGLINE)) + strcpy(TAGLINE, "Web File Manager"); + + checkdirectory(); + + // main routine - regular actions + cgiFormStringNoNewlines("action", action, sizeof(action)); + if(cgiFormSubmitClicked("multi_delete_prompt")==cgiFormSuccess && access_level >= PERM_RO) multiprompt_ui("delete"); + else if(cgiFormSubmitClicked("multi_move_prompt")==cgiFormSuccess && access_level >= PERM_RO) multiprompt_ui("move"); + else if(cgiFormSubmitClicked("upload")==cgiFormSuccess && access_level >= PERM_RW) receivefile(); + else if(strcmp(action, "sendfile")==0 && access_level >= PERM_RO) sendfile(); + else if(strcmp(action, "delete")==0 && access_level >= PERM_RW) delete(); + else if(strcmp(action, "delete_prompt")==0 && access_level >= PERM_RW) multiprompt_ui("delete"); + else if(strcmp(action, "move_prompt")==0 && access_level >= PERM_RW) multiprompt_ui("move"); + else if(strcmp(action, "rename_prompt")==0 && access_level >= PERM_RW) singleprompt_ui("move"); + else if(strcmp(action, "move")==0 && access_level >= PERM_RW) move(); + else if(strcmp(action, "edit")==0 && access_level >= PERM_RO) edit_ui(); + else if(strcmp(action, "edit_save")==0 && access_level >= PERM_RW) edit_save(); + else if(strcmp(action, "mkfile")==0 && access_level >= PERM_RW) mkfile(); + else if(strcmp(action, "mkfile_prompt")==0 && access_level >= PERM_RW) singleprompt_ui("mkfile"); + else if(strcmp(action, "mkdir")==0 && access_level >= PERM_RW) newdir(); + else if(strcmp(action, "mkdir_prompt")==0 && access_level >= PERM_RW) singleprompt_ui("mkdir"); + else if(strcmp(action, "about")==0 && access_level >= PERM_RO) about(); + else if(strcmp(action, "login")==0 ) login(); + else if( access_level >= PERM_RO) dirlist(); + else + if(users_defined) // if users present but supplied credentials didn't match, or credentials not specified + redirect("?action=login"); + else + error("Access Denied."); + + return 0; +} + + @@ -0,0 +1,28 @@ +# tagline or application name +tagline=Web File Manager + +# home directory +directory=/home/tenox/wfm10 + +# recursively summarize directory sizes - only enable if you have +# fast disk (SSD) or a small directory tree structure +#recursive-du=true + +# When clicking on txt file - edit instead of download by default +#txt-default-edit=true + +# Edit any file as it was txt +edit-any-file=false + +# optional browser url prefix - aka "external link" - if defined, file +# names will be glued to it giving option to be opened directly with the +# external link button without going through cgi routines +browser-url=http://x.x.x.x/files/ + +# access lists - ace type is either access-ip or access-user +# mixable, eg access-ip=ro:* with number of access-user=rw +# level is ro|rw, one host or username per line * denotes all hosts +# user is username:password combination +access-ip=ro:* +access-user=ro:guest:secret +access-user=rw:admin:password @@ -0,0 +1,149 @@ +#define VERSION "1.0.0" +#define copyright "<!-- WFM Version " VERSION ", Mountain View, CA, " __DATE__ " [" __TIME__ "] -->\n<!-- Copyright (c) 1994-2015 by Antoni Sawicki -->\n" + +#define CSS_STYLE \ + " <STYLE TYPE=\"text/css\"><!-- \n" \ + " A:link {text-decoration: none; color:#0000CE; } \n" \ + " A:visited {text-decoration: none; color:#0000CE; } \n" \ + " A:active {text-decoration: none; color:#FF0000; } \n" \ + " A:hover {text-decoration: none; color:#FF0000; } \n" \ + " body, td, th, input { font-family: Tahoma, Sans-Serif; font-size:11px; } \n" \ + " html, body { box-sizing: border-box; width:100%%; height:100%%; margin:0px; padding:0px; } \n" \ + " input { border-color:#000000; border-style:solid; }\n" \ + " img { vertical-align: middle; }\n" \ + " .tbr { border-width: 1px; border-style: solid solid solid solid; border-color: #AAAAAA #555555 #555555 #AAAAAA; }\n" \ + " .twh { width:100%%; height:100%%; }\n" \ + " --></STYLE>\n" + +#define HTML_HEADER \ + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n" \ + " \"http://www.w3.org/TR/html4/loose.dtd\">\n" \ + "<HTML>\n" \ + copyright \ + "<HEAD>\n" \ + " <META HTTP-EQUIV=\"Content-type\" CONTENT=\"text/html;charset=UTF-8\">\n" \ + CSS_STYLE + + +#define _FILE_OFFSET_BITS 64 + +#ifdef __sun__ +#define _POSIX_PTHREAD_SEMANTICS +#endif + +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <unistd.h> +#include <libgen.h> +#include <ctype.h> +#include <dirent.h> +#include <regex.h> +#include <stdarg.h> +#include <errno.h> +#include <time.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/shm.h> +//#include <sys/dir.h> +#include "md5.h" +#include "cgic.h" +#include "wfmiconres.h" + +#define VALIDCHRS "anu-_." +#define VALIDCHRS_DST "anu-_./" + + +#define P1024_1 1024.0f +#define P1024_2 1048576.0f +#define P1024_3 1073741824.0f +#define P1024_4 1099511627776.0f + +#define SHM_SIZE 16 + +#define NORMAL_COLOR "FFFFFF" +#define HIGH_COLOR "33CC33" +#define HL_COLOR "FFD700" + + +#define VIRT_DIRNAME_SIZE NAME_MAX +#define PHYS_DIRNAME_SIZE 1024 +#define VIRT_FILENAME_SIZE NAME_MAX +#define PHYS_FILENAME_SIZE 1280 +#define VIRT_DESTINATION_SIZE NAME_MAX +#define PHYS_DESTINATION_SIZE 1280 + +char virt_dirname[VIRT_DIRNAME_SIZE]; +char phys_dirname[PHYS_DIRNAME_SIZE]; +char virt_filename[VIRT_FILENAME_SIZE]; +char phys_filename[PHYS_FILENAME_SIZE]; +char virt_destination[VIRT_DESTINATION_SIZE]; +char phys_destination[PHYS_DESTINATION_SIZE]; +char virt_parent[VIRT_DIRNAME_SIZE]; + +char ICONSURL[1024]; +char HOMEDIR[1024]; +char HOMEURL[1024]; +char TAGLINE[1024]; + +char token[256]; +char loggedinuser[64]; + +regex_t dotdot; +int access_level; +int access_as_user; +int users_defined; +int edit_by_default; +int edit_any_file; +int recursive_du; + +double t1, t2; +struct timeval mt; + +enum { FALSE, TRUE }; +enum { PERM_NO, PERM_RO, PERM_RW }; + +typedef struct asdir_ { + char name[NAME_MAX]; + mode_t type; + off_t size; + time_t atime, mtime, rtime; +} ASDIR; + +int namesort(const void *, const void *); +int rnamesort(const void *, const void *); +int sizesort(const void *, const void *); +int rsizesort(const void *, const void *); +int timesort(const void *, const void *); +int rtimesort(const void *, const void *); +int asscandir(const char *, ASDIR **, int (*compar)(const void *, const void *)); + +void error(char *, ...); +void redirect(char *, ...); +char *buprintf(float, int); +int strip(char *, int, char *); +void checkfilename(char *); +void checkdestination(void); +void mkfile(void); +void newdir(void); +void edit_save(void); +void delete(void); +void move(void); +void dirlist(void); +void edit_ui(void); +void rename_ui(void); +void mkdir_ui(void); +void mkfile_ui(void); +void multiprompt_ui(char *); +void about(void); +void sendfile(void); +void receivefile(void); +off_t du(char *); +void re_dir_ui(char *, int); +void login_ui(void); +void tstop(void); +void html_title(char *); +void singleprompt_ui(char *); + |