blob: 6c646e861b002d763c743595eda8f4c5ebbed5b4 [file] [log] [blame]
/** Apache SOAP module for Apache 1.3.x
* @file mod_gsoap.c
* @author Christian Aberger (http://www.aberger.at)
* updated by David Viner (dviner@apache.org)
*/
#if !defined(__GNUC__) || __GNUC__ < 2 || \
(__GNUC__ == 2 && __GNUC_MINOR__ < 7) ||\
defined(NEXT)
#ifndef __attribute__
#define __attribute__(__x)
#endif
#endif
/*
* Moved gsoap include to top. Without this, there's a parse error
* introduced if gsoap was compiled with -DWITH_OPENSSL.
*/
#include "stdsoap2.h" // standard header for gsoap
#include <stdio.h>
#include <assert.h>
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_main.h"
#include "http_protocol.h"
#include "http_request.h"
#include "util_script.h"
#include "apache_gsoap.h"
typedef int bool;
#define false 0
#define true ((int)0xffff)
#define IOBUF_CHUNK_SIZE 8192
#define GSOAP_ID "Apache mod_gsoap gsoap httpd extension 0.0.6"
/** A shared library containing a SOAP server.
*/
typedef struct SoapSharedLibrary_S {
pool *m_pPool;
void *m_hLibrary; ///< handle of the loaded libray.
const char *m_pszPath; ///< the path of the library, including the name.
bool m_bIsSOAPLibrary; ///< is this library a SOAP library that contains the server factory entry point?
} SoapSharedLibrary;
/** Table of shared libraries that are already loaded.
* a singleton.
*/
typedef struct SoapSharedLibraries_S {
pool *m_pPool;
SoapSharedLibrary *m_pSOAPLibrary; ///< the main SOAP library that will serve our requests
array_header *m_pLibraries; ///< the array where we store our libraries.
apache_init_soap_interface_fn m_pfnEntryPoint;
struct apache_soap_interface *m_pIntf;
bool m_bAllLibrariesLoaded; ///< have all libraries in our libraries collection been already successfully loaded?
} SoapSharedLibraries;
/** Environment to which record applies (directory,
* server, or combination).
*/
typedef enum enConfigurationType {
ct_server = 1, ///< used for per-server configuration
ct_directory = 2, ///< used for per-directory configuration
ct_both = 3 ///< used for both
} ConfigurationType;
/** Store the configuration information set in the Apache Server configuration directives.
* These are set e.g. in the file httpd.conf
* It's perfectly reasonable to have two different structures for the two
* different environments. The same command handlers will be called for
* both, though, so the handlers need to be able to tell them apart. One
* possibility is for both structures to start with an int which is zero for
* one and 1 for the other.
*
* Note that while the per-directory and per-server configuration records are
* available to most of the module handlers, they should be treated as
* READ-ONLY by all except the command and merge handlers. Sometimes handlers
* are handed a record that applies to the current location by implication or
* inheritance, and modifying it will change the rules for other locations.
*/
typedef struct gsoapConfiguration_S {
SoapSharedLibraries *m_pLibraries;
ConfigurationType m_Type; ///< the type of configuration environment
} gsoapConfiguration;
/** Our internal per request soap configuration
*/
typedef struct gsoapRequestConfiguration_S {
request_rec *r; ///< the current request record.
char *m_pszAllHeaders; ///< all headers received as a string, this is returned to gsoap's http_parse function before we return the body.
const char *m_pszCurrentHeaderReadingPosition; ///< the position where the next header read operation will start.
unsigned int m_nHeaderLength; ///< total length of all headers concatenated as string
char *m_pOutBuf; ///< output buffer
size_t m_nOutBufLength; ///< allocated length of output buffer
size_t m_nOutBufCount; ///< bytes in output buffer
int headers_sent; ///< have http - headers already been sent to client us?
int headers_received; ///< have the request headers already been passed to gsoap ?
int (*http_parse) (struct soap *); ///< the original gsoap parsing function.
struct apache_soap_interface *pIntf;
} gsoapRequestConfiguration;
/*
* To avoid leaking memory from pools other than the per-request one, we
* allocate a module-private pool.
*/
static pool *the_gsoapPool = NULL;
/** @return our pool for gsoapConfiguration */
static pool *
gsoapConfiguration_getModulePool()
{
if(NULL == the_gsoapPool)
{
the_gsoapPool = ap_make_sub_pool(NULL);
}
return the_gsoapPool;
}
static gsoapConfiguration *getConfiguration(request_rec * r);
static gsoapRequestConfiguration *getRequestConfiguration(struct soap *);
/**
@param p the pool to use for memory allocations.
@param pszPath the path of the library.
*/
static void
SoapSharedLibrary_init(SoapSharedLibrary * This, pool * p,
const SoapSharedLibrary * pLib)
{
This->m_pPool = p;
This->m_hLibrary = NULL;
This->m_pszPath = ap_pstrdup(p, pLib->m_pszPath);
This->m_bIsSOAPLibrary = pLib->m_bIsSOAPLibrary;
}
static void
SoapSharedLibrary_init2(SoapSharedLibrary * This, pool * p, const char *pszPath)
{
This->m_pPool = p;
This->m_hLibrary = NULL;
This->m_pszPath = ap_pstrdup(p, pszPath);
This->m_bIsSOAPLibrary = false;
}
static void
SoapSharedLibrary_clear(SoapSharedLibrary * This, pool * p)
{
This->m_pPool = p;
This->m_hLibrary = NULL;
This->m_pszPath = NULL;
This->m_bIsSOAPLibrary = false;
}
static SoapSharedLibrary *
SoapSharedLibrary_create(pool * p)
{
SoapSharedLibrary *This =
(SoapSharedLibrary *) ap_pcalloc(p, sizeof(SoapSharedLibrary));
SoapSharedLibrary_clear(This, p);
return This;
}
/**
* @param pTempPool pool to use for allocating temporary objects (e.g. error message).
*/
static const char *
SoapSharedLibrary_load(SoapSharedLibrary * This, pool * pTempPool)
{
const char *pszError = NULL;
const int nFlags = RTLD_LAZY | RTLD_GLOBAL;
This->m_hLibrary = (void *)dlopen(This->m_pszPath, nFlags);
pszError = dlerror();
if(NULL == This->m_hLibrary)
{
pszError =
ap_psprintf(NULL == pTempPool ? This->m_pPool : pTempPool,
"mod_gsoap: %s loading library %s", pszError,
This->m_pszPath);
}
return pszError;
}
/*-------------------------------------------------------*/
static void
SoapSharedLibraries_init(SoapSharedLibraries * This, pool * p)
{
This->m_pPool = p;
This->m_pSOAPLibrary = NULL;
This->m_pLibraries = ap_make_array(p, 0, sizeof(SoapSharedLibrary **));
This->m_bAllLibrariesLoaded = false;
This->m_pfnEntryPoint = NULL;
This->m_pIntf =
(struct apache_soap_interface *)ap_pcalloc(p,
sizeof(struct
apache_soap_interface));
}
static SoapSharedLibrary *
SoapSharedLibraries_getLibrary(SoapSharedLibraries * This, unsigned nIndex)
{
SoapSharedLibrary **ppLibs = NULL;
SoapSharedLibrary *pLib = NULL;
assert(NULL != This);
assert(NULL != This->m_pLibraries);
assert(nIndex >= 0);
assert(nIndex < This->m_pLibraries->nelts);
ppLibs = (SoapSharedLibrary **) This->m_pLibraries->elts;
pLib = ppLibs[nIndex];
return pLib;
}
/**
* @param pszPath the operating system name of the library.
*/
static bool
SoapSharedLibraries_contains(SoapSharedLibraries * This, const char *pszPath)
{
int i = 0;
bool bContains = false;
for(i = 0; i < This->m_pLibraries->nelts && !bContains; i++)
{
SoapSharedLibrary *pLib = SoapSharedLibraries_getLibrary(This, i);
assert(NULL != pLib);
if(0 == strcmp(pszPath, pLib->m_pszPath))
{
bContains = true;
}
}
return bContains;
}
static void
SoapSharedLibraries_addLibrary(SoapSharedLibraries * This,
SoapSharedLibrary * pLibrary)
{
assert(NULL != pLibrary);
This->m_bAllLibrariesLoaded = false;
if(!SoapSharedLibraries_contains(This, pLibrary->m_pszPath))
{
SoapSharedLibrary **ppNewLib =
(SoapSharedLibrary **) ap_push_array(This->m_pLibraries);
*ppNewLib = pLibrary;
if(pLibrary->m_bIsSOAPLibrary)
{
This->m_pSOAPLibrary = pLibrary;
}
}
}
static const char *
SoapSharedLibraries_getEntryPoints(SoapSharedLibraries * This,
SoapSharedLibrary * pLib, pool * pTempPool,
request_rec * r)
{
/*
* now we also pass the request record
*/
(*This->m_pfnEntryPoint) (This->m_pIntf, r);
return NULL;
}
/**
* @return the error message if a load failed, NULL on success.
*/
static const char *
SoapSharedLibraries_loadAllLibraries(SoapSharedLibraries * This,
pool * pTempPool, request_rec * r)
{
bool bAllLibrariesLoaded = false;
const char *pszError = NULL;
bool bRetry = false;
int i = 0;
int nRetry = 0;
assert(NULL != This);
if(This->m_bAllLibrariesLoaded)
{
return NULL;
}
for(nRetry = 0; nRetry < 5 && !bAllLibrariesLoaded; nRetry++)
{
do
{
pszError = NULL;
bRetry = false; // don't try it again.
bAllLibrariesLoaded = true;
for(i = 0; i < This->m_pLibraries->nelts; i++)
{
SoapSharedLibrary *pLib =
SoapSharedLibraries_getLibrary(This, i);
if(NULL == pLib->m_hLibrary)
{
pszError = SoapSharedLibrary_load(pLib, pTempPool);
if(NULL == pszError)
{
assert(NULL != pLib->m_hLibrary);
bRetry = true; ///< we loaded one, lets retry with all others, maybe they depend on that.
}
else
{
bAllLibrariesLoaded = false;
}
if(NULL != pLib->m_hLibrary && pLib->m_bIsSOAPLibrary)
{
void *pfun = (void *)dlsym(pLib->m_hLibrary,
APACHE_HTTPSERVER_ENTRY_POINT);
if(NULL == pfun)
{
pszError = ap_psprintf(pTempPool,
"gsoap: load library \"%s\" success, but failed to find the \"%s\" entry point",
pLib->m_pszPath,
APACHE_HTTPSERVER_ENTRY_POINT);
return pszError;
}
else
{
This->m_pfnEntryPoint =
(apache_init_soap_interface_fn) pfun;
pszError =
SoapSharedLibraries_getEntryPoints(This, pLib,
pTempPool,
r);
}
}
}
}
}
while(bRetry);
}
if(bAllLibrariesLoaded)
{
This->m_bAllLibrariesLoaded = true;
pszError = NULL;
}
return pszError;
}
static void
SoapSharedLibraries_merge(SoapSharedLibraries * This,
SoapSharedLibraries * pLibs)
{
int i = 0;
assert(NULL != This);
if(NULL == pLibs)
{
return;
}
This->m_bAllLibrariesLoaded = false;
for(i = 0; i < pLibs->m_pLibraries->nelts; i++)
{
SoapSharedLibrary *pLib = SoapSharedLibraries_getLibrary(pLibs, i);
if(!SoapSharedLibraries_contains(This, pLib->m_pszPath))
{
SoapSharedLibrary *pNewLib =
SoapSharedLibrary_create(This->m_pPool);
SoapSharedLibrary_init(pNewLib, This->m_pPool, pLib);
SoapSharedLibraries_addLibrary(This, pNewLib);
}
}
}
static void
SoapSharedLibraries_merge3(SoapSharedLibraries * This,
SoapSharedLibraries * libraries1,
SoapSharedLibraries * libraries2)
{
SoapSharedLibraries_merge(This, libraries1);
SoapSharedLibraries_merge(This, libraries2);
}
static void
gsoapConfiguration_merge(gsoapConfiguration * This,
gsoapConfiguration * pParentConfig,
gsoapConfiguration * pNewConfig)
{
assert(NULL != This);
SoapSharedLibraries_merge3(This->m_pLibraries, pParentConfig->m_pLibraries,
pNewConfig->m_pLibraries);
This->m_Type = ct_both;
}
static void
gsoapConfiguration_init(gsoapConfiguration * This, pool * p)
{
This->m_pLibraries =
(SoapSharedLibraries *) ap_pcalloc(p, sizeof(SoapSharedLibraries));
SoapSharedLibraries_init(This->m_pLibraries, p);
This->m_Type = ct_directory;
}
static gsoapConfiguration *
gsoapConfiguration_create(pool * p)
{
gsoapConfiguration *pConfig =
(gsoapConfiguration *) ap_pcalloc(p, sizeof(gsoapConfiguration));
gsoapConfiguration_init(pConfig, p);
return pConfig;
}
/*
* forward declarations
*/
static int gsoap_handler(request_rec * r);
static void gsoap_init(server_rec * s, pool * p);
static void gsoap_child_init(server_rec * s, pool * p);
static void gsoap_child_exit(server_rec * s, pool * p);
static void *gsoap_create_dir_config(pool * p, char *dirspec);
static void *gsoap_merge_dir_config(pool * p, void *parent_conf,
void *newloc_conf);
static void *gsoap_create_server_config(pool * p, server_rec * s);
static void *gsoap_merge_server_config(pool * p, void *server1_conf,
void *server2_conf);
static int gsoap_post_read_request(request_rec * r);
static int gsoap_translate_handler(request_rec * r);
static int gsoap_check_user_id(request_rec * r);
static int gsoap_auth_checker(request_rec * r);
static int gsoap_access_checker(request_rec * r);
static int gsoap_type_checker(request_rec * r);
static int gsoap_fixer_upper(request_rec * r);
static int gsoap_logger(request_rec * r);
static int gsoap_header_parser(request_rec * r);
static bool AddSharedLibrary(gsoapConfiguration * pConfig, const char *pszPath,
const bool bIsSOAPLibrary);
/*
* We prototyped the various syntax for command handlers (routines that
* are called when the configuration parser detects a directive declared
* by our module) earlier. Now we actually declare a "real" routine that
* will be invoked by the parser when our "real" directive is
* encountered.
*
* If a command handler encounters a problem processing the directive, it
* signals this fact by returning a non-NULL pointer to a string
* describing the problem.
*
* The magic return value DECLINE_CMD is used to deal with directives
* that might be declared by multiple modules. If the command handler
* returns NULL, the directive was processed; if it returns DECLINE_CMD,
* the next module (if any) that declares the directive is given a chance
* at it. If it returns any other value, it's treated as the text of an
* error message.
*
*/
/** Command handler for the TAKE1 "SOAPLibrary" directive.
* We remember the load path for the shared library that contains the SOAP server.
*/
static const char *
cmd_SoapLibrary(cmd_parms * cmd, void *mconfig, const char *pszPath)
{
gsoapConfiguration *pConfig = (gsoapConfiguration *) mconfig;
AddSharedLibrary(pConfig, pszPath, true);
return NULL;
}
/** Command handler for the TAKE1 "SOAPSupportLibrary" directive.
* We remember the load path for a shared library that must additionally loaded.
* This is a mechanism to load libraries that the SOAPLibrary depends on.
* This type of libraries do not contain our soap server.
*/
static const char *
cmd_SupportLibrary(cmd_parms * cmd, void *mconfig, const char *pszPath)
{
gsoapConfiguration *pConfig = (gsoapConfiguration *) mconfig;
AddSharedLibrary(pConfig, pszPath, false);
return NULL;
}
typedef const char *(*command_function_interface) ();
/** List of directives specific to our module.
*/
static const command_rec gsoap_cmds[] = {
{
"SOAPLibrary", ///< directive name
(command_function_interface) cmd_SoapLibrary, ///< config action routine
NULL, ///< argument to include in call
ACCESS_CONF, ///< where available
TAKE1, ///< arguments
"SOAP shared Library that will be dynamically loaded. - 1 argument (path)" ///< directive description
},
{
"SupportLibrary", ///< directive name
(command_function_interface) cmd_SupportLibrary, ///< config action routine
NULL, ///< argument to include in call
ACCESS_CONF, ///< where available
TAKE1, ///< arguments
"additional library that must be dynamically loaded - 1 argument (path)" ///< directive description
},
{NULL}
};
/*
* --------------------------------------------------------------------------
*
* Now the list of content handlers available from this module.
*
*
* List of content handlers our module supplies. Each handler is defined by
* two parts: a name by which it can be referenced (such as by
* {Add,Set}Handler), and the actual routine name. The list is terminated by
* a NULL block, since it can be of variable length.
*
* Note that content-handlers are invoked on a most-specific to least-specific
* basis; that is, a handler that is declared for "text/plain" will be
* invoked before one that was declared for "text / *". Note also that
* if a content-handler returns anything except DECLINED, no other
* content-handlers will be called.
*/
static const handler_rec gsoap_handlers[] = {
{"gsoap-handler", gsoap_handler},
{NULL}
};
/*
*--------------------------------------------------------------------------
*
* All of the routines have been declared now. Here's the list of
* directives specific to our module, and information about where they
* may appear and how the command parser should pass them to us for
* processing. Note that care must be taken to ensure that there are NO
* collisions of directive names between modules.
*
*/
/*
* Module definition for configuration. If a particular callback is not
* needed, replace its routine name below with the word NULL.
*
* The number in brackets indicates the order in which the routine is called
* during request processing. Note that not all routines are necessarily
* called (such as if a resource doesn't have access restrictions).
*/
/** List of callback routines and data structures that provide the hooks into our module.
*/
module MODULE_VAR_EXPORT gsoap_module = {
STANDARD_MODULE_STUFF,
gsoap_init, /* module initializer */
gsoap_create_dir_config, /* per-directory config creator */
gsoap_merge_dir_config, /* dir config merger */
gsoap_create_server_config, /* server config creator */
gsoap_merge_server_config, /* server config merger */
gsoap_cmds, /* command table */
gsoap_handlers, /* [9] list of handlers */
gsoap_translate_handler, /* [2] filename-to-URI translation */
gsoap_check_user_id, /* [5] check/validate user_id */
gsoap_auth_checker, /* [6] check user_id is valid *here* */
gsoap_access_checker, /* [4] check access by host address */
gsoap_type_checker, /* [7] MIME type checker/setter */
gsoap_fixer_upper, /* [8] fixups */
gsoap_logger, /* [10] logger */
gsoap_header_parser, /* [3] header parser */
gsoap_child_init, /* process initializer */
gsoap_child_exit, /* process exit/cleanup */
gsoap_post_read_request /* [1] post read_request handling */
};
/** helper to write out the headers */
static int
ListHeadersCallback(void *rec, const char *key, const char *value)
{
request_rec *r = (request_rec *) rec;
ap_rprintf(r, "%s: %s<br>", key, value);
return 1;
}
/** write out the headers of the request. */
static void
ListHeaders(request_rec * r)
{
ap_table_do(ListHeadersCallback, r, r->headers_in, NULL);
}
/** send the error message to the client browser */
static void
SendErrorMessage(request_rec * r, const char *pszError)
{
/*
* gsoapConfiguration *pConfig = getConfiguration(r);
*/
ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "mod_gsoap: %s", pszError);
r->content_type = "text/html";
ap_send_http_header(r);
ap_rputs(DOCTYPE_HTML_3_2, r);
ap_rputs("<HTML>\n", r);
ap_rputs(" <HEAD>\n", r);
ap_rputs(" <TITLE>Apache Soap Handler\n", r);
ap_rputs(" </TITLE>\n", r);
ap_rputs(" </HEAD>\n", r);
ap_rputs(" <BODY>\n", r);
ap_rputs(" <H1>mod_gsoap Apache SOAP Server Error</H1>\n", r);
ap_rprintf(r,
"<p><strong>%s</strong><br>Please see the documentation at <a href=\"http://www.webware.at/SOAP\">WebWare</a> for details.</p>",
pszError);
ap_rputs(" <H2>Content headers of the request</H2>\n", r);
ListHeaders(r);
ap_rputs("</BODY></HTML>\n", r);
}
static int
send_header_to_gsoap(void *pvoid, const char *key, const char *value)
{
gsoapRequestConfiguration *pRqConf = NULL;
struct soap *psoap = (struct soap *)pvoid;
if(NULL != psoap)
{
pRqConf = getRequestConfiguration(psoap);
}
if(NULL == pRqConf)
{
return 0;
}
if(0 == strcasecmp(key, "SOAPAction") ||
0 == strcasecmp(key, "Content-Type") ||
0 == strcasecmp(key, "Status") || 0 == strcasecmp(key, "Content-Length"))
{
psoap->fparsehdr(psoap, key, value);
}
return 1;
}
/*
* Callback functions for gsoap. We must parse the headers ourselves and
* we must handle send/receive properly.
*/
static int
http_post_header(struct soap *soap, const char *key, const char *value)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
request_rec *r = NULL == pRqConf ? NULL : pRqConf->r;
if(NULL != value)
{
if(0 == strcasecmp(key, "SOAPAction"))
{
ap_table_set(r->headers_out, key, value);
}
else if(0 == strcasecmp(key, "Content-Type"))
{
r->content_type = ap_pstrdup(r->pool, value);
}
else if(0 == strcasecmp(key, "Content-Length"))
{
ap_set_content_length(r, atoi(value));
}
}
return SOAP_OK;
}
/** gsoap function that requests the next piece of data from us */
static size_t
frecv(struct soap *psoap, char *pBuf, size_t len)
{
request_rec *r = NULL;
int nRet = 0;
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(psoap);
if(NULL != pRqConf)
{
r = pRqConf->r;
if(!pRqConf->headers_received)
{
ap_table_do(send_header_to_gsoap, psoap, r->headers_in, NULL);
pRqConf->headers_received = true;
}
if(r->remaining > 0)
{
nRet =
ap_get_client_block(r, pBuf,
len > r->remaining ? r->remaining : len);
}
}
return nRet;
}
static int
fsend(struct soap *psoap, const char *pBuf, size_t len)
{
int nRet = SOAP_OK;
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(psoap);
if(NULL != pRqConf)
{
request_rec *r = pRqConf->r;
if(!pRqConf->headers_sent)
{
ap_send_http_header(r);
pRqConf->headers_sent = true;
}
nRet = ap_rwrite(pBuf, len, r) == len ? SOAP_OK : SOAP_FAULT;
}
else
{
nRet = SOAP_FAULT;
}
return nRet;
}
/** instead of real header parsing we skip that. */
static int
http_parse(struct soap *psoap)
{
return SOAP_OK;
}
/*
* plugin functions
*/
static int
mod_gsoap_plugin_copy(struct soap *soap, struct soap_plugin *dst,
struct soap_plugin *src)
{
*dst = *src;
/*
* should this be a deep copy?
*/
return SOAP_OK;
}
static void
mod_gsoap_delete(struct soap *soap, struct soap_plugin *p)
{
}
static int
mod_gsoap_plugin(struct soap *soap, struct soap_plugin *p, void *arg)
{
p->id = GSOAP_ID;
p->data = arg;
p->fcopy = mod_gsoap_plugin_copy;
p->fdelete = mod_gsoap_delete;
return SOAP_OK;
}
static void
set_callbacks(request_rec * r, gsoapRequestConfiguration * pRqConf,
struct soap *psoap)
{
gsoapConfiguration *pConfig = getConfiguration(r);
struct apache_soap_interface *pIntf = pConfig->m_pLibraries->m_pIntf;
pRqConf->r = r;
pRqConf->http_parse = psoap->fparse;
psoap->user = pRqConf;
psoap->frecv = frecv;
psoap->fsend = fsend;
psoap->fparse = http_parse;
psoap->fposthdr = http_post_header;
if(NULL != pIntf->fsoap_init)
{
(*pIntf->fsoap_register_plugin_arg) (psoap, mod_gsoap_plugin,
(void *)pRqConf, r);
}
else
{
psoap->user = pRqConf;
}
}
/*
*--------------------------------------------------------------------------
*
* Now we declare our content handlers, which are invoked when the server
* encounters a document which our module is supposed to have a chance to
* see. (See mod_mime's SetHandler and AddHandler directives, and the
* mod_info and mod_status examples, for more details.)
*
* Since content handlers are dumping data directly into the connexion
* (using the r*() routines, such as rputs() and rprintf()) without
* intervention by other parts of the server, they need to make
* sure any accumulated HTTP headers are sent first. This is done by
* calling send_http_header(). Otherwise, no header will be sent at all,
* and the output sent to the client will actually be HTTP-uncompliant.
*--------------------------------------------------------------------------
*/
/**
* SOAP content handler.
*
* @return the value that instructs the caller concerning what happened and what to do next.
* OK ("we did our thing")
* DECLINED ("this isn't something with which we want to get involved")
* HTTP_mumble ("an error status should be reported")
*/
static int
gsoap_handler(request_rec * r)
{
static const int nResponseBufferLen = IOBUF_CHUNK_SIZE;
const char *pszError = NULL;
struct soap *psoap = NULL;
struct apache_soap_interface *pIntf = NULL;
int nRet = 0;
/*
* char *pszResponse = ap_pcalloc(r->pool, nResponseBufferLen);
*/
gsoapConfiguration *pConfig = getConfiguration(r);
gsoapRequestConfiguration *pRqConf = NULL;
assert(NULL != pConfig);
psoap = (struct soap *)ap_pcalloc(r->pool, sizeof(struct soap));
pRqConf = ap_pcalloc(r->pool, sizeof(gsoapRequestConfiguration));
pszError =
SoapSharedLibraries_loadAllLibraries(pConfig->m_pLibraries, r->pool, r);
pIntf = pConfig->m_pLibraries->m_pIntf;
ap_update_mtime(r, r->request_time);
ap_set_last_modified(r);
if(NULL != pszError)
{
static bool bFirstTime = true;
if(bFirstTime)
{
ap_log_error(APLOG_MARK, APLOG_ERR, r->server, pszError);
bFirstTime = false;
}
}
if(NULL == pszError)
{
if(M_POST != r->method_number && M_GET != r->method_number)
{
pszError = "Only POST and GET allowed as request for SOAP!";
}
}
/*
* as a next step, we prepare a buffer that sends the request as first line to gsoap.
* Then the remaining data.
* We start returning bytes on frecv from this buffer, until it is empty.
* then it is not necessary to fiddle around with gsoap's request line parsing.
*/
if(NULL == pszError)
{
pRqConf->r = r;
pRqConf->headers_sent = false;
pRqConf->headers_received = false;
pRqConf->m_pszAllHeaders = NULL;
pRqConf->m_nHeaderLength = strlen(r->the_request) + 2;
pRqConf->m_pszCurrentHeaderReadingPosition = NULL;
pRqConf->m_nOutBufCount = 0;
pRqConf->m_nOutBufLength = nResponseBufferLen;
pRqConf->m_pOutBuf = ap_pcalloc(r->pool, nResponseBufferLen);
pRqConf->http_parse = NULL;
pRqConf->m_pszAllHeaders =
ap_pcalloc(r->pool, pRqConf->m_nHeaderLength + 1);
pRqConf->m_pszCurrentHeaderReadingPosition = pRqConf->m_pszAllHeaders;
strcpy(pRqConf->m_pszAllHeaders, r->the_request);
strcat(pRqConf->m_pszAllHeaders, "\r\n");
pRqConf->pIntf = pIntf;
}
/*
* We're about to start sending content, so we need to force the HTTP
* headers to be sent at this point. Otherwise, no headers will be sent
* at all. We can set any we like first, of course. **NOTE** Here's
* where you set the "Content-type" header, and you do so by putting it in
* r->content_type, *not* r->headers_out("Content-type"). If you don't
* set it, it will be filled in with the server's default type (typically
* "text/plain"). You *must* also ensure that r->content_type is lower
* case.
*
* We also need to start a timer so the server can know if the connection
* is broken.
*/
ap_soft_timeout("gsoap xml response", r);
/*
* If we're only supposed to send header information (HEAD request), we're
* already there.
*/
if(r->header_only)
{
ap_send_http_header(r);
ap_kill_timeout(r);
return OK;
}
if(NULL != pszError)
{
SendErrorMessage(r, pszError);
ap_kill_timeout(r);
return OK;
}
nRet = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
if(OK != nRet)
{
SendErrorMessage(r, "Failed to start receiving POST buffer");
ap_kill_timeout(r);
return OK;
}
nRet = ap_should_client_block(r);
if(0 == nRet)
{
SendErrorMessage(r, "No body received");
ap_kill_timeout(r);
return OK;
}
if(NULL != pszError)
{
ap_log_error(APLOG_MARK, APLOG_ERR, r->server, pszError);
SendErrorMessage(r, pszError);
ap_kill_timeout(r);
return OK;
}
if(NULL != pIntf->fsoap_init)
{
(*pIntf->fsoap_init) (psoap, r);
psoap->namespaces = pIntf->namespaces;
set_callbacks(r, pRqConf, psoap);
if(NULL != pIntf->fsoap_serve)
{
(*pIntf->fsoap_serve) (psoap, r);
}
else
{
SendErrorMessage(r, "no soap_serve entry point");
ap_kill_timeout(r);
return OK;
}
if(NULL != pIntf->fsoap_destroy)
{
pIntf->fsoap_destroy(psoap, r); // not an error in 2.1.10 any more.
}
if(NULL != pIntf->fsoap_end)
{
pIntf->fsoap_end(psoap, r);
}
else
{
SendErrorMessage(r, "no soap_end entry point");
ap_kill_timeout(r);
}
if(NULL != pIntf->fsoap_done)
{
pIntf->fsoap_done(psoap, r);
}
else
{
SendErrorMessage(r, "no soap_done entry point");
ap_kill_timeout(r);
}
}
else
{
SendErrorMessage(r, "no soap_init entry point");
ap_kill_timeout(r);
return OK;
}
/*
* We did what we wanted to do, so tell the rest of the server we
* succeeded. We need not delete pszResponse, because it was allocated from the request pool.
*/
ap_kill_timeout(r);
return OK;
}
/*
* --------------------------------------------------------------------------
* *
* * Now let's declare routines for each of the callback phase in order.
* * (That's the order in which they're listed in the callback list, *not
* * the order in which the server calls them! See the command_rec
* * declaration). Note that these may be
* * called for situations that don't relate primarily to our function - in
* * other words, the fixup handler shouldn't assume that the request has
* * to do with "gsoap" stuff.
* *
* * With the exception of the content handler, all of our routines will be
* * called for each request, unless an earlier handler from another module
* * aborted the sequence.
* *
* * Handlers that are declared as "int" can return the following:
* *
* * OK Handler accepted the request and did its thing with it.
* * DECLINED Handler took no action.
* * HTTP_mumble Handler looked at request and found it wanting.
* *
* * What the server does after calling a module handler depends upon the
* * handler's return value. In all cases, if the handler returns
* * DECLINED, the server will continue to the next module with an handler
* * for the current phase. However, if the handler return a non-OK,
* * non-DECLINED status, the server aborts the request right there. If
* * the handler returns OK, the server's next action is phase-specific;
* * see the individual handler comments below for details.
* *
* *--------------------------------------------------------------------------
*/
/*
* This function is called during server initialisation. Any information
* that needs to be recorded must be in static cells, since there's no
* configuration record.
*
* There is no return value.
*/
static void
gsoap_init(server_rec * s, pool * p)
{
// ap_log_error(APLOG_MARK, APLOG_ERR, s, "mod_gsoap initialized", NULL);
}
/*
* This function is called during server initialisation when an heavy-weight
* process (such as a child) is being initialised. As with the
* module-initialisation function, any information that needs to be recorded
* must be in static cells, since there's no configuration record.
*
* There is no return value.
*/
static void
gsoap_child_init(server_rec * s, pool * p)
{
}
/*
* This function is called when an heavy-weight process (such as a child) is
* being run down or destroyed. As with the child-initialisation function,
* any information that needs to be recorded must be in static cells, since
* there's no configuration record.
*
* There is no return value.
*/
static void
gsoap_child_exit(server_rec * s, pool * p)
{
//gsoapConfiguration::getLibraries()->clear();
}
/*
* This function gets called to create a per-directory configuration
* record. This will be called for the "default" server environment, and for
* each directory for which the parser finds any of our directives applicable.
* If a directory doesn't have any of our directives involved (i.e., they
* aren't in the .htaccess file, or a <Location>, <Directory>, or related
* block), this routine will *not* be called - the configuration for the
* closest ancestor is used.
*
* The return value is a pointer to the created module-specific
* structure.
*/
static void *
gsoap_create_dir_config(pool * p, char *dirspec)
{
gsoapConfiguration *pConfig = gsoapConfiguration_create(p);
pConfig->m_Type = ct_directory;
return pConfig;
}
/*
* This function gets called to merge two per-directory configuration
* records. This is typically done to cope with things like .htaccess files
* or <Location> directives for directories that are beneath one for which a
* configuration record was already created. The routine has the
* responsibility of creating a new record and merging the contents of the
* other two into it appropriately. If the module doesn't declare a merge
* routine, the record for the closest ancestor location (that has one) is
* used exclusively.
*
* The routine MUST NOT modify any of its arguments!
*
* The return value is a pointer to the created module-specific structure
* containing the merged values.
*/
static void *
gsoap_merge_dir_config(pool * p, void *parent_conf, void *newloc_conf)
{
gsoapConfiguration *pMergedConfig = gsoapConfiguration_create(p);
gsoapConfiguration *pParentConfig = (gsoapConfiguration *) parent_conf;
gsoapConfiguration *pNewConfig = (gsoapConfiguration *) newloc_conf;
gsoapConfiguration_merge(pMergedConfig, pParentConfig, pNewConfig);
return pMergedConfig;
}
/*
* This function gets called to create a per-server configuration
* record. It will always be called for the "default" server.
*
* The return value is a pointer to the created module-specific
* structure.
*/
static void *
gsoap_create_server_config(pool * p, server_rec * s)
{
gsoapConfiguration *pConfig = gsoapConfiguration_create(p);
pConfig->m_Type = ct_server;
return pConfig;
}
/*
* This function gets called to merge two per-server configuration
* records. This is typically done to cope with things like virtual hosts and
* the default server configuration. The routine has the responsibility of
* creating a new record and merging the contents of the other two into it
* appropriately. If the module doesn't declare a merge routine, the more
* specific existing record is used exclusively.
*
* The routine MUST NOT modify any of its arguments!
*
* The return value is a pointer to the created module-specific structure
* containing the merged values.
*/
static void *
gsoap_merge_server_config(pool * p, void *server1_conf, void *server2_conf)
{
gsoapConfiguration *pMergedConfig = gsoapConfiguration_create(p);
gsoapConfiguration *pServer1Config = (gsoapConfiguration *) server1_conf;
gsoapConfiguration *pServer2Config = (gsoapConfiguration *) server2_conf;
gsoapConfiguration_merge(pMergedConfig, pServer1Config, pServer2Config);
pMergedConfig->m_Type = ct_server;
return (void *)pMergedConfig;
}
/*
* This routine is called after the request has been read but before any other
* phases have been processed. This allows us to make decisions based upon
* the input header fields.
*
* The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no
* further modules are called for this phase.
*/
static int
gsoap_post_read_request(request_rec * r)
{
return DECLINED;
}
/*
* This routine gives our module an opportunity to translate the URI into an
* actual filename. If we don't do anything special, the server's default
* rules (Alias directives and the like) will continue to be followed.
*
* The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no
* further modules are called for this phase.
*/
static int
gsoap_translate_handler(request_rec * r)
{
return DECLINED;
}
/*
* This routine is called to check the authentication information sent with
* the request (such as looking up the user in a database and verifying that
* the [encrypted] password sent matches the one in the database).
*
* The return value is OK, DECLINED, or some HTTP_mumble error (typically
* HTTP_UNAUTHORIZED). If we return OK, no other modules are given a chance
* at the request during this phase.
*/
static int
gsoap_check_user_id(request_rec * r)
{
return DECLINED;
}
/*
* This routine is called to check to see if the resource being requested
* requires authorisation.
*
* The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no
* other modules are called during this phase.
*
* If *all* modules return DECLINED, the request is aborted with a server
* error.
*/
static int
gsoap_auth_checker(request_rec * r)
{
return DECLINED;
}
/*
* This routine is called to check for any module-specific restrictions placed
* upon the requested resource. (See the mod_access module for an example.)
*
* The return value is OK, DECLINED, or HTTP_mumble. All modules with an
* handler for this phase are called regardless of whether their predecessors
* return OK or DECLINED. The first one to return any other status, however,
* will abort the sequence (and the request) as usual.
*/
static int
gsoap_access_checker(request_rec * r)
{
return DECLINED;
}
/*
* This routine is called to determine and/or set the various document type
* information bits, like Content-type (via r->content_type), language, et
* cetera.
*
* The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no
* further modules are given a chance at the request for this phase.
*/
static int
gsoap_type_checker(request_rec * r)
{
return DECLINED;
}
/*
* This routine is called to perform any module-specific fixing of header
* fields, et cetera. It is invoked just before any content-handler.
*
* The return value is OK, DECLINED, or HTTP_mumble. If we return OK, the
* server will still call any remaining modules with an handler for this
* phase.
*/
static int
gsoap_fixer_upper(request_rec * r)
{
return OK;
}
/*
* This routine is called to perform any module-specific logging activities
* over and above the normal server things.
*
* The return value is OK, DECLINED, or HTTP_mumble. If we return OK, any
* remaining modules with an handler for this phase will still be called.
*/
static int
gsoap_logger(request_rec * r)
{
return DECLINED;
}
/*
* This routine is called to give the module a chance to look at the request
* headers and take any appropriate specific actions early in the processing
* sequence.
*
* The return value is OK, DECLINED, or HTTP_mumble. If we return OK, any
* remaining modules with handlers for this phase will still be called.
*/
static int
gsoap_header_parser(request_rec * r)
{
return DECLINED;
}
/** helper funciton for library command handler.
* @param pszPath the path of the library.
* @param bIsSOAPLibrary true if it is a shared library containing a SOAP server.
* @return true if the library was added, false if it was aleady in the collection.
*/
static bool
AddSharedLibrary(gsoapConfiguration * pConfig, const char *pszPath,
const bool bIsSOAPLibrary)
{
bool bAdded = false;
pool *pPool = gsoapConfiguration_getModulePool();
if(!SoapSharedLibraries_contains(pConfig->m_pLibraries, pszPath))
{
SoapSharedLibrary *pLibrary = SoapSharedLibrary_create(pPool);
SoapSharedLibrary_init2(pLibrary, pPool, ap_pstrdup(pPool, pszPath));
pLibrary->m_bIsSOAPLibrary = bIsSOAPLibrary;
SoapSharedLibraries_addLibrary(pConfig->m_pLibraries, pLibrary);
bAdded = true;
}
return bAdded;
}
static gsoapConfiguration *
getConfiguration(request_rec * r)
{
return (gsoapConfiguration *) ap_get_module_config(r->per_dir_config,
&gsoap_module);
}
static gsoapRequestConfiguration *
getRequestConfiguration(struct soap *soap)
{
gsoapRequestConfiguration *pRqConf =
(gsoapRequestConfiguration *) soap->fplugin(soap, GSOAP_ID);
return pRqConf;
}
/*
* Patch from Ryan Troll
*
* Implmement these as weak symbols, allowing symbol checking during
* compilation to succeed, even when another object is actually
* providing these symbols at runtime.
*/
SOAP_NMAC struct Namespace namespaces[] __attribute__ ((weak));
SOAP_NMAC struct Namespace namespaces[] = {
{"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/",
"http://www.w3.org/*/soap-envelope"},
{"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/",
"http://www.w3.org/*/soap-encoding"},
{"xsi", "http://www.w3.org/2001/XMLSchema-instance",
"http://www.w3.org/*/XMLSchema-instance"},
{"xsd", "http://www.w3.org/2001/XMLSchema",
"http://www.w3.org/*/XMLSchema"},
{NULL, NULL}
};
SOAP_FMAC3 void SOAP_FMAC4 soap_serializeheader(struct soap *soap)
__attribute__ ((weak));
SOAP_FMAC3 void SOAP_FMAC4
soap_serializeheader(struct soap *soap)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
pRqConf->pIntf->soap_serializeheader(soap);
}
SOAP_FMAC3 int SOAP_FMAC4 soap_putheader(struct soap *soap)
__attribute__ ((weak));
SOAP_FMAC3 int SOAP_FMAC4
soap_putheader(struct soap *soap)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
return pRqConf->pIntf->soap_putheader(soap);
}
SOAP_FMAC3 int SOAP_FMAC4 soap_getheader(struct soap *soap)
__attribute__ ((weak));
SOAP_FMAC3 int SOAP_FMAC4
soap_getheader(struct soap *soap)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
return pRqConf->pIntf->soap_getheader(soap);
}
SOAP_FMAC3 void SOAP_FMAC4 soap_fault(struct soap *soap) __attribute__ ((weak));
SOAP_FMAC3 void SOAP_FMAC4
soap_fault(struct soap *soap)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
pRqConf->pIntf->soap_fault(soap);
}
SOAP_FMAC3 void SOAP_FMAC4 soap_serializefault(struct soap *soap)
__attribute__ ((weak));
SOAP_FMAC3 void SOAP_FMAC4
soap_serializefault(struct soap *soap)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
pRqConf->pIntf->soap_serializefault(soap);
}
SOAP_FMAC3 int SOAP_FMAC4 soap_putfault(struct soap *soap)
__attribute__ ((weak));
SOAP_FMAC3 int SOAP_FMAC4
soap_putfault(struct soap *soap)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
return pRqConf->pIntf->soap_putfault(soap);
}
SOAP_FMAC3 int SOAP_FMAC4 soap_getfault(struct soap *soap)
__attribute__ ((weak));
SOAP_FMAC3 int SOAP_FMAC4
soap_getfault(struct soap *soap)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
return pRqConf->pIntf->soap_getfault(soap);
}
SOAP_FMAC3 const char **SOAP_FMAC4 soap_faultcode(struct soap *soap)
__attribute__ ((weak));
SOAP_FMAC3 const char **SOAP_FMAC4
soap_faultcode(struct soap *soap)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
return pRqConf->pIntf->soap_faultcode(soap);
}
SOAP_FMAC3 const char **SOAP_FMAC4 soap_faultstring(struct soap *soap)
__attribute__ ((weak));
SOAP_FMAC3 const char **SOAP_FMAC4
soap_faultstring(struct soap *soap)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
return pRqConf->pIntf->soap_faultstring(soap);
}
SOAP_FMAC3 const char **SOAP_FMAC4 soap_faultdetail(struct soap *soap)
__attribute__ ((weak));
SOAP_FMAC3 const char **SOAP_FMAC4
soap_faultdetail(struct soap *soap)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
return pRqConf->pIntf->soap_faultdetail(soap);
}
/*
* Patch from Ryan Troll
*
* These may never be used within a server context. However,
* gsoap-2.7.0e requires these functions to be defined. Thus,
* we need to define them here.
*
*/
SOAP_FMAC3 void SOAP_FMAC4 soap_markelement(struct soap *soap, const void *a,
int b) __attribute__ ((weak));
SOAP_FMAC3 void SOAP_FMAC4
soap_markelement(struct soap *soap, const void *a, int b)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
return pRqConf->pIntf->soap_markelement(soap, a, b);
}
SOAP_FMAC3 int SOAP_FMAC4 soap_putelement(struct soap *soap, const void *a,
const char *b, int c, int d)
__attribute__ ((weak));
SOAP_FMAC3 int SOAP_FMAC4
soap_putelement(struct soap *soap, const void *a, const char *b, int c, int d)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
return pRqConf->pIntf->soap_putelement(soap, a, b, c, d);
}
SOAP_FMAC3 void *SOAP_FMAC4 soap_getelement(struct soap *soap, int *a)
__attribute__ ((weak));
SOAP_FMAC3 void *SOAP_FMAC4
soap_getelement(struct soap *soap, int *a)
{
gsoapRequestConfiguration *pRqConf = getRequestConfiguration(soap);
return pRqConf->pIntf->soap_getelement(soap, a);
}