Blob Blame History Raw

/**
 * @file agCgi.c
 *
 *  This is a CGI wrapper for AutoGen.  It will take POST-method
 *  name-value pairs and emit AutoGen definitions to a spawned
 *  AutoGen process.
 *
 *  This file is part of AutoGen.
 *  AutoGen Copyright (C) 1992-2016 by Bruce Korb - all rights reserved
 *
 * AutoGen is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * AutoGen is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

typedef struct {
    char const * name;
    char *       cgi_val;
} name_map_t;

#define ENV_TABLE \
    _ET_(REQUEST_METHOD) \
    _ET_(QUERY_STRING) \
    _ET_(CONTENT_LENGTH) \
\
    _ET_(AUTH_TYPE) \
    _ET_(CONTENT_TYPE) \
    _ET_(GATEWAY_INTERFACE) \
    _ET_(HTTP_ACCEPT) \
    _ET_(HTTP_REFERER) \
    _ET_(HTTP_USER_AGENT) \
    _ET_(PATH_INFO) \
    _ET_(PATH_TRANSLATED) \
    _ET_(REMOTE_ADDR) \
    _ET_(REMOTE_HOST) \
    _ET_(REMOTE_IDENT) \
    _ET_(REMOTE_USER) \
    _ET_(SCRIPT_NAME) \
    _ET_(SERVER_NAME) \
    _ET_(SERVER_PORT) \
    _ET_(SERVER_PROTOCOL) \
    _ET_(SERVER_SOFTWARE)

static name_map_t name_val_map[] = {
#define _ET_(n) { #n, NULL },
    ENV_TABLE
#undef _ET_
    { NULL, NULL }
};

typedef enum {
#define _ET_(n) n ## _IDX,
    ENV_TABLE
#undef _ET_
    NAME_CT
} name_idx_t;

#define cgi_method name_val_map[ REQUEST_METHOD_IDX ].cgi_val
#define cgi_query  name_val_map[ QUERY_STRING_IDX   ].cgi_val
#define cgi_len    name_val_map[ CONTENT_LENGTH_IDX ].cgi_val

 static inline char *
parse_input(char * src, int len)
{
    size_t rsz = (len * 4) + PARSE_INPUT_AG_DEF_STR_LEN;
    char * res = AGALOC(rsz, "CGI Defs");

    memcpy(res, PARSE_INPUT_AG_DEF_STR, PARSE_INPUT_AG_DEF_STR_LEN);
    (void)cgi_run_fsm(src, len, res + PARSE_INPUT_AG_DEF_STR_LEN, len * 2);
    assert(rsz > strlen(res));
    return AGREALOC(res, strlen(res)+1, "CGI input");
}

LOCAL void
load_cgi(void)
{
    /*
     *  Redirect stderr to a file.  If it gets used, we must trap it
     *  and emit the content-type: preamble before actually emitting it.
     *  First, tho, do a simple stderr->stdout redirection just in case
     *  we stumble before we're done with this.
     */
    dup2(STDOUT_FILENO, STDERR_FILENO);
    {
        FILE * fp = fdopen(STDERR_FILENO, "w" FOPEN_BINARY_FLAG);
        (void)fp;
    }
    {
        int tmpfd;
        oops_pfx = CGI_ERR_MSG_FMT;
        AGDUPSTR(cgi_stderr, CGI_TEMP_ERR_FILE_STR, "stderr file");
        tmpfd = mkstemp(cgi_stderr);
        if (tmpfd < 0)
            AG_ABEND(aprf(MKSTEMP_FAIL_FMT, cgi_stderr));
        dup2(tmpfd, STDERR_FILENO);
        close(tmpfd);
    }

    /*
     *  Pull the CGI-relevant environment variables.  Anything we don't find
     *  gets an empty string default.
     */
    {
        name_map_t * nm_map = name_val_map;
        name_idx_t   ix     = (name_idx_t)0;

        do  {
            nm_map->cgi_val = getenv(nm_map->name);
            if (nm_map->cgi_val == NULL)
                nm_map->cgi_val = (char *)zNil;
        } while (nm_map++, ++ix < NAME_CT);
    }

    base_ctx = (scan_ctx_t *)AGALOC(sizeof(scan_ctx_t), "CGI ctx");
    memset(VOIDP(base_ctx), 0, sizeof(scan_ctx_t));

    {
        size_t len = strtoul(cgi_len, NULL, 0);
        char * text;

        if (strcasecmp(cgi_method, "POST") == 0) {
            if (len == 0)
                AG_ABEND(LOAD_CGI_NO_DATA_MSG);

            text  = AGALOC(len + 1, "CGI POST");
            if (fread(text, (size_t)1, len, stdin) != len)
                AG_CANT(LOAD_CGI_READ_NAME, LOAD_CGI_READ_WHAT);

            text[ len ] = NUL;

            base_ctx->scx_data = parse_input(text, (int)len);
            AGFREE(text);

        } else if (strcasecmp(cgi_method, LOAD_CGI_GET_NAME) == 0) {
            if (len == 0)
                len = strlen(cgi_query);
            base_ctx->scx_data = parse_input(cgi_query, (int)len);

        } else {
            AG_ABEND(aprf(LOAD_CGI_INVAL_REQ_FMT, cgi_method));
            /* NOTREACHED */
#ifdef  WARNING_WATCH
            text = NULL;
#endif
        }
    }

    base_ctx->scx_line  = 1;
    base_ctx->scx_fname = LOAD_CGI_DEFS_MARKER;
    base_ctx->scx_scan  = base_ctx->scx_data;
}

/*
 * Local Variables:
 * mode: C
 * c-file-style: "stroustrup"
 * indent-tabs-mode: nil
 * End:
 * end of agen5/agCgi.c */