You are viewing a plain text version of this content. The canonical link for it is here.
Posted to apreq-dev@httpd.apache.org by Joe Schaefer <jo...@yahoo.com> on 2009/01/13 17:13:10 UTC

Re: svn commit: r734121 - in /httpd/apreq/trunk: ./ CHANGES library/module_cgi.c

Have you tried compiling this with --enable-maintainer-mode
configured, or are you still working on it?



----- Original Message ----
> From: "issac@apache.org" <is...@apache.org>
> To: apreq-cvs@httpd.apache.org
> Sent: Tuesday, January 13, 2009 8:06:57 AM
> Subject: svn commit: r734121 - in /httpd/apreq/trunk: ./ CHANGES library/module_cgi.c
> 
> Author: issac
> Date: Tue Jan 13 05:06:44 2009
> New Revision: 734121
> 
> URL: http://svn.apache.org/viewvc?rev=734121&view=rev
> Log:
> merge enhanced-cgi into trunk
> 
> Modified:
>     httpd/apreq/trunk/   (props changed)
>     httpd/apreq/trunk/CHANGES
>     httpd/apreq/trunk/library/module_cgi.c
> 
> Propchange: httpd/apreq/trunk/
> ------------------------------------------------------------------------------
>     svn:mergeinfo = /httpd/apreq/branches/enhanced-cgi:464940-733743
> 
> Modified: httpd/apreq/trunk/CHANGES
> URL: 
> http://svn.apache.org/viewvc/httpd/apreq/trunk/CHANGES?rev=734121&r1=734120&r2=734121&view=diff
> ==============================================================================
> --- httpd/apreq/trunk/CHANGES (original)
> +++ httpd/apreq/trunk/CHANGES Tue Jan 13 05:06:44 2009
> @@ -1,6 +1,12 @@
> /** @page apreq_changes CHANGES
> //! brief List of major changes.
> 
> +@section v2_11 Changes with libapreq2-2.11 (in development)
> +
> +- Interactive CGI module [issac]
> +  Allow cgi module to interactively prompt for parameters and cookies when
> +  running a script from the command line and not from a CGI interface
> +
> @section v2_10 Changes with libapreq2-2.10 (not released)
> 
> - Perl Glue [joes]
> 
> Modified: httpd/apreq/trunk/library/module_cgi.c
> URL: 
> http://svn.apache.org/viewvc/httpd/apreq/trunk/library/module_cgi.c?rev=734121&r1=734120&r2=734121&view=diff
> ==============================================================================
> --- httpd/apreq/trunk/library/module_cgi.c (original)
> +++ httpd/apreq/trunk/library/module_cgi.c Tue Jan 13 05:06:44 2009
> @@ -16,6 +16,8 @@
> */
> #include 
> 
> +#define APR_WANT_STRFUNC
> +#include "apr_want.h"
> #include "apreq_module.h"
> #include "apreq_error.h"
> #include "apr_strings.h"
> @@ -39,8 +41,15 @@
> #define CGILOG_LEVELMASK 7
> #define CGILOG_MARK     __FILE__, __LINE__
> 
> -
> -
> +/** Interactive patch:
> + * TODO Don't use 65K buffer
> + * TODO Handle empty/non-existant parameters
> + * TODO Allow body elements to be files
> + * TODO When running body/get/cookies all at once, include previous cached
> + * values (and don't start at 0 in count)
> + * TODO What happens if user does apreq_param, but needs POST value - we'll
> + * never catch it now, as args param will match...
> + */
> 
> struct cgi_handle {
>      struct apreq_handle_t       handle;
> @@ -62,9 +71,16 @@
>      apr_bucket_brigade          *in;
>      apr_bucket_brigade          *tmpbb;
> 
> +    int                         interactive_mode;
> +    const char                  *promptstr;
> +    apr_file_t                  *sout, *sin;
> };
> 
> #define CRLF "\015\012"
> +const char *nullstr;
> +#define DEFAULT_PROMPT "([$t] )$n(\\($l\\))([$d]): "
> +#define MAX_PROMPT_NESTING_LEVELS 8
> +#define MAX_BUFFER_SIZE 65536
> 
> typedef struct {
>      const char *t_name;
> @@ -83,6 +99,161 @@
>      {NULL,      -1},
> };
> 
> +static char* chomp(char* str) {
> +    apr_size_t p = strlen(str);
> +    while (--p >= 0) {
> +        switch ((char)(str[p])) {
> +        case '\015':
> +        case '\012':str[p]='\000';
> +                    break;
> +        default:return str;
> +        }
> +    }
> +    return str;
> +}
> +
> +/** TODO: Support wide-characters */
> +/* prompt takes a apreq_handle and 2 strings - name and type - and prompts a
> +   user for input via stdin/stdout.  used in interactive mode.
> +  
> +   name must be defined.  type can be null.
> +  
> +   we take the promptstring defined in the handle and interpolate variables as
> +   follows:
> +  
> +   $n - name of the variable we're asking for (param 2 to prompt())
> +   $t - type of the variable we're asking for - like cookie, get, post, etc
> +        (param 3 to prompt())
> +   parentheses - if a variable is surrounded by parentheses, and interpolates
> +                 as null, then nothing else in the parentheses will be 
> displayed
> +                 Useful if you want a string to only show up if a given 
> variable
> +                 is available
> +                
> +   These are planned for forward-compatibility, but the underlying features
> +   need some love...  I left these in here just as feature reminders, rather
> +   than completely removing them from the code - at least they provide sanity
> +   testing of the default prompt & parentheses - issac
> +  
> +   $l - label for the param  - the end-user-developer can provide a textual
> +        description of the param (name) being requested (currently unused in
> +        lib)
> +   $d - default value for the param (currently unused in lib)
> +  
> +*/
> +static char *prompt(apreq_handle_t *handle, const char *name,
> +                    const char *type) {
> +    struct cgi_handle *req = (struct cgi_handle *)handle;
> +    const char *defval = nullstr;
> +    const char *label = NULL;
> +    const char *prompt;
> +    char buf[MAX_PROMPT_NESTING_LEVELS][MAX_BUFFER_SIZE];
> +    /* Array of current arg for given p-level */
> +    char *start, curarg[MAX_PROMPT_NESTING_LEVELS] = ""; 
> +    /* Parenthesis level (for argument/text grouping) */
> +    int plevel; 
> +
> +    prompt = req->promptstr - 1;
> +    *buf[0] = plevel = 0;
> +    start = buf[0];
> +
> +    while (*(++prompt) != 0) {
> +        switch (*prompt) {
> +        case '$':  /* interpolate argument; curarg[plevel] => 1 */
> +            prompt++;          
> +            switch (*prompt) {
> +            case 't':
> +                if (type != NULL) {
> +                    strcpy(start, type);
> +                    start += strlen(type);
> +                    curarg[plevel] = 1;
> +                } else {
> +                    curarg[plevel] = curarg[plevel] | 0;
> +                }
> +                break;
> +            case 'n':
> +                /* Name can't be null :-) [If it can, we should 
> +                 * immediately return NULL] */
> +                strcpy(start, name);
> +                start += strlen(name);
> +                curarg[plevel] = 1;
> +                break;
> +            case 'l':
> +                if (label != NULL) {
> +                    strcpy(start, label);
> +                    start += strlen(label);
> +                    curarg[plevel] = 1;
> +                } else {
> +                    curarg[plevel] = curarg[plevel] | 0;
> +                }
> +                break;
> +            case 'd':
> +                /* TODO: Once null defaults are available, 
> +                 * remove if and use nullstr if defval == NULL */
> +                if (defval != NULL) {
> +                    strcpy(start, defval);
> +                    start += strlen(defval);
> +                    curarg[plevel] = 1;
> +                } else {
> +                    curarg[plevel] = curarg[plevel] | 0;
> +                }
> +                break;
> +            default:
> +                /* Handle this? */
> +                break;
> +            }
> +            break;
> +
> +        case '(':
> +            if (plevel <= MAX_PROMPT_NESTING_LEVELS) {
> +                plevel++;
> +                curarg[plevel] = *buf[plevel] = 0;
> +                start = buf[plevel];
> +            }
> +            /* else? */
> +            break;
> +
> +        case ')':
> +            if (plevel > 0) {
> +                *start = 0; /* Null terminate current string */
> +                
> +                /* Move pointer to end of string */
> +                start = buf[--plevel] + strlen(buf[plevel]);
> +                
> +                /* If old curarg was set, concat buffer with level down */
> +                if (curarg[plevel + 1]) {
> +                    strcpy(start, buf[plevel + 1]);
> +                    start += strlen(buf[plevel + 1]);
> +                }
> +
> +                break;
> +            }
> +        case '\\': /* Check next character for escape sequence 
> +                    * (just ignore it for now) */
> +            *prompt++;
> +            /* Fallthrough */
> +
> +        default:      
> +            *start++ = *prompt;
> +        }
> +    }
> +
> +    *start = 0; /* Null terminate the string */
> +    
> +    apr_file_printf(req->sout, "%s", buf[0]);
> +    apr_file_gets(buf[0], MAX_BUFFER_SIZE, req->sin);
> +    chomp(buf[0]);
> +    if (stricmp(buf[0], "")) {
> +/*        if (strcmp(buf[0], nullstr)) */
> +            return apr_pstrdup(handle->pool, buf[0]);
> +/*        return NULL; */
> +    }
> +
> +    if (strcmp(defval, nullstr))
> +        return apr_pstrdup(handle->pool, defval);
> +
> +    return NULL;
> +}
> +
> static const char *cgi_header_in(apreq_handle_t *handle,
>                                   const char *name)
> {
> @@ -179,8 +350,6 @@
>      apr_file_t *file;
>      apr_bucket *eos, *pipe;
> 
> -    req->body  = apr_table_make(pool, APREQ_DEFAULT_NELTS);
> -
>      if (cl_header != NULL) {
>          char *dummy;
>          apr_int64_t content_length = apr_strtoi64(cl_header, &dummy, 0);
> @@ -327,10 +496,35 @@
> {
>      struct cgi_handle *req = (struct cgi_handle *)handle;
> 
> +    if (req->interactive_mode && req->jar_status != APR_SUCCESS) {
> +        char buf[65536];
> +        const char *name, *val;
> +        apreq_cookie_t *p;
> +        int i = 1;
> +        apr_file_printf(req->sout, "[CGI] Requested all cookies\n");
> +        while (1) {
> +            apr_file_printf(req->sout, "[CGI] Please enter a name for cookie %d 
> (or just hit ENTER to end): ",
> +                     i++);
> +            apr_file_gets(buf, 65536, req->sin);
> +            chomp(buf);
> +            if (!strcmp(buf, "")) {
> +                break;
> +            }
> +            name = apr_pstrdup(handle->pool, buf);
> +            val = prompt(handle, name, "cookie");
> +            if (val == NULL)
> +                val = "";
> +            p = apreq_cookie_make(handle->pool, name, strlen(name), val, 
> strlen(val));
> +            apreq_cookie_tainted_on(p);
> +            apreq_value_table_add(&p->v, req->jar);
> +            val = p->v.data;
> +        }
> +        req->jar_status = APR_SUCCESS;
> +    } /** Fallthrough */
> +
>      if (req->jar_status == APR_EINIT) {
>          const char *cookies = cgi_header_in(handle, "Cookie");
>          if (cookies != NULL) {
> -            req->jar = apr_table_make(handle->pool, APREQ_DEFAULT_NELTS);
>              req->jar_status =
>                  apreq_parse_cookie_header(handle->pool, req->jar, cookies);
>          }
> @@ -347,10 +541,35 @@
> {
>      struct cgi_handle *req = (struct cgi_handle *)handle;
> 
> +    if (req->interactive_mode && req->args_status != APR_SUCCESS) {
> +        char buf[65536];
> +        const char *name, *val;
> +        apreq_param_t *p;
> +        int i = 1;
> +        apr_file_printf(req->sout, "[CGI] Requested all argument 
> parameters\n");
> +        while (1) {
> +            apr_file_printf(req->sout, "[CGI] Please enter a name for parameter 
> %d (or jusr hit ENTER to end): ",
> +                     i++);
> +            apr_file_gets(buf, 65536, req->sin);
> +            chomp(buf);
> +            if (!strcmp(buf, "")) {
> +                break;
> +            }
> +            name = apr_pstrdup(handle->pool, buf);
> +            val = prompt(handle, name, "parameter");
> +            if (val == NULL)
> +                val = "";
> +            p = apreq_param_make(handle->pool, name, strlen(name), val, 
> strlen(val));
> +            apreq_param_tainted_on(p);
> +            apreq_value_table_add(&p->v, req->args);
> +            val = p->v.data;
> +        }
> +        req->args_status = APR_SUCCESS;
> +    } /** Fallthrough */
> +
>      if (req->args_status == APR_EINIT) {
>          const char *qs = cgi_query_string(handle);
>          if (qs != NULL) {
> -            req->args = apr_table_make(handle->pool, APREQ_DEFAULT_NELTS);
>              req->args_status =
>                  apreq_parse_query_string(handle->pool, req->args, qs);
>          }
> @@ -370,19 +589,29 @@
> {
>      struct cgi_handle *req = (struct cgi_handle *)handle;
>      const apr_table_t *t;
> -    const char *val;
> +    const char *val = NULL;
> 
> -    if (req->jar_status == APR_EINIT)
> +    if (req->jar_status == APR_EINIT && !req->interactive_mode)
>          cgi_jar(handle, &t);
>      else
>          t = req->jar;
> 
> -    if (t == NULL)
> -        return NULL;
> +    val = (char *)apr_table_get(t, name);
> +    if (val == NULL) {
> +        if (!req->interactive_mode) {
> +            return NULL;
> +        } else {
> +            apreq_cookie_t *p;
> +            val = prompt(handle, name, "cookie");
> +            if (val == NULL)
> +                return NULL;
> +            p = apreq_cookie_make(handle->pool, name, strlen(name), val, 
> strlen(val));
> +            apreq_cookie_tainted_on(p);
> +            apreq_value_table_add(&p->v, req->jar);
> +            val = p->v.data;
> +        }
> +    }
> 
> -    val = apr_table_get(t, name);
> -    if (val == NULL)
> -        return NULL;
> 
>      return apreq_value_to_cookie(val);
> }
> @@ -392,19 +621,29 @@
> {
>      struct cgi_handle *req = (struct cgi_handle *)handle;
>      const apr_table_t *t;
> -    const char *val;
> +    const char *val = NULL;
> 
> -    if (req->args_status == APR_EINIT)
> +    if (req->args_status == APR_EINIT && !req->interactive_mode)
>          cgi_args(handle, &t);
>      else
>          t = req->args;
> 
> -    if (t == NULL)
> -        return NULL;
> -
>      val = apr_table_get(t, name);
> -    if (val == NULL)
> -        return NULL;
> +    if (val == NULL) {
> +        if (!req->interactive_mode) {
> +            return NULL;
> +        } else {
> +            apreq_param_t *p;
> +            val = prompt(handle, name, "parameter");
> +            if (val == NULL)
> +                return NULL;
> +            p = apreq_param_make(handle->pool, name, strlen(name), val, 
> strlen(val));
> +            apreq_param_tainted_on(p);
> +            apreq_value_table_add(&p->v, req->args);
> +            val = p->v.data;
> +        }
> +    }
> +
> 
>      return apreq_value_to_param(val);
> }
> @@ -416,6 +655,32 @@
> {
>      struct cgi_handle *req = (struct cgi_handle *)handle;
> 
> +    if (req->interactive_mode && req->body_status != APR_SUCCESS) {
> +        const char *name, *val;
> +        apreq_param_t *p;
> +        int i = 1;
> +        apr_file_printf(req->sout, "[CGI] Requested all body parameters\n");
> +        while (1) {
> +            char buf[65536];
> +            apr_file_printf(req->sout, "[CGI] Please enter a name for parameter 
> %d (or just hit ENTER to end): ",
> +                     i++);
> +            apr_file_gets(buf, 65536, req->sin);
> +            chomp(buf);
> +            if (!strcmp(buf, "")) {
> +                break;
> +            }
> +            name = apr_pstrdup(handle->pool, buf);
> +            val = prompt(handle, name, "parameter");
> +            if (val == NULL)
> +                val = "";
> +            p = apreq_param_make(handle->pool, name, strlen(name), val, 
> strlen(val));
> +            apreq_param_tainted_on(p);
> +            apreq_value_table_add(&p->v, req->body);
> +            val = p->v.data;
> +        }
> +        req->body_status = APR_SUCCESS;
> +    } /** Fallthrough */
> +    
>      switch (req->body_status) {
> 
>      case APR_EINIT:
> @@ -437,10 +702,28 @@
>                                     const char *name)
> {
>      struct cgi_handle *req = (struct cgi_handle *)handle;
> -    const char *val;
> +    const char *val = NULL;
>      apreq_hook_t *h;
>      apreq_hook_find_param_ctx_t *hook_ctx;
> 
> +    if (req->interactive_mode) {
> +        val = apr_table_get(req->body, name);
> +        if (val == NULL) {
> +            return NULL;
> +        } else {
> +            apreq_param_t *p;
> +            val = prompt(handle, name, "parameter");
> +            if (val == NULL)
> +                return NULL;
> +            p = apreq_param_make(handle->pool, name, strlen(name), val, 
> strlen(val));
> +            apreq_param_tainted_on(p);
> +            apreq_value_table_add(&p->v, req->body);
> +            val = p->v.data;
> +            return apreq_value_to_param(val);
> +        }
> +    }
> +
> +
>      switch (req->body_status) {
> 
>      case APR_SUCCESS:
> @@ -655,6 +938,31 @@
> }
> #endif
> 
> +/** Determine if we're interactive mode or not.  Order is
> +  QUERY_STRING ? NO : Interactive
> +
> + I think we should just rely on GATEWAY_INTERFACE to set
> + non-interactive mode, and be interactive if it's not there
> +
> + Behaviour change should really be:
> + Always check query_string before prompting user,
> +  but rewrite body/cookies to get if interactive
> +
> + Definately more work needed here...
> +*/
> +static int is_interactive_mode(apr_pool_t *pool) {
> +    char *value = NULL, qs[] = "GATEWAY_INTERFACE";
> +    apr_status_t rv;
> +
> +    rv = apr_env_get(&value, qs, pool);
> +    if (rv != APR_SUCCESS)
> +        if (rv == APR_ENOENT)
> +            return 1;
> +        
> +        /** handle else? (!SUCCESS && !ENOENT) */
> +    return 0;
> +}
> +
> static APREQ_MODULE(cgi, 20090110);
> 
> APREQ_DECLARE(apreq_handle_t *)apreq_handle_cgi(apr_pool_t *pool)
> @@ -679,10 +987,24 @@
>      req->read_limit           = (apr_uint64_t) -1;
>      req->brigade_limit        = APREQ_DEFAULT_BRIGADE_LIMIT;
> 
> +    req->args = apr_table_make(pool, APREQ_DEFAULT_NELTS);
> +    req->body = apr_table_make(pool, APREQ_DEFAULT_NELTS);
> +    req->jar  = apr_table_make(pool, APREQ_DEFAULT_NELTS);
> +
>      req->args_status =
>          req->jar_status =
>              req->body_status = APR_EINIT;
> 
> +    if (is_interactive_mode(pool)) {
> +        char buf[10];
> +        req->interactive_mode = 1;
> +        apr_file_open_stdout(&(req->sout), pool);
> +        apr_file_open_stdin(&(req->sin), pool);
> +        req->promptstr=apr_pstrdup(pool, DEFAULT_PROMPT);
> +        sprintf(buf, "%s", NULL);
> +        nullstr = apr_pstrdup(pool, buf);
> +    }
> +
>      apr_pool_userdata_setn(&req->handle, USER_DATA_KEY, NULL, pool);
> 
> #ifdef APR_POOL_DEBUG