/* 
 * This is a minimal lua interpreter designed to be run as a CGI script from
 * a web server.  A global table named CGI is created. Then, we query the
 * environment for several CGI specific variables, and use them to set 
 * members in that table. (see below for the mapping). Once that is done, 
 * the query string is collected, either from the environment or from 
 * standard input (depending on form type) and parsed out into a series of 
 * key/value pairs, which are also stored in a key named parsedQueryString.
 *
 * For data in multipart/form-data type forms, regular form fields are
 * stored similarly to above, but in a table named parsedMultipartData.
 * Fields in the form that represented files are stored in a table named
 * parsedFiles, as an array. Each entry in the array is a table that 
 * contains the field name, the filename, the size of the data, and the
 * data itself. 
 *
 * This can handle both GET and POST type forms only. For POST forms, only
 * application/x-www-form-urlencoded and multipart/form-data encodings are
 * supported. Things are easily extensible to other encoding types, or special
 * custom form types, if that sort of thing is required.
 *
 * I should note that most of the CGI environment variables that I have 
 * included came from a CGI spec. The rest, like DOCUMENT_ROOT, I happened
 * to notice during testing, and they seemed interesting. So I'm not sure
 * if these are supposed to be set, or if this is just an apache-specific
 * thing. Also, when pulling in environment variables, variables that seem
 * to be set to nothing are skipped. So, if QUERY_STRING is "", then it
 * won't show up in the CGI table. I'm not sure if there's some valid reason
 * for QUERY_STRING to be set when there isn't any query string, since you
 * can't do anything, so I opted to skip that out. This is easily modifiable
 * if that's somehow taboo or something.
 *
 * Anyone is free to use, modify and distribute this source code, so long as
 * you don't use it as-is and pretend that you wrote it, or make minor
 * modifications and give yourself all the credit, or charge anyone money
 * to get a copy of it (short of a reasonable transfer fee like the cost of
 * a floppy disk), or get mad at me if it doesn't work for you or damages
 * something. Note this means you can use this without giving me any credit,
 * if that's your bag.
 *
 * Oh yeah, and it's written to use lua 4.0 from the brilliant lua team.
 * You can get your own copy of lua at http://www.lua.org. When I wrote this,
 * I slightly modified my version of lua 4.0 in the following ways:
 * 
 * - Removed the function tmpnam by commenting the relevant lines out of the
 *   relevant files. The god damn GNU linker is such a whiney bitch I couldn't 
 *   take it anymore. I didn't feel like modifying things to use the function
 *   it seems to want me to use. This is really a personal preference anyways.
 *
 * - I modified luaB__ALERT andd errormessage so that they print the string you 
 *   pass in to stdout instead of stderr. This allows you to capture scripting
 *   errors in your browser. If you don't do this, then your errors generally go
 *   to some server-specific place. For apache, this is the apache error_log file.
 *
 * - Merged the defintions of the "date" and "time" function from the 4.1
 *   version into the 4.0 version, because my web site requires them to work
 *   in the new way. If you're linking against the 4.1 version, this is of
 *   course no problem. (UPDATE: As of 1.0.3, these two functions have been placed
 *   into this file directly)
 *
 * These modifications aren't neccesary for this code to work, though.
 *
 * This really is just a minimal lua parser. Ideally, some other things added to
 * this would be nice. Among them are (in no particular order):
 *
 *   - Modify all error output so that it's generated as HTML and easier to read
 *
 *   - Add some HTML specific functions to make generation of things easier. An
 *     example would be to generate a select form tag from a table.
 *
 *   - Support cookies, by parsing and saving them the same sort of way that
 *     the query string is currently handled.
 *
 * These things, and maybe others, will be added if I ever need them, or find 
 * myself wanting to code them up, or if someone donates the code.
 *
 * The date and time functions below are taken from the 4.1 version of lua. These
 * aren't part of the standard lua 4 distribution, but some stuff I had used them.
 * I also got the jpeg size code from some place on the internet, though I can't 
 * remember where at the moment. I butchered it a bit to fit into this concept of a
 * single C file with no external headers needed. Anyone know where I got this from?
 * I claim no credit for anything but putting it in here so that my web pages would
 * load faster.
 *
 * To compile this, you'd first want to install lua 4.x , and then do something like:
 *     gcc lua-web.c -o lua-web -llua -llualib -lm
 *
 * Adjust accordingly if you need different libraries or to add lib or include paths.
 *
 * To use, you would put the generated executable into a cgi-bin directory, make it
 * executable, and then call it from a script. Here's what a sample might look like.
 * (Note that the script has to be set executable as well):
 *
 * ------ Start Test Script ------
 *
 * #!./lua-web
 *
 * foreach (CGI , print)
 * foreach (CGI.parsedQueryString , print)
 * foreach (CGI.parsedMultipartData , print)
 * foreach (CGI.parsedFiles , print)
 * print (CGI.multipartError)
 * print (CGI.multipartCancel)
 *
 * ------ Stop  Test Script ------
 *
 * Of course, you could put lua-web in it's own path and update the first line accordingly.
 * Note of course that except for the first print line, an error will result if the type
 * of data you are trying to print isn't there, because keys are only populated to the table
 * when data for them is aquired. Thus you should always check that the data you want exists
 * before you continue
 * Also, unless the URL of the script is submitted with some sort of query (as a form, or something
 * like /myscript.cgi?arg1=1&arg2=2), the second line will cause an error, because parsedQueryString is
 * only set if there was a query string.
 *
 * Likewise, the third line will cause an error if the form was not submitted as multipart/form-data.
 *
 * Data decoded as multipart/form-data uses different fields in the CGI table:
 *
 * parsedMultipartData - Like parsedQueryString
 * parsedFiles         - An array of tables for files that were submitted. Each table has 4 keys:
 *                       formName, length, filename, data. Hopefully self explanatory. filename might
 *                       have path information, and it's format depends on the host.
 * multipartError      - If there is no parsedMultipartData or parsedFiles, this might exist as a string
 *                       error message to tell you why. Usually, lack of memory or improperly formatted
 *                       data are to blame.
 * multipartCancel     - If this has a value, it means that there is no parsed data, and the reason is that
 *                       the form submission was cancelled by the user before it was fully sent.
 * multipartTooBig     - If this has a value, it means that there is no parsed data, and the reason 
 *                       is that the submitted data was too big. See  MAX_MULTIPART_DATA.
 *
 * That plus knowledge of lua, and perusal of the code, should be enough to get you going.
 * 
 * Revision History
 *     July 22 2001 - Initial Revision - Terence Martin (poindexter@nurdz.com)
 *     Feb   9 2002 - Function to return the size of a jpeg - Terence Martin (poindexter@nurdz.com)
 *     Oct   8 2003 - Add in functions from lua-extended, to give dircontents, stat,etc. Also date
 *                    and time, in case this gets linked against normal lua4 libs. - Terence Martin (poindexter@nurdz.com)
 *     Nov  18 2003 - Modify header of this file to have more documentation for this, and initial posting to the web - Terence Martin (poindexter@nurdz.com)
 *     Mar   2 2004 - Bump version to 1.1.0 and add in handling for multipart/form-data. - Terence Martin (poindexter@nurdz.com)
 */

/* The version number of the code. This *MUST* be changed every time this code is altered! */
#define LUAWEB_VERSION "LuaWeb 1.1.0"
 
/* When this is defined, functions that allow you to get the contents of a directory, stat files, etc
 * are added into the interpreter. Turn this off if you're compiling this on a host that doesn't
 * have those functions. Or rewite this and let me know what you did, if you need to fix anything. This
 * has only been tested on linux.
 */
#define USE_EXTENSIONS
 
#ifdef USE_EXTENSIONS
#define __USE_BSD
#include <time.h>
#include <dirent.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libgen.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>


/* These are used in the code that gets the size of a jpeg. */
#define readbyte(a,b) do if (((a) = getc((b))) == EOF) { fclose (b); return 0; } while (0)
#define readword(a,b) do { int cc_=0,dd_=0; \
                           if((cc_=getc((b))) == EOF || (dd_=getc((b))) == EOF) { fclose (b); return 0; } \
                           (a) = (cc_<<8) + (dd_); \
                         } while(0)

/* A simple function that tells you if there was no parameter given. */
#define lua_isnoneornil(L, n)   (lua_type(L,n) == LUA_TNIL || lua_type(L,n) == LUA_TNONE)

/* These define how big a key and a value in a Query String can be. Keys and 
 * values larger than these sizes get truncated during parsing. Generally
 * speaking, low values are good for keys because they're not likely to be
 * very long, while value should be fairly big because form fields like text
 * area objects can have a lot of text in them.
 *
 * The sizes here don't take NULL termination into account, so the value here
 * and the maxiumum length of the key/value is identical. 
 *
 * These only have an effect when we are parsing URL encoded data.*/
#define MAX_KEY_LEN           256
#define MAX_VALUE_LEN (64 * 1024)

/* Define the maximum multipart data a single form submission
 * can contain. This includes the data and the mime headers
 * and such. This roughly limits the maximum size of file that 
 * can be uploaded, while leaving space for the headers and other
 * form components.
 *
 * This implicitly limits how big keys and values in the submitted data
 * can be. As long as the encoded data fits into this buffer, we will
 * have no problems with the data.
 *
 * NOTE: Actual memory required is roughly double what is set here (at worst).
 * This is because we store a copy of the data before we give it to
 * lua, which will make it's own copy and allocate accordingly.
 *
 * Here we're setting 1.5 meg, which seems like a good compromise. */
#define MAX_MULTIPART_DATA ((1024 * 1024) + (1024 * 512))

/* Definitions of the content type encodings that we know about.
 *
 * url encoded data is either attached to the end of the URL submitted
 * (GET forms), or written to our stdin for us by the server (POST forms). 
 *
 * multipart data is a mime type encoding. This is only ever sent via
 * a POST form. This form of encoding allows uploading of files. */
#define URL_ENCODED_DATA "application/x-www-form-urlencoded"
#define MULTIPART_DATA   "multipart/form-data"

/* This structure represents a piece of data parsed from a form
 * submission of type MULTIPART_DATA. Name and value are as you
 * would expect. The data in the value is null terminated. The
 * length of the data is stored in the field length.
 *
 * In cases where the part was a file upload submission, the filename
 * field is the name supplied by the browser, which may or may not have
 * some sort of path information in it. If there is path information,
 * it could be windows or linux style path information. 
 *
 * The data stored in instances of these structures are pointers into
 * the actual received multipart data, which is modified as it is parsed
 * to make sure that the data is always NULL terminated. 
 *
 * Note that when there is a filename, the data is an uploaded file, which
 * may be binary and contain embedded null bytes. This is why the length
 * field is provided.*/
typedef struct MultipartData_TAG
{
    char *fieldName;
    char *fieldFilename;
    char *fieldValue;
    int  fieldLength;

    struct MultipartData_TAG *next;
} MultipartData;
 
/* This defines all of the environment variables that will be pulled and
 * placed into the CGI table upon startup. The first is the actual name of
 * the environment variable, and the second is it's internal name in the
 * table. Variables that don't exist at the time the script executes will be
 * silently dropped. Terminate the list with NULL. 
 *
 * Others will perhaps want to modify this so that the key name and the
 * environment name are the same. I consider this a matter of personal
 * preference, and even though I think constant defines should have
 * CAPITAL_NAMES, it just seems wrong to do that in this context. */
const char *vars[] = {
    "AUTH_TYPE"            , "authType"           ,
    "CONTENT_LENGTH"       , "contentLength"      ,
    "CONTENT_TYPE"         , "contentType"        ,
    "DOCUMENT_ROOT"        , "documentRoot"       ,
    "GATEWAY_INTERFACE"    , "gatewayInterface"   ,
    "HTTP_ACCEPT"          , "httpAccept"         ,
    "HTTP_ACCEPT_ENCODING" , "httpAcceptEncoding" ,
    "HTTP_ACCEPT_LANGUAGE" , "httpAcceptLanguage" ,
    "HTTP_HOST"            , "httpHost"           ,
    "HTTP_USER_AGENT"      , "httpUserAgent"      ,
    "PATH_INFO"            , "pathInfo"           ,
    "PATH_TRANSLATED"      , "pathTranslated"     ,
    "QUERY_STRING"         , "queryString"        ,
    "REMOTE_ADDR"          , "remoteAddr"         ,
    "REMOTE_HOST"          , "remoteHost"         ,
    "REMOTE_IDENT"         , "remoteIdent"        ,
    "REMOTE_USER"          , "remoteUser"         ,
    "REQUEST_METHOD"       , "requestMethod"      ,
    "REQUEST_URI"          , "requestURI"         ,
    "SCRIPT_FILENAME"      , "scriptFilename"     ,
    "SCRIPT_NAME"          , "scriptName"         ,
    "SERVER_ADDR"          , "serverAddr"         ,
    "SERVER_ADMIN"         , "serverAdmin"        ,
    "SERVER_NAME"          , "serverName"         ,
    "SERVER_PORT"          , "serverPort"         ,
    "SERVER_PROTOCOL"      , "serverProtocol"     ,
    "SERVER_SIGNATURE"     , "serverSignature"    ,
    "SERVER_SOFTWARE"      , "serverSoftware"     ,
    NULL
}; 

/* Checks the size of a jpeg file, returning the dimensions in the values
 * given. If it works, 1 is returned, else 0 is returned. */
int jpeg_size (char *filename, int *image_width , int *image_height) 
{
    int marker=0;
    int dummy=0;
    FILE *infile;

    infile = fopen (filename , "rb");
    if (infile == NULL)
        return 0;
  
    /* Eat the file header. Leave if we don't get the bytes we think we
     * should. */
    if (getc (infile) != 0xFF || getc (infile) != 0xD8)
    {
        fclose (infile);
        return 0;
    }

    /* Loop forever reading bytes, because jpeg files store data in chunks,
     * like wave files, and the chunk with the dimensions aren't neccesarily
     * at the beginning. */
    while (1)
    {
        int discarded_bytes=0;
        
        /* Read a marker. */
        readbyte (marker,infile);

        /* If this marker isn't 0xFF, then throw away bytes until we find
         * one that is. */
        while (marker != 0xFF) 
        {
            discarded_bytes++;
            readbyte (marker,infile);
        }
        
        /* Now, read bytes until we hit a 0xFF again. */
        do 
            readbyte(marker,infile); 
        while (marker == 0xFF);

        /* If we had to discard any bytes to do that, then this isn't a valid
         * jpeg file. */
        if (discarded_bytes != 0) 
        {
            fclose (infile);
            return 0;
        }
   
        /* Check the marker to see what's what. */
        switch (marker) 
        {
            /* Chunk types that carry dimension information. */
            case 0xC0: case 0xC1: case 0xC2: case 0xC3: case 0xC5: case 0xC6:
            case 0xC7: case 0xC9: case 0xCA: case 0xCB: case 0xCD: case 0xCE:
            case 0xCF: 
            {
                /* Get chumk parameter length, a filler byte, then we can
                 * get the dimensions out, and return. */
                readword (dummy , infile);    
                readbyte (dummy , infile);
                readword ((*image_height) , infile);
                readword ((*image_width) , infile);
                fclose (infile);

                return 1;
            }

            /* If we hit these, that's it for us. I'm not sure, but I think that
             * they indicate the end of the file, and so if we've hit them,
             * we already know that we can't get any info out. */
            case 0xDA:
            case 0xD9:
                fclose (infile);
                return 0;

            /* All other chunk types. Just read and discard. */
            default: 
            {
                int length;
        
                /* Get chunk length. It includes itself, so it's always two
                 * bigger than you'd think. */
                readword (length,infile);

                /* Chunk has no length? Then leave. */
                if (length < 2)
                {
                    fclose (infile);
                    return 0;
                }

                /* Take the length of the length into account, then throw the
                 * rest of the bytes away. */
                length -= 2;
                while (length > 0) 
                {
                    readbyte (dummy, infile);
                    length--;
                }
            }
            break;
        }
    }
}

#ifdef USE_EXTENSIONS

#define LUAEXTENSIONS_VERSION "1.0.0"

/* A simple helper, it assumes the top of the stack is a table, and adds the
 * given number to the table with the given key name. */
static void helper_addnumber (lua_State *L , long value , char *keyName)
{
    /* Push the key and value, and then leave. */
    lua_pushstring (L , keyName);
    lua_pushnumber (L , value);
    lua_settable (L , -3);
}


/* dircontents ([dirname])
 *
 * Returns to you a table contains the contents of the passed in directory, or 
 * the contents of the current directory, if no directory name is given. If 
 * there is an error, nil and an error message are returned instead. */
static int ext_dircontents (lua_State *L) 
{
    const char *startDir = luaL_opt_lstr (L , 1 , "." , NULL);
    struct dirent **nameList;
    int retVal,i;

    /* Scan the directory given to attempt to get the contents */
    retVal = scandir (startDir , &nameList , NULL , alphasort);

    /* If the return value is -1, it means that something has gone
     * horribly wrong. So, return back nil and an error message. */
    if (retVal == -1)
    {
        lua_pushnil (L);
        lua_pushstring (L , strerror (errno));
        return 2;
    }

    /* We got something. It might be nothing, though. Put a table on the stack,
     * and then insert all the items. */
    lua_newtable (L);

    for (i = 0 ; i < retVal ; i++)
    {
        /* Push the name of this item onto the stack, then free it. Finally,
         * set it into the table at the current index. */
        lua_pushstring (L , nameList[i]->d_name);
        free (nameList[i]);
        lua_rawseti (L , -2 , i + 1);
    }

    /* Now that we're done, throw the name list away. */
    free (nameList);

    /* For a bit of a speedup, insert the total amount of entries as "n".
     * the function getn () checks for this value before counting. */
    lua_pushstring (L , "n");
    lua_pushnumber (L , retVal);
    lua_settable (L , -3);

    /* Return the table back */
    return 1;
}

/* This is a helper for ext_stat. It takes a time value and a key name,
 * assumes that the top of the stack is a table, and inserts into that
 * table a table with the given key. The inserted table is the date, broken
 * apart into constituent parts. */
static void helper_stattime (lua_State *L , time_t timeVal , char *keyName)
{
    /* Insert the key. */
    lua_pushstring (L , keyName);

    /* Call the date function, telling it to return a table for the date
     * that we were given. */
    lua_getglobal (L , "date");
    lua_pushstring (L , "*t");
    lua_pushnumber (L , timeVal);
    lua_call (L , 2 , 1);

    /* Now we can set this key into the table. */
    lua_settable (L , -3);
}

/* This is a helper for ext_stat. It takes a group ID and a key, and sets
 * into the table at the top of the stack a key/value pair that is the name
 * of that group. */
static void helper_groupid (lua_State *L , gid_t gid , char *keyName)
{
    struct group *grInfo;

    /* Push the key. */
    lua_pushstring (L , keyName);

    /* Look up the group name, if we can. If it works, push the group
     * name. If it doesn't, push the group number instead. */
    grInfo = getgrgid (gid);
    if (grInfo == NULL)
        lua_pushnumber (L , gid);
    else
        lua_pushstring (L , grInfo->gr_name);

    /* Go */
    lua_settable (L , -3);
}

/* This is a helper for ext_stat. It sets the given key with the type of
 * the file, as given by the passed in mode paramter. */
static void helper_filetype (lua_State *L , mode_t mode , char *keyName)
{
    /* Push the key */
    lua_pushstring (L , keyName);

    /* Push the type */
    if (S_ISREG  (mode)) lua_pushstring (L , "file"); else
    if (S_ISDIR  (mode)) lua_pushstring (L , "directory"); else
    if (S_ISCHR  (mode)) lua_pushstring (L , "charDevice"); else
    if (S_ISBLK  (mode)) lua_pushstring (L , "blockDevice"); else
    if (S_ISFIFO (mode)) lua_pushstring (L , "fifo"); else
    if (S_ISLNK  (mode)) lua_pushstring (L , "link"); else
    if (S_ISSOCK (mode)) lua_pushstring (L , "socket");  else
        lua_pushstring (L , "unknown");

    /* Go */
    lua_settable (L , -3);
}

/* This is a helper for ext_stat. It takes a user ID and a key, and sets
 * into the table at the top of the stack a key/value pair that is the name
 * of that user. */
static void helper_userid (lua_State *L , uid_t uid , char *keyName)
{
    struct passwd *usInfo;

    /* Push the key. */
    lua_pushstring (L , keyName);

    /* Look up the user name, if we can. If it works, push the user
     * name. If it doesn't, push the user number instead. */
    usInfo = getpwuid (uid);
    if (usInfo == NULL)
        lua_pushnumber (L , uid);
    else
        lua_pushstring (L , usInfo->pw_name);

    /* Go */
    lua_settable (L , -3);
}

/* This is a helper for ext_stat. It takes 3 bools that indicate read/write/exec
 * permissions, and then creates a table with the given keyname that has keys
 * for those three things. This table is set into the table on the top of the
 * stack. */
static void helper_permissions (lua_State *L , int r , int w , int x , char *keyName)
{
    /* Push the key, then add a new table. */
    lua_pushstring (L , keyName);
    lua_newtable (L);

    /* For each permission, add an entry to the table, if it is set. */
    if (r) helper_addnumber (L , 1 , "read");
    if (w) helper_addnumber (L , 1 , "write");
    if (x) helper_addnumber (L , 1 , "execute");

    /* Set it in */
    lua_settable (L , -3);
}

/* stat (filename)
 *
 * Returns back a table that contains information about the given file, or
 * nil and an error message, if something goes horribly wrong. */
static int ext_stat (lua_State *L)
{
    /* Get the filename that we will stat */
    const char *fname = luaL_check_lstr (L , 1 , NULL);
    int doLinkStat;
    int result;
    struct stat statBuf;

    /* Determine if we will lstat or not. The second paramter has to be
     * non-nil to lstat. */
    if (lua_isnoneornil (L , 2))
        doLinkStat = 0;
    else
        doLinkStat = 1;

    /* Attempt the stat. If it doesn't work, then bail out. */
    if (doLinkStat)
        result = lstat (fname , &statBuf);
    else
        result = stat (fname , &statBuf);

    if (result != 0)
    {
        lua_pushnil (L);
        lua_pushstring (L , strerror (errno));
        return 2;
    } 

    /* Must have worked. Put a table on the stack so we can set stuff
     * into it. */
    lua_newtable (L);

    /* Insert the stat type that we did. This is primarily for debugging, but
     * someone might want to know after the fact the type, since with a
     * non-lstat, you can never tell if anything is a link. */
    lua_pushstring (L , "statType");
    if (doLinkStat)
        lua_pushstring (L , "lstat");
    else
        lua_pushstring (L , "stat");
    lua_settable (L , -3);

    /* Insert the size of the file */
    helper_addnumber (L , statBuf.st_size , "size");

    /* Insert the user and group ID */
    helper_addnumber (L , statBuf.st_uid , "ownerUID");
    helper_userid    (L , statBuf.st_uid , "user");
    helper_addnumber (L , statBuf.st_gid , "ownerGID");
    helper_groupid   (L , statBuf.st_gid , "group");
   
    /* Insert the access time */
    helper_stattime (L , statBuf.st_atime , "accessTime");

    /* Insert the modification time */
    helper_stattime (L , statBuf.st_mtime , "modificationTime");

    /* Insert the change time */
    helper_stattime (L , statBuf.st_ctime , "changeTime");

    /* Insert the file type */
    helper_filetype (L , statBuf.st_mode , "type");

    /* Insert the owner permissions. */
    helper_permissions (L , statBuf.st_mode & S_IRUSR ,
                            statBuf.st_mode & S_IWUSR , 
                            statBuf.st_mode & S_IXUSR , "ownerPermissions");

    /* Insert the group permissions. */
    helper_permissions (L , statBuf.st_mode & S_IRGRP ,
                            statBuf.st_mode & S_IWGRP , 
                            statBuf.st_mode & S_IXGRP , "groupPermissions");
    
    /* Insert the owner permissions. */
    helper_permissions (L , statBuf.st_mode & S_IROTH ,
                            statBuf.st_mode & S_IWOTH , 
                            statBuf.st_mode & S_IXOTH , "otherPermissions");

    /* If the setUID bit is set, then add that in. Do the same for setGID
     * and sticky. */
    if (statBuf.st_mode & S_ISUID) helper_addnumber (L , 1 , "setUID");
    if (statBuf.st_mode & S_ISGID) helper_addnumber (L , 1 , "setGID");
    if (statBuf.st_mode & S_ISVTX) helper_addnumber (L , 1 , "isSticky");

    /* Return the table back */
    return 1;
}

/* readlink (filename)
 *
 * Assumes that the passed in filename is a symlink. This will query to see
 * where the link is pointing. That is returned. Otherwise, nil and an error
 * message is returned. */
static int ext_readlink (lua_State *L)
{
    /* Get the filename that we will check */
    const char *fname = luaL_check_lstr (L , 1 , NULL);
    char fbuffer[256];
    int result;

    /* Do the call, check for error. */
    if ((result = readlink (fname , fbuffer , sizeof (fbuffer) - 1)) == -1)
    {
        lua_pushnil (L);
        lua_pushstring (L , strerror (errno));
        return 2;
    }

    /* Put the string. Since it's not null terminated, tell the other side how
     * big it is. */
    lua_pushlstring (L , fbuffer , result);
    return 1;
}

/* dirname (filename)
 * 
 * Returns the path portion of the given filename. */
static int ext_dirname (lua_State *L)
{
    /* Get the filename that we will check */
    const char *fname = luaL_check_lstr (L , 1 , NULL);
    char buffer[256];

    /* Make a copy */
    strncpy (buffer , fname , sizeof (buffer) - 1);
    buffer[sizeof (buffer) - 1] = '\0';

    /* Do the job and return the string. */
    lua_pushstring (L , dirname (buffer));
    return 1;
}

/* basename (filename)
 *
 * Returns the filename portion of the given filename. */
static int ext_basename (lua_State *L)
{
    /* Get the filename that we will check */
    const char *fname = luaL_check_lstr (L , 1 , NULL);
    char buffer[256];

    /* Make a copy */
    strncpy (buffer , fname , sizeof (buffer) - 1);
    buffer[sizeof (buffer) - 1] = '\0';

    /* Do the job and return the string. */
    lua_pushstring (L , basename (buffer));
    return 1;
}

/*
** {======================================================
** Time/Date operations
** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,
**   wday=%w+1, yday=%j, isdst=? }
** =======================================================
*/

static void setfield (lua_State *L, const char *key, int value) {
  lua_pushstring(L, key);
  lua_pushnumber(L, value);
  lua_rawset(L, -3);
}


static int getfield (lua_State *L, const char *key, int d) {
  int res;
  lua_pushstring(L, key);
  lua_gettable(L, -2);
  if (lua_isnumber(L, -1))
    res = (int)(lua_tonumber(L, -1));
  else {
    if (d == -2)
    {
      luaL_verror(L, "field `%s' missing in date table", key);
      return 0;
    }
    res = d;
  }
  lua_pop(L, 1);
  return res;
}

static int io_date (lua_State *L) {
  const char *s = luaL_opt_string(L, 1, "%c");
  time_t t = (time_t)(luaL_opt_number(L, 2, -1));
  struct tm *stm;
  if (t == (time_t)(-1))  /* no time given? */
    t = time(NULL);  /* use current time */
  if (*s == '!') {  /* UTC? */
    stm = gmtime(&t);
    s++;  /* skip `!' */
  }
  else
    stm = localtime(&t);
  if (stm == NULL)  /* invalid date? */
    lua_pushnil(L);
  else if (strcmp(s, "*t") == 0) {
    lua_newtable(L);
    setfield(L, "sec", stm->tm_sec);
    setfield(L, "min", stm->tm_min);
    setfield(L, "hour", stm->tm_hour);
    setfield(L, "day", stm->tm_mday);
    setfield(L, "month", stm->tm_mon+1);
    setfield(L, "year", stm->tm_year+1900);
    setfield(L, "wday", stm->tm_wday+1);
    setfield(L, "yday", stm->tm_yday+1);
    setfield(L, "isdst", stm->tm_isdst);
  }
  else {
    char b[256];
    if (strftime(b, sizeof(b), s, stm))
      lua_pushstring(L, b);
    else
    {
      luaL_verror(L, "invalid `date' format");
      return 0;
    }
  }
  return 1;
}

static int io_time (lua_State *L) {
  if (lua_isnoneornil(L, 1))  /* called without args? */
    lua_pushnumber(L, time(NULL));  /* return current time */
  else {
    time_t t;
    struct tm ts;
    luaL_checktype(L, 1, LUA_TTABLE);
    lua_settop(L, 1);  /* make sure table is at the top */
    ts.tm_sec = getfield(L, "sec", 0);
    ts.tm_min = getfield(L, "min", 0);
    ts.tm_hour = getfield(L, "hour", 12);
    ts.tm_mday = getfield(L, "day", -2);
    ts.tm_mon = getfield(L, "month", -2)-1;
    ts.tm_year = getfield(L, "year", -2)-1900;
    ts.tm_isdst = getfield(L, "isdst", -1);
    t = mktime(&ts);
    if (t == (time_t)(-1))
      lua_pushnil(L);
    else
      lua_pushnumber(L, t);
  }
  return 1;
}


static int io_difftime (lua_State *L) {
  lua_pushnumber(L, difftime((time_t)(luaL_check_number(L, 1)),
                             (time_t)(luaL_opt_number(L, 2, 0))));
  return 1;
}


static const struct luaL_reg extendedlib[] = {
  {"dircontents",     ext_dircontents},
  {"stat",            ext_stat},
  {"readlink",        ext_readlink},
  {"dirname",         ext_dirname},
  {"basename",        ext_basename},
  {"date",            io_date},
  {"time",            io_time},
  {"difftime",        io_difftime},
};

LUALIB_API void lua_extendedlibopen(lua_State *L)
{
    /* Set a global that tells people what version of the extensions they have. */
    lua_pushstring (L , LUAEXTENSIONS_VERSION);
    lua_setglobal (L , "LUAEXTENSIONS_VERSION");

    /* Add our extension functions */
    luaL_openl (L, extendedlib);
}

#else

/* This is only compiled in if USE_EXTENSIONS is NOT defined. A simple stub. */
LUALIB_API void lua_extendedlibopen (lua_State *L)
{
}

#endif

/* This converts a hex character into an integer in the range of 0-15, unless
 * the character passed in isn't a valid hex character, in which case it 
 * returns -1 instead. */
int hexcharToDigit (char digit)
{
    if (digit >= '0' && digit <= '9')
        return digit - '0';
    else if (digit >= 'A' && digit <= 'F')
        return digit - 'A' + 10;
    else if (digit >= 'a' && digit <= 'f')
        return digit - 'a' + 10;
    else 
        return -1;
}

/* This takes two hex digits and turns them into their integer value. If
 * either one of the digits is not a valid hex digit, the integer value
 * for '!' is returns instead. This allows you to spot horribly broken
 * URL's easier. Generally this "shouldn't happen" and "is nothing to
 * worry about". */
int hexToDec (char digit1 , char digit2)
{
    /* Convert each digit to it's hex value. */
    digit1 = hexcharToDigit (digit1);
    digit2 = hexcharToDigit (digit2);

    /* If either is bad, return a static char. */
    if (digit1 == -1 || digit2 == -1)
        return '!';

    /* Convert to full hex value and return it. */
    return digit1 * 16 + digit2;
}

/* Copies a string from one place to another in a special way. string is the 
 * string we want to copy out of. buffer is where we want to copy to. maxLen is
 * the maximum length (not counting null byte) that we want to copy.
 * stopChar is the character we want to assume is the end of the string.
 * 
 * The string is copied until maxLen bytes have been copied, the end of the
 * string has been reached, or stopChar is hit. The buffer is always
 * null terminated, but stopChar will not be copied.
 *
 * During copying, sequences like %XX, where XX is a hex value, are
 * replaced with their ascii value. Decoding of + as a space is also done.
 * No other special URL decoding is done.
 *
 * The return value is a pointer inside string where we stopped. This would
 * be either pointing at stopChar or at the terminating null byte on the
 * string. */
char *copyStr (char *string , char *buffer , int maxLen , char stopChar)
{
    int strIndex=0,bufferIndex=0;

    /* Keep looping until the character we landed on is the stop
     * character or the end of the string. */
    while (string[strIndex] != stopChar && string[strIndex] != '\0')
    {
        /* If this character isn't a '%' character, then copy it.
         * Otherwise, expand it. */
        if (string[strIndex] == '%')
        {
            int c1,c2;

            /* The two characters after the % are hex digits. Get them out.
             * If there aren't two characters before we hit the null
             * terminator, break out leaving strIndex at the null, so our
             * return makes the caller stop. */
            strIndex++;
            c1 = string[strIndex++]; if (c1 == '\0') break;
            c2 = string[strIndex++]; if (c2 == '\0') break;
            buffer[bufferIndex++] = hexToDec (c1 , c2);
        }
        else if (string[strIndex] == '+')
        {
            /* Copy a + as a space, but still bump the str index. */
            buffer[bufferIndex++] = ' ';
            strIndex++;
        }
        else
            /* Just copy, it's not special. */
            buffer[bufferIndex++] = string[strIndex++];

        /* If we've now copied as many characters as we're allowed, then
         * break out now. */
        if (bufferIndex >= maxLen) 
            break;
    }

    /* Make sure strIndex is either the character we were supposed to stop
     * at, or the null byte at the end. This is to clean up if a key or
     * value is longer than we allow it to be, and we stop copying it in the
     * middle. */
    while (string[strIndex] != '\0' && string[strIndex] != stopChar)
        strIndex++;
    
    /* Null terminate our buffer. */
    buffer[bufferIndex] = '\0';

    /* Return where we stopped. */
    return string + strIndex;
}

/* This function is a helper for parseURLEncodedString and handleParsedMultipartData. 
 * It assumes the lua stack in the given state has a top element that is the table 
 * we're putting our parsed values into. This will add the given value to the table 
 * under the given key. If the key already exists, then it's value is turned into a
 * table. 
 *
 * On return, the stack is left in the same state as it was when we started. */
void addKeyValue (lua_State *L , char *key , char *value)
{
    char *oldVal;

    /* In order to properly add this value into the table, we first have to
     * query the table to see if it has a value for this key, and if so, what
     * it is. We push the key onto the stack and get the value for that key.
     * This will pop the key we push, and replace it with the current value of
     * that key. */
    lua_pushstring (L , key);
    lua_gettable (L , -2);

    /* Depending on what the type of the value at the top of the stack is,
     * we will do something different. */
    switch (lua_tag (L , -1))
    {
        /* When the type is nil, it means this key doesn't exist yet. So we
         * simply pop the nil off the stack to get rid of it, then set the 
         * key as normal. The settable will pop the key and value while
         * doing it's job. */
        case LUA_TNIL:
            lua_pop (L , 1);
            lua_pushstring (L , key);
            lua_pushstring (L , value);
            lua_settable (L , -3);
            break;
    
        /* When the type is string, it means we've already added a single
         * value for this key. In that case, we need to save the current
         * value, replace it with a table, then put the old value and the
         * new value back into the new table. */           
        case LUA_TSTRING:
            /* First of all, get the value of the string at the top of the
             * stack (the current value of the key we want to set). Once we
             * do that, we will pop that string off the stack so the top is
             * the parsedString table again. */
            oldVal = (char *) lua_tostring (L , -1);
            lua_pop (L , 1);

            /* Now, push the key we want to use onto the stack, followed by 
             * a brand new table. */
            lua_pushstring (L , key);
            lua_newtable (L);

            /* Now, we will set the original value into the table at
             * index 1. Note that rawseti pops the value from the stack. */
            lua_pushstring (L , oldVal);
            lua_rawseti (L , -2 , 1);

            /* Set the new value to index 2 in the same way. */
            lua_pushstring (L , value);
            lua_rawseti (L , -2 , 2);

            /* Lastly, do a settable to set this new table as the value for
             * the key, which will replace the original string that was in
             * there. It will also remove the key and inner table from the
             * stack. */
            lua_settable (L , -3);
            break;

        /* When the type is table, it means this is the 3rd or more time that
         * this key has appeared. The item at the top of the stack is the
         * table, so just add this new value to that table at the next
         * numeric index. */
        case LUA_TTABLE:
            /* Set this new value into the table at the next numerical index
             * in the table, as returned by getn. Remember that the top of the
             * stack is this table associated with the current key, because of
             * our earlier query for the current value of the key. We presume
             * that getn returns how many indexes there are so far, and add 1
             * to it to get the new one. */
            lua_pushstring (L , value);
            lua_rawseti (L , -2 , lua_getn (L , -2) + 1);

            /* Now that that's done, pop this sub-table, since we're done
             * fooling with it. */
            lua_pop (L , 1);
            break;
            
        /* This should never happen, since we are strictly in control of the
         * table at this point. Nevertheless, specifically do nothing if the
         * type of the object at the top of the stack has a weird type. */
        default:
            break;
    }
}

/* This parses a query string into a key/value paired table. The
 * return value is the number of key/value pairs that were parsed. 0 generally
 * indicates failure, or an empty query string.
 *
 * On return, if 1 or more pairs were parsed, a table will be left on the
 * top of the lua stack. If nothing was parsed, the lua stack is left as it was
 * on entry. 
 *
 * This intelligently handles multiple values for the same key (as would be
 * returned by a multi-select list, for example) by setting the value of that
 * key to a table with all the values in it. 
 * 
 * This allocates a buffer for the key and value, and ensures the parsed values 
 * don't overrun that buffer. So, no worries about tromping into places man
 * should be afraid to tread. */
int parseURLEncodedString (lua_State *L , char *cmdStr)
{
    char *key,*value;
    int keysParsed=0;

    /* Start by trying to allocate memory for our buffers. If either of these
     * don't work, we should just return out right now. Notice that on  a failure 
     * of the second allocation, we're careful not to leak the first one. */
    key = (char *) malloc (MAX_KEY_LEN + 1);
    if (key == NULL)
        return 0;
    
    value = (char *) malloc (MAX_VALUE_LEN + 1);
    if (value == NULL)
    {
        free (key);
        return 0;
    }

    /* Now, we can put an empty table on the top of the stack. We will be 
     * setting keys in this table to the values that we parse. */
    lua_newtable (L);

    /* Loop until we hit the end of the string */
    while (*cmdStr != '\0')
    {
        /* Fish out the key string. The return value is the postion
         * of what made us stop, either the = sign, or a null byte.
         * If we hit a null byte, this isn't a valid key, so we can
         * skip out right now. */
        cmdStr = copyStr (cmdStr , key , MAX_KEY_LEN , '=');
        if (*cmdStr == '\0') break;

        /* Do the same thing for the value. The difference here is that
         * we don't mind if we hit the null byte, that just tells us
         * there are no more key/value pairs left. Which means, we
         * examine the return value. If it's not a null byte, then we
         * assume there's more data and increment the cmdStr by 1 to
         * skip past the & that splits keys pairs, so the next iteration
         * starts at the next key. */
        cmdStr = copyStr (cmdStr + 1 , value , MAX_VALUE_LEN , '&');
        if (*cmdStr != '\0') cmdStr++;

        /* Do something with the key/value pair */
        addKeyValue (L , key , value);

        /* One more key parsed */
        keysParsed++;
    }
 
    /* Blow away the buffers, we're done now. */
    free (key);
    free (value);
   
    /* If we parsed no keys, then we have an empty table on the stack that we
     * want to throw away. */
    if (keysParsed == 0)
       lua_pop (L , 1);

    /* Return the number of keys we parsed */
    return keysParsed;
}

/* This attempts to return back the query string of the current request for
 * parsing. If this is a GET form, then this will return the value of the
 * QUERY_STRING environment variable. If this is a POST form, the data will
 * be read from stdin. If the form type is unknown, or the query string is
 * empty, then NULL will be returned instead. 
 *
 * Either way, the return value is a pointer to an allocated buffer holding
 * the data. The caller should free () this once they're done with it. */
char *queryString (void)
{
    char *string;
    char *returnValue;

    /* First thing we're going to do is attempt to get the request type
     * from the environment. From this, we will decide where to get the
     * query string from. If this doesn't work, then we will return out
     * right away. */
    string = getenv ("REQUEST_METHOD");
    if (string == NULL)
        return NULL;

    /* For a GET request, the query string is in an environment variable
     * named QUERY_STRING. */
    if (strcasecmp (string , "GET") == 0)
    {
        /* Try to get the query string from the environment. */
        string = getenv ("QUERY_STRING");
        if (string == NULL)
            return NULL;

        /* Duplicate the value of the variable and return that. */
        returnValue = (char *) malloc (strlen (string) + 1);
        strcpy (returnValue , string);
        return returnValue;
    }

    /* For a POST request, some data was put into our standard input. Here,
     * we need to query the environment for the content encoding and the
     * length of data written. */
    if (strcasecmp (string , "POST") == 0)
    {
        char *encode;
        int length;
        int bytesRead;

        /* Start by getting the encoding type. If we can't find it, or
         * it's not URL encoded data, then return NULL right now so that
         * we won't parse it. */
        encode = getenv ("CONTENT_TYPE");
        if (encode == NULL || strcasecmp (encode , URL_ENCODED_DATA) != 0)
            return NULL;

        /* Encoding type is good, so try to get the content length. Fail
         * out if we can't get it, or it's not valid. Reuse the encoding
         * variable since we don't need it any longer. */
        encode = getenv ("CONTENT_LENGTH");
        if (encode == NULL || (length = atoi (encode)) <= 0)
            return NULL;
        
        /* Allocate memory, read the data, then null terminate it. */
        returnValue = (char *) malloc (length + 1);
        bytesRead = fread (returnValue , 1 , length , stdin);
        returnValue[bytesRead] = '\0';
        return returnValue;
    }

    /* If we get here, then we don't know what the request type is. In that
     * case, we will just leave. */
    return NULL;
}

/* If the content type in the request is MULTIPART_DATA, this will return
 * back the mime mulipart boundary between the different parts of the data.
 * Otherwise, NULL is returned. 
 *
 * When the return value is NON-NULL, it is up to the caller to free () the
 * returned boundary string when it is no longer needed.
 *
 * This returns the boundary with "--" prepended to it, since most operations
 * requiring the boundary will contain that string, and it's easier to alias
 * past it than it is to add it in when you need it.  */
char *getBoundary (void)
{
    char *contentType = getenv ("CONTENT_TYPE");
    char *tmp;
    char *retVal;

    /* Guard against missing variables. */
    if (contentType == NULL)
        return NULL;

    /* Get the content type to see if it is multipart data. */
    if (strncasecmp (MULTIPART_DATA , contentType , strlen (MULTIPART_DATA)) != 0)
        return NULL;

    /* Find the start of the boundary. It comes after the first = in the content type. */
    tmp = strchr (contentType , '=');
    if (tmp == NULL)
        return NULL;

    /* Skip past the '=' character, and all following whitespace. */
    tmp++;
    while (isspace (*tmp))
        tmp++;

    /* Allocate a boundary string. We add 2 because we want to add "--" at the beginning, and
     * 1 for a null terminator. */
    retVal = (char *) malloc (strlen (tmp) + 3);

    /* Copy a double dash string in, and then append the boundary string */
    strcpy (retVal , "--");
    strcat (retVal , tmp);

    /* Return the completed boundary string */
    return retVal;
}

/* This takes some multipart data of a given size, whose mime boundary is boundary,
 * and where the current parse position in the data is *pos. This will scan forward
 * until it finds the boundary, and will skip the position past it.
 *
 * On success, 1 is returned and *pos is set to the start of the first character
 * after the completed boundary (a Content-Disposition line). 
 *
 * On failure, 0 is returned. Failure can mean that no boundary was found, that
 * we found a boundary but it was at the end of the data, or that we found the
 * boundary and it was suffixed with "--", which would indicate that we found
 * the end of the data naturally.
 *
 * In all cases, a return value of 0 indicates "stop parsing now".
 *
 * The value of *pos is undefined if the return value is 0. */
int findMultipartBoundary (char *data , int size , char *boundary , int *pos)
{
    int boundaryLen = strlen (boundary);
    int localPos = *pos;

    /* Each part should start with two dashes ("--") followed by the
     * boundary. The boundary that we were given has the two dashes
     * included. We start by advancing along the data until the data
     * starts with the boundary text, or we get to the end of the data. */
    while (localPos < size && strncasecmp (data + localPos , boundary , boundaryLen) != 0)
        localPos++;

    /* We either found what we wanted, or ran the position off the end of the 
     * data trying. We will pretend that we found what we wanted, and advance
     * the position in the data past the boundary. */
    localPos += boundaryLen;
    
    /* Now, is the position past the end of the string? This can be the case 
     * if we didn't find the data, OR if we DID find the data, but it was the
     * last thing in the string. In either case, just bail out now. */
    if (localPos >= size)
        return 0;

    /* Pos is now pointing at the first character past the end of the boundary
     * data. If the string "--" is present at this location, it means we have
     * reached the end of the data, so break now. */
    if (strncmp (data + localPos , "--" , 2) == 0)
        return 0;

    /* Skip forward until we run out of data, or until we find a newline character. */
    while (localPos < size && data[localPos] != '\n')
        localPos++;

    /* Pretend that we found the newline before we ran out of data, and advance
     / past it anyway. */
    localPos++;

    /* Again, if the position is past the end, leave. Here the search could have
     * not found the newline at all and put us at the end, or the newline was the
     * last piece of data. Either way, leave. */
    if (localPos >= size)
        return 0;

    /* We were successful. Store back the altered position before we return. */
    *pos = localPos;
    return 1;
}

/* This helper takes a block of data of the given size, with the knowledge that
 * the current parse position is *pos.
 *
 * This will return the next "line" of data from the file, starting at the character
 * indicated by *pos. 
 *
 * A line is considered everything up until a newline character. The function
 * works by clobbering the newline character with a null byte and returning 
 * a pointer into the original data (literally, data + *pos, the first character
 * that was pointed at when this was called). 
 *
 * On success, the string is returned, and *pos is advanced to be the first
 * character after the trailing newline character.
 *
 * On failure, NULL is returned and *pos is undefined.
 *
 * Failure indicates that we either ran out of data while trying to find the
 * newline character, or we found the newline character, but it was the last
 * thing in the data, and so the data is somehow broken (because multipart
 * form data must end with a boundary). */
char *getNextLine (char *data , int size , int *pos)
{
    int localPos = *pos;
    char *lineStart;

    /* The first thing we are going to do is collect everything from pos up to the 
     * next newline in the string. Start by aliasing the current piece of data. */
    lineStart = data + localPos;

    /* Scan until we find a newline or run out of data. */
    while (localPos < size && data[localPos] != '\n')
        localPos++;

    /* Pretend we found the newline even if we didn't. In this case, even if we have
     * a complete line, if it's the last line, the data is broken and there's nothing 
     * we can do anyway. */
    localPos++;

    /* If we ran out of data, leave. */
    if (localPos >= size) 
        return NULL;

    /* Clobber the byte at pos-1 with a NULL byte to terminate the string. */
    data[localPos-1] = '\0';

    /* Return back now */
    *pos = localPos;
    return lineStart;
}

/* This works similar to strstr, except for the following:
 *  - The comparison is case-insensitive
 *  - The return value is the position in line immediately FOLLOWING the substr.
 *
 * NULL is returned if the string wasn't found, or if it was found at the very
 * end of the string. */
char *findSubStrPos (char *line , char *substr)
{
    /* Cache the length */
    int len = strlen (substr);

    /* Search until we find the end of the string, or what we want, */
    while (*line != '\0' && strncasecmp (line , substr , len) != 0)
        line++;

    /* If we found what we wanted, then advance the line past it. */
    if (*line != '\0')
        line += len;

    /* Now, if the first search failed, OR what we matched was at the
     * end of the string, return failure. */
    if (*line == '\0')
        return NULL;

    /* Success */
    return line;
}

/* This is a helper for getDispositionNames. It takes a pointer to a line,
 * and a field name (which should have the field separated appended, as in 
 * "thekey=", and a pointer to store the value into.
 *
 * On success, line is modified to null terminate the field with the name
 * given (whose value must be wrapped with double quotes), the pointer into
 * line where the value starts is stored in value, and the function returns
 * a pointer to the first character in line AFTER the value, which may
 * point to a NULL byte if the value was the last thing in the string.
 *
 * On error (no such field found, or it wasn't wrapped in quotes properly)
 * NULL is returned and the value of the paramter "value" is undefined. */
char *getFieldValue (char *line , char *fieldName , char **value)
{
    /* First, find the location of the value we want. Bail if we don't
     * find it. */
    line = findSubStrPos (line , fieldName);
    if (line == NULL)
        return NULL;

    /* Skip the current character, which should be an open quote. This makes
     * line point at the start of the desired field value, so set *value to
     * that for return purposes. */
    line++;
    *value = line;

    /* Now, advance line forward until we find a double quote, or we run out of
     * data. */
    while (*line != '\0' && *line != '"')
        line++;

    /* If we found the end of the data, it's broken, return failure. */
    if (*line == '\0')
        return NULL;

    /* We found the closing doublequote. Clobber it will a nullbyte, and then
     * advance past it. */
    *line = '\0';
    line++;

    /* Now, return line. Everything else is set up for us. */
    return line;
}

/* This takes a self contained line that is a potential Content-Disposition header,
 * and tried to find the name of the field, and an optional filename.
 *
 * On success, fieldName is set to the pointer into the given line to where the
 * field name starts. filename may be filled out similiary, if there is a filename
 * found. Otherwise, it is set to NULL. 1 is then returned.
 *
 * On error, 0 is returned and both pointers are left in an undefined state. 
 * Possible errors are that the line isn't a content disposition line, or 
 * there was no field name provided.
 *
 * The function works by modifying the passed in line, so that it can return
 * pointers to strings contained within. */
int getDispositionNames (char *line , char **fieldName , char **filename)
{
    /* NULL out the filename, because we might not find it, and that's not
     * really an error. */
    *filename = NULL;

    /* Make sure we have good input. */
    line = findSubStrPos (line , "Content-Disposition:");
    if (line == NULL)
        return 0;

    /* Check for failure. */
    if (*line == '\0')
        return 0;

    /* Try to find the name field. */
    line = getFieldValue (line , "name=" , fieldName);
    if (line == NULL)
        return 0;

    /* We successfully got a name, see if we can get a filename too.
     * Wether this worked or not, we are going to return success. */
    getFieldValue (line , "filename=" , filename);

    return 1;
}

/* Advances *pos until a newline is found, or until we run out of data.
 *
 * If a newline is found, pos is advanced one more time to point to the
 * first charcter after the newline.
 *
 * It's up to the caller to check for overflow of data (if there was no
 * newline before the end of the data) */
void skipToNewline (char *data , int size , int *pos)
{
    int localPos = *pos;

    /* Scan until we find the newline we seek. */
    while (localPos < size && data[localPos] != '\n')
        localPos++;

    /* Pretend that we found the newline, and advance one position past it.
     * If we did find it, this is the right thing to do. If we didn't, well, 
     * we already know the data is broken, so one more position can't hurt. */
    localPos++;

    /* Set back the position for return */
    *pos = localPos;
}

/* This helper presumes that data[*pos] is the first character in a line in the
 * multipart data. If this line starts with "Content-Type:", *pos will be advanced
 * to the first character after the newlien that terminates this line, or past the
 * end of the data if there is no such newline.
 *
 * If the line doesn't start with the desired string, nothing happens.
 *
 * A check should be done on return to make sure that *pos isn't at the end 
 * of the data. */
void skipContentType (char *data , int size , int *pos)
{
    static char *content = "Content-Type:";
    int cLen = strlen (content);
    int localPos = *pos;

    /* Return if the line doesn't start with the content type string. */
    if (strncasecmp (data + localPos , content , cLen) != 0)
        return;

    /* We found what we wanted! Scan localPos forward until we hit the end of 
     * data or stop on a newline character. */
    skipToNewline (data , size , &localPos);

    /* Store the position back before we return */
    *pos = localPos;
}

/* This assumes that data[*pos] is the first byte of data in a part. This scans
 * to find the end of the part. The end is defined as a newline which is followed
 * by the boundary we have been given.
 *
 * On success, *pos is set to point at the last character before the newline that
 * terminates the data (the newline is not part of the data). 
 *
 * On failure, *pos is set to be past the end of the data. */
void seekFieldEnd (char *data , int size , int *pos , char *boundary)
{
    int boundaryLen = strlen (boundary);
    int localPos = *pos;

    /* Scan until we run out of data. */
    while (localPos < size)
    {
        /* Is the character at this position a newline? */
        if (data[localPos] == '\n')
        {
            /* Is the data right after this the boundary string? */
            if (strncmp (data + localPos + 1 , boundary , boundaryLen) == 0)
            {
                /* The newline at data[localPos] is followed by the boundary, so
                 * the field is ending. The data should end at the character 2 characters
                 * before us (because there is a \r right before the \n we are at)
                 *
                 * It is theoretically possible that the field be empty. In this
                 * case, we do NOT want to back up one. So, we will only back up
                 * one if this position and the start position are different. The
                 * caller can work out the data on it's own (set up an empty string,
                 * etc) based on the start and stop position being the same.). */
                if (localPos != *pos)
                    localPos -= 2;

                /* Done now */
                break;
            }
        }

        /* This is nothing we care about, advance one position and try again. */
        localPos++;
    }

    /* Store the local position back. On success, this is the location that we
     * care about. On failure, this is past the end of the data so the caller knows
     * the data is broken. */
    *pos = localPos;
}

/* Takes multipart data of a given size, and the multipart mine boundary that is
 * seperating things, and parses it out. 
 *
 * The return value is a linked list of multipart data segments that were parsed,
 * or NULL if there was no data parsed. 
 *
 * In cases of errors during parsing of a data part, it is skipped and we move on 
 * to the next one in sequence. Thus the data you get may not be all of the data.
 *
 * It is up to the caller to deallocate the linked list when it is no longer needed.
 *
 * NOTE: The data in the returned linked list contains pointers aliased from the 
 * passed in chunk of data. So, if you release the data, you should no longer
 * use the linked list. */
MultipartData *parseMultipartData (char *data , int size , char *boundary)
{
    int pos=0;
    char *fieldName;
    char *fieldFilename;
    char *fieldValue;
    int  fieldLength;
    char *lineBuff;
    MultipartData *root=NULL,*newNode=NULL,*lastAdded=NULL;

    /* Keep looping until we run out of data.
     * Each pass through the loop is one part of the data */
    while (pos < size)
    {
        /* Make sure all fields are initialized to start with. */
        fieldName = fieldValue = fieldFilename = NULL;
        fieldLength = 0;

        /* The first thing we will do is scan to find a multipart boundary with the
         * boundary we were provided. This function returns 0 when the data is broken
         * or we are at the end of the data, and 1 if we successfully found a boundary.
         * In the success case, the position is advanced to the first character after
         * the boundary (and it's trailing newline). */
        if (findMultipartBoundary (data , size , boundary , &pos) == 0)
            break;

        /* pos is now pointing at the first byte of the data for this "part". The first
         * line in any part looks something like this:
         * Content-Disposition: form-data; name="FormFieldName"; filename="AFilename"
         * 
         * The filename portion only exists if this part is part of a file field. Otherwise,
         * it doesn't exist. */
        lineBuff = getNextLine (data , size , &pos);
        if (lineBuff == NULL)
            break;

        /* Try to get the field name and filename from this line. If this doesn't work, the
         * multipart data is broken, so break out of parsing right now. */
        if (getDispositionNames (lineBuff , &fieldName , &fieldFilename) == 0)
            break;

        /* The above line capture left pos at the start of the first line in the multipart
         * data for this part. It might be a content type line. If it is, skip past it. */
        skipContentType (data , size , &pos);

        /* The first byte of data for this field comes after a single newline that isn't part
         * of the mime data. So, wether there was a content type or not, we need to skip one
         * more newline, before we're at the actual data. Note that here pos might already
         * be past the end of the data, from the skipContentType call above. In this case,
         * this will do nothing, so we're ok. */
        skipToNewline (data , size , &pos);

        /* If we are past the end of data now, we're unhappy */
        if (pos >= size)
            break;

        /* The field data starts right here, so alias a pointer to it now. */
        fieldValue = data + pos;

        /* Now, we need to seek to find the end of the data. The data for this field is
         * immediately followed by a newline, and the boundary. Find where the data ends.
         * This will leave pos on the very last byte of data before the newline that is
         * going to terminate it. */
        seekFieldEnd (data , size , &pos , boundary);

        /* If we passed the end of the data, the data is broken, so bail. */
        if (pos >= size)
            break;

        /* The data is good. Determine the length of the data now, based on where we stopped
         * and where we started. */
        fieldLength = (data + pos) - fieldValue + 1;
        
        /* If the length we have calculated is 1 or larger, then we want to advance the
         * position one ahead, to the \r immediately following the data (this is followed
         * by a \n, because mime line ends are \r\n) and then clobber the \r with a NULL
         * byte to terminate the data (just in case it's a string).
         *
         * When this is not the case, if the field length is 0, then fieldValue is already
         * pointing at the \r character, so clobber it now to be NULL, again, just in 
         * case this is a string.
         *
         * This only happens for text fields in forms that have no value. */
        if (fieldLength > 0)
        {
            pos++;
            data[pos] = '\0';
        }
        else if (fieldLength == 0)
        {
            *fieldValue = '\0';
        }

        /* Now, advance pos one more time, so that it is on the boundary that starts the
         * next part, for the next iteration. */
        pos++;

        /* Create a new node to store the data we just collected. */
        newNode = (MultipartData *) malloc (sizeof (MultipartData));

        /* Copy the data over. */
        newNode->fieldName     = fieldName;
        newNode->fieldFilename = fieldFilename;
        newNode->fieldValue    = fieldValue;
        newNode->fieldLength   = fieldLength;
        newNode->next          = NULL;

        /* Now, link this node into the chain. */
        if (root == NULL)
        {
            /* This is the root node, and also the last node that was added. */
            root = lastAdded = newNode;
        }
        else
        {
            /* If there was a previous node added, make it's
             * next point to us. */
            if (lastAdded != NULL)
                lastAdded->next = newNode;

            /* We are the last node added now. */
            lastAdded = newNode;
        }
    }

    /* Return the root of the linked list that we have created. */
    return root;
}

/* Presuming that we have successfully parsed some kind of Multipart data,
 * this does the work of adding it to lua. It is safe to call this with
 * a parameter of NULL. */
void handleParsedMultipartData (lua_State *L , MultipartData *contents)
{
    MultipartData *tmp;
    int regularKeys=0,fileKeys=0,filesAdded=0;

    /* If there was no data parsed, we have nothing to do. */
    if (contents == NULL)
        return;

    /* Now, we are going to get ready to store regular (non-file) data. 
     * Start by pushing a key and table onto the stack. */
    lua_pushstring (L , "parsedMultipartData");
    lua_newtable (L);

    /* Now, loop over the entire contents. For anything that's not a file,
     * add it in now. We will maintain a count of each type found along
     * the way. */
    tmp = contents;
    while (tmp != NULL)
    {
        /* If this is not a file, go ahead and add it in. */
        if (tmp->fieldFilename == NULL)
        {
            /* Add in the key and value for this, and then count
             * this as a regular key. */
            addKeyValue (L , tmp->fieldName , tmp->fieldValue);

            regularKeys++;
        }
        else
            /* This is a file, so just count it so we know how many there are. */
            fileKeys++;
        
        /* Go on to the next one. */
        tmp = tmp->next;
    }

    /* Now, if we didn't parse any regular keys, the data we pushed onto the
     * stack at the beginning is useless, so throw is away. */
    if (regularKeys == 0)
    {
        lua_pop (L , 2);
    }
    else
    {
        /* We got data! Set the key into the CGI table now. The third position
         * down on the stack is our CGI table. The second down is the key we
         * are using, and the last is the table that will be the value for that
         * key. */
        lua_settable (L , -3);
    }

    /* If there were no file keys, then exit now. */
    if (fileKeys == 0)
        return;

    /* Now we are going to store files, and we know we have at least one. Set up
     * a key and a table to hold the things. */
    lua_pushstring (L , "parsedFiles");
    lua_newtable (L);

    /* Now loop over everything again. Only this time, we care about things that
     * DO have files. */
    tmp = contents;
    while (tmp != NULL)
    {
        /* If this is not a file, go ahead and add it in. */
        if (tmp->fieldFilename != NULL)
        {
            /* Count this as a file added. */
            filesAdded++;

            /* Push a new table onto the top of the stack. */
            lua_newtable (L);

            /* Setup the name of the field, the filename, the data, and the length. */
            lua_pushstring (L , "formName");
            lua_pushstring (L , tmp->fieldName);
            lua_settable (L , -3);

            lua_pushstring (L , "filename");
            lua_pushstring (L , tmp->fieldFilename);
            lua_settable (L , -3);

            lua_pushstring (L , "length");
            lua_pushnumber (L , tmp->fieldLength);
            lua_settable (L , -3);

            lua_pushstring (L , "data");
            lua_pushlstring (L , tmp->fieldValue , tmp->fieldLength);
            lua_settable (L , -3);

            /* Now store this file into the array. */
            lua_rawseti (L , -2 , filesAdded);
        }
        
        /* Go on to the next one. */
        tmp = tmp->next;
    }

    /* Now set the files into the table, as above. */
    lua_settable (L , -3);
}

/* This sets the field multipartError to the string provided. This
 * presumes that the table on the top of the lua stack is the CGI
 * table. */
void multipartError (lua_State *L , char *key , char *errorMsg)
{
    lua_pushstring (L , key);
    lua_pushstring (L , errorMsg);
    lua_settable (L , -3);
}

/* This function is called if attempts to get a query string of type url encoded
 * data failed. We will check to see if there is any multipart data, and if there is, handle it
 * appropriately.
 *
 * This assumes that the lua state has been set up, and that the item at the top of
 * the stack is the table that will eventually become the CGI table when we are
 * finished. */
void attemptMultipartDecode (lua_State *L)
{
    char *string,*data,*boundary;
    int contentLength,contentRead;
    MultipartData *contents,*tmp;

    /* Get the request method from the environment. We need to know this
     * because we can only handle POST forms. */
    string = getenv ("REQUEST_METHOD");
    if (string == NULL)
        return;

    /* If we don't have a POST request, then leave, we can do nothing. */
    if (strcasecmp (string , "POST") != 0)
        return;

    /* Get the CONTENT_TYPE, so that we can see if it is multipart data.
     * Here we have to be careful because the content type also contains
     * a multipart mime boundary string, if we are multipart data. */
    string = getenv ("CONTENT_TYPE");
    if (string == NULL || strncasecmp (string , MULTIPART_DATA , strlen (MULTIPART_DATA)) != 0)
        return;
    
    /* Lastly, we need a content length, because we need to know ahead of time
     * how big the multipart data is. */
    string = getenv ("CONTENT_LENGTH");
    if (string == NULL || (contentLength = atoi (string)) <= 0)
        return;

    /* If the content length is too big, print an error to stderr so that it will show up
     * in the logs, and then do nothing. */
    if (contentLength >= MAX_MULTIPART_DATA)
    {
        fprintf (stderr , "Content length %d is too big, maximum is %d\n" , contentLength , MAX_MULTIPART_DATA);
        multipartError (L , "multipartTooBig" , "1");
        return;
    }

    /* Get the multipart boundary string. */
    boundary = getBoundary ();
    if (boundary == NULL)
    {
        fprintf (stderr , "Unable to get multipart boundary from the content type, ignoring data.\n");
        multipartError (L , "multipartError" , "Content type was missing the multipart boundary");
        return;
    }

    /* Allocate memory for the data now. */
    data = (char *) malloc (contentLength + 1);
    if (data == NULL)
    {
        fprintf (stderr , "Not enough memory to allocate %d bytes for the content data\n" , contentLength + 1);
        free (boundary);
        multipartError (L , "multipartError" , "Not enough memory to allocate data for multipart parsing");
        return;
    }

    /* Read in the data. If we didn't read all of the data, then we need to fail out. */
    contentRead = fread (data , 1 , contentLength , stdin);
    if (contentRead != contentLength)
    {
        free (data);
        free (boundary);
        fprintf (stderr , "Error reading content data; probably cause: form submission incomplete. Ignoring data.\n");
        multipartError (L , "multipartCancel" , "1");
        return;
    }
    
    /* Null terminate the data we got, just in case. */
    data[contentRead] = '\0';

    /* Parse out the actual data now. */
    contents = parseMultipartData (data , contentRead , boundary);

    /* Actually handle the fully parsed data, adding it to the proper places and so on. */
    handleParsedMultipartData (L , contents);

    /* Free up all of the allocated data now. */
    free (data);
    free (boundary);

    /* Deallocate the linked list now, if we have one. */
    while (contents != NULL)
    {
        tmp = contents;
        contents = contents->next;

        free (tmp);
    }
}

/* This sets up our whole CGI world. This pulls in environment variables and
 * sets members in a table named CGI to their values. We also parse any query
 * string we might have gotten as well. */
void setupCGI (lua_State *L)
{
    int varNum;
    char *queryStr;

    /* First thing to do is put a new empty table onto the stack. */
    lua_newtable (L);

    /* Now, we will scan through the list of environment variables, and
     * set any we find. We make sure not to set an empty variable. Hope
     * that's not bad? */
    for (varNum = 0 ; vars[varNum] != NULL ; varNum += 2)
    {
        /* Get the value of the variable */
        char *value = getenv (vars[varNum]);

        /* If we got a value, and it's not empty, put it into the
         * table. */
        if (value != NULL && value[0] != '\0')
        {
            /* Push first the key, then the value, then set it into the
             * table. The call to set the table pops the key and value 
             * from the stack. */
            lua_pushstring (L , vars[varNum + 1]);
            lua_pushstring (L , value);
            lua_settable (L , -3);
        }
    }

    /* Now we want to parse up the query string. First step, see if we can
     * even get one. */
    queryStr = queryString ();
    if (queryStr != NULL)
    {
        /* In preparation for setting up an entry in the CGI table for this
         * parsed info, push a key onto the stack. */
        lua_pushstring (L , "parsedQueryString");

        /* Now we will call the parse function. If it returns non-zero, then
         * it left a table on the stack, which we can set into this main table.
         * If it returns 0, then it took the table off the stack before it
         * returned, so we should pop the key we just put on to throw it
         * away. */
        if (parseURLEncodedString (L , queryStr) != 0)
            lua_settable (L , -3);
        else
            lua_pop (L , 1);
       
        /* With that out of the way, release our copy of the query string. */ 
        free (queryStr);
    }
    else
    {
        /* We did not get a query string to parse. Do a check to see if maybe
         * we are a POST form that is multipart encoded, and do something
         * with that. */
        attemptMultipartDecode (L);
    }

    /* Now that we're all done, we need to set a global variable using
     * this table. This is, fortunately, easy. */
    lua_setglobal (L , "CGI");
}

/* A simple lua function that can tell you the size of a jpeg file. */
static int cgi_jpegsize (lua_State *L)
{
    int width,height,result;
    char *fname;

    /* Get the filename */
    fname = (char *) luaL_check_string (L , 1);

    /* Try to determine the size of that file. */
    if (jpeg_size (fname , &width , &height) == 0)
        return 0;

    lua_pushnumber (L , width);
    lua_pushnumber (L , height);
   
    return 2; 
}

/* Entry point of the program. Duh. */
int main (int argc , char **argv)
{
    lua_State *L;

    /* Check to see if we were called properly or not. */
    if (argc < 2)
    {
        printf ("Content-Type: text/html\n\n");
        printf ("You're supposed to pass me a script when you call me.\n");
        return 1;
    }
    
    /* We must be good to go. Firstly, open up a lua state. We specify 
     * default stacksize. */
    L = lua_open (0);

    /* Now we can initialize the state by registering the standard libraries
     * and such. Last step here is to do our web-specific initialization
     * stuff. */
    lua_baselibopen (L);
    lua_iolibopen (L);
    lua_strlibopen (L);
    lua_mathlibopen (L);
    lua_extendedlibopen (L);
    setupCGI (L);
    lua_pushcfunction (L , cgi_jpegsize);
    lua_setglobal (L , "jpeg_size");
    lua_pushstring (L , LUAWEB_VERSION);
    lua_setglobal (L , "luaweb_version");


    /* Now we can execute the script we were passed. */
    return lua_dofile (L , argv[1]);
}

