bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / tomcat-connectors-1.2.32-src / native / iis / jk_isapi_plugin.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: ISAPI plugin for IIS/PWS                                   *
20  * Author:      Gal Shachor <shachor@il.ibm.com>                           *
21  * Author:      Larry Isaacs <larryi@apache.org>                           *
22  * Author:      Ignacio J. Ortega <nacho@apache.org>                       *
23  * Author:      Mladen Turk <mturk@apache.org>                             *
24  * Version:     $Revision: 1129429 $                                        *
25  ***************************************************************************/
26
27 // This define is needed to include wincrypt,h, needed to get client certificates
28 #ifndef _WIN32_WINNT
29 #define _WIN32_WINNT 0x0500
30 #endif
31 #include <winsock2.h>
32 #include <httpext.h>
33 #include <httpfilt.h>
34 #include <wininet.h>
35
36 #include "jk_global.h"
37 #include "jk_url.h"
38 #include "jk_util.h"
39 #include "jk_map.h"
40 #include "jk_pool.h"
41 #include "jk_service.h"
42 #include "jk_worker.h"
43 #include "jk_uri_worker_map.h"
44 #include "jk_shm.h"
45 #include "jk_ajp13.h"
46 #include "pcre.h"
47
48 #ifndef POSIX_MALLOC_THRESHOLD
49 #define POSIX_MALLOC_THRESHOLD (10)
50 #endif
51
52 #include <strsafe.h>
53
54 #define VERSION_STRING "Jakarta/ISAPI/" JK_EXPOSED_VERSION
55 #define FULL_VERSION_STRING "Jakarta/ISAPI/" JK_FULL_EXPOSED_VERSION
56 #define SHM_DEF_NAME   "JKISAPISHMEM"
57 #define DEFAULT_WORKER_NAME ("ajp13")
58
59 /*
60  * This is default value found inside httpd.conf
61  * for MaxClients
62  */
63 #define DEFAULT_WORKER_THREADS  250
64
65 /*
66  * We use special headers to pass values from the filter to the
67  * extension. These values are:
68  *
69  * 1. The real URI before redirection took place
70  * 2. The name of the worker to be used.
71  * 3. The contents of the Translate header, if any
72  *
73  */
74 #define URI_HEADER_NAME_BASE              ("TOMCATURI")
75 #define QUERY_HEADER_NAME_BASE            ("TOMCATQUERY")
76 #define WORKER_HEADER_NAME_BASE           ("TOMCATWORKER")
77 #define WORKER_HEADER_INDEX_BASE          ("TOMCATWORKERIDX")
78 #define TOMCAT_TRANSLATE_HEADER_NAME_BASE ("TOMCATTRANSLATE")
79 #ifndef USE_CGI_HEADERS
80 #define CONTENT_LENGTH                    ("CONTENT-LENGTH:")
81 #else
82 #define CONTENT_LENGTH                    ("CONTENT_LENGTH:")
83 #endif
84
85 /* The HTTP_ form of the header for use in ExtensionProc */
86 #define HTTP_HEADER_PREFIX       "HTTP_"
87 #ifdef USE_CGI_HEADERS
88 #define HTTP_HEADER_PREFIX_LEN   5
89 #endif
90
91 /* The template used to construct our unique headers
92  * from the base name and module instance
93  */
94 #define HEADER_TEMPLATE      "%s%p:"
95 #define HTTP_HEADER_TEMPLATE HTTP_HEADER_PREFIX "%s%p"
96
97 static char URI_HEADER_NAME[MAX_PATH];
98 static char QUERY_HEADER_NAME[MAX_PATH];
99 static char WORKER_HEADER_NAME[MAX_PATH];
100 static char TOMCAT_TRANSLATE_HEADER_NAME[MAX_PATH];
101 static char WORKER_HEADER_INDEX[MAX_PATH];
102
103 /* The variants of the special headers after IIS adds
104  * "HTTP_" to the front of them
105  */
106 static char HTTP_URI_HEADER_NAME[MAX_PATH];
107 static char HTTP_QUERY_HEADER_NAME[MAX_PATH];
108 static char HTTP_WORKER_HEADER_NAME[MAX_PATH];
109 static char HTTP_WORKER_HEADER_INDEX[MAX_PATH];
110
111 #define REGISTRY_LOCATION       ("Software\\Apache Software Foundation\\Jakarta Isapi Redirector\\1.0")
112 #define W3SVC_REGISTRY_KEY      ("SYSTEM\\CurrentControlSet\\Services\\W3SVC\\Parameters")
113 #define EXTENSION_URI_TAG       ("extension_uri")
114
115 #define URI_SELECT_TAG              ("uri_select")
116 #define URI_SELECT_PARSED_VERB      ("parsed")
117 #define URI_SELECT_UNPARSED_VERB    ("unparsed")
118 #define URI_SELECT_ESCAPED_VERB     ("escaped")
119 #define URI_SELECT_PROXY_VERB       ("proxy")
120 #define URI_REWRITE_TAG             ("rewrite_rule_file")
121 #define SHM_SIZE_TAG                ("shm_size")
122 #define WORKER_MOUNT_RELOAD_TAG     ("worker_mount_reload")
123 #define STRIP_SESSION_TAG           ("strip_session")
124 #ifndef AUTOMATIC_AUTH_NOTIFICATION
125 #define AUTH_COMPLETE_TAG           ("auth_complete")
126 #endif
127 #define REJECT_UNSAFE_TAG           ("reject_unsafe")
128 #define WATCHDOG_INTERVAL_TAG       ("watchdog_interval")
129 #define ENABLE_CHUNKED_ENCODING_TAG ("enable_chunked_encoding")
130 #define ERROR_PAGE_TAG              ("error_page")
131
132 #define LOG_ROTATION_TIME_TAG       ("log_rotationtime")
133 #define LOG_FILESIZE_TAG            ("log_filesize")
134
135 /* HTTP standard headers */
136 #define TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE     ("Transfer-Encoding: chunked")
137 #define TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE_LEN (26)
138 #define TRANSFER_ENCODING_HEADER_NAME                 ("Transfer-Encoding")
139 #define TRANSFER_ENCODING_HEADER_NAME_LEN             (17)
140 #define TRANSFER_ENCODING_IDENTITY_VALUE              ("identity")
141 #define TRANSFER_ENCODING_CHUNKED_VALUE               ("chunked")
142 #define TRANSFER_ENCODING_CHUNKED_VALUE_LEN           (7)
143
144 #define CONTENT_LENGTH_HEADER_NAME                    ("Content-Length")
145 #define CONTENT_LENGTH_HEADER_NAME_LEN                (14)
146
147 #define CONNECTION_HEADER_NAME      ("Connection")
148 #define CONNECTION_CLOSE_VALUE      ("Close")
149
150 #define TRANSLATE_HEADER            ("Translate:")
151 #define TRANSLATE_HEADER_NAME       ("Translate")
152 #define TRANSLATE_HEADER_NAME_LC    ("translate")
153
154 /* HTTP protocol CRLF */
155 #define CRLF                        ("\r\n")
156 #define CRLF_LEN                    (2)
157
158 /* Transfer-Encoding: chunked content trailer */
159 #define CHUNKED_ENCODING_TRAILER     ("0\r\n\r\n")
160 #define CHUNKED_ENCODING_TRAILER_LEN (5)
161
162 /* Hex of chunk length (one char per byte) + CRLF + terminator. */
163 #define CHUNK_HEADER_BUFFER_SIZE     (sizeof(unsigned int)*2+CRLF_LEN+1)
164
165 #define BAD_REQUEST     -1
166 #define BAD_PATH        -2
167 #define MAX_SERVERNAME  128
168 #define MAX_INSTANCEID  32
169 #define MAX_PACKET_SIZE 65536
170
171 char HTML_ERROR_HEAD[] =        "<!--\n"
172                                 "  Licensed to the Apache Software Foundation (ASF) under one or more\n"
173                                 "  contributor license agreements.  See the NOTICE file distributed with\n"
174                                 "  this work for additional information regarding copyright ownership.\n"
175                                 "  The ASF licenses this file to You under the Apache License, Version 2.0\n"
176                                 "  (the \"License\"); you may not use this file except in compliance with\n"
177                                 "  the License.  You may obtain a copy of the License at\n\n"
178                                 "      http://www.apache.org/licenses/LICENSE-2.0\n\n"
179                                 "  Unless required by applicable law or agreed to in writing, software\n"
180                                 "  distributed under the License is distributed on an \"AS IS\" BASIS,\n"
181                                 "  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
182                                 "  See the License for the specific language governing permissions and\n"
183                                 "  limitations under the License.\n"
184                                 "  -->\n"
185                                 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"\n"
186                                 "\"http://www.w3c.org/TR/REC-html40/loose.dtd\">\n"
187                                 "<HTML>\n<HEAD>\n"
188                                 "<META http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\n"
189                                 "<STYLE TYPE=\"text/css\">\n"
190                                 "    body {\n"
191                                 "       color: #000000;\n"
192                                 "       background-color: #FFFFFF;\n"
193                                 "       font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif;\n"
194                                 "       font-size: 9pt;\n"
195                                 "       margin: 10px 10px;\n"
196                                 "    }\n"
197                                 "    p#footer {\n"
198                                 "       text-align: right;\n"
199                                 "       font-size: 80%;\n"
200                                 "    }\n"
201                                 "</STYLE>\n";
202
203 #define HTML_ERROR_BODY_FMT     "<TITLE>%s!</TITLE>\n</HEAD>\n<BODY>\n<H1>%s!</H1>\n<P>\n%s\n</P>\n"
204
205 char HTML_ERROR_TAIL[] =        "<P>\n<BR/>&nbsp;<BR/>&nbsp;<BR/>&nbsp;<BR/>&nbsp;\n"
206                                 FULL_VERSION_STRING "\n"
207                                 "<BR/>&nbsp;\n"
208                                 "<HR/>\n"
209                                 "<P id=\"footer\">\n"
210                                 "Copyright &copy; 1999-2011 Apache Software Foundation<BR/>\n"
211                                 "All Rights Reserved\n"
212                                 "</P>\n</BODY>\n</HTML>\n";
213
214 static struct error_reasons {
215     int status;
216     const char *reason;
217     const char *title;
218     const char *description;
219 } error_reasons[] = {
220     { 100,
221       "Continue",
222       NULL,
223       NULL
224     },
225     { 101,
226       "Switching Protocols",
227       NULL,
228       NULL
229     },
230     { 200,
231       "OK",
232       NULL,
233       NULL
234     },
235     { 201,
236       "Created",
237       NULL,
238       NULL
239     },
240     { 202,
241       "Accepted",
242       NULL,
243       NULL
244     },
245     { 203,
246       "Non-Authoritative Information",
247       NULL,
248       NULL
249     },
250     { 204,
251       "No Content",
252       NULL,
253       NULL
254     },
255     { 205,
256       "Reset Content",
257       NULL,
258       NULL
259     },
260     { 206,
261       "Partial Content",
262       NULL,
263       NULL
264     },
265     { 300,
266       "Multiple Choices",
267       NULL,
268       NULL
269     },
270     { 301,
271       "Moved Permanently",
272       NULL,
273       NULL
274     },
275     { 302,
276       "Moved Temporarily",
277       NULL,
278       NULL
279     },
280     { 303,
281       "See Other",
282       NULL,
283       NULL
284     },
285     { 304,
286       "Not Modified",
287       NULL,
288       NULL
289     },
290     { 305,
291       "Use Proxy",
292       NULL,
293       NULL
294     },
295     { 400,
296       "Bad Request",
297       "Bad Request",
298       "Your browser (or proxy) sent a request that "
299       "this server could not understand."
300     },
301     { 401,
302       "Unauthorized",
303       "Access is denied due to invalid credentials",
304       "You do not have permission to view this directory or "
305       "page using the credentials that you supplied."
306     },
307     { 402,
308       "Payment Required",
309       NULL,
310       NULL
311     },
312     { 403,
313       "Forbidden",
314       "Access is denied",
315       "You do not have permission to view this directory or page "
316       "using the credentials that you supplied."
317     },
318     { 404,
319       "Not Found",
320       "The requested URL was not found on this server",
321       "If you entered the URL manually please check your"
322       "spelling and try again."
323     },
324     { 405,
325       "Method Not Allowed",
326       "HTTP method used to access this page is not allowed",
327       "The page you are looking for cannot be displayed because an "
328       "invalid method (HTTP method) was used to attempt access."
329     },
330     { 406,
331       "Not Acceptable",
332       "Client browser does not accept the MIME type of the requested page",
333       "The page you are looking for cannot be opened by your browser "
334       "because it has a file name extension that your browser "
335       "does not accept."
336     },
337     { 407,
338       "Proxy Authentication Required",
339       NULL,
340       "The client must first authenticate itself with the proxy"
341     },
342     { 408,
343       "Request Timeout",
344       NULL,
345       "The client did not produce a request within the time "
346       "that the server was prepared to wait."
347     },
348     { 409,
349       "Conflict",
350       NULL,
351       "The request could not be completed due to a conflict with "
352       "the current state of the resource."
353     },
354     { 410,
355       "Gone",
356       NULL,
357       "The requested resource is no longer available at the "
358       "server and no forwarding address is known."
359     },
360     { 411,
361       "Length Required",
362       NULL,
363       "The server refuses to accept the request without a "
364       "defined Content-Length."
365     },
366     { 412,
367       "Precondition Failed",
368       NULL,
369       "The precondition given in one or more of the request "
370       "header fields evaluated to false when it was tested on the server."
371     },
372     { 413,
373       "Request Entity Too Large",
374       NULL,
375       "The HTTP method does not allow the data transmitted, "
376       "or the data volume exceeds the capacity limit."
377     },
378     { 414,
379       "Request-URI Too Long",
380       "Submitted URI too large",
381       "The length of the requested URL exceeds the capacity limit "
382       "for this server. The request cannot be processed."
383     },
384     { 415,
385       "Unsupported Media Type",
386       NULL,
387       "The server is refusing to service the request because the "
388       "entity of the request is in a format not supported by the "
389       "requested resource for the requested method."
390     },
391     { 500,
392       "Internal Server Error",
393       NULL,
394       "The server encountered an internal error and was "
395       "unable to complete your request."
396     },
397     { 501,
398       "Not Implemented",
399       NULL,
400       "The server does not support the functionality required "
401       "to fulfill the request."
402     },
403     { 502,
404       "Bad Gateway",
405       NULL,
406       "There is a problem with the page you are looking for, "
407       "and it cannot be displayed. When the Web server (while "
408       "acting as a gateway or proxy) contacted the upstream content "
409       "server, it received an invalid response from the content server."
410     },
411     { 503,
412       "Service Unavailable",
413       "Service Temporary Unavailable",
414       "The server is temporarily unable to service your "
415       "request due to maintenance downtime or capacity problems. "
416       "Please try again later."
417     },
418     { 504,
419       "Gateway Timeout",
420       NULL,
421       "The server, while acting as a gateway or proxy, "
422       "did not receive a timely response from the upstream server"
423     },
424     { 505,
425       "HTTP Version Not Supported",
426       NULL,
427       "The server does not support, or refuses to support, the "
428       "HTTP protocol version that was used in the request message."
429     },
430     { 0,
431       NULL,
432       NULL,
433       NULL
434     }
435 };
436
437
438
439 #define STRNULL_FOR_NULL(x) ((x) ? (x) : "(null)")
440 #ifdef USE_CGI_HEADERS
441 #define JK_TOLOWER(x)   ((char)tolower((BYTE)(x)))
442 #endif
443
444 #define GET_SERVER_VARIABLE_VALUE(name, place)          \
445   do {                                                  \
446     (place) = NULL;                                     \
447     huge_buf_sz = MAX_PACKET_SIZE;                      \
448     if (get_server_value(private_data->lpEcb,           \
449                         (name),                         \
450                         huge_buf,                       \
451                         huge_buf_sz)) {                 \
452         (place) = jk_pool_strdup(&private_data->p,      \
453                                  huge_buf);             \
454   } } while(0)
455
456 #define GET_SERVER_VARIABLE_VALUE_INT(name, place, def)     \
457   do {                                                      \
458     huge_buf_sz = MAX_PACKET_SIZE;                          \
459     if (get_server_value(private_data->lpEcb,               \
460                         (name),                             \
461                         huge_buf,                           \
462                         huge_buf_sz)) {                     \
463         (place) = atoi(huge_buf);                           \
464         if (((place) == 0) && (errno == EINVAL ||           \
465                                errno == ERANGE)) {          \
466             (place) = def;                                  \
467         }                                                   \
468     } else {                                                \
469         (place) = def;                                      \
470   } } while(0)
471
472 static char dll_file_path[MAX_PATH];
473 static char ini_file_name[MAX_PATH];
474 static int using_ini_file = JK_FALSE;
475 static JK_CRIT_SEC init_cs;
476 static int is_inited = JK_FALSE;
477 static int is_mapread = JK_FALSE;
478
479 static jk_uri_worker_map_t *uw_map = NULL;
480 static jk_map_t *workers_map = NULL;
481 static jk_map_t *rewrite_map = NULL;
482 static jk_map_t *rregexp_map = NULL;
483 static jk_map_t *jk_environment_map = NULL;
484
485 static jk_logger_t *logger = NULL;
486 static JK_CRIT_SEC log_cs;
487 static char *SERVER_NAME = "SERVER_NAME";
488 static char *SERVER_SOFTWARE = "SERVER_SOFTWARE";
489 static char *INSTANCE_ID = "INSTANCE_ID";
490 static char *CONTENT_TYPE = "Content-Type:text/html\r\n\r\n";
491
492 static char extension_uri[INTERNET_MAX_URL_LENGTH] =
493     "/jakarta/isapi_redirect.dll";
494 static char log_file[MAX_PATH * 2];
495 static char log_file_effective[MAX_PATH * 2];
496 static int  log_level = JK_LOG_DEF_LEVEL;
497 static long log_rotationtime = 0;
498 static time_t log_next_rotate_time = 0;
499 static ULONGLONG log_filesize = 0;
500
501 static char worker_file[MAX_PATH * 2];
502 static char worker_mount_file[MAX_PATH * 2] = {0};
503 static int  worker_mount_reload = JK_URIMAP_DEF_RELOAD;
504 static char rewrite_rule_file[MAX_PATH * 2] = {0};
505 static size_t shm_config_size = 0;
506 static int  strip_session = 0;
507 #ifndef AUTOMATIC_AUTH_NOTIFICATION
508 static int  use_auth_notification_flags = 1;
509 #endif
510 static int  chunked_encoding_enabled = JK_FALSE;
511 static int  reject_unsafe = 0;
512 static int  watchdog_interval = 0;
513 static HANDLE watchdog_handle = NULL;
514 static char error_page_buf[INTERNET_MAX_URL_LENGTH] = {0};
515 static char *error_page = NULL;
516
517 #define URI_SELECT_OPT_PARSED       0
518 #define URI_SELECT_OPT_UNPARSED     1
519 #define URI_SELECT_OPT_ESCAPED      2
520 #define URI_SELECT_OPT_PROXY        3
521
522 static int uri_select_option = URI_SELECT_OPT_PROXY;
523
524 static jk_worker_env_t worker_env;
525
526 typedef struct isapi_private_data_t isapi_private_data_t;
527 struct isapi_private_data_t
528 {
529     jk_pool_t p;
530
531     unsigned int bytes_read_so_far;
532     int chunk_content;          /* Whether we're responding with Transfer-Encoding: chunked content */
533     LPEXTENSION_CONTROL_BLOCK lpEcb;
534 };
535
536 typedef struct isapi_log_data_t isapi_log_data_t;
537 struct isapi_log_data_t {
538     char uri[INTERNET_MAX_URL_LENGTH];
539     char query[INTERNET_MAX_URL_LENGTH];
540     int request_matched;        /* Whether this request (within a multi-request connection)
541                                    was handled and needs the log values adjusted */
542 };
543
544 typedef struct iis_info_t iis_info_t;
545 struct iis_info_t {
546     int major;                  /* The major version */
547     int minor;                  /* The minor version */
548     DWORD filter_notify_event;  /* The primary filter SF_NOTIFY_* event */
549 };
550
551 static iis_info_t iis_info;
552
553 static int JK_METHOD start_response(jk_ws_service_t *s,
554                                     int status,
555                                     const char *reason,
556                                     const char *const *header_names,
557                                     const char *const *header_values,
558                                     unsigned int num_of_headers);
559
560 static int JK_METHOD iis_read(jk_ws_service_t *s,
561                           void *b, unsigned int l, unsigned int *a);
562
563 static int JK_METHOD iis_write(jk_ws_service_t *s, const void *b, unsigned int l);
564
565 static int JK_METHOD iis_done(jk_ws_service_t *s);
566
567 static int init_ws_service(isapi_private_data_t * private_data,
568                            jk_ws_service_t *s, char **worker_name);
569
570 static int init_jk(char *serverName);
571
572
573 static int JK_METHOD iis_log_to_file(jk_logger_t *l, int level,
574                                     int used, char *what);
575
576 static BOOL initialize_extension(void);
577
578 static int read_registry_init_data(void);
579
580 static int get_config_parameter(LPVOID src, const char *tag,
581                                 char *val, DWORD sz);
582
583 static int get_config_bool(LPVOID src, const char *tag, int def);
584
585 static int get_config_int(LPVOID src, const char *tag, int def);
586
587 static int get_registry_config_parameter(HKEY hkey,
588                                          const char *tag, char *b, DWORD sz);
589
590 static int get_registry_config_number(HKEY hkey, const char *tag,
591                                       int *val);
592
593
594 static int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
595                             char *name,
596                             char *buf, DWORD bufsz);
597
598 static int base64_encode_cert_len(int len);
599
600 static int base64_encode_cert(char *encoded,
601                               const char *string, int len);
602
603 static int get_iis_info(iis_info_t *info);
604
605 static int isapi_write_client(isapi_private_data_t *p, const char *buf, unsigned int write_length);
606
607 static char x2c(const char *what)
608 {
609     register char digit;
610
611     digit =
612         ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
613     digit *= 16;
614     digit +=
615         (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
616     return (digit);
617 }
618
619 static int unescape_url(char *url)
620 {
621     register int x, y, badesc, badpath;
622
623     badesc = 0;
624     badpath = 0;
625     for (x = 0, y = 0; url[y]; ++x, ++y) {
626         if (url[y] != '%')
627             url[x] = url[y];
628         else {
629             if (!isxdigit(url[y + 1]) || !isxdigit(url[y + 2])) {
630                 badesc = 1;
631                 url[x] = '%';
632             }
633             else {
634                 url[x] = x2c(&url[y + 1]);
635                 y += 2;
636                 if (url[x] == '/' || url[x] == '\0')
637                     badpath = 1;
638             }
639         }
640     }
641     url[x] = '\0';
642     if (badesc)
643         return BAD_REQUEST;
644     else if (badpath)
645         return BAD_PATH;
646     else
647         return 0;
648 }
649
650 static void getparents(char *name)
651 {
652     int l, w;
653
654     /* Four paseses, as per RFC 1808 */
655     /* a) remove ./ path segments */
656
657     for (l = 0, w = 0; name[l] != '\0';) {
658         if (name[l] == '.' && name[l + 1] == '/'
659             && (l == 0 || name[l - 1] == '/'))
660             l += 2;
661         else
662             name[w++] = name[l++];
663     }
664
665     /* b) remove trailing . path, segment */
666     if (w == 1 && name[0] == '.')
667         w--;
668     else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/')
669         w--;
670     name[w] = '\0';
671
672     /* c) remove all xx/../ segments. (including leading ../ and /../) */
673     l = 0;
674
675     while (name[l] != '\0') {
676         if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' &&
677             (l == 0 || name[l - 1] == '/')) {
678             register int m = l + 3, n;
679
680             l = l - 2;
681             if (l >= 0) {
682                 while (l >= 0 && name[l] != '/')
683                     l--;
684                 l++;
685             }
686             else
687                 l = 0;
688             n = l;
689             while ((name[n] = name[m]) != '\0') {
690                 n++;
691                 m++;
692             }
693         }
694         else
695             ++l;
696     }
697
698     /* d) remove trailing xx/.. segment. */
699     if (l == 2 && name[0] == '.' && name[1] == '.')
700         name[0] = '\0';
701     else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
702              && name[l - 3] == '/') {
703         l = l - 4;
704         if (l >= 0) {
705             while (l >= 0 && name[l] != '/')
706                 l--;
707             l++;
708         }
709         else
710             l = 0;
711         name[l] = '\0';
712     }
713 }
714
715 /* Apache code to escape a URL */
716
717 #define T_OS_ESCAPE_PATH    (4)
718
719 static const BYTE test_char_table[256] = {
720      0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14,
721     14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
722     14,  0,  7,  6,  1,  6,  1,  1,  9,  9,  1,  0,  8,  0,  0, 10,
723      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  8, 15, 15,  8, 15, 15,
724      8,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
725      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 15, 15, 15,  7,  0,
726      7,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
727      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 15,  7, 15,  1, 14,
728      6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
729      6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
730      6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
731      6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
732      6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
733      6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
734      6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
735      6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6
736 };
737
738 #define TEST_CHAR(c, f) (test_char_table[(unsigned int)(c)] & (f))
739
740 static const char c2x_table[] = "0123456789abcdef";
741
742 static BYTE *c2x(unsigned int what, BYTE *where)
743 {
744     *where++ = '%';
745     *where++ = c2x_table[what >> 4];
746     *where++ = c2x_table[what & 0xf];
747     return where;
748 }
749
750 static const char *status_reason(int status)
751 {
752     struct error_reasons *r;
753
754     r = error_reasons;
755     while (r->status <= status) {
756         if (r->status == status)
757             return r->reason;
758         else
759             r++;
760     }
761     return "No Reason";
762 }
763
764 static const char *status_title(int status)
765 {
766     struct error_reasons *r;
767
768     r = error_reasons;
769     while (r->status <= status) {
770         if (r->status == status) {
771             if (r->title)
772                 return r->title;
773             else
774                 return r->reason;
775         }
776         else
777             r++;
778     }
779     return "Unknown Error";
780 }
781
782 static const char *status_description(int status)
783 {
784     struct error_reasons *r;
785
786     r = error_reasons;
787     while (r->status <= status) {
788         if (r->status == status) {
789             if (r->description)
790                 return r->description;
791             else
792                 return r->reason;
793         }
794         else
795             r++;
796     }
797     return "Unknown Error";
798 }
799
800 static int escape_url(const char *path, char *dest, int destsize)
801 {
802     const BYTE *s = (const BYTE *)path;
803     BYTE *d = (BYTE *)dest;
804     BYTE *e = d + destsize - 1;
805     BYTE *ee = d + destsize - 3;
806
807     while (*s) {
808         if (TEST_CHAR(*s, T_OS_ESCAPE_PATH)) {
809             if (d >= ee)
810                 return JK_FALSE;
811             d = c2x(*s, d);
812         }
813         else {
814             if (d >= e)
815                 return JK_FALSE;
816             *d++ = *s;
817         }
818         ++s;
819     }
820     *d = '\0';
821     return JK_TRUE;
822 }
823
824 /*
825  * Find the first occurrence of find in s.
826  */
827 static char *stristr(const char *s, const char *find)
828 {
829     char c, sc;
830     size_t len;
831
832     if ((c = tolower((unsigned char)(*find++))) != 0) {
833         len = strlen(find);
834         do {
835             do {
836                 if ((sc = tolower((unsigned char)(*s++))) == 0)
837                     return (NULL);
838             } while (sc != c);
839         } while (strnicmp(s, find, len) != 0);
840         s--;
841     }
842     return ((char *)s);
843 }
844
845 static int uri_is_web_inf(const char *uri)
846 {
847     if (stristr(uri, "/web-inf")) {
848         return JK_TRUE;
849     }
850     if (stristr(uri, "/meta-inf")) {
851         return JK_TRUE;
852     }
853
854     return JK_FALSE;
855 }
856
857 static void write_error_response(PHTTP_FILTER_CONTEXT pfc, int err)
858 {
859     char status[MAX_PATH];
860     char body[8192] = "";
861     DWORD len;
862
863     /* reject !!! */
864     pfc->AddResponseHeaders(pfc, CONTENT_TYPE, 0);
865     StringCbPrintf(status, MAX_PATH, "%d %s", err, status_reason(err));
866     pfc->ServerSupportFunction(pfc,
867                                SF_REQ_SEND_RESPONSE_HEADER,
868                                status, 0, 0);
869     len = (DWORD)(sizeof(HTML_ERROR_HEAD) - 1);
870     pfc->WriteClient(pfc, HTML_ERROR_HEAD, &len,
871                      HSE_IO_SYNC);
872     StringCbPrintf(body, sizeof(body), HTML_ERROR_BODY_FMT,
873                    status_reason(err), status_title(err),
874                    status_description(err));
875     len = (DWORD)(strlen(body));
876     pfc->WriteClient(pfc, body, &len,
877                      HSE_IO_SYNC);
878     len = (DWORD)(sizeof(HTML_ERROR_TAIL) - 1);
879     pfc->WriteClient(pfc, HTML_ERROR_TAIL, &len,
880                      HSE_IO_SYNC);
881 }
882
883 static void write_error_message(LPEXTENSION_CONTROL_BLOCK lpEcb, int err)
884 {
885     DWORD len;
886     char status[MAX_PATH];
887     char body[8192] = "";
888
889     if (error_page) {
890         char error_page_url[INTERNET_MAX_URL_LENGTH] = "";
891         int len_of_error_page;
892         StringCbPrintf(error_page_url, INTERNET_MAX_URL_LENGTH,
893                        (LPCSTR)error_page, err);
894         len_of_error_page = (int)strlen(error_page_url);
895         if (!lpEcb->ServerSupportFunction(lpEcb->ConnID,
896                                           HSE_REQ_SEND_URL_REDIRECT_RESP,
897                                           error_page_url,
898                                           (LPDWORD)&len_of_error_page,
899                                           (LPDWORD)NULL)) {
900             lpEcb->dwHttpStatusCode = err;
901         }
902         else {
903             return;
904         }
905     }
906     lpEcb->dwHttpStatusCode = err;
907
908     StringCbPrintf(status, MAX_PATH, "%d %s", err, status_reason(err));
909     lpEcb->ServerSupportFunction(lpEcb->ConnID,
910                                  HSE_REQ_SEND_RESPONSE_HEADER,
911                                  status,
912                                  0,
913                                  (LPDWORD)CONTENT_TYPE);
914     /* First write the HEAD */
915     len = (DWORD)(sizeof(HTML_ERROR_HEAD) - 1);
916     lpEcb->WriteClient(lpEcb->ConnID,
917                        HTML_ERROR_HEAD, &len,
918                        HSE_IO_SYNC);
919     StringCbPrintf(body, sizeof(body), HTML_ERROR_BODY_FMT,
920                    status_reason(err), status_title(err),
921                    status_description(err));
922     len = (DWORD)(strlen(body));
923     lpEcb->WriteClient(lpEcb->ConnID,
924                        body, &len,
925                        HSE_IO_SYNC);
926     len = (DWORD)(sizeof(HTML_ERROR_TAIL) - 1);
927     lpEcb->WriteClient(lpEcb->ConnID,
928                        HTML_ERROR_TAIL, &len,
929                        HSE_IO_SYNC);
930
931 }
932
933
934 static int JK_METHOD start_response(jk_ws_service_t *s,
935                                     int status,
936                                     const char *reason,
937                                     const char *const *header_names,
938                                     const char *const *header_values,
939                                     unsigned int num_of_headers)
940 {
941     JK_TRACE_ENTER(logger);
942     if (status < 100 || status > 1000) {
943         jk_log(logger, JK_LOG_ERROR,
944                "invalid status %d",
945                status);
946         JK_TRACE_EXIT(logger);
947         return JK_FALSE;
948     }
949
950     if (s && s->ws_private) {
951         int rv = JK_TRUE;
952         isapi_private_data_t *p = s->ws_private;
953
954         /* If we use proxy error pages, still pass
955          * through context headers needed for special status codes.
956          */
957         if (s->extension.use_server_error_pages &&
958             status >= s->extension.use_server_error_pages) {
959             if (status == JK_HTTP_UNAUTHORIZED) {
960                 int found = JK_FALSE;
961                 unsigned int h;
962                 for (h = 0; h < num_of_headers; h++) {
963                     if (!strcasecmp(header_names[h], "WWW-Authenticate")) {
964                         /*
965                          * TODO: we need to save a copy of header_values[h]
966                          * for later reuse in write_error_message()
967                          * which is called later on form HttpExtensionProc
968                          * because of use_server_error_pages.
969                          */
970                         found = JK_TRUE;
971                     }
972                 }
973                 if (found == JK_FALSE) {
974                     jk_log(logger, JK_LOG_INFO,
975                            "origin server sent 401 without"
976                            " WWW-Authenticate header");
977                 }
978             }
979             return JK_TRUE;
980         }
981
982         if (!s->response_started) {
983             char *status_str = NULL;
984             char *headers_str = NULL;
985             BOOL keep_alive = FALSE;     /* Whether the downstream or us can supply content length */
986             BOOL rc;
987             size_t i, len_of_headers = 0;
988
989             s->response_started = JK_TRUE;
990
991             if (JK_IS_DEBUG_LEVEL(logger)) {
992                 jk_log(logger, JK_LOG_DEBUG, "Starting response for URI '%s' (protocol %s)",
993                        s->req_uri, s->protocol);
994             }
995
996             /*
997              * Create the status line
998              */
999             if (!reason) {
1000                 reason = status_reason(status);
1001             }
1002             status_str = (char *)malloc((6 + strlen(reason)));
1003             StringCbPrintf(status_str, 6 + strlen(reason), "%d %s", status, reason);
1004
1005             if (chunked_encoding_enabled) {
1006                 /* Check if we've got an HTTP/1.1 response */
1007                 if (!strcasecmp(s->protocol, "HTTP/1.1")) {
1008                     keep_alive = TRUE;
1009                     /* Chunking only when HTTP/1.1 client and enabled */
1010                     p->chunk_content = JK_TRUE;
1011                 }
1012             }
1013
1014             /*
1015              * Create response headers string
1016              */
1017
1018             /* Calculate length of headers block */
1019             for (i = 0; i < num_of_headers; i++) {
1020                 len_of_headers += strlen(header_names[i]);
1021                 len_of_headers += strlen(header_values[i]);
1022                 len_of_headers += 4;   /* extra for colon, space and crlf */
1023             }
1024
1025             /*
1026              * Exclude status codes that MUST NOT include message bodies
1027              */
1028             if ((status == 204) || (status == 205) || (status == 304)) {
1029                 p->chunk_content = JK_FALSE;
1030                 /* Keep alive is still possible */
1031                 if (JK_IS_DEBUG_LEVEL(logger))
1032                     jk_log(logger, JK_LOG_DEBUG, "Response status %d implies no message body", status );
1033             }
1034             if (p->chunk_content) {
1035                 for (i = 0; i < num_of_headers; i++) {
1036                     /* Check the downstream response to see whether
1037                      * it's appropriate to chunk the response content
1038                      * and whether it supports keeping the connection open.
1039
1040                      * This implements the rules for HTTP/1.1 message length determination
1041                      * with the exception of multipart/byteranges media types.
1042                      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4
1043                      */
1044                     if (!strcasecmp(CONTENT_LENGTH_HEADER_NAME, header_names[i])) {
1045                         p->chunk_content = JK_FALSE;
1046                         if (JK_IS_DEBUG_LEVEL(logger))
1047                             jk_log(logger, JK_LOG_DEBUG, "Response specifies Content-Length" );
1048                     }
1049                     else if (!strcasecmp(CONNECTION_HEADER_NAME, header_names[i])
1050                             && !strcasecmp(CONNECTION_CLOSE_VALUE, header_values[i])) {
1051                         keep_alive = FALSE;
1052                         p->chunk_content = JK_FALSE;
1053                         if (JK_IS_DEBUG_LEVEL(logger))
1054                             jk_log(logger, JK_LOG_DEBUG, "Response specifies Connection: Close" );
1055                     }
1056                     else if (!strcasecmp(TRANSFER_ENCODING_HEADER_NAME, header_names[i])
1057                             && !strcasecmp(TRANSFER_ENCODING_IDENTITY_VALUE, header_values[i])) {
1058                         /* HTTP states that this must include 'chunked' as the last value.
1059                             * 'identity' is the same as absence of the header */
1060                         p->chunk_content = JK_FALSE;
1061                         if (JK_IS_DEBUG_LEVEL(logger))
1062                             jk_log(logger, JK_LOG_DEBUG, "Response specifies Transfer-Encoding" );
1063                     }
1064                 }
1065
1066                 /* Provide room in the buffer for the Transfer-Encoding header if we use it. */
1067                 len_of_headers += TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE_LEN + 2;
1068             }
1069
1070             /* Allocate and init the headers string */
1071             len_of_headers += 3;       /* crlf and terminating null char */
1072             headers_str = (char *)malloc(len_of_headers);
1073             headers_str[0] = '\0';
1074
1075             /* Copy headers into headers block for sending */
1076             for (i = 0; i < num_of_headers; i++) {
1077                 StringCbCat(headers_str, len_of_headers, header_names[i]);
1078                 StringCbCat(headers_str, len_of_headers, ": ");
1079                 StringCbCat(headers_str, len_of_headers, header_values[i]);
1080                 StringCbCat(headers_str, len_of_headers, CRLF);
1081             }
1082
1083             if (p->chunk_content) {
1084                 /* Configure the response if chunked encoding is used */
1085                 if (JK_IS_DEBUG_LEVEL(logger))
1086                     jk_log(logger, JK_LOG_DEBUG, "Using Transfer-Encoding: chunked");
1087
1088                 /** We will supply the transfer-encoding to allow IIS to keep the connection open */
1089                 keep_alive = TRUE;
1090
1091                 /* Indicate to the client that the content will be chunked
1092                 - We've already reserved space for this */
1093                 StringCbCat(headers_str, len_of_headers, TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE);
1094                 StringCbCat(headers_str, len_of_headers, CRLF);
1095             }
1096
1097             /* Terminate the headers */
1098             StringCbCat(headers_str, len_of_headers, CRLF);
1099
1100             if (JK_IS_DEBUG_LEVEL(logger))
1101                 jk_log(logger, JK_LOG_DEBUG, "%ssing Keep-Alive", (keep_alive ? "U" : "Not u"));
1102
1103             if (keep_alive) {
1104                 HSE_SEND_HEADER_EX_INFO hi;
1105
1106                 /* Fill in the response */
1107                 hi.pszStatus = status_str;
1108                 hi.pszHeader = headers_str;
1109                 hi.cchStatus = (DWORD)strlen(status_str);
1110                 hi.cchHeader = (DWORD)strlen(headers_str);
1111
1112                 /*
1113                  * Using the extended form of the API means we have to get this right,
1114                  * i.e. IIS won't keep connections open if there's a Content-Length and close them if there isn't.
1115                  */
1116                 hi.fKeepConn = keep_alive;
1117
1118                 /* Send the response to the client */
1119                 rc = p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
1120                                                       HSE_REQ_SEND_RESPONSE_HEADER_EX,
1121                                                       &hi,
1122                                                       NULL, NULL);
1123             }
1124             else {
1125                 DWORD status_str_len = (DWORD)strlen(status_str);
1126                 /* Old style response - forces Connection: close if Tomcat response doesn't
1127                    specify necessary details to allow keep alive */
1128                 rc = p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
1129                                                       HSE_REQ_SEND_RESPONSE_HEADER,
1130                                                       status_str,
1131                                                       &status_str_len,
1132                                                       (LPDWORD)headers_str);
1133             }
1134
1135             if (!rc) {
1136                 jk_log(logger, JK_LOG_ERROR,
1137                        "HSE_REQ_SEND_RESPONSE_HEADER%s failed with error=%d (0x%08x)",
1138                        (keep_alive ? "_EX" : ""), GetLastError(), GetLastError());
1139                 rv = JK_FALSE;
1140             }
1141             if (headers_str)
1142                 free(headers_str);
1143             if (status_str)
1144                 free(status_str);
1145         }
1146         JK_TRACE_EXIT(logger);
1147         return rv;
1148     }
1149
1150     JK_LOG_NULL_PARAMS(logger);
1151     JK_TRACE_EXIT(logger);
1152     return JK_FALSE;
1153 }
1154
1155 static int JK_METHOD iis_read(jk_ws_service_t *s,
1156                           void *b, unsigned int l, unsigned int *a)
1157 {
1158     JK_TRACE_ENTER(logger);
1159
1160     if (s && s->ws_private && b && a) {
1161         isapi_private_data_t *p = s->ws_private;
1162
1163         if (JK_IS_DEBUG_LEVEL(logger)) {
1164             jk_log(logger, JK_LOG_DEBUG,
1165                    "Preparing to read %d bytes. "
1166                    "ECB reports %d bytes total, with %d available.",
1167                    l, p->lpEcb->cbTotalBytes, p->lpEcb->cbAvailable);
1168         }
1169
1170         *a = 0;
1171         if (l) {
1172             char *buf = b;
1173             DWORD already_read = p->lpEcb->cbAvailable - p->bytes_read_so_far;
1174
1175             if (already_read >= l) {
1176                 if (JK_IS_DEBUG_LEVEL(logger)) {
1177                     jk_log(logger, JK_LOG_DEBUG,
1178                            "Already read %d bytes - supplying %d bytes from buffer",
1179                            already_read, l);
1180                 }
1181                 memcpy(buf, p->lpEcb->lpbData + p->bytes_read_so_far, l);
1182                 p->bytes_read_so_far += l;
1183                 *a = l;
1184             }
1185             else {
1186                 /*
1187                  * Try to copy what we already have
1188                  */
1189                 if (already_read > 0) {
1190                     if (JK_IS_DEBUG_LEVEL(logger)) {
1191                         jk_log(logger, JK_LOG_DEBUG,
1192                                "Supplying %d bytes from buffer",
1193                                already_read);
1194                     }
1195                     memcpy(buf, p->lpEcb->lpbData + p->bytes_read_so_far,
1196                            already_read);
1197                     buf += already_read;
1198                     l -= already_read;
1199                     p->bytes_read_so_far = p->lpEcb->cbAvailable;
1200
1201                     *a = already_read;
1202                 }
1203
1204                 /*
1205                  * Now try to read from the client ...
1206                  */
1207                 if (JK_IS_DEBUG_LEVEL(logger)) {
1208                     jk_log(logger, JK_LOG_DEBUG,
1209                            "Attempting to read %d bytes from client", l);
1210                 }
1211                 if (p->lpEcb->ReadClient(p->lpEcb->ConnID, buf, (LPDWORD)&l)) {
1212                     /* ReadClient will succeed with dwSize == 0 for last chunk 
1213                        if request chunk encoded */
1214                     *a += l;
1215                 }
1216                 else {
1217                     jk_log(logger, JK_LOG_ERROR,
1218                            "ReadClient failed with %d (0x%08x)", GetLastError(), GetLastError());
1219                     JK_TRACE_EXIT(logger);
1220                     return JK_FALSE;
1221                 }
1222             }
1223         }
1224         JK_TRACE_EXIT(logger);
1225         return JK_TRUE;
1226     }
1227
1228     JK_LOG_NULL_PARAMS(logger);
1229     JK_TRACE_EXIT(logger);
1230     return JK_FALSE;
1231 }
1232
1233 /*
1234  * Writes a buffer to the ISAPI response.
1235  */
1236 static int isapi_write_client(isapi_private_data_t *p, const char *buf, unsigned int write_length)
1237 {
1238     unsigned int written = 0;
1239     DWORD try_to_write = 0;
1240
1241     JK_TRACE_ENTER(logger);
1242
1243     if (JK_IS_DEBUG_LEVEL(logger))
1244         jk_log(logger, JK_LOG_DEBUG, "Writing %d bytes of data to client", write_length);
1245
1246     while (written < write_length) {
1247         try_to_write = write_length - written;
1248         if (!p->lpEcb->WriteClient(p->lpEcb->ConnID,
1249                                    (LPVOID)(buf + written), &try_to_write, HSE_IO_SYNC)) {
1250             jk_log(logger, JK_LOG_ERROR,
1251                    "WriteClient failed with %d (0x%08x)", GetLastError(), GetLastError());
1252             JK_TRACE_EXIT(logger);
1253             return JK_FALSE;
1254         }
1255         written += try_to_write;
1256         if (JK_IS_DEBUG_LEVEL(logger))
1257             jk_log(logger, JK_LOG_DEBUG, "Wrote %d bytes of data successfully", try_to_write);
1258     }
1259     JK_TRACE_EXIT(logger);
1260     return JK_TRUE;
1261 }
1262
1263 /*
1264  * Write content to the response.
1265  * If chunked encoding has been enabled and the client supports it
1266  *(and it's appropriate for the response), then this will write a
1267  * single "Transfer-Encoding: chunked" chunk
1268  */
1269 static int JK_METHOD iis_write(jk_ws_service_t *s, const void *b, unsigned int l)
1270 {
1271     JK_TRACE_ENTER(logger);
1272
1273     if (!l) {
1274         JK_TRACE_EXIT(logger);
1275         return JK_TRUE;
1276     }
1277
1278     if (s && s->ws_private && b) {
1279         isapi_private_data_t *p = s->ws_private;
1280         const char *buf = (const char *)b;
1281
1282         if (!p) {
1283             JK_TRACE_EXIT(logger);
1284             return JK_FALSE;
1285         }
1286
1287         if (!s->response_started) {
1288             start_response(s, 200, NULL, NULL, NULL, 0);
1289         }
1290
1291         if (p->chunk_content == JK_FALSE) {
1292             if (isapi_write_client(p, buf, l) == JK_FALSE) {
1293                 JK_TRACE_EXIT(logger);
1294                 return JK_FALSE;
1295             }
1296         }
1297         else {
1298             char chunk_header[CHUNK_HEADER_BUFFER_SIZE];
1299
1300             /* Construct chunk header : HEX CRLF*/
1301             StringCbPrintf(chunk_header, CHUNK_HEADER_BUFFER_SIZE, "%X%s", l, CRLF);
1302
1303             if (iis_info.major >= 6) {
1304                 HSE_RESPONSE_VECTOR response_vector;
1305                 HSE_VECTOR_ELEMENT response_elements[3];
1306
1307                 response_elements[0].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER;
1308                 response_elements[0].pvContext = chunk_header;
1309                 response_elements[0].cbOffset = 0;
1310                 response_elements[0].cbSize = strlen(chunk_header);
1311
1312                 response_elements[1].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER;
1313                 response_elements[1].pvContext = (PVOID)buf;
1314                 response_elements[1].cbOffset = 0;
1315                 response_elements[1].cbSize = l;
1316
1317                 response_elements[2].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER;
1318                 response_elements[2].pvContext = CRLF;
1319                 response_elements[2].cbOffset = 0;
1320                 response_elements[2].cbSize = CRLF_LEN;
1321
1322                 response_vector.dwFlags = HSE_IO_SYNC;
1323                 response_vector.pszStatus = NULL;
1324                 response_vector.pszHeaders = NULL;
1325                 response_vector.nElementCount = 3;
1326                 response_vector.lpElementArray = response_elements;
1327
1328                 if (JK_IS_DEBUG_LEVEL(logger))
1329                     jk_log(logger, JK_LOG_DEBUG,
1330                            "Using vector write for chunk encoded %d byte chunk", l);
1331
1332                 if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
1333                     HSE_REQ_VECTOR_SEND,
1334                     &response_vector,
1335                     (LPDWORD)NULL,
1336                     (LPDWORD)NULL)) {
1337                         jk_log(logger, JK_LOG_ERROR,
1338                                "Vector write of chunk encoded response failed with %d (0x%08x)",
1339                                GetLastError(), GetLastError());
1340                         JK_TRACE_EXIT(logger);
1341                         return JK_FALSE;
1342                 }
1343             } else {
1344                 /* Write chunk header */
1345                 if (JK_IS_DEBUG_LEVEL(logger))
1346                     jk_log(logger, JK_LOG_DEBUG,
1347                     "Using chunked encoding - writing chunk header for %d byte chunk", l);
1348
1349                 if (!isapi_write_client(p, chunk_header, (unsigned int)strlen(chunk_header))) {
1350                     jk_log(logger, JK_LOG_ERROR, "WriteClient for chunk header failed");
1351                     JK_TRACE_EXIT(logger);
1352                     return JK_FALSE;
1353                 }
1354
1355                 /* Write chunk body (or simple body block) */
1356                 if (JK_IS_DEBUG_LEVEL(logger)) {
1357                     jk_log(logger, JK_LOG_DEBUG, "Writing %s of size %d",
1358                            (p->chunk_content ? "chunk body" : "simple response"), l);
1359                 }
1360                 if (!isapi_write_client(p, buf, l)) {
1361                     jk_log(logger, JK_LOG_ERROR, "WriteClient for response body chunk failed");
1362                     JK_TRACE_EXIT(logger);
1363                     return JK_FALSE;
1364                 }
1365                 /* Write chunk trailer */
1366                 if (JK_IS_DEBUG_LEVEL(logger)) {
1367                     jk_log(logger, JK_LOG_DEBUG, "Using chunked encoding - writing chunk trailer");
1368                 }
1369
1370                 if (!isapi_write_client(p, CRLF, CRLF_LEN)) {
1371                     jk_log(logger, JK_LOG_ERROR, "WriteClient for chunk trailer failed");
1372                     JK_TRACE_EXIT(logger);
1373                     return JK_FALSE;
1374                 }
1375             }
1376         }
1377
1378         JK_TRACE_EXIT(logger);
1379         return JK_TRUE;
1380
1381     }
1382
1383     JK_LOG_NULL_PARAMS(logger);
1384     JK_TRACE_EXIT(logger);
1385     return JK_FALSE;
1386 }
1387
1388 /**
1389  * In the case of a Transfer-Encoding: chunked response, this will write the terminator chunk.
1390  */
1391 static int JK_METHOD iis_done(jk_ws_service_t *s)
1392 {
1393     JK_TRACE_ENTER(logger);
1394
1395     if (s && s->ws_private) {
1396         isapi_private_data_t *p = s->ws_private;
1397
1398         if (p->chunk_content == JK_FALSE) {
1399             JK_TRACE_EXIT(logger);
1400             return JK_TRUE;
1401         }
1402
1403         /* Write last chunk + terminator */
1404         if (iis_info.major >= 6) {
1405             HSE_RESPONSE_VECTOR response_vector;
1406             HSE_VECTOR_ELEMENT response_elements[1];
1407
1408             response_elements[0].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER;
1409             response_elements[0].pvContext = CHUNKED_ENCODING_TRAILER;
1410             response_elements[0].cbOffset = 0;
1411             response_elements[0].cbSize = CHUNKED_ENCODING_TRAILER_LEN;
1412
1413             /* HSE_IO_FINAL_SEND lets IIS process the response to the client before we return */
1414             response_vector.dwFlags = HSE_IO_SYNC | HSE_IO_FINAL_SEND;
1415             response_vector.pszStatus = NULL;
1416             response_vector.pszHeaders = NULL;
1417             response_vector.nElementCount = 1;
1418             response_vector.lpElementArray = response_elements;
1419
1420             if (JK_IS_DEBUG_LEVEL(logger))
1421                 jk_log(logger, JK_LOG_DEBUG,
1422                        "Using vector write to terminate chunk encoded response.");
1423
1424             if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
1425                                                  HSE_REQ_VECTOR_SEND,
1426                                                  &response_vector,
1427                                                  (LPDWORD)NULL,
1428                                                  (LPDWORD)NULL)) {
1429                     jk_log(logger, JK_LOG_ERROR,
1430                            "Vector termination of chunk encoded response failed with %d (0x%08x)",
1431                            GetLastError(), GetLastError());
1432                     JK_TRACE_EXIT(logger);
1433                     return JK_FALSE;
1434             }
1435         }
1436         else {
1437             if (JK_IS_DEBUG_LEVEL(logger))
1438                 jk_log(logger, JK_LOG_DEBUG, "Terminating chunk encoded response");
1439
1440             if (!isapi_write_client(p, CHUNKED_ENCODING_TRAILER, CHUNKED_ENCODING_TRAILER_LEN)) {
1441                 jk_log(logger, JK_LOG_ERROR,
1442                        "WriteClient for chunked response terminator failed with %d (0x%08x)",
1443                        GetLastError(), GetLastError());
1444                 JK_TRACE_EXIT(logger);
1445                 return JK_FALSE;
1446             }
1447         }
1448
1449         JK_TRACE_EXIT(logger);
1450         return JK_TRUE;
1451     }
1452
1453     JK_LOG_NULL_PARAMS(logger);
1454     JK_TRACE_EXIT(logger);
1455     return JK_FALSE;
1456 }
1457
1458 BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
1459 {
1460     int rc;
1461     BOOL rv = TRUE;
1462     ULONG http_filter_revision = HTTP_FILTER_REVISION;
1463
1464     pVer->dwFilterVersion = pVer->dwServerFilterVersion;
1465
1466     if (pVer->dwFilterVersion > http_filter_revision) {
1467         pVer->dwFilterVersion = http_filter_revision;
1468     }
1469     JK_ENTER_CS(&(init_cs), rc);
1470     if (!is_inited) {
1471         rv = initialize_extension();
1472     }
1473     JK_LEAVE_CS(&(init_cs), rc);
1474     pVer->dwFlags = SF_NOTIFY_ORDER_HIGH |
1475                     SF_NOTIFY_SECURE_PORT |
1476                     SF_NOTIFY_NONSECURE_PORT |
1477                     SF_NOTIFY_LOG |
1478                     iis_info.filter_notify_event;
1479
1480     StringCbCopy(pVer->lpszFilterDesc, SF_MAX_FILTER_DESC_LEN, (VERSION_STRING));
1481     return rv;
1482 }
1483
1484
1485 #define AP_REG_ICASE    0x01 /** use a case-insensitive match */
1486 #define AP_REG_NEWLINE  0x02 /** don't match newlines against '.' etc */
1487 #define AP_REG_NOTBOL   0x04 /** ^ will not match against start-of-string */
1488 #define AP_REG_NOTEOL   0x08 /** $ will not match against end-of-string */
1489
1490 #define AP_REG_EXTENDED (0)  /** unused */
1491 #define AP_REG_NOSUB    (0)  /** unused */
1492 /** The max number of regex captures that can be expanded by ap_pregsub */
1493 #define AP_MAX_REG_MATCH 10
1494
1495 /* Error values: */
1496 enum {
1497     AP_REG_ASSERT = 1,  /** internal error ? */
1498     AP_REG_ESPACE,      /** failed to get memory */
1499     AP_REG_INVARG,      /** invalid argument */
1500     AP_REG_NOMATCH      /** match failed */
1501 };
1502
1503 /* The structure representing a compiled regular expression. */
1504 typedef struct {
1505     void *re_pcre;
1506     size_t re_nsub;
1507     size_t re_erroffset;
1508     const char *real;
1509     const char *fake;
1510 } ap_regex_t;
1511
1512 /* The structure in which a captured offset is returned. */
1513 typedef struct {
1514     int rm_so;
1515     int rm_eo;
1516 } ap_regmatch_t;
1517
1518
1519 /* Table of error strings corresponding to POSIX error codes; must be
1520  * kept in synch with include/ap_regex.h's AP_REG_E* definitions. */
1521
1522 static const char *const pstring[] = {
1523   "",                                /* Dummy for value 0 */
1524   "internal error",                  /* AP_REG_ASSERT */
1525   "failed to get memory",            /* AP_REG_ESPACE */
1526   "bad argument",                    /* AP_REG_INVARG */
1527   "match failed"                     /* AP_REG_NOMATCH */
1528 };
1529
1530 static size_t ap_regerror(int errcode, const ap_regex_t *preg,
1531                           char *errbuf, size_t errbuf_size)
1532 {
1533     const char *message, *addmessage;
1534     size_t length, addlength;
1535
1536     message = (errcode >= (int)(sizeof(pstring)/sizeof(char *))) ?
1537                                 "unknown error code" : pstring[errcode];
1538     length = strlen(message) + 1;
1539
1540     addmessage = " at offset ";
1541     addlength = (preg != NULL && (int)preg->re_erroffset != -1)?
1542                                         strlen(addmessage) + 6 : 0;
1543
1544     if (errbuf_size > 0) {
1545         if (addlength > 0 && errbuf_size >= length + addlength)
1546             StringCbPrintf(errbuf, sizeof(errbuf), "%s%s%-6d",
1547                           message, addmessage,
1548                           (int)preg->re_erroffset);
1549         else {
1550             strncpy(errbuf, message, errbuf_size - 1);
1551             errbuf[errbuf_size-1] = 0;
1552         }
1553     }
1554
1555     return length + addlength;
1556 }
1557
1558 /*************************************************
1559  *           Free store held by a regex          *
1560  *************************************************/
1561
1562 static void ap_regfree(ap_regex_t *preg)
1563 {
1564     (pcre_free)(preg->re_pcre);
1565 }
1566
1567
1568
1569
1570 /*************************************************
1571  *            Compile a regular expression       *
1572  *************************************************/
1573
1574 /*
1575 Arguments:
1576   preg        points to a structure for recording the compiled expression
1577   pattern     the pattern to compile
1578   cflags      compilation flags
1579
1580 Returns:      0 on success
1581               various non-zero codes on failure
1582 */
1583
1584 static int ap_regcomp(ap_regex_t *preg, const char *pattern, int cflags)
1585 {
1586     const char *errorptr;
1587     int erroffset;
1588     int options = 0;
1589
1590     if ((cflags & AP_REG_ICASE) != 0) options |= PCRE_CASELESS;
1591     if ((cflags & AP_REG_NEWLINE) != 0) options |= PCRE_MULTILINE;
1592
1593     preg->re_pcre = pcre_compile(pattern, options, &errorptr, &erroffset, NULL);
1594     preg->re_erroffset = erroffset;
1595
1596     if (preg->re_pcre == NULL) return AP_REG_INVARG;
1597
1598     preg->re_nsub = pcre_info((const pcre *)preg->re_pcre, NULL, NULL);
1599     return 0;
1600 }
1601
1602 /*************************************************
1603  *              Match a regular expression       *
1604  *************************************************/
1605
1606 /* Unfortunately, PCRE requires 3 ints of working space for each captured
1607 substring, so we have to get and release working store instead of just using
1608 the POSIX structures as was done in earlier releases when PCRE needed only 2
1609 ints. However, if the number of possible capturing brackets is small, use a
1610 block of store on the stack, to reduce the use of malloc/free. The threshold is
1611 in a macro that can be changed at configure time. */
1612
1613 static int ap_regexec(const ap_regex_t *preg, const char *string,
1614                       int nmatch, ap_regmatch_t pmatch[],
1615                       int eflags)
1616 {
1617     int rc;
1618     int options = 0;
1619     int *ovector = NULL;
1620     int small_ovector[POSIX_MALLOC_THRESHOLD * 3];
1621     int allocated_ovector = 0;
1622
1623     if ((eflags & AP_REG_NOTBOL) != 0) options |= PCRE_NOTBOL;
1624     if ((eflags & AP_REG_NOTEOL) != 0) options |= PCRE_NOTEOL;
1625
1626     ((ap_regex_t *)preg)->re_erroffset = (size_t)(-1);  /* Only has meaning after compile */
1627
1628     if (nmatch > 0) {
1629         if (nmatch <= POSIX_MALLOC_THRESHOLD) {
1630             ovector = &(small_ovector[0]);
1631         }
1632         else {
1633             ovector = (int *)malloc(sizeof(int) * nmatch * 3);
1634             if (ovector == NULL)
1635                 return AP_REG_ESPACE;
1636             allocated_ovector = 1;
1637         }
1638     }
1639
1640     rc = pcre_exec((const pcre *)preg->re_pcre, NULL, string,
1641                    (int)strlen(string),
1642                     0, options, ovector, nmatch * 3);
1643
1644     if (rc == 0)
1645         rc = nmatch;    /* All captured slots were filled in */
1646     if (rc >= 0) {
1647         int i;
1648         for (i = 0; i < rc; i++) {
1649             pmatch[i].rm_so = ovector[i*2];
1650             pmatch[i].rm_eo = ovector[i*2+1];
1651         }
1652         if (allocated_ovector)
1653             free(ovector);
1654         for (; i < nmatch; i++)
1655             pmatch[i].rm_so = pmatch[i].rm_eo = -1;
1656         return 0;
1657     }
1658     else {
1659         if (allocated_ovector)
1660             free(ovector);
1661         switch(rc) {
1662             case PCRE_ERROR_NOMATCH: return AP_REG_NOMATCH;
1663             case PCRE_ERROR_NULL: return AP_REG_INVARG;
1664             case PCRE_ERROR_BADOPTION: return AP_REG_INVARG;
1665             case PCRE_ERROR_BADMAGIC: return AP_REG_INVARG;
1666             case PCRE_ERROR_UNKNOWN_NODE: return AP_REG_ASSERT;
1667             case PCRE_ERROR_NOMEMORY: return AP_REG_ESPACE;
1668 #ifdef PCRE_ERROR_MATCHLIMIT
1669             case PCRE_ERROR_MATCHLIMIT: return AP_REG_ESPACE;
1670 #endif
1671 #ifdef PCRE_ERROR_BADUTF8
1672             case PCRE_ERROR_BADUTF8: return AP_REG_INVARG;
1673 #endif
1674 #ifdef PCRE_ERROR_BADUTF8_OFFSET
1675             case PCRE_ERROR_BADUTF8_OFFSET: return AP_REG_INVARG;
1676 #endif
1677             default: return AP_REG_ASSERT;
1678         }
1679     }
1680 }
1681
1682 /* This function substitutes for $0-$9, filling in regular expression
1683  * submatches. Pass it the same nmatch and pmatch arguments that you
1684  * passed ap_regexec(). pmatch should not be greater than the maximum number
1685  * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t.
1686  *
1687  * input should be the string with the $-expressions, source should be the
1688  * string that was matched against.
1689  *
1690  * It returns the substituted string, or NULL on error.
1691  *
1692  * Parts of this code are based on Henry Spencer's regsub(), from his
1693  * AT&T V8 regexp package.
1694  */
1695
1696 static char *ap_pregsub(const char *input,
1697                         const char *source, size_t nmatch,
1698                         ap_regmatch_t pmatch[])
1699 {
1700     const char *src = input;
1701     char *dest, *dst;
1702     char c;
1703     size_t no;
1704     int len;
1705
1706     if (!source)
1707         return NULL;
1708     if (!nmatch)
1709         return strdup(src);
1710
1711     /* First pass, find the size */
1712     len = 0;
1713
1714     while ((c = *src++) != '\0') {
1715         if (c == '&')
1716             no = 0;
1717         else if (c == '$' && isdigit((unsigned char)*src))
1718             no = *src++ - '0';
1719         else
1720             no = 10;
1721
1722         if (no > 9) {                /* Ordinary character. */
1723             if (c == '\\' && (*src == '$' || *src == '&'))
1724                 c = *src++;
1725             len++;
1726         }
1727         else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
1728             len += pmatch[no].rm_eo - pmatch[no].rm_so;
1729         }
1730
1731     }
1732
1733     dest = dst = calloc(1, len + 1);
1734
1735     /* Now actually fill in the string */
1736
1737     src = input;
1738
1739     while ((c = *src++) != '\0') {
1740         if (c == '&')
1741             no = 0;
1742         else if (c == '$' && isdigit((unsigned char)*src))
1743             no = *src++ - '0';
1744         else
1745             no = 10;
1746
1747         if (no > 9) {                /* Ordinary character. */
1748             if (c == '\\' && (*src == '$' || *src == '&'))
1749                 c = *src++;
1750             *dst++ = c;
1751         }
1752         else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
1753             len = pmatch[no].rm_eo - pmatch[no].rm_so;
1754             memcpy(dst, source + pmatch[no].rm_so, len);
1755             dst += len;
1756         }
1757
1758     }
1759     *dst = '\0';
1760     return dest;
1761 }
1762
1763 static int simple_rewrite(char *uri)
1764 {
1765     if (rewrite_map) {
1766         int i;
1767         char buf[INTERNET_MAX_URL_LENGTH];
1768         for (i = 0; i < jk_map_size(rewrite_map); i++) {
1769             const char *src = jk_map_name_at(rewrite_map, i);
1770             if (*src == '~')
1771                 continue;   /* Skip regexp rewrites */
1772             if (strncmp(uri, src, strlen(src)) == 0) {
1773                 StringCbCopy(buf, INTERNET_MAX_URL_LENGTH, jk_map_value_at(rewrite_map, i));
1774                 StringCbCat(buf,  INTERNET_MAX_URL_LENGTH, uri + strlen(src));
1775                 StringCbCopy(uri, INTERNET_MAX_URL_LENGTH, buf);
1776                 return 1;
1777             }
1778         }
1779     }
1780     return 0;
1781 }
1782
1783 static int rregex_rewrite(char *uri)
1784 {
1785     ap_regmatch_t regm[AP_MAX_REG_MATCH];
1786
1787     if (rregexp_map) {
1788         int i;
1789         for (i = 0; i < jk_map_size(rregexp_map); i++) {
1790             ap_regex_t *regexp = (ap_regex_t *)jk_map_value_at(rregexp_map, i);
1791             if (!ap_regexec(regexp, uri, AP_MAX_REG_MATCH, regm, 0)) {
1792                 char *subs = ap_pregsub(regexp->fake, uri,
1793                                        AP_MAX_REG_MATCH, regm);
1794                 if (subs) {
1795                     char buf[INTERNET_MAX_URL_LENGTH];
1796                     size_t diffsz = strlen(subs) - (regm[0].rm_eo - regm[0].rm_so);
1797                     memcpy(&buf[0], uri, regm[0].rm_so);
1798                     StringCbCopy(&buf[regm[0].rm_so], INTERNET_MAX_URL_LENGTH - regm[0].rm_so, subs);
1799                     StringCbCat(&buf[0], INTERNET_MAX_URL_LENGTH, uri + regm[0].rm_eo);
1800                     StringCbCopy(uri, INTERNET_MAX_URL_LENGTH, &buf[0]);
1801                     free(subs);
1802                     return 1;
1803                 }
1804             }
1805         }
1806     }
1807     return 0;
1808 }
1809
1810 DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,
1811                             DWORD dwNotificationType, LPVOID pvNotification)
1812 {
1813     int rc;
1814
1815     /* Initialise jk */
1816     if (is_inited && !is_mapread) {
1817         char serverName[MAX_SERVERNAME] = "";
1818         char instanceId[MAX_INSTANCEID] = "";
1819         DWORD dwLen = MAX_SERVERNAME - MAX_INSTANCEID - 1;
1820
1821         if (pfc->GetServerVariable(pfc, SERVER_NAME, serverName, &dwLen)) {
1822             if (dwLen > 0) {
1823                 serverName[dwLen - 1] = '\0';
1824                 dwLen = MAX_INSTANCEID;
1825                 if (pfc->GetServerVariable(pfc, INSTANCE_ID, instanceId, &dwLen)) {
1826                     if (dwLen > 0) {
1827                         instanceId[dwLen - 1] = '\0';
1828                         StringCbCat(serverName, MAX_SERVERNAME, "_");
1829                         StringCbCat(serverName, MAX_SERVERNAME, instanceId);
1830                     }
1831                 }
1832             }
1833             JK_ENTER_CS(&(init_cs), rc);
1834             if (!is_mapread && init_jk(serverName))
1835                 is_mapread = JK_TRUE;
1836             JK_LEAVE_CS(&(init_cs), rc);
1837         }
1838         /* If we can't read the map we become dormant */
1839         if (!is_mapread)
1840             is_inited = JK_FALSE;
1841     }
1842     if (!is_inited) {
1843         /* In case the initialization failed
1844          * return error. This will make entire IIS
1845          * unusable like with Apache servers
1846          */
1847          SetLastError(ERROR_INVALID_FUNCTION);
1848          return SF_STATUS_REQ_ERROR;
1849     }
1850     if (iis_info.filter_notify_event == dwNotificationType) {
1851         char uri[INTERNET_MAX_URL_LENGTH];
1852         char snuri[INTERNET_MAX_URL_LENGTH] = "/";
1853         char Host[INTERNET_MAX_URL_LENGTH] = "";
1854         char Port[INTERNET_MAX_URL_LENGTH] = "";
1855         char Translate[INTERNET_MAX_URL_LENGTH];
1856         char squery[INTERNET_MAX_URL_LENGTH] = "";
1857         char swindex[MAX_INSTANCEID] = "";
1858         BOOL(WINAPI * GetHeader)
1859             (struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
1860              LPVOID lpvBuffer, LPDWORD lpdwSize);
1861         BOOL(WINAPI * SetHeader)
1862             (struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
1863              LPSTR lpszValue);
1864         BOOL(WINAPI * AddHeader)
1865             (struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
1866              LPSTR lpszValue);
1867         char *query;
1868         DWORD sz = sizeof(uri);
1869         DWORD szHost = sizeof(Host);
1870         DWORD szPort = sizeof(Port);
1871         DWORD szTranslate = sizeof(Translate);
1872
1873         if (iis_info.filter_notify_event == SF_NOTIFY_AUTH_COMPLETE) {
1874             GetHeader =
1875                 ((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->GetHeader;
1876             SetHeader =
1877                 ((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->SetHeader;
1878             AddHeader =
1879                 ((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->AddHeader;
1880         }
1881         else {
1882             GetHeader =
1883                 ((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->GetHeader;
1884             SetHeader =
1885                 ((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->SetHeader;
1886             AddHeader =
1887                 ((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->AddHeader;
1888         }
1889
1890         if (JK_IS_DEBUG_LEVEL(logger))
1891             jk_log(logger, JK_LOG_DEBUG, "Filter started");
1892
1893         /*
1894          * Just in case somebody set these headers in the request!
1895          */
1896         SetHeader(pfc, URI_HEADER_NAME, NULL);
1897         SetHeader(pfc, QUERY_HEADER_NAME, NULL);
1898         SetHeader(pfc, WORKER_HEADER_NAME, NULL);
1899         SetHeader(pfc, WORKER_HEADER_INDEX, NULL);
1900         SetHeader(pfc, TOMCAT_TRANSLATE_HEADER_NAME, NULL);
1901
1902         // Suppress logging of original uri/query when we don't map a URL
1903         if (pfc->pFilterContext) {
1904             isapi_log_data_t *ld = (isapi_log_data_t *)pfc->pFilterContext;
1905             ld->request_matched = JK_FALSE;
1906         }
1907
1908         if (!GetHeader(pfc, "url", (LPVOID) uri, (LPDWORD) & sz)) {
1909             jk_log(logger, JK_LOG_ERROR,
1910                    "error while getting the url");
1911             return SF_STATUS_REQ_ERROR;
1912         }
1913
1914         if (strlen(uri)) {
1915             int rc;
1916             const char *worker = NULL;
1917             rule_extension_t *extensions;
1918             int worker_index = -1;
1919             query = strchr(uri, '?');
1920             if (query) {
1921                 *query++ = '\0';
1922                 StringCbCopy(squery, INTERNET_MAX_URL_LENGTH, query);
1923             }
1924
1925             rc = unescape_url(uri);
1926             if (rc == BAD_REQUEST) {
1927                 jk_log(logger, JK_LOG_ERROR,
1928                        "[%s] contains one or more invalid escape sequences.",
1929                        uri);
1930                 write_error_response(pfc, 400);
1931                 return SF_STATUS_REQ_FINISHED;
1932             }
1933             else if (rc == BAD_PATH) {
1934                 jk_log(logger, JK_LOG_EMERG,
1935                        "[%s] contains forbidden escape sequences.",
1936                        uri);
1937                 write_error_response(pfc, 404);
1938                 return SF_STATUS_REQ_FINISHED;
1939             }
1940             getparents(uri);
1941             if (pfc->
1942                 GetServerVariable(pfc, SERVER_NAME, (LPVOID) Host,
1943                                   (LPDWORD) & szHost)) {
1944                 if (szHost > 0) {
1945                     Host[szHost - 1] = '\0';
1946                 }
1947             }
1948             Port[0] = '\0';
1949             if (pfc->
1950                 GetServerVariable(pfc, "SERVER_PORT", (LPVOID) Port,
1951                                   (LPDWORD) & szPort)) {
1952                 if (szPort > 0) {
1953                     Port[szPort - 1] = '\0';
1954                 }
1955             }
1956             szPort = atoi(Port);
1957             if (szPort != 80 && szPort != 443 && szHost > 0) {
1958                 StringCbCat(Host, INTERNET_MAX_URL_LENGTH, ":");
1959                 StringCbCat(Host, INTERNET_MAX_URL_LENGTH, Port);
1960             }
1961             if (szHost > 0) {
1962                 StringCbCat(snuri, INTERNET_MAX_URL_LENGTH, Host);
1963                 worker = map_uri_to_worker_ext(uw_map, uri, snuri,
1964                                                &extensions, &worker_index, logger);
1965             }
1966             else {
1967                 worker = map_uri_to_worker_ext(uw_map, uri, NULL,
1968                                                &extensions, &worker_index, logger);
1969             }
1970             /*
1971              * Check if somebody is feading us with his own TOMCAT data headers.
1972              * We reject such postings !
1973              */
1974             if (worker) {
1975                 char *forwardURI;
1976
1977                 if (JK_IS_DEBUG_LEVEL(logger))
1978                     jk_log(logger, JK_LOG_DEBUG,
1979                            "check if [%s] points to the web-inf directory",
1980                         uri);
1981
1982                 if (uri_is_web_inf(uri)) {
1983                     jk_log(logger, JK_LOG_EMERG,
1984                            "[%s] points to the web-inf or meta-inf directory. "
1985                            "Somebody tries to hack into the site!!!",
1986                            uri);
1987
1988                     write_error_response(pfc, 404);
1989                     return SF_STATUS_REQ_FINISHED;
1990                 }
1991
1992                 /* This is a servlet, should redirect ... */
1993                 if (JK_IS_DEBUG_LEVEL(logger))
1994                     jk_log(logger, JK_LOG_DEBUG,
1995                         "[%s] is a servlet url - should redirect to %s",
1996                         uri, worker);
1997
1998                 /* get URI we should forward */
1999                 if (uri_select_option == URI_SELECT_OPT_UNPARSED) {
2000                     /* get original unparsed URI */
2001                     GetHeader(pfc, "url", (LPVOID) uri, (LPDWORD) & sz);
2002                     /* restore terminator for uri portion */
2003                     if (query)
2004                         *(query - 1) = '\0';
2005                     if (JK_IS_DEBUG_LEVEL(logger))
2006                         jk_log(logger, JK_LOG_DEBUG,
2007                                "forwarding original URI [%s]",
2008                                uri);
2009                     forwardURI = uri;
2010                 }
2011                 else if (uri_select_option == URI_SELECT_OPT_ESCAPED) {
2012                     if (!escape_url(uri, snuri, INTERNET_MAX_URL_LENGTH)) {
2013                         jk_log(logger, JK_LOG_ERROR,
2014                                "[%s] re-encoding request exceeds maximum buffer size.",
2015                                uri);
2016                         write_error_response(pfc, 400);
2017                         return SF_STATUS_REQ_FINISHED;
2018                     }
2019                     if (JK_IS_DEBUG_LEVEL(logger))
2020                         jk_log(logger, JK_LOG_DEBUG,
2021                                "fowarding escaped URI [%s]",
2022                                snuri);
2023                     forwardURI = snuri;
2024                 }
2025                 else if (uri_select_option == URI_SELECT_OPT_PROXY) {
2026                     if (!jk_canonenc(uri, snuri, INTERNET_MAX_URL_LENGTH)) {
2027                         jk_log(logger, JK_LOG_ERROR,
2028                                "[%s] re-encoding request exceeds maximum buffer size.",
2029                                uri);
2030                         write_error_response(pfc, 400);
2031                         return SF_STATUS_REQ_FINISHED;
2032                     }
2033                     if (JK_IS_DEBUG_LEVEL(logger))
2034                         jk_log(logger, JK_LOG_DEBUG,
2035                                "fowarding escaped URI [%s]",
2036                                snuri);
2037                     forwardURI = snuri;
2038                 }
2039                 else {
2040                     forwardURI = uri;
2041                 }
2042                 /* Do a simple rewrite .
2043                  * Note that URI can be escaped, so thus the rule has
2044                  * to be in that case.
2045                  *
2046                  * TODO: Add more advanced regexp rewrite.
2047                  */
2048                 if (JK_IS_DEBUG_LEVEL(logger)) {
2049                     char duri[INTERNET_MAX_URL_LENGTH];
2050                     StringCbCopy(duri, INTERNET_MAX_URL_LENGTH, forwardURI);
2051                     if (simple_rewrite(forwardURI)) {
2052                         jk_log(logger, JK_LOG_DEBUG,
2053                                "rewritten URI [%s]->[%s]",
2054                                duri, forwardURI);
2055                     }
2056                     else if (rregex_rewrite(forwardURI)) {
2057                         jk_log(logger, JK_LOG_DEBUG,
2058                                "rewritten URI [%s]->[%s]",
2059                                duri, forwardURI);
2060                     }
2061                 }
2062                 else {
2063                     if (!simple_rewrite(forwardURI))
2064                         rregex_rewrite(forwardURI);
2065                 }
2066
2067                 itoa(worker_index, swindex, 10);
2068                 if (!AddHeader(pfc, URI_HEADER_NAME, forwardURI) ||
2069                     ((strlen(squery) > 0)
2070                      ? !AddHeader(pfc, QUERY_HEADER_NAME, squery) : FALSE) ||
2071                     !AddHeader(pfc, WORKER_HEADER_NAME, (LPSTR)worker) ||
2072                     !AddHeader(pfc, WORKER_HEADER_INDEX, swindex) ||
2073                     !SetHeader(pfc, "url", extension_uri)) {
2074                     jk_log(logger, JK_LOG_ERROR,
2075                            "error while adding request headers");
2076                     SetLastError(ERROR_INVALID_PARAMETER);
2077                     return SF_STATUS_REQ_ERROR;
2078                 }
2079
2080                 /* Move Translate: header to a temporary header so
2081                  * that the extension proc will be called.
2082                  * This allows the servlet to handle 'Translate: f'.
2083                  */
2084                 if (GetHeader
2085                     (pfc, TRANSLATE_HEADER, (LPVOID) Translate,
2086                      (LPDWORD) & szTranslate) && Translate != NULL
2087                     && szTranslate > 0) {
2088                     if (!AddHeader
2089                         (pfc, TOMCAT_TRANSLATE_HEADER_NAME, Translate)) {
2090                         jk_log(logger, JK_LOG_ERROR,
2091                                "error while adding Tomcat-Translate headers");
2092                         return SF_STATUS_REQ_ERROR;
2093                     }
2094                     SetHeader(pfc, "Translate:", NULL);
2095                 }
2096                 if (!pfc->pFilterContext) {
2097                     isapi_log_data_t *ld = (isapi_log_data_t *)pfc->AllocMem(pfc, sizeof(isapi_log_data_t), 0);
2098                     if (!ld) {
2099                         jk_log(logger, JK_LOG_ERROR,
2100                                "error while allocating memory");
2101                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2102                         return SF_STATUS_REQ_ERROR;
2103                     }
2104                     memset(ld, 0, sizeof(isapi_log_data_t));
2105                     StringCbCopy(ld->uri, INTERNET_MAX_URL_LENGTH, forwardURI);
2106                     StringCbCopy(ld->query, INTERNET_MAX_URL_LENGTH, squery);
2107                     ld->request_matched = JK_TRUE;
2108                     pfc->pFilterContext = ld;
2109                 } else {
2110                     isapi_log_data_t *ld = (isapi_log_data_t *)pfc->pFilterContext;
2111                     memset(ld, 0, sizeof(isapi_log_data_t));
2112                     StringCbCopy(ld->uri, INTERNET_MAX_URL_LENGTH, forwardURI);
2113                     StringCbCopy(ld->query, INTERNET_MAX_URL_LENGTH, squery);
2114                     ld->request_matched = JK_TRUE;
2115                 }
2116             }
2117             else {
2118                 if (JK_IS_DEBUG_LEVEL(logger))
2119                     jk_log(logger, JK_LOG_DEBUG,
2120                            "[%s] is not a servlet url", uri);
2121                 if (strip_session) {
2122                     char *jsessionid = strstr(uri, JK_PATH_SESSION_IDENTIFIER);
2123                     if (jsessionid) {
2124                         if (JK_IS_DEBUG_LEVEL(logger))
2125                             jk_log(logger, JK_LOG_DEBUG,
2126                                    "removing session identifier [%s] for non servlet url [%s]",
2127                                    jsessionid, uri);
2128                         *jsessionid = '\0';
2129                         SetHeader(pfc, "url", uri);
2130                     }
2131                 }
2132             }
2133         }
2134     }
2135     else if (dwNotificationType == SF_NOTIFY_LOG) {
2136         if (pfc->pFilterContext) {
2137             isapi_log_data_t *ld = (isapi_log_data_t *)pfc->pFilterContext;
2138             if (ld->request_matched) {
2139                 HTTP_FILTER_LOG  *pl = (HTTP_FILTER_LOG *)pvNotification;
2140                 pl->pszTarget = ld->uri;
2141                 pl->pszParameters = ld->query;
2142             }
2143         }
2144     }
2145     return SF_STATUS_REQ_NEXT_NOTIFICATION;
2146 }
2147
2148
2149 BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO * pVer)
2150 {
2151     int rc;
2152     BOOL rv = TRUE;
2153
2154     pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
2155
2156     StringCbCopy(pVer->lpszExtensionDesc, HSE_MAX_EXT_DLL_NAME_LEN, (VERSION_STRING));
2157
2158
2159     JK_ENTER_CS(&(init_cs), rc);
2160     if (!is_inited) {
2161         rv = initialize_extension();
2162     }
2163     JK_LEAVE_CS(&(init_cs), rc);
2164
2165     return rv;
2166 }
2167
2168 DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpEcb)
2169 {
2170     int rv;
2171     DWORD rc = HSE_STATUS_ERROR;
2172
2173     lpEcb->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
2174
2175     JK_TRACE_ENTER(logger);
2176
2177     /* Initialise jk */
2178     if (is_inited && !is_mapread) {
2179         char serverName[MAX_SERVERNAME] = "";
2180         char instanceId[MAX_INSTANCEID] = "";
2181
2182         DWORD dwLen = MAX_SERVERNAME - MAX_INSTANCEID - 1;
2183         if (lpEcb->GetServerVariable(lpEcb->ConnID,
2184                     SERVER_NAME, serverName, &dwLen)) {
2185             if (dwLen > 0) {
2186                 serverName[dwLen - 1] = '\0';
2187                 dwLen = MAX_INSTANCEID;
2188                 if (lpEcb->GetServerVariable(lpEcb->ConnID,
2189                             INSTANCE_ID, instanceId, &dwLen)) {
2190                     if (dwLen > 0) {
2191                         instanceId[dwLen - 1] = '\0';
2192                         StringCbCat(serverName, MAX_SERVERNAME, "_");
2193                         StringCbCat(serverName, MAX_SERVERNAME, instanceId);
2194                     }
2195                 }
2196             }
2197             JK_ENTER_CS(&(init_cs), rv);
2198             if (!is_mapread && init_jk(serverName))
2199                 is_mapread = JK_TRUE;
2200             JK_LEAVE_CS(&(init_cs), rv);
2201         }
2202         if (!is_mapread)
2203             is_inited = JK_FALSE;
2204     }
2205
2206     if (is_inited) {
2207         isapi_private_data_t private_data;
2208         jk_ws_service_t s;
2209         jk_pool_atom_t buf[SMALL_POOL_SIZE];
2210         char *worker_name;
2211
2212         if (!watchdog_interval)
2213             wc_maintain(logger);
2214         jk_init_ws_service(&s);
2215         jk_open_pool(&private_data.p, buf, sizeof(buf));
2216
2217         private_data.bytes_read_so_far = 0;
2218         private_data.lpEcb = lpEcb;
2219         private_data.chunk_content = JK_FALSE;
2220
2221         s.ws_private = &private_data;
2222         s.pool = &private_data.p;
2223
2224         if (init_ws_service(&private_data, &s, &worker_name)) {
2225             jk_worker_t *worker = wc_get_worker_for_name(worker_name, logger);
2226
2227             if (JK_IS_DEBUG_LEVEL(logger))
2228                 jk_log(logger, JK_LOG_DEBUG,
2229                        "%s a worker for name %s",
2230                        worker ? "got" : "could not get", worker_name);
2231
2232             if (worker) {
2233                 jk_endpoint_t *e = NULL;
2234                 if (worker->get_endpoint(worker, &e, logger)) {
2235                     int is_error = JK_HTTP_SERVER_ERROR;
2236                     int result;
2237                     if ((result = e->service(e, &s, logger, &is_error)) > 0) {
2238                         if (s.extension.use_server_error_pages &&
2239                             s.http_response_status >= s.extension.use_server_error_pages) {
2240                             if (JK_IS_DEBUG_LEVEL(logger))
2241                                 jk_log(logger, JK_LOG_DEBUG, "Forwarding status=%d"
2242                                        " for worker=%s",
2243                                        s.http_response_status, worker_name);
2244                             lpEcb->dwHttpStatusCode = s.http_response_status;
2245                             write_error_message(lpEcb, s.http_response_status);
2246                         }
2247                         else {
2248                             rc = HSE_STATUS_SUCCESS;
2249                             lpEcb->dwHttpStatusCode = s.http_response_status;
2250                             if (JK_IS_DEBUG_LEVEL(logger))
2251                                 jk_log(logger, JK_LOG_DEBUG,
2252                                        "service() returned OK");
2253                         }
2254                     }
2255                     else {
2256                         if ((result == JK_CLIENT_ERROR) && (is_error == JK_HTTP_OK)) {
2257                             jk_log(logger, JK_LOG_INFO,
2258                                    "service() failed because client aborted connection");
2259                         }
2260                         else {
2261                             jk_log(logger, JK_LOG_ERROR,
2262                                    "service() failed with http error %d", is_error);
2263                         }
2264                         lpEcb->dwHttpStatusCode = is_error;
2265                         write_error_message(lpEcb, is_error);
2266                     }
2267                     e->done(&e, logger);
2268                 }
2269                 else {
2270                     jk_log(logger, JK_LOG_ERROR,
2271                         "Failed to obtain an endpoint to service request - "
2272                         "your connection_pool_size is probably less than the threads in your web server!");
2273                 }
2274             }
2275             else {
2276                 jk_log(logger, JK_LOG_ERROR,
2277                        "could not get a worker for name %s",
2278                        worker_name);
2279             }
2280         }
2281         else {
2282             jk_log(logger, JK_LOG_ERROR,
2283                 "failed to init service for request.");
2284          }
2285         jk_close_pool(&private_data.p);
2286     }
2287     else {
2288         jk_log(logger, JK_LOG_ERROR,
2289                "not initialized");
2290     }
2291
2292     JK_TRACE_EXIT(logger);
2293     return rc;
2294 }
2295
2296
2297
2298 BOOL WINAPI TerminateExtension(DWORD dwFlags)
2299 {
2300     return TerminateFilter(dwFlags);
2301 }
2302
2303 BOOL WINAPI TerminateFilter(DWORD dwFlags)
2304 {
2305     int rc;
2306
2307     UNREFERENCED_PARAMETER(dwFlags);
2308
2309     JK_ENTER_CS(&(init_cs), rc);
2310     if (is_inited) {
2311         jk_log(logger, JK_LOG_INFO, "%s stopping", (FULL_VERSION_STRING));
2312         is_inited = JK_FALSE;
2313         watchdog_interval = 0;
2314         if (watchdog_handle) {
2315             WaitForSingleObject(watchdog_handle, INFINITE);
2316             CloseHandle(watchdog_handle);
2317             watchdog_handle = NULL;
2318         }
2319         if (is_mapread) {
2320             uri_worker_map_free(&uw_map, logger);
2321             is_mapread = JK_FALSE;
2322         }
2323         if (workers_map) {
2324             jk_map_free(&workers_map);
2325         }
2326         if (rewrite_map) {
2327             jk_map_free(&rewrite_map);
2328         }
2329         if (rregexp_map) {
2330             int i;
2331             for (i = 0; i < jk_map_size(rregexp_map); i++) {
2332                 ap_regex_t *regexp = (ap_regex_t *)jk_map_value_at(rregexp_map, i);
2333                 if (regexp) {
2334                     ap_regfree(regexp);
2335                     free(regexp);
2336                 }
2337             }
2338             jk_map_free(&rregexp_map);
2339         }
2340         wc_close(logger);
2341         jk_shm_close();
2342         JK_ENTER_CS(&(log_cs), rc);
2343         if (logger) {
2344             jk_close_file_logger(&logger);
2345         }
2346         JK_LEAVE_CS(&(log_cs), rc);
2347     }
2348     JK_LEAVE_CS(&(init_cs), rc);
2349
2350     return TRUE;
2351 }
2352
2353
2354 BOOL WINAPI DllMain(HINSTANCE hInst,    // Instance Handle of the DLL
2355                     ULONG ulReason,     // Reason why NT called this DLL
2356                     LPVOID lpReserved)  // Reserved parameter for future use
2357 {
2358     int rc;
2359     BOOL fReturn = TRUE;
2360     char fname[MAX_PATH];
2361
2362     UNREFERENCED_PARAMETER(lpReserved);
2363
2364     switch (ulReason) {
2365     case DLL_PROCESS_ATTACH:
2366         if (GetModuleFileName(hInst, fname, sizeof(fname))) {
2367             char *p = strrchr(fname, '.');
2368             if (p) {
2369                 *p = '\0';
2370                 StringCbCopy(ini_file_name, MAX_PATH, fname);
2371                 StringCbCat(ini_file_name, MAX_PATH, ".properties");
2372             }
2373             else {
2374                 /* Cannot obtain file name ? */
2375                 fReturn = JK_FALSE;
2376             }
2377             if ((p = strrchr(fname, '\\'))) {
2378                 *(p++) = '\0';
2379                 StringCbCopy(dll_file_path, MAX_PATH, fname);
2380                 jk_map_alloc(&jk_environment_map);
2381                 jk_map_add(jk_environment_map, "JKISAPI_PATH", dll_file_path);
2382                 jk_map_add(jk_environment_map, "JKISAPI_NAME", p);
2383             }
2384             else {
2385                 /* Cannot obtain file name ? */
2386                 fReturn = JK_FALSE;
2387             }
2388         }
2389         else {
2390             fReturn = JK_FALSE;
2391         }
2392         /* Construct redirector headers to use for this redirector instance */
2393         StringCbPrintf(URI_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, URI_HEADER_NAME_BASE, hInst);
2394         StringCbPrintf(QUERY_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, QUERY_HEADER_NAME_BASE, hInst);
2395         StringCbPrintf(WORKER_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, WORKER_HEADER_NAME_BASE, hInst);
2396         StringCbPrintf(WORKER_HEADER_INDEX, MAX_PATH, HEADER_TEMPLATE, WORKER_HEADER_INDEX_BASE, hInst);
2397         StringCbPrintf(TOMCAT_TRANSLATE_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, TOMCAT_TRANSLATE_HEADER_NAME_BASE, hInst);
2398
2399         /* Construct the HTTP_ headers that will be seen in ExtensionProc */
2400         StringCbPrintf(HTTP_URI_HEADER_NAME, MAX_PATH, HTTP_HEADER_TEMPLATE, URI_HEADER_NAME_BASE, hInst);
2401         StringCbPrintf(HTTP_QUERY_HEADER_NAME, MAX_PATH, HTTP_HEADER_TEMPLATE, QUERY_HEADER_NAME_BASE, hInst);
2402         StringCbPrintf(HTTP_WORKER_HEADER_NAME, MAX_PATH, HTTP_HEADER_TEMPLATE, WORKER_HEADER_NAME_BASE, hInst);
2403         StringCbPrintf(HTTP_WORKER_HEADER_INDEX, MAX_PATH, HTTP_HEADER_TEMPLATE, WORKER_HEADER_INDEX_BASE, hInst);
2404
2405         JK_INIT_CS(&init_cs, rc);
2406         JK_INIT_CS(&log_cs, rc);
2407
2408     break;
2409     case DLL_PROCESS_DETACH:
2410         __try {
2411             TerminateFilter(HSE_TERM_MUST_UNLOAD);
2412         }
2413         __except(1) {
2414         }
2415         JK_DELETE_CS(&init_cs, rc);
2416         JK_DELETE_CS(&log_cs, rc);
2417         break;
2418
2419     default:
2420         break;
2421     }
2422
2423     return fReturn;
2424 }
2425
2426 static DWORD WINAPI watchdog_thread(void *param)
2427 {
2428     int i;
2429     if (JK_IS_DEBUG_LEVEL(logger)) {
2430         jk_log(logger, JK_LOG_DEBUG,
2431                "Watchdog thread initialized with %u second interval",
2432                watchdog_interval);
2433     }
2434     while (watchdog_interval) {
2435         for (i = 0; i < (watchdog_interval * 10); i++) {
2436             if (!watchdog_interval)
2437                 break;
2438             Sleep(100);
2439         }
2440         if (!watchdog_interval)
2441             break;
2442         if (JK_IS_DEBUG_LEVEL(logger)) {
2443             jk_log(logger, JK_LOG_DEBUG,
2444                    "Watchdog thread running");
2445         }
2446         if (worker_mount_file[0]) {
2447             jk_shm_lock();
2448             uri_worker_map_update(uw_map, 0, logger);
2449             jk_shm_unlock();
2450         }
2451         wc_maintain(logger);
2452     }
2453     if (JK_IS_DEBUG_LEVEL(logger)) {
2454         jk_log(logger, JK_LOG_DEBUG,
2455                "Watchdog thread finished");
2456     }
2457     return 0;
2458 }
2459
2460 /*
2461  * Reinitializes the logger, formatting the log file name if rotation is enabled,
2462  * and calculating the next rotation time if applicable.
2463  */
2464 static int init_logger(int rotate, jk_logger_t **l)
2465 {
2466     int rc = JK_TRUE;
2467     int log_open = rotate;  /* log is assumed open if a rotate is requested */
2468     char *log_file_name;
2469     char log_file_name_buf[MAX_PATH*2];
2470
2471     /* If log rotation is enabled, format the log filename */
2472     if ((log_rotationtime > 0) || (log_filesize > 0)) {
2473         time_t t;
2474         t = time(NULL);
2475
2476         if (log_rotationtime > 0) {
2477             /* Align time to rotationtime intervals */
2478             t = (t / log_rotationtime) * log_rotationtime;
2479
2480             /* Calculate rotate time */
2481             log_next_rotate_time = t + log_rotationtime;
2482         }
2483
2484         log_file_name = log_file_name_buf;
2485         if (strchr(log_file, '%')) {
2486             struct tm *tm_now;
2487
2488             /* If there are %s in the log file name, treat it as a sprintf format */
2489             tm_now = localtime(&t);
2490             strftime(log_file_name, sizeof(log_file_name_buf), log_file, tm_now);
2491         } else {
2492             /* Otherwise append the number of seconds to the base name */
2493             StringCbPrintf(log_file_name, sizeof(log_file_name_buf), "%s.%d", log_file, (long)t);
2494         }
2495     } else {
2496         log_file_name = log_file;
2497     }
2498
2499     /* Close the current log file if required, and the effective log file name has changed */
2500     if (log_open && strncmp(log_file_name, log_file_effective, strlen(log_file_name)) != 0) {
2501         FILE* lf = ((jk_file_logger_t* )logger->logger_private)->logfile;
2502         fprintf(lf, "Log rotated to %s\r\n", log_file_name);
2503         fflush(lf);
2504         rc = jk_close_file_logger(&logger);
2505         log_open = JK_FALSE;
2506     }
2507
2508     if (!log_open) {
2509         if (jk_open_file_logger(&logger, log_file_name, log_level)) {
2510             logger->log = iis_log_to_file;
2511
2512             /* Remember the current log file name for the next potential rotate */
2513             StringCbCopy(log_file_effective, sizeof(log_file_effective), log_file_name);
2514             rc = JK_TRUE;
2515         } else {
2516             logger = NULL;
2517             rc = JK_FALSE;
2518         }
2519     }
2520
2521     /* Update logger being used for this log call so it occurs on new file */
2522     (*l) = logger;
2523     return rc;
2524 }
2525
2526 /*
2527  * Checks whether the log needs to be rotated. Must be called while holding the log lock.
2528  * The behaviour here is based on the Apache rotatelogs program.
2529  * http://httpd.apache.org/docs/2.0/programs/rotatelogs.html
2530  */
2531 static int JK_METHOD rotate_log_file(jk_logger_t **l)
2532 {
2533     int rc = JK_TRUE;
2534     int rotate = JK_FALSE;
2535
2536     if (log_rotationtime > 0) {
2537         time_t t = time(NULL);
2538
2539         if (t >= log_next_rotate_time) {
2540             rotate = JK_TRUE;
2541         }
2542     } else if (log_filesize > 0) {
2543         LARGE_INTEGER filesize;
2544         HANDLE h = (HANDLE)_get_osfhandle(fileno(((jk_file_logger_t *)(*l)->logger_private)->logfile));
2545         GetFileSizeEx(h, &filesize);
2546
2547         if ((ULONGLONG)filesize.QuadPart >= log_filesize) {
2548             rotate = JK_TRUE;
2549         }
2550     }
2551     if (rotate) {
2552         rc = init_logger(JK_TRUE, l);
2553     }
2554     return rc;
2555 }
2556
2557 /*
2558  * Log messages to the log file, rotating the log when required.
2559  */
2560 static int JK_METHOD iis_log_to_file(jk_logger_t *l, int level,
2561                                     int used, char *what)
2562 {
2563     int rc = JK_FALSE;
2564
2565     if (l &&
2566         (l->level <= level || level == JK_LOG_REQUEST_LEVEL) &&
2567         l->logger_private && what && used > 0) {
2568         jk_file_logger_t *p = l->logger_private;
2569         rc = JK_TRUE;
2570
2571         if (p->logfile) {
2572             what[used++] = '\r';
2573             what[used++] = '\n';
2574             what[used] = '\0';
2575
2576             /* Perform logging within critical section to protect rotation */
2577             JK_ENTER_CS(&(log_cs), rc);
2578             if (rc && rotate_log_file(&l)) {
2579                 /* The rotation process will reallocate the jk_logger_t structure, so refetch */
2580                 FILE *rotated = ((jk_file_logger_t *)l->logger_private)->logfile;
2581                 fputs(what, rotated);
2582                 fflush(rotated);
2583                 JK_LEAVE_CS(&(log_cs), rc);
2584             }
2585         }
2586     }
2587     return rc;
2588 }
2589
2590 static int init_jk(char *serverName)
2591 {
2592     char shm_name[MAX_PATH];
2593     int rc = JK_FALSE;
2594
2595     init_logger(JK_FALSE, &logger);
2596     /* TODO: Use System logging to notify the user that
2597      *       we cannot open the configured log file.
2598      */
2599
2600     StringCbCopy(shm_name, MAX_PATH, SHM_DEF_NAME);
2601
2602     jk_log(logger, JK_LOG_INFO, "Starting %s", (FULL_VERSION_STRING));
2603
2604     if (*serverName) {
2605         size_t i;
2606         StringCbCat(shm_name, MAX_PATH, "_");
2607         StringCbCat(shm_name, MAX_PATH, serverName);
2608         for(i = 0; i < strlen(shm_name); i++) {
2609             shm_name[i] = toupper(shm_name[i]);
2610             if (!isalnum(shm_name[i]))
2611                 shm_name[i] = '_';
2612         }
2613     }
2614
2615     jk_set_worker_def_cache_size(DEFAULT_WORKER_THREADS);
2616
2617     /* Logging the initialization type: registry or properties file in virtual dir
2618      */
2619     if (JK_IS_DEBUG_LEVEL(logger)) {
2620         jk_log(logger, JK_LOG_DEBUG, "Detected IIS version %d.%d", iis_info.major, iis_info.minor);
2621         if (using_ini_file) {
2622             jk_log(logger, JK_LOG_DEBUG, "Using ini file %s.", ini_file_name);
2623         }
2624         else {
2625             jk_log(logger, JK_LOG_DEBUG, "Using registry.");
2626         }
2627
2628         jk_log(logger, JK_LOG_DEBUG, "Using log file %s.", log_file);
2629         jk_log(logger, JK_LOG_DEBUG, "Using log level %d.", log_level);
2630         jk_log(logger, JK_LOG_DEBUG, "Using log rotation time %d seconds.", log_rotationtime);
2631         jk_log(logger, JK_LOG_DEBUG, "Using log file size %d bytes.", log_filesize);
2632
2633         jk_log(logger, JK_LOG_DEBUG, "Using extension uri %s.", extension_uri);
2634         jk_log(logger, JK_LOG_DEBUG, "Using worker file %s.", worker_file);
2635         jk_log(logger, JK_LOG_DEBUG, "Using worker mount file %s.",
2636                worker_mount_file);
2637         jk_log(logger, JK_LOG_DEBUG, "Using rewrite rule file %s.",
2638                rewrite_rule_file);
2639         jk_log(logger, JK_LOG_DEBUG, "Using uri select %d.", uri_select_option);
2640         jk_log(logger, JK_LOG_DEBUG, "Using%s chunked encoding.", (chunked_encoding_enabled ? "" : " no"));
2641
2642         jk_log(logger, JK_LOG_DEBUG, "Using notification event %s (0x%08x)",
2643                (iis_info.filter_notify_event == SF_NOTIFY_AUTH_COMPLETE) ?
2644                "SF_NOTIFY_AUTH_COMPLETE" :
2645                ((iis_info.filter_notify_event == SF_NOTIFY_PREPROC_HEADERS) ?
2646                "SF_NOTIFY_PREPROC_HEADERS" : "UNKNOWN"),
2647                iis_info.filter_notify_event);
2648
2649         if (error_page) {
2650             jk_log(logger, JK_LOG_DEBUG, "Using error page '%s'.", error_page);
2651         }
2652         jk_log(logger, JK_LOG_DEBUG, "Using uri header %s.", URI_HEADER_NAME);
2653         jk_log(logger, JK_LOG_DEBUG, "Using query header %s.", QUERY_HEADER_NAME);
2654         jk_log(logger, JK_LOG_DEBUG, "Using worker header %s.", WORKER_HEADER_NAME);
2655         jk_log(logger, JK_LOG_DEBUG, "Using worker index %s.", WORKER_HEADER_INDEX);
2656         jk_log(logger, JK_LOG_DEBUG, "Using translate header %s.", TOMCAT_TRANSLATE_HEADER_NAME);
2657         jk_log(logger, JK_LOG_DEBUG, "Using a default of %d connections per pool.",
2658                                      DEFAULT_WORKER_THREADS);
2659     }
2660
2661     if ((log_rotationtime > 0) && (log_filesize > 0)) {
2662         jk_log(logger, JK_LOG_WARNING,
2663             "%s is defined in configuration, but will be ignored because %s is set. ",
2664             LOG_FILESIZE_TAG, LOG_ROTATION_TIME_TAG);
2665     }
2666
2667     if (rewrite_rule_file[0] && jk_map_alloc(&rewrite_map)) {
2668         if (jk_map_read_properties(rewrite_map, NULL, rewrite_rule_file,
2669                                    NULL, JK_MAP_HANDLE_RAW, logger)) {
2670             int i;
2671             if (JK_IS_DEBUG_LEVEL(logger)) {
2672                 jk_log(logger, JK_LOG_DEBUG, "Loaded rewrite rule file %s.",
2673                        rewrite_rule_file);
2674
2675             }
2676             jk_map_alloc(&rregexp_map);
2677             for (i = 0; i < jk_map_size(rewrite_map); i++) {
2678                 const char *src = jk_map_name_at(rewrite_map, i);
2679                 if (*src == '~') {
2680                     ap_regex_t *regexp = malloc(sizeof(ap_regex_t));
2681                     const char *val = jk_map_value_at(rewrite_map, i);
2682                     /* Skip leading tilde */
2683                     regexp->real = src + 1;
2684                     regexp->fake = val;
2685                     if (!ap_regcomp(regexp, regexp->real, AP_REG_EXTENDED)) {
2686                         jk_map_add(rregexp_map, regexp->real, regexp);
2687                         if (JK_IS_DEBUG_LEVEL(logger)) {
2688                             jk_log(logger, JK_LOG_DEBUG,
2689                                    "Added regular expression rule %s -> %s",
2690                                    regexp->real, regexp->fake);
2691                         }
2692                     }
2693                     else {
2694                         jk_log(logger, JK_LOG_ERROR,
2695                                "Unable to compile regular expression %s",
2696                                regexp->real);
2697                         free(regexp);
2698                     }
2699                 }
2700             }
2701         }
2702         else {
2703             jk_map_free(&rewrite_map);
2704             rewrite_map = NULL;
2705         }
2706     }
2707
2708     if (uri_worker_map_alloc(&uw_map, NULL, logger)) {
2709         rc = JK_FALSE;
2710         if (reject_unsafe)
2711             uw_map->reject_unsafe = 1;
2712         else
2713             uw_map->reject_unsafe = 0;
2714         uw_map->reload = worker_mount_reload;
2715         if (worker_mount_file[0]) {
2716             uw_map->fname = worker_mount_file;
2717             rc = uri_worker_map_load(uw_map, logger);
2718         }
2719     }
2720     if (rc) {
2721         rc = JK_FALSE;
2722         if (jk_map_alloc(&workers_map)) {
2723             if (jk_map_read_properties(workers_map, NULL, worker_file, NULL,
2724                                        JK_MAP_HANDLE_DUPLICATES, logger)) {
2725                 int rv;
2726
2727                 /* we add the URI->WORKER MAP since workers using AJP14 will feed it */
2728
2729                 if (jk_map_resolve_references(workers_map, "worker.", 1, 1, logger) == JK_FALSE) {
2730                     jk_log(logger, JK_LOG_ERROR, "Error in resolving configuration references");
2731                 }
2732                 /*
2733                  * Create named shared memory for each server
2734                  */
2735                 if (shm_config_size == 0)
2736                     shm_config_size = jk_shm_calculate_size(workers_map, logger);
2737                 else {
2738                     jk_log(logger, JK_LOG_WARNING,
2739                            "The optimal shared memory size can now be determined automatically.");
2740                     jk_log(logger, JK_LOG_WARNING,
2741                            "You can remove the shm_size attribute if you want to use the optimal size.");
2742                 }
2743                 if ((rv = jk_shm_open(shm_name, shm_config_size, logger)) != 0) {
2744                     /* TODO: Do not try to open the worker if we cannot create
2745                      *       the shared memory segment.
2746                      */
2747                     jk_log(logger, JK_LOG_ERROR,
2748                            "Initializing shm:%s errno=%d. Load balancing workers will not function properly.",
2749                            jk_shm_name(), rv);
2750                 }
2751                 worker_env.uri_to_worker = uw_map;
2752                 worker_env.server_name = serverName;
2753                 worker_env.pool = NULL;
2754
2755                 if (wc_open(workers_map, &worker_env, logger)) {
2756                     rc = JK_TRUE;
2757                 }
2758                 uri_worker_map_ext(uw_map, logger);
2759                 uri_worker_map_switch(uw_map, logger);
2760             }
2761             else
2762                 jk_log(logger, JK_LOG_EMERG,
2763                        "Unable to read worker file %s.", worker_file);
2764             if (rc != JK_TRUE) {
2765                 jk_map_free(&workers_map);
2766                 workers_map = NULL;
2767             }
2768         }
2769     }
2770     if (rc) {
2771         if (watchdog_interval) {
2772             DWORD  wi;
2773             watchdog_handle = CreateThread(NULL, 0, watchdog_thread,
2774                                            NULL, 0, &wi);
2775             if (!watchdog_handle) {
2776                 jk_log(logger, JK_LOG_EMERG, "Error %d (0x%08x) creating Watchdog thread",
2777                        GetLastError(), GetLastError());
2778                 watchdog_interval = 0;
2779             }
2780         }
2781         jk_log(logger, JK_LOG_INFO, "%s initialized", (FULL_VERSION_STRING));
2782     }
2783     return rc;
2784 }
2785
2786 static BOOL initialize_extension(void)
2787 {
2788
2789     if (read_registry_init_data()) {
2790         if (get_iis_info(&iis_info) != JK_TRUE) {
2791             jk_log(logger, JK_LOG_ERROR, "Could not retrieve IIS version from registry");
2792         }
2793         is_inited = JK_TRUE;
2794     }
2795     return is_inited;
2796 }
2797
2798 int parse_uri_select(const char *uri_select)
2799 {
2800     if (0 == strcasecmp(uri_select, URI_SELECT_PARSED_VERB)) {
2801         return URI_SELECT_OPT_PARSED;
2802     }
2803
2804     if (0 == strcasecmp(uri_select, URI_SELECT_UNPARSED_VERB)) {
2805         return URI_SELECT_OPT_UNPARSED;
2806     }
2807
2808     if (0 == strcasecmp(uri_select, URI_SELECT_ESCAPED_VERB)) {
2809         return URI_SELECT_OPT_ESCAPED;
2810     }
2811
2812     if (0 == strcasecmp(uri_select, URI_SELECT_PROXY_VERB)) {
2813         return URI_SELECT_OPT_PROXY;
2814     }
2815
2816     return -1;
2817 }
2818
2819 static int read_registry_init_data(void)
2820 {
2821     char tmpbuf[MAX_PATH];
2822     int ok = JK_TRUE;
2823     LPVOID src;
2824     HKEY hkey;
2825     jk_map_t *map = NULL;
2826
2827     if (jk_map_alloc(&map)) {
2828         if (jk_map_read_properties(map, jk_environment_map, ini_file_name, NULL,
2829                                    JK_MAP_HANDLE_DUPLICATES, logger)) {
2830             using_ini_file = JK_TRUE;
2831             src = map;
2832         }
2833         else {
2834             jk_map_free(&map);
2835         }
2836     }
2837     if (!using_ini_file) {
2838         long rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_LOCATION,
2839                                (DWORD)0, KEY_READ, &hkey);
2840         if (ERROR_SUCCESS != rc) {
2841             return JK_FALSE;
2842         }
2843         else {
2844             src = &hkey;
2845         }
2846     }
2847     ok = ok && get_config_parameter(src, JK_LOG_FILE_TAG, log_file, sizeof(log_file));
2848     if (get_config_parameter(src, JK_LOG_LEVEL_TAG, tmpbuf, sizeof(tmpbuf))) {
2849         log_level = jk_parse_log_level(tmpbuf);
2850     }
2851     if (get_config_parameter(src, LOG_ROTATION_TIME_TAG, tmpbuf, sizeof(tmpbuf))) {
2852         log_rotationtime = atol(tmpbuf);
2853         if (log_rotationtime < 0) {
2854             log_rotationtime = 0;
2855         }
2856     }
2857     if (get_config_parameter(src, LOG_FILESIZE_TAG, tmpbuf, sizeof(tmpbuf))) {
2858         size_t tl = strlen(tmpbuf);
2859         if (tl > 0) {
2860             /* rotatelogs has an 'M' suffix on filesize, which we optionally support for consistency */
2861             if (tmpbuf[tl - 1] == 'M') {
2862                 tmpbuf[tl - 1] = '\0';
2863             }
2864             log_filesize = atol(tmpbuf);
2865             if (log_filesize < 0) {
2866                 log_filesize = 0;
2867             }
2868             /* Convert to MB as per Apache rotatelogs */
2869             log_filesize *= (1000 * 1000);
2870         }
2871     }
2872
2873     ok = ok && get_config_parameter(src, EXTENSION_URI_TAG, extension_uri, sizeof(extension_uri));
2874     ok = ok && get_config_parameter(src, JK_WORKER_FILE_TAG, worker_file, sizeof(worker_file));
2875     ok = ok && get_config_parameter(src, JK_MOUNT_FILE_TAG, worker_mount_file, sizeof(worker_mount_file));
2876     get_config_parameter(src, URI_REWRITE_TAG, rewrite_rule_file, sizeof(rewrite_rule_file));
2877     if (get_config_parameter(src, URI_SELECT_TAG, tmpbuf, sizeof(tmpbuf))) {
2878         int opt = parse_uri_select(tmpbuf);
2879         if (opt >= 0) {
2880             uri_select_option = opt;
2881         }
2882         else {
2883             ok = JK_FALSE;
2884         }
2885     }
2886     shm_config_size = (size_t) get_config_int(src, SHM_SIZE_TAG, 0);
2887     worker_mount_reload = get_config_int(src, WORKER_MOUNT_RELOAD_TAG, JK_URIMAP_DEF_RELOAD);
2888     strip_session = get_config_bool(src, STRIP_SESSION_TAG, JK_FALSE);
2889 #ifndef AUTOMATIC_AUTH_NOTIFICATION
2890     use_auth_notification_flags = get_config_int(src, AUTH_COMPLETE_TAG, 1);
2891 #endif
2892     reject_unsafe = get_config_bool(src, REJECT_UNSAFE_TAG, JK_FALSE);
2893     watchdog_interval = get_config_int(src, WATCHDOG_INTERVAL_TAG, 0);
2894     if (watchdog_interval < 0)
2895         watchdog_interval = 0;
2896     chunked_encoding_enabled = get_config_bool(src, ENABLE_CHUNKED_ENCODING_TAG, JK_FALSE);
2897     if (get_config_parameter(src, ERROR_PAGE_TAG, error_page_buf, sizeof(error_page_buf))) {
2898         error_page = error_page_buf;
2899     }
2900
2901     if (using_ini_file) {
2902         jk_map_free(&map);
2903     }
2904     else {
2905         RegCloseKey(hkey);
2906     }
2907     return ok;
2908 }
2909
2910 static int get_config_parameter(LPVOID src, const char *tag,
2911                                 char *val, DWORD sz)
2912 {
2913     const char *tmp = NULL;
2914     if (using_ini_file) {
2915         tmp = jk_map_get_string((jk_map_t*)src, tag, NULL);
2916         if (tmp && (strlen(tmp) < sz)) {
2917             StringCbCopy(val, sz, tmp);
2918             return JK_TRUE;
2919         }
2920         else {
2921             return JK_FALSE;
2922         }
2923     } else {
2924         return get_registry_config_parameter(*((HKEY*)src), tag, val, sz);
2925     }
2926 }
2927
2928 static int get_config_int(LPVOID src, const char *tag, int def)
2929 {
2930     if (using_ini_file) {
2931         return jk_map_get_int((jk_map_t*)src, tag, def);
2932     } else {
2933         int val;
2934         if (get_registry_config_number(*((HKEY*)src), tag, &val) ) {
2935             return val;
2936         }
2937         else {
2938             return def;
2939         }
2940     }
2941 }
2942
2943 static int get_config_bool(LPVOID src, const char *tag, int def)
2944 {
2945     if (using_ini_file) {
2946         return jk_map_get_bool((jk_map_t*)src, tag, def);
2947     } else {
2948         char tmpbuf[128];
2949         if (get_registry_config_parameter(*((HKEY*)src), tag,
2950                                           tmpbuf, sizeof(tmpbuf))) {
2951             return jk_get_bool_code(tmpbuf, def);
2952         }
2953         else {
2954             return def;
2955         }
2956     }
2957 }
2958
2959 static int get_registry_config_parameter(HKEY hkey,
2960                                          const char *tag, char *b, DWORD sz)
2961 {
2962     DWORD type = 0;
2963     LONG lrc;
2964
2965     sz = sz - 1; /* Reserve space for RegQueryValueEx to add null terminator */
2966     b[sz] = '\0'; /* Null terminate in case RegQueryValueEx doesn't */
2967
2968     lrc = RegQueryValueEx(hkey, tag, (LPDWORD) 0, &type, (LPBYTE) b, &sz);
2969     if ((ERROR_SUCCESS != lrc) || (type != REG_SZ)) {
2970         return JK_FALSE;
2971     }
2972
2973     return JK_TRUE;
2974 }
2975
2976 static int get_registry_config_number(HKEY hkey,
2977                                       const char *tag, int *val)
2978 {
2979     DWORD type = 0;
2980     DWORD data = 0;
2981     DWORD sz   = sizeof(DWORD);
2982     LONG lrc;
2983
2984     lrc = RegQueryValueEx(hkey, tag, (LPDWORD)0, &type, (LPBYTE)&data, &sz);
2985     if ((ERROR_SUCCESS != lrc) || (type != REG_DWORD)) {
2986         return JK_FALSE;
2987     }
2988
2989     *val = (int)data;
2990
2991     return JK_TRUE;
2992 }
2993
2994 static int init_ws_service(isapi_private_data_t * private_data,
2995                            jk_ws_service_t *s, char **worker_name)
2996 {
2997     char *huge_buf = NULL;   /* should be enough for all */
2998     int worker_index = -1;
2999     rule_extension_t *e;
3000     char  temp_buf[64];
3001     DWORD huge_buf_sz;
3002     BOOL  unknown_content_length = FALSE;
3003
3004     JK_TRACE_ENTER(logger);
3005
3006     s->start_response = start_response;
3007     s->read  = iis_read;
3008     s->write = iis_write;
3009     s->done  = iis_done;
3010
3011     if (!(huge_buf = jk_pool_alloc(&private_data->p, MAX_PACKET_SIZE))) {
3012         JK_TRACE_EXIT(logger);
3013         return JK_FALSE;
3014     }
3015     huge_buf_sz = MAX_PACKET_SIZE;
3016     GET_SERVER_VARIABLE_VALUE(HTTP_WORKER_HEADER_NAME, (*worker_name));
3017     GET_SERVER_VARIABLE_VALUE(HTTP_URI_HEADER_NAME, s->req_uri);
3018     GET_SERVER_VARIABLE_VALUE(HTTP_QUERY_HEADER_NAME, s->query_string);
3019     GET_SERVER_VARIABLE_VALUE_INT(HTTP_WORKER_HEADER_INDEX, worker_index, -1);
3020
3021     if (JK_IS_DEBUG_LEVEL(logger)) {
3022         jk_log(logger, JK_LOG_DEBUG, "Reading extension header %s: %s", HTTP_WORKER_HEADER_NAME, (*worker_name));
3023         jk_log(logger, JK_LOG_DEBUG, "Reading extension header %s: %d", HTTP_WORKER_HEADER_INDEX, worker_index);
3024         jk_log(logger, JK_LOG_DEBUG, "Reading extension header %s: %s", HTTP_URI_HEADER_NAME, s->req_uri);
3025         jk_log(logger, JK_LOG_DEBUG, "Reading extension header %s: %s", HTTP_QUERY_HEADER_NAME, s->query_string);
3026     }
3027
3028     if (s->req_uri == NULL) {
3029         if (JK_IS_DEBUG_LEVEL(logger))
3030             jk_log(logger, JK_LOG_DEBUG, "No URI header value provided. Defaulting to old behaviour" );
3031         s->query_string = private_data->lpEcb->lpszQueryString;
3032         *worker_name = DEFAULT_WORKER_NAME;
3033         GET_SERVER_VARIABLE_VALUE("URL", s->req_uri);
3034         if (unescape_url(s->req_uri) < 0) {
3035             JK_TRACE_EXIT(logger);
3036             return JK_FALSE;
3037         }
3038         getparents(s->req_uri);
3039     }
3040
3041     GET_SERVER_VARIABLE_VALUE("AUTH_TYPE", s->auth_type);
3042     GET_SERVER_VARIABLE_VALUE("REMOTE_USER", s->remote_user);
3043     GET_SERVER_VARIABLE_VALUE("SERVER_PROTOCOL", s->protocol);
3044     GET_SERVER_VARIABLE_VALUE("REMOTE_HOST", s->remote_host);
3045     GET_SERVER_VARIABLE_VALUE("REMOTE_ADDR", s->remote_addr);
3046     GET_SERVER_VARIABLE_VALUE("REMOTE_PORT", s->remote_port);
3047     GET_SERVER_VARIABLE_VALUE(SERVER_NAME, s->server_name);
3048     GET_SERVER_VARIABLE_VALUE_INT("SERVER_PORT", s->server_port, 80);
3049     GET_SERVER_VARIABLE_VALUE(SERVER_SOFTWARE, s->server_software);
3050     GET_SERVER_VARIABLE_VALUE_INT("SERVER_PORT_SECURE", s->is_ssl, 0);
3051
3052     s->method = private_data->lpEcb->lpszMethod;
3053     /* Check for Transfer Encoding */
3054     if (get_server_value(private_data->lpEcb,
3055                          "HTTP_TRANSFER_ENCODING",
3056                          temp_buf,
3057                          (DWORD)sizeof(temp_buf))) {
3058         if (strcasecmp(temp_buf, TRANSFER_ENCODING_CHUNKED_VALUE) == 0) {
3059             s->is_chunked = JK_TRUE;
3060             if (JK_IS_DEBUG_LEVEL(logger)) {
3061                 jk_log(logger, JK_LOG_DEBUG, "Request is Transfer-Encoding: chunked");
3062             }
3063         }
3064         else {
3065             /* XXX: What to do with non chunked T-E ?
3066              */
3067             if (JK_IS_DEBUG_LEVEL(logger))
3068                 jk_log(logger, JK_LOG_DEBUG, "Unsupported Transfer-Encoding: %s",
3069                        temp_buf);
3070         }
3071     }
3072     if (private_data->lpEcb->cbTotalBytes == 0xFFFFFFFF) {
3073         /* We have size larger then 4Gb or Transfer-Encoding: chunked
3074          * ReadClient should be called until no more data is returned
3075          */
3076         unknown_content_length = TRUE;
3077     }
3078     else {
3079         /* Use the IIS provided content length */
3080         s->content_length = (jk_uint64_t)private_data->lpEcb->cbTotalBytes;
3081     }
3082     e = get_uri_to_worker_ext(uw_map, worker_index);
3083     if (e) {
3084         if (JK_IS_DEBUG_LEVEL(logger))
3085             jk_log(logger, JK_LOG_DEBUG, "Applying service extensions" );
3086         s->extension.reply_timeout = e->reply_timeout;
3087         s->extension.use_server_error_pages = e->use_server_error_pages;
3088         if (e->activation) {
3089             s->extension.activation = jk_pool_alloc(s->pool, e->activation_size * sizeof(int));
3090             memcpy(s->extension.activation, e->activation, e->activation_size * sizeof(int));
3091         }
3092         if (e->fail_on_status_size > 0) {
3093             s->extension.fail_on_status_size = e->fail_on_status_size;
3094             s->extension.fail_on_status = jk_pool_alloc(s->pool, e->fail_on_status_size * sizeof(int));
3095             memcpy(s->extension.fail_on_status, e->fail_on_status, e->fail_on_status_size * sizeof(int));
3096         }
3097     }
3098
3099     s->uw_map = uw_map;
3100     /*
3101      * Add SSL IIS environment
3102      */
3103     if (s->is_ssl) {
3104         char *ssl_env_names[9] = {
3105             "CERT_ISSUER",
3106             "CERT_SUBJECT",
3107             "CERT_COOKIE",
3108             "HTTPS_SERVER_SUBJECT",
3109             "CERT_FLAGS",
3110             "HTTPS_SECRETKEYSIZE",
3111             "CERT_SERIALNUMBER",
3112             "HTTPS_SERVER_ISSUER",
3113             "HTTPS_KEYSIZE"
3114         };
3115         char *ssl_env_values[9] = {
3116             NULL,
3117             NULL,
3118             NULL,
3119             NULL,
3120             NULL,
3121             NULL,
3122             NULL,
3123             NULL,
3124             NULL
3125         };
3126         unsigned int i;
3127         unsigned int num_of_vars = 0;
3128
3129         for (i = 0; i < 9; i++) {
3130             GET_SERVER_VARIABLE_VALUE(ssl_env_names[i], ssl_env_values[i]);
3131             if (ssl_env_values[i]) {
3132                 num_of_vars++;
3133             }
3134         }
3135         /* XXX: To make the isapi plugin more consistent with the other web servers */
3136         /* we should also set s->ssl_cipher, s->ssl_session, and s->ssl_key_size. */
3137         if (num_of_vars) {
3138             unsigned int j;
3139
3140             s->attributes_names =
3141                 jk_pool_alloc(&private_data->p, num_of_vars * sizeof(char *));
3142             s->attributes_values =
3143                 jk_pool_alloc(&private_data->p, num_of_vars * sizeof(char *));
3144
3145             j = 0;
3146             for (i = 0; i < 9; i++) {
3147                 if (ssl_env_values[i]) {
3148                     s->attributes_names[j] = ssl_env_names[i];
3149                     s->attributes_values[j] = ssl_env_values[i];
3150                     j++;
3151                 }
3152             }
3153             s->num_attributes = num_of_vars;
3154             if (ssl_env_values[4] && ssl_env_values[4][0] == '1') {
3155                 CERT_CONTEXT_EX cc;
3156                 cc.cbAllocated = MAX_PACKET_SIZE;
3157                 cc.CertContext.pbCertEncoded = (BYTE *) huge_buf;
3158                 cc.CertContext.cbCertEncoded = 0;
3159
3160                 if (private_data->lpEcb->
3161                     ServerSupportFunction(private_data->lpEcb->ConnID,
3162                                           (DWORD) HSE_REQ_GET_CERT_INFO_EX,
3163                                           (LPVOID) & cc, NULL,
3164                                           NULL) != FALSE) {
3165                     jk_log(logger, JK_LOG_DEBUG,
3166                            "Client Certificate encoding:%d sz:%d flags:%ld",
3167                            cc.CertContext.
3168                            dwCertEncodingType & X509_ASN_ENCODING,
3169                            cc.CertContext.cbCertEncoded,
3170                            cc.dwCertificateFlags);
3171                     s->ssl_cert =
3172                         jk_pool_alloc(&private_data->p,
3173                                       base64_encode_cert_len(cc.CertContext.
3174                                                              cbCertEncoded));
3175
3176                     s->ssl_cert_len = base64_encode_cert(s->ssl_cert,
3177                                                          huge_buf,
3178                                                          cc.CertContext.
3179                                                          cbCertEncoded) - 1;
3180                 }
3181             }
3182         }
3183     }
3184
3185     huge_buf_sz = MAX_PACKET_SIZE;
3186     if (get_server_value(private_data->lpEcb,
3187 #ifndef USE_CGI_HEADERS
3188                          "ALL_RAW", huge_buf, huge_buf_sz)) {
3189 #else
3190                          "ALL_HTTP", huge_buf, huge_buf_sz)) {
3191 #endif
3192         unsigned int cnt = 0;
3193         char *tmp;
3194
3195         for (tmp = huge_buf; *tmp; tmp++) {
3196             if (*tmp == '\n') {
3197                 cnt++;
3198             }
3199         }
3200
3201         if (cnt) {
3202             char *headers_buf = huge_buf;
3203             unsigned int i;
3204             BOOL need_content_length_header = FALSE;
3205
3206             if (s->content_length == 0 && unknown_content_length == FALSE) {
3207                 /* Add content-length=0 only if really zero
3208                  */
3209                 need_content_length_header = TRUE;
3210             }
3211
3212             /* allocate an extra header slot in case we need to add a content-length header */
3213             s->headers_names =
3214                 jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
3215             s->headers_values =
3216                 jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
3217
3218             if (!s->headers_names || !s->headers_values || !headers_buf) {
3219                 JK_TRACE_EXIT(logger);
3220                 return JK_FALSE;
3221             }
3222
3223             for (i = 0, tmp = headers_buf; *tmp && i < cnt;) {
3224                 int real_header = JK_TRUE;
3225
3226 #ifdef USE_CGI_HEADERS
3227                 /* Skip the HTTP_ prefix to the beginning of the header name */
3228                 tmp += HTTP_HEADER_PREFIX_LEN;
3229 #endif
3230
3231                 if (!strnicmp(tmp, URI_HEADER_NAME, strlen(URI_HEADER_NAME))
3232                     || !strnicmp(tmp, WORKER_HEADER_NAME, strlen(WORKER_HEADER_NAME))
3233                     || !strnicmp(tmp, WORKER_HEADER_INDEX, strlen(WORKER_HEADER_INDEX))
3234                     || !strnicmp(tmp, QUERY_HEADER_NAME, strlen(QUERY_HEADER_NAME))) {
3235                     /* Skip redirector headers */
3236                     cnt--;
3237                     real_header = JK_FALSE;
3238                 }
3239                 else if (!strnicmp(tmp, CONTENT_LENGTH,
3240                                    sizeof(CONTENT_LENGTH) - 1)) {
3241                     need_content_length_header = FALSE;
3242                     
3243                     /* If the content-length is unknown
3244                      * or larger then 4Gb do not send it.
3245                      * IIS can also create a synthetic Content-Length header to make
3246                      * lpcbTotalBytes and the CONTENT_LENGTH server variable agree
3247                      * on small requests where the entire chunk encoded message is
3248                      * read into the available buffer.
3249                      */
3250                     if (unknown_content_length || s->is_chunked) {
3251                         if (JK_IS_DEBUG_LEVEL(logger)) {
3252                             jk_log(logger, JK_LOG_DEBUG,
3253                                    "Disregarding Content-Length in request - content is %s",
3254                                    s->is_chunked ? "chunked" : "unknown length");
3255                         }
3256                         cnt--;
3257                         real_header = JK_FALSE;
3258                     }
3259                     else {
3260                         s->headers_names[i] = tmp;
3261                     }
3262                 }
3263                 else if (!strnicmp(tmp, TOMCAT_TRANSLATE_HEADER_NAME,
3264                                    strlen(TOMCAT_TRANSLATE_HEADER_NAME))) {
3265                     s->headers_names[i] = TRANSLATE_HEADER_NAME_LC;
3266                 }
3267                 else {
3268                     s->headers_names[i] = tmp;
3269                 }
3270
3271                 while (':' != *tmp && *tmp) {
3272 #ifdef USE_CGI_HEADERS
3273                     if (real_header) {
3274                         if ('_' == *tmp) {
3275                             *tmp = '-';
3276                         }
3277                         else {
3278                             *tmp = JK_TOLOWER(*tmp);
3279                         }
3280                     }
3281 #endif
3282                     tmp++;
3283                 }
3284                 *tmp = '\0';
3285                 tmp++;
3286
3287                 /* Skip all the WS chars after the ':' to the beginning of the header value */
3288                 while (' ' == *tmp || '\t' == *tmp || '\v' == *tmp) {
3289                     tmp++;
3290                 }
3291
3292                 if (real_header) {
3293                     s->headers_values[i] = tmp;
3294                 }
3295
3296                 while (*tmp && *tmp != '\n' && *tmp != '\r') {
3297                     tmp++;
3298                 }
3299                 *tmp = '\0';
3300                 tmp++;
3301
3302                 /* skip CR LF */
3303                 while (*tmp == '\n' || *tmp == '\r') {
3304                     tmp++;
3305                 }
3306
3307                 if (real_header) {
3308                     if (JK_IS_DEBUG_LEVEL(logger)) {
3309                         jk_log(logger, JK_LOG_DEBUG, "Forwarding request header %s : %s",
3310                                s->headers_names[i], s->headers_values[i]);
3311                     }
3312                     i++;
3313                 }
3314             }
3315             /* Add a content-length = 0 header if needed.
3316              * Ajp13 assumes an absent content-length header means an unknown,
3317              * but non-zero length body.
3318              */
3319             if (need_content_length_header) {
3320                 if (JK_IS_DEBUG_LEVEL(logger)) {
3321                     jk_log(logger, JK_LOG_DEBUG, "Incoming request needs explicit Content-Length: 0 in AJP13");
3322                 }
3323                 s->headers_names[cnt] = "Content-Length";
3324                 s->headers_values[cnt] = "0";
3325                 cnt++;
3326             }
3327             s->num_headers = cnt;
3328         }
3329         else {
3330             /* We must have our two headers */
3331             JK_TRACE_EXIT(logger);
3332             return JK_FALSE;
3333         }
3334     }
3335     else {
3336         JK_TRACE_EXIT(logger);
3337         return JK_FALSE;
3338     }
3339
3340     /* Dump all connection param so we can trace what's going to
3341      * the remote tomcat
3342      */
3343     if (JK_IS_DEBUG_LEVEL(logger)) {
3344         jk_log(logger, JK_LOG_DEBUG,
3345                "Service protocol=%s method=%s host=%s addr=%s name=%s port=%d "
3346                "auth=%s user=%s uri=%s",
3347                STRNULL_FOR_NULL(s->protocol),
3348                STRNULL_FOR_NULL(s->method),
3349                STRNULL_FOR_NULL(s->remote_host),
3350                STRNULL_FOR_NULL(s->remote_addr),
3351                STRNULL_FOR_NULL(s->server_name),
3352                s->server_port,
3353                STRNULL_FOR_NULL(s->auth_type),
3354                STRNULL_FOR_NULL(s->remote_user),
3355                STRNULL_FOR_NULL(s->req_uri));
3356         jk_log(logger, JK_LOG_DEBUG,
3357                "Service request headers=%d attributes=%d "
3358                "chunked=%s content-length=%" JK_UINT64_T_FMT " available=%u",
3359                s->num_headers,
3360                s->num_attributes,
3361                (s->is_chunked == JK_TRUE) ? "yes" : "no",
3362                s->content_length,
3363                private_data->lpEcb->cbTotalBytes);
3364     }
3365
3366     JK_TRACE_EXIT(logger);
3367     return JK_TRUE;
3368 }
3369
3370 static int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
3371                             char *name, char *buf, DWORD bufsz)
3372 {
3373     DWORD sz = bufsz;
3374     buf[0]   = '\0';
3375     if (!lpEcb->GetServerVariable(lpEcb->ConnID, name,
3376                                   buf, (LPDWORD) &sz))
3377         return JK_FALSE;
3378
3379     if (sz <= bufsz)
3380         buf[sz-1] = '\0';
3381     return JK_TRUE;
3382 }
3383
3384 static const char begin_cert[] = "-----BEGIN CERTIFICATE-----\r\n";
3385
3386 static const char end_cert[] = "-----END CERTIFICATE-----\r\n";
3387
3388 static const char basis_64[] =
3389     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3390
3391 static int base64_encode_cert_len(int len)
3392 {
3393     int n = ((len + 2) / 3 * 4) + 1;    /* base64 encoded size */
3394     n += (n + 63 / 64) * 2;             /* add CRLF's */
3395     n += sizeof(begin_cert) + sizeof(end_cert) - 2;  /* add enclosing strings. */
3396     return n;
3397 }
3398
3399 static int base64_encode_cert(char *encoded,
3400                               const char *string, int len)
3401 {
3402     int i, c;
3403     char *p;
3404     const char *t;
3405
3406     p = encoded;
3407
3408     t = begin_cert;
3409     while (*t != '\0')
3410         *p++ = *t++;
3411
3412     c = 0;
3413     for (i = 0; i < len - 2; i += 3) {
3414         *p++ = basis_64[(string[i] >> 2) & 0x3F];
3415         *p++ = basis_64[((string[i] & 0x3) << 4) |
3416                         ((int)(string[i + 1] & 0xF0) >> 4)];
3417         *p++ = basis_64[((string[i + 1] & 0xF) << 2) |
3418                         ((int)(string[i + 2] & 0xC0) >> 6)];
3419         *p++ = basis_64[string[i + 2] & 0x3F];
3420         c += 4;
3421         if (c >= 64) {
3422             *p++ = '\r';
3423             *p++ = '\n';
3424             c = 0;
3425         }
3426     }
3427     if (i < len) {
3428         *p++ = basis_64[(string[i] >> 2) & 0x3F];
3429         if (i == (len - 1)) {
3430             *p++ = basis_64[((string[i] & 0x3) << 4)];
3431             *p++ = '=';
3432         }
3433         else {
3434             *p++ = basis_64[((string[i] & 0x3) << 4) |
3435                             ((int)(string[i + 1] & 0xF0) >> 4)];
3436             *p++ = basis_64[((string[i + 1] & 0xF) << 2)];
3437         }
3438         *p++ = '=';
3439         c++;
3440     }
3441     if (c != 0) {
3442         *p++ = '\r';
3443         *p++ = '\n';
3444     }
3445
3446     t = end_cert;
3447     while (*t != '\0')
3448         *p++ = *t++;
3449
3450     *p++ = '\0';
3451     return (int)(p - encoded);
3452 }
3453
3454 /**
3455 * Determine version info and the primary notification event
3456 */
3457 static int get_iis_info(iis_info_t* iis_info)
3458 {
3459     HKEY hkey;
3460     long rc;
3461     int rv = JK_FALSE;
3462
3463     iis_info->major = 0;
3464     iis_info->minor = 0;
3465     iis_info->filter_notify_event = SF_NOTIFY_PREPROC_HEADERS;
3466
3467     /* Retrieve the IIS version Major/Minor */
3468     rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
3469                       W3SVC_REGISTRY_KEY, (DWORD) 0, KEY_READ, &hkey);
3470     if (ERROR_SUCCESS == rc) {
3471         if (get_registry_config_number(hkey, "MajorVersion", &iis_info->major) == JK_TRUE) {
3472 #ifdef AUTOMATIC_AUTH_NOTIFICATION
3473             if (iis_info->major > 4)
3474 #else
3475             if (use_auth_notification_flags && iis_info->major > 4)
3476 #endif
3477                 iis_info->filter_notify_event = SF_NOTIFY_AUTH_COMPLETE;
3478             if (get_registry_config_number(hkey, "MinorVersion", &iis_info->minor) == JK_TRUE) {
3479
3480 #ifdef AUTOMATIC_AUTH_NOTIFICATION
3481                 /* SF_NOTIFY_AUTH_COMPLETE causes redirect failures
3482                  * (ERROR_INVALID_PARAMETER) on IIS 5.1 with OPTIONS/PUT
3483                  * and is only available from IIS 5+
3484                  */
3485                 if (iis_info->major == 5 && iis_info->minor == 1) {
3486                     iis_info->filter_notify_event = SF_NOTIFY_PREPROC_HEADERS;
3487                 }
3488 #endif
3489                 rv = JK_TRUE;
3490             }
3491         }
3492     }
3493     CloseHandle(hkey);
3494     return rv;
3495 }