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