You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by hg...@apache.org on 2001/06/08 16:57:56 UTC
cvs commit: jakarta-tomcat-connectors/jk/src/native/domino jk_dsapi_plugin.c
hgomez 01/06/08 07:57:55
Modified: jk/src/native/domino jk_dsapi_plugin.c
Log:
Updated domino from 1.0.2 version
We'll need also fix the SSL vars which are not
set at the rigth place
Revision Changes Path
1.4 +1054 -989 jakarta-tomcat-connectors/jk/src/native/domino/jk_dsapi_plugin.c
Index: jk_dsapi_plugin.c
===================================================================
RCS file: /home/cvs/jakarta-tomcat-connectors/jk/src/native/domino/jk_dsapi_plugin.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- jk_dsapi_plugin.c 2001/06/07 14:03:48 1.3
+++ jk_dsapi_plugin.c 2001/06/08 14:57:52 1.4
@@ -1,989 +1,1054 @@
-/*
- * Copyright (c) 1997-1999 The Java Apache Project. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. All advertising materials mentioning features or use of this
- * software must display the following acknowledgment:
- * "This product includes software developed by the Java Apache
- * Project for use in the Apache JServ servlet engine project
- * <http://java.apache.org/>."
- *
- * 4. The names "Apache JServ", "Apache JServ Servlet Engine" and
- * "Java Apache Project" must not be used to endorse or promote products
- * derived from this software without prior written permission.
- *
- * 5. Products derived from this software may not be called "Apache JServ"
- * nor may "Apache" nor "Apache JServ" appear in their names without
- * prior written permission of the Java Apache Project.
- *
- * 6. Redistributions of any form whatsoever must retain the following
- * acknowledgment:
- * "This product includes software developed by the Java Apache
- * Project for use in the Apache JServ servlet engine project
- * <http://java.apache.org/>."
- *
- * THIS SOFTWARE IS PROVIDED BY THE JAVA APACHE PROJECT "AS IS" AND ANY
- * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JAVA APACHE PROJECT OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Java Apache Group. For more information
- * on the Java Apache Project and the Apache JServ Servlet Engine project,
- * please see <http://java.apache.org/>.
- *
- */
-
-/***************************************************************************
- * Description: DSAPI plugin for Lotus Domino *
- * Author: Andy Armstrong <an...@tagish.com> *
- * Version: $Revision: 1.3 $ *
- ***************************************************************************/
-
-/* Based on the IIS redirector by Gal Shachor <sh...@il.ibm.com> */
-
-#include "config.h"
-#include "inifile.h"
-
-/* JK stuff */
-#include "jk_global.h"
-#include "jk_util.h"
-#include "jk_map.h"
-#include "jk_pool.h"
-#include "jk_service.h"
-#include "jk_worker.h"
-#include "jk_ajp12_worker.h"
-#include "jk_uri_worker_map.h"
-
-#ifndef NO_CAPI
-/* Domino stuff */
-#include <global.h>
-#include <addin.h>
-#else
-#include <stdarg.h>
-#define NOERROR 0
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include "dsapifilter.h"
-#if !defined(DLLEXPORT)
-#ifdef WIN32
-#define DLLEXPORT __declspec(dllexport)
-#else
-#define DLLEXPORT
-#endif
-#endif
-
-#define VERSION "1.0"
-#define VERSION_STRING "Jakarta/DSAPI/" VERSION
-/* What we call ourselves */
-#define FILTERDESC "Apache Tomcat Interceptor (" VERSION_STRING ")"
-/* Registry location of configuration data */
-#define REGISTRY_LOCATION "Software\\Apache Software Foundation\\Jakarta Dsapi Redirector\\1.0"
-#define ININAME "libtomcat.ini"
-
-/* Names of registry keys/ini items that contain commands to start, stop Tomcat */
-#define TOMCAT_START "tomcat_start"
-#define TOMCAT_STOP "tomcat_stop"
-#define TOMCAT_STARTSTOP_TO 30000 /* 30 seconds */
-
-static int initDone = JK_FALSE;
-static jk_uri_worker_map_t *uw_map = NULL;
-static jk_logger_t *logger = NULL;
-
-static int logLevel = JK_LOG_EMERG_LEVEL;
-
-#ifdef USE_INIFILE
-static const char *logFile;
-static const char *workerFile;
-static const char *workerMountFile;
-static const char *tomcatStart;
-static const char *tomcatStop;
-#else
-#ifndef MAX_PATH
-#define MAX_PATH 1024
-#endif
-static char logFile[MAX_PATH];
-static char workerFile[MAX_PATH];
-static char workerMountFile[MAX_PATH];
-static char tomcatStart[MAX_PATH];
-static char tomcatStop[MAX_PATH];
-#endif
-
-static char *crlf = "\r\n";
-
-typedef struct private_ws
-{
- jk_pool_t p;
-
- /* These get passed in by Domino and are used to access various
- * Domino methods and data.
- */
- FilterContext *context;
- FilterParsedRequest *reqData;
-
- /* True iff the response headers have been sent
- */
- int responseStarted;
-
- /* Current pointer into and remaining size
- * of request body data
- */
- char *reqBuffer;
- unsigned int reqSize;
-
-} private_ws_t;
-
-/* These three functions are called back (indirectly) by
- * Tomcat during request processing. StartResponse() sends
- * the headers associated with the response.
- */
-static int JK_METHOD StartResponse(jk_ws_service_t * s, int status, const char *reason,
- const char *const *hdrNames,
- const char *const *hdrValues, unsigned hdrCount);
-/* Read() is called by Tomcat to read from the request body (if any).
- */
-static int JK_METHOD Read(jk_ws_service_t * s, void *b, unsigned l, unsigned *a);
-/* Write() is called by Tomcat to send data back to the client.
- */
-static int JK_METHOD Write(jk_ws_service_t * s, const void *b, unsigned l);
-
-static int ReadInitData(void);
-#ifndef USE_INIFILE
-static int GetRegParam(HKEY hkey, const char *tag, char *b, DWORD sz);
-#endif
-
-static unsigned int ParsedRequest(FilterContext *context, FilterParsedRequest *reqData);
-
-/* Case insentive memcmp() clone
- */
-#ifdef HAVE_MEMICMP
-#define NoCaseCmp(ci, cj, l) _memicmp((void *) (ci), (void *) (cj), (l))
-#else
-static int NoCaseCmp(const char *ci, const char *cj, int len)
-{
- if (0 == memcmp(ci, cj, len))
- return 0;
- while (len > 0)
- {
- int cmp = tolower(*ci) - tolower(*cj);
- if (cmp != 0) return cmp;
- ci++;
- cj++;
- len--;
- }
- return 0;
-}
-#endif
-
-/* Case insensitive substring search.
- * str string to search
- * slen length of string to search
- * ptn pattern to search for
- * plen length of pattern
- * returns 1 if there's a match otherwise 0
- */
-static int NoCaseFind(const char *str, int slen, const char *ptn, int plen)
-{
- while (slen >= plen)
- {
- if (NoCaseCmp(str, ptn, plen) == 0)
- return 1;
- slen--;
- str++;
- }
- return 0;
-}
-
-#ifdef NO_CAPI
-/* Alternative to the Domino function */
-static void AddInLogMessageText(char *msg, unsigned short code, ...)
-{
- va_list ap;
-
- if (code != NOERROR)
- printf("Error %d: ", code);
-
- va_start(ap, code);
- vprintf(msg, ap);
- va_end(ap);
- printf("\n");
-}
-
-#endif
-
-/* Return 1 iff the supplied string contains "web-inf" (in any case
- * variation. We don't allow URIs containing web-inf.
- */
-static int BadURI(const char *uri)
-{
- static char *wi = "web-inf";
- return NoCaseFind(uri, strlen(uri), wi, strlen(wi));
-}
-
-/* Replacement for strcat() that updates a buffer pointer. It's
- * probably marginal, but this should be more efficient that strcat()
- * in cases where the string being concatenated to gets long because
- * strcat() has to count from start of the string each time.
- */
-static void Append(char **buf, const char *str)
-{
- int l = strlen(str);
- memcpy(*buf, str, l);
- (*buf)[l] = '\0';
- *buf += l;
-}
-
-/* Start the response by sending any headers. Invoked by Tomcat. I don't
- * particularly like the fact that this always allocates memory, but
- * perhaps jk_pool_alloc() is efficient.
- */
-static int JK_METHOD StartResponse(jk_ws_service_t *s, int status, const char *reason,
- const char *const *hdrNames,
- const char *const *hdrValues, unsigned hdrCount)
-{
- DEBUG(("StartResponse()\n"));
- jk_log(logger, JK_LOG_DEBUG, "Into jk_ws_service_t::StartResponse\n");
-
- if (status < 100 || status > 1000)
- {
- jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::StartResponse, invalid status %d\n", status);
- return JK_FALSE;
- }
-
- if (s && s->ws_private)
- {
- private_ws_t *p = s->ws_private;
-
- if (!p->responseStarted)
- {
- char *hdrBuf;
- FilterResponseHeaders frh;
- int rc, errID;
-
- p->responseStarted = JK_TRUE;
-
- if (NULL == reason)
- reason = "";
-
- /* Build a single string containing all the headers
- * because that's what Domino needs.
- */
- if (hdrCount > 0)
- {
- unsigned i;
- unsigned hdrLen;
- char *bufp;
-
- for (i = 0, hdrLen = 3; i < hdrCount; i++)
- hdrLen += strlen(hdrNames[i]) + strlen(hdrValues[i]) + 4;
-
- hdrBuf = jk_pool_alloc(&p->p, hdrLen);
- bufp = hdrBuf;
-
- for (i = 0; i < hdrCount; i++)
- {
- Append(&bufp, hdrNames[i]);
- Append(&bufp, ": ");
- Append(&bufp, hdrValues[i]);
- Append(&bufp, crlf);
- }
-
- Append(&bufp, crlf);
- }
- else
- {
- hdrBuf = crlf;
- }
-
- frh.responseCode = status;
- frh.reasonText = (char *) reason;
- frh.headerText = hdrBuf;
-
- DEBUG(("%d %s\n%s", status, reason, hdrBuf));
-
- /* Send the headers */
- rc = p->context->ServerSupport(p->context, kWriteResponseHeaders, &frh, NULL, 0, &errID);
-
- /*
- if (rc)
- {
- jk_log(logger, JK_LOG_ERROR,
- "jk_ws_service_t::StartResponse, ServerSupportFunction failed\n");
- return JK_FALSE;
- }
- */
-
- }
- return JK_TRUE;
- }
-
- jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::StartResponse, NULL parameters\n");
-
- return JK_FALSE;
-
-}
-
-static int JK_METHOD Read(jk_ws_service_t * s, void *bytes, unsigned len, unsigned *countp)
-{
- DEBUG(("Read(%p, %p, %u, %p)\n", s, bytes, len, countp));
- jk_log(logger, JK_LOG_DEBUG, "Into jk_ws_service_t::Read\n");
-
- if (s && s->ws_private && bytes && countp)
- {
- private_ws_t *p = s->ws_private;
-
- /* Copy data from Domino's buffer. Although it seems slightly
- * improbably we're believing that Domino always buffers the
- * entire request in memory. Not properly tested yet.
- */
- if (len > p->reqSize) len = p->reqSize;
- memcpy(bytes, p->reqBuffer, len);
- p->reqBuffer += len;
- p->reqSize -= len;
- *countp = len;
- return JK_TRUE;
- }
-
- jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::Read, NULL parameters\n");
-
- return JK_FALSE;
-}
-
-static int JK_METHOD Write(jk_ws_service_t *s, const void *bytes, unsigned len)
-{
- DEBUG(("Write(%p, %p, %u)\n", s, bytes, len));
- jk_log(logger, JK_LOG_DEBUG, "Into jk_ws_service_t::Write\n");
-
- if (s && s->ws_private && bytes)
- {
- private_ws_t *p = s->ws_private;
- int errID, rc;
-
- /* Make sure the response has really started. I'm almost certain
- * this isn't necessary, but it was in the ISAPI code, so it's in
- * here too.
- */
- if (!p->responseStarted)
- StartResponse(s, 200, NULL, NULL, NULL, 0);
-
- DEBUG(("Writing %d bytes of content\n", len));
-
- /* Send the data */
- if (len > 0)
- rc = p->context->WriteClient(p->context, (char *) bytes, len, 0, &errID);
-
- return JK_TRUE;
- }
-
- jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::Write, NULL parameters\n");
-
- return JK_FALSE;
-}
-
-static int RunProg(const char *cmd)
-{
-#ifdef WIN32
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
-
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si); // Start the child process.
- si.dwFlags = STARTF_USESHOWWINDOW;
- si.wShowWindow = SW_SHOWMAXIMIZED;
-
- if (!CreateProcess(NULL, (char *) cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
- {
- DWORD err = GetLastError();
- AddInLogMessageText("Command \"%s\" (error %u)", NOERROR, cmd, err);
- return FALSE;
- }
-
- if (WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, TOMCAT_STARTSTOP_TO))
- return TRUE;
-
- AddInLogMessageText("Command \"%s\" didn't complete in time", NOERROR, cmd);
- return FALSE;
-
-#else
- int err = system(cmd);
- if (0 == err) return 1;
- AddInLogMessageText("Command \"%s\" failed (error %d)", NOERROR, cmd, err);
- return 0;
-#endif
-}
-
-/* Called when the filter is unloaded. Free various resources and
- * display a banner.
- */
-DLLEXPORT unsigned int TerminateFilter(unsigned int reserved)
-{
- if (initDone)
- {
- initDone = JK_FALSE;
-
- uri_worker_map_free(&uw_map, logger);
- wc_close(logger);
- if (logger)
- jk_close_file_logger(&logger);
- }
-
- if (NULL != tomcatStop && '\0' != *tomcatStop)
- {
- AddInLogMessageText("Attempting to stop Tomcat: %s", NOERROR, tomcatStop);
- RunProg(tomcatStop);
- }
-
- AddInLogMessageText(FILTERDESC " unloaded", NOERROR);
- return kFilterHandledEvent;
-}
-
-/* Called when Domino loads the filter. Reads a load of config data from
- * the registry and elsewhere and displays a banner.
- */
-DLLEXPORT unsigned int FilterInit(FilterInitData * filterInitData)
-{
- int rc = JK_FALSE;
- jk_map_t *map = NULL;
-
- if (!ReadInitData())
- goto initFailed;
-
- if (!jk_open_file_logger(&logger, logFile, logLevel))
- logger = NULL;
-
- if (NULL != tomcatStart && '\0' != *tomcatStart)
- {
- AddInLogMessageText("Attempting to start Tomcat: %s", NOERROR, tomcatStart);
- RunProg(tomcatStart);
- }
-
- if (map_alloc(&map))
- {
- if (map_read_properties(map, workerMountFile))
- if (uri_worker_map_alloc(&uw_map, map, logger))
- rc = JK_TRUE;
-
- map_free(&map);
- }
-
- if (!rc) goto initFailed;
-
- rc = JK_FALSE;
- if (map_alloc(&map))
- {
- if (map_read_properties(map, workerFile))
- if (wc_open(map, uw_map, logger))
- rc = JK_TRUE;
-
- map_free(&map);
- }
-
- if (!rc) goto initFailed;
-
- initDone = JK_TRUE;
-
- filterInitData->appFilterVersion = kInterfaceVersion;
- filterInitData->eventFlags = kFilterParsedRequest;
- strcpy(filterInitData->filterDesc, FILTERDESC);
-
- // Banner
- AddInLogMessageText("%s loaded", NOERROR, filterInitData->filterDesc);
-
- return kFilterHandledEvent;
-
-initFailed:
- AddInLogMessageText("Error loading %s", NOERROR, FILTERDESC);
-
- return kFilterError;
-
-}
-
-/* Read parameters from the registry
- */
-static int ReadInitData(void)
-{
-#ifdef USE_INIFILE
-
-#define GET(tag, var) \
- var = inifile_lookup(tag); \
- if (NULL == var) \
- { \
- AddInLogMessageText("%s not defined in %s", NOERROR, tag, ININAME); \
- ok = JK_FALSE; \
- }
-
- int ok = JK_TRUE;
- ERRTYPE e;
- const char *v;
-
- if (e = inifile_read(ININAME), ERRNONE != e)
- {
- AddInLogMessageText("Error reading: %s, %s", NOERROR, ININAME, ERRTXT(e));
- return JK_FALSE;
- }
-
- GET(JK_LOG_FILE_TAG, logFile)
- GET(JK_LOG_LEVEL_TAG, v);
- GET(JK_WORKER_FILE_TAG, workerFile);
- GET(JK_MOUNT_FILE_TAG, workerMountFile);
-
- logLevel = (NULL == v) ? 0 : jk_parse_log_level(v);
-
- tomcatStart = inifile_lookup(TOMCAT_START);
- tomcatStop = inifile_lookup(TOMCAT_STOP);
-
- return ok;
-
-#else
- int ok = JK_TRUE;
- char tmpbuf[1024];
- HKEY hkey;
- long rc;
-
- rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_LOCATION, (DWORD) 0, KEY_READ, &hkey);
- if (ERROR_SUCCESS != rc) return JK_FALSE;
-
- if (GetRegParam(hkey, JK_LOG_FILE_TAG, tmpbuf, sizeof (logFile)))
- strcpy(logFile, tmpbuf);
- else
- ok = JK_FALSE;
-
- if (GetRegParam(hkey, JK_LOG_LEVEL_TAG, tmpbuf, sizeof (tmpbuf)))
- logLevel = jk_parse_log_level(tmpbuf);
- else
- ok = JK_FALSE;
-
- if (GetRegParam(hkey, JK_WORKER_FILE_TAG, tmpbuf, sizeof (workerFile)))
- strcpy(workerFile, tmpbuf);
- else
- ok = JK_FALSE;
-
- if (GetRegParam(hkey, JK_MOUNT_FILE_TAG, tmpbuf, sizeof (workerMountFile)))
- strcpy(workerMountFile, tmpbuf);
- else
- ok = JK_FALSE;
-
- /* Get the commands that will start and stop Tomcat. We're not too bothered
- * if they don't exist.
- */
- tomcatStart[0] = '\0';
- if (GetRegParam(hkey, TOMCAT_START, tmpbuf, sizeof (tomcatStart)))
- strcpy(tomcatStart, tmpbuf);
-
- tomcatStop[0] = '\0';
- if (GetRegParam(hkey, TOMCAT_STOP, tmpbuf, sizeof (tomcatStop)))
- strcpy(tomcatStop, tmpbuf);
-
- RegCloseKey(hkey);
-
- return ok;
-#endif
-}
-
-#ifndef USE_INIFILE
-static int GetRegParam(HKEY hkey, const char *tag, char *b, DWORD sz)
-{
- DWORD type = 0;
- LONG lrc;
-
- lrc = RegQueryValueEx(hkey, tag, (LPDWORD) 0, &type, (LPBYTE) b, &sz);
- if (ERROR_SUCCESS != lrc || type != REG_SZ)
- return JK_FALSE;
-
- b[sz] = '\0';
-
- DEBUG(("%s = %s\n", tag, b));
-
- return JK_TRUE;
-}
-#endif
-
-/* Main entry point for the filter. Called by Domino for every HTTP request.
- */
-DLLEXPORT unsigned int HttpFilterProc(FilterContext *context, unsigned int eventType, void *eventData)
-{
- if (initDone)
- {
- switch (eventType)
- {
- case kFilterParsedRequest:
- return ParsedRequest(context, (FilterParsedRequest *) eventData);
- default:
- break;
- }
- }
- return kFilterNotHandled;
-}
-
-/* Send a simple response. Used when we don't want to bother Tomcat,
- * which in practice means for various error conditions that we can
- * detect internally.
- */
-static void SimpleResponse(FilterContext *context, int status, char *reason, char *body)
-{
- FilterResponseHeaders frh;
- int rc, errID;
- char hdrBuf[40];
-
- sprintf(hdrBuf, "Content-type: text/html%s%s", crlf, crlf);
-
- frh.responseCode = status;
- frh.reasonText = reason;
- frh.headerText = hdrBuf;
-
- rc = context->ServerSupport(context, kWriteResponseHeaders, &frh, NULL, 0, &errID);
- rc = context->WriteClient(context, body, strlen(body), 0, &errID);
-}
-
-/* Called to reject a URI that contains the string "web-inf". We block
- * these because they may indicate an attempt to invoke arbitrary code.
- */
-static unsigned int RejectBadURI(FilterContext *context)
-{
- static char *msg = "<HTML><BODY><H1>Access is Forbidden</H1></BODY></HTML>";
-
- SimpleResponse(context, 403, "Forbidden", msg);
- return kFilterHandledRequest;
-}
-
-/* Get the value of a server (CGI) variable as a string
- */
-static int GetVariable(private_ws_t *ws, char *hdrName,
- char *buf, DWORD bufsz, char **dest, const char *dflt)
-{
- int errID;
-
- if (ws->context->GetServerVariable(ws->context, hdrName, buf, bufsz, &errID))
- *dest = jk_pool_strdup(&ws->p, buf);
- else
- *dest = jk_pool_strdup(&ws->p, dflt);
-
- DEBUG(("%s = %s\n", hdrName, *dest));
-
- return JK_TRUE;
-}
-
-/* Get the value of a server (CGI) variable as an integer
- */
-static int GetVariableInt(private_ws_t *ws, char *hdrName,
- char *buf, DWORD bufsz, int *dest, int dflt)
-{
- int errID;
-
- if (ws->context->GetServerVariable(ws->context, hdrName, buf, bufsz, &errID))
- *dest = atoi(buf);
- else
- *dest = dflt;
-
- DEBUG(("%s = %d\n", hdrName, *dest));
-
- return JK_TRUE;
-}
-
-/* A couple of utility macros to supply standard arguments to GetVariable() and
- * GetVariableInt().
- */
-#define GETVARIABLE(name, dest, dflt) GetVariable(ws, (name), workBuf, sizeof(workBuf), (dest), (dflt))
-#define GETVARIABLEINT(name, dest, dflt) GetVariableInt(ws, (name), workBuf, sizeof(workBuf), (dest), (dflt))
-
-/* Allocate space for a string given a start pointer and an end pointer
- * and return a pointer to the allocated, copied string.
- */
-static char *MemDup(private_ws_t *ws, const char *start, const char *end)
-{
- char *out = NULL;
-
- if (start != NULL && end != NULL && end > start)
- {
- int len = end - start;
- out = jk_pool_alloc(&ws->p, len + 1);
- memcpy(out, start, len);
- out[len] = '\0';
- }
-
- return out;
-}
-
-/* Given all the HTTP headers as a single string parse them into individual
- * name, value pairs. Called twice: once to work out how many headers there
- * are, then again to copy them.
- */
-static int ParseHeaders(private_ws_t *ws, const char *hdrs, int hdrsz, jk_ws_service_t *s)
-{
- int hdrCount = 0;
- const char *limit = hdrs + hdrsz;
- const char *name, *nameEnd;
- const char *value, *valueEnd;
-
- while (hdrs < limit)
- {
- /* Skip line *before* doing anything, cos we want to lose the first line which
- * contains the request.
- */
- while (hdrs < limit && (*hdrs != '\n' && *hdrs != '\r'))
- hdrs++;
- while (hdrs < limit && (*hdrs == '\n' || *hdrs == '\r'))
- hdrs++;
-
- if (hdrs >= limit)
- break;
-
- name = nameEnd = value = valueEnd = NULL;
-
- name = hdrs;
- while (hdrs < limit && *hdrs >= ' ' && *hdrs != ':')
- hdrs++;
- nameEnd = hdrs;
-
- if (hdrs < limit && *hdrs == ':')
- {
- hdrs++;
- while (hdrs < limit && (*hdrs == ' ' || *hdrs == '\t'))
- hdrs++;
- value = hdrs;
- while (hdrs < limit && *hdrs >= ' ')
- hdrs++;
- valueEnd = hdrs;
- }
-
- if (s->headers_names != NULL && s->headers_values != NULL)
- {
- s->headers_names[hdrCount] = MemDup(ws, name, nameEnd);
- s->headers_values[hdrCount] = MemDup(ws, value, valueEnd);
- DEBUG(("%s = %s\n", s->headers_names[hdrCount], s->headers_values[hdrCount]));
- }
- hdrCount++;
- }
-
- return hdrCount;
-}
-
-/* Set up all the necessary jk_* workspace based on the current HTTP request.
- */
-static int InitService(private_ws_t *ws, jk_ws_service_t *s)
-{
- char workBuf[16 * 1024];
- FilterRequest fr;
- char *hdrs, *qp;
- int hdrsz;
- int errID;
- int hdrCount;
- int rc;
-
- static char *methodName[] = { "", "HEAD", "GET", "POST", "PUT", "DELETE" };
-
- rc = ws->context->GetRequest(ws->context, &fr, &errID);
-
- s->jvm_route = NULL;
- s->start_response = StartResponse;
- s->read = Read;
- s->write = Write;
-
- s->req_uri = jk_pool_strdup(&ws->p, fr.URL);
- s->query_string = NULL;
- if (qp = strchr(s->req_uri, '?'), qp != NULL)
- {
- *qp++ = '\0';
- if (strlen(qp))
- s->query_string = qp;
- }
-
- GETVARIABLE("AUTH_TYPE", &s->auth_type, "");
- GETVARIABLE("REMOTE_USER", &s->remote_user, "");
- GETVARIABLE("SERVER_PROTOCOL", &s->protocol, "");
- GETVARIABLE("REMOTE_HOST", &s->remote_host, "");
- GETVARIABLE("REMOTE_ADDR", &s->remote_addr, "");
- GETVARIABLE("SERVER_NAME", &s->server_name, "");
- GETVARIABLEINT("SERVER_PORT", &s->server_port, 80);
- GETVARIABLE("SERVER_SOFTWARE", &s->server_software, "Lotus Domino");
- GETVARIABLEINT("SERVER_PORT_SECURE", &s->is_ssl, 0);
- GETVARIABLEINT("CONTENT_LENGTH", &s->content_length, 0); // not tested
-
- s->method = methodName[ws->reqData->requestMethod];
-
- s->ssl_cert = NULL;
- s->ssl_cert_len = 0;
- s->ssl_cipher = NULL;
- s->ssl_session = NULL;
-
- s->headers_names = NULL;
- s->headers_values = NULL;
- s->num_headers = 0;
-
- if (s->is_ssl)
- {
- char *sslNames[] =
- {
- "CERT_ISSUER", "CERT_SUBJECT", "CERT_COOKIE", "CERT_FLAGS", "CERT_SERIALNUMBER",
- "HTTPS_SERVER_SUBJECT", "HTTPS_SECRETKEYSIZE", "HTTPS_SERVER_ISSUER", "HTTPS_KEYSIZE"
- };
-
- char *sslValues[] =
- {
- NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL
- };
-
- unsigned i, varCount = 0;
-
- for (i = 0; i < sizeof(sslNames)/sizeof(sslNames[0]); i++)
- {
- GETVARIABLE(sslNames[i], &sslValues[i], NULL);
- if (sslValues[i]) varCount++;
- }
-
- if (varCount > 0)
- {
- unsigned j;
-
- s->attributes_names = jk_pool_alloc(&ws->p, varCount * sizeof (char *));
- s->attributes_values = jk_pool_alloc(&ws->p, varCount * sizeof (char *));
-
- j = 0;
- for (i = 0; i < sizeof(sslNames)/sizeof(sslNames[0]); i++)
- {
- if (sslValues[i])
- {
- s->attributes_names[j] = sslNames[i];
- s->attributes_values[j] = sslValues[i];
- j++;
- }
- }
- s->num_attributes = varCount;
- }
- }
-
- /* Duplicate all the headers now */
-
- hdrsz = ws->reqData->GetAllHeaders(ws->context, &hdrs, &errID);
- DEBUG(("\nGot headers (length %d)\n--------\n%s\n--------\n\n", hdrsz, hdrs));
-
- s->headers_names =
- s->headers_values = NULL;
- hdrCount = ParseHeaders(ws, hdrs, hdrsz, s);
- DEBUG(("Found %d headers\n", hdrCount));
- s->num_headers = hdrCount;
- s->headers_names = jk_pool_alloc(&ws->p, hdrCount * sizeof(char *));
- s->headers_values = jk_pool_alloc(&ws->p, hdrCount * sizeof(char *));
- hdrCount = ParseHeaders(ws, hdrs, hdrsz, s);
-
- return JK_TRUE;
-}
-
-/* Handle an HTTP request. Works out whether Tomcat will be interested then either
- * despatches it to Tomcat or passes it back to Domino.
- */
-static unsigned int ParsedRequest(FilterContext *context, FilterParsedRequest *reqData)
-{
- unsigned int errID;
- int rc;
- FilterRequest fr;
- int result = kFilterNotHandled;
-
- DEBUG(("\nParsedRequest starting\n"));
-
- rc = context->GetRequest(context, &fr, &errID);
-
- if (fr.URL && strlen(fr.URL))
- {
- char *uri = fr.URL;
- char *workerName, *qp;
-
- if (qp = strchr(uri, '?'), qp != NULL) *qp = '\0';
- workerName = map_uri_to_worker(uw_map, uri, logger);
- if (qp) *qp = '?';
-
- DEBUG(("Worker for this URL is %s\n", workerName));
-
- if (NULL != workerName)
- {
- private_ws_t ws;
- jk_ws_service_t s;
- jk_pool_atom_t buf[SMALL_POOL_SIZE];
-
- if (BadURI(uri))
- return RejectBadURI(context);
-
- /* Go dispatch the call */
-
- jk_init_ws_service(&s);
- jk_open_pool(&ws.p, buf, sizeof (buf));
-
- ws.responseStarted = JK_FALSE;
- ws.context = context;
- ws.reqData = reqData;
-
- ws.reqSize = context->GetRequestContents(context, &ws.reqBuffer, &errID);
-
- s.ws_private = &ws;
- s.pool = &ws.p;
-
- if (InitService(&ws, &s))
- {
- jk_worker_t *worker = wc_get_worker_for_name(workerName, logger);
-
- jk_log(logger, JK_LOG_DEBUG, "HttpExtensionProc %s a worker for name %s\n",
- worker ? "got" : "could not get", workerName);
-
- if (worker)
- {
- jk_endpoint_t *e = NULL;
-
- if (worker->get_endpoint(worker, &e, logger))
- {
- int recover = JK_FALSE;
- DEBUG(("About to call e->service()\n"));
-
- if (e->service(e, &s, logger, &recover))
- {
- result = kFilterHandledRequest;
- jk_log(logger, JK_LOG_DEBUG, "HttpExtensionProc service() returned OK\n");
- DEBUG(("HttpExtensionProc service() returned OK\n"));
- }
- else
- {
- result = kFilterError;
- jk_log(logger, JK_LOG_ERROR, "HttpExtensionProc error, service() failed\n");
- DEBUG(("HttpExtensionProc error, service() failed\n"));
- }
- DEBUG(("About to call e->done()\n"));
- e->done(&e, logger);
- DEBUG(("Returned OK\n"));
- }
- }
- else
- {
- jk_log(logger, JK_LOG_ERROR,
- "HttpExtensionProc error, could not get a worker for name %s\n",
- workerName);
- }
- }
-
- jk_close_pool(&ws.p);
- }
- }
-
- return result;
-}
+/*
+ * Copyright (c) 1997-1999 The Java Apache Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Java Apache
+ * Project for use in the Apache JServ servlet engine project
+ * <http://java.apache.org/>."
+ *
+ * 4. The names "Apache JServ", "Apache JServ Servlet Engine" and
+ * "Java Apache Project" must not be used to endorse or promote products
+ * derived from this software without prior written permission.
+ *
+ * 5. Products derived from this software may not be called "Apache JServ"
+ * nor may "Apache" nor "Apache JServ" appear in their names without
+ * prior written permission of the Java Apache Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Java Apache
+ * Project for use in the Apache JServ servlet engine project
+ * <http://java.apache.org/>."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JAVA APACHE PROJECT "AS IS" AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JAVA APACHE PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Java Apache Group. For more information
+ * on the Java Apache Project and the Apache JServ Servlet Engine project,
+ * please see <http://java.apache.org/>.
+ *
+ */
+
+/***************************************************************************
+ * Description: DSAPI plugin for Lotus Domino *
+ * Author: Andy Armstrong <an...@tagish.com> *
+ * Version: $Revision: 1.4 $ *
+ ***************************************************************************/
+
+/* Based on the IIS redirector by Gal Shachor <sh...@il.ibm.com> */
+
+#include "config.h"
+#include "inifile.h"
+
+/* JK stuff */
+#include "jk_global.h"
+#include "jk_util.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "jk_ajp12_worker.h"
+#include "jk_uri_worker_map.h"
+
+#ifndef NO_CAPI
+/* Domino stuff */
+#include <global.h>
+#include <addin.h>
+#else
+#include <stdarg.h>
+#define NOERROR 0
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "dsapifilter.h"
+#if !defined(DLLEXPORT)
+#ifdef WIN32
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT
+#endif
+#endif
+
+#define VERSION "1.0.2"
+#define VERSION_STRING "Jakarta/DSAPI/" VERSION
+/* What we call ourselves */
+#define FILTERDESC "Apache Tomcat Interceptor (" VERSION_STRING ")"
+/* Registry location of configuration data */
+#define REGISTRY_LOCATION "Software\\Apache Software Foundation\\Jakarta Dsapi Redirector\\1.0"
+/* Name of INI file relative to whatever the 'current' directory is when the filter is
+ * loaded. Certainly on Linux this is the Domino data directory -- it seems likely that
+ * it's the same on other platforms
+ */
+#define ININAME "libtomcat.ini"
+
+/* Names of registry keys/ini items that contain commands to start, stop Tomcat */
+#define TOMCAT_START "tomcat_start"
+#define TOMCAT_STOP "tomcat_stop"
+#define TOMCAT_STARTSTOP_TO 30000 /* 30 seconds */
+
+static int initDone = JK_FALSE;
+static jk_uri_worker_map_t *uw_map = NULL;
+static jk_logger_t *logger = NULL;
+
+static int logLevel = JK_LOG_EMERG_LEVEL;
+static jk_pool_t cfgPool;
+
+static const char *logFile;
+static const char *workerFile;
+static const char *workerMountFile;
+static const char *tomcatStart;
+static const char *tomcatStop;
+
+static jk_worker_env_t worker_env;
+
+static char *crlf = "\r\n";
+
+typedef struct private_ws
+{
+ jk_pool_t p;
+
+ /* These get passed in by Domino and are used to access various
+ * Domino methods and data.
+ */
+ FilterContext *context;
+ FilterParsedRequest *reqData;
+
+ /* True iff the response headers have been sent
+ */
+ int responseStarted;
+
+ /* Current pointer into and remaining size
+ * of request body data
+ */
+ char *reqBuffer;
+ unsigned int reqSize;
+
+} private_ws_t;
+
+/* These three functions are called back (indirectly) by
+ * Tomcat during request processing. StartResponse() sends
+ * the headers associated with the response.
+ */
+static int JK_METHOD StartResponse(jk_ws_service_t * s, int status, const char *reason,
+ const char *const *hdrNames,
+ const char *const *hdrValues, unsigned hdrCount);
+/* Read() is called by Tomcat to read from the request body (if any).
+ */
+static int JK_METHOD Read(jk_ws_service_t * s, void *b, unsigned l, unsigned *a);
+/* Write() is called by Tomcat to send data back to the client.
+ */
+static int JK_METHOD Write(jk_ws_service_t * s, const void *b, unsigned l);
+
+static int ReadInitData(void);
+
+#ifndef USE_INIFILE
+static const char *GetRegString(HKEY hkey, const char *key);
+#endif
+
+static unsigned int ParsedRequest(FilterContext *context, FilterParsedRequest *reqData);
+
+/* Case insentive memcmp() clone
+ */
+#ifdef HAVE_MEMICMP
+#define NoCaseMemCmp(ci, cj, l) _memicmp((void *) (ci), (void *) (cj), (l))
+#else
+static int NoCaseMemCmp(const char *ci, const char *cj, int len)
+{
+ if (0 == memcmp(ci, cj, len))
+ return 0;
+ while (len > 0)
+ {
+ int cmp = tolower(*ci) - tolower(*cj);
+ if (cmp != 0) return cmp;
+ ci++;
+ cj++;
+ len--;
+ }
+ return 0;
+}
+#endif
+
+/* Case insentive strcmp() clone
+ */
+#ifdef HAVE_STRICMP
+#define NoCaseStrCmp(si, sj) _stricmp((void *) (si), (void *) (sj))
+#else
+static int NoCaseStrCmp(const char *si, const char *sj)
+{
+ if (0 == strcmp(si, sj))
+ return 0;
+
+ while (*si && tolower(*si) == tolower(*sj))
+ si++, sj++;
+
+ return tolower(*si) - tolower(*sj);
+}
+#endif
+
+/* Case insensitive substring search.
+ * str string to search
+ * slen length of string to search
+ * ptn pattern to search for
+ * plen length of pattern
+ * returns 1 if there's a match otherwise 0
+ */
+static int FindPathElem(const char *str, int slen, const char *ptn, int plen)
+{
+ const char *sp = str;
+
+ while (slen >= plen)
+ {
+ /* We're looking for a match for the specified string bounded by
+ * the start of the string, \ or / at the left and the end of the
+ * string, \ or / at the right. We look for \ as well as / on the
+ * suspicion that a Windows hosted server might accept URIs
+ * containing \.
+ */
+ if (NoCaseMemCmp(sp, ptn, plen) == 0 &&
+ (sp == str || *sp == '\\' || *sp == '/') &&
+ (*sp == '\0' || *sp == '\\' || *sp == '/'))
+ return 1;
+ slen--;
+ sp++;
+ }
+ return 0;
+}
+
+#ifdef NO_CAPI
+/* Alternative to the Domino function */
+static void AddInLogMessageText(char *msg, unsigned short code, ...)
+{
+ va_list ap;
+
+ if (code != NOERROR)
+ printf("Error %d: ", code);
+
+ va_start(ap, code);
+ vprintf(msg, ap);
+ va_end(ap);
+ printf("\n");
+}
+#endif
+
+/* Return 1 iff the supplied string contains "web-inf" (in any case
+ * variation. We don't allow URIs containing web-inf, although
+ * FindPathElem() actually looks for the string bounded by path punctuation
+ * or the ends of the string, so web-inf must appear as a single element
+ * of the supplied URI
+ */
+static int BadURI(const char *uri)
+{
+ static char *wi = "web-inf";
+ return FindPathElem(uri, strlen(uri), wi, strlen(wi));
+}
+
+/* Replacement for strcat() that updates a buffer pointer. It's
+ * probably marginal, but this should be more efficient that strcat()
+ * in cases where the string being concatenated to gets long because
+ * strcat() has to count from start of the string each time.
+ */
+static void Append(char **buf, const char *str)
+{
+ int l = strlen(str);
+ memcpy(*buf, str, l);
+ (*buf)[l] = '\0';
+ *buf += l;
+}
+
+/* Start the response by sending any headers. Invoked by Tomcat. I don't
+ * particularly like the fact that this always allocates memory, but
+ * perhaps jk_pool_alloc() is efficient.
+ */
+static int JK_METHOD StartResponse(jk_ws_service_t *s, int status, const char *reason,
+ const char *const *hdrNames,
+ const char *const *hdrValues, unsigned hdrCount)
+{
+ DEBUG(("StartResponse()\n"));
+ jk_log(logger, JK_LOG_DEBUG, "Into jk_ws_service_t::StartResponse\n");
+
+ if (status < 100 || status > 1000)
+ {
+ jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::StartResponse, invalid status %d\n", status);
+ return JK_FALSE;
+ }
+
+ if (s && s->ws_private)
+ {
+ private_ws_t *p = s->ws_private;
+
+ if (!p->responseStarted)
+ {
+ char *hdrBuf;
+ FilterResponseHeaders frh;
+ int rc, errID;
+
+ p->responseStarted = JK_TRUE;
+
+ if (NULL == reason)
+ reason = "";
+
+ /* Build a single string containing all the headers
+ * because that's what Domino needs.
+ */
+ if (hdrCount > 0)
+ {
+ unsigned i;
+ unsigned hdrLen;
+ char *bufp;
+
+ for (i = 0, hdrLen = 3; i < hdrCount; i++)
+ hdrLen += strlen(hdrNames[i]) + strlen(hdrValues[i]) + 4;
+
+ hdrBuf = jk_pool_alloc(&p->p, hdrLen);
+ bufp = hdrBuf;
+
+ for (i = 0; i < hdrCount; i++)
+ {
+ Append(&bufp, hdrNames[i]);
+ Append(&bufp, ": ");
+ Append(&bufp, hdrValues[i]);
+ Append(&bufp, crlf);
+ }
+
+ Append(&bufp, crlf);
+ }
+ else
+ {
+ hdrBuf = crlf;
+ }
+
+ frh.responseCode = status;
+ frh.reasonText = (char *) reason;
+ frh.headerText = hdrBuf;
+
+ DEBUG(("%d %s\n%s", status, reason, hdrBuf));
+
+ /* Send the headers */
+ rc = p->context->ServerSupport(p->context, kWriteResponseHeaders, &frh, NULL, 0, &errID);
+
+ /*
+ if (rc)
+ {
+ jk_log(logger, JK_LOG_ERROR,
+ "jk_ws_service_t::StartResponse, ServerSupportFunction failed\n");
+ return JK_FALSE;
+ }
+ */
+
+ }
+ return JK_TRUE;
+ }
+
+ jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::StartResponse, NULL parameters\n");
+
+ return JK_FALSE;
+
+}
+
+static int JK_METHOD Read(jk_ws_service_t * s, void *bytes, unsigned len, unsigned *countp)
+{
+ DEBUG(("Read(%p, %p, %u, %p)\n", s, bytes, len, countp));
+ jk_log(logger, JK_LOG_DEBUG, "Into jk_ws_service_t::Read\n");
+
+ if (s && s->ws_private && bytes && countp)
+ {
+ private_ws_t *p = s->ws_private;
+
+ /* Copy data from Domino's buffer. Although it seems slightly
+ * improbably we're believing that Domino always buffers the
+ * entire request in memory. Not properly tested yet.
+ */
+ if (len > p->reqSize) len = p->reqSize;
+ memcpy(bytes, p->reqBuffer, len);
+ p->reqBuffer += len;
+ p->reqSize -= len;
+ *countp = len;
+ return JK_TRUE;
+ }
+
+ jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::Read, NULL parameters\n");
+
+ return JK_FALSE;
+}
+
+static int JK_METHOD Write(jk_ws_service_t *s, const void *bytes, unsigned len)
+{
+ DEBUG(("Write(%p, %p, %u)\n", s, bytes, len));
+ jk_log(logger, JK_LOG_DEBUG, "Into jk_ws_service_t::Write\n");
+
+ if (s && s->ws_private && bytes)
+ {
+ private_ws_t *p = s->ws_private;
+ int errID, rc;
+
+ /* Make sure the response has really started. I'm almost certain
+ * this isn't necessary, but it was in the ISAPI code, so it's in
+ * here too.
+ */
+ if (!p->responseStarted)
+ StartResponse(s, 200, NULL, NULL, NULL, 0);
+
+ DEBUG(("Writing %d bytes of content\n", len));
+
+ /* Send the data */
+ if (len > 0)
+ rc = p->context->WriteClient(p->context, (char *) bytes, len, 0, &errID);
+
+ return JK_TRUE;
+ }
+
+ jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::Write, NULL parameters\n");
+
+ return JK_FALSE;
+}
+
+static int RunProg(const char *cmd)
+{
+#ifdef WIN32
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si); // Start the child process.
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_SHOWMAXIMIZED;
+
+ if (!CreateProcess(NULL, (char *) cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
+ {
+ DWORD err = GetLastError();
+ AddInLogMessageText("Command \"%s\" (error %u)", NOERROR, cmd, err);
+ return FALSE;
+ }
+
+ if (WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, TOMCAT_STARTSTOP_TO))
+ return TRUE;
+
+ AddInLogMessageText("Command \"%s\" didn't complete in time", NOERROR, cmd);
+ return FALSE;
+
+#else
+ int err = system(cmd);
+ if (0 == err) return 1;
+ AddInLogMessageText("Command \"%s\" failed (error %d)", NOERROR, cmd, err);
+ return 0;
+#endif
+}
+
+/* Called when the filter is unloaded. Free various resources and
+ * display a banner.
+ */
+DLLEXPORT unsigned int TerminateFilter(unsigned int reserved)
+{
+ if (initDone)
+ {
+ initDone = JK_FALSE;
+
+ uri_worker_map_free(&uw_map, logger);
+ wc_close(logger);
+ if (logger)
+ jk_close_file_logger(&logger);
+ }
+
+ if (NULL != tomcatStop && '\0' != *tomcatStop)
+ {
+ AddInLogMessageText("Attempting to stop Tomcat: %s", NOERROR, tomcatStop);
+ RunProg(tomcatStop);
+ }
+
+ AddInLogMessageText(FILTERDESC " unloaded", NOERROR);
+
+ jk_close_pool(&cfgPool);
+
+ return kFilterHandledEvent;
+}
+
+/* Called when Domino loads the filter. Reads a load of config data from
+ * the registry and elsewhere and displays a banner.
+ */
+DLLEXPORT unsigned int FilterInit(FilterInitData * filterInitData)
+{
+ int rc = JK_FALSE;
+ jk_map_t *map = NULL;
+
+ jk_open_pool(&cfgPool, NULL, 0); /* empty pool for config data */
+
+ if (!ReadInitData())
+ goto initFailed;
+
+ if (!jk_open_file_logger(&logger, logFile, logLevel))
+ logger = NULL;
+
+ if (NULL != tomcatStart && '\0' != *tomcatStart)
+ {
+ AddInLogMessageText("Attempting to start Tomcat: %s", NOERROR, tomcatStart);
+ RunProg(tomcatStart);
+ }
+
+ if (map_alloc(&map))
+ {
+ if (map_read_properties(map, workerMountFile))
+ if (uri_worker_map_alloc(&uw_map, map, logger))
+ rc = JK_TRUE;
+
+ map_free(&map);
+ }
+
+ if (!rc) goto initFailed;
+
+ rc = JK_FALSE;
+ if (map_alloc(&map))
+ {
+ if (map_read_properties(map, workerFile))
+ /* we add the URI->WORKER MAP since workers using AJP14 will feed it */
+ worker_env.uri_to_worker = &uw_map;
+ GETVARIABLE("SERVER_SOFTWARE", &worker_env.server_name, "Lotus Domino");
+ if (wc_open(map, &worker_env, logger))
+ rc = JK_TRUE;
+
+ map_free(&map);
+ }
+
+ if (!rc) goto initFailed;
+
+ initDone = JK_TRUE;
+
+ filterInitData->appFilterVersion = kInterfaceVersion;
+ filterInitData->eventFlags = kFilterParsedRequest;
+ strcpy(filterInitData->filterDesc, FILTERDESC);
+
+ // Banner
+ AddInLogMessageText("%s loaded", NOERROR, filterInitData->filterDesc);
+
+ return kFilterHandledEvent;
+
+initFailed:
+ AddInLogMessageText("Error loading %s", NOERROR, FILTERDESC);
+
+ return kFilterError;
+}
+
+/* Read parameters from the registry
+ */
+static int ReadInitData(void)
+{
+ int ok = JK_TRUE;
+ const char *v;
+
+#ifdef USE_INIFILE
+// Using an INIFILE
+
+#define GETV(key) inifile_lookup(key)
+
+ ERRTYPE e;
+
+ if (e = inifile_read(&cfgPool, ININAME), ERRNONE != e)
+ {
+ AddInLogMessageText("Error reading: %s, %s", NOERROR, ININAME, ERRTXT(e));
+ return JK_FALSE;
+ }
+
+#else
+// Using the registry
+#define GETV(key) GetRegString(hkey, key)
+
+ HKEY hkey;
+ long rc;
+
+ rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_LOCATION, (DWORD) 0, KEY_READ, &hkey);
+ if (ERROR_SUCCESS != rc) return JK_FALSE;
+
+#endif
+
+#define GETVNB(tag, var) \
+ var = GETV(tag); \
+ if (NULL == var) \
+ { \
+ AddInLogMessageText("%s not defined in %s", NOERROR, tag, ININAME); \
+ ok = JK_FALSE; \
+ }
+
+ GETVNB(JK_LOG_FILE_TAG, logFile)
+ GETVNB(JK_LOG_LEVEL_TAG, v);
+ GETVNB(JK_WORKER_FILE_TAG, workerFile);
+ GETVNB(JK_MOUNT_FILE_TAG, workerMountFile);
+
+ logLevel = (NULL == v) ? 0 : jk_parse_log_level(v);
+
+ tomcatStart = GETV(TOMCAT_START);
+ tomcatStop = GETV(TOMCAT_STOP);
+
+#ifndef USE_INIFILE
+ RegCloseKey(hkey);
+#endif
+
+ return ok;
+}
+
+#ifndef USE_INIFILE
+static const char *GetRegString(HKEY hkey, const char *key)
+{
+ DWORD type = 0;
+ DWORD sz = 0;
+ LONG rc;
+ char *val;
+
+ rc = RegQueryValueEx(hkey, key, (LPDWORD) 0, &type, NULL, &sz);
+ if (rc != ERROR_SUCCESS || type != REG_SZ)
+ return NULL;
+
+ if (val = jk_pool_alloc(&cfgPool, sz), NULL == val)
+ return NULL;
+
+ rc = RegQueryValueEx(hkey, key, (LPDWORD) 0, &type, val, &sz);
+ if (rc == ERROR_SUCCESS)
+ return val;
+
+ return NULL;
+}
+#endif
+
+/* Main entry point for the filter. Called by Domino for every HTTP request.
+ */
+DLLEXPORT unsigned int HttpFilterProc(FilterContext *context, unsigned int eventType, void *eventData)
+{
+ if (initDone)
+ {
+ switch (eventType)
+ {
+ case kFilterParsedRequest:
+ return ParsedRequest(context, (FilterParsedRequest *) eventData);
+ default:
+ break;
+ }
+ }
+ return kFilterNotHandled;
+}
+
+/* Send a simple response. Used when we don't want to bother Tomcat,
+ * which in practice means for various error conditions that we can
+ * detect internally.
+ */
+static void SimpleResponse(FilterContext *context, int status, char *reason, char *body)
+{
+ FilterResponseHeaders frh;
+ int rc, errID;
+ char hdrBuf[35];
+
+ sprintf(hdrBuf, "Content-type: text/html%s%s", crlf, crlf);
+
+ frh.responseCode = status;
+ frh.reasonText = reason;
+ frh.headerText = hdrBuf;
+
+ rc = context->ServerSupport(context, kWriteResponseHeaders, &frh, NULL, 0, &errID);
+ rc = context->WriteClient(context, body, strlen(body), 0, &errID);
+}
+
+/* Called to reject a URI that contains the string "web-inf". We block
+ * these because they may indicate an attempt to invoke arbitrary code.
+ */
+static unsigned int RejectBadURI(FilterContext *context)
+{
+ static char *msg = "<HTML><BODY><H1>Access is Forbidden</H1></BODY></HTML>";
+
+ SimpleResponse(context, 403, "Forbidden", msg);
+ return kFilterHandledRequest;
+}
+
+/* Get the value of a server (CGI) variable as a string
+ */
+static int GetVariable(private_ws_t *ws, char *hdrName,
+ char *buf, DWORD bufsz, char **dest, const char *dflt)
+{
+ int errID;
+
+ if (ws->context->GetServerVariable(ws->context, hdrName, buf, bufsz, &errID))
+ *dest = jk_pool_strdup(&ws->p, buf);
+ else
+ *dest = jk_pool_strdup(&ws->p, dflt);
+
+ DEBUG(("%s = %s\n", hdrName, *dest));
+
+ return JK_TRUE;
+}
+
+/* Get the value of a server (CGI) variable as an integer
+ */
+static int GetVariableInt(private_ws_t *ws, char *hdrName,
+ char *buf, DWORD bufsz, int *dest, int dflt)
+{
+ int errID;
+
+ if (ws->context->GetServerVariable(ws->context, hdrName, buf, bufsz, &errID))
+ *dest = atoi(buf);
+ else
+ *dest = dflt;
+
+ DEBUG(("%s = %d\n", hdrName, *dest));
+
+ return JK_TRUE;
+}
+/* Get the value of a server (CGI) variable as an integer
+ */
+static int GetVariableBool(private_ws_t *ws, char *hdrName,
+ char *buf, DWORD bufsz, int *dest, int dflt)
+{
+ int errID;
+
+ if (ws->context->GetServerVariable(ws->context, hdrName, buf, bufsz, &errID))
+ {
+ if (isdigit(buf[0]))
+ *dest = atoi(buf) != 0;
+ else if (NoCaseStrCmp(buf, "yes") == 0 || NoCaseStrCmp(buf, "on") == 0)
+ *dest = 1;
+ else
+ *dest = 0;
+ }
+ else
+ {
+ *dest = dflt;
+ }
+
+ DEBUG(("%s = %d\n", hdrName, *dest));
+
+ return JK_TRUE;
+}
+
+/* Allocate space for a string given a start pointer and an end pointer
+ * and return a pointer to the allocated, copied string.
+ */
+static char *MemDup(private_ws_t *ws, const char *start, const char *end)
+{
+ char *out = NULL;
+
+ if (start != NULL && end != NULL && end > start)
+ {
+ int len = end - start;
+ out = jk_pool_alloc(&ws->p, len + 1);
+ memcpy(out, start, len);
+ out[len] = '\0';
+ }
+
+ return out;
+}
+
+/* Given all the HTTP headers as a single string parse them into individual
+ * name, value pairs. Called twice: once to work out how many headers there
+ * are, then again to copy them.
+ */
+static int ParseHeaders(private_ws_t *ws, const char *hdrs, int hdrsz, jk_ws_service_t *s)
+{
+ int hdrCount = 0;
+ const char *limit = hdrs + hdrsz;
+ const char *name, *nameEnd;
+ const char *value, *valueEnd;
+
+ while (hdrs < limit)
+ {
+ /* Skip line *before* doing anything, cos we want to lose the first line which
+ * contains the request.
+ */
+ while (hdrs < limit && (*hdrs != '\n' && *hdrs != '\r'))
+ hdrs++;
+ while (hdrs < limit && (*hdrs == '\n' || *hdrs == '\r'))
+ hdrs++;
+
+ if (hdrs >= limit)
+ break;
+
+ name = nameEnd = value = valueEnd = NULL;
+
+ name = hdrs;
+ while (hdrs < limit && *hdrs >= ' ' && *hdrs != ':')
+ hdrs++;
+ nameEnd = hdrs;
+
+ if (hdrs < limit && *hdrs == ':')
+ {
+ hdrs++;
+ while (hdrs < limit && (*hdrs == ' ' || *hdrs == '\t'))
+ hdrs++;
+ value = hdrs;
+ while (hdrs < limit && *hdrs >= ' ')
+ hdrs++;
+ valueEnd = hdrs;
+ }
+
+ if (s->headers_names != NULL && s->headers_values != NULL)
+ {
+ s->headers_names[hdrCount] = MemDup(ws, name, nameEnd);
+ s->headers_values[hdrCount] = MemDup(ws, value, valueEnd);
+ DEBUG(("%s = %s\n", s->headers_names[hdrCount], s->headers_values[hdrCount]));
+ }
+ hdrCount++;
+ }
+
+ return hdrCount;
+}
+
+/* A couple of utility macros to supply standard arguments to GetVariable() and
+ * GetVariableInt().
+ */
+#define GETVARIABLE(name, dest, dflt) GetVariable(ws, (name), workBuf, sizeof(workBuf), (dest), (dflt))
+#define GETVARIABLEINT(name, dest, dflt) GetVariableInt(ws, (name), workBuf, sizeof(workBuf), (dest), (dflt))
+#define GETVARIABLEBOOL(name, dest, dflt) GetVariableBool(ws, (name), workBuf, sizeof(workBuf), (dest), (dflt))
+
+/* Set up all the necessary jk_* workspace based on the current HTTP request.
+ */
+static int InitService(private_ws_t *ws, jk_ws_service_t *s)
+{
+ /* This is the only fixed size buffer left. It won't be overflowed
+ * because the Domino API that reads into the buffer accepts a length
+ * constraint, and it's unlikely ever to be exhausted because the
+ * strings being will typically be short, but it's still aesthetically
+ * troublesome.
+ */
+ char workBuf[16 * 1024];
+ FilterRequest fr;
+ char *hdrs, *qp;
+ int hdrsz;
+ int errID;
+ int hdrCount;
+ int rc /*, dummy*/;
+
+ static char *methodName[] = { "", "HEAD", "GET", "POST", "PUT", "DELETE" };
+
+ rc = ws->context->GetRequest(ws->context, &fr, &errID);
+
+ s->jvm_route = NULL;
+ s->start_response = StartResponse;
+ s->read = Read;
+ s->write = Write;
+
+ s->req_uri = jk_pool_strdup(&ws->p, fr.URL);
+ s->query_string = NULL;
+ if (qp = strchr(s->req_uri, '?'), qp != NULL)
+ {
+ *qp++ = '\0';
+ if (strlen(qp))
+ s->query_string = qp;
+ }
+
+ GETVARIABLE("AUTH_TYPE", &s->auth_type, "");
+ GETVARIABLE("REMOTE_USER", &s->remote_user, "");
+ GETVARIABLE("SERVER_PROTOCOL", &s->protocol, "");
+ GETVARIABLE("REMOTE_HOST", &s->remote_host, "");
+ GETVARIABLE("REMOTE_ADDR", &s->remote_addr, "");
+ GETVARIABLE("SERVER_NAME", &s->server_name, "");
+ GETVARIABLEINT("SERVER_PORT", &s->server_port, 80);
+ GETVARIABLE("SERVER_SOFTWARE", &s->server_software, "Lotus Domino");
+ GETVARIABLEINT("CONTENT_LENGTH", &s->content_length, 0);
+
+ /* SSL Support
+ */
+ GETVARIABLEBOOL("HTTPS", &s->is_ssl, 0);
+
+ s->ssl_cert_len = fr.clientCertLen;
+ s->ssl_cert = fr.clientCert;
+ s->ssl_cipher = NULL; /* required by Servlet 2.3 Api */
+ s->ssl_session = NULL;
+ s->ssl_key_size = -1; /* required by Servlet 2.3 Api, added in jtc */
+
+ if (ws->reqData->requestMethod < 0 ||
+ ws->reqData->requestMethod >= sizeof(methodName) / sizeof(methodName[0]))
+ return JK_FALSE;
+
+ s->method = methodName[ws->reqData->requestMethod];
+
+ s->headers_names = NULL;
+ s->headers_values = NULL;
+ s->num_headers = 0;
+
+ /* There's no point in doing this because Domino never seems to
+ * set any of these CGI variables.
+ */
+ /*
+ if (s->is_ssl)
+ {
+ char *sslNames[] =
+ {
+ "CERT_ISSUER", "CERT_SUBJECT", "CERT_COOKIE", "CERT_FLAGS", "CERT_SERIALNUMBER",
+ "HTTPS_SERVER_SUBJECT", "HTTPS_SECRETKEYSIZE", "HTTPS_SERVER_ISSUER", "HTTPS_KEYSIZE"
+ };
+
+ char *sslValues[] =
+ {
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL
+ };
+
+ unsigned i, varCount = 0;
+
+ DEBUG(("SSL request\n"));
+
+ for (i = 0; i < sizeof(sslNames)/sizeof(sslNames[0]); i++)
+ {
+ GETVARIABLE(sslNames[i], &sslValues[i], NULL);
+ if (sslValues[i]) varCount++;
+ }
+
+ /* Andy, some SSL vars must be mapped directly in s->ssl_cipher,
+ * ssl-session and s->ssl_key_size
+ * ie:
+ * Cipher could be "RC4-MD5"
+ * KeySize 128 (bits)
+ * SessionID a string containing the UniqID used in SSL dialogue
+ */
+
+ if (varCount > 0)
+ {
+ unsigned j;
+
+ s->attributes_names = jk_pool_alloc(&ws->p, varCount * sizeof (char *));
+ s->attributes_values = jk_pool_alloc(&ws->p, varCount * sizeof (char *));
+
+ j = 0;
+ for (i = 0; i < sizeof(sslNames)/sizeof(sslNames[0]); i++)
+ {
+ if (sslValues[i])
+ {
+ s->attributes_names[j] = sslNames[i];
+ s->attributes_values[j] = sslValues[i];
+ j++;
+ }
+ }
+ s->num_attributes = varCount;
+ }
+ }
+ */
+
+ /* Duplicate all the headers now */
+
+ hdrsz = ws->reqData->GetAllHeaders(ws->context, &hdrs, &errID);
+ DEBUG(("\nGot headers (length %d)\n--------\n%s\n--------\n\n", hdrsz, hdrs));
+
+ s->headers_names =
+ s->headers_values = NULL;
+ hdrCount = ParseHeaders(ws, hdrs, hdrsz, s);
+ DEBUG(("Found %d headers\n", hdrCount));
+ s->num_headers = hdrCount;
+ s->headers_names = jk_pool_alloc(&ws->p, hdrCount * sizeof(char *));
+ s->headers_values = jk_pool_alloc(&ws->p, hdrCount * sizeof(char *));
+ hdrCount = ParseHeaders(ws, hdrs, hdrsz, s);
+
+ return JK_TRUE;
+}
+
+/* Handle an HTTP request. Works out whether Tomcat will be interested then either
+ * despatches it to Tomcat or passes it back to Domino.
+ */
+static unsigned int ParsedRequest(FilterContext *context, FilterParsedRequest *reqData)
+{
+ unsigned int errID;
+ int rc;
+ FilterRequest fr;
+ int result = kFilterNotHandled;
+
+ DEBUG(("\nParsedRequest starting\n"));
+
+ rc = context->GetRequest(context, &fr, &errID);
+
+ if (fr.URL && strlen(fr.URL))
+ {
+ char *uri = fr.URL;
+ char *workerName, *qp;
+
+ if (qp = strchr(uri, '?'), qp != NULL) *qp = '\0';
+ workerName = map_uri_to_worker(uw_map, uri, logger);
+ if (qp) *qp = '?';
+
+ DEBUG(("Worker for this URL is %s\n", workerName));
+
+ if (NULL != workerName)
+ {
+ private_ws_t ws;
+ jk_ws_service_t s;
+ jk_pool_atom_t buf[SMALL_POOL_SIZE];
+
+ if (BadURI(uri))
+ return RejectBadURI(context);
+
+ /* Go dispatch the call */
+
+ jk_init_ws_service(&s);
+ jk_open_pool(&ws.p, buf, sizeof (buf));
+
+ ws.responseStarted = JK_FALSE;
+ ws.context = context;
+ ws.reqData = reqData;
+
+ ws.reqSize = context->GetRequestContents(context, &ws.reqBuffer, &errID);
+
+ s.ws_private = &ws;
+ s.pool = &ws.p;
+
+ if (InitService(&ws, &s))
+ {
+ jk_worker_t *worker = wc_get_worker_for_name(workerName, logger);
+
+ jk_log(logger, JK_LOG_DEBUG, "HttpExtensionProc %s a worker for name %s\n",
+ worker ? "got" : "could not get", workerName);
+
+ if (worker)
+ {
+ jk_endpoint_t *e = NULL;
+
+ if (worker->get_endpoint(worker, &e, logger))
+ {
+ int recover = JK_FALSE;
+
+ if (e->service(e, &s, logger, &recover))
+ {
+ result = kFilterHandledRequest;
+ jk_log(logger, JK_LOG_DEBUG, "HttpExtensionProc service() returned OK\n");
+ DEBUG(("HttpExtensionProc service() returned OK\n"));
+ }
+ else
+ {
+ result = kFilterError;
+ jk_log(logger, JK_LOG_ERROR, "HttpExtensionProc error, service() failed\n");
+ DEBUG(("HttpExtensionProc error, service() failed\n"));
+ }
+ e->done(&e, logger);
+ }
+ }
+ else
+ {
+ jk_log(logger, JK_LOG_ERROR,
+ "HttpExtensionProc error, could not get a worker for name %s\n",
+ workerName);
+ }
+ }
+
+ jk_close_pool(&ws.p);
+ }
+ }
+
+ return result;
+}