]> granicus.if.org Git - apache/blob - server/apreq_module_cgi.c
fr doc rebuild.
[apache] / server / apreq_module_cgi.c
1 /*
2 **  Licensed to the Apache Software Foundation (ASF) under one or more
3 ** contributor license agreements.  See the NOTICE file distributed with
4 ** this work for additional information regarding copyright ownership.
5 ** The ASF licenses this file to You under the Apache License, Version 2.0
6 ** (the "License"); you may not use this file except in compliance with
7 ** the License.  You may obtain a copy of the License at
8 **
9 **      http://www.apache.org/licenses/LICENSE-2.0
10 **
11 **  Unless required by applicable law or agreed to in writing, software
12 **  distributed under the License is distributed on an "AS IS" BASIS,
13 **  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 **  See the License for the specific language governing permissions and
15 **  limitations under the License.
16 */
17
18 #define APR_WANT_STRFUNC
19 #include "apr_want.h"
20 #include "apreq_module.h"
21 #include "apreq_error.h"
22 #include "apr_strings.h"
23 #include "apr_lib.h"
24 #include "apr_env.h"
25 #include "apreq_util.h"
26
27 #define USER_DATA_KEY "apreq"
28
29 /* Parroting APLOG_* ... */
30
31 #define CGILOG_EMERG    0       /* system is unusable */
32 #define CGILOG_ALERT    1       /* action must be taken immediately */
33 #define CGILOG_CRIT     2       /* critical conditions */
34 #define CGILOG_ERR      3       /* error conditions */
35 #define CGILOG_WARNING  4       /* warning conditions */
36 #define CGILOG_NOTICE   5       /* normal but significant condition */
37 #define CGILOG_INFO     6       /* informational */
38 #define CGILOG_DEBUG    7       /* debug-level messages */
39
40 #define CGILOG_LEVELMASK 7
41 #define CGILOG_MARK     __FILE__, __LINE__
42
43 /** Interactive patch:
44  * TODO Don't use 65K buffer
45  * TODO Handle empty/non-existent parameters
46  * TODO Allow body elements to be files
47  * TODO When running body/get/cookies all at once, include previous cached
48  * values (and don't start at 0 in count)
49  * TODO What happens if user does apreq_param, but needs POST value - we'll
50  * never catch it now, as args param will match...
51  */
52
53 struct cgi_handle {
54     struct apreq_handle_t       handle;
55
56     apr_table_t                 *jar, *args, *body;
57     apr_status_t                 jar_status,
58                                  args_status,
59                                  body_status;
60
61     apreq_parser_t              *parser;
62     apreq_hook_t                *hook_queue;
63     apreq_hook_t                *find_param;
64
65     const char                  *temp_dir;
66     apr_size_t                   brigade_limit;
67     apr_uint64_t                 read_limit;
68     apr_uint64_t                 bytes_read;
69
70     apr_bucket_brigade          *in;
71     apr_bucket_brigade          *tmpbb;
72
73     int                         interactive_mode;
74     const char                  *promptstr;
75     apr_file_t                  *sout, *sin;
76 };
77
78 #define CRLF "\015\012"
79 static const char *nullstr = 0;
80 #define DEFAULT_PROMPT "([$t] )$n(\\($l\\))([$d]): "
81 #define MAX_PROMPT_NESTING_LEVELS 8
82 #define MAX_BUFFER_SIZE 65536
83
84 typedef struct {
85     const char *t_name;
86     int      t_val;
87 } TRANS;
88
89 static const TRANS priorities[] = {
90     {"emerg",   CGILOG_EMERG},
91     {"alert",   CGILOG_ALERT},
92     {"crit",    CGILOG_CRIT},
93     {"error",   CGILOG_ERR},
94     {"warn",    CGILOG_WARNING},
95     {"notice",  CGILOG_NOTICE},
96     {"info",    CGILOG_INFO},
97     {"debug",   CGILOG_DEBUG},
98     {NULL,      -1},
99 };
100
101 static char* chomp(char* str)
102 {
103     long p = (long)strlen(str);
104     while (--p >= 0) {
105         switch ((char)(str[p])) {
106         case '\015':
107         case '\012':str[p]='\000';
108                     break;
109         default:return str;
110         }
111     }
112     return str;
113 }
114
115 /** TODO: Support wide-characters */
116 /* prompt takes a apreq_handle and 2 strings - name and type - and prompts a
117    user for input via stdin/stdout.  used in interactive mode.
118    
119    name must be defined.  type can be null.
120    
121    we take the promptstring defined in the handle and interpolate variables as
122    follows:
123    
124    $n - name of the variable we're asking for (param 2 to prompt())
125    $t - type of the variable we're asking for - like cookie, get, post, etc
126         (param 3 to prompt())
127    parentheses - if a variable is surrounded by parentheses, and interpolates
128                  as null, then nothing else in the parentheses will be displayed
129                  Useful if you want a string to only show up if a given variable
130                  is available
131                  
132    These are planned for forward-compatibility, but the underlying features
133    need some love...  I left these in here just as feature reminders, rather
134    than completely removing them from the code - at least they provide sanity
135    testing of the default prompt & parentheses - issac
136    
137    $l - label for the param  - the end-user-developer can provide a textual
138         description of the param (name) being requested (currently unused in
139         lib)
140    $d - default value for the param (currently unused in lib)
141    
142 */
143 static char *prompt(apreq_handle_t *handle, const char *name,
144                     const char *type) {
145     struct cgi_handle *req = (struct cgi_handle *)handle;
146     const char *defval = nullstr;
147     const char *label = NULL;
148     const char *cprompt;
149     char buf[MAX_PROMPT_NESTING_LEVELS][MAX_BUFFER_SIZE];
150     /* Array of current arg for given p-level */
151     char *start, curarg[MAX_PROMPT_NESTING_LEVELS] = ""; 
152     /* Parenthesis level (for argument/text grouping) */
153     int plevel; 
154
155     cprompt = req->promptstr - 1;
156     *buf[0] = plevel = 0;
157     start = buf[0];
158
159     while (*(++cprompt) != 0) {
160         switch (*cprompt) {
161         case '$':  /* interpolate argument; curarg[plevel] => 1 */
162             cprompt++;           
163             switch (*cprompt) {
164             case 't':
165                 if (type != NULL) {
166                     strcpy(start, type);
167                     start += strlen(type);
168                     curarg[plevel] = 1;
169                 } else {
170                     curarg[plevel] = curarg[plevel] | 0;
171                 }
172                 break;
173             case 'n':
174                 /* Name can't be null :-) [If it can, we should 
175                  * immediately return NULL] */
176                 strcpy(start, name);
177                 start += strlen(name);
178                 curarg[plevel] = 1;
179                 break;
180             case 'l':
181                 if (label != NULL) {
182                     strcpy(start, label);
183                     start += strlen(label);
184                     curarg[plevel] = 1;
185                 } else {
186                     curarg[plevel] = curarg[plevel] | 0;
187                 }
188                 break;
189             case 'd':
190                 /* TODO: Once null defaults are available, 
191                  * remove if and use nullstr if defval == NULL */
192                 if (defval != NULL) {
193                     strcpy(start, defval);
194                     start += strlen(defval);
195                     curarg[plevel] = 1;
196                 } else {
197                     curarg[plevel] = curarg[plevel] | 0;
198                 }
199                 break;
200             default:
201                 /* Handle this? */
202                 break;
203             }
204             break;
205
206         case '(':
207             if (plevel <= MAX_PROMPT_NESTING_LEVELS) {
208                 plevel++;
209                 curarg[plevel] = *buf[plevel] = 0;
210                 start = buf[plevel];
211             }
212             /* else? */
213             break;
214
215         case ')':
216             if (plevel > 0) {
217                 *start = 0; /* Null terminate current string */
218                 
219                 /* Move pointer to end of string */
220                 plevel--;
221                 start = buf[plevel] + strlen(buf[plevel]);
222                 
223                 /* If old curarg was set, concat buffer with level down */
224                 if (curarg[plevel + 1]) {
225                     strcpy(start, buf[plevel + 1]);
226                     start += strlen(buf[plevel + 1]);
227                 }
228
229                 break;
230             }
231         case '\\': /* Check next character for escape sequence 
232                     * (just ignore it for now) */
233             (void)*cprompt++;
234             /* Fallthrough */
235
236         default:       
237             *start++ = *cprompt;
238         }
239     }
240
241     *start = 0; /* Null terminate the string */
242     
243     apr_file_printf(req->sout, "%s", buf[0]);
244     apr_file_gets(buf[0], MAX_BUFFER_SIZE, req->sin);
245     chomp(buf[0]);
246     if (strcmp(buf[0], "")) {
247 /*        if (strcmp(buf[0], nullstr)) */
248             return apr_pstrdup(handle->pool, buf[0]);
249 /*        return NULL; */
250     }
251
252     if (defval != nullstr)
253         return apr_pstrdup(handle->pool, defval);
254
255     return NULL;
256 }
257
258 static const char *cgi_header_in(apreq_handle_t *handle,
259                                  const char *name)
260 {
261     apr_pool_t *p = handle->pool;
262     char *key = apr_pstrcat(p, "HTTP_", name, NULL);
263     char *k, *value = NULL;
264
265     for (k = key+5; *k; ++k) {
266         if (*k == '-')
267             *k = '_';
268         else
269             *k = apr_toupper(*k);
270     }
271
272     if (!strcmp(key, "HTTP_CONTENT_TYPE")
273         || !strcmp(key, "HTTP_CONTENT_LENGTH")) {
274
275         key += 5; /* strlen("HTTP_") */
276     }
277
278     apr_env_get(&value, key, p);
279
280     return value;
281 }
282
283
284 static void cgi_log_error(const char *file, int line, int level,
285                           apr_status_t status, apreq_handle_t *handle,
286                           const char *fmt, ...)
287 {
288     apr_pool_t *p = handle->pool;
289     char buf[256];
290     char *log_level_string, *ra;
291     const char *remote_addr;
292     unsigned log_level = CGILOG_WARNING;
293     char date[APR_CTIME_LEN];
294     va_list vp;
295 #ifndef WIN32
296     apr_file_t *err;
297 #endif
298
299     va_start(vp, fmt);
300
301     if (apr_env_get(&log_level_string, "LOG_LEVEL", p) == APR_SUCCESS)
302         log_level = (log_level_string[0] - '0');
303
304     level &= CGILOG_LEVELMASK;
305
306     if (level < (int)log_level) {
307
308         if (apr_env_get(&ra, "REMOTE_ADDR", p) == APR_SUCCESS)
309             remote_addr = ra;
310         else
311             remote_addr = "address unavailable";
312
313         apr_ctime(date, apr_time_now());
314
315 #ifndef WIN32
316
317         apr_file_open_stderr(&err, p);
318         apr_file_printf(err, "[%s] [%s] [%s] %s(%d): %s: %s\n",
319                         date, priorities[level].t_name, remote_addr, file, line,
320                         apr_strerror(status,buf,255),apr_pvsprintf(p,fmt,vp));
321         apr_file_flush(err);
322
323 #else
324         fprintf(stderr, "[%s] [%s] [%s] %s(%d): %s: %s\n",
325                 date, priorities[level].t_name, remote_addr, file, line,
326                 apr_strerror(status,buf,255),apr_pvsprintf(p,fmt,vp));
327 #endif
328     }
329
330     va_end(vp);
331
332 }
333
334
335 APR_INLINE
336 static const char *cgi_query_string(apreq_handle_t *handle)
337 {
338     char *value = NULL, qs[] = "QUERY_STRING";
339     apr_env_get(&value, qs, handle->pool);
340     return value;
341 }
342
343
344 static void init_body(apreq_handle_t *handle)
345 {
346     struct cgi_handle *req = (struct cgi_handle *)handle;
347     const char *cl_header = cgi_header_in(handle, "Content-Length");
348     apr_bucket_alloc_t *ba = handle->bucket_alloc;
349     apr_pool_t *pool = handle->pool;
350     apr_file_t *file;
351     apr_bucket *eos, *pipe;
352
353     if (cl_header != NULL) {
354         char *dummy;
355         apr_int64_t content_length = apr_strtoi64(cl_header, &dummy, 10);
356
357         if (dummy == NULL || *dummy != 0) {
358             req->body_status = APREQ_ERROR_BADHEADER;
359             cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status, handle,
360                           "Invalid Content-Length header (%s)", cl_header);
361             return;
362         }
363         else if ((apr_uint64_t)content_length > req->read_limit) {
364             req->body_status = APREQ_ERROR_OVERLIMIT;
365             cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status, handle,
366                           "Content-Length header (%s) exceeds configured "
367                           "max_body limit (%" APR_UINT64_T_FMT ")",
368                           cl_header, req->read_limit);
369             return;
370         }
371     }
372
373     if (req->parser == NULL) {
374         const char *ct_header = cgi_header_in(handle, "Content-Type");
375
376         if (ct_header != NULL) {
377             apreq_parser_function_t pf = apreq_parser(ct_header);
378
379             if (pf != NULL) {
380                 req->parser = apreq_parser_make(pool,
381                                                 ba,
382                                                 ct_header,
383                                                 pf,
384                                                 req->brigade_limit,
385                                                 req->temp_dir,
386                                                 req->hook_queue,
387                                                 NULL);
388             }
389             else {
390                 req->body_status = APREQ_ERROR_NOPARSER;
391                 return;
392             }
393         }
394         else {
395             req->body_status = APREQ_ERROR_NOHEADER;
396             return;
397         }
398     }
399     else {
400         if (req->parser->brigade_limit > req->brigade_limit)
401             req->parser->brigade_limit = req->brigade_limit;
402         if (req->temp_dir != NULL)
403             req->parser->temp_dir = req->temp_dir;
404         if (req->hook_queue != NULL)
405             apreq_parser_add_hook(req->parser, req->hook_queue);
406     }
407
408     req->hook_queue = NULL;
409     req->in         = apr_brigade_create(pool, ba);
410     req->tmpbb      = apr_brigade_create(pool, ba);
411
412     apr_file_open_stdin(&file, pool); /* error status? */
413     pipe = apr_bucket_pipe_create(file, ba);
414     eos = apr_bucket_eos_create(ba);
415     APR_BRIGADE_INSERT_HEAD(req->in, pipe);
416     APR_BRIGADE_INSERT_TAIL(req->in, eos);
417
418     req->body_status = APR_INCOMPLETE;
419
420 }
421
422 static apr_status_t cgi_read(apreq_handle_t *handle,
423                              apr_off_t bytes)
424 {
425     struct cgi_handle *req = (struct cgi_handle *)handle;
426     apr_bucket *e;
427     apr_status_t s;
428
429     if (req->body_status == APR_EINIT)
430         init_body(handle);
431
432     if (req->body_status != APR_INCOMPLETE)
433         return req->body_status;
434
435
436     switch (s = apr_brigade_partition(req->in, bytes, &e)) {
437         apr_off_t len;
438
439     case APR_SUCCESS:
440
441         apreq_brigade_move(req->tmpbb, req->in, e);
442         req->bytes_read += bytes;
443
444         if (req->bytes_read > req->read_limit) {
445             req->body_status = APREQ_ERROR_OVERLIMIT;
446             cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status,
447                           handle, "Bytes read (%" APR_UINT64_T_FMT
448                           ") exceeds configured limit (%" APR_UINT64_T_FMT ")",
449                           req->bytes_read, req->read_limit);
450             break;
451         }
452
453         req->body_status =
454             apreq_parser_run(req->parser, req->body, req->tmpbb);
455         apr_brigade_cleanup(req->tmpbb);
456         break;
457
458
459     case APR_INCOMPLETE:
460
461         apreq_brigade_move(req->tmpbb, req->in, e);
462         s = apr_brigade_length(req->tmpbb, 1, &len);
463
464         if (s != APR_SUCCESS) {
465             req->body_status = s;
466             break;
467         }
468         req->bytes_read += len;
469
470         if (req->bytes_read > req->read_limit) {
471             req->body_status = APREQ_ERROR_OVERLIMIT;
472             cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status, handle,
473                           "Bytes read (%" APR_UINT64_T_FMT
474                           ") exceeds configured limit (%" APR_UINT64_T_FMT ")",
475                           req->bytes_read, req->read_limit);
476
477             break;
478         }
479
480         req->body_status =
481             apreq_parser_run(req->parser, req->body, req->tmpbb);
482         apr_brigade_cleanup(req->tmpbb);
483         break;
484
485     default:
486         req->body_status = s;
487     }
488
489     return req->body_status;
490 }
491
492
493
494 static apr_status_t cgi_jar(apreq_handle_t *handle,
495                             const apr_table_t **t)
496 {
497     struct cgi_handle *req = (struct cgi_handle *)handle;
498
499     if (req->interactive_mode && req->jar_status != APR_SUCCESS) {
500         char buf[65536];
501         const char *name, *val;
502         apreq_cookie_t *p;
503         int i = 1;
504         apr_file_printf(req->sout, "[CGI] Requested all cookies\n");
505         while (1) {
506             apr_file_printf(req->sout, "[CGI] Please enter a name for cookie %d (or just hit ENTER to end): ",
507                      i++);
508             apr_file_gets(buf, 65536, req->sin);
509             chomp(buf);
510             if (!strcmp(buf, "")) {
511                 break;
512             }
513             name = apr_pstrdup(handle->pool, buf);
514             val = prompt(handle, name, "cookie");
515             if (val == NULL)
516                 val = "";
517             p = apreq_cookie_make(handle->pool, name, strlen(name), val, strlen(val));
518             apreq_cookie_tainted_on(p);
519             apreq_value_table_add(&p->v, req->jar);
520         }
521         req->jar_status = APR_SUCCESS;
522     } /** Fallthrough */
523
524     if (req->jar_status == APR_EINIT) {
525         const char *cookies = cgi_header_in(handle, "Cookie");
526         if (cookies != NULL) {
527             req->jar_status =
528                 apreq_parse_cookie_header(handle->pool, req->jar, cookies);
529         }
530         else
531             req->jar_status = APREQ_ERROR_NODATA;
532     }
533
534     *t = req->jar;
535     return req->jar_status;
536 }
537
538 static apr_status_t cgi_args(apreq_handle_t *handle,
539                              const apr_table_t **t)
540 {
541     struct cgi_handle *req = (struct cgi_handle *)handle;
542
543     if (req->interactive_mode && req->args_status != APR_SUCCESS) {
544         char buf[65536];
545         const char *name, *val;
546         apreq_param_t *p;
547         int i = 1;
548         apr_file_printf(req->sout, "[CGI] Requested all argument parameters\n");
549         while (1) {
550             apr_file_printf(req->sout, "[CGI] Please enter a name for parameter %d (or just hit ENTER to end): ",
551                      i++);
552             apr_file_gets(buf, 65536, req->sin);
553             chomp(buf);
554             if (!strcmp(buf, "")) {
555                 break;
556             }
557             name = apr_pstrdup(handle->pool, buf);
558             val = prompt(handle, name, "parameter");
559             if (val == NULL)
560                 val = "";
561             p = apreq_param_make(handle->pool, name, strlen(name), val, strlen(val));
562             apreq_param_tainted_on(p);
563             apreq_value_table_add(&p->v, req->args);
564             val = p->v.data;
565         }
566         req->args_status = APR_SUCCESS;
567     } /** Fallthrough */
568
569     if (req->args_status == APR_EINIT) {
570         const char *qs = cgi_query_string(handle);
571         if (qs != NULL) {
572             req->args_status =
573                 apreq_parse_query_string(handle->pool, req->args, qs);
574         }
575         else
576             req->args_status = APREQ_ERROR_NODATA;
577     }
578
579     *t = req->args;
580     return req->args_status;
581 }
582
583
584
585
586 static apreq_cookie_t *cgi_jar_get(apreq_handle_t *handle,
587                                    const char *name)
588 {
589     struct cgi_handle *req = (struct cgi_handle *)handle;
590     const apr_table_t *t;
591     const char *val = NULL;
592
593     if (req->jar_status == APR_EINIT && !req->interactive_mode)
594         cgi_jar(handle, &t);
595     else
596         t = req->jar;
597
598     val = apr_table_get(t, name);
599     if (val == NULL) {
600         if (!req->interactive_mode) {
601             return NULL;
602         } else {
603             apreq_cookie_t *p;
604             val = prompt(handle, name, "cookie");
605             if (val == NULL)
606                 return NULL;
607             p = apreq_cookie_make(handle->pool, name, strlen(name), val, strlen(val));
608             apreq_cookie_tainted_on(p);
609             apreq_value_table_add(&p->v, req->jar);
610             val = p->v.data;
611         }
612     }
613
614
615     return apreq_value_to_cookie(val);
616 }
617
618 static apreq_param_t *cgi_args_get(apreq_handle_t *handle,
619                                    const char *name)
620 {
621     struct cgi_handle *req = (struct cgi_handle *)handle;
622     const apr_table_t *t;
623     const char *val = NULL;
624
625     if (req->args_status == APR_EINIT && !req->interactive_mode)
626         cgi_args(handle, &t);
627     else
628         t = req->args;
629
630     val = apr_table_get(t, name);
631     if (val == NULL) {
632         if (!req->interactive_mode) {
633             return NULL;
634         } else {
635             apreq_param_t *p;
636             val = prompt(handle, name, "parameter");
637             if (val == NULL)
638                 return NULL;
639             p = apreq_param_make(handle->pool, name, strlen(name), val, strlen(val));
640             apreq_param_tainted_on(p);
641             apreq_value_table_add(&p->v, req->args);
642             val = p->v.data;
643         }
644     }
645
646
647     return apreq_value_to_param(val);
648 }
649
650
651
652 static apr_status_t cgi_body(apreq_handle_t *handle,
653                              const apr_table_t **t)
654 {
655     struct cgi_handle *req = (struct cgi_handle *)handle;
656
657     if (req->interactive_mode && req->body_status != APR_SUCCESS) {
658         const char *name, *val;
659         apreq_param_t *p;
660         int i = 1;
661         apr_file_printf(req->sout, "[CGI] Requested all body parameters\n");
662         while (1) {
663             char buf[65536];
664             apr_file_printf(req->sout, "[CGI] Please enter a name for parameter %d (or just hit ENTER to end): ",
665                      i++);
666             apr_file_gets(buf, 65536, req->sin);
667             chomp(buf);
668             if (!strcmp(buf, "")) {
669                 break;
670             }
671             name = apr_pstrdup(handle->pool, buf);
672             val = prompt(handle, name, "parameter");
673             if (val == NULL)
674                 val = "";
675             p = apreq_param_make(handle->pool, name, strlen(name), val, strlen(val));
676             apreq_param_tainted_on(p);
677             apreq_value_table_add(&p->v, req->body);
678             val = p->v.data;
679         }
680         req->body_status = APR_SUCCESS;
681     } /** Fallthrough */
682     
683     switch (req->body_status) {
684
685     case APR_EINIT:
686         init_body(handle);
687         if (req->body_status != APR_INCOMPLETE)
688             break;
689
690     case APR_INCOMPLETE:
691         while (cgi_read(handle, APREQ_DEFAULT_READ_BLOCK_SIZE)
692                == APR_INCOMPLETE)
693             ;   /*loop*/
694     }
695
696     *t = req->body;
697     return req->body_status;
698 }
699
700 static apreq_param_t *cgi_body_get(apreq_handle_t *handle,
701                                    const char *name)
702 {
703     struct cgi_handle *req = (struct cgi_handle *)handle;
704     const char *val = NULL;
705     apreq_hook_t *h;
706     apreq_hook_find_param_ctx_t *hook_ctx;
707
708     if (req->interactive_mode) {
709         val = apr_table_get(req->body, name);
710         if (val == NULL) {
711             return NULL;
712         } else {
713             apreq_param_t *p;
714             val = prompt(handle, name, "parameter");
715             if (val == NULL)
716                 return NULL;
717             p = apreq_param_make(handle->pool, name, strlen(name), val, strlen(val));
718             apreq_param_tainted_on(p);
719             apreq_value_table_add(&p->v, req->body);
720             val = p->v.data;
721             return apreq_value_to_param(val);
722         }
723     }
724
725
726     switch (req->body_status) {
727
728     case APR_SUCCESS:
729
730         val = apr_table_get(req->body, name);
731         if (val != NULL)
732             return apreq_value_to_param(val);
733         return NULL;
734
735
736     case APR_EINIT:
737
738         init_body(handle);
739         if (req->body_status != APR_INCOMPLETE)
740             return NULL;
741         cgi_read(handle, APREQ_DEFAULT_READ_BLOCK_SIZE);
742
743
744     case APR_INCOMPLETE:
745
746         val = apr_table_get(req->body, name);
747         if (val != NULL)
748             return apreq_value_to_param(val);
749
750         /* Not seen yet, so we need to scan for
751            param while prefetching the body */
752
753         hook_ctx = apr_palloc(handle->pool, sizeof *hook_ctx);
754
755         if (req->find_param == NULL)
756             req->find_param = apreq_hook_make(handle->pool,
757                                               apreq_hook_find_param,
758                                               NULL, NULL);
759         h = req->find_param;
760         h->next = req->parser->hook;
761         req->parser->hook = h;
762         h->ctx = hook_ctx;
763         hook_ctx->name = name;
764         hook_ctx->param = NULL;
765         hook_ctx->prev = req->parser->hook;
766
767         do {
768             cgi_read(handle, APREQ_DEFAULT_READ_BLOCK_SIZE);
769             if (hook_ctx->param != NULL)
770                 return hook_ctx->param;
771         } while (req->body_status == APR_INCOMPLETE);
772
773         req->parser->hook = h->next;
774         return NULL;
775
776
777     default:
778
779         if (req->body == NULL)
780             return NULL;
781
782         val = apr_table_get(req->body, name);
783         if (val != NULL)
784             return apreq_value_to_param(val);
785         return NULL;
786     }
787
788     /* not reached */
789     return NULL;
790 }
791
792 static apr_status_t cgi_parser_get(apreq_handle_t *handle,
793                                    const apreq_parser_t **parser)
794 {
795     struct cgi_handle *req = (struct cgi_handle *)handle;
796
797     *parser = req->parser;
798     return APR_SUCCESS;
799 }
800
801 static apr_status_t cgi_parser_set(apreq_handle_t *handle,
802                                    apreq_parser_t *parser)
803 {
804     struct cgi_handle *req = (struct cgi_handle *)handle;
805
806     if (req->parser == NULL) {
807
808         if (req->hook_queue != NULL) {
809             apr_status_t s = apreq_parser_add_hook(parser, req->hook_queue);
810             if (s != APR_SUCCESS)
811                 return s;
812         }
813         if (req->temp_dir != NULL) {
814             parser->temp_dir = req->temp_dir;
815         }
816         if (req->brigade_limit < parser->brigade_limit) {
817             parser->brigade_limit = req->brigade_limit;
818         }
819
820         req->hook_queue = NULL;
821         req->parser = parser;
822         return APR_SUCCESS;
823     }
824     else
825         return APREQ_ERROR_MISMATCH;
826 }
827
828
829 static apr_status_t cgi_hook_add(apreq_handle_t *handle,
830                                      apreq_hook_t *hook)
831 {
832     struct cgi_handle *req = (struct cgi_handle *)handle;
833
834     if (req->parser != NULL) {
835         return apreq_parser_add_hook(req->parser, hook);
836     }
837     else if (req->hook_queue != NULL) {
838         apreq_hook_t *h = req->hook_queue;
839         while (h->next != NULL)
840             h = h->next;
841         h->next = hook;
842     }
843     else {
844         req->hook_queue = hook;
845     }
846     return APR_SUCCESS;
847
848 }
849
850 static apr_status_t cgi_brigade_limit_set(apreq_handle_t *handle,
851                                           apr_size_t bytes)
852 {
853     struct cgi_handle *req = (struct cgi_handle *)handle;
854     apr_size_t *limit = (req->parser == NULL)
855                       ? &req->brigade_limit
856                       : &req->parser->brigade_limit;
857
858     if (*limit > bytes) {
859         *limit = bytes;
860         return APR_SUCCESS;
861     }
862
863     return APREQ_ERROR_MISMATCH;
864 }
865
866 static apr_status_t cgi_brigade_limit_get(apreq_handle_t *handle,
867                                           apr_size_t *bytes)
868 {
869     struct cgi_handle *req = (struct cgi_handle *)handle;
870     *bytes = (req->parser == NULL)
871            ?  req->brigade_limit
872            :  req->parser->brigade_limit;
873
874     return APR_SUCCESS;
875 }
876
877 static apr_status_t cgi_read_limit_set(apreq_handle_t *handle,
878                                        apr_uint64_t bytes)
879 {
880     struct cgi_handle *req = (struct cgi_handle *)handle;
881
882     if (req->read_limit > bytes && req->bytes_read < bytes) {
883         req->read_limit = bytes;
884         return APR_SUCCESS;
885     }
886
887     return APREQ_ERROR_MISMATCH;
888 }
889
890
891 static apr_status_t cgi_read_limit_get(apreq_handle_t *handle,
892                                        apr_uint64_t *bytes)
893 {
894     struct cgi_handle *req = (struct cgi_handle *)handle;
895     *bytes = req->read_limit;
896     return APR_SUCCESS;
897 }
898
899
900 static apr_status_t cgi_temp_dir_set(apreq_handle_t *handle,
901                                      const char *path)
902 {
903     struct cgi_handle *req = (struct cgi_handle *)handle;
904     const char **temp_dir = (req->parser == NULL)
905                           ? &req->temp_dir
906                           : &req->parser->temp_dir;
907
908
909     if (*temp_dir == NULL && req->bytes_read == 0) {
910         if (path != NULL)
911             *temp_dir = apr_pstrdup(handle->pool, path);
912         return APR_SUCCESS;
913     }
914
915     return APREQ_ERROR_MISMATCH;
916 }
917
918
919 static apr_status_t cgi_temp_dir_get(apreq_handle_t *handle,
920                                      const char **path)
921 {
922     struct cgi_handle *req = (struct cgi_handle *)handle;
923     *path = (req->parser == NULL)
924            ? req->temp_dir
925            : req->parser->temp_dir;
926     return APR_SUCCESS;
927 }
928
929
930
931 #ifdef APR_POOL_DEBUG
932 static apr_status_t ba_cleanup(void *data)
933 {
934     apr_bucket_alloc_t *ba = data;
935     apr_bucket_alloc_destroy(ba);
936     return APR_SUCCESS;
937 }
938 #endif
939
940 /** Determine if we're interactive mode or not.  Order is
941   QUERY_STRING ? NO : Interactive
942
943  I think we should just rely on GATEWAY_INTERFACE to set
944  non-interactive mode, and be interactive if it's not there
945
946  Behaviour change should really be:
947  Always check query_string before prompting user,
948   but rewrite body/cookies to get if interactive
949
950  Definitely more work needed here...
951 */
952 static int is_interactive_mode(apr_pool_t *pool)
953 {
954     char *value = NULL, qs[] = "GATEWAY_INTERFACE";
955     apr_status_t rv;
956
957     rv = apr_env_get(&value, qs, pool);
958     if (rv != APR_SUCCESS)
959         if (rv == APR_ENOENT)
960             return 1;
961         
962         /** handle else? (!SUCCESS && !ENOENT) */
963     return 0;
964 }
965
966 static APREQ_MODULE(cgi, 20090110);
967
968 APREQ_DECLARE(apreq_handle_t *)apreq_handle_cgi(apr_pool_t *pool)
969 {
970     apr_bucket_alloc_t *ba;
971     struct cgi_handle *req;
972     void *data;
973
974     apr_pool_userdata_get(&data, USER_DATA_KEY, pool);
975
976     if (data != NULL)
977         return data;
978
979     req = apr_pcalloc(pool, sizeof *req);
980     ba = apr_bucket_alloc_create(pool);
981
982     /* check pool's userdata first. */
983
984     req->handle.module        = &cgi_module;
985     req->handle.pool          = pool;
986     req->handle.bucket_alloc  = ba;
987     req->read_limit           = (apr_uint64_t) -1;
988     req->brigade_limit        = APREQ_DEFAULT_BRIGADE_LIMIT;
989
990     req->args = apr_table_make(pool, APREQ_DEFAULT_NELTS);
991     req->body = apr_table_make(pool, APREQ_DEFAULT_NELTS);
992     req->jar  = apr_table_make(pool, APREQ_DEFAULT_NELTS);
993
994     req->args_status =
995         req->jar_status =
996             req->body_status = APR_EINIT;
997
998     if (is_interactive_mode(pool)) {
999         req->interactive_mode = 1;
1000         apr_file_open_stdout(&(req->sout), pool);
1001         apr_file_open_stdin(&(req->sin), pool);
1002         req->promptstr=apr_pstrdup(pool, DEFAULT_PROMPT);
1003     }
1004
1005     apr_pool_userdata_setn(&req->handle, USER_DATA_KEY, NULL, pool);
1006
1007 #ifdef APR_POOL_DEBUG
1008     apr_pool_cleanup_register(pool, ba, ba_cleanup, ba_cleanup);
1009 #endif
1010
1011     return &req->handle;
1012 }