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