You are viewing a plain text version of this content. The canonical link for it is here.
Posted to apreq-cvs@httpd.apache.org by is...@apache.org on 2009/01/13 14:06:57 UTC
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 <assert.h>
+#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
Re: svn commit: r734121 - in /httpd/apreq/trunk: ./ CHANGES library/module_cgi.c
Posted by Joe Schaefer <jo...@yahoo.com>.
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