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


Reply via email to