bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / tomcat-connectors-1.2.32-src / native / apache-2.0 / mod_jk.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 /***************************************************************************
19  * Description: Apache 2 plugin for Tomcat                                 *
20  * Author:      Gal Shachor <shachor@il.ibm.com>                           *
21  *              Henri Gomez <hgomez@apache.org>                            *
22  * Version:     $Revision: 1126561 $                                        *
23  ***************************************************************************/
24
25 /*
26  * mod_jk: keeps all servlet related ramblings together.
27  */
28
29 #include "ap_config.h"
30 #include "apr_lib.h"
31 #include "apr_date.h"
32 #include "apr_file_info.h"
33 #include "apr_file_io.h"
34 #include "httpd.h"
35 #include "http_config.h"
36 #include "http_request.h"
37 #include "http_core.h"
38 #include "http_protocol.h"
39 #include "http_main.h"
40 #include "http_log.h"
41 #include "util_script.h"
42 #include "ap_mpm.h"
43
44 #if defined(AS400) && !defined(AS400_UTF8)
45 #include "ap_charset.h"
46 #include "util_charset.h"       /* ap_hdrs_from_ascii */
47 #endif
48
49 /* deprecated with apr 0.9.3 */
50
51 #include "apr_version.h"
52 #if (APR_MAJOR_VERSION == 0) && \
53     (APR_MINOR_VERSION <= 9) && \
54     (APR_PATCH_VERSION < 3)
55 #define apr_filepath_name_get apr_filename_of_pathname
56 #endif
57
58 #include "apr_strings.h"
59
60 /* Yes; sorta sucks - with luck we will clean this up before httpd-2.2
61  * ships, leaving AP_NEED_SET_MUTEX_PERMS def'd as 1 or 0 on all platforms.
62  */
63 #ifdef AP_NEED_SET_MUTEX_PERMS
64 # define JK_NEED_SET_MUTEX_PERMS AP_NEED_SET_MUTEX_PERMS
65 #else
66   /* A special case for httpd-2.0 */
67 # if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) && !defined(AS400)
68 #  define JK_NEED_SET_MUTEX_PERMS 1
69 # else
70 #  define JK_NEED_SET_MUTEX_PERMS 0
71 # endif
72 #endif
73
74 #if JK_NEED_SET_MUTEX_PERMS
75 #include "unixd.h"      /* for unixd_set_global_mutex_perms */
76 #endif
77 /*
78  * jk_ include files
79  */
80 #ifdef NETWARE
81 #define __sys_types_h__
82 #define __sys_socket_h__
83 #define __netdb_h__
84 #define __netinet_in_h__
85 #define __arpa_inet_h__
86 #define __sys_timeval_h__
87 #endif
88
89 #include "jk_global.h"
90 #include "jk_ajp13.h"
91 #include "jk_logger.h"
92 #include "jk_map.h"
93 #include "jk_pool.h"
94 #include "jk_service.h"
95 #include "jk_uri_worker_map.h"
96 #include "jk_util.h"
97 #include "jk_worker.h"
98 #include "jk_shm.h"
99 #include "jk_url.h"
100
101 #define JK_LOG_DEF_FILE             ("logs/mod_jk.log")
102 #define JK_SHM_DEF_FILE             ("logs/jk-runtime-status")
103 #define JK_ENV_REMOTE_ADDR          ("JK_REMOTE_ADDR")
104 #define JK_ENV_REMOTE_PORT          ("JK_REMOTE_PORT")
105 #define JK_ENV_REMOTE_HOST          ("JK_REMOTE_HOST")
106 #define JK_ENV_REMOTE_USER          ("JK_REMOTE_USER")
107 #define JK_ENV_AUTH_TYPE            ("JK_AUTH_TYPE")
108 #define JK_ENV_LOCAL_NAME           ("JK_LOCAL_NAME")
109 #define JK_ENV_LOCAL_PORT           ("JK_LOCAL_PORT")
110 #define JK_ENV_HTTPS                ("HTTPS")
111 #define JK_ENV_CERTS                ("SSL_CLIENT_CERT")
112 #define JK_ENV_CIPHER               ("SSL_CIPHER")
113 #define JK_ENV_SESSION              ("SSL_SESSION_ID")
114 #define JK_ENV_KEY_SIZE             ("SSL_CIPHER_USEKEYSIZE")
115 #define JK_ENV_CERTCHAIN_PREFIX     ("SSL_CLIENT_CERT_CHAIN_")
116 #define JK_ENV_REPLY_TIMEOUT        ("JK_REPLY_TIMEOUT")
117 #define JK_ENV_WORKER_NAME          ("JK_WORKER_NAME")
118 #define JK_NOTE_WORKER_NAME         ("JK_WORKER_NAME")
119 #define JK_NOTE_WORKER_TYPE         ("JK_WORKER_TYPE")
120 #define JK_NOTE_REQUEST_DURATION    ("JK_REQUEST_DURATION")
121 #define JK_NOTE_WORKER_ROUTE        ("JK_WORKER_ROUTE")
122 #define JK_HANDLER          ("jakarta-servlet")
123 #define JK_MAGIC_TYPE       ("application/x-jakarta-servlet")
124 #define NULL_FOR_EMPTY(x)   ((x && !strlen(x)) ? NULL : x)
125 #define STRNULL_FOR_NULL(x) ((x) ? (x) : "(null)")
126 #define JK_LOG_LOCK_KEY     ("jk_log_lock_key")
127 #define JKLOG_MARK          __FILE__,__LINE__
128
129 /*
130  * If you are not using SSL, comment out the following line. It will make
131  * apache run faster.
132  *
133  * Personally, I (DM), think this may be a lie.
134  */
135 #define ADD_SSL_INFO
136
137 /* Needed for Apache 2.3/2.4 per-module log config */
138 #ifdef APLOG_USE_MODULE
139 APLOG_USE_MODULE(jk);
140 #endif
141
142 /* module MODULE_VAR_EXPORT jk_module; */
143 AP_MODULE_DECLARE_DATA module jk_module;
144
145 /*
146  * Environment variable forward object
147  */
148 typedef struct
149 {
150     int has_default;
151     char *name;
152     char *value;
153 } envvar_item;
154
155 /*
156  * Configuration object for the mod_jk module.
157  */
158 typedef struct
159 {
160
161     /*
162      * Log stuff
163      */
164     char *log_file;
165     int log_level;
166     jk_logger_t *log;
167     apr_file_t *jklogfp;
168
169     /*
170      * Mount stuff
171      */
172     char *mount_file;
173     int mount_file_reload;
174     jk_map_t *uri_to_context;
175
176     int mountcopy;
177
178     jk_uri_worker_map_t *uw_map;
179
180     int was_initialized;
181
182     /*
183      * Automatic context path apache alias
184      */
185     char *alias_dir;
186
187     /*
188      * Request Logging
189      */
190
191     char *stamp_format_string;
192     char *format_string;
193     apr_array_header_t *format;
194
195     /*
196      * Setting target worker via environment
197      */
198     char *worker_indicator;
199
200     /*
201      * Configurable environment variables to overwrite
202      * request information using meta data send by a
203      * proxy in front of us.
204      */
205     char *remote_addr_indicator;
206     char *remote_port_indicator;
207     char *remote_host_indicator;
208     char *remote_user_indicator;
209     char *auth_type_indicator;
210     char *local_name_indicator;
211     char *local_port_indicator;
212
213     /*
214      * SSL Support
215      */
216     int ssl_enable;
217     char *https_indicator;
218     char *certs_indicator;
219     char *cipher_indicator;
220     char *session_indicator;    /* Servlet API 2.3 requirement */
221     char *key_size_indicator;   /* Servlet API 2.3 requirement */
222     char *certchain_prefix;     /* Client certificate chain prefix */
223
224     /*
225      * Jk Options
226      */
227     int options;
228     int exclude_options;
229
230     int strip_session;
231     char *strip_session_name;
232     /*
233      * Environment variables support
234      */
235     int envvars_has_own;
236     apr_table_t *envvars;
237     apr_table_t *envvars_def;
238     apr_array_header_t *envvar_items;
239
240     server_rec *s;
241 } jk_server_conf_t;
242
243 struct apache_private_data
244 {
245     jk_pool_t p;
246
247     int read_body_started;
248     request_rec *r;
249 };
250 typedef struct apache_private_data apache_private_data_t;
251
252 static server_rec *main_server = NULL;
253 static jk_logger_t *main_log = NULL;
254 static apr_hash_t *jk_log_fps = NULL;
255 static jk_worker_env_t worker_env;
256 static apr_global_mutex_t *jk_log_lock = NULL;
257 static char *jk_shm_file = NULL;
258 static size_t jk_shm_size = 0;
259 static int jk_shm_size_set = 0;
260 static volatile int jk_watchdog_interval = 0;
261 static volatile int jk_watchdog_running  = 0;
262
263 /*
264  * Worker stuff
265 */
266 static jk_map_t *jk_worker_properties = NULL;
267 static char *jk_worker_file = NULL;
268 static int jk_mount_copy_all = JK_FALSE;
269
270 static int JK_METHOD ws_start_response(jk_ws_service_t *s,
271                                        int status,
272                                        const char *reason,
273                                        const char *const *header_names,
274                                        const char *const *header_values,
275                                        unsigned num_of_headers);
276
277 static int JK_METHOD ws_read(jk_ws_service_t *s,
278                              void *b, unsigned len, unsigned *actually_read);
279
280 static int init_jk(apr_pool_t * pconf, jk_server_conf_t * conf,
281                     server_rec * s);
282
283 static int JK_METHOD ws_write(jk_ws_service_t *s, const void *b, unsigned l);
284
285 static void JK_METHOD ws_add_log_items(jk_ws_service_t *s,
286                                        const char *const *log_names,
287                                        const char *const *log_values,
288                                        unsigned num_of_log_items);
289
290 static void * JK_METHOD ws_next_vhost(void *d);
291
292 static void JK_METHOD ws_vhost_to_text(void *d, char *buf, size_t len);
293
294 static jk_uri_worker_map_t * JK_METHOD ws_vhost_to_uw_map(void *d);
295
296 /* ========================================================================= */
297 /* JK Service step callbacks                                                 */
298 /* ========================================================================= */
299
300 static int JK_METHOD ws_start_response(jk_ws_service_t *s,
301                                        int status,
302                                        const char *reason,
303                                        const char *const *header_names,
304                                        const char *const *header_values,
305                                        unsigned num_of_headers)
306 {
307     unsigned h;
308     apache_private_data_t *p = s->ws_private;
309     request_rec *r = p->r;
310
311     /* If we use proxy error pages, still pass
312      * through context headers needed for special status codes.
313      */
314     if (s->extension.use_server_error_pages &&
315         status >= s->extension.use_server_error_pages) {
316         if (status == HTTP_UNAUTHORIZED) {
317             int found = JK_FALSE;
318             for (h = 0; h < num_of_headers; h++) {
319                 if (!strcasecmp(header_names[h], "WWW-Authenticate")) {
320                     char *tmp = apr_pstrdup(r->pool, header_values[h]);
321                     apr_table_set(r->err_headers_out,
322                                   "WWW-Authenticate", tmp);
323                     found = JK_TRUE;
324                 }
325             }
326             if (found == JK_FALSE) {
327                 jk_server_conf_t *xconf = (jk_server_conf_t *)
328                                            ap_get_module_config(r->server->module_config,
329                                                                 &jk_module);
330                 jk_log(xconf->log, JK_LOG_INFO,
331                        "origin server sent 401 without"
332                        " WWW-Authenticate header");
333             }
334         }
335         return JK_TRUE;
336     }
337
338     /* If there is no reason given (or an empty one),
339      * we'll try to guess a good one.
340      */
341     if (!reason || *reason == '\0') {
342         /* We ask Apache httpd about a good reason phrase. */
343         reason = ap_get_status_line(status);
344         /* Unfortunately it returns with a 500 reason phrase,
345          * whenever it does not know about the given status code,
346          * e.g. in the case of custom status codes.
347          */
348         if (status != 500 && !strncmp(reason, "500 ", 4)) {
349             reason = "Unknown Reason";
350         } else {
351             /* Apache httpd returns a full status line,
352              * but we only want a reason phrase, so skip
353              * the prepended status code.
354              */
355             reason = reason + 4;
356         }
357     }
358     r->status = status;
359     r->status_line = apr_psprintf(r->pool, "%d %s", status, reason);
360
361     for (h = 0; h < num_of_headers; h++) {
362         if (!strcasecmp(header_names[h], "Content-type")) {
363             char *tmp = apr_pstrdup(r->pool, header_values[h]);
364             ap_content_type_tolower(tmp);
365             /* It should be done like this in Apache 2.0 */
366             /* This way, Apache 2.0 will be able to set the output filter */
367             /* and it make jk useable with deflate using */
368             /* AddOutputFilterByType DEFLATE text/html */
369             ap_set_content_type(r, tmp);
370         }
371         else if (!strcasecmp(header_names[h], "Location")) {
372 #if defined(AS400) && !defined(AS400_UTF8)
373             /* Fix escapes in Location Header URL */
374             ap_fixup_escapes((char *)header_values[h],
375                              strlen(header_values[h]), ap_hdrs_from_ascii);
376 #endif
377             apr_table_set(r->headers_out, header_names[h], header_values[h]);
378         }
379         else if (!strcasecmp(header_names[h], "Content-Length")) {
380             apr_table_set(r->headers_out, header_names[h], header_values[h]);
381         }
382         else if (!strcasecmp(header_names[h], "Transfer-Encoding")) {
383             apr_table_set(r->headers_out, header_names[h], header_values[h]);
384         }
385         else if (!strcasecmp(header_names[h], "Last-Modified")) {
386             /*
387              * If the script gave us a Last-Modified header, we can't just
388              * pass it on blindly because of restrictions on future values.
389              */
390             ap_update_mtime(r, apr_date_parse_http(header_values[h]));
391             ap_set_last_modified(r);
392         }
393         else {
394             apr_table_add(r->headers_out, header_names[h], header_values[h]);
395         }
396     }
397
398     /* this NOP function was removed in apache 2.0 alpha14 */
399     /* ap_send_http_header(r); */
400     s->response_started = JK_TRUE;
401
402     return JK_TRUE;
403 }
404
405 /*
406  * Read a chunk of the request body into a buffer.  Attempt to read len
407  * bytes into the buffer.  Write the number of bytes actually read into
408  * actually_read.
409  *
410  * Think of this function as a method of the apache1.3-specific subclass of
411  * the jk_ws_service class.  Think of the *s param as a "this" or "self"
412  * pointer.
413  */
414 static int JK_METHOD ws_read(jk_ws_service_t *s,
415                              void *b, unsigned len, unsigned *actually_read)
416 {
417     if (s && s->ws_private && b && actually_read) {
418         apache_private_data_t *p = s->ws_private;
419         if (!p->read_body_started) {
420             if (ap_should_client_block(p->r)) {
421                 p->read_body_started = JK_TRUE;
422             }
423         }
424
425         if (p->read_body_started) {
426 #if defined(AS400) && !defined(AS400_UTF8)
427             int long rv = OK;
428             if (rv = ap_change_request_body_xlate(p->r, 65535, 65535)) {        /* turn off request body translation */
429                 ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_CRIT, 0, NULL,
430                              "mod_jk: Error on ap_change_request_body_xlate, rc=%d",
431                              rv);
432                 return JK_FALSE;
433             }
434 #else
435             long rv;
436 #endif
437
438             if ((rv = ap_get_client_block(p->r, b, len)) < 0) {
439                 *actually_read = 0;
440             }
441             else {
442                 *actually_read = (unsigned)rv;
443             }
444             return JK_TRUE;
445         }
446     }
447     return JK_FALSE;
448 }
449
450 static void JK_METHOD ws_flush(jk_ws_service_t *s)
451 {
452 #if ! (defined(AS400) && !defined(AS400_UTF8))
453     if (s && s->ws_private) {
454         apache_private_data_t *p = s->ws_private;
455         ap_rflush(p->r);
456     }
457 #endif
458 }
459
460 static void JK_METHOD ws_done(jk_ws_service_t *s)
461 {
462 #if ! (defined(AS400) && !defined(AS400_UTF8))
463     if (s && s->ws_private) {
464         apache_private_data_t *p = s->ws_private;
465         ap_finalize_request_protocol(p->r);
466     }
467 #endif
468 }
469
470 /*
471  * Write a chunk of response data back to the browser.  If the headers
472  * haven't yet been sent over, send over default header values (Status =
473  * 200, basically).
474  *
475  * Write len bytes from buffer b.
476  *
477  * Think of this function as a method of the apache1.3-specific subclass of
478  * the jk_ws_service class.  Think of the *s param as a "this" or "self"
479  * pointer.
480  */
481 /* Works with 4096, fails with 8192 */
482 #ifndef CHUNK_SIZE
483 #define CHUNK_SIZE 4096
484 #endif
485
486 static int JK_METHOD ws_write(jk_ws_service_t *s, const void *b, unsigned int l)
487 {
488 #if defined(AS400) && !defined(AS400_UTF8)
489     int rc;
490 #endif
491
492     if (s && s->ws_private && b) {
493         apache_private_data_t *p = s->ws_private;
494
495         if (l) {
496             /* BUFF *bf = p->r->connection->client; */
497             int r = 0;
498             int ll = l;
499             const char *bb = (const char *)b;
500
501             if (!s->response_started) {
502                 if (main_log)
503                     jk_log(main_log, JK_LOG_INFO,
504                            "Write without start, starting with defaults");
505                 if (!s->start_response(s, 200, NULL, NULL, NULL, 0)) {
506                     return JK_FALSE;
507                 }
508             }
509             if (p->r->header_only) {
510 #if ! (defined(AS400) && !defined(AS400_UTF8))
511                 ap_rflush(p->r);
512 #endif
513                 return JK_TRUE;
514             }
515 #if defined(AS400) && !defined(AS400_UTF8)
516             /* turn off response body translation */
517             rc = ap_change_response_body_xlate(p->r, 65535, 65535);
518             if (rc) {
519                 ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_CRIT, 0, NULL,
520                              "mod_jk: Error on ap_change_response_body_xlate, rc=%d",
521                              rc);
522                 return JK_FALSE;
523             }
524 #endif
525
526             while (ll > 0 && !p->r->connection->aborted) {
527 #if 0
528                 /* Apache 2 output filter does not write
529                  * directly to the wire.
530                  */
531                 int toSend = (ll > CHUNK_SIZE) ? CHUNK_SIZE : ll;
532                 r = ap_rwrite(bb, toSend, p->r);
533 #else
534                 r = ap_rwrite(bb, ll, p->r);
535 #endif
536                 if (JK_IS_DEBUG_LEVEL(main_log))
537                     jk_log(main_log, JK_LOG_DEBUG,
538                            "written %d out of %d", r, ll);
539
540                 if (r < 0)
541                     return JK_FALSE;
542                 ll -= r;
543                 bb += r;
544             }
545             if (ll && p->r->connection->aborted) {
546                 /* Fail if there is something left to send and
547                  * the connection was aborted by the client
548                  */
549                 return JK_FALSE;
550             }
551         }
552
553         return JK_TRUE;
554     }
555     return JK_FALSE;
556 }
557
558 static void JK_METHOD ws_add_log_items(jk_ws_service_t *s,
559                                        const char *const *log_names,
560                                        const char *const *log_values,
561                                        unsigned num_of_log_items)
562 {
563     unsigned h;
564     apache_private_data_t *p = s->ws_private;
565     request_rec *r = p->r;
566
567     for (h = 0; h < num_of_log_items; h++) {
568         if (log_names[h] && log_values[h]) {
569             apr_table_setn(r->notes, log_names[h], log_values[h]);
570         }
571     }
572 }
573
574 static void * JK_METHOD ws_next_vhost(void *d)
575 {
576     server_rec *s = (server_rec *)d;
577     if (s == NULL)
578         return main_server;
579     return s->next;
580 }
581
582 static void JK_METHOD ws_vhost_to_text(void *d, char *buf, size_t len)
583 {
584     server_rec *s = (server_rec *)d;
585     size_t used = 0;
586
587     if (s->server_hostname)
588         used += strlen(s->server_hostname);
589     if (!s->is_virtual) {
590         if (s->port)
591             used += strlen(":XXXXX");
592     }
593     else if (s->addrs) {
594         used += strlen(" [");
595         if (s->addrs->virthost)
596             used += strlen(s->addrs->virthost);
597         if (s->addrs->host_port)
598             used += strlen(":XXXXX");
599         used += strlen("]");
600     }
601
602     if (len < used && len > strlen("XXX")) {
603         strcpy(buf, "XXX");
604         return;
605     }
606
607     used = 0;
608
609     if (s->server_hostname) {
610         strcpy(buf + used, s->server_hostname);
611         used += strlen(s->server_hostname);
612     }
613     if (!s->is_virtual) {
614         if (s->port) {
615             sprintf(buf + used, ":%hu", s->port);
616             used = strlen(buf);
617         }
618     }
619     else if (s->addrs) {
620         strcpy(buf + used, " [");
621         used += strlen(" [");
622         if (s->addrs->virthost) {
623             strcpy(buf + used, s->addrs->virthost);
624             used += strlen(s->addrs->virthost);
625         }
626         if (s->addrs->host_port) {
627             sprintf(buf + used, ":%hu", s->addrs->host_port);
628             used = strlen(buf);
629         }
630         strcpy(buf + used, "]");
631         used += strlen("]");
632     }
633 }
634
635 static jk_uri_worker_map_t * JK_METHOD ws_vhost_to_uw_map(void *d)
636 {
637     server_rec *s = (server_rec *)d;
638     jk_server_conf_t *conf = NULL;
639     if (s == NULL)
640         return NULL;
641     conf = (jk_server_conf_t *) ap_get_module_config(s->module_config,
642                                                      &jk_module);
643     return conf->uw_map;
644 }
645
646 /* ========================================================================= */
647 /* Utility functions                                                         */
648 /* ========================================================================= */
649
650 /* ========================================================================= */
651 /* Log something to Jk log file then exit */
652 static void jk_error_exit(const char *file,
653                           int line,
654                           int level,
655                           const server_rec * s,
656                           apr_pool_t * p, const char *fmt, ...)
657 {
658     va_list ap;
659     char *res;
660     char *ch;
661
662     va_start(ap, fmt);
663     res = apr_pvsprintf(s->process->pool, fmt, ap);
664     va_end(ap);
665     /* Replace all format characters in the resulting message */
666     /* because we feed the message to ap_log_error(). */
667     ch = res;
668     while (*ch) {
669         if (*ch == '%') {
670             *ch = '#';
671         }
672         ch++;
673     }
674
675 #if (MODULE_MAGIC_NUMBER_MAJOR >= 20100606)
676     ap_log_error(file, line, APLOG_MODULE_INDEX, level, 0, s, res);
677 #else
678     ap_log_error(file, line, level, 0, s, res);
679 #endif
680     if ( s ) {
681 #if (MODULE_MAGIC_NUMBER_MAJOR >= 20100606)
682         ap_log_error(file, line, APLOG_MODULE_INDEX, level, 0, NULL, res);
683 #else
684         ap_log_error(file, line, level, 0, NULL, res);
685 #endif
686     }
687
688     /* Exit process */
689     exit(1);
690 }
691
692 static jk_uint64_t get_content_length(request_rec * r)
693 {
694     if (r->clength > 0) {
695         return (jk_uint64_t)r->clength;
696     }
697     else if (r->main == NULL || r->main == r) {
698         char *lenp = (char *)apr_table_get(r->headers_in, "Content-Length");
699
700         if (lenp) {
701             jk_uint64_t rc = 0;
702             if (sscanf(lenp, "%" JK_UINT64_T_FMT, &rc) > 0 && rc > 0) {
703                 return rc;
704             }
705         }
706     }
707
708     return 0;
709 }
710
711 /* Retrieve string value from env var, use default if env var does not exist. */
712 static const char *get_env_string(request_rec *r, const char *def,
713                                   char *env, int null_for_empty)
714 {
715     char *value = (char *)apr_table_get(r->subprocess_env, env);
716     if (value)
717         return null_for_empty ? NULL_FOR_EMPTY(value) : value;
718     return null_for_empty ? NULL_FOR_EMPTY(def) : def;
719 }
720
721 /* Retrieve integer value from env var, use default if env var does not exist. */
722 static int get_env_int(request_rec *r, int def, char *env)
723 {
724     char *value = (char *)apr_table_get(r->subprocess_env, env);
725     if (value)
726         return atoi(value);
727     return def;
728 }
729
730 static int init_ws_service(apache_private_data_t * private_data,
731                            jk_ws_service_t *s, jk_server_conf_t * conf)
732 {
733     int size;
734     request_rec *r = private_data->r;
735     char *ssl_temp = NULL;
736     const char *reply_timeout = NULL;
737     rule_extension_t *e;
738
739     /* Copy in function pointers (which are really methods) */
740     s->start_response = ws_start_response;
741     s->read = ws_read;
742     s->write = ws_write;
743     s->flush = ws_flush;
744     s->done = ws_done;
745     s->add_log_items = ws_add_log_items;
746     s->next_vhost = ws_next_vhost;
747     s->vhost_to_text = ws_vhost_to_text;
748     s->vhost_to_uw_map = ws_vhost_to_uw_map;
749
750     s->auth_type = get_env_string(r, r->ap_auth_type,
751                                   conf->auth_type_indicator, 1);
752     s->remote_user = get_env_string(r, r->user,
753                                     conf->remote_user_indicator, 1);
754
755     s->protocol = r->protocol;
756     s->remote_host = (char *)ap_get_remote_host(r->connection,
757                                                 r->per_dir_config,
758                                                 REMOTE_HOST, NULL);
759     s->remote_host = get_env_string(r, s->remote_host,
760                                     conf->remote_host_indicator, 1);
761     if (conf->options & JK_OPT_FWDLOCAL) {
762         s->remote_addr = r->connection->local_ip;
763         /* We don't know the client port of the backend connection. */
764         s->remote_port = "0";
765     }
766     else {
767         s->remote_addr = r->connection->remote_ip;
768         s->remote_port = apr_itoa(r->pool, r->connection->remote_addr->port);
769     }
770     s->remote_addr = get_env_string(r, s->remote_addr,
771                                     conf->remote_addr_indicator, 1);
772     s->remote_port = get_env_string(r, s->remote_port,
773                                     conf->remote_port_indicator, 1);
774
775     if (conf->options & JK_OPT_FLUSHPACKETS)
776         s->flush_packets = 1;
777     if (conf->options & JK_OPT_FLUSHEADER)
778         s->flush_header = 1;
779
780     e = (rule_extension_t *)ap_get_module_config(r->request_config, &jk_module);
781     if (e) {
782         s->extension.reply_timeout = e->reply_timeout;
783         s->extension.use_server_error_pages = e->use_server_error_pages;
784         if (e->activation) {
785             s->extension.activation = apr_palloc(r->pool, e->activation_size * sizeof(int));
786             memcpy(s->extension.activation, e->activation, e->activation_size * sizeof(int));
787         }
788         if (e->fail_on_status_size > 0) {
789             s->extension.fail_on_status_size = e->fail_on_status_size;
790             s->extension.fail_on_status = apr_palloc(r->pool, e->fail_on_status_size * sizeof(int));
791             memcpy(s->extension.fail_on_status, e->fail_on_status, e->fail_on_status_size * sizeof(int));
792         }
793     }
794     reply_timeout = apr_table_get(r->subprocess_env, "JK_REPLY_TIMEOUT");
795     if (reply_timeout) {
796         int r = atoi(reply_timeout);
797         if (r >= 0)
798             s->extension.reply_timeout = r;
799     }
800
801     if (conf->options & JK_OPT_DISABLEREUSE)
802         s->disable_reuse = 1;
803
804     /* get server name */
805     s->server_name = get_env_string(r, (char *)ap_get_server_name(r),
806                                     conf->local_name_indicator, 0);
807
808     /* get the real port (otherwise redirect failed) */
809     /* XXX: use apache API for getting server port
810      *
811      * Pre 1.2.7 versions used:
812      * s->server_port = r->connection->local_addr->port;
813      */
814     s->server_port = get_env_int(r, ap_get_server_port(r),
815                                  conf->local_port_indicator);
816
817 #if ((AP_MODULE_MAGIC_AT_LEAST(20051115,4)) && !defined(API_COMPATIBILITY)) || (MODULE_MAGIC_NUMBER_MAJOR >= 20060905)
818     s->server_software = (char *)ap_get_server_description();
819 #else
820     s->server_software = (char *)ap_get_server_version();
821 #endif
822     s->method = (char *)r->method;
823     s->content_length = get_content_length(r);
824     s->is_chunked = r->read_chunked;
825     s->no_more_chunks = 0;
826 #if defined(AS400) && !defined(AS400_UTF8)
827     /* Get the query string that is not translated to EBCDIC  */
828     s->query_string = ap_get_original_query_string(r);
829 #else
830     s->query_string = r->args;
831 #endif
832
833     /*
834      * The 2.2 servlet spec errata says the uri from
835      * HttpServletRequest.getRequestURI() should remain encoded.
836      * [http://java.sun.com/products/servlet/errata_042700.html]
837      *
838      * We use JkOptions to determine which method to be used
839      *
840      * ap_escape_uri is the latest recommanded but require
841      *               some java decoding (in TC 3.3 rc2)
842      *
843      * unparsed_uri is used for strict compliance with spec and
844      *              old Tomcat (3.2.3 for example)
845      *
846      * uri is use for compatibilty with mod_rewrite with old Tomcats
847      */
848
849     switch (conf->options & JK_OPT_FWDURIMASK) {
850
851     case JK_OPT_FWDURICOMPATUNPARSED:
852         s->req_uri = r->unparsed_uri;
853         if (s->req_uri != NULL) {
854             char *query_str = strchr(s->req_uri, '?');
855             if (query_str != NULL) {
856                 *query_str = 0;
857             }
858         }
859
860         break;
861
862     case JK_OPT_FWDURICOMPAT:
863         s->req_uri = r->uri;
864         break;
865
866     case JK_OPT_FWDURIPROXY:
867         size = 3 * strlen(r->uri) + 1;
868         s->req_uri = apr_palloc(r->pool, size);
869         jk_canonenc(r->uri, s->req_uri, size);
870         break;
871
872     case JK_OPT_FWDURIESCAPED:
873         s->req_uri = ap_escape_uri(r->pool, r->uri);
874         break;
875
876     default:
877         return JK_FALSE;
878     }
879
880     if (conf->ssl_enable || conf->envvars) {
881         ap_add_common_vars(r);
882
883         if (conf->ssl_enable) {
884             ssl_temp =
885                 (char *)apr_table_get(r->subprocess_env,
886                                       conf->https_indicator);
887             if (ssl_temp && !strcasecmp(ssl_temp, "on")) {
888                 s->is_ssl = JK_TRUE;
889                 s->ssl_cert =
890                     (char *)apr_table_get(r->subprocess_env,
891                                           conf->certs_indicator);
892
893                 if (conf->options & JK_OPT_FWDCERTCHAIN) {
894                     const apr_array_header_t *t = apr_table_elts(r->subprocess_env);
895                     if (t && t->nelts) {
896                         int i;
897                         const apr_table_entry_t *elts = (const apr_table_entry_t *) t->elts;
898                         apr_array_header_t *certs = apr_array_make(r->pool, 1, sizeof(char *));
899                         *(const char **)apr_array_push(certs) = s->ssl_cert;
900                         for (i = 0; i < t->nelts; i++) {
901                             if (!elts[i].key)
902                                 continue;
903                             if (!strncasecmp(elts[i].key, conf->certchain_prefix,
904                                              strlen(conf->certchain_prefix)))
905                                 *(const char **)apr_array_push(certs) = elts[i].val;
906                         }
907                         s->ssl_cert = apr_array_pstrcat(r->pool, certs, '\0');
908                     }
909                 }
910
911                 if (s->ssl_cert) {
912                     s->ssl_cert_len = strlen(s->ssl_cert);
913                     if (JK_IS_DEBUG_LEVEL(conf->log)) {
914                         jk_log(conf->log, JK_LOG_DEBUG,
915                                "SSL client certificate (%d bytes): %s",
916                                s->ssl_cert_len, s->ssl_cert);
917                     }
918                 }
919                 /* Servlet 2.3 API */
920                 s->ssl_cipher =
921                     (char *)apr_table_get(r->subprocess_env,
922                                           conf->cipher_indicator);
923                 s->ssl_session =
924                     (char *)apr_table_get(r->subprocess_env,
925                                           conf->session_indicator);
926
927                 if (conf->options & JK_OPT_FWDKEYSIZE) {
928                     /* Servlet 2.3 API */
929                     ssl_temp = (char *)apr_table_get(r->subprocess_env,
930                                                      conf->
931                                                      key_size_indicator);
932                     if (ssl_temp)
933                         s->ssl_key_size = atoi(ssl_temp);
934                 }
935
936
937             }
938         }
939
940         if (conf->envvars) {
941             const apr_array_header_t *t = conf->envvar_items;
942             if (t && t->nelts) {
943                 int i;
944                 int j = 0;
945                 envvar_item *elts = (envvar_item *) t->elts;
946                 s->attributes_names = apr_palloc(r->pool,
947                                                  sizeof(char *) * t->nelts);
948                 s->attributes_values = apr_palloc(r->pool,
949                                                   sizeof(char *) * t->nelts);
950
951                 for (i = 0; i < t->nelts; i++) {
952                     s->attributes_names[i - j] = elts[i].name;
953                     s->attributes_values[i - j] =
954                         (char *)apr_table_get(r->subprocess_env, elts[i].name);
955                     if (!s->attributes_values[i - j]) {
956                         if (elts[i].has_default) {
957                             s->attributes_values[i - j] = elts[i].value;
958                         }
959                         else {
960                             s->attributes_values[i - j] = "";
961                             s->attributes_names[i - j] = "";
962                             j++;
963                         }
964                     }
965                 }
966
967                 s->num_attributes = t->nelts - j;
968             }
969         }
970     }
971
972     if (r->headers_in && apr_table_elts(r->headers_in)) {
973         int need_content_length_header = (!s->is_chunked
974                                           && s->content_length ==
975                                           0) ? JK_TRUE : JK_FALSE;
976         const apr_array_header_t *t = apr_table_elts(r->headers_in);
977         if (t && t->nelts) {
978             int i;
979             apr_table_entry_t *elts = (apr_table_entry_t *) t->elts;
980             s->num_headers = t->nelts;
981             /* allocate an extra header slot in case we need to add a content-length header */
982             s->headers_names =
983                 apr_palloc(r->pool, sizeof(char *) * (t->nelts + 1));
984             s->headers_values =
985                 apr_palloc(r->pool, sizeof(char *) * (t->nelts + 1));
986             if (!s->headers_names || !s->headers_values)
987                 return JK_FALSE;
988             for (i = 0; i < t->nelts; i++) {
989                 char *hname = apr_pstrdup(r->pool, elts[i].key);
990                 s->headers_values[i] = apr_pstrdup(r->pool, elts[i].val);
991                 s->headers_names[i] = hname;
992                 if (need_content_length_header &&
993                     !strcasecmp(s->headers_names[i], "content-length")) {
994                     need_content_length_header = JK_FALSE;
995                 }
996             }
997             /* Add a content-length = 0 header if needed.
998              * Ajp13 assumes an absent content-length header means an unknown,
999              * but non-zero length body.
1000              */
1001             if (need_content_length_header) {
1002                 s->headers_names[s->num_headers] = "content-length";
1003                 s->headers_values[s->num_headers] = "0";
1004                 s->num_headers++;
1005             }
1006         }
1007         /* Add a content-length = 0 header if needed. */
1008         else if (need_content_length_header) {
1009             s->headers_names = apr_palloc(r->pool, sizeof(char *));
1010             s->headers_values = apr_palloc(r->pool, sizeof(char *));
1011             if (!s->headers_names || !s->headers_values)
1012                 return JK_FALSE;
1013             s->headers_names[0] = "content-length";
1014             s->headers_values[0] = "0";
1015             s->num_headers++;
1016         }
1017     }
1018     s->uw_map = conf->uw_map;
1019
1020     /* Dump all connection param so we can trace what's going to
1021      * the remote tomcat
1022      */
1023     if (JK_IS_DEBUG_LEVEL(conf->log)) {
1024         jk_log(conf->log, JK_LOG_DEBUG,
1025                "Service protocol=%s method=%s ssl=%s host=%s addr=%s name=%s port=%d auth=%s user=%s laddr=%s raddr=%s uri=%s",
1026                STRNULL_FOR_NULL(s->protocol),
1027                STRNULL_FOR_NULL(s->method),
1028                s->is_ssl ? "true" : "false",
1029                STRNULL_FOR_NULL(s->remote_host),
1030                STRNULL_FOR_NULL(s->remote_addr),
1031                STRNULL_FOR_NULL(s->server_name),
1032                s->server_port,
1033                STRNULL_FOR_NULL(s->auth_type),
1034                STRNULL_FOR_NULL(s->remote_user),
1035                STRNULL_FOR_NULL(r->connection->local_ip),
1036                STRNULL_FOR_NULL(r->connection->remote_ip),
1037                STRNULL_FOR_NULL(s->req_uri));
1038     }
1039
1040     return JK_TRUE;
1041 }
1042
1043 /*
1044  * The JK module command processors
1045  *
1046  * The below are all installed so that Apache calls them while it is
1047  * processing its config files.  This allows configuration info to be
1048  * copied into a jk_server_conf_t object, which is then used for request
1049  * filtering/processing.
1050  *
1051  * See jk_cmds definition below for explanations of these options.
1052  */
1053
1054 /*
1055  * JkMountCopy directive handling
1056  *
1057  * JkMountCopy On/Off/All
1058  */
1059
1060 static const char *jk_set_mountcopy(cmd_parms * cmd,
1061                                     void *dummy, const char *mount_copy)
1062 {
1063     server_rec *s = cmd->server;
1064     jk_server_conf_t *conf =
1065         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1066                                                   &jk_module);
1067
1068     if (! strcasecmp(mount_copy, "all")) {
1069         const char *err_string = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1070         if (err_string != NULL) {
1071             return err_string;
1072         }
1073         jk_mount_copy_all = JK_TRUE;
1074     }
1075     else if (strcasecmp(mount_copy, "on") && strcasecmp(mount_copy, "off")) {
1076         return "JkMountCopy must be All, On or Off";
1077     }
1078     else {
1079         conf->mountcopy = strcasecmp(mount_copy, "off") ? JK_TRUE : JK_FALSE;
1080     }
1081
1082     return NULL;
1083 }
1084
1085 /*
1086  * JkMount directive handling
1087  *
1088  * JkMount URI(context) worker
1089  */
1090
1091 static const char *jk_mount_context(cmd_parms * cmd,
1092                                     void *dummy,
1093                                     const char *context,
1094                                     const char *worker)
1095 {
1096     server_rec *s = cmd->server;
1097     jk_server_conf_t *conf =
1098         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1099                                                   &jk_module);
1100     const char *c, *w;
1101
1102     if (worker != NULL && cmd->path == NULL ) {
1103         c = context;
1104         w = worker;
1105     }
1106     else if (worker == NULL && cmd->path != NULL) {
1107         c = cmd->path;
1108         w = context;
1109     }
1110     else {
1111         if (worker == NULL)
1112             return "JkMount needs a path when not defined in a location";
1113         else
1114             return "JkMount can not have a path when defined in a location";
1115     }
1116
1117     if (c[0] != '/')
1118         return "JkMount context should start with /";
1119
1120     if (!conf->uri_to_context) {
1121         if (!jk_map_alloc(&(conf->uri_to_context))) {
1122             return "JkMount Memory error";
1123         }
1124     }
1125     /*
1126      * Add the new worker to the alias map.
1127      */
1128     jk_map_put(conf->uri_to_context, c, w, NULL);
1129     return NULL;
1130 }
1131
1132 /*
1133  * JkUnMount directive handling
1134  *
1135  * JkUnMount URI(context) worker
1136  */
1137
1138 static const char *jk_unmount_context(cmd_parms * cmd,
1139                                       void *dummy,
1140                                       const char *context,
1141                                       const char *worker)
1142 {
1143     server_rec *s = cmd->server;
1144     jk_server_conf_t *conf =
1145         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1146                                                   &jk_module);
1147     char *uri;
1148     const char *c, *w;
1149
1150     if (worker != NULL && cmd->path == NULL ) {
1151         c = context;
1152         w = worker;
1153     }
1154     else if (worker == NULL && cmd->path != NULL) {
1155         c = cmd->path;
1156         w = context;
1157     }
1158     else {
1159         if (worker == NULL)
1160             return "JkUnMount needs a path when not defined in a location";
1161         else
1162             return "JkUnMount can not have a path when defined in a location";
1163     }
1164
1165     if (c[0] != '/')
1166         return "JkUnMount context should start with /";
1167
1168     uri = apr_pstrcat(cmd->temp_pool, "!", c, NULL);
1169
1170     if (!conf->uri_to_context) {
1171         if (!jk_map_alloc(&(conf->uri_to_context))) {
1172             return "JkUnMount Memory error";
1173         }
1174     }
1175     /*
1176      * Add the new worker to the alias map.
1177      */
1178     jk_map_put(conf->uri_to_context, uri, w, NULL);
1179     return NULL;
1180 }
1181
1182
1183 /*
1184  * JkWorkersFile Directive Handling
1185  *
1186  * JkWorkersFile file
1187  */
1188
1189 static const char *jk_set_worker_file(cmd_parms * cmd,
1190                                       void *dummy, const char *worker_file)
1191 {
1192     const char *err_string = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1193     if (err_string != NULL) {
1194         return err_string;
1195     }
1196
1197     if (jk_worker_file != NULL)
1198         return "JkWorkersFile only allowed once";
1199
1200     /* we need an absolute path (ap_server_root_relative does the ap_pstrdup) */
1201     jk_worker_file = ap_server_root_relative(cmd->pool, worker_file);
1202
1203     if (jk_worker_file == NULL)
1204         return "JkWorkersFile file name invalid";
1205
1206     if (jk_file_exists(jk_worker_file) != JK_TRUE)
1207         return "JkWorkersFile: Can't find the workers file specified";
1208
1209     return NULL;
1210 }
1211
1212 /*
1213  * JkMountFile Directive Handling
1214  *
1215  * JkMountFile file
1216  */
1217
1218 static const char *jk_set_mount_file(cmd_parms * cmd,
1219                                      void *dummy, const char *mount_file)
1220 {
1221     server_rec *s = cmd->server;
1222
1223     jk_server_conf_t *conf =
1224         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1225                                                   &jk_module);
1226
1227     /* we need an absolute path (ap_server_root_relative does the ap_pstrdup) */
1228     conf->mount_file = ap_server_root_relative(cmd->pool, mount_file);
1229
1230     if (conf->mount_file == NULL)
1231         return "JkMountFile file name invalid";
1232
1233     if (jk_file_exists(conf->mount_file) != JK_TRUE)
1234         return "JkMountFile: Can't find the mount file specified";
1235
1236     if (!conf->uri_to_context) {
1237         if (!jk_map_alloc(&(conf->uri_to_context))) {
1238             return "JkMountFile Memory error";
1239         }
1240     }
1241
1242     return NULL;
1243 }
1244
1245 /*
1246  * JkMountFileReload Directive Handling
1247  *
1248  * JkMountFileReload seconds
1249  */
1250
1251 static const char *jk_set_mount_file_reload(cmd_parms * cmd,
1252                                             void *dummy, const char *mount_file_reload)
1253 {
1254     server_rec *s = cmd->server;
1255     int interval;
1256
1257     jk_server_conf_t *conf =
1258         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1259                                                   &jk_module);
1260
1261     interval = atoi(mount_file_reload);
1262     if (interval < 0) {
1263         interval = 0;
1264     }
1265
1266     conf->mount_file_reload = interval;
1267
1268     return NULL;
1269 }
1270
1271 /*
1272  * JkWatchdogInterval Directive Handling
1273  *
1274  * JkWatchdogInterval seconds
1275  */
1276
1277 static const char *jk_set_watchdog_interval(cmd_parms * cmd,
1278                                             void *dummy, const char *watchdog_interval)
1279 {
1280     const char *err_string = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1281     if (err_string != NULL) {
1282         return err_string;
1283     }
1284
1285 #if APR_HAS_THREADS
1286     jk_watchdog_interval = atoi(watchdog_interval);
1287     if (jk_watchdog_interval < 0) {
1288         jk_watchdog_interval = 0;
1289     }
1290     return NULL;
1291 #else
1292     return "JkWatchdogInterval: APR was compiled without threading support. Cannot create watchdog thread";
1293 #endif
1294 }
1295
1296 /*
1297  * JkLogFile Directive Handling
1298  *
1299  * JkLogFile file
1300  */
1301
1302 static const char *jk_set_log_file(cmd_parms * cmd,
1303                                    void *dummy, const char *log_file)
1304 {
1305     server_rec *s = cmd->server;
1306     jk_server_conf_t *conf =
1307         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1308                                                   &jk_module);
1309
1310     /* we need an absolute path */
1311     if (*log_file != '|')
1312         conf->log_file = ap_server_root_relative(cmd->pool, log_file);
1313     else
1314         conf->log_file = apr_pstrdup(cmd->pool, log_file);
1315
1316     if (conf->log_file == NULL)
1317         return "JkLogFile file name invalid";
1318
1319     return NULL;
1320 }
1321
1322 /*
1323  * JkShmFile Directive Handling
1324  *
1325  * JkShmFile file
1326  */
1327
1328 static const char *jk_set_shm_file(cmd_parms * cmd,
1329                                    void *dummy, const char *shm_file)
1330 {
1331     const char *err_string = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1332     if (err_string != NULL) {
1333         return err_string;
1334     }
1335
1336     /* we need an absolute path */
1337     jk_shm_file = ap_server_root_relative(cmd->pool, shm_file);
1338     if (jk_shm_file == NULL)
1339         return "JkShmFile file name invalid";
1340
1341     return NULL;
1342 }
1343
1344 /*
1345  * JkShmSize Directive Handling
1346  *
1347  * JkShmSize size in kilobytes
1348  */
1349
1350 static const char *jk_set_shm_size(cmd_parms * cmd,
1351                                    void *dummy, const char *shm_size)
1352 {
1353     int sz = 0;
1354     const char *err_string = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1355     if (err_string != NULL) {
1356         return err_string;
1357     }
1358
1359     sz = atoi(shm_size) * 1024;
1360     if (sz < JK_SHM_DEF_SIZE)
1361         sz = JK_SHM_DEF_SIZE;
1362     else
1363         sz = JK_SHM_ALIGN(sz);
1364     jk_shm_size = (size_t)sz;
1365     if (jk_shm_size)
1366         jk_shm_size_set = 1;
1367     return NULL;
1368 }
1369
1370 /*
1371  * JkLogLevel Directive Handling
1372  *
1373  * JkLogLevel debug/info/error/emerg
1374  */
1375
1376 static const char *jk_set_log_level(cmd_parms * cmd,
1377                                     void *dummy, const char *log_level)
1378 {
1379     server_rec *s = cmd->server;
1380     jk_server_conf_t *conf =
1381         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1382                                                   &jk_module);
1383
1384     conf->log_level = jk_parse_log_level(log_level);
1385
1386     return NULL;
1387 }
1388
1389 /*
1390  * JkLogStampFormat Directive Handling
1391  *
1392  * JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
1393  */
1394
1395 static const char *jk_set_log_fmt(cmd_parms * cmd,
1396                                   void *dummy, const char *log_format)
1397 {
1398     server_rec *s = cmd->server;
1399     jk_server_conf_t *conf =
1400         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1401                                                   &jk_module);
1402
1403     conf->stamp_format_string = apr_pstrdup(cmd->pool, log_format);
1404
1405     return NULL;
1406 }
1407
1408
1409 /*
1410  * JkAutoAlias Directive Handling
1411  *
1412  * JkAutoAlias application directory
1413  */
1414
1415 static const char *jk_set_auto_alias(cmd_parms * cmd,
1416                                      void *dummy, const char *directory)
1417 {
1418     server_rec *s = cmd->server;
1419     jk_server_conf_t *conf =
1420         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1421                                                   &jk_module);
1422
1423     conf->alias_dir = apr_pstrdup(cmd->pool, directory);
1424
1425     if (conf->alias_dir == NULL)
1426         return "JkAutoAlias directory invalid";
1427
1428     return NULL;
1429 }
1430
1431 /*
1432  * JkStripSession directive handling
1433  *
1434  * JkStripSession On/Off [session path identifier]
1435  */
1436
1437 static const char *jk_set_strip_session(cmd_parms * cmd, void *dummy,
1438                                         const char *flag, const char *name)
1439 {
1440     server_rec *s = cmd->server;
1441     jk_server_conf_t *conf =
1442         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1443                                                   &jk_module);
1444
1445     if (strcasecmp(flag, "on") && strcasecmp(flag, "off")) {
1446         return "JkStripSession must be On or Off";
1447     }
1448     else {
1449         conf->strip_session = strcasecmp(flag, "off") ? JK_TRUE : JK_FALSE;
1450     }
1451
1452     /* Check for optional path value */
1453     if (name)
1454         conf->strip_session_name = apr_pstrdup(cmd->pool, name);
1455     else
1456         conf->strip_session_name = apr_pstrdup(cmd->pool, JK_PATH_SESSION_IDENTIFIER);
1457
1458     return NULL;
1459 }
1460
1461 /*****************************************************************
1462  *
1463  * Actually logging.
1464  */
1465
1466 typedef const char *(*item_key_func) (request_rec *, char *);
1467
1468 typedef struct
1469 {
1470     item_key_func func;
1471     char *arg;
1472 } request_log_format_item;
1473
1474 static const char *process_item(request_rec * r,
1475                                 request_log_format_item * item)
1476 {
1477     const char *cp;
1478
1479     cp = (*item->func) (r, item->arg);
1480     return cp ? cp : "-";
1481 }
1482
1483 static void request_log_transaction(request_rec * r, jk_server_conf_t * conf)
1484 {
1485     request_log_format_item *items;
1486     char *str, *s;
1487     int i;
1488     int len = 0;
1489     const char **strs;
1490     int *strl;
1491     apr_array_header_t *format = conf->format;
1492
1493     strs = apr_palloc(r->pool, sizeof(char *) * (format->nelts));
1494     strl = apr_palloc(r->pool, sizeof(int) * (format->nelts));
1495     items = (request_log_format_item *) format->elts;
1496     for (i = 0; i < format->nelts; ++i) {
1497         strs[i] = process_item(r, &items[i]);
1498     }
1499     for (i = 0; i < format->nelts; ++i) {
1500         len += strl[i] = strlen(strs[i]);
1501     }
1502     str = apr_palloc(r->pool, len + 1);
1503     for (i = 0, s = str; i < format->nelts; ++i) {
1504         memcpy(s, strs[i], strl[i]);
1505         s += strl[i];
1506     }
1507     *s = 0;
1508
1509     jk_log(conf->log, JK_LOG_REQUEST, "%s", str);
1510
1511 }
1512
1513 /*****************************************************************
1514  *
1515  * Parsing the log format string
1516  */
1517
1518 static char *format_integer(apr_pool_t * p, int i)
1519 {
1520     return apr_psprintf(p, "%d", i);
1521 }
1522
1523 static char *pfmt(apr_pool_t * p, int i)
1524 {
1525     if (i <= 0) {
1526         return "-";
1527     }
1528     else {
1529         return format_integer(p, i);
1530     }
1531 }
1532
1533 static const char *constant_item(request_rec * dummy, char *stuff)
1534 {
1535     return stuff;
1536 }
1537
1538 static const char *log_worker_name(request_rec * r, char *a)
1539 {
1540     return apr_table_get(r->notes, JK_NOTE_WORKER_NAME);
1541 }
1542
1543 static const char *log_worker_route(request_rec * r, char *a)
1544 {
1545     return apr_table_get(r->notes, JK_NOTE_WORKER_ROUTE);
1546 }
1547
1548
1549 static const char *log_request_duration(request_rec * r, char *a)
1550 {
1551     return apr_table_get(r->notes, JK_NOTE_REQUEST_DURATION);
1552 }
1553
1554 static const char *log_request_line(request_rec * r, char *a)
1555 {
1556     /* NOTE: If the original request contained a password, we
1557      * re-write the request line here to contain XXXXXX instead:
1558      * (note the truncation before the protocol string for HTTP/0.9 requests)
1559      * (note also that r->the_request contains the unmodified request)
1560      */
1561     return (r->parsed_uri.password) ? apr_pstrcat(r->pool, r->method, " ",
1562                                                   apr_uri_unparse(r->pool,
1563                                                                   &r->
1564                                                                   parsed_uri,
1565                                                                   0),
1566                                                   r->
1567                                                   assbackwards ? NULL : " ",
1568                                                   r->protocol, NULL)
1569         : r->the_request;
1570 }
1571
1572 /* These next two routines use the canonical name:port so that log
1573  * parsers don't need to duplicate all the vhost parsing crud.
1574  */
1575 static const char *log_virtual_host(request_rec * r, char *a)
1576 {
1577     return r->server->server_hostname;
1578 }
1579
1580 static const char *log_server_port(request_rec * r, char *a)
1581 {
1582     return apr_psprintf(r->pool, "%u",
1583                         r->server->port ? r->server->
1584                         port : ap_default_port(r));
1585 }
1586
1587 /* This respects the setting of UseCanonicalName so that
1588  * the dynamic mass virtual hosting trick works better.
1589  */
1590 static const char *log_server_name(request_rec * r, char *a)
1591 {
1592     return ap_get_server_name(r);
1593 }
1594
1595 static const char *log_request_uri(request_rec * r, char *a)
1596 {
1597     return r->uri;
1598 }
1599 static const char *log_request_method(request_rec * r, char *a)
1600 {
1601     return r->method;
1602 }
1603
1604 static const char *log_request_protocol(request_rec * r, char *a)
1605 {
1606     return r->protocol;
1607 }
1608 static const char *log_request_query(request_rec * r, char *a)
1609 {
1610     return (r->args != NULL) ? apr_pstrcat(r->pool, "?", r->args, NULL)
1611         : "";
1612 }
1613 static const char *log_status(request_rec * r, char *a)
1614 {
1615     return pfmt(r->pool, r->status);
1616 }
1617
1618 static const char *clf_log_bytes_sent(request_rec * r, char *a)
1619 {
1620     if (!r->sent_bodyct) {
1621         return "-";
1622     }
1623     else {
1624         return apr_off_t_toa(r->pool, r->bytes_sent);
1625     }
1626 }
1627
1628 static const char *log_bytes_sent(request_rec * r, char *a)
1629 {
1630     if (!r->sent_bodyct) {
1631         return "0";
1632     }
1633     else {
1634         return apr_psprintf(r->pool, "%" APR_OFF_T_FMT, r->bytes_sent);
1635     }
1636 }
1637
1638 static struct log_item_list
1639 {
1640     char ch;
1641     item_key_func func;
1642 } log_item_keys[] = {
1643
1644     {
1645     'T', log_request_duration}, {
1646     'r', log_request_line}, {
1647     'U', log_request_uri}, {
1648     's', log_status}, {
1649     'b', clf_log_bytes_sent}, {
1650     'B', log_bytes_sent}, {
1651     'V', log_server_name}, {
1652     'v', log_virtual_host}, {
1653     'p', log_server_port}, {
1654     'H', log_request_protocol}, {
1655     'm', log_request_method}, {
1656     'q', log_request_query}, {
1657     'w', log_worker_name}, {
1658     'R', log_worker_route}, {
1659     '\0'}
1660 };
1661
1662 static struct log_item_list *find_log_func(char k)
1663 {
1664     int i;
1665
1666     for (i = 0; log_item_keys[i].ch; ++i)
1667         if (k == log_item_keys[i].ch) {
1668             return &log_item_keys[i];
1669         }
1670
1671     return NULL;
1672 }
1673
1674 static char *parse_request_log_misc_string(apr_pool_t * p,
1675                                            request_log_format_item * it,
1676                                            const char **sa)
1677 {
1678     const char *s;
1679     char *d;
1680
1681     it->func = constant_item;
1682
1683     s = *sa;
1684     while (*s && *s != '%') {
1685         s++;
1686     }
1687     /*
1688      * This might allocate a few chars extra if there's a backslash
1689      * escape in the format string.
1690      */
1691     it->arg = apr_palloc(p, s - *sa + 1);
1692
1693     d = it->arg;
1694     s = *sa;
1695     while (*s && *s != '%') {
1696         if (*s != '\\') {
1697             *d++ = *s++;
1698         }
1699         else {
1700             s++;
1701             switch (*s) {
1702             case '\\':
1703                 *d++ = '\\';
1704                 s++;
1705                 break;
1706             case 'n':
1707                 *d++ = '\n';
1708                 s++;
1709                 break;
1710             case 't':
1711                 *d++ = '\t';
1712                 s++;
1713                 break;
1714             default:
1715                 /* copy verbatim */
1716                 *d++ = '\\';
1717                 /*
1718                  * Allow the loop to deal with this *s in the normal
1719                  * fashion so that it handles end of string etc.
1720                  * properly.
1721                  */
1722                 break;
1723             }
1724         }
1725     }
1726     *d = '\0';
1727
1728     *sa = s;
1729     return NULL;
1730 }
1731
1732 static char *parse_request_log_item(apr_pool_t * p,
1733                                     request_log_format_item * it,
1734                                     const char **sa)
1735 {
1736     const char *s = *sa;
1737     struct log_item_list *l;
1738
1739     if (*s != '%') {
1740         return parse_request_log_misc_string(p, it, sa);
1741     }
1742
1743     ++s;
1744     it->arg = "";               /* For safety's sake... */
1745
1746     l = find_log_func(*s++);
1747     if (!l) {
1748         char dummy[2];
1749
1750         dummy[0] = s[-1];
1751         dummy[1] = '\0';
1752         return apr_pstrcat(p, "Unrecognized JkRequestLogFormat directive %",
1753                            dummy, NULL);
1754     }
1755     it->func = l->func;
1756     *sa = s;
1757     return NULL;
1758 }
1759
1760 static apr_array_header_t *parse_request_log_string(apr_pool_t * p,
1761                                                     const char *s,
1762                                                     const char **err)
1763 {
1764     apr_array_header_t *a =
1765         apr_array_make(p, 0, sizeof(request_log_format_item));
1766     char *res;
1767
1768     while (*s) {
1769         if ((res =
1770              parse_request_log_item(p,
1771                                     (request_log_format_item *)
1772                                     apr_array_push(a), &s))) {
1773             *err = res;
1774             return NULL;
1775         }
1776     }
1777
1778     return a;
1779 }
1780
1781 /*
1782  * JkRequestLogFormat Directive Handling
1783  *
1784  * JkRequestLogFormat format string
1785  *
1786  * %b - Bytes sent, excluding HTTP headers. In CLF format
1787  * %B - Bytes sent, excluding HTTP headers.
1788  * %H - The request protocol
1789  * %m - The request method
1790  * %p - The canonical Port of the server serving the request
1791  * %q - The query string (prepended with a ? if a query string exists,
1792  *      otherwise an empty string)
1793  * %r - First line of request
1794  * %s - request HTTP status code
1795  * %T - Requset duration, elapsed time to handle request in seconds '.' micro seconds
1796  * %U - The URL path requested, not including any query string.
1797  * %v - The canonical ServerName of the server serving the request.
1798  * %V - The server name according to the UseCanonicalName setting.
1799  * %w - Tomcat worker name
1800  */
1801
1802 static const char *jk_set_request_log_format(cmd_parms * cmd,
1803                                              void *dummy, const char *format)
1804 {
1805     server_rec *s = cmd->server;
1806     jk_server_conf_t *conf =
1807         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1808                                                   &jk_module);
1809
1810     conf->format_string = apr_pstrdup(cmd->pool, format);
1811
1812     return NULL;
1813 }
1814
1815
1816 /*
1817  * JkWorkerIndicator Directive Handling
1818  *
1819  * JkWorkerIndicator JkWorker
1820  */
1821
1822 static const char *jk_set_worker_indicator(cmd_parms * cmd,
1823                                            void *dummy, const char *indicator)
1824 {
1825     server_rec *s = cmd->server;
1826     jk_server_conf_t *conf =
1827         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1828                                                   &jk_module);
1829
1830     conf->worker_indicator = apr_pstrdup(cmd->pool, indicator);
1831
1832     return NULL;
1833 }
1834
1835 /*
1836  * Directives Handling for setting various environment names
1837  * used to overwrite the following request information:
1838  * - remote_addr
1839  * - remote_port
1840  * - remote_host
1841  * - remote_user
1842  * - auth_type
1843  * - server_name
1844  * - server_port
1845  */
1846 static const char *jk_set_remote_addr_indicator(cmd_parms * cmd,
1847                                                 void *dummy, const char *indicator)
1848 {
1849     server_rec *s = cmd->server;
1850     jk_server_conf_t *conf =
1851         (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module);
1852     conf->remote_addr_indicator = apr_pstrdup(cmd->pool, indicator);
1853     return NULL;
1854 }
1855
1856 static const char *jk_set_remote_port_indicator(cmd_parms * cmd,
1857                                                 void *dummy, const char *indicator)
1858 {
1859     server_rec *s = cmd->server;
1860     jk_server_conf_t *conf =
1861         (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module);
1862     conf->remote_port_indicator = apr_pstrdup(cmd->pool, indicator);
1863     return NULL;
1864 }
1865
1866 static const char *jk_set_remote_host_indicator(cmd_parms * cmd,
1867                                                 void *dummy, const char *indicator)
1868 {
1869     server_rec *s = cmd->server;
1870     jk_server_conf_t *conf =
1871         (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module);
1872     conf->remote_host_indicator = apr_pstrdup(cmd->pool, indicator);
1873     return NULL;
1874 }
1875
1876 static const char *jk_set_remote_user_indicator(cmd_parms * cmd,
1877                                                 void *dummy, const char *indicator)
1878 {
1879     server_rec *s = cmd->server;
1880     jk_server_conf_t *conf =
1881         (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module);
1882     conf->remote_user_indicator = apr_pstrdup(cmd->pool, indicator);
1883     return NULL;
1884 }
1885
1886 static const char *jk_set_auth_type_indicator(cmd_parms * cmd,
1887                                               void *dummy, const char *indicator)
1888 {
1889     server_rec *s = cmd->server;
1890     jk_server_conf_t *conf =
1891         (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module);
1892     conf->auth_type_indicator = apr_pstrdup(cmd->pool, indicator);
1893     return NULL;
1894 }
1895
1896 static const char *jk_set_local_name_indicator(cmd_parms * cmd,
1897                                                void *dummy, const char *indicator)
1898 {
1899     server_rec *s = cmd->server;
1900     jk_server_conf_t *conf =
1901         (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module);
1902     conf->local_name_indicator = apr_pstrdup(cmd->pool, indicator);
1903     return NULL;
1904 }
1905
1906 static const char *jk_set_local_port_indicator(cmd_parms * cmd,
1907                                                void *dummy, const char *indicator)
1908 {
1909     server_rec *s = cmd->server;
1910     jk_server_conf_t *conf =
1911         (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module);
1912     conf->local_port_indicator = apr_pstrdup(cmd->pool, indicator);
1913     return NULL;
1914 }
1915
1916 /*
1917  * JkExtractSSL Directive Handling
1918  *
1919  * JkExtractSSL On/Off
1920  */
1921
1922 static const char *jk_set_enable_ssl(cmd_parms * cmd, void *dummy, int flag)
1923 {
1924     server_rec *s = cmd->server;
1925     jk_server_conf_t *conf =
1926         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1927                                                   &jk_module);
1928
1929     /* Set up our value */
1930     conf->ssl_enable = flag ? JK_TRUE : JK_FALSE;
1931
1932     return NULL;
1933 }
1934
1935 /*
1936  * JkHTTPSIndicator Directive Handling
1937  *
1938  * JkHTTPSIndicator HTTPS
1939  */
1940
1941 static const char *jk_set_https_indicator(cmd_parms * cmd,
1942                                           void *dummy, const char *indicator)
1943 {
1944     server_rec *s = cmd->server;
1945     jk_server_conf_t *conf =
1946         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1947                                                   &jk_module);
1948
1949     conf->https_indicator = apr_pstrdup(cmd->pool, indicator);
1950
1951     return NULL;
1952 }
1953
1954 /*
1955  * JkCERTSIndicator Directive Handling
1956  *
1957  * JkCERTSIndicator SSL_CLIENT_CERT
1958  */
1959
1960 static const char *jk_set_certs_indicator(cmd_parms * cmd,
1961                                           void *dummy, const char *indicator)
1962 {
1963     server_rec *s = cmd->server;
1964     jk_server_conf_t *conf =
1965         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1966                                                   &jk_module);
1967
1968     conf->certs_indicator = apr_pstrdup(cmd->pool, indicator);
1969
1970     return NULL;
1971 }
1972
1973 /*
1974  * JkCIPHERIndicator Directive Handling
1975  *
1976  * JkCIPHERIndicator SSL_CIPHER
1977  */
1978
1979 static const char *jk_set_cipher_indicator(cmd_parms * cmd,
1980                                            void *dummy, const char *indicator)
1981 {
1982     server_rec *s = cmd->server;
1983     jk_server_conf_t *conf =
1984         (jk_server_conf_t *) ap_get_module_config(s->module_config,
1985                                                   &jk_module);
1986
1987     conf->cipher_indicator = apr_pstrdup(cmd->pool, indicator);
1988
1989     return NULL;
1990 }
1991
1992 /*
1993  * JkCERTCHAINPrefix Directive Handling
1994  *
1995  * JkCERTCHAINPrefix SSL_CLIENT_CERT_CHAIN_
1996  */
1997
1998 static const char *jk_set_certchain_prefix(cmd_parms * cmd,
1999                                            void *dummy, const char *prefix)
2000 {
2001     server_rec *s = cmd->server;
2002     jk_server_conf_t *conf =
2003         (jk_server_conf_t *) ap_get_module_config(s->module_config,
2004                                                   &jk_module);
2005
2006     conf->certchain_prefix = apr_pstrdup(cmd->pool, prefix);
2007
2008     return NULL;
2009 }
2010
2011 /*
2012  * JkSESSIONIndicator Directive Handling
2013  *
2014  * JkSESSIONIndicator SSL_SESSION_ID
2015  */
2016
2017 static const char *jk_set_session_indicator(cmd_parms * cmd,
2018                                             void *dummy,
2019                                             const char *indicator)
2020 {
2021     server_rec *s = cmd->server;
2022     jk_server_conf_t *conf =
2023         (jk_server_conf_t *) ap_get_module_config(s->module_config,
2024                                                   &jk_module);
2025
2026     conf->session_indicator = apr_pstrdup(cmd->pool, indicator);
2027
2028     return NULL;
2029 }
2030
2031 /*
2032  * JkKEYSIZEIndicator Directive Handling
2033  *
2034  * JkKEYSIZEIndicator SSL_CIPHER_USEKEYSIZE
2035  */
2036
2037 static const char *jk_set_key_size_indicator(cmd_parms * cmd,
2038                                              void *dummy,
2039                                              const char *indicator)
2040 {
2041     server_rec *s = cmd->server;
2042     jk_server_conf_t *conf =
2043         (jk_server_conf_t *) ap_get_module_config(s->module_config,
2044                                                   &jk_module);
2045
2046     conf->key_size_indicator = apr_pstrdup(cmd->pool, indicator);
2047
2048     return NULL;
2049 }
2050
2051 /*
2052  * JkOptions Directive Handling
2053  *
2054  *
2055  * +ForwardSSLKeySize        => Forward SSL Key Size, to follow 2.3 specs but may broke old TC 3.2
2056  * -ForwardSSLKeySize        => Don't Forward SSL Key Size, will make mod_jk works with all TC release
2057  *  ForwardURICompat         => Forward URI normally, less spec compliant but mod_rewrite compatible (old TC)
2058  *  ForwardURICompatUnparsed => Forward URI as unparsed, spec compliant but broke mod_rewrite (old TC)
2059  *  ForwardURIEscaped        => Forward URI escaped and Tomcat (3.3 rc2) stuff will do the decoding part
2060  *  ForwardDirectories       => Forward all directory requests with no index files to Tomcat
2061  * +ForwardSSLCertChain      => Forward SSL Cert Chain
2062  * -ForwardSSLCertChain      => Don't Forward SSL Cert Chain (default)
2063  */
2064
2065 static const char *jk_set_options(cmd_parms * cmd, void *dummy,
2066                                   const char *line)
2067 {
2068     int opt = 0;
2069     int mask = 0;
2070     char action;
2071     char *w;
2072
2073     server_rec *s = cmd->server;
2074     jk_server_conf_t *conf =
2075         (jk_server_conf_t *) ap_get_module_config(s->module_config,
2076                                                   &jk_module);
2077
2078     while (line[0] != 0) {
2079         w = ap_getword_conf(cmd->pool, &line);
2080         action = 0;
2081
2082         if (*w == '+' || *w == '-') {
2083             action = *(w++);
2084         }
2085
2086         mask = 0;
2087
2088         if (action == '-' && !strncasecmp(w, "ForwardURI", strlen("ForwardURI")))
2089             return apr_pstrcat(cmd->pool, "JkOptions: Illegal option '-", w,
2090                                "': ForwardURI* options can not be disabled", NULL);
2091
2092         if (!strcasecmp(w, "ForwardURICompat")) {
2093             opt = JK_OPT_FWDURICOMPAT;
2094             mask = JK_OPT_FWDURIMASK;
2095         }
2096         else if (!strcasecmp(w, "ForwardURICompatUnparsed")) {
2097             opt = JK_OPT_FWDURICOMPATUNPARSED;
2098             mask = JK_OPT_FWDURIMASK;
2099         }
2100         else if (!strcasecmp(w, "ForwardURIEscaped")) {
2101             opt = JK_OPT_FWDURIESCAPED;
2102             mask = JK_OPT_FWDURIMASK;
2103         }
2104         else if (!strcasecmp(w, "ForwardURIProxy")) {
2105             opt = JK_OPT_FWDURIPROXY;
2106             mask = JK_OPT_FWDURIMASK;
2107         }
2108         else if (!strcasecmp(w, "ForwardDirectories")) {
2109             opt = JK_OPT_FWDDIRS;
2110         }
2111         else if (!strcasecmp(w, "ForwardLocalAddress")) {
2112             opt = JK_OPT_FWDLOCAL;
2113         }
2114         else if (!strcasecmp(w, "FlushPackets")) {
2115             opt = JK_OPT_FLUSHPACKETS;
2116         }
2117         else if (!strcasecmp(w, "FlushHeader")) {
2118             opt = JK_OPT_FLUSHEADER;
2119         }
2120         else if (!strcasecmp(w, "DisableReuse")) {
2121             opt = JK_OPT_DISABLEREUSE;
2122         }
2123         else if (!strcasecmp(w, "ForwardSSLCertChain")) {
2124             opt = JK_OPT_FWDCERTCHAIN;
2125         }
2126         else if (!strcasecmp(w, "ForwardKeySize")) {
2127             opt = JK_OPT_FWDKEYSIZE;
2128         }
2129         else if (!strcasecmp(w, "RejectUnsafeURI")) {
2130             opt = JK_OPT_REJECTUNSAFE;
2131         }
2132         else
2133             return apr_pstrcat(cmd->pool, "JkOptions: Illegal option '", w,
2134                                "'", NULL);
2135
2136         conf->options &= ~mask;
2137
2138         if (action == '-') {
2139             conf->exclude_options |= opt;
2140         }
2141         else if (action == '+') {
2142             conf->options |= opt;
2143         }
2144         else {                  /* for now +Opt == Opt */
2145             conf->options |= opt;
2146         }
2147     }
2148     return NULL;
2149 }
2150
2151 /*
2152  * JkEnvVar Directive Handling
2153  *
2154  * JkEnvVar MYOWNDIR
2155  */
2156
2157 static const char *jk_add_env_var(cmd_parms * cmd,
2158                                   void *dummy,
2159                                   const char *env_name,
2160                                   const char *default_value)
2161 {
2162     server_rec *s = cmd->server;
2163     jk_server_conf_t *conf =
2164         (jk_server_conf_t *) ap_get_module_config(s->module_config,
2165                                                   &jk_module);
2166
2167     conf->envvars_has_own = JK_TRUE;
2168     if (!conf->envvars) {
2169         conf->envvars      = apr_table_make(cmd->pool, 0);
2170         conf->envvars_def  = apr_table_make(cmd->pool, 0);
2171         conf->envvar_items = apr_array_make(cmd->pool, 0,
2172                                             sizeof(envvar_item));
2173     }
2174
2175     /* env_name is mandatory, default_value is optional.
2176      * No value means send the attribute only, if the env var is set during runtime.
2177      */
2178     apr_table_setn(conf->envvars, env_name, default_value ? default_value : "");
2179     apr_table_setn(conf->envvars_def, env_name, default_value ? "1" : "0");
2180
2181     return NULL;
2182 }
2183
2184 /*
2185  * JkWorkerProperty Directive Handling
2186  *
2187  * JkWorkerProperty name=value
2188  */
2189
2190 static const char *jk_set_worker_property(cmd_parms * cmd,
2191                                           void *dummy,
2192                                           const char *line)
2193 {
2194     server_rec *s = cmd->server;
2195     jk_server_conf_t *conf =
2196         (jk_server_conf_t *) ap_get_module_config(s->module_config,
2197                                                   &jk_module);
2198
2199     const char *err_string = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2200     if (err_string != NULL) {
2201         return err_string;
2202     }
2203
2204     if (!jk_worker_properties)
2205         jk_map_alloc(&jk_worker_properties);
2206     if (jk_map_read_property(jk_worker_properties, NULL, line,
2207                              JK_MAP_HANDLE_DUPLICATES, conf->log) == JK_FALSE)
2208         return apr_pstrcat(cmd->temp_pool, "Invalid JkWorkerProperty ", line, NULL);
2209
2210     return NULL;
2211 }
2212
2213 static const command_rec jk_cmds[] = {
2214     /*
2215      * JkWorkersFile specifies a full path to the location of the worker
2216      * properties file.
2217      *
2218      * This file defines the different workers used by apache to redirect
2219      * servlet requests.
2220      */
2221     AP_INIT_TAKE1("JkWorkersFile", jk_set_worker_file, NULL, RSRC_CONF,
2222                   "The name of a worker file for the Tomcat servlet containers"),
2223
2224     /*
2225      * JkMountFile specifies a full path to the location of the
2226      * uriworker properties file.
2227      *
2228      * This file defines the different mapping for workers used by apache
2229      * to redirect servlet requests.
2230      */
2231     AP_INIT_TAKE1("JkMountFile", jk_set_mount_file, NULL, RSRC_CONF,
2232                   "The name of a mount file for the Tomcat servlet uri mapping"),
2233
2234     /*
2235      * JkMountFileReload specifies the reload check interval for the
2236      * uriworker properties file.
2237      *
2238      * Default value is: JK_URIMAP_DEF_RELOAD
2239      */
2240     AP_INIT_TAKE1("JkMountFileReload", jk_set_mount_file_reload, NULL, RSRC_CONF,
2241                   "The reload check interval of the mount file"),
2242
2243     /*
2244      * JkWatchdogInterval specifies the maintain interval for the
2245      * watchdog thread.
2246      *
2247      * Default value is: 0 meaning watchdog thread will not be created
2248      */
2249     AP_INIT_TAKE1("JkWatchdogInterval", jk_set_watchdog_interval, NULL, RSRC_CONF,
2250                   "The maintain interval of the watchdog thread"),
2251
2252     /*
2253      * JkMount mounts a url prefix to a worker (the worker need to be
2254      * defined in the worker properties file.
2255      */
2256     AP_INIT_TAKE12("JkMount", jk_mount_context, NULL, RSRC_CONF|ACCESS_CONF,
2257                    "A mount point from a context to a Tomcat worker"),
2258
2259     /*
2260      * JkUnMount unmounts a url prefix to a worker (the worker need to be
2261      * defined in the worker properties file.
2262      */
2263     AP_INIT_TAKE12("JkUnMount", jk_unmount_context, NULL, RSRC_CONF|ACCESS_CONF,
2264                    "A no mount point from a context to a Tomcat worker"),
2265
2266     /*
2267      * JkMountCopy specifies if mod_jk should copy the mount points
2268      * from the main server to the virtual servers.
2269      */
2270     AP_INIT_TAKE1("JkMountCopy", jk_set_mountcopy, NULL, RSRC_CONF,
2271                   "Should the base server mounts be copied to the virtual server"),
2272
2273     /*
2274      * JkStripSession specifies if mod_jk should strip the ;jsessionid
2275      * from the unmapped urls
2276      */
2277     AP_INIT_TAKE12("JkStripSession", jk_set_strip_session, NULL, RSRC_CONF,
2278                    "Should the server strip the jsessionid from unmapped URLs"),
2279
2280     /*
2281      * JkLogFile & JkLogLevel specifies to where should the plugin log
2282      * its information and how much.
2283      * JkLogStampFormat specify the time-stamp to be used on log
2284      */
2285     AP_INIT_TAKE1("JkLogFile", jk_set_log_file, NULL, RSRC_CONF,
2286                   "Full path to the Tomcat module log file"),
2287
2288     AP_INIT_TAKE1("JkShmFile", jk_set_shm_file, NULL, RSRC_CONF,
2289                   "Full path to the Tomcat module shared memory file"),
2290
2291     AP_INIT_TAKE1("JkShmSize", jk_set_shm_size, NULL, RSRC_CONF,
2292                   "Size of the shared memory file in KBytes"),
2293
2294     AP_INIT_TAKE1("JkLogLevel", jk_set_log_level, NULL, RSRC_CONF,
2295                   "The Tomcat module log level, can be debug, "
2296                   "info, error or emerg"),
2297     AP_INIT_TAKE1("JkLogStampFormat", jk_set_log_fmt, NULL, RSRC_CONF,
2298                   "The Tomcat module log format, follow strftime syntax"),
2299     AP_INIT_TAKE1("JkRequestLogFormat", jk_set_request_log_format, NULL,
2300                   RSRC_CONF,
2301                   "The mod_jk module request log format string"),
2302
2303     /*
2304      * Automatically Alias webapp context directories into the Apache
2305      * document space.
2306      */
2307     AP_INIT_TAKE1("JkAutoAlias", jk_set_auto_alias, NULL, RSRC_CONF,
2308                   "The mod_jk module automatic context apache alias directory"),
2309
2310     /*
2311      * Enable worker name to be set in an environment variable.
2312      * This way one can use LocationMatch together with mod_env,
2313      * mod_setenvif and mod_rewrite to set the target worker.
2314      * Use this in combination with SetHandler jakarta-servlet to
2315      * make mod_jk the handler for the request.
2316      *
2317      */
2318     AP_INIT_TAKE1("JkWorkerIndicator", jk_set_worker_indicator, NULL, RSRC_CONF,
2319                   "Name of the Apache environment that contains the worker name"),
2320
2321     /*
2322      * Environment variables used to overwrite the following
2323      * request information which gets forwarded:
2324      * - remote_addr
2325      * - remote_port
2326      * - remote_host
2327      * - remote_user
2328      * - auth_type
2329      * - server_name
2330      * - server_port
2331      */
2332     AP_INIT_TAKE1("JkRemoteAddrIndicator", jk_set_remote_addr_indicator, NULL, RSRC_CONF,
2333                   "Name of the Apache environment that contains the remote address"),
2334     AP_INIT_TAKE1("JkRemotePortIndicator", jk_set_remote_port_indicator, NULL, RSRC_CONF,
2335                   "Name of the Apache environment that contains the remote port"),
2336     AP_INIT_TAKE1("JkRemoteHostIndicator", jk_set_remote_host_indicator, NULL, RSRC_CONF,
2337                   "Name of the Apache environment that contains the remote host name"),
2338     AP_INIT_TAKE1("JkRemoteUserIndicator", jk_set_remote_user_indicator, NULL, RSRC_CONF,
2339                   "Name of the Apache environment that contains the remote user name"),
2340     AP_INIT_TAKE1("JkAuthTypeIndicator", jk_set_auth_type_indicator, NULL, RSRC_CONF,
2341                   "Name of the Apache environment that contains the type of authentication"),
2342     AP_INIT_TAKE1("JkLocalNameIndicator", jk_set_local_name_indicator, NULL, RSRC_CONF,
2343                   "Name of the Apache environment that contains the local name"),
2344     AP_INIT_TAKE1("JkLocalPortIndicator", jk_set_local_port_indicator, NULL, RSRC_CONF,
2345                   "Name of the Apache environment that contains the local port"),
2346
2347     /*
2348      * Apache has multiple SSL modules (for example apache_ssl, stronghold
2349      * IHS ...). Each of these can have a different SSL environment names
2350      * The following properties let the administrator specify the envoiroment
2351      * variables names.
2352      *
2353      * HTTPS - indication for SSL
2354      * CERTS - Base64-Der-encoded client certificates.
2355      * CIPHER - A string specifing the ciphers suite in use.
2356      * KEYSIZE - Size of Key used in dialogue (#bits are secure)
2357      * SESSION - A string specifing the current SSL session.
2358      */
2359     AP_INIT_TAKE1("JkHTTPSIndicator", jk_set_https_indicator, NULL, RSRC_CONF,
2360                   "Name of the Apache environment that contains SSL indication"),
2361     AP_INIT_TAKE1("JkCERTSIndicator", jk_set_certs_indicator, NULL, RSRC_CONF,
2362                   "Name of the Apache environment that contains SSL client certificates"),
2363     AP_INIT_TAKE1("JkCIPHERIndicator", jk_set_cipher_indicator, NULL,
2364                   RSRC_CONF,
2365                   "Name of the Apache environment that contains SSL client cipher"),
2366     AP_INIT_TAKE1("JkSESSIONIndicator", jk_set_session_indicator, NULL,
2367                   RSRC_CONF,
2368                   "Name of the Apache environment that contains SSL session"),
2369     AP_INIT_TAKE1("JkKEYSIZEIndicator", jk_set_key_size_indicator, NULL,
2370                   RSRC_CONF,
2371                   "Name of the Apache environment that contains SSL key size in use"),
2372     AP_INIT_TAKE1("JkCERTCHAINPrefix", jk_set_certchain_prefix, NULL, RSRC_CONF,
2373                   "Name of the Apache environment (prefix) that contains SSL client chain certificates"),
2374     AP_INIT_FLAG("JkExtractSSL", jk_set_enable_ssl, NULL, RSRC_CONF,
2375                  "Turns on SSL processing and information gathering by mod_jk"),
2376
2377     /*
2378      * Options to tune mod_jk configuration
2379      * for now we understand :
2380      * +ForwardSSLKeySize        => Forward SSL Key Size, to follow 2.3 specs but may broke old TC 3.2
2381      * -ForwardSSLKeySize        => Don't Forward SSL Key Size, will make mod_jk works with all TC release
2382      *  ForwardURICompat         => Forward URI normally, less spec compliant but mod_rewrite compatible (old TC)
2383      *  ForwardURICompatUnparsed => Forward URI as unparsed, spec compliant but broke mod_rewrite (old TC)
2384      *  ForwardURIEscaped        => Forward URI escaped and Tomcat (3.3 rc2) stuff will do the decoding part
2385      * +ForwardSSLCertChain      => Forward SSL certificate chain
2386      * -ForwardSSLCertChain      => Don't forward SSL certificate chain
2387      */
2388     AP_INIT_RAW_ARGS("JkOptions", jk_set_options, NULL, RSRC_CONF,
2389                      "Set one of more options to configure the mod_jk module"),
2390
2391     /*
2392      * JkEnvVar let user defines envs var passed from WebServer to
2393      * Servlet Engine
2394      */
2395     AP_INIT_TAKE12("JkEnvVar", jk_add_env_var, NULL, RSRC_CONF,
2396                    "Adds a name of environment variable and an optional value "
2397                    "that should be sent to servlet-engine"),
2398
2399     AP_INIT_RAW_ARGS("JkWorkerProperty", jk_set_worker_property,
2400                      NULL, RSRC_CONF,
2401                      "Set workers.properties formated directive"),
2402
2403     {NULL}
2404 };
2405
2406 /* ========================================================================= */
2407 /* The JK module handlers                                                    */
2408 /* ========================================================================= */
2409
2410 /** Util - cleanup shmem.
2411  */
2412 static apr_status_t jk_cleanup_shmem(void *data)
2413 {
2414     /* Force the watchdog thread exit */
2415     if (jk_watchdog_interval > 0) {
2416         jk_watchdog_interval = 0;
2417         while (jk_watchdog_running)
2418             apr_sleep(apr_time_from_sec(1));
2419     }
2420     jk_shm_close();
2421     return APR_SUCCESS;
2422 }
2423
2424 /** Main service method, called to forward a request to tomcat
2425  */
2426 static int jk_handler(request_rec * r)
2427 {
2428     const char *worker_name;
2429     jk_server_conf_t *xconf;
2430     int rc, dmt = 1;
2431
2432     /* We do DIR_MAGIC_TYPE here to make sure TC gets all requests, even
2433      * if they are directory requests, in case there are no static files
2434      * visible to Apache and/or DirectoryIndex was not used. This is only
2435      * used when JkOptions has ForwardDirectories set. */
2436     /* Not for me, try next handler */
2437     if (strcmp(r->handler, JK_HANDLER)
2438         && (dmt = strcmp(r->handler, DIR_MAGIC_TYPE)))
2439         return DECLINED;
2440
2441     xconf = (jk_server_conf_t *) ap_get_module_config(r->server->module_config,
2442                                                       &jk_module);
2443     JK_TRACE_ENTER(xconf->log);
2444     if (apr_table_get(r->subprocess_env, "no-jk")) {
2445         if (JK_IS_DEBUG_LEVEL(xconf->log))
2446             jk_log(xconf->log, JK_LOG_DEBUG,
2447                    "Into handler no-jk env var detected for uri=%s, declined",
2448                    r->uri);
2449
2450         JK_TRACE_EXIT(xconf->log);
2451         return DECLINED;
2452     }
2453
2454     /* Was the option to forward directories to Tomcat set? */
2455     if (!dmt && !(xconf->options & JK_OPT_FWDDIRS)) {
2456         JK_TRACE_EXIT(xconf->log);
2457         return DECLINED;
2458     }
2459     worker_name = apr_table_get(r->notes, JK_NOTE_WORKER_NAME);
2460
2461     /* Set up r->read_chunked flags for chunked encoding, if present */
2462     if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) != APR_SUCCESS) {
2463         JK_TRACE_EXIT(xconf->log);
2464         return rc;
2465     }
2466
2467     if (worker_name == NULL) {
2468         /* we may be here because of a manual directive ( that overrides
2469            translate and
2470            sets the handler directly ). We still need to know the worker.
2471          */
2472         worker_name = apr_table_get(r->subprocess_env, xconf->worker_indicator);
2473         if (worker_name) {
2474           /* The JkWorkerIndicator environment variable has
2475            * been used to explicitely set the worker without JkMount.
2476            * This is useful in combination with LocationMatch or mod_rewrite.
2477            */
2478             if (JK_IS_DEBUG_LEVEL(xconf->log))
2479                 jk_log(xconf->log, JK_LOG_DEBUG,
2480                        "Retrieved worker (%s) from env %s for %s",
2481                        worker_name, xconf->worker_indicator, r->uri);
2482         }
2483         else if (worker_env.num_of_workers == 1) {
2484           /** We have a single worker ( the common case ).
2485               ( lb is a bit special, it should count as a single worker but
2486               I'm not sure how ). We also have a manual config directive that
2487               explicitely give control to us. */
2488             worker_name = worker_env.worker_list[0];
2489             if (JK_IS_DEBUG_LEVEL(xconf->log))
2490                 jk_log(xconf->log, JK_LOG_DEBUG,
2491                        "Single worker (%s) configuration for %s",
2492                        worker_name, r->uri);
2493         }
2494         else {
2495             if (!xconf->uw_map) {
2496                 if (JK_IS_DEBUG_LEVEL(xconf->log))
2497                     jk_log(xconf->log, JK_LOG_DEBUG,
2498                            "missing uri map for %s:%s",
2499                            xconf->s->server_hostname ? xconf->s->server_hostname : "_default_",
2500                            r->uri);
2501             }
2502             else {
2503                 rule_extension_t *e;
2504                 worker_name = map_uri_to_worker_ext(xconf->uw_map, r->uri,
2505                                                     NULL, &e, NULL, xconf->log);
2506                 ap_set_module_config(r->request_config, &jk_module, e);
2507             }
2508
2509             if (worker_name == NULL && worker_env.num_of_workers) {
2510                 worker_name = worker_env.worker_list[0];
2511                 if (JK_IS_DEBUG_LEVEL(xconf->log))
2512                     jk_log(xconf->log, JK_LOG_DEBUG,
2513                            "Using first worker (%s) from %d workers for %s",
2514                            worker_name, worker_env.num_of_workers, r->uri);
2515             }
2516         }
2517         if (worker_name)
2518             apr_table_setn(r->notes, JK_NOTE_WORKER_NAME, worker_name);
2519     }
2520
2521     if (JK_IS_DEBUG_LEVEL(xconf->log))
2522        jk_log(xconf->log, JK_LOG_DEBUG, "Into handler %s worker=%s"
2523               " r->proxyreq=%d",
2524               r->handler, STRNULL_FOR_NULL(worker_name), r->proxyreq);
2525
2526     /* If this is a proxy request, we'll notify an error */
2527     if (r->proxyreq) {
2528         jk_log(xconf->log, JK_LOG_INFO, "Proxy request for worker=%s"
2529               " is not allowed",
2530               STRNULL_FOR_NULL(worker_name));
2531         JK_TRACE_EXIT(xconf->log);
2532         return HTTP_INTERNAL_SERVER_ERROR;
2533     }
2534
2535     if (worker_name) {
2536         jk_worker_t *worker = wc_get_worker_for_name(worker_name, xconf->log);
2537
2538         /* If the remote client has aborted, just ignore the request */
2539         if (r->connection->aborted) {
2540             jk_log(xconf->log, JK_LOG_INFO, "Client connection aborted for"
2541                    " worker=%s",
2542                    worker_name);
2543             JK_TRACE_EXIT(xconf->log);
2544             return OK;
2545         }
2546
2547         if (worker) {
2548             long micro, seconds;
2549             char *duration = NULL;
2550             apr_time_t rd;
2551             apr_time_t request_begin = 0;
2552             int is_error = HTTP_INTERNAL_SERVER_ERROR;
2553             int rc = JK_FALSE;
2554             apache_private_data_t private_data;
2555             jk_ws_service_t s;
2556             jk_pool_atom_t buf[SMALL_POOL_SIZE];
2557             jk_open_pool(&private_data.p, buf, sizeof(buf));
2558
2559             private_data.read_body_started = JK_FALSE;
2560             private_data.r = r;
2561
2562             /* Maintain will be done by watchdog thread */
2563             if (!jk_watchdog_interval)
2564                 wc_maintain(xconf->log);
2565             jk_init_ws_service(&s);
2566             s.ws_private = &private_data;
2567             s.pool = &private_data.p;
2568             apr_table_setn(r->notes, JK_NOTE_WORKER_TYPE,
2569                            wc_get_name_for_type(worker->type, xconf->log));
2570
2571             request_begin = apr_time_now();
2572
2573             if (init_ws_service(&private_data, &s, xconf)) {
2574                 jk_endpoint_t *end = NULL;
2575
2576                 /* Use per/thread pool ( or "context" ) to reuse the
2577                    endpoint. It's a bit faster, but I don't know
2578                    how to deal with load balancing - but it's usefull for JNI
2579                  */
2580
2581                 /* worker->get_endpoint might fail if we are out of memory so check */
2582                 /* and handle it */
2583                 if (worker->get_endpoint(worker, &end, xconf->log)) {
2584                     rc = end->service(end, &s, xconf->log,
2585                                       &is_error);
2586                     end->done(&end, xconf->log);
2587                     if (s.content_read < s.content_length ||
2588                         (s.is_chunked && !s.no_more_chunks)) {
2589                         /*
2590                          * If the servlet engine didn't consume all of the
2591                          * request data, consume and discard all further
2592                          * characters left to read from client
2593                          */
2594                         char *buff = apr_palloc(r->pool, 2048);
2595                         int consumed = 0;
2596                         if (buff != NULL) {
2597                             int rd;
2598                             while ((rd =
2599                                     ap_get_client_block(r, buff, 2048)) > 0) {
2600                                 s.content_read += rd;
2601                                 consumed += rd;
2602                             }
2603                         }
2604                         if (JK_IS_DEBUG_LEVEL(xconf->log)) {
2605                            jk_log(xconf->log, JK_LOG_DEBUG,
2606                                   "Consumed %d bytes of remaining request data for worker=%s",
2607                                   consumed, STRNULL_FOR_NULL(worker_name));
2608                         }
2609                     }
2610                 }
2611                 else {            /* this means we couldn't get an endpoint */
2612                     jk_log(xconf->log, JK_LOG_ERROR, "Could not get endpoint"
2613                            " for worker=%s",
2614                            worker_name);
2615                     rc = 0;       /* just to make sure that we know we've failed */
2616                 }
2617             }
2618             else {
2619                 jk_log(xconf->log, JK_LOG_ERROR, "Could not init service"
2620                        " for worker=%s",
2621                        worker_name);
2622                 jk_close_pool(&private_data.p);
2623                 JK_TRACE_EXIT(xconf->log);
2624                 return HTTP_INTERNAL_SERVER_ERROR;
2625             }
2626             rd = apr_time_now() - request_begin;
2627             seconds = (long)apr_time_sec(rd);
2628             micro = (long)(rd - apr_time_from_sec(seconds));
2629
2630             duration = apr_psprintf(r->pool, "%.1ld.%.6ld", seconds, micro);
2631             apr_table_setn(r->notes, JK_NOTE_REQUEST_DURATION, duration);
2632             if (s.route && *s.route)
2633                 apr_table_setn(r->notes, JK_NOTE_WORKER_ROUTE, s.route);
2634
2635             if (xconf->format != NULL) {
2636                 request_log_transaction(r, xconf);
2637             }
2638
2639             jk_close_pool(&private_data.p);
2640
2641             if (rc > 0) {
2642                 if (s.extension.use_server_error_pages &&
2643                     s.http_response_status >= s.extension.use_server_error_pages) {
2644                     if (JK_IS_DEBUG_LEVEL(xconf->log))
2645                         jk_log(xconf->log, JK_LOG_DEBUG, "Forwarding status=%d"
2646                                " for worker=%s",
2647                                s.http_response_status, worker_name);
2648                     JK_TRACE_EXIT(xconf->log);
2649                     return s.http_response_status;
2650                 }
2651                 /* If tomcat returned no body and the status is not OK,
2652                    let apache handle the error code */
2653
2654                 if (!r->sent_bodyct && r->status >= HTTP_BAD_REQUEST) {
2655                     jk_log(xconf->log, JK_LOG_INFO, "No body with status=%d"
2656                            " for worker=%s",
2657                            r->status, worker_name);
2658                     JK_TRACE_EXIT(xconf->log);
2659                     return r->status;
2660                 }
2661                 if (JK_IS_DEBUG_LEVEL(xconf->log))
2662                     jk_log(xconf->log, JK_LOG_DEBUG, "Service finished"
2663                            " with status=%d for worker=%s",
2664                            r->status, worker_name);
2665                 JK_TRACE_EXIT(xconf->log);
2666                 return OK;      /* NOT r->status, even if it has changed. */
2667             }
2668             else if (rc == JK_CLIENT_ERROR) {
2669                 if (is_error != HTTP_REQUEST_ENTITY_TOO_LARGE)
2670                     r->connection->aborted = 1;
2671                 jk_log(xconf->log, JK_LOG_INFO, "Aborting connection"
2672                        " for worker=%s",
2673                        worker_name);
2674                 JK_TRACE_EXIT(xconf->log);
2675                 return is_error;
2676             }
2677             else {
2678                 jk_log(xconf->log, JK_LOG_INFO, "Service error=%d"
2679                        " for worker=%s",
2680                        rc, worker_name);
2681                 JK_TRACE_EXIT(xconf->log);
2682                 return is_error;
2683             }
2684         }
2685         else {
2686             jk_log(xconf->log, JK_LOG_INFO, "Could not find a worker"
2687                    " for worker name=%s",
2688                    worker_name);
2689             JK_TRACE_EXIT(xconf->log);
2690             return HTTP_INTERNAL_SERVER_ERROR;
2691         }
2692     }
2693
2694     JK_TRACE_EXIT(xconf->log);
2695     return DECLINED;
2696 }
2697
2698 /** Standard apache hook, cleanup jk
2699  */
2700 static apr_status_t jk_apr_pool_cleanup(void *data)
2701 {
2702     server_rec *s = data;
2703
2704     if (jk_worker_properties) {
2705         jk_map_free(&jk_worker_properties);
2706         jk_worker_properties = NULL;
2707         jk_worker_file = NULL;
2708         jk_mount_copy_all = JK_FALSE;
2709     }
2710
2711     while (NULL != s) {
2712         jk_server_conf_t *conf =
2713             (jk_server_conf_t *) ap_get_module_config(s->module_config,
2714                                                       &jk_module);
2715
2716         if (conf && conf->was_initialized == JK_TRUE) {
2717             /* On pool cleanup pass NULL for the jk_logger to
2718                prevent segmentation faults on Windows because
2719                we can't guarantee what order pools get cleaned
2720                up between APR implementations. */
2721             wc_close(NULL);
2722             if (conf->uri_to_context) {
2723                 jk_map_free(&conf->uri_to_context);
2724                 /* We cannot have allocated uw_map
2725                  * unless we've allocated uri_to_context
2726                  */
2727                 if (conf->uw_map)
2728                     uri_worker_map_free(&conf->uw_map, NULL);
2729             }
2730             conf->was_initialized = JK_FALSE;
2731         }
2732         s = s->next;
2733     }
2734     return APR_SUCCESS;
2735 }
2736
2737 /** Create default jk_config. XXX This is mostly server-independent,
2738     all servers are using something similar - should go to common.
2739  */
2740 static void *create_jk_config(apr_pool_t * p, server_rec * s)
2741 {
2742     jk_server_conf_t *c =
2743         (jk_server_conf_t *) apr_pcalloc(p, sizeof(jk_server_conf_t));
2744
2745     c->was_initialized = JK_FALSE;
2746
2747     if (s->is_virtual) {
2748         c->mountcopy = JK_UNSET;
2749         c->mount_file_reload = JK_UNSET;
2750         c->log_level = JK_UNSET;
2751         c->ssl_enable = JK_UNSET;
2752         c->strip_session = JK_UNSET;
2753     } else {
2754         if (!jk_map_alloc(&(c->uri_to_context))) {
2755             ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Memory error");
2756         }
2757         c->mountcopy = JK_FALSE;
2758         c->mount_file_reload = JK_URIMAP_DEF_RELOAD;
2759         c->log_level = JK_LOG_DEF_LEVEL;
2760         c->options = JK_OPT_DEFAULT;
2761         c->worker_indicator = JK_ENV_WORKER_NAME;
2762
2763         /*
2764          * Configurable environment variables to overwrite
2765          * request information using meta data send by a
2766          * proxy in front of us.
2767          */
2768         c->remote_addr_indicator = JK_ENV_REMOTE_ADDR;
2769         c->remote_port_indicator = JK_ENV_REMOTE_PORT;
2770         c->remote_host_indicator = JK_ENV_REMOTE_HOST;
2771         c->remote_user_indicator = JK_ENV_REMOTE_USER;
2772         c->auth_type_indicator = JK_ENV_AUTH_TYPE;
2773         c->local_name_indicator = JK_ENV_LOCAL_NAME;
2774         c->local_port_indicator = JK_ENV_LOCAL_PORT;
2775
2776         /*
2777          * By default we will try to gather SSL info.
2778          * Disable this functionality through JkExtractSSL
2779          */
2780         c->ssl_enable = JK_TRUE;
2781         /*
2782          * The defaults ssl indicators match those in mod_ssl (seems
2783          * to be in more use).
2784          */
2785         c->https_indicator = JK_ENV_HTTPS;
2786         c->certs_indicator = JK_ENV_CERTS;
2787         c->cipher_indicator = JK_ENV_CIPHER;
2788         c->certchain_prefix = JK_ENV_CERTCHAIN_PREFIX;
2789         c->session_indicator = JK_ENV_SESSION;
2790         c->key_size_indicator = JK_ENV_KEY_SIZE;
2791         c->strip_session = JK_FALSE;
2792     }
2793     c->envvars_has_own = JK_FALSE;
2794
2795     c->s = s;
2796     apr_pool_cleanup_register(p, s, jk_apr_pool_cleanup, apr_pool_cleanup_null);
2797     return c;
2798 }
2799
2800
2801 /*
2802  * Utility - copy items from apr table src to dst,
2803  * for keys that exist in src but not in dst.
2804  */
2805 static void merge_apr_table(apr_table_t *src, apr_table_t *dst)
2806 {
2807     int i;
2808     const apr_array_header_t *arr;
2809     const apr_table_entry_t *elts;
2810
2811     arr = apr_table_elts(src);
2812     elts = (const apr_table_entry_t *)arr->elts;
2813     for (i = 0; i < arr->nelts; ++i) {
2814         if (!apr_table_get(dst, elts[i].key)) {
2815             apr_table_setn(dst, elts[i].key, elts[i].val);
2816         }
2817     }
2818 }
2819
2820
2821 /** Standard apache callback, merge jk options specified in <Directory>
2822     context or <Host>.
2823  */
2824 static void *merge_jk_config(apr_pool_t * p, void *basev, void *overridesv)
2825 {
2826     jk_server_conf_t *base = (jk_server_conf_t *) basev;
2827     jk_server_conf_t *overrides = (jk_server_conf_t *) overridesv;
2828
2829     if (!overrides->log_file)
2830         overrides->log_file = base->log_file;
2831     if (overrides->log_level == JK_UNSET)
2832         overrides->log_level = base->log_level;
2833
2834     if (!overrides->stamp_format_string)
2835         overrides->stamp_format_string = base->stamp_format_string;
2836     if (!overrides->format_string)
2837         overrides->format_string = base->format_string;
2838
2839     if (!overrides->worker_indicator)
2840         overrides->worker_indicator = base->worker_indicator;
2841
2842     if (!overrides->remote_addr_indicator)
2843         overrides->remote_addr_indicator = base->remote_addr_indicator;
2844     if (!overrides->remote_port_indicator)
2845         overrides->remote_port_indicator = base->remote_port_indicator;
2846     if (!overrides->remote_host_indicator)
2847         overrides->remote_host_indicator = base->remote_host_indicator;
2848     if (!overrides->remote_user_indicator)
2849         overrides->remote_user_indicator = base->remote_user_indicator;
2850     if (!overrides->auth_type_indicator)
2851         overrides->auth_type_indicator = base->auth_type_indicator;
2852     if (!overrides->local_name_indicator)
2853         overrides->local_name_indicator = base->local_name_indicator;
2854     if (!overrides->local_port_indicator)
2855         overrides->local_port_indicator = base->local_port_indicator;
2856
2857     if (overrides->ssl_enable == JK_UNSET)
2858         overrides->ssl_enable = base->ssl_enable;
2859     if (!overrides->https_indicator)
2860         overrides->https_indicator = base->https_indicator;
2861     if (!overrides->certs_indicator)
2862         overrides->certs_indicator = base->certs_indicator;
2863     if (!overrides->cipher_indicator)
2864         overrides->cipher_indicator = base->cipher_indicator;
2865     if (!overrides->certchain_prefix)
2866         overrides->certchain_prefix = base->certchain_prefix;
2867     if (!overrides->session_indicator)
2868         overrides->session_indicator = base->session_indicator;
2869     if (!overrides->key_size_indicator)
2870         overrides->key_size_indicator = base->key_size_indicator;
2871
2872 /* Don't simply accumulate bits in the JK_OPT_FWDURIMASK region, */
2873 /* because those are multi-bit values. */
2874     if (overrides->options & JK_OPT_FWDURIMASK)
2875         overrides->options |= (base->options & ~base->exclude_options) & ~JK_OPT_FWDURIMASK;
2876     else
2877         overrides->options |= (base->options & ~base->exclude_options);
2878
2879     if (base->envvars) {
2880         if (overrides->envvars && overrides->envvars_has_own) {
2881 /* merge_apr_table() preserves existing entries in overrides table */
2882             merge_apr_table(base->envvars, overrides->envvars);
2883             merge_apr_table(base->envvars_def, overrides->envvars_def);
2884         }
2885         else {
2886             overrides->envvars = base->envvars;
2887             overrides->envvars_def = base->envvars_def;
2888             overrides->envvar_items = base->envvar_items;
2889         }
2890     }
2891
2892     if (overrides->mountcopy == JK_UNSET && jk_mount_copy_all == JK_TRUE) {
2893         overrides->mountcopy = JK_TRUE;
2894     }
2895     if (overrides->uri_to_context && overrides->mountcopy == JK_TRUE) {
2896 /* jk_map_copy() preserves existing entries in overrides map */
2897         if (jk_map_copy(base->uri_to_context, overrides->uri_to_context) == JK_FALSE) {
2898                 jk_error_exit(JKLOG_MARK, APLOG_EMERG, overrides->s, p, "Memory error");
2899         }
2900         if (!overrides->mount_file)
2901             overrides->mount_file = base->mount_file;
2902     }
2903     if (overrides->mountcopy == JK_TRUE) {
2904         if (!overrides->alias_dir)
2905             overrides->alias_dir = base->alias_dir;
2906     }
2907     if (overrides->mount_file_reload == JK_UNSET)
2908         overrides->mount_file_reload = base->mount_file_reload;
2909     if (overrides->strip_session == JK_UNSET) {
2910         overrides->strip_session = base->strip_session;
2911         overrides->strip_session_name = base->strip_session_name;
2912     }
2913     return overrides;
2914 }
2915
2916 static int JK_METHOD jk_log_to_file(jk_logger_t *l, int level,
2917                                     int used, char *what)
2918 {
2919     if (l &&
2920         (l->level <= level || level == JK_LOG_REQUEST_LEVEL) &&
2921         l->logger_private && what && used > 0) {
2922         jk_file_logger_t *p = l->logger_private;
2923         if (p->jklogfp) {
2924             apr_status_t rv;
2925             apr_size_t wrote;
2926 #if defined(WIN32)
2927             what[used++] = '\r';
2928 #endif
2929             what[used++] = '\n';
2930             wrote = used;
2931             rv = apr_global_mutex_lock(jk_log_lock);
2932             if (rv != APR_SUCCESS) {
2933                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
2934                              "apr_global_mutex_lock(jk_log_lock) failed");
2935                 /* XXX: Maybe this should be fatal? */
2936             }
2937             rv = apr_file_write(p->jklogfp, what, &wrote);
2938             if (rv != APR_SUCCESS) {
2939                 char error[256];
2940                 apr_strerror(rv, error, 254);
2941                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
2942                              "mod_jk: jk_log_to_file %s failed: %s",
2943                              what, error);
2944             }
2945             rv = apr_global_mutex_unlock(jk_log_lock);
2946             if (rv != APR_SUCCESS) {
2947                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
2948                              "apr_global_mutex_unlock(jk_log_lock) failed");
2949                 /* XXX: Maybe this should be fatal? */
2950             }
2951         }
2952
2953         return JK_TRUE;
2954     }
2955
2956     return JK_FALSE;
2957 }
2958
2959 /*
2960 ** +-------------------------------------------------------+
2961 ** |                                                       |
2962 ** |              jk logfile support                       |
2963 ** |                                                       |
2964 ** +-------------------------------------------------------+
2965 */
2966
2967 static apr_status_t jklog_cleanup(void *d)
2968 {
2969     /* hgomez@20070425 */
2970     /* Clean up pointer content */
2971     if (d != NULL)
2972         *(jk_logger_t **)d = NULL;
2973
2974     return APR_SUCCESS;
2975 }
2976
2977 static int open_jklog(server_rec * s, apr_pool_t * p)
2978 {
2979     jk_server_conf_t *conf;
2980     const char *fname;
2981     apr_status_t rc;
2982     apr_file_t *jklogfp;
2983     piped_log *pl;
2984     jk_logger_t *jkl;
2985     jk_file_logger_t *flp;
2986     int jklog_flags = (APR_WRITE | APR_APPEND | APR_CREATE);
2987     apr_fileperms_t jklog_mode =
2988         (APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD);
2989
2990     conf = ap_get_module_config(s->module_config, &jk_module);
2991
2992     if (conf->log_file == NULL) {
2993         conf->log_file = ap_server_root_relative(p, JK_LOG_DEF_FILE);
2994         if (conf->log_file)
2995             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
2996                          "No JkLogFile defined in httpd.conf. "
2997                          "Using default %s", conf->log_file);
2998     }
2999     if (*(conf->log_file) == '\0') {
3000         ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
3001                      "mod_jk: Invalid JkLogFile EMPTY");
3002         conf->log = main_log;
3003         return 0;
3004     }
3005
3006     jklogfp = apr_hash_get(jk_log_fps, conf->log_file, APR_HASH_KEY_STRING);
3007     if (!jklogfp) {
3008         if (*conf->log_file == '|') {
3009             if ((pl = ap_open_piped_log(p, conf->log_file + 1)) == NULL) {
3010                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
3011                              "mod_jk: could not open reliable pipe "
3012                              "to jk log %s", conf->log_file + 1);
3013                 return -1;
3014             }
3015             jklogfp = (void *)ap_piped_log_write_fd(pl);
3016         }
3017         else {
3018             fname = ap_server_root_relative(p, conf->log_file);
3019             if (!fname) {
3020                 ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
3021                              "mod_jk: Invalid JkLog " "path %s", conf->log_file);
3022                 return -1;
3023             }
3024             if ((rc = apr_file_open(&jklogfp, fname,
3025                                     jklog_flags, jklog_mode, p))
3026                 != APR_SUCCESS) {
3027                 ap_log_error(APLOG_MARK, APLOG_ERR, rc, s,
3028                              "mod_jk: could not open JkLog " "file %s", fname);
3029                 return -1;
3030             }
3031         }
3032         apr_file_inherit_set(jklogfp);
3033         apr_hash_set(jk_log_fps, conf->log_file, APR_HASH_KEY_STRING, jklogfp);
3034     }
3035     conf->jklogfp = jklogfp;
3036     jkl = (jk_logger_t *)apr_palloc(p, sizeof(jk_logger_t));
3037     flp = (jk_file_logger_t *) apr_palloc(p, sizeof(jk_file_logger_t));
3038     if (jkl && flp) {
3039         jkl->log = jk_log_to_file;
3040         jkl->level = conf->log_level;
3041         jkl->logger_private = flp;
3042         flp->jklogfp = conf->jklogfp;
3043         conf->log = jkl;
3044         jk_set_time_fmt(conf->log, conf->stamp_format_string);
3045         if (main_log == NULL) {
3046             main_log = conf->log;
3047
3048             /* hgomez@20070425 */
3049             /* Shouldn't we clean both conf->log and main_log ?                   */
3050             /* Also should we pass pointer (ie: main_log) or handle (*main_log) ? */
3051             apr_pool_cleanup_register(p, &main_log,
3052                                       jklog_cleanup,
3053                                       apr_pool_cleanup_null);
3054         }
3055
3056         return 0;
3057     }
3058
3059     return -1;
3060 }
3061
3062 #if APR_HAS_THREADS
3063
3064 static void * APR_THREAD_FUNC jk_watchdog_func(apr_thread_t *thd, void *data)
3065 {
3066     int i;
3067     jk_server_conf_t *conf = (jk_server_conf_t *)data;
3068
3069     if (JK_IS_DEBUG_LEVEL(conf->log))
3070         jk_log(conf->log, JK_LOG_DEBUG,
3071                "Watchdog thread initialized with %d second interval",
3072                jk_watchdog_interval);
3073     for (;;) {
3074         for (i = 0; i < (jk_watchdog_interval * 10); i++) {
3075             /* apr_sleep() uses microseconds */
3076             apr_sleep((apr_time_t)(100000));
3077             if (!jk_watchdog_interval)
3078                 break;
3079         }
3080         if (!jk_watchdog_interval)
3081             break;
3082         if (JK_IS_DEBUG_LEVEL(conf->log))
3083            jk_log(conf->log, JK_LOG_DEBUG,
3084                   "Watchdog thread running");
3085         jk_watchdog_running = 1;
3086         wc_maintain(conf->log);
3087         if (!jk_watchdog_interval)
3088             break;
3089     }
3090     jk_watchdog_running = 0;
3091     return NULL;
3092 }
3093
3094 #endif
3095
3096 /** Standard apache callback, initialize jk.
3097  */
3098 static void jk_child_init(apr_pool_t * pconf, server_rec * s)
3099 {
3100     jk_server_conf_t *conf;
3101     apr_status_t rv;
3102     int rc;
3103     apr_thread_t *wdt;
3104
3105     conf = ap_get_module_config(s->module_config, &jk_module);
3106
3107     rv = apr_global_mutex_child_init(&jk_log_lock, NULL, pconf);
3108     if (rv != APR_SUCCESS) {
3109         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
3110                      "mod_jk: could not init JK log lock in child");
3111     }
3112
3113     JK_TRACE_ENTER(conf->log);
3114
3115     if (jk_watchdog_interval) {
3116 #if APR_HAS_THREADS
3117         rv = apr_thread_create(&wdt, NULL, jk_watchdog_func, conf, pconf);
3118         if (rv != APR_SUCCESS) {
3119             jk_log(conf->log, JK_LOG_ERROR,
3120                    "Could not init watchdog thread, error=%d", rv);
3121             jk_watchdog_interval = 0;
3122         }
3123         apr_thread_detach(wdt);
3124 #endif
3125     }
3126
3127     if ((rc = jk_shm_attach(jk_shm_file, jk_shm_size, conf->log)) == 0) {
3128         apr_pool_cleanup_register(pconf, conf->log, jk_cleanup_shmem,
3129                                   apr_pool_cleanup_null);
3130     }
3131     else
3132         jk_log(conf->log, JK_LOG_ERROR, "Attaching shm:%s errno=%d",
3133                jk_shm_name(), rc);
3134
3135     if (JK_IS_DEBUG_LEVEL(conf->log))
3136         jk_log(conf->log, JK_LOG_DEBUG, "Initialized %s", JK_FULL_EXPOSED_VERSION);
3137     JK_TRACE_EXIT(conf->log);
3138 }
3139
3140 /** Initialize jk, using worker.properties.
3141     We also use apache commands ( JkWorker, etc), but this use is
3142     deprecated, as we'll try to concentrate all config in
3143     workers.properties, urimap.properties, and ajp14 autoconf.
3144
3145     Apache config will only be used for manual override, using
3146     SetHandler and normal apache directives ( but minimal jk-specific
3147     stuff )
3148 */
3149 static int init_jk(apr_pool_t * pconf, jk_server_conf_t * conf,
3150                     server_rec * s)
3151 {
3152     int rc;
3153     int is_threaded;
3154     int mpm_threads = 1;
3155
3156     if (!jk_worker_properties)
3157         jk_map_alloc(&jk_worker_properties);
3158     jk_map_put(jk_worker_properties, "ServerRoot", ap_server_root, NULL);
3159
3160     /* Set default connection cache size for multi-threaded MPMs */
3161     if (ap_mpm_query(AP_MPMQ_IS_THREADED, &is_threaded) == APR_SUCCESS &&
3162         is_threaded != AP_MPMQ_NOT_SUPPORTED) {
3163         if (ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads) != APR_SUCCESS)
3164             mpm_threads = 1;
3165     }
3166     if (mpm_threads > 1) {
3167 #if _MT_CODE
3168         /* _MT_CODE  */
3169         if (JK_IS_DEBUG_LEVEL(conf->log)) {
3170 #if !defined(WIN32) && !defined(NETWARE)
3171 #if USE_FLOCK_LK
3172             jk_log(conf->log, JK_LOG_DEBUG,
3173                    "Using flock() for locking.");
3174 #else
3175             jk_log(conf->log, JK_LOG_DEBUG,
3176                    "Using fcntl() for locking.");
3177 #endif /* USE_FLOCK_LK */
3178 #else  /* WIN32 */
3179             jk_log(conf->log, JK_LOG_DEBUG,
3180                    "Not using locking.");
3181 #endif
3182         }
3183 #else
3184         /* !_MT_CODE */
3185         ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
3186                      "Cannot run prefork mod_jk on threaded server.");
3187         return JK_FALSE;
3188 #endif
3189     }
3190     if (JK_IS_DEBUG_LEVEL(conf->log))
3191         jk_log(conf->log, JK_LOG_DEBUG,
3192                "Setting default connection pool max size to %d",
3193                mpm_threads);
3194     jk_set_worker_def_cache_size(mpm_threads);
3195
3196     if ((jk_worker_file != NULL) &&
3197         !jk_map_read_properties(jk_worker_properties, NULL, jk_worker_file, NULL,
3198                                 JK_MAP_HANDLE_DUPLICATES, conf->log)) {
3199         ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
3200                      "Error in reading worker properties from '%s'",
3201                      jk_worker_file);
3202         return JK_FALSE;
3203     }
3204
3205     if (jk_map_resolve_references(jk_worker_properties, "worker.",
3206                                   1, 1, conf->log) == JK_FALSE) {
3207         ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
3208                      "Error in resolving configuration references");
3209         return JK_FALSE;
3210     }
3211
3212 #if !defined(WIN32) && !defined(NETWARE)
3213     if (!jk_shm_file) {
3214         jk_shm_file = ap_server_root_relative(pconf, JK_SHM_DEF_FILE);
3215         if (jk_shm_file)
3216             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
3217                          "No JkShmFile defined in httpd.conf. "
3218                          "Using default %s", jk_shm_file);
3219     }
3220 #endif
3221     if (jk_shm_size == 0)
3222         jk_shm_size = jk_shm_calculate_size(jk_worker_properties, conf->log);
3223     else if (jk_shm_size_set) {
3224         jk_log(conf->log, JK_LOG_WARNING,
3225                "The optimal shared memory size can now be determined automatically.");
3226         jk_log(conf->log, JK_LOG_WARNING,
3227                "You can remove the JkShmSize directive if you want to use the optimal size.");
3228     }
3229     if ((rc = jk_shm_open(jk_shm_file, jk_shm_size, conf->log)) == 0) {
3230         apr_pool_cleanup_register(pconf, conf->log,
3231                                   jk_cleanup_shmem,
3232                                   apr_pool_cleanup_null);
3233     }
3234     else
3235         jk_log(conf->log, JK_LOG_ERROR,
3236                "Initializing shm:%s errno=%d. Load balancing workers will not function properly.",
3237                jk_shm_name(), rc);
3238
3239     /* we add the URI->WORKER MAP since workers using AJP14
3240        will feed it */
3241     worker_env.uri_to_worker = conf->uw_map;
3242     worker_env.virtual = "*";   /* for now */
3243 #if ((AP_MODULE_MAGIC_AT_LEAST(20051115,4)) && !defined(API_COMPATIBILITY)) || (MODULE_MAGIC_NUMBER_MAJOR >= 20060905)
3244     worker_env.server_name = (char *)ap_get_server_description();
3245 #else
3246     worker_env.server_name = (char *)ap_get_server_version();
3247 #endif
3248     worker_env.pool = pconf;
3249
3250     if (wc_open(jk_worker_properties, &worker_env, conf->log)) {
3251         ap_add_version_component(pconf, JK_EXPOSED_VERSION);
3252         jk_log(conf->log, JK_LOG_INFO,
3253                "%s initialized",
3254                JK_FULL_EXPOSED_VERSION);
3255     }
3256     else {
3257         ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
3258                      "Error in creating the workers."
3259                      " Please consult your mod_jk log file '%s'.", conf->log_file);
3260         return JK_FALSE;
3261     }
3262     return JK_TRUE;
3263 }
3264
3265 static int jk_post_config(apr_pool_t * pconf,
3266                           apr_pool_t * plog,
3267                           apr_pool_t * ptemp, server_rec * s)
3268 {
3269     apr_status_t rv;
3270     jk_server_conf_t *conf;
3271     server_rec *srv = s;
3272     const char *err_string = NULL;
3273     void *data = NULL;
3274
3275     apr_pool_userdata_get(&data, JK_LOG_LOCK_KEY, s->process->pool);
3276     if (data == NULL) {
3277         /* create the jk log lockfiles in the parent */
3278         if ((rv = apr_global_mutex_create(&jk_log_lock, NULL,
3279                                           APR_LOCK_DEFAULT,
3280                                           s->process->pool)) != APR_SUCCESS) {
3281             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
3282                          "mod_jk: could not create jk_log_lock");
3283             return HTTP_INTERNAL_SERVER_ERROR;
3284         }
3285
3286 #if JK_NEED_SET_MUTEX_PERMS
3287 #if (MODULE_MAGIC_NUMBER_MAJOR >= 20090208)
3288         rv = ap_unixd_set_global_mutex_perms(jk_log_lock);
3289 #else
3290         rv = unixd_set_global_mutex_perms(jk_log_lock);
3291 #endif
3292         if (rv != APR_SUCCESS) {
3293             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
3294                          "mod_jk: Could not set permissions on "
3295                          "jk_log_lock; check User and Group directives");
3296             return HTTP_INTERNAL_SERVER_ERROR;
3297         }
3298 #endif
3299         apr_pool_userdata_set((const void *)jk_log_lock, JK_LOG_LOCK_KEY,
3300                               apr_pool_cleanup_null, s->process->pool);
3301     } else {
3302         jk_log_lock = (apr_global_mutex_t *)data;
3303     }
3304
3305     main_server = s;
3306     jk_log_fps = apr_hash_make(pconf);
3307
3308     if (!s->is_virtual) {
3309         conf = (jk_server_conf_t *)ap_get_module_config(s->module_config,
3310                                                         &jk_module);
3311         if (conf->was_initialized == JK_FALSE) {
3312             /* step through the servers and open each jk logfile
3313              * and do additional post config initialization.
3314              */
3315             for (; srv; srv = srv->next) {
3316                 jk_server_conf_t *sconf = (jk_server_conf_t *)ap_get_module_config(srv->module_config,
3317                                                                                    &jk_module);
3318
3319 /*
3320  * If a virtual server contains no JK directive, httpd shares
3321  * the config structure. But we don't want to share some settings
3322  * by default, especially the JkMount rules.
3323  * Therefore we check, if this config structure really belongs to this
3324  * vhost, otherwise we create a new one and merge.
3325  */
3326                 if (sconf && sconf->s != srv) {
3327                     jk_server_conf_t *srvconf = (jk_server_conf_t *)create_jk_config(pconf, srv);
3328                     sconf = (jk_server_conf_t *)merge_jk_config(pconf, sconf, srvconf);
3329                     ap_set_module_config(srv->module_config, &jk_module, sconf);
3330
3331                 }
3332
3333                 if (sconf && sconf->was_initialized == JK_FALSE) {
3334                     sconf->was_initialized = JK_TRUE;
3335                     if (open_jklog(srv, pconf))
3336                         return HTTP_INTERNAL_SERVER_ERROR;
3337                     sconf->options &= ~sconf->exclude_options;
3338                     if (sconf->uri_to_context) {
3339                         if (!uri_worker_map_alloc(&(sconf->uw_map),
3340                                                   sconf->uri_to_context, sconf->log))
3341                             jk_error_exit(JKLOG_MARK, APLOG_EMERG, srv,
3342                                           srv->process->pool, "Memory error");
3343                         if (sconf->options & JK_OPT_REJECTUNSAFE)
3344                             sconf->uw_map->reject_unsafe = 1;
3345                         else
3346                             sconf->uw_map->reject_unsafe = 0;
3347                         if (sconf->mount_file) {
3348                             sconf->uw_map->fname = sconf->mount_file;
3349                             sconf->uw_map->reload = sconf->mount_file_reload;
3350                             uri_worker_map_switch(sconf->uw_map, sconf->log);
3351                             uri_worker_map_load(sconf->uw_map, sconf->log);
3352                         }
3353                     }
3354                     else {
3355                         if (sconf->mountcopy == JK_TRUE) {
3356                             sconf->uw_map = conf->uw_map;
3357                         }
3358                     }
3359                     if (sconf->format_string) {
3360                         sconf->format =
3361                             parse_request_log_string(pconf, sconf->format_string, &err_string);
3362                         if (sconf->format == NULL)
3363                             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
3364                                          "JkRequestLogFormat format array NULL");
3365                     }
3366                     if (sconf->envvars && sconf->envvars_has_own) {
3367                         int i;
3368                         const apr_array_header_t *arr;
3369                         const apr_table_entry_t *elts;
3370                         envvar_item *item;
3371                         const char *envvar_def;
3372
3373                         arr = apr_table_elts(sconf->envvars);
3374                         if (arr) {
3375                             elts = (const apr_table_entry_t *)arr->elts;
3376                             for (i = 0; i < arr->nelts; ++i) {
3377                                 item = (envvar_item *)apr_array_push(sconf->envvar_items);
3378                                 if (!item)
3379                                     return HTTP_INTERNAL_SERVER_ERROR;
3380                                 item->name = elts[i].key;
3381                                 envvar_def = apr_table_get(sconf->envvars_def, elts[i].key);
3382                                 if (envvar_def && !strcmp("1", envvar_def) ) {
3383                                     item->value = elts[i].val;
3384                                     item->has_default = 1;
3385                                 }
3386                                 else {
3387                                     item->value = "";
3388                                     item->has_default = 0;
3389                                 }
3390                             }
3391                         }
3392                     }
3393                 }
3394             }
3395             conf->was_initialized = JK_TRUE;
3396             if (init_jk(pconf, conf, s) == JK_FALSE)
3397                 return HTTP_INTERNAL_SERVER_ERROR;
3398             if (conf->uw_map) {
3399                 uri_worker_map_ext(conf->uw_map, conf->log);
3400                 uri_worker_map_switch(conf->uw_map, conf->log);
3401             }
3402             for (srv = s; srv; srv = srv->next) {
3403                 jk_server_conf_t *sconf = (jk_server_conf_t *)ap_get_module_config(srv->module_config,
3404                                                                                    &jk_module);
3405                 if (conf->uw_map != sconf->uw_map && sconf->uw_map) {
3406                     uri_worker_map_ext(sconf->uw_map, sconf->log);
3407                     uri_worker_map_switch(sconf->uw_map, sconf->log);
3408                 }
3409             }
3410
3411         }
3412     }
3413
3414     return OK;
3415 }
3416
3417 /** Use the internal mod_jk mappings to find if this is a request for
3418  *    tomcat and what worker to use.
3419  */
3420 static int jk_translate(request_rec * r)
3421 {
3422     ap_set_module_config(r->request_config, &jk_module, NULL);
3423
3424     if (!r->proxyreq) {
3425         jk_server_conf_t *conf =
3426             (jk_server_conf_t *) ap_get_module_config(r->server->
3427                                                       module_config,
3428                                                       &jk_module);
3429
3430         if (conf) {
3431             const char *worker;
3432             if ((r->handler != NULL) && (!strcmp(r->handler, JK_HANDLER))) {
3433                 /* Somebody already set the handler, probably manual config
3434                  * or "native" configuration, no need for extra overhead
3435                  */
3436                 if (JK_IS_DEBUG_LEVEL(conf->log))
3437                     jk_log(conf->log, JK_LOG_DEBUG,
3438                            "Manually mapped, no need to call uri_to_worker");
3439                 return DECLINED;
3440             }
3441
3442             if (apr_table_get(r->subprocess_env, "no-jk")) {
3443                 if (JK_IS_DEBUG_LEVEL(conf->log))
3444                     jk_log(conf->log, JK_LOG_DEBUG,
3445                            "Into translate no-jk env var detected for uri=%s, declined",
3446                            r->uri);
3447
3448                 return DECLINED;
3449             }
3450
3451             /* Special case to make sure that apache can serve a directory
3452                listing if there are no matches for the DirectoryIndex and
3453                Tomcat webapps are mapped into apache using JkAutoAlias. */
3454             if (r->main != NULL && r->main->handler != NULL &&
3455                 (conf->alias_dir != NULL) &&
3456                 !strcmp(r->main->handler, DIR_MAGIC_TYPE)) {
3457
3458                 /* Append the request uri to the JkAutoAlias directory and
3459                    determine if the file exists. */
3460                 char *clean_uri;
3461                 apr_finfo_t finfo;
3462                 finfo.filetype = APR_NOFILE;
3463                 clean_uri = apr_pstrdup(r->pool, r->uri);
3464                 ap_no2slash(clean_uri);
3465                 /* Map uri to a context static file */
3466                 if (strlen(clean_uri) > 1) {
3467                     char *context_path = NULL;
3468
3469                     context_path = apr_pstrcat(r->pool, conf->alias_dir,
3470                                                ap_os_escape_path(r->pool,
3471                                                                  clean_uri,
3472                                                                  1), NULL);
3473                     if (context_path != NULL) {
3474                         apr_stat(&finfo, context_path, APR_FINFO_TYPE,
3475                                  r->pool);
3476                     }
3477                 }
3478                 if (finfo.filetype != APR_REG) {
3479                     if (JK_IS_DEBUG_LEVEL(conf->log))
3480                         jk_log(conf->log, JK_LOG_DEBUG,
3481                                "JkAutoAlias, no DirectoryIndex file for URI %s",
3482                                r->uri);
3483                     return DECLINED;
3484                 }
3485             }
3486             if (!conf->uw_map) {
3487                 if (JK_IS_DEBUG_LEVEL(conf->log))
3488                     jk_log(conf->log, JK_LOG_DEBUG,
3489                            "missing uri map for %s:%s",
3490                            conf->s->server_hostname ? conf->s->server_hostname : "_default_",
3491                            r->uri);
3492                 return DECLINED;
3493             }
3494             else {
3495                 rule_extension_t *e;
3496                 worker = map_uri_to_worker_ext(conf->uw_map, r->uri,
3497                                                NULL, &e, NULL, conf->log);
3498                 ap_set_module_config(r->request_config, &jk_module, e);
3499             }
3500
3501             if (worker) {
3502                 r->handler = apr_pstrdup(r->pool, JK_HANDLER);
3503                 apr_table_setn(r->notes, JK_NOTE_WORKER_NAME, worker);
3504
3505                 /* This could be a sub-request, possibly from mod_dir */
3506                 /* Also add the the HANDLER to the main request */
3507                 if (r->main) {
3508                     r->main->handler = apr_pstrdup(r->main->pool, JK_HANDLER);
3509                     apr_table_setn(r->main->notes, JK_NOTE_WORKER_NAME, worker);
3510                 }
3511
3512                 return OK;
3513             }
3514             else if (conf->alias_dir != NULL) {
3515                 char *clean_uri = apr_pstrdup(r->pool, r->uri);
3516                 ap_no2slash(clean_uri);
3517                 /* Automatically map uri to a context static file */
3518                 if (JK_IS_DEBUG_LEVEL(conf->log))
3519                     jk_log(conf->log, JK_LOG_DEBUG,
3520                            "check alias_dir: %s",
3521                            conf->alias_dir);
3522                 if (strlen(clean_uri) > 1) {
3523                     /* Get the context directory name */
3524                     char *context_dir = NULL;
3525                     char *context_path = NULL;
3526                     char *child_dir = NULL;
3527                     char *index = clean_uri;
3528                     char *suffix = strchr(index + 1, '/');
3529                     if (suffix != NULL) {
3530                         int size = suffix - index;
3531                         context_dir = apr_pstrndup(r->pool, index, size);
3532                         /* Get the context child directory name */
3533                         index = index + size + 1;
3534                         suffix = strchr(index, '/');
3535                         if (suffix != NULL) {
3536                             size = suffix - index;
3537                             child_dir = apr_pstrndup(r->pool, index, size);
3538                         }
3539                         else {
3540                             child_dir = index;
3541                         }
3542                         /* Deny access to WEB-INF and META-INF directories */
3543                         if (child_dir != NULL) {
3544                             if (JK_IS_DEBUG_LEVEL(conf->log))
3545                                 jk_log(conf->log, JK_LOG_DEBUG,
3546                                        "AutoAlias child_dir: %s",
3547                                        child_dir);
3548                             if (!strcasecmp(child_dir, "WEB-INF")
3549                                 || !strcasecmp(child_dir, "META-INF")) {
3550                                 if (JK_IS_DEBUG_LEVEL(conf->log))
3551                                     jk_log(conf->log, JK_LOG_DEBUG,
3552                                            "AutoAlias HTTP_NOT_FOUND for URI: %s",
3553                                            r->uri);
3554                                 return HTTP_NOT_FOUND;
3555                             }
3556                         }
3557                     }
3558                     else {
3559                         context_dir = apr_pstrdup(r->pool, index);
3560                     }
3561
3562                     context_path = apr_pstrcat(r->pool, conf->alias_dir,
3563                                                ap_os_escape_path(r->pool,
3564                                                                  context_dir,
3565                                                                  1), NULL);
3566                     if (context_path != NULL) {
3567                         apr_finfo_t finfo;
3568                         finfo.filetype = APR_NOFILE;
3569                         apr_stat(&finfo, context_path, APR_FINFO_TYPE,
3570                                  r->pool);
3571                         if (finfo.filetype == APR_DIR) {
3572                             char *escurl =
3573                                 ap_os_escape_path(r->pool, clean_uri, 1);
3574                             char *ret =
3575                                 apr_pstrcat(r->pool, conf->alias_dir, escurl,
3576                                             NULL);
3577                             /* Add code to verify real path ap_os_canonical_name */
3578                             if (ret != NULL) {
3579                                 if (JK_IS_DEBUG_LEVEL(conf->log))
3580                                     jk_log(conf->log, JK_LOG_DEBUG,
3581                                            "AutoAlias OK for file: %s",
3582                                            ret);
3583                                 r->filename = ret;
3584                                 return OK;
3585                             }
3586                         }
3587                         else {
3588                             /* Deny access to war files in web app directory */
3589                             int size = strlen(context_dir);
3590                             if (size > 4
3591                                 && !strcasecmp(context_dir + (size - 4),
3592                                                ".war")) {
3593                                 if (JK_IS_DEBUG_LEVEL(conf->log))
3594                                     jk_log(conf->log, JK_LOG_DEBUG,
3595                                            "AutoAlias HTTP_FORBIDDEN for URI: %s",
3596                                            r->uri);
3597                                 return HTTP_FORBIDDEN;
3598                             }
3599                         }
3600                     }
3601                 }
3602             }
3603             else {
3604                 if (JK_IS_DEBUG_LEVEL(conf->log))
3605                     jk_log(conf->log, JK_LOG_DEBUG,
3606                            "no match for %s found",
3607                            r->uri);
3608             }
3609         }
3610     }
3611
3612     return DECLINED;
3613 }
3614
3615 /* bypass the directory_walk and file_walk for non-file requests */
3616 static int jk_map_to_storage(request_rec * r)
3617 {
3618
3619     if (!r->proxyreq && !apr_table_get(r->notes, JK_NOTE_WORKER_NAME)) {
3620         jk_server_conf_t *conf =
3621             (jk_server_conf_t *) ap_get_module_config(r->server->
3622                                                       module_config,
3623                                                       &jk_module);
3624
3625         if (conf) {
3626             const char *worker;
3627             if ((r->handler != NULL) && (!strcmp(r->handler, JK_HANDLER))) {
3628                 /* Somebody already set the handler, probably manual config
3629                  * or "native" configuration, no need for extra overhead
3630                  */
3631                 if (JK_IS_DEBUG_LEVEL(conf->log))
3632                     jk_log(conf->log, JK_LOG_DEBUG,
3633                            "Manually mapped, no need to call uri_to_worker");
3634                 return DECLINED;
3635             }
3636
3637             if (apr_table_get(r->subprocess_env, "no-jk")) {
3638                 if (JK_IS_DEBUG_LEVEL(conf->log))
3639                     jk_log(conf->log, JK_LOG_DEBUG,
3640                            "Into map_to_storage no-jk env var detected for uri=%s, declined",
3641                            r->uri);
3642
3643                 return DECLINED;
3644             }
3645             if (!conf->uw_map) {
3646                 if (JK_IS_DEBUG_LEVEL(conf->log))
3647                     jk_log(conf->log, JK_LOG_DEBUG,
3648                            "missing uri map for %s:%s",
3649                            conf->s->server_hostname ? conf->s->server_hostname : "_default_",
3650                            r->uri);
3651                 return DECLINED;
3652             }
3653             else {
3654                 rule_extension_t *e;
3655                 worker = map_uri_to_worker_ext(conf->uw_map, r->uri,
3656                                                NULL, &e, NULL, conf->log);
3657                 ap_set_module_config(r->request_config, &jk_module, e);
3658             }
3659
3660             if (worker) {
3661                 r->handler = apr_pstrdup(r->pool, JK_HANDLER);
3662                 apr_table_setn(r->notes, JK_NOTE_WORKER_NAME, worker);
3663
3664                 /* This could be a sub-request, possibly from mod_dir */
3665                 if (r->main)
3666                     apr_table_setn(r->main->notes, JK_NOTE_WORKER_NAME, worker);
3667
3668             }
3669             else {
3670                 if (JK_IS_DEBUG_LEVEL(conf->log))
3671                     jk_log(conf->log, JK_LOG_DEBUG,
3672                            "no match for %s found",
3673                            r->uri);
3674                 if (conf->strip_session == JK_TRUE &&
3675                     conf->strip_session_name) {
3676                     char *jsessionid;
3677                     if (r->uri) {
3678                         jsessionid = strstr(r->uri, conf->strip_session_name);
3679                         if (jsessionid) {
3680                             if (JK_IS_DEBUG_LEVEL(conf->log))
3681                                 jk_log(conf->log, JK_LOG_DEBUG,
3682                                        "removing session identifier [%s] for non servlet url [%s]",
3683                                        jsessionid, r->uri);
3684                             *jsessionid = '\0';
3685                         }
3686                     }
3687                     if (r->filename) {
3688                         jsessionid = strstr(r->filename, conf->strip_session_name);
3689                         if (jsessionid)
3690                             *jsessionid = '\0';
3691                     }
3692                     return DECLINED;
3693                 }
3694             }
3695         }
3696     }
3697
3698     if (apr_table_get(r->notes, JK_NOTE_WORKER_NAME)) {
3699
3700         /* First find just the name of the file, no directory */
3701         r->filename = (char *)apr_filepath_name_get(r->uri);
3702
3703         /* Only if sub-request for a directory, most likely from mod_dir */
3704         if (r->main && r->main->filename &&
3705             (!apr_filepath_name_get(r->main->filename) ||
3706              !strlen(apr_filepath_name_get(r->main->filename)))) {
3707
3708             /* The filename from the main request will be set to what should
3709              * be picked up, aliases included. Tomcat will need to know about
3710              * those aliases or things won't work for them. Normal files should
3711              * be fine. */
3712
3713             /* Need absolute path to stat */
3714             if (apr_filepath_merge(&r->filename,
3715                                    r->main->filename, r->filename,
3716                                    APR_FILEPATH_SECUREROOT |
3717                                    APR_FILEPATH_TRUENAME, r->pool)
3718                 != APR_SUCCESS) {
3719                 return DECLINED;        /* We should never get here, very bad */
3720             }
3721
3722             /* Stat the file so that mod_dir knows it's there */
3723             apr_stat(&r->finfo, r->filename, APR_FINFO_TYPE, r->pool);
3724         }
3725
3726         return OK;
3727     }
3728     return DECLINED;
3729 }
3730
3731 static void jk_register_hooks(apr_pool_t * p)
3732 {
3733     ap_hook_handler(jk_handler, NULL, NULL, APR_HOOK_MIDDLE);
3734     ap_hook_post_config(jk_post_config, NULL, NULL, APR_HOOK_MIDDLE);
3735     ap_hook_child_init(jk_child_init, NULL, NULL, APR_HOOK_MIDDLE);
3736     ap_hook_translate_name(jk_translate, NULL, NULL, APR_HOOK_MIDDLE);
3737     ap_hook_map_to_storage(jk_map_to_storage, NULL, NULL, APR_HOOK_MIDDLE);
3738 }
3739
3740 module AP_MODULE_DECLARE_DATA jk_module = {
3741     STANDARD20_MODULE_STUFF,
3742     NULL,                       /* dir config creater */
3743     NULL,                       /* dir merger --- default is to override */
3744     create_jk_config,           /* server config */
3745     merge_jk_config,            /* merge server config */
3746     jk_cmds,                    /* command ap_table_t */
3747     jk_register_hooks           /* register hooks */
3748 };