blob: d6021f91bfeabf0774f1b6e6553933dd0dc4a33a [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Utils for processing various request components
*
* Author: Gal Shachor <shachor@il.ibm.com>
* Author: Henri Gomez <hgomez@apache.org>
* Author: Costin Manolache
*/
/* XXX make them virtual methods, allow servers to override
*/
#include "jk_global.h"
#include "jk_channel.h"
#include "jk_env.h"
#include "jk_requtil.h"
#ifdef AS400
#include "util_ebcdic.h"
#endif
#define CHUNK_BUFFER_PAD (12)
static const char *response_trans_headers[] = {
"Content-Type",
"Content-Language",
"Content-Length",
"Date",
"Last-Modified",
"Location",
"Set-Cookie",
"Set-Cookie2",
"Servlet-Engine",
"Status",
"WWW-Authenticate"
};
/*
* Conditional request attributes
*
*/
#define SC_A_CONTEXT (unsigned char)1
#define SC_A_SERVLET_PATH (unsigned char)2
#define SC_A_REMOTE_USER (unsigned char)3
#define SC_A_AUTH_TYPE (unsigned char)4
#define SC_A_QUERY_STRING (unsigned char)5
#define SC_A_JVM_ROUTE (unsigned char)6
#define SC_A_SSL_CERT (unsigned char)7
#define SC_A_SSL_CIPHER (unsigned char)8
#define SC_A_SSL_SESSION (unsigned char)9
#define SC_A_REQ_ATTRIBUTE (unsigned char)10
/* only in if JkOptions +ForwardKeySize */
#define SC_A_SSL_KEY_SIZE (unsigned char)11
#define SC_A_SECRET (unsigned char)12
#define SC_A_STORED_METHOD (unsigned char)13
#define SC_A_ARE_DONE (unsigned char)0xFF
/*
* Forward a request from the web server to the servlet container.
*/
#define JK_AJP13_FORWARD_REQUEST (unsigned char)2
/* Important: ajp13 protocol has the strange habit of sending
a second ( untyped ) message imediately following the request,
with a first chunk of POST body. This is nice for small post
requests, since it avoids a roundtrip, but it's horrible
because it brakes the model. So we'll have to remember this
as an exception to the rule as long as ajp13 is alive
*/
/** Get header value using a lookup table.
*
*
* long_res_header_for_sc
*/
const char *jk2_requtil_getHeaderById(jk_env_t *env, int sc)
{
const char *rc = NULL;
if (sc <= SC_RES_HEADERS_NUM && sc > 0) {
rc = response_trans_headers[sc - 1];
}
return rc;
}
/**
* Get method id.
*
* sc_for_req_method
*/
int jk2_requtil_getMethodId(jk_env_t *env, const char *method,
unsigned char *sc)
{
int rc = JK_OK;
if (0 == strcmp(method, "GET")) {
*sc = SC_M_GET;
}
else if (0 == strcmp(method, "POST")) {
*sc = SC_M_POST;
}
else if (0 == strcmp(method, "HEAD")) {
*sc = SC_M_HEAD;
}
else if (0 == strcmp(method, "PUT")) {
*sc = SC_M_PUT;
}
else if (0 == strcmp(method, "DELETE")) {
*sc = SC_M_DELETE;
}
else if (0 == strcmp(method, "OPTIONS")) {
*sc = SC_M_OPTIONS;
}
else if (0 == strcmp(method, "TRACE")) {
*sc = SC_M_TRACE;
}
else if (0 == strcmp(method, "PROPFIND")) {
*sc = SC_M_PROPFIND;
}
else if (0 == strcmp(method, "PROPPATCH")) {
*sc = SC_M_PROPPATCH;
}
else if (0 == strcmp(method, "MKCOL")) {
*sc = SC_M_MKCOL;
}
else if (0 == strcmp(method, "COPY")) {
*sc = SC_M_COPY;
}
else if (0 == strcmp(method, "MOVE")) {
*sc = SC_M_MOVE;
}
else if (0 == strcmp(method, "LOCK")) {
*sc = SC_M_LOCK;
}
else if (0 == strcmp(method, "UNLOCK")) {
*sc = SC_M_UNLOCK;
}
else if (0 == strcmp(method, "ACL")) {
*sc = SC_M_ACL;
}
else if (0 == strcmp(method, "REPORT")) {
*sc = SC_M_REPORT;
}
else if (0 == strcmp(method, "VERSION-CONTROL")) {
*sc = SC_M_VERSION_CONTROL;
}
else if (0 == strcmp(method, "CHECKIN")) {
*sc = SC_M_CHECKIN;
}
else if (0 == strcmp(method, "CHECKOUT")) {
*sc = SC_M_CHECKOUT;
}
else if (0 == strcmp(method, "UNCHECKOUT")) {
*sc = SC_M_UNCHECKOUT;
}
else if (0 == strcmp(method, "SEARCH")) {
*sc = SC_M_SEARCH;
}
else if (0 == strcmp(method, "MKWORKSPACE")) {
*sc = SC_M_MKWORKSPACE;
}
else if (0 == strcmp(method, "UPDATE")) {
*sc = SC_M_UPDATE;
}
else if (0 == strcmp(method, "LABEL")) {
*sc = SC_M_LABEL;
}
else if (0 == strcmp(method, "MERGE")) {
*sc = SC_M_MERGE;
}
else if (0 == strcmp(method, "BASELINE-CONTROL")) {
*sc = SC_M_BASELINE_CONTROL;
}
else if (0 == strcmp(method, "MKACTIVITY")) {
*sc = SC_M_MKACTIVITY;
}
else {
*sc = SC_M_JK_STORED;
}
return rc;
}
/**
* Get header id.
*
* sc_for_req_header
*/
int jk2_requtil_getHeaderId(jk_env_t *env, const char *header_name,
unsigned short *sc)
{
/* char lowerCased[30]; */
/* if( strlen( header_name ) > 30 ) */
/* return JK_FALSE; */
/* strncpy( lowerCased, header_name, 30 ); */
switch (header_name[0]) {
case 'a':
case 'A':
if (strncasecmp(header_name, "accept", 6) == 0) {
if ('-' == header_name[6]) {
if (!strcasecmp(header_name + 7, "charset")) {
*sc = SC_ACCEPT_CHARSET;
}
else if (!strcasecmp(header_name + 7, "encoding")) {
*sc = SC_ACCEPT_ENCODING;
}
else if (!strcasecmp(header_name + 7, "language")) {
*sc = SC_ACCEPT_LANGUAGE;
}
else {
return JK_ERR;
}
}
else if ('\0' == header_name[6]) {
*sc = SC_ACCEPT;
}
else {
return JK_ERR;
}
}
else if (!strcasecmp(header_name, "authorization")) {
*sc = SC_AUTHORIZATION;
}
else {
return JK_ERR;
}
break;
case 'c':
case 'C':
if (!strcasecmp(header_name, "cookie")) {
*sc = SC_COOKIE;
}
else if (!strcasecmp(header_name, "connection")) {
*sc = SC_CONNECTION;
}
else if (!strcasecmp(header_name, "content-type")) {
*sc = SC_CONTENT_TYPE;
}
else if (!strcasecmp(header_name, "content-length")) {
*sc = SC_CONTENT_LENGTH;
}
else if (!strcasecmp(header_name, "cookie2")) {
*sc = SC_COOKIE2;
}
else {
return JK_ERR;
}
break;
case 'h':
case 'H':
if (!strcasecmp(header_name, "host")) {
*sc = SC_HOST;
}
else {
return JK_ERR;
}
break;
case 'p':
case 'P':
if (!strcasecmp(header_name, "pragma")) {
*sc = SC_PRAGMA;
}
else {
return JK_ERR;
}
break;
case 'r':
case 'R':
if (!strcasecmp(header_name, "referer")) {
*sc = SC_REFERER;
}
else {
return JK_ERR;
}
break;
case 'u':
case 'U':
if (!strcasecmp(header_name, "user-agent")) {
*sc = SC_USER_AGENT;
}
else {
return JK_ERR;
}
break;
default:
/* env->l->jkLog(env, env->l, JK_LOG_INFO, */
/* "requtil.getHeaderId() long header %s\n", header_name); */
return JK_ERR;
}
return JK_OK;
}
/** Retrieve the cookie with the given name
*/
char *jk2_requtil_getCookieByName(jk_env_t *env, jk_ws_service_t *s,
const char *name)
{
int i;
jk_map_t *headers = s->headers_in;
/* XXX use 'get' - and make sure jk_map has support for
case insensitive search */
for (i = 0; i < headers->size(NULL, headers); i++) {
if (0 == strcasecmp(headers->nameAt(NULL, headers, i), "cookie")) {
char *id_start;
for (id_start = strstr(headers->valueAt(NULL, headers, i), name);
id_start; id_start = strstr(id_start + 1, name)) {
if ('=' == id_start[strlen(name)]) {
/*
* Session cookie was found, get it's value
*/
id_start += (1 + strlen(name));
if (strlen(id_start)) {
char *id_end;
id_start = s->pool->pstrdup(env, s->pool, id_start);
if (id_end = strchr(id_start, ';')) {
*id_end = '\0';
}
return id_start;
}
}
}
}
}
return NULL;
}
/* Retrieve the parameter with the given name
*/
char *jk2_requtil_getPathParam(jk_env_t *env, jk_ws_service_t *s,
const char *name)
{
char *id_start = NULL;
for (id_start = strstr(s->req_uri, name);
id_start; id_start = strstr(id_start + 1, name)) {
if ('=' == id_start[strlen(name)]) {
/*
* Session path-cookie was found, get it's value
*/
id_start += (1 + strlen(name));
if (strlen(id_start)) {
char *id_end;
id_start = s->pool->pstrdup(env, s->pool, id_start);
/*
* The query string is not part of req_uri, however
* to be on the safe side lets remove the trailing query
* string if appended...
*/
if (id_end = strchr(id_start, '?')) {
*id_end = '\0';
}
return id_start;
}
}
}
return NULL;
}
/** Retrieve session id from the cookie or the parameter
* (parameter first)
*/
char *jk2_requtil_getSessionId(jk_env_t *env, jk_ws_service_t *s)
{
char *val;
val = jk2_requtil_getPathParam(env, s, JK_PATH_SESSION_IDENTIFIER);
if (!val) {
val = jk2_requtil_getCookieByName(env, s, JK_SESSION_IDENTIFIER);
}
return val;
}
/** Extract the 'route' from the session id. The route is
* the id of the worker that generated the session and where all
* further requests in that session will be sent.
*/
char *jk2_requtil_getSessionRoute(jk_env_t *env, jk_ws_service_t *s)
{
char *sessionid = jk2_requtil_getSessionId(env, s);
char *ch;
if (!sessionid) {
return NULL;
}
/*
* Balance parameter is appended to the end
*/
ch = strrchr(sessionid, '.');
if (!ch) {
return 0;
}
ch++;
if (*ch == '\0') {
return NULL;
}
return ch;
}
/*
* Read data from the web server.
*
* Socket API didn't garanty all the data will be kept in a single
* read, so we must loop up to all awaited data are received
*/
int jk2_requtil_readFully(jk_env_t *env, jk_ws_service_t *s,
unsigned char *buf, unsigned len)
{
unsigned rdlen = 0;
unsigned padded_len = len;
if (s->is_chunked && s->no_more_chunks) {
return 0;
}
if (s->is_chunked) {
/* Corner case: buf must be large enough to hold next
* chunk size (if we're on or near a chunk border).
* Pad the length to a reasonable value, otherwise the
* read fails and the remaining chunks are tossed.
*/
padded_len = (len < CHUNK_BUFFER_PAD) ? len : len - CHUNK_BUFFER_PAD;
}
while (rdlen < padded_len) {
unsigned this_time = 0;
if (s->read(env, s, buf + rdlen, len - rdlen, &this_time)) {
return -1;
}
if (0 == this_time) {
if (s->is_chunked) {
s->no_more_chunks = 1; /* read no more */
}
break;
}
rdlen += this_time;
}
return (int)rdlen;
}
/* -------------------- Printf writing -------------------- */
#define JK_BUF_SIZE 4096
static int jk2_requtil_createBuffer(jk_env_t *env, jk_ws_service_t *s)
{
int bsize = JK_BUF_SIZE;
s->outSize = bsize;
s->outBuf = (char *)s->pool->alloc(env, s->pool, bsize);
return JK_OK;
}
static void jk2_requtil_printf(jk_env_t *env, jk_ws_service_t *s, char *fmt,
...)
{
va_list vargs;
int ret = 0;
if (s->outBuf == NULL) {
jk2_requtil_createBuffer(env, s);
}
va_start(vargs, fmt);
s->outPos = 0; /* Temp - we don't buffer */
ret =
apr_vsnprintf(s->outBuf + s->outPos, s->outSize - s->outPos, fmt, vargs);
va_end(vargs);
s->write(env, s, s->outBuf, strlen(s->outBuf));
}
/* -------------------- Request serialization -------------------- */
/* XXX optimization - this can be overriden by server to avoid
multiple copies
*/
/**
Message structure
AJPV13_REQUEST/AJPV14_REQUEST=
request_prefix (1) (byte)
method (byte)
protocol (string)
req_uri (string)
remote_addr (string)
remote_host (string)
server_name (string)
server_port (short)
is_ssl (boolean)
num_headers (short)
num_headers*(req_header_name header_value)
?context (byte)(string)
?servlet_path (byte)(string)
?remote_user (byte)(string)
?auth_type (byte)(string)
?query_string (byte)(string)
?jvm_route (byte)(string)
?ssl_cert (byte)(string)
?ssl_cipher (byte)(string)
?ssl_session (byte)(string)
?ssl_key_size (byte)(int) via JkOptions +ForwardKeySize
request_terminator (byte)
?body content_length*(var binary)
Was: ajp_marshal_into_msgb
*/
int jk2_serialize_request13(jk_env_t *env, jk_msg_t *msg,
jk_ws_service_t *s, jk_endpoint_t *ae)
{
unsigned char method;
int i;
int headerCount;
int rc;
int debug = 0;
if (s->uriEnv != NULL) {
debug = s->uriEnv->mbean->debug;
}
rc = jk2_requtil_getMethodId(env, s->method, &method);
if (rc != JK_OK) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"Error ajp_marshal_into_msgb - method %s\n",
s->method);
return JK_ERR;
}
headerCount = s->headers_in->size(env, s->headers_in);
if (msg->appendByte(env, msg, JK_AJP13_FORWARD_REQUEST) ||
msg->appendByte(env, msg, method) ||
msg->appendString(env, msg, s->protocol) ||
msg->appendString(env, msg, s->req_uri) ||
msg->appendString(env, msg, s->remote_addr) ||
msg->appendString(env, msg, s->remote_host) ||
msg->appendString(env, msg, s->server_name) ||
msg->appendInt(env, msg, (unsigned short)s->server_port) ||
msg->appendByte(env, msg, (unsigned char)(s->is_ssl)) ||
msg->appendInt(env, msg, (unsigned short)(headerCount))) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handle.request() Error serializing the message head\n");
return JK_ERR;
}
for (i = 0; i < headerCount; i++) {
unsigned short sc;
char *name = s->headers_in->nameAt(env, s->headers_in, i);
if (jk2_requtil_getHeaderId(env, name, &sc) == JK_OK) {
/* env->l->jkLog(env, env->l, JK_LOG_INFO, */
/* "serialize.request() Add headerId %s %d\n", name, sc); */
if (msg->appendInt(env, msg, sc)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"serialize.request() Error serializing header id\n");
return JK_ERR;
}
}
else {
if (debug > 0)
env->l->jkLog(env, env->l, JK_LOG_DEBUG,
"serialize.request() Add headerName %s\n",
name);
if (msg->appendString(env, msg, name)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"serialize.request() Error serializing header name\n");
return JK_ERR;
}
}
if (msg->appendString(env, msg,
s->headers_in->valueAt(env, s->headers_in,
i))) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"serialize.request() Error serializing header value\n");
return JK_ERR;
}
}
if (s->remote_user) {
if (msg->appendByte(env, msg, SC_A_REMOTE_USER) ||
msg->appendString(env, msg, s->remote_user)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"serialize.request() Error serializing user name\n");
return JK_ERR;
}
}
if (s->auth_type) {
if (msg->appendByte(env, msg, SC_A_AUTH_TYPE) ||
msg->appendString(env, msg, s->auth_type)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handle.request() Error serializing auth type\n");
return JK_ERR;
}
}
if (s->query_string) {
if (msg->appendByte(env, msg, SC_A_QUERY_STRING) ||
#ifdef AS400
msg->appendAsciiString(env, msg, s->query_string)) {
#else
msg->appendString(env, msg, s->query_string)) {
#endif
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handle.request() Error serializing query string\n");
return JK_ERR;
}
}
/* XXX This can be sent only on startup ( ajp14 ) */
if (s->jvm_route) {
if (msg->appendByte(env, msg, SC_A_JVM_ROUTE) ||
msg->appendString(env, msg, s->jvm_route)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handle.request() Error serializing worker id\n");
return JK_ERR;
}
}
if (s->ssl_cert_len) {
if (msg->appendByte(env, msg, SC_A_SSL_CERT) ||
msg->appendString(env, msg, s->ssl_cert)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handle.request() Error serializing SSL cert\n");
return JK_ERR;
}
}
if (s->ssl_cipher) {
if (msg->appendByte(env, msg, SC_A_SSL_CIPHER) ||
msg->appendString(env, msg, s->ssl_cipher)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handle.request() Error serializing SSL cipher\n");
return JK_ERR;
}
}
if (s->ssl_session) {
if (msg->appendByte(env, msg, SC_A_SSL_SESSION) ||
msg->appendString(env, msg, s->ssl_session)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handle.request() Error serializing SSL session\n");
return JK_ERR;
}
}
/*
* ssl_key_size is required by Servlet 2.3 API
* added support only in ajp14 mode
* JFC removed: ae->proto == AJP14_PROTO
*/
if (s->ssl_key_size != -1) {
if (msg->appendByte(env, msg, SC_A_SSL_KEY_SIZE) ||
msg->appendInt(env, msg, (unsigned short)s->ssl_key_size)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handle.request() Error serializing SSL key size\n");
return JK_ERR;
}
}
if (ae->worker->secret) {
if (msg->appendByte(env, msg, SC_A_SECRET) ||
msg->appendString(env, msg, ae->worker->secret)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handle.request() Error serializing secret\n");
return JK_ERR;
}
}
/* If the method was unrecognized, encode it as an attribute */
if (method == SC_M_JK_STORED) {
if (msg->appendByte(env, msg, SC_A_STORED_METHOD) ||
msg->appendString(env, msg, s->method)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handle.request() Error encoding method %s\n",
s->method);
}
}
if (s->attributes->size(env, s->attributes) > 0) {
for (i = 0; i < s->attributes->size(env, s->attributes); i++) {
char *name = s->attributes->nameAt(env, s->attributes, i);
char *val = s->attributes->valueAt(env, s->attributes, i);
if (msg->appendByte(env, msg, SC_A_REQ_ATTRIBUTE) ||
msg->appendString(env, msg, name) ||
msg->appendString(env, msg, val)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handle.request() Error serializing attribute %s=%s\n",
name, val);
return JK_ERR;
}
}
}
if (msg->appendByte(env, msg, SC_A_ARE_DONE)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handle.request() Error serializing end marker\n");
return JK_ERR;
}
if (debug > 0)
env->l->jkLog(env, env->l, JK_LOG_DEBUG,
"serialize.request() serialized %s\n", s->req_uri);
/* msg->dump( env, msg, "Dump: " ); */
return JK_OK;
}
/** The inital BODY chunk
*/
int jk2_serialize_postHead(jk_env_t *env, jk_msg_t *msg,
jk_ws_service_t *r, jk_endpoint_t *ae)
{
int len = r->left_bytes_to_send;
if (len > AJP13_MAX_SEND_BODY_SZ) {
len = AJP13_MAX_SEND_BODY_SZ;
}
if (len <= 0) {
len = 0;
return JK_OK;
}
len = msg->appendFromServer(env, msg, r, ae, len);
/* the right place to add file storage for upload */
if (len >= 0) {
r->content_read += len;
return JK_OK;
}
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"handler.marshalPostHead() - error len=%d\n", len);
return JK_ERR;
}
/* -------------------- Query decoding -------------------- */
/** Read a query string into the map
*/
int jk2_requtil_queryRead(jk_env_t *env, jk_map_t *m, const char *query)
{
char *sep;
char *value;
char *qry = m->pool->pstrdup(env, m->pool, query);
while (qry != NULL) {
sep = strchr(qry, '&');
if (sep != NULL) {
*sep = '\0';
sep++;
}
value = strchr(qry, '=');
if (value == NULL) {
value = "";
}
else {
*value = '\0';
value++;
}
m->add(env, m, m->pool->pstrdup(env, m->pool, qry),
m->pool->pstrdup(env, m->pool, value));
qry = sep;
}
return JK_OK;
}
/* -------------------- Request encoding -------------------- */
/* Moved from IIS adapter */
#define T_OS_ESCAPE_PATH (4)
static const unsigned char test_char_table[256] = {
0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, 14,
14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 7, 6, 1, 6, 1, 1,
9, 9, 1, 0, 8, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 15,
15, 8, 15, 15, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 7, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 15, 7, 15, 1, 14, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
};
#define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f))
static const char c2x_table[] = "0123456789abcdef";
static unsigned char *c2x(unsigned what, unsigned char *where)
{
*where++ = '%';
*where++ = c2x_table[what >> 4];
*where++ = c2x_table[what & 0xf];
return where;
}
int jk_requtil_escapeUrl(const char *path, char *dest, int destsize)
{
const unsigned char *s = (const unsigned char *)path;
unsigned char *d = (unsigned char *)dest;
unsigned char *e = (unsigned char *)(dest + destsize - 1);
unsigned char *ee = (unsigned char *)(dest + destsize - 3);
unsigned c;
while ((c = *s)) {
if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
if (d >= ee)
return JK_ERR;
d = c2x(c, d);
}
else {
if (d >= e)
return JK_ERR;
*d++ = c;
}
++s;
}
*d = '\0';
return JK_OK;
}
/* XXX Make it a default checking in uri worker map
*/
int jk_requtil_uriIsWebInf(char *uri)
{
char *c = uri;
while (*c) {
*c = tolower(*c);
c++;
}
if (strstr(uri, "web-inf")) {
return JK_TRUE;
}
if (strstr(uri, "meta-inf")) {
return JK_TRUE;
}
return JK_FALSE;
}
static char x2c(const char *what)
{
register char digit;
digit =
((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
digit *= 16;
digit +=
(what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
return (digit);
}
int jk_requtil_unescapeUrl(char *url)
{
register int x, y, badesc, badpath;
badesc = 0;
badpath = 0;
for (x = 0, y = 0; url[y]; ++x, ++y) {
if (url[y] != '%')
url[x] = url[y];
else {
if (!isxdigit(url[y + 1]) || !isxdigit(url[y + 2])) {
badesc = 1;
url[x] = '%';
}
else {
url[x] = x2c(&url[y + 1]);
y += 2;
if (url[x] == '/' || url[x] == '\0')
badpath = 1;
}
}
}
url[x] = '\0';
if (badesc)
return -1;
else if (badpath)
return -2;
else
return JK_OK;
}
void jk_requtil_getParents(char *name)
{
int l, w;
/* Four paseses, as per RFC 1808 */
/* a) remove ./ path segments */
for (l = 0, w = 0; name[l] != '\0';) {
if (name[l] == '.' && name[l + 1] == '/'
&& (l == 0 || name[l - 1] == '/'))
l += 2;
else
name[w++] = name[l++];
}
/* b) remove trailing . path, segment */
if (w == 1 && name[0] == '.')
w--;
else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/')
w--;
name[w] = '\0';
/* c) remove all xx/../ segments. (including leading ../ and /../) */
l = 0;
while (name[l] != '\0') {
if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' &&
(l == 0 || name[l - 1] == '/')) {
register int m = l + 3, n;
l = l - 2;
if (l >= 0) {
while (l >= 0 && name[l] != '/')
l--;
l++;
}
else
l = 0;
n = l;
while ((name[n] = name[m]))
(++n, ++m);
}
else
++l;
}
/* d) remove trailing xx/.. segment. */
if (l == 2 && name[0] == '.' && name[1] == '.')
name[0] = '\0';
else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
&& name[l - 3] == '/') {
l = l - 4;
if (l >= 0) {
while (l >= 0 && name[l] != '/')
l--;
l++;
}
else
l = 0;
name[l] = '\0';
}
}
static const char begin_cert[] = "-----BEGIN CERTIFICATE-----\r\n";
static const char end_cert[] = "-----END CERTIFICATE-----\r\n";
static const char basis_64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int jk_requtil_base64CertLen(int len)
{
int n = ((len + 2) / 3 * 4) + 1; /* base64 encoded size */
n += (n + 63 / 64) * 2; /* add CRLF's */
n += sizeof(begin_cert) + sizeof(end_cert) - 2; /* add enclosing strings. */
return n;
}
int jk_requtil_base64EncodeCert(char *encoded,
const unsigned char *string, int len)
{
int i, c;
char *p;
const char *t;
p = encoded;
t = begin_cert;
while (*t != '\0')
*p++ = *t++;
c = 0;
for (i = 0; i < len - 2; i += 3) {
*p++ = basis_64[(string[i] >> 2) & 0x3F];
*p++ = basis_64[((string[i] & 0x3) << 4) |
((int)(string[i + 1] & 0xF0) >> 4)];
*p++ = basis_64[((string[i + 1] & 0xF) << 2) |
((int)(string[i + 2] & 0xC0) >> 6)];
*p++ = basis_64[string[i + 2] & 0x3F];
c += 4;
if (c >= 64) {
*p++ = '\r';
*p++ = '\n';
c = 0;
}
}
if (i < len) {
*p++ = basis_64[(string[i] >> 2) & 0x3F];
if (i == (len - 1)) {
*p++ = basis_64[((string[i] & 0x3) << 4)];
*p++ = '=';
}
else {
*p++ = basis_64[((string[i] & 0x3) << 4) |
((int)(string[i + 1] & 0xF0) >> 4)];
*p++ = basis_64[((string[i + 1] & 0xF) << 2)];
}
*p++ = '=';
c++;
}
if (c != 0) {
*p++ = '\r';
*p++ = '\n';
}
t = end_cert;
while (*t != '\0')
*p++ = *t++;
*p++ = '\0';
return p - encoded;
}
/** Initialize the request
*
* jk_init_ws_service
*/
void jk2_requtil_initRequest(jk_env_t *env, jk_ws_service_t *s)
{
s->ws_private = NULL;
s->method = NULL;
s->protocol = NULL;
s->req_uri = NULL;
s->remote_addr = NULL;
s->remote_host = NULL;
s->remote_user = NULL;
s->auth_type = NULL;
s->query_string = NULL;
s->server_name = NULL;
s->server_port = 80;
s->server_software = NULL;
s->content_length = 0;
s->is_chunked = 0;
s->no_more_chunks = 0;
s->content_read = 0;
s->is_ssl = JK_FALSE;
s->ssl_cert = NULL;
s->ssl_cert_len = 0;
s->ssl_cipher = NULL;
s->ssl_session = NULL;
s->jvm_route = NULL;
s->uriEnv = NULL;
s->outBuf = NULL;
s->msg = NULL;
s->jkprintf = jk2_requtil_printf;
}