Imported Upstream version 0.7.1
[hcoop/zz_old/debian/suphp.git] / src / apache / mod_suphp.c
1 /*
2 suPHP - (c)2002-2005 Sebastian Marsching <sebastian@marsching.com>
3
4 This file is part of suPHP.
5
6 suPHP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 suPHP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with suPHP; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21
22 #define CORE_PRIVATE
23
24 #include "httpd.h"
25 #include "http_config.h"
26 #include "http_request.h"
27 #include "http_core.h"
28 #include "http_protocol.h"
29 #include "http_main.h"
30 #include "http_log.h"
31 #include "util_script.h"
32
33
34 #define SUPHP_CONFIG_MODE_SERVER 1
35 #define SUPHP_CONFIG_MODE_DIRECTORY 2
36
37 #define SUPHP_ENGINE_OFF 0
38 #define SUPHP_ENGINE_ON 1
39 #define SUPHP_ENGINE_UNDEFINED 2
40
41 #ifndef SUPHP_PATH_TO_SUPHP
42 #define SUPHP_PATH_TO_SUPHP "/usr/sbin/suphp"
43 #endif
44
45
46 /* Module declaration */
47 module MODULE_VAR_EXPORT suphp_module;
48
49
50 /* Configuration structure */
51 typedef struct {
52 int engine; // Status of suPHP_Engine
53 char *php_config;
54 int cmode; // Server of directory configuration?
55 #ifdef SUPHP_USE_USERGROUP
56 char *target_user;
57 char *target_group;
58 #endif
59 table *handlers;
60 char *php_path;
61 } suphp_conf;
62
63
64 /* Configuration mergers/creators */
65
66 static void *suphp_create_dir_config(pool *p, char *dir) {
67 suphp_conf *cfg = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf));
68 cfg->php_config = NULL;
69 cfg->engine = SUPHP_ENGINE_UNDEFINED;
70 cfg->cmode = SUPHP_CONFIG_MODE_DIRECTORY;
71
72 #ifdef SUPHP_USE_USERGROUP
73 cfg->target_user = NULL;
74 cfg->target_group = NULL;
75 #endif
76
77 /* Create table with 0 initial elements */
78 /* This size may be increased for performance reasons */
79 cfg->handlers = ap_make_table(p, 0);
80
81 return (void *) cfg;
82 }
83
84 static void *suphp_merge_dir_config(pool *p, void *base, void *overrides) {
85 suphp_conf *parent = (suphp_conf *) base;
86 suphp_conf *child = (suphp_conf *) overrides;
87 suphp_conf *merged = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf));
88
89 merged->cmode = SUPHP_CONFIG_MODE_DIRECTORY;
90
91 if (child->php_config)
92 merged->php_config = ap_pstrdup(p, child->php_config);
93 else if (parent->php_config)
94 merged->php_config = ap_pstrdup(p, parent->php_config);
95 else
96 merged->php_config = NULL;
97
98 if (child->engine != SUPHP_ENGINE_UNDEFINED)
99 merged->engine = child->engine;
100 else
101 merged->engine = parent->engine;
102
103 #ifdef SUPHP_USE_USERGROUP
104 if (child->target_user)
105 merged->target_user = ap_pstrdup(p, child->target_user);
106 else if (parent->target_user)
107 merged->target_user = ap_pstrdup(p, parent->target_user);
108 else
109 merged->target_user = NULL;
110
111 if (child->target_group)
112 merged->target_group = ap_pstrdup(p, child->target_group);
113 else if (parent->target_group)
114 merged->target_group = ap_pstrdup(p, parent->target_group);
115 else
116 merged->target_group = NULL;
117 #endif
118
119 merged->handlers = ap_overlay_tables(p, child->handlers, parent->handlers);
120
121 return (void *) merged;
122 }
123
124
125 static void *suphp_create_server_config(pool *p, server_rec *s) {
126 suphp_conf *cfg = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf));
127
128 cfg->engine = SUPHP_ENGINE_UNDEFINED;
129 cfg->php_path = NULL;
130 cfg->cmode = SUPHP_CONFIG_MODE_SERVER;
131
132 #ifdef SUPHP_USE_USERGROUP
133 cfg->target_user = NULL;
134 cfg->target_group = NULL;
135 #endif
136
137 /* Create table with 0 initial elements */
138 /* This size may be increased for performance reasons */
139 cfg->handlers = ap_make_table(p, 0);
140
141 return (void *) cfg;
142 }
143
144
145 static void *suphp_merge_server_config(pool *p, void *base, void *overrides) {
146 suphp_conf *parent = (suphp_conf *) base;
147 suphp_conf *child = (suphp_conf *) overrides;
148 suphp_conf *merged = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf));
149
150 if (child->engine != SUPHP_ENGINE_UNDEFINED)
151 merged->engine = child->engine;
152 else
153 merged->engine = parent->engine;
154
155 if (child->php_path != NULL)
156 merged->php_path = ap_pstrdup(p, child->php_path);
157 else
158 merged->php_path = ap_pstrdup(p, parent->php_path);
159
160 #ifdef SUPHP_USE_USERGROUP
161 if (child->target_user)
162 merged->target_user = ap_pstrdup(p, child->target_user);
163 else if (parent->target_user)
164 merged->target_user = ap_pstrdup(p, parent->target_user);
165 else
166 merged->target_user = NULL;
167
168 if (child->target_group)
169 merged->target_group = ap_pstrdup(p, child->target_group);
170 else if (parent->target_group)
171 merged->target_group = ap_pstrdup(p, parent->target_group);
172 else
173 merged->target_group = NULL;
174 #endif
175
176 merged->handlers = ap_overlay_tables(p, child->handlers, parent->handlers);
177
178 return (void *) merged;
179 }
180
181
182 /* Command handlers */
183
184 static const char *suphp_handle_cmd_engine(cmd_parms *cmd, void *mconfig,
185 int flag) {
186 suphp_conf *cfg;
187
188 if (mconfig)
189 cfg = (suphp_conf *) mconfig;
190 else
191 cfg = (suphp_conf *) ap_get_module_config(cmd->server->module_config,
192 &suphp_module);
193
194 if (flag)
195 cfg->engine = SUPHP_ENGINE_ON;
196 else
197 cfg->engine = SUPHP_ENGINE_OFF;
198
199 return NULL;
200 }
201
202
203 static const char *suphp_handle_cmd_config(cmd_parms *cmd, void *mconfig,
204 const char *arg) {
205 suphp_conf *cfg = (suphp_conf *) mconfig;
206
207 cfg->php_config = ap_pstrdup(cmd->pool, arg);
208
209 return NULL;
210 }
211
212
213 #ifdef SUPHP_USE_USERGROUP
214 static const char *suphp_handle_cmd_user_group(cmd_parms *cmd, void *mconfig,
215 const char *arg1,
216 const char *arg2) {
217 suphp_conf *cfg;
218
219 if (mconfig)
220 cfg = (suphp_conf *) mconfig;
221 else
222 cfg = ap_get_module_config(cmd->server->module_config, &suphp_module);
223
224 cfg->target_user = ap_pstrdup(cmd->pool, arg1);
225 cfg->target_group = ap_pstrdup(cmd->pool, arg2);
226
227 return NULL;
228 }
229 #endif
230
231
232 static const char *suphp_handle_cmd_add_handler(cmd_parms *cmd, void *mconfig,
233 const char *arg) {
234 suphp_conf *cfg;
235 if (mconfig)
236 cfg = (suphp_conf *) mconfig;
237 else
238 cfg = ap_get_module_config(cmd->server->module_config, &suphp_module);
239
240 // Mark active handlers with '1'
241 ap_table_set(cfg->handlers, arg, "1");
242
243 return NULL;
244 }
245
246 static const char *suphp_handle_cmd_remove_handler(cmd_parms *cmd,
247 void *mconfig,
248 const char *arg) {
249 suphp_conf *cfg;
250 if (mconfig)
251 cfg = (suphp_conf *) mconfig;
252 else
253 cfg = ap_get_module_config(cmd->server->module_config, &suphp_module);
254
255 // Mark deactivated handlers with '0'
256 ap_table_set(cfg->handlers, arg, "0");
257
258 return NULL;
259 }
260
261
262 static const char *suphp_handle_cmd_phppath(cmd_parms *cmd, void* mconfig, const char *arg)
263 {
264 suphp_conf *cfg;
265
266 cfg = (suphp_conf *) ap_get_module_config(cmd->server->module_config, &suphp_module);
267
268 cfg->php_path = ap_pstrdup(cmd->pool, arg);
269
270 return NULL;
271 }
272
273
274 /* Command table */
275
276 static const command_rec suphp_cmds[] = {
277 {"suPHP_Engine", suphp_handle_cmd_engine, NULL, RSRC_CONF|ACCESS_CONF,
278 FLAG, "Whether suPHP is on or off, default is off"},
279 {"suPHP_ConfigPath", suphp_handle_cmd_config, NULL, OR_OPTIONS, TAKE1,
280 "Where the php.ini resides, default is the PHP default"},
281 #ifdef SUPHP_USE_USERGROUP
282 {"suPHP_UserGroup", suphp_handle_cmd_user_group, NULL,
283 RSRC_CONF|ACCESS_CONF, TAKE2, "User and group scripts shall be run as"},
284 #endif
285 {"suPHP_AddHandler", suphp_handle_cmd_add_handler, NULL, RSRC_CONF | ACCESS_CONF,
286 ITERATE, "Tells mod_suphp to handle these MIME-types"},
287 {"suphp_RemoveHandler", suphp_handle_cmd_remove_handler, NULL, RSRC_CONF | ACCESS_CONF,
288 ITERATE, "Tells mod_suphp not to handle these MIME-types"},
289 {"suPHP_PHPPath", suphp_handle_cmd_phppath, NULL, RSRC_CONF, TAKE1, "Path to the PHP binary used to render source view"},
290 {NULL}
291 };
292
293
294 /* Helper function which is called when spawning child process */
295
296 int suphp_source_child(void *rp, child_info *cinfo) {
297 request_rec *r = (request_rec *) rp;
298 suphp_conf *conf;
299 pool *p = r->main ? r->main->pool : r->pool;
300 char **argv, **env;
301
302 conf = ap_get_module_config(r->server->module_config, &suphp_module);
303
304 /* We want to log output written to stderr */
305 ap_error_log2stderr(r->server);
306
307 /* prepare argv for new process */
308
309 argv = ap_palloc(p, 4 * sizeof(char *));
310 argv[0] = ap_pstrdup(p, conf->php_path);
311 argv[1] = "-s";
312 argv[2] = ap_pstrdup(p, r->filename);
313 argv[3] = NULL;
314
315 /* prepare environment */
316
317 env = ap_create_environment(p, r->subprocess_env);
318
319 /* We cannot use ap_call_exec because of the interference with suExec */
320 /* So we do everything ourselves */
321
322 /* mandatory cleanup before execution */
323 ap_cleanup_for_exec();
324
325 execve(ap_pstrdup(p, conf->php_path), argv, env);
326
327 /* We are still here? Okay - exec failed */
328 ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "exec of %s failed",
329 conf->php_path);
330 exit(0);
331 /* NOT REACHED */
332 return (0);
333 }
334
335
336 int suphp_child(void *rp, child_info *cinfo) {
337 request_rec *r = (request_rec *) rp;
338 core_dir_config *core_conf;
339 pool *p = r->main ? r->main->pool : r->pool;
340
341 char **argv, **env;
342
343 core_conf = (core_dir_config *) ap_get_module_config(
344 r->per_dir_config, &core_module);
345
346 /* We want to log output written to stderr */
347 ap_error_log2stderr(r->server);
348
349 /* prepare argv for new process */
350
351 argv = ap_palloc(p, 2 * sizeof(char *));
352 argv[0] = SUPHP_PATH_TO_SUPHP;
353 argv[1] = NULL;
354
355 /* prepare environment */
356
357 env = ap_create_environment(p, r->subprocess_env);
358
359 /* We cannot use ap_call_exec because of the interference with suExec */
360 /* So we do everything ourselves */
361
362 /* Set resource limits from core config */
363
364 #ifdef RLIMIT_CPU
365 if (core_conf->limit_cpu != NULL) {
366 if ((setrlimit(RLIMIT_CPU, core_conf->limit_cpu)) != 0) {
367 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
368 "setrlimit: failed to set CPU usage limit");
369 }
370 }
371 #endif /* RLIMIT_CPU */
372 #ifdef RLIMIT_NPROC
373 if (core_conf->limit_nproc != NULL) {
374 if ((setrlimit(RLIMIT_NPROC, core_conf->limit_nproc)) != 0) {
375 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
376 "setrlimit: failed to set process limit");
377 }
378 }
379 #endif /* RLIMIT_NPROC */
380 #ifdef RLIMIT_AS
381 if (core_conf->limit_mem != NULL) {
382 if ((setrlimit(RLIMIT_AS, core_conf->limit_mem)) != 0) {
383 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
384 "setrlimit: failed to set memory limit");
385 }
386 }
387 #endif /* RLIMIT_VMEM */
388 #ifdef RLIMIT_DATA
389 if (core_conf->limit_mem != NULL) {
390 if ((setrlimit(RLIMIT_DATA, core_conf->limit_mem)) != 0) {
391 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
392 "setrlimit: failed to set memory limit");
393 }
394 }
395 #endif /* RLIMIT_VMEM */
396 #ifdef RLIMIT_VMEM
397 if (core_conf->limit_mem != NULL) {
398 if ((setrlimit(RLIMIT_VMEM, core_conf->limit_mem)) != 0) {
399 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
400 "setrlimit: failed to set memory limit");
401 }
402 }
403 #endif /* RLIMIT_VMEM */
404
405 /* mandatory cleanup before execution */
406 ap_cleanup_for_exec();
407
408 execve(SUPHP_PATH_TO_SUPHP, argv, env);
409
410 /* We are still here? Okay - exec failed */
411 ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "exec of %s failed",
412 SUPHP_PATH_TO_SUPHP);
413 exit(0);
414 /* NOT REACHED */
415 return (0);
416 }
417
418
419 /* Handlers */
420
421 static int suphp_source_handler(request_rec *r) {
422 suphp_conf *conf;
423 int rv;
424 pool *p;
425 int fd;
426 BUFF *script_in, *script_out, *script_err;
427 char buffer[HUGE_STRING_LEN];
428
429 if (strcmp(r->method, "GET")) {
430 return DECLINED;
431 }
432
433 conf = ap_get_module_config(r->server->module_config, &suphp_module);
434 if (conf->php_path == NULL) {
435 return DECLINED;
436 }
437
438 p = r->main ? r->main->pool : r->pool;
439
440 fd = open(r->filename, O_NOCTTY, O_RDONLY);
441 if (fd != -1) {
442 close(fd);
443 } else if (errno == EACCES) {
444 ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "access to %s denied",
445 r->filename);
446 return HTTP_FORBIDDEN;
447 } else if (errno == ENOENT || errno == ENOTDIR) {
448 ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "File does not exist: %s",
449 r->filename);
450 return HTTP_NOT_FOUND;
451 } else {
452 ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "could open file: %s",
453 r->filename);
454 return HTTP_NOT_FOUND;
455 }
456
457 /* Fork child process */
458
459 if (!ap_bspawn_child(p, suphp_source_child, (void *) r, kill_after_timeout,
460 &script_in, &script_out, &script_err)) {
461 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
462 "couldn't spawn child process for: %s", r->filename);
463 return HTTP_INTERNAL_SERVER_ERROR;
464 }
465
466 /* Read request body */
467
468 if (ap_should_client_block(r)) {
469 char buffer[HUGE_STRING_LEN];
470 int len_read;
471
472 ap_hard_timeout("reading request body", r);
473
474 while (ap_get_client_block(r, buffer, HUGE_STRING_LEN) > 0) {
475 ap_reset_timeout(r);
476 // Ignore input
477 }
478
479 ap_bflush(script_in);
480 ap_kill_timeout(r);
481 }
482
483 ap_bclose(script_in);
484
485 /* Transfer output from PHP to client */
486
487 if (script_out) {
488 /* Output headers and body */
489
490 r->content_type = "text/html";
491 ap_send_http_header(r);
492 if (!r->header_only) {
493 ap_send_fb(script_out, r);
494 }
495 ap_bclose(script_out);
496 /* Errors have already been logged by child */
497 ap_bclose(script_err);
498 }
499
500 return OK;
501
502 }
503
504
505 static int suphp_handler(request_rec *r) {
506 suphp_conf *sconf;
507 suphp_conf *dconf;
508
509 #ifdef SUPHP_USE_USERGROUP
510 char *ud_user = NULL;
511 char *ud_group = NULL;
512 #endif
513
514 struct stat finfo;
515
516 int rv;
517
518 char *auth_user = NULL;
519 char *auth_pass = NULL;
520
521 pool *p;
522
523 BUFF *script_in, *script_out, *script_err;
524
525 const char *handler;
526
527 sconf = ap_get_module_config(r->server->module_config, &suphp_module);
528 dconf = ap_get_module_config(r->per_dir_config, &suphp_module);
529
530 p = r->main ? r->main->pool : r->pool;
531
532 /* only handle request if mod_suphp is active for this handler */
533 /* check only first byte of value (second has to be \0) */
534 if (r->handler != NULL) {
535 handler = r->handler;
536 } else {
537 handler = r->content_type;
538 }
539 if ((ap_table_get(dconf->handlers, handler) == NULL)) {
540 if ((ap_table_get(sconf->handlers, handler) == NULL)
541 || (*(ap_table_get(sconf->handlers, handler)) == '0')) {
542 return DECLINED;
543 }
544 } else if (*(ap_table_get(dconf->handlers, handler)) == '0') {
545 return DECLINED;
546 }
547
548 /* check if suPHP is enabled for this request */
549
550 if (((sconf->engine != SUPHP_ENGINE_ON)
551 && (dconf->engine != SUPHP_ENGINE_ON))
552 || ((sconf->engine == SUPHP_ENGINE_ON)
553 && (dconf->engine == SUPHP_ENGINE_OFF)))
554 return DECLINED;
555
556 /* check if file is existing and accessible */
557
558 rv = stat(ap_pstrdup(p, r->filename), &finfo);
559 if (rv == 0) {
560 ; /* do nothing */
561 } else if (errno == EACCES) {
562 ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "access to %s denied",
563 r->filename);
564 return HTTP_FORBIDDEN;
565 } else if (errno == ENOENT || errno == ENOTDIR) {
566 ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "File does not exist: %s",
567 r->filename);
568 return HTTP_NOT_FOUND;
569 } else {
570 ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "could not get fileinfo: %s",
571 r->filename);
572 return HTTP_NOT_FOUND;
573 }
574
575 #ifdef SUPHP_USE_USERGROUP
576 if ((sconf->target_user == NULL || sconf->target_group == NULL)
577 && (dconf->target_user == NULL || dconf->target_group == NULL)) {
578
579 /* Identify mod_userdir request
580 As Apache 1.3 does not yet provide a clean way to see
581 whether a request was handled by mod_userdir, we assume
582 this is true for any request beginning with ~ */
583
584 int ud_success = 0; /* set to 1 on success */
585
586 if (!strncmp("/~", r->uri, 2)) {
587 char *username = ap_pstrdup(r->pool, r->uri + 2);
588 char *pos = strchr(username, '/');
589 if (pos) {
590 *pos = 0;
591 if (strlen(username)) {
592 struct passwd *pw;
593 struct group *gr;
594 gid_t gid;
595 char *grpname;
596 if ((pw = getpwnam(username)) != NULL) {
597 gid = pw->pw_gid;
598
599 if ((gr = getgrgid(gid)) != NULL) {
600 grpname = gr->gr_name;
601 } else {
602 if ((grpname = ap_palloc(r->pool, 16)) == NULL) {
603 return HTTP_INTERNAL_SERVER_ERROR;
604 }
605 ap_snprintf(grpname, 16, "#%ld", (long) gid);
606 }
607
608 ud_user = username;
609 ud_group = grpname;
610 ud_success = 1;
611 }
612 }
613 }
614 }
615
616 if (!ud_success) {
617 /* This is not a userdir request and user/group are not
618 set, so log the error and return */
619 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
620 "No user or group set - set suPHP_UserGroup");
621 return HTTP_INTERNAL_SERVER_ERROR;
622 }
623 }
624 #endif /* SUPHP_USE_USERGROUP */
625
626
627 /* prepare environment for new process */
628
629 ap_add_common_vars(r);
630 ap_add_cgi_vars(r);
631
632 ap_table_unset(r->subprocess_env, "SUPHP_PHP_CONFIG");
633 ap_table_unset(r->subprocess_env, "SUPHP_AUTH_USER");
634 ap_table_unset(r->subprocess_env, "SUPHP_AUTH_PW");
635
636 #ifdef SUPHP_USE_USERGROUP
637 ap_table_unset(r->subprocess_env, "SUPHP_USER");
638 ap_table_unset(r->subprocess_env, "SUPHP_GROUP");
639 #endif /* SUPHP_USE_USERGROUP */
640
641 if (dconf->php_config) {
642 ap_table_set(r->subprocess_env, "SUPHP_PHP_CONFIG", dconf->php_config);
643 }
644
645 ap_table_set(r->subprocess_env, "SUPHP_HANDLER", handler);
646
647 if (r->headers_in) {
648 const char *auth;
649 auth = ap_table_get(r->headers_in, "Authorization");
650 if (auth && auth[0] != 0 && strncmp(auth, "Basic ", 6) == 0) {
651 char *user;
652 char *pass;
653 user = ap_pbase64decode(p, auth + 6);
654 if (user) {
655 pass = strchr(user, ':');
656 if (pass) {
657 *pass++ = '\0';
658 auth_user = ap_pstrdup(p, user);
659 auth_pass = ap_pstrdup(p, pass);
660 }
661 }
662 }
663 }
664
665 if (auth_user && auth_pass) {
666 ap_table_setn(r->subprocess_env, "SUPHP_AUTH_USER", auth_user);
667 ap_table_setn(r->subprocess_env, "SUPHP_AUTH_PW", auth_pass);
668 }
669
670 #ifdef SUPHP_USE_USERGROUP
671 if (dconf->target_user) {
672 ap_table_set(r->subprocess_env, "SUPHP_USER", dconf->target_user);
673 } else if (sconf->target_user) {
674 ap_table_set(r->subprocess_env, "SUPHP_USER", sconf->target_user);
675 } else {
676 ap_table_set(r->subprocess_env, "SUPHP_USER", ud_user);
677 }
678
679 if (dconf->target_group) {
680 ap_table_set(r->subprocess_env, "SUPHP_GROUP", dconf->target_group);
681 } else if (sconf->target_group) {
682 ap_table_set(r->subprocess_env, "SUPHP_GROUP", sconf->target_group);
683 } else {
684 ap_table_set(r->subprocess_env, "SUPHP_GROUP", ud_group);
685 }
686 #endif /* SUPHP_USE_USERGROUP */
687
688 /* Fork child process */
689
690 if (!ap_bspawn_child(p, suphp_child, (void *) r, kill_after_timeout,
691 &script_in, &script_out, &script_err)) {
692 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
693 "couldn't spawn child process for: %s", r->filename);
694 return HTTP_INTERNAL_SERVER_ERROR;
695 }
696
697 /* Transfer request body to script */
698
699 if ((rv = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) != OK) {
700 /* Call failed, return status */
701 return rv;
702 }
703
704 if (ap_should_client_block(r)) {
705 char buffer[HUGE_STRING_LEN];
706 int len_read;
707
708 ap_hard_timeout("reading request body", r);
709
710 while ((len_read = ap_get_client_block(r, buffer, HUGE_STRING_LEN))
711 > 0) {
712 ap_reset_timeout(r);
713 if (ap_bwrite(script_in, buffer, len_read) < len_read) {
714 /* silly script stopped reading, soak up remaining message */
715 while (ap_get_client_block(r, buffer, HUGE_STRING_LEN) > 0) {
716 /* dump it */
717 }
718 break;
719 }
720 }
721
722 ap_bflush(script_in);
723 ap_kill_timeout(r);
724 }
725
726 ap_bclose(script_in);
727
728 /* Transfer output from script to client */
729
730 if (script_out) {
731 const char *location;
732 char hbuffer[MAX_STRING_LEN];
733 char buffer[HUGE_STRING_LEN];
734
735 rv = ap_scan_script_header_err_buff(r, script_out, hbuffer);
736 if (rv == HTTP_NOT_MODIFIED) {
737 return rv;
738 } else if (rv) {
739 return HTTP_INTERNAL_SERVER_ERROR;
740 }
741
742 location = ap_table_get(r->headers_out, "Location");
743 if (location && r->status == 200) {
744 /* Soak up all the script output */
745 ap_hard_timeout("reading from script", r);
746 while (ap_bgets(buffer, HUGE_STRING_LEN, script_out) > 0) {
747 continue;
748 }
749 ap_kill_timeout(r);
750 ap_bclose(script_out);
751 ap_bclose(script_err);
752
753 if (location[0] == '/') {
754 /* Redirect has always GET method */
755 r->method = ap_pstrdup(p, "GET");
756 r->method_number = M_GET;
757
758 /* Remove Content-Length - redirect should not read *
759 * request body */
760 ap_table_unset(r->headers_in, "Content-Length");
761
762 /* Do the redirect */
763 ap_internal_redirect_handler(location, r);
764 return OK;
765 } else {
766 /* Script did not set status 302 - so it does not want *
767 * to send its own body. Simply set redirect status */
768 return REDIRECT;
769 }
770 }
771
772 /* Output headers and body */
773
774 ap_send_http_header(r);
775 if (!r->header_only) {
776 ap_send_fb(script_out, r);
777 }
778 ap_bclose(script_out);
779 /* Errors have already been logged by child */
780 ap_bclose(script_err);
781 }
782
783 return OK;
784 }
785
786
787 /* Handlers table */
788
789 static handler_rec suphp_handlers[] = {
790 {"*", suphp_handler},
791 {"x-httpd-php-source", suphp_source_handler},
792 {"application/x-httpd-php-source", suphp_source_handler},
793 {NULL}
794 };
795
796 /* Module definition */
797
798 module MODULE_VAR_EXPORT suphp_module = {
799 STANDARD_MODULE_STUFF,
800 NULL, /* initializer */
801 suphp_create_dir_config, /* create directory config */
802 suphp_merge_dir_config, /* merge directory config */
803 suphp_create_server_config, /* create server config */
804 suphp_merge_server_config, /* merge server config */
805 suphp_cmds, /* command table */
806 suphp_handlers, /* content handlers */
807 NULL, /* URI-to-filename translation */
808 NULL, /* check/validate user_id */
809 NULL, /* check user_id is valid *here* */
810 NULL, /* check access by host address */
811 NULL, /* MIME type checker/setter */
812 NULL, /* fixups */
813 NULL, /* logger */
814 NULL, /* header parser */
815 NULL, /* process initialaization */
816 NULL, /* process exit/cleanup */
817 NULL /* post read_request handling */
818 };