~ubuntu-branches/ubuntu/oneiric/libapache-mod-jk/oneiric

« back to all changes in this revision

Viewing changes to jk/native/iis/jk_isapi_plugin.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2006-08-05 16:30:53 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060805163053-myf66gm6j1a21ps6
Tags: 1:1.2.18-1ubuntu1
Merge from Debian unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 *  Copyright 1999-2004 The Apache Software Foundation
3
 
 *
4
 
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 
 *  you may not use this file except in compliance with the License.
6
 
 *  You may obtain a copy of the License at
7
 
 *
8
 
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 
 *
10
 
 *  Unless required by applicable law or agreed to in writing, software
11
 
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 
 *  See the License for the specific language governing permissions and
14
 
 *  limitations under the License.
15
 
 */
16
 
 
17
 
/***************************************************************************
18
 
 * Description: ISAPI plugin for IIS/PWS                                   *
19
 
 * Author:      Gal Shachor <shachor@il.ibm.com>                           *
20
 
 * Author:      Larry Isaacs <larryi@apache.org>                           *
21
 
 * Author:      Ignacio J. Ortega <nacho@apache.org>                       *
22
 
 * Author:      Mladen Turk <mturk@apache.org>                             *
23
 
 * Version:     $Revision: 1.49 $                                          *
24
 
 ***************************************************************************/
25
 
 
26
 
// This define is needed to include wincrypt,h, needed to get client certificates
27
 
#define _WIN32_WINNT 0x0400
28
 
 
29
 
#include <httpext.h>
30
 
#include <httpfilt.h>
31
 
#include <wininet.h>
32
 
 
33
 
#include "jk_global.h"
34
 
#include "jk_util.h"
35
 
#include "jk_map.h"
36
 
#include "jk_pool.h"
37
 
#include "jk_service.h"
38
 
#include "jk_worker.h"
39
 
#include "jk_uri_worker_map.h"
40
 
#include "jk_shm.h"
41
 
 
42
 
#define VERSION_STRING "Jakarta/ISAPI/" JK_VERSTRING
43
 
 
44
 
#define DEFAULT_WORKER_NAME ("ajp13")
45
 
/*
46
 
 * We use special headers to pass values from the filter to the
47
 
 * extension. These values are:
48
 
 *
49
 
 * 1. The real URI before redirection took place
50
 
 * 2. The name of the worker to be used.
51
 
 * 3. The contents of the Translate header, if any
52
 
 *
53
 
 */
54
 
#define URI_HEADER_NAME              ("TOMCATURI:")
55
 
#define QUERY_HEADER_NAME            ("TOMCATQUERY:")
56
 
#define WORKER_HEADER_NAME           ("TOMCATWORKER:")
57
 
#define TOMCAT_TRANSLATE_HEADER_NAME ("TOMCATTRANSLATE:")
58
 
#define CONTENT_LENGTH               ("CONTENT_LENGTH:")
59
 
 
60
 
#define HTTP_URI_HEADER_NAME         ("HTTP_TOMCATURI")
61
 
#define HTTP_QUERY_HEADER_NAME       ("HTTP_TOMCATQUERY")
62
 
#define HTTP_WORKER_HEADER_NAME      ("HTTP_TOMCATWORKER")
63
 
 
64
 
#define REGISTRY_LOCATION       ("Software\\Apache Software Foundation\\Jakarta Isapi Redirector\\1.0")
65
 
#define EXTENSION_URI_TAG       ("extension_uri")
66
 
 
67
 
#define URI_SELECT_TAG              ("uri_select")
68
 
#define URI_SELECT_PARSED_VERB      ("parsed")
69
 
#define URI_SELECT_UNPARSED_VERB    ("unparsed")
70
 
#define URI_SELECT_ESCAPED_VERB     ("escaped")
71
 
 
72
 
#define BAD_REQUEST     -1
73
 
#define BAD_PATH        -2
74
 
#define MAX_SERVERNAME  128
75
 
 
76
 
#define HTML_ERROR_400          "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">"  \
77
 
                                "<HTML><HEAD><TITLE>Bad request!</TITLE></HEAD>"                    \
78
 
                                "<BODY><H1>Bad request!</H1><DL><DD>\n"                             \
79
 
                                "Your browser (or proxy) sent a request that "                      \
80
 
                                "this server could not understand.</DL></DD></BODY></HTML>"
81
 
 
82
 
#define HTML_ERROR_404          "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">"  \
83
 
                                "<HTML><HEAD><TITLE>Object not found!</TITLE></HEAD>"               \
84
 
                                "<BODY><H1>The requested URL was not found on this server"          \
85
 
                                "</H1><DL><DD>\nIf you entered the URL manually please check your"  \
86
 
                                "spelling and try again.</DL></DD></BODY></HTML>"
87
 
 
88
 
 
89
 
#define JK_TOLOWER(x)   ((char)tolower((BYTE)(x)))
90
 
 
91
 
#define GET_SERVER_VARIABLE_VALUE(name, place)          \
92
 
  do {                                                  \
93
 
    (place) = NULL;                                     \
94
 
    huge_buf_sz = sizeof(huge_buf);                     \
95
 
    if (get_server_value(private_data->lpEcb,           \
96
 
                        (name),                         \
97
 
                        huge_buf,                       \
98
 
                        huge_buf_sz)) {                 \
99
 
        (place) = jk_pool_strdup(&private_data->p,      \
100
 
                                 huge_buf);             \
101
 
  } } while(0)
102
 
 
103
 
#define GET_SERVER_VARIABLE_VALUE_INT(name, place, def)     \
104
 
  do {                                                      \
105
 
    huge_buf_sz = sizeof(huge_buf);                         \
106
 
    if (get_server_value(private_data->lpEcb,               \
107
 
                        (name),                             \
108
 
                        huge_buf,                           \
109
 
                        huge_buf_sz)) {                     \
110
 
        (place) = atoi(huge_buf);                           \
111
 
        if (0 == (place)) {                                 \
112
 
            (place) = def;                                  \
113
 
        }                                                   \
114
 
    } else {                                                \
115
 
        (place) = def;                                      \
116
 
  } } while(0)
117
 
 
118
 
static char ini_file_name[MAX_PATH];
119
 
static int using_ini_file = JK_FALSE;
120
 
static int is_inited = JK_FALSE;
121
 
static int is_mapread = JK_FALSE;
122
 
static int iis5 = -1;
123
 
 
124
 
static jk_uri_worker_map_t *uw_map = NULL;
125
 
static jk_logger_t *logger = NULL;
126
 
static char *SERVER_NAME = "SERVER_NAME";
127
 
static char *SERVER_SOFTWARE = "SERVER_SOFTWARE";
128
 
static char *CONTENT_TYPE = "Content-Type:text/html\r\n\r\n";
129
 
 
130
 
static char extension_uri[INTERNET_MAX_URL_LENGTH] =
131
 
    "/jakarta/isapi_redirect.dll";
132
 
static char log_file[MAX_PATH * 2];
133
 
static int log_level = JK_LOG_EMERG_LEVEL;
134
 
static char worker_file[MAX_PATH * 2];
135
 
static char worker_mount_file[MAX_PATH * 2] = {0};
136
 
 
137
 
#define URI_SELECT_OPT_PARSED       0
138
 
#define URI_SELECT_OPT_UNPARSED     1
139
 
#define URI_SELECT_OPT_ESCAPED      2
140
 
 
141
 
static int uri_select_option = URI_SELECT_OPT_PARSED;
142
 
 
143
 
static jk_worker_env_t worker_env;
144
 
 
145
 
typedef struct isapi_private_data_t isapi_private_data_t;
146
 
struct isapi_private_data_t
147
 
{
148
 
    jk_pool_t p;
149
 
 
150
 
    int request_started;
151
 
    unsigned int bytes_read_so_far;
152
 
    LPEXTENSION_CONTROL_BLOCK lpEcb;
153
 
};
154
 
 
155
 
typedef struct isapi_log_data_t isapi_log_data_t;
156
 
struct isapi_log_data_t {
157
 
    char uri[INTERNET_MAX_URL_LENGTH];
158
 
    char query[INTERNET_MAX_URL_LENGTH];
159
 
};
160
 
 
161
 
static int JK_METHOD start_response(jk_ws_service_t *s,
162
 
                                    int status,
163
 
                                    const char *reason,
164
 
                                    const char *const *header_names,
165
 
                                    const char *const *header_values,
166
 
                                    unsigned int num_of_headers);
167
 
 
168
 
static int JK_METHOD read(jk_ws_service_t *s,
169
 
                          void *b, unsigned int l, unsigned int *a);
170
 
 
171
 
static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l);
172
 
 
173
 
static int init_ws_service(isapi_private_data_t * private_data,
174
 
                           jk_ws_service_t *s, char **worker_name);
175
 
 
176
 
static int init_jk(char *serverName);
177
 
 
178
 
static int initialize_extension(void);
179
 
 
180
 
static int read_registry_init_data(void);
181
 
 
182
 
static int get_registry_config_parameter(HKEY hkey,
183
 
                                         const char *tag, char *b, DWORD sz);
184
 
 
185
 
 
186
 
static int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
187
 
                            char *name,
188
 
                            char *buf, DWORD bufsz);
189
 
 
190
 
static int base64_encode_cert_len(int len);
191
 
 
192
 
static int base64_encode_cert(char *encoded,
193
 
                              const char *string, int len);
194
 
 
195
 
 
196
 
static char x2c(const char *what)
197
 
{
198
 
    register char digit;
199
 
 
200
 
    digit =
201
 
        ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
202
 
    digit *= 16;
203
 
    digit +=
204
 
        (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
205
 
    return (digit);
206
 
}
207
 
 
208
 
static int unescape_url(char *url)
209
 
{
210
 
    register int x, y, badesc, badpath;
211
 
 
212
 
    badesc = 0;
213
 
    badpath = 0;
214
 
    for (x = 0, y = 0; url[y]; ++x, ++y) {
215
 
        if (url[y] != '%')
216
 
            url[x] = url[y];
217
 
        else {
218
 
            if (!isxdigit(url[y + 1]) || !isxdigit(url[y + 2])) {
219
 
                badesc = 1;
220
 
                url[x] = '%';
221
 
            }
222
 
            else {
223
 
                url[x] = x2c(&url[y + 1]);
224
 
                y += 2;
225
 
                if (url[x] == '/' || url[x] == '\0')
226
 
                    badpath = 1;
227
 
            }
228
 
        }
229
 
    }
230
 
    url[x] = '\0';
231
 
    if (badesc)
232
 
        return BAD_REQUEST;
233
 
    else if (badpath)
234
 
        return BAD_PATH;
235
 
    else
236
 
        return 0;
237
 
}
238
 
 
239
 
static void getparents(char *name)
240
 
{
241
 
    int l, w;
242
 
 
243
 
    /* Four paseses, as per RFC 1808 */
244
 
    /* a) remove ./ path segments */
245
 
 
246
 
    for (l = 0, w = 0; name[l] != '\0';) {
247
 
        if (name[l] == '.' && name[l + 1] == '/'
248
 
            && (l == 0 || name[l - 1] == '/'))
249
 
            l += 2;
250
 
        else
251
 
            name[w++] = name[l++];
252
 
    }
253
 
 
254
 
    /* b) remove trailing . path, segment */
255
 
    if (w == 1 && name[0] == '.')
256
 
        w--;
257
 
    else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/')
258
 
        w--;
259
 
    name[w] = '\0';
260
 
 
261
 
    /* c) remove all xx/../ segments. (including leading ../ and /../) */
262
 
    l = 0;
263
 
 
264
 
    while (name[l] != '\0') {
265
 
        if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' &&
266
 
            (l == 0 || name[l - 1] == '/')) {
267
 
            register int m = l + 3, n;
268
 
 
269
 
            l = l - 2;
270
 
            if (l >= 0) {
271
 
                while (l >= 0 && name[l] != '/')
272
 
                    l--;
273
 
                l++;
274
 
            }
275
 
            else
276
 
                l = 0;
277
 
            n = l;
278
 
            while ((name[n] = name[m]) != '\0') {
279
 
                n++;
280
 
                m++;
281
 
            }
282
 
        }
283
 
        else
284
 
            ++l;
285
 
    }
286
 
 
287
 
    /* d) remove trailing xx/.. segment. */
288
 
    if (l == 2 && name[0] == '.' && name[1] == '.')
289
 
        name[0] = '\0';
290
 
    else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
291
 
             && name[l - 3] == '/') {
292
 
        l = l - 4;
293
 
        if (l >= 0) {
294
 
            while (l >= 0 && name[l] != '/')
295
 
                l--;
296
 
            l++;
297
 
        }
298
 
        else
299
 
            l = 0;
300
 
        name[l] = '\0';
301
 
    }
302
 
}
303
 
 
304
 
/* Apache code to escape a URL */
305
 
 
306
 
#define T_OS_ESCAPE_PATH    (4)
307
 
 
308
 
static const BYTE test_char_table[256] = {
309
 
     0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14,
310
 
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
311
 
    14,  0,  7,  6,  1,  6,  1,  1,  9,  9,  1,  0,  8,  0,  0, 10,
312
 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  8, 15, 15,  8, 15, 15,
313
 
     8,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
314
 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 15, 15, 15,  7,  0,
315
 
     7,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
316
 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 15,  7, 15,  1, 14,
317
 
     6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
318
 
     6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
319
 
     6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
320
 
     6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
321
 
     6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
322
 
     6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
323
 
     6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
324
 
     6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6
325
 
};
326
 
 
327
 
#define TEST_CHAR(c, f) (test_char_table[(unsigned int)(c)] & (f))
328
 
 
329
 
static const char c2x_table[] = "0123456789abcdef";
330
 
 
331
 
static BYTE *c2x(unsigned int what, BYTE *where)
332
 
{
333
 
    *where++ = '%';
334
 
    *where++ = c2x_table[what >> 4];
335
 
    *where++ = c2x_table[what & 0xf];
336
 
    return where;
337
 
}
338
 
 
339
 
static char *status_reason(int status)
340
 
{
341
 
    static struct reasons {
342
 
        int status;
343
 
        char *reason;
344
 
    } *r, reasons[] = {
345
 
        { 100, "Continue" },
346
 
        { 101, "Switching Protocols" },
347
 
        { 200, "OK" },
348
 
        { 201, "Created" },
349
 
        { 202, "Accepted" },
350
 
        { 203, "Non-Authoritative Information" },
351
 
        { 204, "No Content" },
352
 
        { 205, "Reset Content" },
353
 
        { 206, "Partial Content" },
354
 
        { 300, "Multiple Choices" },
355
 
        { 301, "Moved Permanently" },
356
 
        { 302, "Moved Temporarily" },
357
 
        { 303, "See Other" },
358
 
        { 304, "Not Modified" },
359
 
        { 305, "Use Proxy" },
360
 
        { 400, "Bad Request" },
361
 
        { 401, "Unauthorized" },
362
 
        { 402, "Payment Required" },
363
 
        { 403, "Forbidden" },
364
 
        { 404, "Not Found" },
365
 
        { 405, "Method Not Allowed" },
366
 
        { 406, "Not Acceptable" },
367
 
        { 407, "Proxy Authentication Required" },
368
 
        { 408, "Request Timeout" },
369
 
        { 409, "Conflict" },
370
 
        { 410, "Gone" },
371
 
        { 411, "Length Required" },
372
 
        { 412, "Precondition Failed" },
373
 
        { 413, "Request Entity Too Large" },
374
 
        { 414, "Request-URI Too Long" },
375
 
        { 415, "Unsupported Media Type" },
376
 
        { 500, "Internal Server Error" },
377
 
        { 501, "Not Implemented" },
378
 
        { 502, "Bad Gateway" },
379
 
        { 503, "Service Unavailable" },
380
 
        { 504, "Gateway Timeout" },
381
 
        { 505, "HTTP Version Not Supported" },
382
 
        { 000, NULL}
383
 
    };
384
 
 
385
 
    r = reasons;
386
 
    while (r->status <= status)
387
 
        if (r->status == status)
388
 
            return r->reason;
389
 
        else
390
 
            r++;
391
 
    return "No Reason";
392
 
}
393
 
 
394
 
static int escape_url(const char *path, char *dest, int destsize)
395
 
{
396
 
    const BYTE *s = (const BYTE *)path;
397
 
    BYTE *d = (BYTE *)dest;
398
 
    BYTE *e = d + destsize - 1;
399
 
    BYTE *ee = d + destsize - 3;
400
 
 
401
 
    while (*s) {
402
 
        if (TEST_CHAR(*s, T_OS_ESCAPE_PATH)) {
403
 
            if (d >= ee)
404
 
                return JK_FALSE;
405
 
            d = c2x(*s, d);
406
 
        }
407
 
        else {
408
 
            if (d >= e)
409
 
                return JK_FALSE;
410
 
            *d++ = *s;
411
 
        }
412
 
        ++s;
413
 
    }
414
 
    *d = '\0';
415
 
    return JK_TRUE;
416
 
}
417
 
 
418
 
/*
419
 
 * Find the first occurrence of find in s.
420
 
 */
421
 
static char *stristr(const char *s, const char *find)
422
 
{
423
 
    char c, sc;
424
 
    size_t len;
425
 
 
426
 
    if ((c = tolower((unsigned char)(*find++))) != 0) {
427
 
        len = strlen(find);
428
 
        do {
429
 
            do {
430
 
                if ((sc = tolower((unsigned char)(*s++))) == 0)
431
 
                    return (NULL);
432
 
            } while (sc != c);
433
 
        } while (strnicmp(s, find, len) != 0);
434
 
        s--;
435
 
    }
436
 
    return ((char *)s);
437
 
}
438
 
 
439
 
static int uri_is_web_inf(const char *uri)
440
 
{
441
 
    if (stristr(uri, "web-inf")) {
442
 
        return JK_TRUE;
443
 
    }
444
 
    if (stristr(uri, "meta-inf")) {
445
 
        return JK_TRUE;
446
 
    }
447
 
 
448
 
    return JK_FALSE;
449
 
}
450
 
 
451
 
static void write_error_response(PHTTP_FILTER_CONTEXT pfc, char *status,
452
 
                                 char *msg)
453
 
{
454
 
    DWORD len = (DWORD)strlen(msg);
455
 
 
456
 
    /* reject !!! */
457
 
    pfc->AddResponseHeaders(pfc, CONTENT_TYPE, 0);
458
 
    pfc->ServerSupportFunction(pfc,
459
 
                               SF_REQ_SEND_RESPONSE_HEADER,
460
 
                               status, 0, 0);
461
 
    pfc->WriteClient(pfc, msg, &len, 0);
462
 
}
463
 
 
464
 
 
465
 
static int JK_METHOD start_response(jk_ws_service_t *s,
466
 
                                    int status,
467
 
                                    const char *reason,
468
 
                                    const char *const *header_names,
469
 
                                    const char *const *header_values,
470
 
                                    unsigned int num_of_headers)
471
 
{
472
 
    static char crlf[3] = { (char)13, (char)10, '\0' };
473
 
 
474
 
    JK_TRACE_ENTER(logger);
475
 
    if (status < 100 || status > 1000) {
476
 
        jk_log(logger, JK_LOG_ERROR,
477
 
               "invalid status %d",
478
 
               status);
479
 
        JK_TRACE_EXIT(logger);
480
 
        return JK_FALSE;
481
 
    }
482
 
 
483
 
    if (s && s->ws_private) {
484
 
        isapi_private_data_t *p = s->ws_private;
485
 
        if (!p->request_started) {
486
 
            size_t len_of_status;
487
 
            char *status_str;
488
 
            char *headers_str;
489
 
 
490
 
            p->request_started = JK_TRUE;
491
 
 
492
 
            /*
493
 
             * Create the status line
494
 
             */
495
 
            if (!reason) {
496
 
                reason = status_reason(status);
497
 
            }
498
 
            status_str = (char *)_alloca((6 + strlen(reason)) * sizeof(char));
499
 
            sprintf(status_str, "%d %s", status, reason);
500
 
            len_of_status = strlen(status_str);
501
 
 
502
 
            /*
503
 
             * Create response headers string
504
 
             */
505
 
            if (num_of_headers) {
506
 
                size_t i, len_of_headers;
507
 
                for (i = 0, len_of_headers = 0; i < num_of_headers; i++) {
508
 
                    len_of_headers += strlen(header_names[i]);
509
 
                    len_of_headers += strlen(header_values[i]);
510
 
                    len_of_headers += 4;        /* extra for colon, space and crlf */
511
 
                }
512
 
 
513
 
                len_of_headers += 3;    /* crlf and terminating null char */
514
 
                headers_str = (char *)_alloca(len_of_headers * sizeof(char));
515
 
                headers_str[0] = '\0';
516
 
 
517
 
                for (i = 0; i < num_of_headers; i++) {
518
 
                    strcat(headers_str, header_names[i]);
519
 
                    strcat(headers_str, ": ");
520
 
                    strcat(headers_str, header_values[i]);
521
 
                    strcat(headers_str, crlf);
522
 
                }
523
 
                strcat(headers_str, crlf);
524
 
            }
525
 
            else {
526
 
                headers_str = crlf;
527
 
            }
528
 
 
529
 
            if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
530
 
                                                 HSE_REQ_SEND_RESPONSE_HEADER,
531
 
                                                 status_str,
532
 
                                                 (LPDWORD) &len_of_status,
533
 
                                                 (LPDWORD) headers_str)) {
534
 
                jk_log(logger, JK_LOG_ERROR,
535
 
                       "HSE_REQ_SEND_RESPONSE_HEADER failed");
536
 
                JK_TRACE_EXIT(logger);
537
 
                return JK_FALSE;
538
 
            }
539
 
        }
540
 
        JK_TRACE_EXIT(logger);
541
 
        return JK_TRUE;
542
 
 
543
 
    }
544
 
 
545
 
    JK_LOG_NULL_PARAMS(logger);
546
 
    JK_TRACE_EXIT(logger);
547
 
    return JK_FALSE;
548
 
}
549
 
 
550
 
static int JK_METHOD read(jk_ws_service_t *s,
551
 
                          void *b, unsigned int l, unsigned int *a)
552
 
{
553
 
    JK_TRACE_ENTER(logger);
554
 
 
555
 
    if (s && s->ws_private && b && a) {
556
 
        isapi_private_data_t *p = s->ws_private;
557
 
 
558
 
        *a = 0;
559
 
        if (l) {
560
 
            char *buf = b;
561
 
            DWORD already_read = p->lpEcb->cbAvailable - p->bytes_read_so_far;
562
 
 
563
 
            if (already_read >= l) {
564
 
                memcpy(buf, p->lpEcb->lpbData + p->bytes_read_so_far, l);
565
 
                p->bytes_read_so_far += l;
566
 
                *a = l;
567
 
            }
568
 
            else {
569
 
                /*
570
 
                 * Try to copy what we already have
571
 
                 */
572
 
                if (already_read > 0) {
573
 
                    memcpy(buf, p->lpEcb->lpbData + p->bytes_read_so_far,
574
 
                           already_read);
575
 
                    buf += already_read;
576
 
                    l -= already_read;
577
 
                    p->bytes_read_so_far = p->lpEcb->cbAvailable;
578
 
 
579
 
                    *a = already_read;
580
 
                }
581
 
 
582
 
                /*
583
 
                 * Now try to read from the client ...
584
 
                 */
585
 
                if (p->lpEcb->ReadClient(p->lpEcb->ConnID, buf, (LPDWORD)&l)) {
586
 
                    *a += l;
587
 
                }
588
 
                else {
589
 
                    jk_log(logger, JK_LOG_ERROR,
590
 
                           "ReadClient failed with %08x", GetLastError());
591
 
                    JK_TRACE_EXIT(logger);
592
 
                    return JK_FALSE;
593
 
                }
594
 
            }
595
 
        }
596
 
        JK_TRACE_EXIT(logger);
597
 
        return JK_TRUE;
598
 
    }
599
 
 
600
 
    JK_LOG_NULL_PARAMS(logger);
601
 
    JK_TRACE_EXIT(logger);
602
 
    return JK_FALSE;
603
 
}
604
 
 
605
 
static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l)
606
 
{
607
 
    JK_TRACE_ENTER(logger);
608
 
 
609
 
    if (s && s->ws_private && b) {
610
 
        isapi_private_data_t *p = s->ws_private;
611
 
 
612
 
        if (l) {
613
 
            unsigned int written = 0;
614
 
            char *buf = (char *)b;
615
 
 
616
 
            if (!p->request_started) {
617
 
                start_response(s, 200, NULL, NULL, NULL, 0);
618
 
            }
619
 
 
620
 
            while (written < l) {
621
 
                DWORD try_to_write = l - written;
622
 
                if (!p->lpEcb->WriteClient(p->lpEcb->ConnID,
623
 
                                           buf + written, &try_to_write, 0)) {
624
 
                    jk_log(logger, JK_LOG_ERROR,
625
 
                           "WriteClient failed with %08x", GetLastError());
626
 
                    JK_TRACE_EXIT(logger);
627
 
                    return JK_FALSE;
628
 
                }
629
 
                written += try_to_write;
630
 
            }
631
 
        }
632
 
 
633
 
        JK_TRACE_EXIT(logger);
634
 
        return JK_TRUE;
635
 
 
636
 
    }
637
 
 
638
 
    JK_LOG_NULL_PARAMS(logger);
639
 
    JK_TRACE_EXIT(logger);
640
 
    return JK_FALSE;
641
 
}
642
 
 
643
 
BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
644
 
{
645
 
    ULONG http_filter_revision = HTTP_FILTER_REVISION;
646
 
 
647
 
    pVer->dwFilterVersion = pVer->dwServerFilterVersion;
648
 
 
649
 
    if (pVer->dwFilterVersion > http_filter_revision) {
650
 
        pVer->dwFilterVersion = http_filter_revision;
651
 
    }
652
 
 
653
 
    pVer->dwFlags = SF_NOTIFY_ORDER_HIGH |
654
 
                    SF_NOTIFY_SECURE_PORT |
655
 
                    SF_NOTIFY_NONSECURE_PORT |
656
 
                    SF_NOTIFY_PREPROC_HEADERS |
657
 
                    SF_NOTIFY_LOG |
658
 
                    SF_NOTIFY_AUTH_COMPLETE;
659
 
 
660
 
    strcpy(pVer->lpszFilterDesc, VERSION_STRING);
661
 
 
662
 
    if (!is_inited) {
663
 
        return initialize_extension();
664
 
    }
665
 
 
666
 
    return TRUE;
667
 
}
668
 
 
669
 
DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,
670
 
                            DWORD dwNotificationType, LPVOID pvNotification)
671
 
{
672
 
    /* Initialise jk */
673
 
    if (is_inited && !is_mapread) {
674
 
        char serverName[MAX_SERVERNAME];
675
 
        DWORD dwLen = sizeof(serverName);
676
 
 
677
 
        if (pfc->GetServerVariable(pfc, SERVER_NAME, serverName, &dwLen)) {
678
 
            if (dwLen > 0)
679
 
                serverName[dwLen - 1] = '\0';
680
 
            if (init_jk(serverName))
681
 
                is_mapread = JK_TRUE;
682
 
        }
683
 
        /* If we can't read the map we become dormant */
684
 
        if (!is_mapread)
685
 
            is_inited = JK_FALSE;
686
 
    }
687
 
 
688
 
    if (is_inited && (iis5 < 0)) {
689
 
        char serverSoftware[256];
690
 
        DWORD dwLen = sizeof(serverSoftware);
691
 
        iis5 = 0;
692
 
        if (pfc->
693
 
            GetServerVariable(pfc, SERVER_SOFTWARE, serverSoftware, &dwLen)) {
694
 
            iis5 = (atof(serverSoftware + 14) >= 5.0);
695
 
            if (iis5) {
696
 
                jk_log(logger, JK_LOG_DEBUG, "Detected IIS >= 5.0");
697
 
            }
698
 
            else {
699
 
                jk_log(logger, JK_LOG_DEBUG, "Detected IIS < 5.0");
700
 
            }
701
 
        }
702
 
    }
703
 
 
704
 
    if (is_inited &&
705
 
        (((SF_NOTIFY_PREPROC_HEADERS == dwNotificationType) && !iis5) ||
706
 
         ((SF_NOTIFY_AUTH_COMPLETE == dwNotificationType) && iis5)
707
 
        )
708
 
        ) {
709
 
        char uri[INTERNET_MAX_URL_LENGTH];
710
 
        char snuri[INTERNET_MAX_URL_LENGTH] = "/";
711
 
        char Host[INTERNET_MAX_URL_LENGTH] = "";
712
 
        char Port[INTERNET_MAX_URL_LENGTH] = "";
713
 
        char Translate[INTERNET_MAX_URL_LENGTH];
714
 
        BOOL(WINAPI * GetHeader)
715
 
            (struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
716
 
             LPVOID lpvBuffer, LPDWORD lpdwSize);
717
 
        BOOL(WINAPI * SetHeader)
718
 
            (struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
719
 
             LPSTR lpszValue);
720
 
        BOOL(WINAPI * AddHeader)
721
 
            (struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
722
 
             LPSTR lpszValue);
723
 
        char *query;
724
 
        DWORD sz = sizeof(uri);
725
 
        DWORD szHost = sizeof(Host);
726
 
        DWORD szPort = sizeof(Port);
727
 
        DWORD szTranslate = sizeof(Translate);
728
 
 
729
 
        if (iis5) {
730
 
            GetHeader =
731
 
                ((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->GetHeader;
732
 
            SetHeader =
733
 
                ((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->SetHeader;
734
 
            AddHeader =
735
 
                ((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->AddHeader;
736
 
        }
737
 
        else {
738
 
            GetHeader =
739
 
                ((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->GetHeader;
740
 
            SetHeader =
741
 
                ((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->SetHeader;
742
 
            AddHeader =
743
 
                ((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->AddHeader;
744
 
        }
745
 
 
746
 
        if (JK_IS_DEBUG_LEVEL(logger))
747
 
            jk_log(logger, JK_LOG_DEBUG, "Filter started");
748
 
 
749
 
        /*
750
 
         * Just in case somebody set these headers in the request!
751
 
         */
752
 
        SetHeader(pfc, URI_HEADER_NAME, NULL);
753
 
        SetHeader(pfc, QUERY_HEADER_NAME, NULL);
754
 
        SetHeader(pfc, WORKER_HEADER_NAME, NULL);
755
 
        SetHeader(pfc, TOMCAT_TRANSLATE_HEADER_NAME, NULL);
756
 
 
757
 
        if (!GetHeader(pfc, "url", (LPVOID) uri, (LPDWORD) & sz)) {
758
 
            jk_log(logger, JK_LOG_ERROR,
759
 
                   "error while getting the url");
760
 
            return SF_STATUS_REQ_ERROR;
761
 
        }
762
 
 
763
 
        if (strlen(uri)) {
764
 
            int rc;
765
 
            const char *worker = NULL;
766
 
            query = strchr(uri, '?');
767
 
            if (query) {
768
 
                *query++ = '\0';
769
 
            }
770
 
 
771
 
            rc = unescape_url(uri);
772
 
            if (rc == BAD_REQUEST) {
773
 
                jk_log(logger, JK_LOG_ERROR,
774
 
                       "[%s] contains one or more invalid escape sequences.",
775
 
                       uri);
776
 
                write_error_response(pfc, "400 Bad Request",
777
 
                                     HTML_ERROR_400);
778
 
                return SF_STATUS_REQ_FINISHED;
779
 
            }
780
 
            else if (rc == BAD_PATH) {
781
 
                jk_log(logger, JK_LOG_EMERG,
782
 
                       "[%s] contains forbidden escape sequences.",
783
 
                       uri);
784
 
                write_error_response(pfc, "404 Not Found",
785
 
                                     HTML_ERROR_404);
786
 
                return SF_STATUS_REQ_FINISHED;
787
 
            }
788
 
            getparents(uri);
789
 
            if (pfc->
790
 
                GetServerVariable(pfc, SERVER_NAME, (LPVOID) Host,
791
 
                                  (LPDWORD) & szHost)) {
792
 
                if (szHost > 0) {
793
 
                    Host[szHost - 1] = '\0';
794
 
                }
795
 
            }
796
 
            Port[0] = '\0';
797
 
            if (pfc->
798
 
                GetServerVariable(pfc, "SERVER_PORT", (LPVOID) Port,
799
 
                                  (LPDWORD) & szPort)) {
800
 
                if (szPort > 0) {
801
 
                    Port[szPort - 1] = '\0';
802
 
                }
803
 
            }
804
 
            szPort = atoi(Port);
805
 
            if (szPort != 80 && szPort != 443 && szHost > 0) {
806
 
                strcat(Host, ":");
807
 
                strcat(Host, Port);
808
 
            }
809
 
            if (szHost > 0) {
810
 
                strcat(snuri, Host);
811
 
                strcat(snuri, uri);
812
 
                if (JK_IS_DEBUG_LEVEL(logger))
813
 
                    jk_log(logger, JK_LOG_DEBUG,
814
 
                           "Virtual Host redirection of %s",
815
 
                           snuri);
816
 
                worker = map_uri_to_worker(uw_map, snuri, logger);
817
 
            }
818
 
            if (!worker) {
819
 
                if (JK_IS_DEBUG_LEVEL(logger))
820
 
                    jk_log(logger, JK_LOG_DEBUG,
821
 
                           "Default redirection of %s",
822
 
                           uri);
823
 
                worker = map_uri_to_worker(uw_map, uri, logger);
824
 
            }
825
 
            /*
826
 
             * Check if somebody is feading us with his own TOMCAT data headers.
827
 
             * We reject such postings !
828
 
             */
829
 
            if (JK_IS_DEBUG_LEVEL(logger))
830
 
                jk_log(logger, JK_LOG_DEBUG,
831
 
                       "check if [%s] is points to the web-inf directory",
832
 
                       uri);
833
 
 
834
 
            if (uri_is_web_inf(uri)) {
835
 
                jk_log(logger, JK_LOG_EMERG,
836
 
                       "[%s] points to the web-inf or meta-inf directory.\nSomebody try to hack into the site!!!",
837
 
                       uri);
838
 
 
839
 
                write_error_response(pfc, "404 Not Found",
840
 
                                     HTML_ERROR_404);
841
 
                return SF_STATUS_REQ_FINISHED;
842
 
            }
843
 
 
844
 
            if (worker) {
845
 
                char *forwardURI;
846
 
 
847
 
                /* This is a servlet, should redirect ... */
848
 
                jk_log(logger, JK_LOG_DEBUG,
849
 
                       "[%s] is a servlet url - should redirect to %s",
850
 
                       uri, worker);
851
 
 
852
 
                /* get URI we should forward */
853
 
                if (uri_select_option == URI_SELECT_OPT_UNPARSED) {
854
 
                    /* get original unparsed URI */
855
 
                    GetHeader(pfc, "url", (LPVOID) uri, (LPDWORD) & sz);
856
 
                    /* restore terminator for uri portion */
857
 
                    if (query)
858
 
                        *(query - 1) = '\0';
859
 
                    if (JK_IS_DEBUG_LEVEL(logger))
860
 
                        jk_log(logger, JK_LOG_DEBUG,
861
 
                               "fowarding original URI [%s]",
862
 
                               uri);
863
 
                    forwardURI = uri;
864
 
                }
865
 
                else if (uri_select_option == URI_SELECT_OPT_ESCAPED) {
866
 
                    if (!escape_url(uri, snuri, INTERNET_MAX_URL_LENGTH)) {
867
 
                        jk_log(logger, JK_LOG_ERROR,
868
 
                               "[%s] re-encoding request exceeds maximum buffer size.",
869
 
                               uri);
870
 
                        write_error_response(pfc, "400 Bad Request",
871
 
                                             HTML_ERROR_400);
872
 
                        return SF_STATUS_REQ_FINISHED;
873
 
                    }
874
 
                    if (JK_IS_DEBUG_LEVEL(logger))
875
 
                        jk_log(logger, JK_LOG_DEBUG,
876
 
                               "fowarding escaped URI [%s]",
877
 
                               snuri);
878
 
                    forwardURI = snuri;
879
 
                }
880
 
                else {
881
 
                    forwardURI = uri;
882
 
                }
883
 
 
884
 
                if (!AddHeader(pfc, URI_HEADER_NAME, forwardURI) ||
885
 
                    ((query != NULL && strlen(query) > 0)
886
 
                     ? !AddHeader(pfc, QUERY_HEADER_NAME, query) : FALSE) ||
887
 
                    !AddHeader(pfc, WORKER_HEADER_NAME, (LPSTR)worker) ||
888
 
                    !SetHeader(pfc, "url", extension_uri)) {
889
 
                    jk_log(logger, JK_LOG_ERROR,
890
 
                           "error while adding request headers");
891
 
                    return SF_STATUS_REQ_ERROR;
892
 
                }
893
 
 
894
 
                /* Move Translate: header to a temporary header so
895
 
                 * that the extension proc will be called.
896
 
                 * This allows the servlet to handle 'Translate: f'.
897
 
                 */
898
 
                if (GetHeader
899
 
                    (pfc, "Translate:", (LPVOID) Translate,
900
 
                     (LPDWORD) & szTranslate) && Translate != NULL
901
 
                    && szTranslate > 0) {
902
 
                    if (!AddHeader
903
 
                        (pfc, TOMCAT_TRANSLATE_HEADER_NAME, Translate)) {
904
 
                        jk_log(logger, JK_LOG_ERROR,
905
 
                               "error while adding Tomcat-Translate headers");
906
 
                        return SF_STATUS_REQ_ERROR;
907
 
                    }
908
 
                    SetHeader(pfc, "Translate:", NULL);
909
 
                }
910
 
                if (!pfc->pFilterContext) {
911
 
                    isapi_log_data_t *ld = (isapi_log_data_t *)pfc->AllocMem(pfc, sizeof(isapi_log_data_t), 0);
912
 
                    if (!ld) {
913
 
                        jk_log(logger, JK_LOG_ERROR,
914
 
                               "error while allocating memory");
915
 
                        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
916
 
                        return SF_STATUS_REQ_ERROR;
917
 
                    }
918
 
                    memset(ld, 0, sizeof(isapi_log_data_t));
919
 
                    strcpy(ld->uri, forwardURI);
920
 
                    if (query && strlen(query) > 0)
921
 
                        strcpy(ld->query, query);
922
 
                    pfc->pFilterContext = ld;
923
 
                }
924
 
            }
925
 
            else {
926
 
                if (JK_IS_DEBUG_LEVEL(logger))
927
 
                    jk_log(logger, JK_LOG_DEBUG,
928
 
                           "[%s] is not a servlet url", uri);
929
 
            }
930
 
        }
931
 
    }
932
 
    else if (is_inited && (dwNotificationType == SF_NOTIFY_LOG)) {
933
 
        if (pfc->pFilterContext) {
934
 
            isapi_log_data_t *ld = (isapi_log_data_t *)pfc->pFilterContext;
935
 
            HTTP_FILTER_LOG  *pl = (HTTP_FILTER_LOG *)pvNotification;
936
 
            pl->pszTarget = ld->uri;
937
 
            pl->pszParameters = ld->query;
938
 
        }
939
 
    }
940
 
    return SF_STATUS_REQ_NEXT_NOTIFICATION;
941
 
}
942
 
 
943
 
 
944
 
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO * pVer)
945
 
{
946
 
    pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
947
 
 
948
 
    strcpy(pVer->lpszExtensionDesc, VERSION_STRING);
949
 
 
950
 
 
951
 
    if (!is_inited) {
952
 
        return initialize_extension();
953
 
    }
954
 
 
955
 
    return TRUE;
956
 
}
957
 
 
958
 
DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpEcb)
959
 
{
960
 
    DWORD rc = HSE_STATUS_ERROR;
961
 
 
962
 
    lpEcb->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
963
 
 
964
 
    JK_TRACE_ENTER(logger);
965
 
 
966
 
    /* Initialise jk */
967
 
    if (is_inited && !is_mapread) {
968
 
        char serverName[MAX_SERVERNAME];
969
 
        DWORD dwLen = sizeof(serverName);
970
 
        if (lpEcb->
971
 
            GetServerVariable(lpEcb->ConnID, SERVER_NAME, serverName,
972
 
                              &dwLen)) {
973
 
            if (dwLen > 0)
974
 
                serverName[dwLen - 1] = '\0';
975
 
            if (init_jk(serverName))
976
 
                is_mapread = JK_TRUE;
977
 
        }
978
 
        if (!is_mapread)
979
 
            is_inited = JK_FALSE;
980
 
    }
981
 
 
982
 
    if (is_inited) {
983
 
        isapi_private_data_t private_data;
984
 
        jk_ws_service_t s;
985
 
        jk_pool_atom_t buf[SMALL_POOL_SIZE];
986
 
        char *worker_name;
987
 
 
988
 
        wc_maintain(logger);
989
 
        jk_init_ws_service(&s);
990
 
        jk_open_pool(&private_data.p, buf, sizeof(buf));
991
 
 
992
 
        private_data.request_started = JK_FALSE;
993
 
        private_data.bytes_read_so_far = 0;
994
 
        private_data.lpEcb = lpEcb;
995
 
 
996
 
        s.ws_private = &private_data;
997
 
        s.pool = &private_data.p;
998
 
 
999
 
        if (init_ws_service(&private_data, &s, &worker_name)) {
1000
 
            jk_worker_t *worker = wc_get_worker_for_name(worker_name, logger);
1001
 
 
1002
 
            if (JK_IS_DEBUG_LEVEL(logger))
1003
 
                jk_log(logger, JK_LOG_DEBUG,
1004
 
                       "%s a worker for name %s",
1005
 
                       worker ? "got" : "could not get", worker_name);
1006
 
 
1007
 
            if (worker) {
1008
 
                jk_endpoint_t *e = NULL;
1009
 
                /* Update retries for this worker */
1010
 
                s.retries = worker->retries;
1011
 
                if (worker->get_endpoint(worker, &e, logger)) {
1012
 
                    int is_error = JK_HTTP_SERVER_ERROR;
1013
 
                    if (e->service(e, &s, logger, &is_error)) {
1014
 
                        rc = HSE_STATUS_SUCCESS;
1015
 
                        lpEcb->dwHttpStatusCode = HTTP_STATUS_OK;
1016
 
                        jk_log(logger, JK_LOG_DEBUG,
1017
 
                               "service() returned OK");
1018
 
                    }
1019
 
                    else {
1020
 
                        lpEcb->dwHttpStatusCode = is_error;
1021
 
                        jk_log(logger, JK_LOG_ERROR,
1022
 
                               "service() failed");
1023
 
                    }
1024
 
                    e->done(&e, logger);
1025
 
                }
1026
 
            }
1027
 
            else {
1028
 
                jk_log(logger, JK_LOG_ERROR,
1029
 
                       "could not get a worker for name %s",
1030
 
                       worker_name);
1031
 
            }
1032
 
        }
1033
 
        jk_close_pool(&private_data.p);
1034
 
    }
1035
 
    else {
1036
 
        jk_log(logger, JK_LOG_ERROR,
1037
 
               "not initialized");
1038
 
    }
1039
 
 
1040
 
    JK_TRACE_EXIT(logger);
1041
 
    return rc;
1042
 
}
1043
 
 
1044
 
 
1045
 
 
1046
 
BOOL WINAPI TerminateExtension(DWORD dwFlags)
1047
 
{
1048
 
    return TerminateFilter(dwFlags);
1049
 
}
1050
 
 
1051
 
BOOL WINAPI TerminateFilter(DWORD dwFlags)
1052
 
{
1053
 
    UNREFERENCED_PARAMETER(dwFlags);
1054
 
 
1055
 
    if (is_inited) {
1056
 
        is_inited = JK_FALSE;
1057
 
 
1058
 
        if (is_mapread) {
1059
 
            uri_worker_map_free(&uw_map, logger);
1060
 
            is_mapread = JK_FALSE;
1061
 
        }
1062
 
        wc_close(logger);
1063
 
        if (logger) {
1064
 
            jk_close_file_logger(&logger);
1065
 
        }
1066
 
    }
1067
 
 
1068
 
    return TRUE;
1069
 
}
1070
 
 
1071
 
 
1072
 
BOOL WINAPI DllMain(HINSTANCE hInst,    // Instance Handle of the DLL
1073
 
                    ULONG ulReason,     // Reason why NT called this DLL
1074
 
                    LPVOID lpReserved)  // Reserved parameter for future use
1075
 
{
1076
 
    BOOL fReturn = TRUE;
1077
 
    char drive[_MAX_DRIVE];
1078
 
    char dir[_MAX_DIR];
1079
 
    char fname[_MAX_FNAME];
1080
 
    char file_name[_MAX_PATH];
1081
 
 
1082
 
    UNREFERENCED_PARAMETER(lpReserved);
1083
 
 
1084
 
    switch (ulReason) {
1085
 
    case DLL_PROCESS_ATTACH:
1086
 
        if (GetModuleFileName(hInst, file_name, sizeof(file_name))) {
1087
 
            _splitpath(file_name, drive, dir, fname, NULL);
1088
 
            _makepath(ini_file_name, drive, dir, fname, ".properties");
1089
 
        }
1090
 
        else {
1091
 
            fReturn = JK_FALSE;
1092
 
        }
1093
 
    break;
1094
 
    case DLL_PROCESS_DETACH:
1095
 
        __try {
1096
 
            TerminateFilter(HSE_TERM_MUST_UNLOAD);
1097
 
        }
1098
 
        __except(1) {
1099
 
        }
1100
 
        break;
1101
 
 
1102
 
    default:
1103
 
        break;
1104
 
    }
1105
 
 
1106
 
    return fReturn;
1107
 
}
1108
 
 
1109
 
static int init_jk(char *serverName)
1110
 
{
1111
 
    int rc = JK_FALSE;
1112
 
    jk_map_t *map;
1113
 
 
1114
 
    if (!jk_open_file_logger(&logger, log_file, log_level)) {
1115
 
        logger = NULL;
1116
 
    }
1117
 
     /* Simulate shared memory
1118
 
      * For now use fixed size.
1119
 
      */
1120
 
     jk_shm_open(NULL, JK_SHM_DEF_SIZE, logger);
1121
 
 
1122
 
     /* 10 is minimum supported on WINXP */
1123
 
     jk_set_worker_def_cache_size(10);
1124
 
 
1125
 
    /* Logging the initialization type: registry or properties file in virtual dir
1126
 
     */
1127
 
    if (JK_IS_DEBUG_LEVEL(logger)) {
1128
 
        if (using_ini_file) {
1129
 
            jk_log(logger, JK_LOG_DEBUG, "Using ini file %s.", ini_file_name);
1130
 
        }
1131
 
        else {
1132
 
            jk_log(logger, JK_LOG_DEBUG, "Using registry.");
1133
 
        }
1134
 
 
1135
 
        jk_log(logger, JK_LOG_DEBUG, "Using log file %s.", log_file);
1136
 
        jk_log(logger, JK_LOG_DEBUG, "Using log level %d.", log_level);
1137
 
        jk_log(logger, JK_LOG_DEBUG, "Using extension uri %s.", extension_uri);
1138
 
        jk_log(logger, JK_LOG_DEBUG, "Using worker file %s.", worker_file);
1139
 
        jk_log(logger, JK_LOG_DEBUG, "Using worker mount file %s.",
1140
 
               worker_mount_file);
1141
 
        jk_log(logger, JK_LOG_DEBUG, "Using uri select %d.", uri_select_option);
1142
 
    }
1143
 
    if (uri_worker_map_alloc(&uw_map, NULL, logger)) {
1144
 
        rc = JK_FALSE;
1145
 
        uw_map->fname = worker_mount_file;
1146
 
        if (worker_mount_file[0])
1147
 
            rc = uri_worker_map_load(uw_map, logger);
1148
 
    }
1149
 
    if (rc) {
1150
 
        rc = JK_FALSE;
1151
 
        if (jk_map_alloc(&map)) {
1152
 
            if (jk_map_read_properties(map, worker_file, NULL)) {
1153
 
                /* we add the URI->WORKER MAP since workers using AJP14 will feed it */
1154
 
 
1155
 
                worker_env.uri_to_worker = uw_map;
1156
 
                worker_env.server_name = serverName;
1157
 
 
1158
 
                if (wc_open(map, &worker_env, logger)) {
1159
 
                    rc = JK_TRUE;
1160
 
                }
1161
 
            }
1162
 
            else {
1163
 
                jk_log(logger, JK_LOG_EMERG,
1164
 
                       "Unable to read worker file %s.", worker_file);
1165
 
            }
1166
 
            jk_map_free(&map);
1167
 
        }
1168
 
    }
1169
 
 
1170
 
    return rc;
1171
 
}
1172
 
 
1173
 
static int initialize_extension(void)
1174
 
{
1175
 
 
1176
 
    if (read_registry_init_data()) {
1177
 
        is_inited = JK_TRUE;
1178
 
    }
1179
 
    return is_inited;
1180
 
}
1181
 
 
1182
 
int parse_uri_select(const char *uri_select)
1183
 
{
1184
 
    if (0 == strcasecmp(uri_select, URI_SELECT_PARSED_VERB)) {
1185
 
        return URI_SELECT_OPT_PARSED;
1186
 
    }
1187
 
 
1188
 
    if (0 == strcasecmp(uri_select, URI_SELECT_UNPARSED_VERB)) {
1189
 
        return URI_SELECT_OPT_UNPARSED;
1190
 
    }
1191
 
 
1192
 
    if (0 == strcasecmp(uri_select, URI_SELECT_ESCAPED_VERB)) {
1193
 
        return URI_SELECT_OPT_ESCAPED;
1194
 
    }
1195
 
 
1196
 
    return -1;
1197
 
}
1198
 
 
1199
 
static int read_registry_init_data(void)
1200
 
{
1201
 
    char tmpbuf[INTERNET_MAX_URL_LENGTH];
1202
 
    HKEY hkey;
1203
 
    long rc;
1204
 
    int ok = JK_TRUE;
1205
 
    const char *tmp;
1206
 
    jk_map_t *map;
1207
 
 
1208
 
    if (jk_map_alloc(&map)) {
1209
 
        if (jk_map_read_properties(map, ini_file_name, NULL)) {
1210
 
            using_ini_file = JK_TRUE;
1211
 
        }
1212
 
    }
1213
 
    if (using_ini_file) {
1214
 
        tmp = jk_map_get_string(map, JK_LOG_FILE_TAG, NULL);
1215
 
        if (tmp) {
1216
 
            strcpy(log_file, tmp);
1217
 
        }
1218
 
        else {
1219
 
            ok = JK_FALSE;
1220
 
        }
1221
 
        tmp = jk_map_get_string(map, JK_LOG_LEVEL_TAG, NULL);
1222
 
        if (tmp) {
1223
 
            log_level = jk_parse_log_level(tmp);
1224
 
        }
1225
 
        else {
1226
 
            ok = JK_FALSE;
1227
 
        }
1228
 
        tmp = jk_map_get_string(map, EXTENSION_URI_TAG, NULL);
1229
 
        if (tmp) {
1230
 
            strcpy(extension_uri, tmp);
1231
 
        }
1232
 
        else {
1233
 
            ok = JK_FALSE;
1234
 
        }
1235
 
        tmp = jk_map_get_string(map, JK_WORKER_FILE_TAG, NULL);
1236
 
        if (tmp) {
1237
 
            strcpy(worker_file, tmp);
1238
 
        }
1239
 
        else {
1240
 
            ok = JK_FALSE;
1241
 
        }
1242
 
        tmp = jk_map_get_string(map, JK_MOUNT_FILE_TAG, NULL);
1243
 
        if (tmp) {
1244
 
            strcpy(worker_mount_file, tmp);
1245
 
        }
1246
 
        else {
1247
 
            ok = JK_FALSE;
1248
 
        }
1249
 
        tmp = jk_map_get_string(map, URI_SELECT_TAG, NULL);
1250
 
        if (tmp) {
1251
 
            int opt = parse_uri_select(tmp);
1252
 
            if (opt >= 0) {
1253
 
                uri_select_option = opt;
1254
 
            }
1255
 
            else {
1256
 
                ok = JK_FALSE;
1257
 
            }
1258
 
        }
1259
 
 
1260
 
    }
1261
 
    else {
1262
 
        rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1263
 
                          REGISTRY_LOCATION, (DWORD) 0, KEY_READ, &hkey);
1264
 
        if (ERROR_SUCCESS != rc) {
1265
 
            return JK_FALSE;
1266
 
        }
1267
 
 
1268
 
        if (get_registry_config_parameter(hkey,
1269
 
                                          JK_LOG_FILE_TAG,
1270
 
                                          tmpbuf, sizeof(log_file))) {
1271
 
            strcpy(log_file, tmpbuf);
1272
 
        }
1273
 
        else {
1274
 
            ok = JK_FALSE;
1275
 
        }
1276
 
 
1277
 
        if (get_registry_config_parameter(hkey,
1278
 
                                          JK_LOG_LEVEL_TAG,
1279
 
                                          tmpbuf, sizeof(tmpbuf))) {
1280
 
            log_level = jk_parse_log_level(tmpbuf);
1281
 
        }
1282
 
        else {
1283
 
            ok = JK_FALSE;
1284
 
        }
1285
 
 
1286
 
        if (get_registry_config_parameter(hkey,
1287
 
                                          EXTENSION_URI_TAG,
1288
 
                                          tmpbuf, sizeof(extension_uri))) {
1289
 
            strcpy(extension_uri, tmpbuf);
1290
 
        }
1291
 
        else {
1292
 
            ok = JK_FALSE;
1293
 
        }
1294
 
 
1295
 
        if (get_registry_config_parameter(hkey,
1296
 
                                          JK_WORKER_FILE_TAG,
1297
 
                                          tmpbuf, sizeof(worker_file))) {
1298
 
            strcpy(worker_file, tmpbuf);
1299
 
        }
1300
 
        else {
1301
 
            ok = JK_FALSE;
1302
 
        }
1303
 
 
1304
 
        if (get_registry_config_parameter(hkey,
1305
 
                                          JK_MOUNT_FILE_TAG,
1306
 
                                          tmpbuf,
1307
 
                                          sizeof(worker_mount_file))) {
1308
 
            strcpy(worker_mount_file, tmpbuf);
1309
 
        }
1310
 
        else {
1311
 
            ok = JK_FALSE;
1312
 
        }
1313
 
 
1314
 
        if (get_registry_config_parameter(hkey,
1315
 
                                          URI_SELECT_TAG,
1316
 
                                          tmpbuf, sizeof(tmpbuf))) {
1317
 
            int opt = parse_uri_select(tmpbuf);
1318
 
            if (opt >= 0) {
1319
 
                uri_select_option = opt;
1320
 
            }
1321
 
            else {
1322
 
                ok = JK_FALSE;
1323
 
            }
1324
 
        }
1325
 
 
1326
 
        RegCloseKey(hkey);
1327
 
    }
1328
 
    return ok;
1329
 
}
1330
 
 
1331
 
static int get_registry_config_parameter(HKEY hkey,
1332
 
                                         const char *tag, char *b, DWORD sz)
1333
 
{
1334
 
    DWORD type = 0;
1335
 
    LONG lrc;
1336
 
 
1337
 
    lrc = RegQueryValueEx(hkey, tag, (LPDWORD) 0, &type, (LPBYTE) b, &sz);
1338
 
    if ((ERROR_SUCCESS != lrc) || (type != REG_SZ)) {
1339
 
        return JK_FALSE;
1340
 
    }
1341
 
 
1342
 
    b[sz] = '\0';
1343
 
 
1344
 
    return JK_TRUE;
1345
 
}
1346
 
 
1347
 
static int init_ws_service(isapi_private_data_t * private_data,
1348
 
                           jk_ws_service_t *s, char **worker_name)
1349
 
{
1350
 
    char huge_buf[16 * 1024];   /* should be enough for all */
1351
 
 
1352
 
    DWORD huge_buf_sz;
1353
 
 
1354
 
    s->jvm_route = NULL;
1355
 
 
1356
 
    s->start_response = start_response;
1357
 
    s->read = read;
1358
 
    s->write = write;
1359
 
    s->flush = NULL;
1360
 
 
1361
 
    /* Clear RECO status */
1362
 
    s->reco_status = RECO_NONE;
1363
 
 
1364
 
    GET_SERVER_VARIABLE_VALUE(HTTP_WORKER_HEADER_NAME, (*worker_name));
1365
 
    GET_SERVER_VARIABLE_VALUE(HTTP_URI_HEADER_NAME, s->req_uri);
1366
 
    GET_SERVER_VARIABLE_VALUE(HTTP_QUERY_HEADER_NAME, s->query_string);
1367
 
 
1368
 
    if (s->req_uri == NULL) {
1369
 
        s->query_string = private_data->lpEcb->lpszQueryString;
1370
 
        *worker_name = DEFAULT_WORKER_NAME;
1371
 
        GET_SERVER_VARIABLE_VALUE("URL", s->req_uri);
1372
 
        if (unescape_url(s->req_uri) < 0)
1373
 
            return JK_FALSE;
1374
 
        getparents(s->req_uri);
1375
 
    }
1376
 
 
1377
 
    GET_SERVER_VARIABLE_VALUE("AUTH_TYPE", s->auth_type);
1378
 
    GET_SERVER_VARIABLE_VALUE("REMOTE_USER", s->remote_user);
1379
 
    GET_SERVER_VARIABLE_VALUE("SERVER_PROTOCOL", s->protocol);
1380
 
    GET_SERVER_VARIABLE_VALUE("REMOTE_HOST", s->remote_host);
1381
 
    GET_SERVER_VARIABLE_VALUE("REMOTE_ADDR", s->remote_addr);
1382
 
    GET_SERVER_VARIABLE_VALUE(SERVER_NAME, s->server_name);
1383
 
    GET_SERVER_VARIABLE_VALUE_INT("SERVER_PORT", s->server_port, 80);
1384
 
    GET_SERVER_VARIABLE_VALUE(SERVER_SOFTWARE, s->server_software);
1385
 
    GET_SERVER_VARIABLE_VALUE_INT("SERVER_PORT_SECURE", s->is_ssl, 0);
1386
 
 
1387
 
    s->method = private_data->lpEcb->lpszMethod;
1388
 
    s->content_length = private_data->lpEcb->cbTotalBytes;
1389
 
 
1390
 
    s->ssl_cert = NULL;
1391
 
    s->ssl_cert_len = 0;
1392
 
    s->ssl_cipher = NULL;
1393
 
    s->ssl_session = NULL;
1394
 
    s->ssl_key_size = -1;
1395
 
 
1396
 
    s->headers_names = NULL;
1397
 
    s->headers_values = NULL;
1398
 
    s->num_headers = 0;
1399
 
    s->uw_map = uw_map;
1400
 
    /*
1401
 
     * Add SSL IIS environment
1402
 
     */
1403
 
    if (s->is_ssl) {
1404
 
        char *ssl_env_names[9] = {
1405
 
            "CERT_ISSUER",
1406
 
            "CERT_SUBJECT",
1407
 
            "CERT_COOKIE",
1408
 
            "HTTPS_SERVER_SUBJECT",
1409
 
            "CERT_FLAGS",
1410
 
            "HTTPS_SECRETKEYSIZE",
1411
 
            "CERT_SERIALNUMBER",
1412
 
            "HTTPS_SERVER_ISSUER",
1413
 
            "HTTPS_KEYSIZE"
1414
 
        };
1415
 
        char *ssl_env_values[9] = {
1416
 
            NULL,
1417
 
            NULL,
1418
 
            NULL,
1419
 
            NULL,
1420
 
            NULL,
1421
 
            NULL,
1422
 
            NULL,
1423
 
            NULL,
1424
 
            NULL
1425
 
        };
1426
 
        unsigned int i;
1427
 
        unsigned int num_of_vars = 0;
1428
 
 
1429
 
        for (i = 0; i < 9; i++) {
1430
 
            GET_SERVER_VARIABLE_VALUE(ssl_env_names[i], ssl_env_values[i]);
1431
 
            if (ssl_env_values[i]) {
1432
 
                num_of_vars++;
1433
 
            }
1434
 
        }
1435
 
        if (num_of_vars) {
1436
 
            unsigned int j;
1437
 
 
1438
 
            s->attributes_names =
1439
 
                jk_pool_alloc(&private_data->p, num_of_vars * sizeof(char *));
1440
 
            s->attributes_values =
1441
 
                jk_pool_alloc(&private_data->p, num_of_vars * sizeof(char *));
1442
 
 
1443
 
            j = 0;
1444
 
            for (i = 0; i < 9; i++) {
1445
 
                if (ssl_env_values[i]) {
1446
 
                    s->attributes_names[j] = ssl_env_names[i];
1447
 
                    s->attributes_values[j] = ssl_env_values[i];
1448
 
                    j++;
1449
 
                }
1450
 
            }
1451
 
            s->num_attributes = num_of_vars;
1452
 
            if (ssl_env_values[4] && ssl_env_values[4][0] == '1') {
1453
 
                CERT_CONTEXT_EX cc;
1454
 
                cc.cbAllocated = sizeof(huge_buf);
1455
 
                cc.CertContext.pbCertEncoded = (BYTE *) huge_buf;
1456
 
                cc.CertContext.cbCertEncoded = 0;
1457
 
 
1458
 
                if (private_data->lpEcb->
1459
 
                    ServerSupportFunction(private_data->lpEcb->ConnID,
1460
 
                                          (DWORD) HSE_REQ_GET_CERT_INFO_EX,
1461
 
                                          (LPVOID) & cc, NULL,
1462
 
                                          NULL) != FALSE) {
1463
 
                    jk_log(logger, JK_LOG_DEBUG,
1464
 
                           "Client Certificate encoding:%d sz:%d flags:%ld",
1465
 
                           cc.CertContext.
1466
 
                           dwCertEncodingType & X509_ASN_ENCODING,
1467
 
                           cc.CertContext.cbCertEncoded,
1468
 
                           cc.dwCertificateFlags);
1469
 
                    s->ssl_cert =
1470
 
                        jk_pool_alloc(&private_data->p,
1471
 
                                      base64_encode_cert_len(cc.CertContext.
1472
 
                                                             cbCertEncoded));
1473
 
 
1474
 
                    s->ssl_cert_len = base64_encode_cert(s->ssl_cert,
1475
 
                                                         huge_buf,
1476
 
                                                         cc.CertContext.
1477
 
                                                         cbCertEncoded) - 1;
1478
 
                }
1479
 
            }
1480
 
        }
1481
 
    }
1482
 
 
1483
 
    huge_buf_sz = sizeof(huge_buf);
1484
 
    if (get_server_value(private_data->lpEcb,
1485
 
                         "ALL_HTTP", huge_buf, huge_buf_sz)) {
1486
 
        unsigned int cnt = 0;
1487
 
        char *tmp;
1488
 
 
1489
 
        for (tmp = huge_buf; *tmp; tmp++) {
1490
 
            if (*tmp == '\n') {
1491
 
                cnt++;
1492
 
            }
1493
 
        }
1494
 
 
1495
 
        if (cnt) {
1496
 
            char *headers_buf = jk_pool_strdup(&private_data->p, huge_buf);
1497
 
            unsigned int i;
1498
 
            size_t len_of_http_prefix = strlen("HTTP_");
1499
 
            BOOL need_content_length_header = (s->content_length == 0);
1500
 
 
1501
 
            cnt -= 2;           /* For our two special headers */
1502
 
            /* allocate an extra header slot in case we need to add a content-length header */
1503
 
            s->headers_names =
1504
 
                jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
1505
 
            s->headers_values =
1506
 
                jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
1507
 
 
1508
 
            if (!s->headers_names || !s->headers_values || !headers_buf) {
1509
 
                return JK_FALSE;
1510
 
            }
1511
 
 
1512
 
            for (i = 0, tmp = headers_buf; *tmp && i < cnt;) {
1513
 
                int real_header = JK_TRUE;
1514
 
 
1515
 
                /* Skipp the HTTP_ prefix to the beginning of th header name */
1516
 
                tmp += len_of_http_prefix;
1517
 
 
1518
 
                if (!strnicmp(tmp, URI_HEADER_NAME, strlen(URI_HEADER_NAME))
1519
 
                    || !strnicmp(tmp, WORKER_HEADER_NAME,
1520
 
                                 strlen(WORKER_HEADER_NAME))) {
1521
 
                    real_header = JK_FALSE;
1522
 
                }
1523
 
                else if (need_content_length_header &&
1524
 
                         !strnicmp(tmp, CONTENT_LENGTH,
1525
 
                                   strlen(CONTENT_LENGTH))) {
1526
 
                    need_content_length_header = FALSE;
1527
 
                    s->headers_names[i] = tmp;
1528
 
                }
1529
 
                else if (!strnicmp(tmp, TOMCAT_TRANSLATE_HEADER_NAME,
1530
 
                                   strlen(TOMCAT_TRANSLATE_HEADER_NAME))) {
1531
 
                    tmp += 6;   /* TOMCAT */
1532
 
                    s->headers_names[i] = tmp;
1533
 
                }
1534
 
                else {
1535
 
                    s->headers_names[i] = tmp;
1536
 
                }
1537
 
 
1538
 
                while (':' != *tmp && *tmp) {
1539
 
                    if ('_' == *tmp) {
1540
 
                        *tmp = '-';
1541
 
                    }
1542
 
                    else {
1543
 
                        *tmp = JK_TOLOWER(*tmp);
1544
 
                    }
1545
 
                    tmp++;
1546
 
                }
1547
 
                *tmp = '\0';
1548
 
                tmp++;
1549
 
 
1550
 
                /* Skip all the WS chars after the ':' to the beginning of th header value */
1551
 
                while (' ' == *tmp || '\t' == *tmp || '\v' == *tmp) {
1552
 
                    tmp++;
1553
 
                }
1554
 
 
1555
 
                if (real_header) {
1556
 
                    s->headers_values[i] = tmp;
1557
 
                }
1558
 
 
1559
 
                while (*tmp != '\n' && *tmp != '\r') {
1560
 
                    tmp++;
1561
 
                }
1562
 
                *tmp = '\0';
1563
 
                tmp++;
1564
 
 
1565
 
                /* skipp CR LF */
1566
 
                while (*tmp == '\n' || *tmp == '\r') {
1567
 
                    tmp++;
1568
 
                }
1569
 
 
1570
 
                if (real_header) {
1571
 
                    i++;
1572
 
                }
1573
 
            }
1574
 
            /* Add a content-length = 0 header if needed.
1575
 
             * Ajp13 assumes an absent content-length header means an unknown,
1576
 
             * but non-zero length body.
1577
 
             */
1578
 
            if (need_content_length_header) {
1579
 
                s->headers_names[cnt] = "Content-Length";
1580
 
                s->headers_values[cnt] = "0";
1581
 
                cnt++;
1582
 
            }
1583
 
            s->num_headers = cnt;
1584
 
        }
1585
 
        else {
1586
 
            /* We must have our two headers */
1587
 
            return JK_FALSE;
1588
 
        }
1589
 
    }
1590
 
    else {
1591
 
        return JK_FALSE;
1592
 
    }
1593
 
 
1594
 
    return JK_TRUE;
1595
 
}
1596
 
 
1597
 
static int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
1598
 
                            char *name, char *buf, DWORD bufsz)
1599
 
{
1600
 
    DWORD sz = bufsz;
1601
 
    buf[0]   = '\0';
1602
 
    if (!lpEcb->GetServerVariable(lpEcb->ConnID, name,
1603
 
                                  buf, (LPDWORD) &sz))
1604
 
        return JK_FALSE;
1605
 
 
1606
 
    if (sz <= bufsz)
1607
 
        buf[sz-1] = '\0';
1608
 
    return JK_TRUE;
1609
 
}
1610
 
 
1611
 
static const char begin_cert[] = "-----BEGIN CERTIFICATE-----\r\n";
1612
 
 
1613
 
static const char end_cert[] = "-----END CERTIFICATE-----\r\n";
1614
 
 
1615
 
static const char basis_64[] =
1616
 
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1617
 
 
1618
 
static int base64_encode_cert_len(int len)
1619
 
{
1620
 
    int n = ((len + 2) / 3 * 4) + 1;    /* base64 encoded size */
1621
 
    n += (n + 63 / 64) * 2;             /* add CRLF's */
1622
 
    n += sizeof(begin_cert) + sizeof(end_cert) - 2;  /* add enclosing strings. */
1623
 
    return n;
1624
 
}
1625
 
 
1626
 
static int base64_encode_cert(char *encoded,
1627
 
                              const char *string, int len)
1628
 
{
1629
 
    int i, c;
1630
 
    char *p;
1631
 
    const char *t;
1632
 
 
1633
 
    p = encoded;
1634
 
 
1635
 
    t = begin_cert;
1636
 
    while (*t != '\0')
1637
 
        *p++ = *t++;
1638
 
 
1639
 
    c = 0;
1640
 
    for (i = 0; i < len - 2; i += 3) {
1641
 
        *p++ = basis_64[(string[i] >> 2) & 0x3F];
1642
 
        *p++ = basis_64[((string[i] & 0x3) << 4) |
1643
 
                        ((int)(string[i + 1] & 0xF0) >> 4)];
1644
 
        *p++ = basis_64[((string[i + 1] & 0xF) << 2) |
1645
 
                        ((int)(string[i + 2] & 0xC0) >> 6)];
1646
 
        *p++ = basis_64[string[i + 2] & 0x3F];
1647
 
        c += 4;
1648
 
        if (c >= 64) {
1649
 
            *p++ = '\r';
1650
 
            *p++ = '\n';
1651
 
            c = 0;
1652
 
        }
1653
 
    }
1654
 
    if (i < len) {
1655
 
        *p++ = basis_64[(string[i] >> 2) & 0x3F];
1656
 
        if (i == (len - 1)) {
1657
 
            *p++ = basis_64[((string[i] & 0x3) << 4)];
1658
 
            *p++ = '=';
1659
 
        }
1660
 
        else {
1661
 
            *p++ = basis_64[((string[i] & 0x3) << 4) |
1662
 
                            ((int)(string[i + 1] & 0xF0) >> 4)];
1663
 
            *p++ = basis_64[((string[i + 1] & 0xF) << 2)];
1664
 
        }
1665
 
        *p++ = '=';
1666
 
        c++;
1667
 
    }
1668
 
    if (c != 0) {
1669
 
        *p++ = '\r';
1670
 
        *p++ = '\n';
1671
 
    }
1672
 
 
1673
 
    t = end_cert;
1674
 
    while (*t != '\0')
1675
 
        *p++ = *t++;
1676
 
 
1677
 
    *p++ = '\0';
1678
 
    return (int)(p - encoded);
1679
 
}