You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by Steven Velez <sv...@alventive.com> on 2002/11/12 18:11:45 UTC

[PATCH] Fixing Interoperability problem in iis connector

I have noticed problems in the Jakarta tomcat IIS connector when it is used
in conjunction with Netegrity's SiteMinder web agent.  The following patch
works-around the problem by giving the user the option of passing data
between the filter and the extension using shared memory instead of headers.
The changes include two new files and I could not include them in the CVS
diff since I could not "add" the files to the repository so I have in-lined
them and hopefully appropriately delimited them.

Index: isapi.dsp
===================================================================
RCS file:
/home/cvspublic/jakarta-tomcat-connectors/jk/native/iis/isapi.dsp,v
retrieving revision 1.9
diff -u -r1.9 isapi.dsp
--- isapi.dsp	9 Apr 2002 23:06:52 -0000	1.9
+++ isapi.dsp	12 Nov 2002 16:58:07 -0000
@@ -170,6 +170,10 @@
 
 SOURCE=..\common\jk_worker.c
 # End Source File
+# Begin Source File
+
+SOURCE=.\req_info.c
+# End Source File
 # End Group
 # Begin Group "Header Files"
 
@@ -265,6 +269,10 @@
 # Begin Source File
 
 SOURCE=..\common\jk_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\req_info.h
 # End Source File
 # End Group
 # Begin Group "Resource Files"
Index: jk_isapi_plugin.c
===================================================================
RCS file:
/home/cvspublic/jakarta-tomcat-connectors/jk/native/iis/jk_isapi_plugin.c,v
retrieving revision 1.18
diff -u -r1.18 jk_isapi_plugin.c
--- jk_isapi_plugin.c	25 Sep 2002 00:49:40 -0000	1.18
+++ jk_isapi_plugin.c	12 Nov 2002 16:58:07 -0000
@@ -78,6 +78,8 @@
 #include "jk_worker.h"
 #include "jk_uri_worker_map.h"
 
+#include "req_info.h"
+
 #define VERSION_STRING "Jakarta/ISAPI/" JK_VERSTRING
 
 #define DEFAULT_WORKER_NAME ("ajp13")
@@ -109,6 +111,8 @@
 #define URI_SELECT_UNPARSED_VERB    ("unparsed")
 #define URI_SELECT_ESCAPED_VERB     ("escaped")
 
+#define SHMEM_REQUEST_INFO_TAG  ("use_shared_mem")
+
 #define BAD_REQUEST		-1
 #define BAD_PATH		-2
 #define MAX_SERVERNAME			128
@@ -142,6 +146,17 @@
     }           \
 }\
 
+#define GET_REQ_INFO_VALUE(ri, name, var) { \
+    char *temp; \
+    if (temp = rinfo_get_##name (ri)) \
+    { \
+        (var) = jk_pool_strdup(&private_data->p, temp); \
+    } else { \
+        (var) = NULL; \
+    } \
+} \
+
+
 static char  ini_file_name[MAX_PATH];
 static int   using_ini_file = JK_FALSE;
 static int   is_inited = JK_FALSE;
@@ -159,6 +174,7 @@
 static int  log_level = JK_LOG_EMERG_LEVEL;
 static char worker_file[MAX_PATH * 2];
 static char worker_mount_file[MAX_PATH * 2];
+static int  shmem_enabled = JK_FALSE;
 
 #define URI_SELECT_OPT_PARSED       0
 #define URI_SELECT_OPT_UNPARSED     1
@@ -670,6 +686,7 @@
         char Host[INTERNET_MAX_URL_LENGTH]="";
         char Port[INTERNET_MAX_URL_LENGTH]="";
         char Translate[INTERNET_MAX_URL_LENGTH];
+        char target_uri_buff[INTERNET_MAX_URL_LENGTH]="";
 		BOOL (WINAPI * GetHeader) 
 			(struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
LPVOID lpvBuffer, LPDWORD lpdwSize );
 		BOOL (WINAPI * SetHeader) 
@@ -681,6 +698,8 @@
         DWORD szHost = sizeof(Host);
         DWORD szPort = sizeof(Port);
         DWORD szTranslate = sizeof(Translate);
+		request_information_t *p_request_info = NULL;
+        request_info_name_t info_name = 0;
 
 		if (iis5) {
 
GetHeader=((PHTTP_FILTER_AUTH_COMPLETE_INFO)pvNotification)->GetHeader;
@@ -700,11 +719,13 @@
         /*
          * Just in case somebody set these headers in the request!
          */
-        SetHeader(pfc, URI_HEADER_NAME, NULL);
-        SetHeader(pfc, QUERY_HEADER_NAME, NULL);
-        SetHeader(pfc, WORKER_HEADER_NAME, NULL);
-        SetHeader(pfc, TOMCAT_TRANSLATE_HEADER_NAME, NULL);
-        
+        if (!shmem_enabled) {
+            SetHeader(pfc, URI_HEADER_NAME, NULL);
+            SetHeader(pfc, QUERY_HEADER_NAME, NULL);
+            SetHeader(pfc, WORKER_HEADER_NAME, NULL);
+            SetHeader(pfc, TOMCAT_TRANSLATE_HEADER_NAME, NULL);
+        }
+
         if (!GetHeader(pfc, "url", (LPVOID)uri, (LPDWORD)&sz)) {
             jk_log(logger, JK_LOG_ERROR, 
                    "HttpFilterProc error while getting the url\n");
@@ -802,14 +823,30 @@
                     forwardURI = uri;
                 }
 
-                if(!AddHeader(pfc, URI_HEADER_NAME, forwardURI) || 
-                   ( (query != NULL && strlen(query) > 0)
-                           ? !AddHeader(pfc, QUERY_HEADER_NAME, query) :
FALSE ) || 
-                   !AddHeader(pfc, WORKER_HEADER_NAME, worker) ||
-                   !SetHeader(pfc, "url", extension_uri)) {
-                    jk_log(logger, JK_LOG_ERROR, 
-                           "HttpFilterProc error while adding request
headers\n");
-                    return SF_STATUS_REQ_ERROR;
+                if (!shmem_enabled) {
+                    if(!AddHeader(pfc, URI_HEADER_NAME, forwardURI) || 
+                       ( (query != NULL && strlen(query) > 0)
+                               ? !AddHeader(pfc, QUERY_HEADER_NAME, query)
: FALSE ) || 
+                       !AddHeader(pfc, WORKER_HEADER_NAME, worker) ||
+                       !SetHeader(pfc, "url", extension_uri)) {
+                        jk_log(logger, JK_LOG_ERROR, 
+                               "HttpFilterProc error while adding request
headers\n");
+                        return SF_STATUS_REQ_ERROR;
+                    }
+                }
+                else {
+                    p_request_info = rinfo_new();
+                    if (p_request_info == NULL) {
+                        jk_log(logger, JK_LOG_ERROR, "HttpFilterProc failed
allocating memory "
+	                        "for request information\n");
+                        return SF_STATUS_REQ_ERROR;
+                    }
+
+                    rinfo_set_uri(p_request_info, forwardURI);
+                    if (query != NULL && strlen(query) > 0) {
+	                    rinfo_set_query_string(p_request_info, query);
+                    }
+                    rinfo_set_worker(p_request_info, worker);
                 }
 				
                 /* Move Translate: header to a temporary header so
@@ -821,9 +858,33 @@
                     if (!AddHeader(pfc, TOMCAT_TRANSLATE_HEADER_NAME,
Translate)) {
                         jk_log(logger, JK_LOG_ERROR, 
                           "HttpFilterProc error while adding
Tomcat-Translate headers\n");
+                        rinfo_delete(p_request_info);
                         return SF_STATUS_REQ_ERROR;
                     }
-                SetHeader(pfc, "Translate:", NULL);
+
+                    SetHeader(pfc, "Translate:", NULL);
+                }
+
+
+                if (shmem_enabled) {
+                    if ((info_name = rinfo_map_insert(p_request_info)) ==
REQUEST_INFO_ERR_NAME) {
+                        rinfo_delete(p_request_info);
+                        jk_log(logger, JK_LOG_ERROR, 
+                               "HttpFilterProc failed to set the request
info\n");
+                        return SF_STATUS_REQ_ERROR;
+                    }
+                
+                    if (!SetHeader(pfc, "url", 
+                                   rinfo_append_name_to_uri(extension_uri,
info_name, 
+
target_uri_buff, 
+
INTERNET_MAX_URL_LENGTH)
+                                   )) {
+
+                        rinfo_delete(p_request_info);
+                        jk_log(logger, JK_LOG_ERROR, 
+                               "HttpFilterProc error while adding request
headers\n");
+                        return SF_STATUS_REQ_ERROR;
+                    }    
                 }
             } else {
                 jk_log(logger, JK_LOG_DEBUG, 
@@ -961,6 +1022,12 @@
 			uri_worker_map_free(&uw_map, logger);
 			is_mapread = JK_FALSE;
 		}
+
+        if (shmem_enabled)
+        {
+		    rinfo_close_map();
+        }
+
         wc_close(logger);
         if (logger) {
             jk_close_file_logger(&logger);
@@ -1023,6 +1090,10 @@
     jk_log(logger, JK_LOG_DEBUG, "Using worker file %s.\n", worker_file);
     jk_log(logger, JK_LOG_DEBUG, "Using worker mount file %s.\n",
worker_mount_file);
     jk_log(logger, JK_LOG_DEBUG, "Using uri select %d.\n",
uri_select_option);
+    if (shmem_enabled) {
+        jk_log(logger, JK_LOG_DEBUG, "Using shared memory to pass
information from "
+               "the filter to the extension.\n");
+    }
 
     if (map_alloc(&map)) {
         if (map_read_properties(map, worker_mount_file)) {
@@ -1079,6 +1150,11 @@
         }
     }
 
+	if (rc && shmem_enabled)
+	{
+		rc = rinfo_init_map(logger);
+	}
+
     return rc;
 }
 
@@ -1108,6 +1184,32 @@
     return -1;
 }
 
+
+int parse_enable_shmem(const char *shmem)
+{
+    char *shmem_indicators[] = {"on", "true", "enable", NULL};
+    int ret =  JK_FALSE;
+    int sh_mem_int = 0;
+    char **cur_indicator = shmem_indicators;
+
+    if (sscanf(shmem, "%d", &sh_mem_int) != EOF && sh_mem_int)
+    {
+        ret = JK_TRUE;
+    } 
+    
+
+    while (!ret && *cur_indicator)
+    {
+        if (stricmp(shmem, *(cur_indicator++)) == 0)
+        {
+            ret = JK_TRUE;
+        }
+    }
+
+    return ret;
+}
+
+
 static int read_registry_init_data(void)
 {
     char tmpbuf[INTERNET_MAX_URL_LENGTH];
@@ -1162,6 +1264,10 @@
                 ok = JK_FALSE;
             }
         }
+        tmp = map_get_string(map, SHMEM_REQUEST_INFO_TAG, NULL);
+        if (tmp) {
+            shmem_enabled = parse_enable_shmem(tmp);
+        }
     
     } else {
         rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
@@ -1230,6 +1336,13 @@
             }
         }
 
+        if (get_registry_config_parameter(hkey,
+                                          SHMEM_REQUEST_INFO_TAG,
+                                          tmpbuf,
+                                          sizeof(tmpbuf))) {
+            shmem_enabled = parse_enable_shmem(tmpbuf);
+        }
+
         RegCloseKey(hkey);
     }    
     return ok;
@@ -1263,6 +1376,8 @@
                            char **worker_name) 
 {
     char huge_buf[16 * 1024]; /* should be enough for all */
+    request_information_t *preq_info = NULL;
+    request_info_name_t name = 0;
 
     DWORD huge_buf_sz;
 
@@ -1272,17 +1387,40 @@
     s->read = read;
     s->write = write;
 
-    GET_SERVER_VARIABLE_VALUE(HTTP_WORKER_HEADER_NAME, (*worker_name));

-    GET_SERVER_VARIABLE_VALUE(HTTP_URI_HEADER_NAME, s->req_uri);     
-    GET_SERVER_VARIABLE_VALUE(HTTP_QUERY_HEADER_NAME, s->query_string);

-
-    if (s->req_uri == NULL) {
-        s->query_string = private_data->lpEcb->lpszQueryString;
-        *worker_name    = DEFAULT_WORKER_NAME;
-        GET_SERVER_VARIABLE_VALUE("URL", s->req_uri);       
-        if (unescape_url(s->req_uri) < 0)
+    if (! shmem_enabled) {
+        GET_SERVER_VARIABLE_VALUE(HTTP_WORKER_HEADER_NAME, (*worker_name));

+        GET_SERVER_VARIABLE_VALUE(HTTP_URI_HEADER_NAME, s->req_uri);     
+        GET_SERVER_VARIABLE_VALUE(HTTP_QUERY_HEADER_NAME, s->query_string);

+
+        if (s->req_uri == NULL) {
+            s->query_string = private_data->lpEcb->lpszQueryString;
+            *worker_name    = DEFAULT_WORKER_NAME;
+            GET_SERVER_VARIABLE_VALUE("URL", s->req_uri);       
+            if (unescape_url(s->req_uri) < 0)
+                return JK_FALSE;
+            getparents(s->req_uri);
+        }
+    }
+    else {
+
+        if ((name =
rinfo_name_from_query(private_data->lpEcb->lpszQueryString)) ==
+            REQUEST_INFO_ERR_NAME) {
+
+            jk_log(logger, JK_LOG_ERROR, "Failed to read request name from
"
+                   "query string\n");
             return JK_FALSE;
-        getparents(s->req_uri);
+        }
+
+        if (!(preq_info = rinfo_map_remove_at(name))) {
+
+            jk_log(logger, JK_LOG_ERROR, "Failed to find and remove the
named "
+                   "request: %d.\n", name);
+            return JK_FALSE;
+        }
+
+        GET_REQ_INFO_VALUE(preq_info, worker, (*worker_name));
+        GET_REQ_INFO_VALUE(preq_info, uri, s->req_uri);
+        GET_REQ_INFO_VALUE(preq_info, query_string, s->query_string);
     }
     
     GET_SERVER_VARIABLE_VALUE("AUTH_TYPE", s->auth_type);
@@ -1406,7 +1544,10 @@
             unsigned len_of_http_prefix = strlen("HTTP_");
             BOOL need_content_length_header = (s->content_length == 0);
             
-            cnt -= 2; /* For our two special headers */
+            if (!shmem_enabled) {
+                cnt -= 2; /* For our two special headers */
+            }
+
             /* allocate an extra header slot in case we need to add a
content-length header */
             s->headers_names  = jk_pool_alloc(&private_data->p, (cnt + 1) *
sizeof(char *));
             s->headers_values = jk_pool_alloc(&private_data->p, (cnt + 1) *
sizeof(char *));







File: /home/cvspublic/jakarta-tomcat-connectors/jk/native/iis/req_info.h
/* =========================================================================
*
 *
*
 *                 The Apache Software License,  Version 1.1
*
 *
*
 *          Copyright (c) 1999-2001 The Apache Software Foundation.
*
 *                           All rights reserved.
*
 *
*
 * =========================================================================
*
 *
*
 * Redistribution and use in source and binary forms,  with or without modi-
*
 * fication, are permitted provided that the following conditions are met:
*
 *
*
 * 1. Redistributions of source code  must retain the above copyright notice
*
 *    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. The end-user documentation  included with the redistribution,  if any,
*
 *    must include the following acknowlegement:
*
 *
*
 *       "This product includes  software developed  by the Apache  Software
*
 *        Foundation <http://www.apache.org/>."
*
 *
*
 *    Alternately, this acknowlegement may appear in the software itself, if
*
 *    and wherever such third-party acknowlegements normally appear.
*
 *
*
 * 4. The names  "The  Jakarta  Project",  "Jk",  and  "Apache  Software
*
 *    Foundation"  must not be used  to endorse or promote  products derived
*
 *    from this  software without  prior  written  permission.  For  written
*
 *    permission, please contact <ap...@apache.org>.
*
 *
*
 * 5. Products derived from this software may not be called "Apache" nor may
*
 *    "Apache" appear in their names without prior written permission of the
*
 *    Apache Software Foundation.
*
 *
*
 * THIS SOFTWARE IS PROVIDED "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 APACHE  SOFTWARE  FOUNDATION 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 indivi-
*
 * duals on behalf of the  Apache Software Foundation.  For more information
*
 * on the Apache Software Foundation, please see <http://www.apache.org/>.
*
 *
*
 * =========================================================================
*/

/***************************************************************************
 * Description: Utilities to to keep track of request information          *
 * Author:      Steven Velez <sv...@alventive.com>                        *

 * Version:     $Revision:     $                                           *
 
***************************************************************************/


#ifndef REQ_INFO_H
#define REQ_INFO_H

#include "jk_pool.h"
#include "jk_logger.h"

#ifdef __cpluplus
extern "C"
{
#endif

/*
 * The information 'class' that will store information
 * for a particular request.  This information will be set in the filter
 * and read in the extension.
 */
struct _request_information
{
    /* The name of the worker selected by the filter.
     * Relaces "TOMCATWORKER:" */
    char *worker_name;
    /* The uri of the original request.
     * Replaces "TOMCATURI:" */
	char *uri;
    /* The query string of the original request.  
     * Replaces "TOMCATQUERY:" */
	char *query_string;

    /** For keeping track of the various allocations of member data */
    jk_pool_t p;
    jk_pool_atom_t buff[BIG_POOL_SIZE];
};
typedef struct _request_information request_information_t;
typedef unsigned long request_info_name_t;

#define REQUEST_INFO_ERR_NAME (0)

/* Management functions to intialize and destroy a global request info map
*/
int rinfo_init_map(jk_logger_t *log);
int rinfo_close_map();

/* Insert a request information object in the map */
request_info_name_t rinfo_map_insert(request_information_t * ri);
/* Find the request information object related to the given request.
 * This function returns 'NULL' if the object can not be found */
request_information_t *rinfo_map_at(request_info_name_t req);
/* Find the requwst information object related to the give request.
 * This function will also remove the object from the map. */
request_information_t *rinfo_map_remove_at(request_info_name_t req);

/* utility function to append a request info name to a uri 
 * the return value of the function is a pointer to the caller-supplied
 * buffer */
char *rinfo_append_name_to_uri(char *uri, request_info_name_t req,
				               char *buff, size_t size);
/* Utiltity function to retreive a request info name from 
 * a query string */
request_info_name_t rinfo_name_from_query(char *query);

/* Object lifetime management functions... these should be used to 
 * create and destroy a request_info object */
request_information_t *rinfo_new();
void rinfo_delete(request_information_t * ri);

/* functions to set data in the request info object.  These functions
 * duplicate the parameters, so it will remain the responsibility of
 * the caller to manage the memory pointed to in the second parameter. */
void rinfo_set_worker(request_information_t * ri, char *name);
void rinfo_set_uri(request_information_t * ri, char *uri);
void rinfo_set_query_string(request_information_t * ri, char *qs);

/* functions to access data in the request info object.
 * They return poitners to internal buffers, so callers should not
manipulate
 * them directly. */
char *rinfo_get_worker(request_information_t * ri);
char *rinfo_get_uri(request_information_t * ri);
char *rinfo_get_query_string(request_information_t * ri);

#ifdef __cplusplus
}
#endif
#endif





File: /home/cvspublic/jakarta-tomcat-connectors/jk/native/iis/req_info.c
/* =========================================================================
*
 *
*
 *                 The Apache Software License,  Version 1.1
*
 *
*
 *          Copyright (c) 1999-2001 The Apache Software Foundation.
*
 *                           All rights reserved.
*
 *
*
 * =========================================================================
*
 *
*
 * Redistribution and use in source and binary forms,  with or without modi-
*
 * fication, are permitted provided that the following conditions are met:
*
 *
*
 * 1. Redistributions of source code  must retain the above copyright notice
*
 *    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. The end-user documentation  included with the redistribution,  if any,
*
 *    must include the following acknowlegement:
*
 *
*
 *       "This product includes  software developed  by the Apache  Software
*
 *        Foundation <http://www.apache.org/>."
*
 *
*
 *    Alternately, this acknowlegement may appear in the software itself, if
*
 *    and wherever such third-party acknowlegements normally appear.
*
 *
*
 * 4. The names  "The  Jakarta  Project",  "Jk",  and  "Apache  Software
*
 *    Foundation"  must not be used  to endorse or promote  products derived
*
 *    from this  software without  prior  written  permission.  For  written
*
 *    permission, please contact <ap...@apache.org>.
*
 *
*
 * 5. Products derived from this software may not be called "Apache" nor may
*
 *    "Apache" appear in their names without prior written permission of the
*
 *    Apache Software Foundation.
*
 *
*
 * THIS SOFTWARE IS PROVIDED "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 APACHE  SOFTWARE  FOUNDATION 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 indivi-
*
 * duals on behalf of the  Apache Software Foundation.  For more information
*
 * on the Apache Software Foundation, please see <http://www.apache.org/>.
*
 *
*
 * =========================================================================
*/

/***************************************************************************
 * Description: Utilities to to keep track of request information          *
 * Author:      Steven Velez <sv...@alventive.com>                        *

 * Version:     $Revision:     $                                           *
 
***************************************************************************/

#include "req_info.h"
#include "jk_mt.h"
#include "jk_util.h"

#include <limits.h>
#include <stddef.h>

/*
 * A node defination for a linked list
 * that will implement a custom map data structure.
 * I am using this as opposed to the jk_map because we need more
 * precise control of memory allocation for the sake of iffeciency since 
 * this will be used as a global structure.
 */
static struct _request_map_node
{
    request_info_name_t name;
    request_information_t *ri;
    struct _request_map_node *next;
};

/* use a define instead of a typedef so that this definition
 * will not be accessible outside this file */
#define NODE_T struct _request_map_node
/* The querystring variable that will hold a request_info_name */
#define QUERY_VAR "REQ_INFO_NAME"

#if defined(_DEBUG)
/* Helper macros for logging */
#define LOG_CRIT_SEC(action, result) {\
    if (!result) { \
        jk_log(logger, JK_LOG_INFO, "Did not " #action \
               " the critical section\n"); \
    } \
} \

#define LOG_ENTER(func_name) { \
    jk_log(logger, JK_LOG_DEBUG, "--> ENTERING " #func_name " <---\n");\
}\

#define LOG_EXIT(func_name) { \
    jk_log(logger, JK_LOG_DEBUG, "--> EXITING " #func_name " <---\n");\
}\

#else
#define LOG_CRIT_SEC(action, result) 
#define LOG_ENTER(func_name) 
#define LOG_EXIT(func_name) 
#endif


/* The root of the map */
static NODE_T *g_map_head = NULL;
/* The insertion point of the map */
static NODE_T *g_map_tail = NULL;
/* Synchronization structure */
static JK_CRIT_SEC g_crit_sec;
/* The next name value */
static request_info_name_t g_next_name = 0;
/* logging object */
jk_logger_t *logger = NULL;

int rinfo_init_map(jk_logger_t *l)
{
    int success = JK_FALSE;
    LOG_ENTER(rinfo_init_map);

    if (l == NULL) {
        LOG_EXIT(rinfo_init_map);
        return JK_FALSE;
    }
    logger = l;

    JK_INIT_CS(&g_crit_sec, success);
    if (success == JK_FALSE) {
        jk_log(logger, JK_LOG_ERROR, "Failed to initialize the map critical
"
               "section\n");
        LOG_EXIT(rinfo_init_map);
        return JK_FALSE;
    }

    g_map_head = NULL;
    g_map_tail = g_map_head;

    LOG_EXIT(rinfo_init_map);
    return JK_TRUE;
}


int rinfo_close_map()
{
    int success = JK_FALSE;
    NODE_T *pcurrent = g_map_head;
    NODE_T *ptemp = NULL;
    LOG_ENTER(rinfo_init_map);

    /* remove the current entries */
    JK_ENTER_CS(&g_crit_sec, success);
    if (success == JK_FALSE) {
	    /* we are in big trouble; however, I guess a memory leak on
shutdown 
         * is better than a crash */
        jk_log(logger, JK_LOG_INFO, "Did not obtain a critical section
object "
               "for freeing map resources.\n");
        LOG_EXIT(rinfo_init_map);
	    return JK_FALSE;
    }

    while (pcurrent != NULL) {
        rinfo_delete(pcurrent->ri);
        ptemp = pcurrent;
        pcurrent = pcurrent->next;

        if (ptemp != NULL) {
	        free((void *) ptemp);
        }
    }

    g_map_head = NULL;
    g_map_tail = NULL;

    JK_LEAVE_CS(&g_crit_sec, success);
    LOG_CRIT_SEC(leave, success);

    /* get rid of the critical section */
    JK_DELETE_CS(&g_crit_sec, success);
    if (!success) {
        jk_log(logger, JK_LOG_INFO, "Failed to delete critical section
object "
               "after freeing map resources.\n");
    }

    LOG_EXIT(rinfo_init_map);
    return JK_TRUE;
}


request_info_name_t rinfo_map_insert(request_information_t * ri)
{
    int success = JK_FALSE;
    request_info_name_t new_name = REQUEST_INFO_ERR_NAME;
    NODE_T *new_node = NULL;
    LOG_ENTER(rinfo_map_insert);

    JK_ENTER_CS(&g_crit_sec, success);
    if (success == JK_FALSE) {
        LOG_CRIT_SEC(enter, success);
        LOG_EXIT(rinfo_map_insert);
        return REQUEST_INFO_ERR_NAME;
    }

    g_next_name = (++g_next_name) % ULONG_MAX;
    g_next_name = REQUEST_INFO_ERR_NAME == g_next_name ? 
                  g_next_name++ : g_next_name;
    new_name = g_next_name;

    new_node = (NODE_T *) malloc(sizeof(NODE_T));
    if (new_node == NULL) {
        jk_log(logger, JK_LOG_EMERG, "Failed to allocate memory for a "
               "new map entry\n");

        JK_LEAVE_CS(&g_crit_sec, success);
        LOG_CRIT_SEC(leave, success);

        LOG_EXIT(rinfo_map_insert);
        return REQUEST_INFO_ERR_NAME;
    }

    new_node->name = new_name;
    new_node->ri = ri;
    new_node->next = NULL;

    if (g_map_head == NULL) {
        g_map_head = new_node;
    }
    else {
        g_map_tail->next = new_node;
    }

    g_map_tail = new_node;

    jk_log(logger, JK_LOG_DEBUG, "Added req: %d\nhead = 0x%x tail = 0x%x\n",

           new_name, g_map_head, g_map_tail);
    JK_LEAVE_CS(&g_crit_sec, success);
    LOG_CRIT_SEC(leave, success);

    LOG_EXIT(rinfo_map_insert);
    return new_name;
}


/* centralize the retrieval of data */
static NODE_T **internal_map_at(request_info_name_t req)
{
    NODE_T **ppreturn = NULL;
    NODE_T **ppcurrent = &g_map_head;

    while (*ppcurrent) {
        if ((*ppcurrent)->name == req) {
            ppreturn = ppcurrent;
            break;
        }

        ppcurrent = &((*ppcurrent)->next);
    }

    if (!ppreturn) {
        jk_log(logger, JK_LOG_DEBUG, "Did not find info named: %d\n",
               req);
    }

    return ppreturn;
}


request_information_t *rinfo_map_at(request_info_name_t req)
{
    int success = JK_FALSE;
    NODE_T **ppresult = NULL;
    LOG_ENTER(rinfo_map_at);

    JK_ENTER_CS(&g_crit_sec, success);
    if (success == JK_FALSE) {
        LOG_CRIT_SEC(enter, success);
        LOG_EXIT(rinfo_map_at);
        return NULL;
    }

    ppresult = internal_map_at(req);

    JK_LEAVE_CS(&g_crit_sec, success);
    LOG_CRIT_SEC(leave, success);

    LOG_EXIT(rinfo_map_at);
    return ppresult ? (*ppresult)->ri : NULL;
}


request_information_t *rinfo_map_remove_at(request_info_name_t req)
{
    int success = JK_FALSE;
    NODE_T **ppresult = NULL;
    NODE_T *ptemp;
    request_information_t *preturn = NULL;
    LOG_ENTER(rinfo_map_remove_at);

    JK_ENTER_CS(&g_crit_sec, success);
    if (success == JK_FALSE) {
        LOG_CRIT_SEC(enter, success);
        LOG_EXIT(rinfo_map_remove_at);
        return NULL;
    }

    ppresult = internal_map_at(req);
    if (ppresult) {
       jk_log(logger, JK_LOG_DEBUG, "Removing request: %d\nhead = 0x%x tail
= 0x%x\n", 
              req, g_map_head, g_map_tail);

        preturn = (*ppresult)->ri;
        
        /*
         * This removal part is a little tricky, so here I try to explain
it.
         * If someone finds out that it's not doing what it's supposed to,
         * then keep these notes in mind.
         *
         * What we are getting from the search function is not a
         * pointer to a node, but instead a pointer to a pointer to a node.
         * The reasoning for this return value is so that the node can be
         * removed without having to return two pointers:  a pointer to the
         * found node and a pointer to the node before that.  In my opinion,
         * this allows for a more graceful implementation... even though the
         * pointer stuff is a little hard to manage.
         *
         * So, it's established that we have a pointer to a pointer.
         * To remove the node, we first need to create a temporary reference
to
         * it. A simple pointer will do here.  We just don't want to lose a
         * handle on the node.  Once we have safely stored a reference to
the
         * node, we check to see if the found node is the last node in list.
         * If it is, then we check for the special case: that there is a 
         * one-element list. The tail pointer is zeroed.  In the more
general
         * case (the list is two or more nodes long), we need to back the 
         * tail up.  Since what we have is a pointer to a pointer to a node,
         * we can't just assign the value of ppresult to the tail.  We need
to
         * find the memory location of the node that contains the "next" 
         * pointer that ppresult is pointing to.  We use the the offsetof
         * macro to find out how far back from the next pointer the node 
         * pointer needs to be. Then we calculate the location of the node
         * pointer based on the location of the next pointer.  This should 
         * be safe because the search function can only return a pointer to 
         * head or to a next field (which is in a node) and we already
checked
         * for the first case.
         *
         * Once we deal with the tail, the rest just falls out.  The value
         * of the pointer that ppresult points to (be it head or next
field),
         * is reassigned to point to the next node in the list.
         *
         * The node is then deleted.
         */
        ptemp = *ppresult;
        if (ptemp == g_map_tail) {
            if (g_map_tail == g_map_head) {
                g_map_tail = NULL;
            } 
            else {
                g_map_tail = (NODE_T *)(
                    ((jk_uintptr_t)(ppresult)) - offsetof(NODE_T, next));
            }
        }

        *ppresult = (*ppresult)->next;

        free((void *) ptemp);

       jk_log(logger, JK_LOG_DEBUG, "Removed request: %d\nhead = 0x%x tail =
0x%x\n", req, 
              g_map_head, g_map_tail);
    }

    JK_LEAVE_CS(&g_crit_sec, success);
    LOG_CRIT_SEC(leave, success);

    LOG_EXIT(rinfo_map_remove_at);
    return preturn;
}


char *rinfo_append_name_to_uri(char *uri, request_info_name_t req,
				               char *buff, size_t size)
{
	char sep = '?';
    LOG_ENTER(rinfo_append_name_to_uri);

	if (buff == NULL) {
        LOG_EXIT(rinfo_append_name_to_uri);
        return NULL;
	}

	if (strchr(uri, sep)) {
        sep = '&';
	}

	snprintf(buff, size, "%s%c" QUERY_VAR "=%d", uri, sep, req);

    LOG_EXIT(rinfo_append_name_to_uri);
	return buff;
}


request_info_name_t rinfo_name_from_query(char *query)
{
    int numconverted = 0;
    request_info_name_t name = REQUEST_INFO_ERR_NAME;
    LOG_ENTER(rinfo_name_from_query);

    query = strstr(query, QUERY_VAR);
    if (!query) {
        jk_log(logger, JK_LOG_ERROR, "Did not find the variable \""
            QUERY_VAR "\" in the query string.\n");
        LOG_EXIT(rinfo_name_from_query);
        return REQUEST_INFO_ERR_NAME;
    }

    if (sscanf(query, QUERY_VAR "=%d", &name) != 1) {
        jk_log(logger, JK_LOG_ERROR, "Query string could not be scanned "
               "for a name.\n");
        LOG_EXIT(rinfo_name_from_query);
        return REQUEST_INFO_ERR_NAME;
    }

    LOG_EXIT(rinfo_name_from_query);
    return name;
}


/* Create a new request info object */
request_information_t *rinfo_new()
{
    request_information_t *pinfo =
	(request_information_t *) malloc(sizeof(request_information_t));
    LOG_ENTER(rinfo_new);

    if (pinfo == NULL) {
        jk_log(logger, JK_LOG_EMERG, "Failed to allocate memory for a "
               "new request information object.\n");
        LOG_EXIT(rinfo_new);
        return NULL;
    }

    memset((void *) pinfo, 0, sizeof(request_information_t));
    jk_open_pool(&pinfo->p, pinfo->buff, sizeof(pinfo->buff));

    LOG_EXIT(rinfo_new);
    return pinfo;
}


/* destroy a request info object */
void rinfo_delete(request_information_t * ri)
{
    LOG_ENTER(rinfo_delete);

    if (ri == NULL) {
        LOG_EXIT(rinfo_delete);
        return;
    }

    jk_close_pool(&ri->p);
    free((void *) ri);

    /* This is not sufficient, caller must do this too */
    ri = NULL;

    LOG_EXIT(rinfo_delete);
}


/* helper to set string memebers */
static internal_set_string(request_information_t * ri, char **member,
			   char *value)
{
    if (value) {
        *member = jk_pool_strdup(&ri->p, value);
    }
    else {
        *member = NULL;
    }
}

void rinfo_set_worker(request_information_t * ri, char *name)
{
    LOG_ENTER(rinfo_set_worker);

    /* protection */
    if (ri == NULL) {
        LOG_EXIT(rinfo_set_worker);
        return;
    }

    internal_set_string(ri, &(ri->worker_name), name);

    LOG_EXIT(rinfo_set_worker);
}


void rinfo_set_uri(request_information_t * ri, char *uri)
{
    LOG_ENTER(rinfo_set_uri);

    /* protection */
    if (ri == NULL) {
        LOG_EXIT(rinfo_set_uri);
        return;
    }

    internal_set_string(ri, &(ri->uri), uri);

    LOG_EXIT(rinfo_set_uri);
}


void rinfo_set_query_string(request_information_t * ri, char *qs)
{
    LOG_ENTER(rinfo_set_query_string);

    /* protection */
    if (ri == NULL) {
        LOG_EXIT(rinfo_set_query_string);
        return;
    }

    internal_set_string(ri, &(ri->query_string), qs);

    LOG_EXIT(rinfo_set_query_string);
}


char *rinfo_get_worker(request_information_t * ri)
{
    LOG_ENTER(rinfo_get_worker);

    if (ri == NULL) {
        LOG_EXIT(rinfo_get_worker);
        return NULL;
    }

    jk_log(logger, JK_LOG_DEBUG, "Retreived: %s\n", ri->worker_name);
    LOG_EXIT(rinfo_get_worker);
    return ri->worker_name;
}


char *rinfo_get_uri(request_information_t * ri)
{
    LOG_ENTER(rinfo_get_uri);

    if (ri == NULL) {
        LOG_EXIT(rinfo_get_uri);
        return NULL;
    }

    jk_log(logger, JK_LOG_DEBUG, "Retreived: %s\n", ri->uri);
    LOG_EXIT(rinfo_get_uri);
    return ri->uri;
}


char *rinfo_get_query_string(request_information_t * ri)
{
    LOG_ENTER(rinfo_get_query_string);

    if (ri == NULL) {
        LOG_EXIT(rinfo_get_query_string);
        return NULL;
    }

    jk_log(logger, JK_LOG_DEBUG, "Retreived: %s\n", ri->query_string);
    LOG_EXIT(rinfo_get_query_string);
    return ri->query_string;
}







Thank you
Steven.