/*******************************************************************************
 * Copyright (C) 2004-2006 Intel Corp. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - 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.
 *
 *  - Neither the name of Intel Corp. nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS 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 Intel Corp. OR THE 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.
 *******************************************************************************/

/**
 * @author Anas Nashif
 * @author Eugene Yarmosh
 * @author Liang Hou
 */

#ifdef HAVE_CONFIG_H
#include <wsman_config.h>
#endif

#define _GNU_SOURCE

#include "u/libu.h"
#include "wsman-xml-api.h"
#include "wsman-soap.h"
#include "wsman-xml.h"
#include "wsman-dispatcher.h"
#include "wsman-xml-serializer.h"
#include "wsman-xml-serialize.h"
#include "wsman-soap-envelope.h"
#include "wsman-faults.h"
#include "wsman-soap-message.h"

#include "wsman-client-api.h"
#include "wsman-client-transport.h"

/*    ENUMERATION  */
#define ENUM_EXPIRED(enuminfo, mytime) \
	((enumInfo->expires > 0) &&        \
	(enumInfo->expires > mytime))



/**
 * Calculate needed space for interface array with Endpoints
 * @param interfaces List of interfaces
 * @return Needed size of WsManDispatcherInfo
 */
static int
calculate_map_count(list_t * interfaces)
{
	int             count = 0;
	int             j;

	lnode_t        *node = list_first(interfaces);
	while (node) {
		WsDispatchInterfaceInfo *ifc =
			(WsDispatchInterfaceInfo *) node->list_data;
		for (j = 0; ifc->endPoints[j].serviceEndPoint != NULL; j++)
			count++;
		node = list_next(interfaces, node);
	}

	return (list_count(interfaces) * sizeof(WsManDispatcherInfo))
		+ (count * sizeof(DispatchToEpMap));
}

/**
 * Register Dispatcher
 * @param cntx Context
 * @param proc Dispatcher Callback
 * @param data Callback data
 */
static void
ws_register_dispatcher(WsContextH cntx, DispatcherCallback proc, void *data)
{
	SoapH soap = ws_context_get_runtime(cntx);
	if (soap) {
		soap->dispatcherProc = proc;
		soap->dispatcherData = data;
	}
	return;
}

static int
set_context_val(WsContextH cntx,
		char *name,
		void *val,
		int size,
		int no_dup,
		unsigned long type)
{
	int retVal = 1;
	debug("Setting context value: %s", name);
	if (cntx && name) {
		void *ptr = val;

		if (!no_dup) {
			if (val && (ptr = u_malloc(size))) {
				memcpy(ptr, val, size);
			}
		}
		if (ptr || val == NULL) {
			u_lock(cntx->soap);
			ws_remove_context_val(cntx, name);
			if (create_context_entry(cntx->entries, name, ptr)) {
				retVal = 0;
			}
			u_unlock(cntx->soap);
		}
	} else {
		error("error setting context value.");
	}
	return retVal;
}


static void
free_hentry_func(hnode_t * n, void *arg)
{
	u_free((void*)hnode_getkey(n));
	u_free(n);
}


static void
remove_locked_enuminfo(WsContextH cntx,
                       WsEnumerateInfo * enumInfo)
{
	u_lock(cntx->soap);
	if (!(enumInfo->flags & WSMAN_ENUMINFO_INWORK_FLAG)) {
		error("locked enuminfo unlocked");
		u_unlock(cntx->soap);
		return;
	}
	hash_delete_free(cntx->enuminfos,
		hash_lookup(cntx->enuminfos, enumInfo->enumId));
	u_unlock(cntx->soap);
}

#ifdef ENABLE_EVENTING_SUPPORT
static void wsman_expiretime2xmldatetime(unsigned long expire, char *str)
{
	time_t t = expire;
	struct tm tm;
	localtime_r(&t, &tm);
	snprintf(str, 30, "%u-%u%u-%u%uT%u%u:%u%u:%u%u+%u%u:%u%u",
			tm.tm_year + 1900, (tm.tm_mon + 1)/10, (tm.tm_mon + 1)%10,
			tm.tm_mday/10, tm.tm_mday%10, tm.tm_hour/10, tm.tm_hour%10,
			tm.tm_min/10, tm.tm_min%10, tm.tm_sec/10, tm.tm_sec%10,
			0, 0, 0,0);

}

static void delete_notification_info(WsNotificationInfoH notificationInfo) {
	if(notificationInfo) {
		ws_xml_destroy_doc(notificationInfo->EventContent);
		ws_xml_destroy_doc(notificationInfo->headerOpaqueData);
		u_free(notificationInfo->EventAction);
		u_free(notificationInfo);
	}
}
#endif


static WsSubscribeInfo*
search_pull_subs_info(SoapH soap, WsXmlDocH indoc)
{
	WsSubscribeInfo *subsInfo = NULL;
	char *uuid = NULL;
	lnode_t *lnode;
	WsContextH soapCntx = ws_get_soap_context(soap);
	WsXmlNodeH node = ws_xml_get_soap_body(indoc);

	node = ws_xml_get_child(node, 0, XML_NS_ENUMERATION, WSENUM_PULL);
	if(node) {
		node = ws_xml_get_child(node, 0, XML_NS_ENUMERATION, WSENUM_ENUMERATION_CONTEXT);
		uuid = ws_xml_get_node_text(node);
	}
	if(uuid == NULL) return subsInfo;
	if (pthread_mutex_lock(&soap->lockSubs))
	{
		error("Error: Can't lock soap->lockSubs");
		return NULL;
	}
	lnode = list_first(soapCntx->subscriptionMemList);
	while(lnode) {
		subsInfo = (WsSubscribeInfo *)lnode->list_data;
		if(!strcmp(subsInfo->subsId, uuid+5)) break;
		lnode = list_next(soapCntx->subscriptionMemList, lnode);
	}
	if (pthread_mutex_unlock(&soap->lockSubs))
	{
		error("Error: Can't unlock soap->lockSubs");
		return NULL;
	}
	if(lnode == NULL) return NULL;
	return subsInfo;
}


static WsXmlDocH
create_enum_info(SoapOpH op,
		 WsContextH epcntx,
              	 WsXmlDocH indoc,
		 WsEnumerateInfo **eInfo)
{
	WsXmlNodeH node = ws_xml_get_soap_body(indoc);
	WsXmlNodeH header = ws_xml_get_soap_header(indoc);
	WsXmlDocH outdoc = NULL;
	WsXmlNodeH enumnode = NULL;
	WsEnumerateInfo *enumInfo;
	WsmanMessage *msg = wsman_get_msg_from_op(op);
	WsmanFaultCodeType fault_code = WSMAN_RC_OK;
	WsmanFaultDetailType fault_detail_code = WSMAN_DETAIL_OK;
	char *uri, *to;

	enumInfo = (WsEnumerateInfo *)u_zalloc(sizeof (WsEnumerateInfo));
	if (enumInfo == NULL) {
		error("No memory");
		fault_code = WSMAN_INTERNAL_ERROR;
		goto DONE;
	}
	enumInfo->encoding = u_strdup(msg->charset);
	enumInfo->maxsize = wsman_get_maxsize_from_op(op);
	if(enumInfo->maxsize == 0) {
		enumnode = ws_xml_get_child(node, 0, XML_NS_ENUMERATION, WSENUM_ENUMERATE);
		enumInfo->maxsize = ws_deserialize_uint32(NULL, enumnode,
					     0, XML_NS_ENUMERATION,
					     WSENUM_MAX_CHARACTERS);
	}
	enumInfo->releaseproc = wsman_get_release_endpoint(epcntx, indoc);
	to = ws_xml_get_node_text(
			ws_xml_get_child(header, 0, XML_NS_ADDRESSING, WSA_TO));
	if (to == NULL) {
		error("No to node");
		fault_code = WSMAN_INTERNAL_ERROR;
		goto DONE;
	}
	uri =  ws_xml_get_node_text(
			ws_xml_get_child(header, 0, XML_NS_WS_MAN, WSM_RESOURCE_URI));
	if (uri == NULL) {
		error("No uri node");
		fault_code = WSMAN_INTERNAL_ERROR;
		goto DONE;
	}

	enumInfo->epr_to = u_strdup(to);
	enumInfo->epr_uri = u_strdup(uri);
	node = ws_xml_get_child(node, 0, XML_NS_ENUMERATION, WSENUM_ENUMERATE);
	node = ws_xml_get_child(node, 0, XML_NS_ENUMERATION, WSENUM_EXPIRES);
	if (node == NULL) {
		debug("No wsen:Expires");
		enumInfo->expires = 0;
	} else {
		wsman_set_expiretime(node, &(enumInfo->expires), &fault_code);
		if (fault_code != WSMAN_RC_OK) {
			fault_detail_code = WSMAN_DETAIL_EXPIRATION_TIME;
			goto DONE;
		}
	}

	if (msg->auth_data.username != NULL) {
		enumInfo->auth_data.username =
				u_strdup(msg->auth_data.username);
		enumInfo->auth_data.password =
				u_strdup(msg->auth_data.password);
	} else {
		enumInfo->auth_data.username = NULL;
		enumInfo->auth_data.password = NULL;
	}
	generate_uuid(enumInfo->enumId, EUIDLEN, 1);

DONE:
	if (fault_code != WSMAN_RC_OK) {
		outdoc = wsman_generate_fault(indoc, fault_code, fault_detail_code, NULL);
		u_free(enumInfo);
                *eInfo = NULL;
	} else {
		*eInfo = enumInfo;
	}
	return outdoc;
}

static void
destroy_enuminfo(WsEnumerateInfo * enumInfo)
{
	debug("destroy enuminfo");
	u_free(enumInfo->auth_data.username);
	u_free(enumInfo->auth_data.password);
	u_free(enumInfo->epr_to);
	u_free(enumInfo->epr_uri);
	u_free(enumInfo->encoding);
	if (enumInfo->filter)
		filter_destroy(enumInfo->filter);
	u_free(enumInfo);
}

static int
wsman_verify_enum_info(SoapOpH op,
		       WsEnumerateInfo * enumInfo,
		       WsXmlDocH doc,
		       WsmanStatus * status)
{

	WsmanMessage *msg = wsman_get_msg_from_op(op);
	WsXmlNodeH header = ws_xml_get_soap_header(doc);

	char *to = ws_xml_get_node_text(ws_xml_get_child(header, 0,
			XML_NS_ADDRESSING, WSA_TO));
	char *uri= ws_xml_get_node_text(ws_xml_get_child(header, 0,
			XML_NS_WS_MAN, WSM_RESOURCE_URI));
	if (to == NULL || uri == NULL) {
		error("No to or uri node");
		status->fault_code = WSMAN_INTERNAL_ERROR;
		status->fault_detail_code = 0;
		return 0;
	}

	if (strcmp(enumInfo->epr_to, to) != 0 ||
			strcmp(enumInfo->epr_uri, uri) != 0 ) {
		status->fault_code = WSA_MESSAGE_INFORMATION_HEADER_REQUIRED;
		status->fault_detail_code = 0;
		debug("verifying enumeration context: ACTUAL  uri: %s, to: %s", uri, to);
		debug("verifying enumeration context: SHOULD uri: %s, to: %s",
				enumInfo->epr_uri, enumInfo->epr_to);
		return 0;
	}

	if (msg->auth_data.username && msg->auth_data.password) {
		if (strcmp(msg->auth_data.username,
				enumInfo->auth_data.username) != 0 &&
				strcmp(msg->auth_data.password,
				enumInfo->auth_data.password) != 0) {
			status->fault_code = WSMAN_ACCESS_DENIED;
			status->fault_detail_code = 0;
			return 0;
		}
	}
	return 1;
}



static int
insert_enum_info(WsContextH cntx,
		WsEnumerateInfo *enumInfo)
{
	struct timeval tv;
	int retVal = 1;

	u_lock(cntx->soap);
	gettimeofday(&tv, NULL);
	enumInfo->timeStamp = tv.tv_sec;
	if (create_context_entry(cntx->enuminfos, enumInfo->enumId, enumInfo)) {
		retVal = 0;
	}
	u_unlock(cntx->soap);
	return retVal;
}



static WsEnumerateInfo *
get_locked_enuminfo(WsContextH cntx,
                    WsXmlDocH doc,
                    SoapOpH op,
                    char *action,
                    WsmanStatus *status)
{
	hnode_t *hn;
	WsEnumerateInfo *eInfo = NULL;
	char *enumId = NULL;
	WsXmlNodeH node = ws_xml_get_soap_body(doc);

	if (node && (node = ws_xml_get_child(node,
			0, XML_NS_ENUMERATION, action))) {
		node = ws_xml_get_child(node, 0,
			XML_NS_ENUMERATION, WSENUM_ENUMERATION_CONTEXT);
		if (node) {
			enumId = ws_xml_get_node_text(node);
		}
	}
	debug("enum context: %s", enumId);

	if (enumId == NULL) {
		status->fault_code = WSEN_INVALID_ENUMERATION_CONTEXT;
		return NULL;
	}
	u_lock(cntx->soap);
	hn = hash_lookup(cntx->enuminfos, enumId);
	if (hn) {
		eInfo = (WsEnumerateInfo *)hnode_get(hn);
		if (strcmp(eInfo->enumId, enumId)) {
			error("enum context mismatch: %s == %s",
			     eInfo->enumId, enumId);
			status->fault_code = WSMAN_INTERNAL_ERROR;
		} else if (wsman_verify_enum_info(op, eInfo, doc,status)) {
			if (eInfo->flags & WSMAN_ENUMINFO_INWORK_FLAG) {
				status->fault_code = WSMAN_CONCURRENCY;
			} else {
				eInfo->flags |= WSMAN_ENUMINFO_INWORK_FLAG;
			}
		}
	} else {
		status->fault_code = WSEN_INVALID_ENUMERATION_CONTEXT;
	}

	if (status->fault_code != WSMAN_RC_OK) {
		eInfo = NULL;
	}
	u_unlock(cntx->soap);
	return eInfo;
}

static void
unlock_enuminfo(WsContextH cntx, WsEnumerateInfo *enumInfo)
{
	struct timeval tv;
	gettimeofday(&tv, NULL);
	u_lock(cntx->soap);
	if (!(enumInfo->flags & WSMAN_ENUMINFO_INWORK_FLAG)) {
		error("locked enuminfo unlocked");
		u_unlock(cntx->soap);
		return;
	}
	enumInfo->flags &= ~WSMAN_ENUMINFO_INWORK_FLAG;
	enumInfo->timeStamp = tv.tv_sec;
	u_unlock(cntx->soap);
}

static void
ws_clear_context_entries(WsContextH hCntx)
{
	hash_t *h;
	if (!hCntx) {
		return;
	}
	h = hCntx->entries;
	hash_free(h);
}

static void
ws_clear_context_enuminfos(WsContextH hCntx)
{
	hash_t *h;
	if (!hCntx) {
		return;
	}
	h = hCntx->enuminfos;
	hash_free(h);
}

callback_t *
make_callback_entry(SoapServiceCallback proc,
		    void *data,
		    list_t * list_to_add)
{
	callback_t *entry = (callback_t *) u_malloc(sizeof(callback_t));
	debug("make new callback entry");
	if (entry) {
		lnode_init(&entry->node, data);
		entry->proc = proc;
		if (list_to_add || (list_to_add = list_create(LISTCOUNT_T_MAX))) {
			list_append(list_to_add, &entry->node);
		} else {
			u_free(entry);
			return NULL;
		}
	} else {
		return NULL;
	}
	return entry;
}



void
ws_initialize_context(WsContextH cntx, SoapH soap)
{
	cntx->entries = hash_create(HASHCOUNT_T_MAX, NULL, NULL);
	if (cntx->entries) {
		hash_set_allocator(cntx->entries, NULL, free_hentry_func, NULL);
	}

	cntx->enuminfos = hash_create(HASHCOUNT_T_MAX, NULL, NULL);
	cntx->subscriptionMemList = list_create(LISTCOUNT_T_MAX);
	if (cntx->enuminfos) {
		hash_set_allocator(cntx->enuminfos, NULL, free_hentry_func, NULL);
	}
	cntx->owner = 1;
	cntx->soap = soap;
	cntx->serializercntx = ws_serializer_init();
}

WsContextH
ws_create_context(SoapH soap)
{
	WsContextH cntx = (WsContextH) u_zalloc(sizeof (*cntx));
	if (cntx) {
		ws_initialize_context(cntx, soap);
	}
	return cntx;
}

SoapH
ws_soap_initialize()
{
	SoapH soap = (SoapH) u_zalloc(sizeof(*soap));

	if (soap == NULL) {
		error("Could not alloc memory");
		return NULL;
	}
	soap->cntx = ws_create_context(soap);

	soap->inboundFilterList = NULL;
	soap->outboundFilterList = NULL;
	soap->dispatchList = NULL;
	soap->processedMsgIdList = NULL;

	u_init_lock(soap);
	u_init_lock(&soap->lockSubs);
	ws_xml_parser_initialize();

	soap_add_filter(soap, outbound_addressing_filter, NULL, 0);
	soap_add_filter(soap, outbound_control_header_filter, NULL, 0);
	return soap;
}

void
ws_set_context_enumIdleTimeout(WsContextH cntx,
                               unsigned long timeout)
{
	cntx->enumIdleTimeout = timeout;
}



WsContextH
ws_create_runtime(list_t * interfaces)
{
	SoapH soap = ws_soap_initialize();
	WsManDispatcherInfo *dispInfo;
	int size;
	lnode_t *node;
	if (soap == NULL) {
		error("Could not initialize soap");
		return NULL;
	}

	if (interfaces == NULL) {
		return soap->cntx;
	}

	size = calculate_map_count(interfaces);
	dispInfo = (WsManDispatcherInfo *) u_zalloc(size);
	if (dispInfo == NULL) {
		error("Could not allocate memory");
		soap_destroy(soap);
		return NULL;
	}
	debug("Registering %d plugins", (int) list_count(interfaces));
	dispInfo->interfaceCount = list_count(interfaces);
	dispInfo->interfaces = interfaces;
	node = list_first(interfaces);
	while (node != NULL) {
	        WsDispatchInterfaceInfo *wdii = (WsDispatchInterfaceInfo *) node->list_data;
		if (wsman_register_interface(soap->cntx, wdii, dispInfo) != 0) {
			error("Interface registration failed for %s", wdii->displayName);
			u_free(dispInfo);
			soap_destroy(soap);
			return NULL;
		}
		node = list_next(interfaces, node);
	}
	ws_register_dispatcher(soap->cntx, wsman_dispatcher, dispInfo);
	return soap->cntx;
}




/**
 * Register Dispatcher Interfaces
 * @param cntx WS-Man Context
 * @param wsInterface Interface
 * @param dispinfo Dispatcher
 */
int
wsman_register_interface(WsContextH cntx,
			 WsDispatchInterfaceInfo * wsInterface,
			 WsManDispatcherInfo * dispInfo)
{
	int i, retVal = 0;
	WsDispatchEndPointInfo *ep = wsInterface->endPoints;

	for (i = 0; ep[i].serviceEndPoint != NULL; i++) {
		if ((retVal = wsman_register_endpoint(cntx, wsInterface,
						  &ep[i], dispInfo)) != 0) {
			break;
		}
	}
	return retVal;
}


/*
 * Register Endpoint
 * @param cntx Context
 * @param wsInterface Interface
 * @param ep Endpoint
 * @param dispInfo Dispatcher information
 */
int
wsman_register_endpoint(WsContextH cntx,
			WsDispatchInterfaceInfo *wsInterface,
			WsDispatchEndPointInfo *ep,
			WsManDispatcherInfo *dispInfo)
{
	SoapDispatchH   disp = NULL;
	unsigned long   flags = SOAP_CUSTOM_DISPATCHER;
	SoapServiceCallback callbackProc = NULL;
	SoapH soap = ws_context_get_runtime(cntx);
	char *action = NULL;

	debug("Registering Endpoint: %s", ep->inAction ? ep->inAction : "<null>");
	switch (ep->flags & WS_DISP_TYPE_MASK) {
	case WS_DISP_TYPE_IDENTIFY:
		debug("Registering endpoint for Identify");
		action = ep->inAction;
		callbackProc = wsman_identify_stub;
		break;
	case WS_DISP_TYPE_ENUMERATE:
		debug("Registering endpoint for Enumerate");
		action = ep->inAction;
		callbackProc = wsenum_enumerate_stub;
		break;
	case WS_DISP_TYPE_RELEASE:
		debug("Registering endpoint for Release");
		action = ep->inAction;
		callbackProc = wsenum_release_stub;
		break;
	case WS_DISP_TYPE_DELETE:
		debug("Registering endpoint for Delete");
		action = ep->inAction;
		callbackProc = ws_transfer_delete_stub;
		break;
	case WS_DISP_TYPE_PULL:
		debug("Registering endpoint for Pull");
		action = ep->inAction;
		callbackProc = wsenum_pull_stub;
		break;
	case WS_DISP_TYPE_DIRECT_PULL:
		debug("Registering endpoint for direct Pull");
		action = ep->inAction;
		callbackProc = wsenum_pull_direct_stub;
		break;
	case WS_DISP_TYPE_GET:
		debug("Registering endpoint for Get");
		action = ep->inAction;
		callbackProc = ws_transfer_get_stub;
		break;
	case WS_DISP_TYPE_DIRECT_GET:
		debug("Registering endpoint for direct Get");
		action = ep->inAction;
		callbackProc = (SoapServiceCallback) ep->serviceEndPoint;
		break;
	case WS_DISP_TYPE_DIRECT_DELETE:
		debug("Registering endpoint for Delete");
		action = ep->inAction;
		callbackProc = (SoapServiceCallback) ep->serviceEndPoint;
		break;
	case WS_DISP_TYPE_DIRECT_PUT:
		debug("Registering endpoint for direct Put");
		action = ep->inAction;
		callbackProc = (SoapServiceCallback) ep->serviceEndPoint;
		break;
	case WS_DISP_TYPE_DIRECT_CREATE:
		debug("Registering endpoint for direct Create");
		action = ep->inAction;
		callbackProc = (SoapServiceCallback) ep->serviceEndPoint;
		break;
	case WS_DISP_TYPE_PUT:
		debug("Registering endpoint for Put");
		action = ep->inAction;
		callbackProc = ws_transfer_put_stub;
		break;
#ifdef ENABLE_EVENTING_SUPPORT
	case WS_DISP_TYPE_SUBSCRIBE:
		debug("Registering endpoint for Subscribe");
		action = ep->inAction;
		callbackProc = wse_subscribe_stub;
		break;
	case WS_DISP_TYPE_UNSUBSCRIBE:
		debug("Registering endpoint for Unsubscribe");
		action = ep->inAction;
		callbackProc = wse_unsubscribe_stub;
		break;
	case WS_DISP_TYPE_RENEW:
		action = ep->inAction;
		callbackProc = wse_renew_stub;
		break;
#endif
	case WS_DISP_TYPE_RAW_DOC:
		action = ep->inAction;
		callbackProc = (SoapServiceCallback) ep->serviceEndPoint;
		break;
	case WS_DISP_TYPE_CUSTOM_METHOD:
		debug("Registering endpoint for custom method");
		action = ep->inAction;
		callbackProc = (SoapServiceCallback) ep->serviceEndPoint;
		break;

	case WS_DISP_TYPE_PRIVATE:
		debug("Registering endpoint for private EndPoint");
		action = ep->inAction;
		callbackProc = (SoapServiceCallback) ep->serviceEndPoint;
		break;

	default:
		debug("unknown dispatch type %lu",
			ep->flags & WS_DISP_TYPE_MASK);
		break;
	}

	if (callbackProc != NULL &&
			(disp = wsman_dispatch_create(soap, action, NULL, NULL, callbackProc, ep, flags))) {
		dispInfo->map[dispInfo->mapCount].ep = ep;
		dispInfo->map[dispInfo->mapCount].disp = disp;
		dispInfo->mapCount++;
		wsman_dispatch_start(disp);
	}
	if (action && action != ep->inAction) {
		u_free(action);
	}
	return (disp == NULL);
}

/*  ENDPOINTS STUBS */

int
wsman_identify_stub(SoapOpH op,
		    void *appData,
			void *opaqueData)
{
	void           *data;
	WsXmlDocH       doc = NULL;
	WsContextH      cntx;
	WsDispatchEndPointInfo *info;
	XmlSerializerInfo *typeInfo;
	WsmanStatus    *status;
	SoapH           soap;
	WsEndPointGet   endPoint;

	soap = soap_get_op_soap(op);
	if (soap == NULL) {
		error("soap_get_op_soap failed");
		return -1;
	}

	status = u_zalloc(sizeof(WsmanStatus *));
	cntx = ws_create_ep_context(soap, soap_get_op_doc(op, 1));
	if (cntx == NULL) {
		error("ws_create_ep_context failed");
		u_free(status);
		return -1;
	}
	info = (WsDispatchEndPointInfo *) appData;
	typeInfo = info->serializationInfo;
	endPoint = (WsEndPointGet) info->serviceEndPoint;
	debug("Identify called");

	if ((data = endPoint(cntx, status, opaqueData)) == NULL) {
		error("Identify Fault");
		doc = wsman_generate_fault(soap_get_op_doc(op, 1), WSMAN_INTERNAL_ERROR, 0, NULL);
	} else {
		doc = wsman_create_response_envelope(soap_get_op_doc(op, 1), NULL);
		ws_serialize(cntx->serializercntx, ws_xml_get_soap_body(doc), data, typeInfo,
			WSMID_IDENTIFY_RESPONSE, (char *) info->data, NULL, 1);
		ws_serializer_free_mem(cntx->serializercntx, data, typeInfo);
                u_free(data);
	}

	if (doc) {
		soap_set_op_doc(op, doc, 0);
	} else {
		error("Response doc invalid");
	}

	ws_destroy_context(cntx);
	u_free(status);

	return 0;
}




int
ws_transfer_put_stub(SoapOpH op,
		     void *appData,
			void *opaqueData)
{
	int             retVal = 0;
	WsXmlDocH       doc = NULL;
	void           *outData = NULL;
	WsmanStatus     status;
	SoapH           soap = soap_get_op_soap(op);

	if (soap == NULL) {
		error("soap_get_op_soap failed");
		return -1;
	}

	WsContextH   cntx = ws_create_ep_context(soap, soap_get_op_doc(op, 1));
	if (cntx == NULL) {
		error("ws_create_ep_context");
		return -1;
	}
	WsDispatchEndPointInfo *info = (WsDispatchEndPointInfo *) appData;
	XmlSerializerInfo *typeInfo = info->serializationInfo;
	WsEndPointPut   endPoint = (WsEndPointPut) info->serviceEndPoint;

	WsXmlDocH       _doc = soap_get_op_doc(op, 1);
	WsXmlNodeH      _body = ws_xml_get_soap_body(_doc);
	WsXmlNodeH      _r = ws_xml_get_child(_body, 0, NULL, NULL);

	void  *data = ws_deserialize(cntx->serializercntx, _body, typeInfo,
					ws_xml_get_node_local_name(_r),
			    	(char *) info->data, NULL, 0, 0);

	if ((retVal = endPoint(cntx, data, &outData, &status, opaqueData))) {
		doc = wsman_generate_fault( _doc, status.fault_code,
					   status.fault_detail_code, NULL);
	} else {
		doc = wsman_create_response_envelope(_doc, NULL);
		if (outData) {
			ws_serialize(cntx->serializercntx, ws_xml_get_soap_body(doc), outData,
				     typeInfo, TRANSFER_PUT_RESP,
                     (char *) info->data, NULL, 1);
			ws_serializer_free_mem(cntx->serializercntx, outData, typeInfo);
		}
	}

	if (doc) {
		soap_set_op_doc(op, doc, 0);
	}
	ws_serializer_free_all(cntx->serializercntx);
	ws_serializer_cleanup(cntx->serializercntx);
	return retVal;
}




int
ws_transfer_delete_stub(SoapOpH op,
		     void *appData,
			void *opaqueData)
{
	WsmanStatus     status;
	SoapH           soap = soap_get_op_soap(op);

	if (soap == NULL) {
		error("soap_get_op_soap failed");
		return -1;
	}

	WsContextH      cntx = ws_create_ep_context(soap,
					soap_get_op_doc(op, 1));

	WsDispatchEndPointInfo *info = (WsDispatchEndPointInfo *) appData;
	WsEndPointGet   endPoint = (WsEndPointGet) info->serviceEndPoint;

	void           *data;
	WsXmlDocH       doc = NULL;

	wsman_status_init(&status);
	if ((data = endPoint(cntx, &status, opaqueData)) == NULL) {
		warning("Transfer Delete fault");
		doc = wsman_generate_fault(soap_get_op_doc(op, 1),
					 WSMAN_INVALID_SELECTORS, 0, NULL);
	} else {
		debug("Creating Response doc");
		doc = wsman_create_response_envelope(soap_get_op_doc(op, 1), NULL);
	}

	if (doc) {
		soap_set_op_doc(op, doc, 0);
	} else {
		error("Response doc invalid");
	}
	ws_destroy_context(cntx);
	return 0;
}




int
ws_transfer_get_stub(SoapOpH op,
		     void *appData,
			void *opaqueData)
{
	WsmanStatus     status;


	SoapH       soap = soap_get_op_soap(op);

	if (soap == NULL) {
		error("soap_get_op_soap failed");
		return -1;
	}

	WsContextH  cntx = ws_create_ep_context(soap, soap_get_op_doc(op, 1));
	if (cntx == NULL) {
		error("ws_create_ep_context failed");
		return -1;
	}
	WsDispatchEndPointInfo *info = (WsDispatchEndPointInfo *) appData;
	XmlSerializerInfo *typeInfo = info->serializationInfo;
	WsEndPointGet   endPoint = (WsEndPointGet) info->serviceEndPoint;

	void           *data;
	WsXmlDocH       doc = NULL;

	wsman_status_init(&status);
	if ((data = endPoint(cntx, &status, opaqueData)) == NULL) {
		warning("Transfer Get fault");
		doc = wsman_generate_fault( soap_get_op_doc(op, 1),
					 WSMAN_INVALID_SELECTORS, 0, NULL);
	} else {
		debug("Creating Response doc");
		doc = wsman_create_response_envelope(soap_get_op_doc(op, 1), NULL);

		ws_serialize(cntx->serializercntx, ws_xml_get_soap_body(doc), data, typeInfo,
			 TRANSFER_GET_RESP, (char *) info->data, NULL, 1);
		ws_serializer_free_mem(cntx->serializercntx, data, typeInfo);
	}

	if (doc) {
		debug("Setting operation document");
		soap_set_op_doc(op, doc, 0);
	} else {
		warning("Response doc invalid");
	}

	ws_destroy_context(cntx);
	return 0;
}



WsmanMessage *wsman_get_msg_from_op(SoapOpH op)
{
	op_t *_op = (op_t *)op;
	WsmanMessage *msg = (WsmanMessage *)_op->data;
	return msg;
}

unsigned long wsman_get_maxsize_from_op(SoapOpH op)
{
	op_t *_op = (op_t *)op;
	return _op->maxsize;
}

static
unsigned long get_total_enum_context(WsContextH cntx){
        hscan_t hs;
        u_lock(cntx->soap);
        hash_scan_begin(&hs, cntx->enuminfos);
        unsigned long total = hash_count(hs.hash_table);
        u_unlock(cntx->soap);
        return total;
}

/**
 * The following extern methods are defined in wsmand-daemon.c,
 * which is compiled into openwsmand binary, which in turn links
 * to libwsman.la. So when a call is made to the following methods
 * from the openwsmand binary, they should be present.
 *
 * However, if they are dlopened from somewhere other than 
 * openwsmand library or linked to some other
 * binary or shared object, then these methods may or may not be
 * preset, hence marking them as weak symbols and testing to see
 * if they are resolved before using them.
 */
#pragma weak wsmand_options_get_max_threads
extern int wsmand_options_get_max_threads(void);
#pragma weak wsmand_options_get_max_connections_per_thread
extern int wsmand_options_get_max_connections_per_thread(void);

/**
 * Enumeration Stub for processing enumeration requests
 * @param op SOAP operation handler
 * @param appData Application data
 * @return status
 */
int
wsenum_enumerate_stub(SoapOpH op,
		      void *appData,
			void *opaqueData)
{
	WsXmlDocH       doc = NULL;
	int             retVal = 0;
	WsEnumerateInfo *enumInfo = NULL;
	WsmanStatus     status;
	WsXmlNodeH      resp_node, body;
	WsContextH      soapCntx;
	SoapH           soap = soap_get_op_soap(op);

	if (soap == NULL) {
		error("soap_get_op_soap failed");
		return -1;
	}

	WsDispatchEndPointInfo *ep = (WsDispatchEndPointInfo *) appData;
	WsEndPointEnumerate endPoint =
			(WsEndPointEnumerate)ep->serviceEndPoint;

	WsXmlDocH       _doc = soap_get_op_doc(op, 1);
	WsContextH      epcntx;

        int max_threads = 0;
        int max_connections_per_thread = 0;
        int(* fptr)(void);
        if((fptr = wsmand_options_get_max_threads) != 0){
                max_threads = (* fptr)();
                if((fptr = wsmand_options_get_max_connections_per_thread) != 0){
                        max_connections_per_thread = (* fptr)();
                }
                else{
                        debug("Could not resolve wsmand_options_get_max_connections_per_thread");
                        max_threads=0;
                }
        }
        else{
                debug("Could not resolve wsman_options_get_max_threads");
        }

        if(max_threads){
                if(get_total_enum_context(ws_get_soap_context(soap)) >= (max_threads * max_connections_per_thread)){
                        debug("enum context queue is full, we wait till some expire or are cleared");
                        doc = wsman_generate_fault(_doc, WSMAN_QUOTA_LIMIT, OWSMAN_NO_DETAILS,
                                    "The service is busy servicing other requests. Try later.");
                        if(doc){
                                soap_set_op_doc(op, doc, 0);
                        }
                        return 1;
                }
        }

	wsman_status_init(&status);

	epcntx = ws_create_ep_context(soap, _doc);
	if (epcntx == NULL) {
		goto DONE;
	}
	doc = create_enum_info(op, epcntx, _doc, &enumInfo);
	if (doc != NULL) {
		/* wrong enum elements met. Fault message generated */
		goto DONE;
	}

	if (endPoint && (retVal = endPoint(epcntx, enumInfo, &status, opaqueData))) {
                debug("enumeration fault");
		doc = wsman_generate_fault( _doc, status.fault_code,
				status.fault_detail_code, status.fault_msg);
		goto DONE;
	}
	if (enumInfo->pullResultPtr) {
		doc = enumInfo->pullResultPtr;
		enumInfo->index++;
	} else {
		doc = wsman_create_response_envelope( _doc, NULL);
	}

	if (!doc)
		goto DONE;

	wsman_set_estimated_total(_doc, doc, enumInfo);
	body = ws_xml_get_soap_body(doc);
	if (!body) {
		goto DONE;
	}

	if (enumInfo->pullResultPtr == NULL) {
		resp_node = ws_xml_add_child(body, XML_NS_ENUMERATION,
					     WSENUM_ENUMERATE_RESP, NULL);
	} else {
		resp_node = ws_xml_get_child(body, 0,
				 XML_NS_ENUMERATION, WSENUM_ENUMERATE_RESP);
	}
	if (!resp_node) {
		goto DONE;
	}

	soapCntx = ws_get_soap_context(soap);
	if (( enumInfo->flags & WSMAN_ENUMINFO_OPT ) == WSMAN_ENUMINFO_OPT  &&
		(enumInfo->totalItems == 0 || enumInfo->index == enumInfo->totalItems)) {
		ws_serialize_str(epcntx->serializercntx, resp_node, NULL,
			    XML_NS_ENUMERATION, WSENUM_ENUMERATION_CONTEXT, 0);
		ws_serialize_str(epcntx->serializercntx, resp_node,
			       NULL, XML_NS_WS_MAN, WSENUM_END_OF_SEQUENCE, 0);
	} else {
		ws_serialize_str(epcntx->serializercntx, resp_node, enumInfo->enumId,
			    XML_NS_ENUMERATION, WSENUM_ENUMERATION_CONTEXT, 0);
		insert_enum_info(soapCntx, enumInfo);
		enumInfo = NULL;
	}

DONE:
	if (doc) {
		soap_set_op_doc(op, doc, 0);
	}
	ws_destroy_context(epcntx);
	u_free(status.fault_msg);
	if (enumInfo) {
		destroy_enuminfo(enumInfo);
	}
	return retVal;
}



int
wsenum_release_stub(SoapOpH op,
		    void *appData,
			void *opaqueData)
{
	int             retVal = 0;
	WsXmlDocH       doc = NULL;
	WsmanStatus     status;
	SoapH           soap = soap_get_op_soap(op);

	if (soap == NULL) {
		error("soap_get_op_soap failed");
		return -1;
	}

	WsContextH      soapCntx = ws_get_soap_context(soap);
	WsDispatchEndPointInfo *ep = (WsDispatchEndPointInfo *) appData;
	WsEndPointRelease endPoint = (WsEndPointRelease) ep->serviceEndPoint;
	WsXmlDocH       _doc = soap_get_op_doc(op, 1);
	WsEnumerateInfo *enumInfo;

	wsman_status_init(&status);
	enumInfo = get_locked_enuminfo(soapCntx, _doc,
		op, WSENUM_RELEASE, &status);

        ws_set_context_xml_doc_val(soapCntx, WSFW_INDOC, _doc);

	if (enumInfo == NULL) {
		doc = wsman_generate_fault( _doc,
			status.fault_code, status.fault_detail_code, NULL);

	} else {
		if (endPoint && (retVal = endPoint(soapCntx,
						enumInfo, &status, opaqueData))) {
			error("endPoint error");
			doc = wsman_generate_fault( _doc,
				WSMAN_INTERNAL_ERROR,
				OWSMAN_DETAIL_ENDPOINT_ERROR, NULL);
			unlock_enuminfo(soapCntx, enumInfo);
		} else {
			doc = wsman_create_response_envelope( _doc, NULL);
			debug("Releasing context: %s", enumInfo->enumId);
			remove_locked_enuminfo(soapCntx, enumInfo);
			destroy_enuminfo(enumInfo);
		}
	}
	if (doc) {
		soap_set_op_doc(op, doc, 0);
	}
	return retVal;
}





int
wsenum_pull_stub(SoapOpH op, void *appData,
			void *opaqueData)
{
	WsXmlNodeH      node;
	WsmanStatus     status;
	SoapH           soap = soap_get_op_soap(op);

	if (soap == NULL) {
		error("soap_get_op_soap failed");
		return -1;
	}

	WsContextH      soapCntx = ws_get_soap_context(soap);

	WsDispatchEndPointInfo *ep = (WsDispatchEndPointInfo *) appData;
	XmlSerializerInfo *typeInfo = ep->serializationInfo;
	WsEndPointPull  endPoint = (WsEndPointPull) ep->serviceEndPoint;

	int retVal = 0, locked = 0;
	WsXmlDocH doc = NULL;
	char *enumId = NULL;

	WsXmlDocH _doc = soap_get_op_doc(op, 1);
	WsEnumerateInfo *enumInfo;
	WsXmlNodeH body;

	wsman_status_init(&status);
	enumInfo = get_locked_enuminfo(soapCntx, _doc,
		op, WSENUM_PULL, &status);

	if (enumInfo == NULL) {
		doc = wsman_generate_fault( _doc, status.fault_code, status.fault_detail_code, NULL);
		goto DONE;
	}
	locked = 1;
	if ((retVal = endPoint(ws_create_ep_context(soap, _doc),
						enumInfo, &status, opaqueData))) {
		doc = wsman_generate_fault(_doc, status.fault_code, status.fault_detail_code, NULL);
		goto DONE;
	}
	enumInfo->index++;
	doc = wsman_create_response_envelope( _doc, NULL);

	if (!doc) {
		goto DONE;
	}

	wsman_set_estimated_total(_doc, doc, enumInfo);
	body = ws_xml_get_soap_body(doc);
	if (!body) {
		goto DONE;
	}

	node = ws_xml_add_child(body, XML_NS_ENUMERATION, WSENUM_PULL_RESP, NULL);
	if (node == NULL) {
		goto DONE;
	}
	if (enumInfo->pullResultPtr) {
		if (enumId) {
			ws_serialize_str(soapCntx->serializercntx, node, enumId,
			    XML_NS_ENUMERATION, WSENUM_ENUMERATION_CONTEXT, 0);
		}
		WsXmlNodeH itemsNode = ws_xml_add_child(node,
				    XML_NS_ENUMERATION, WSENUM_ITEMS, NULL);
		ws_serialize(soapCntx->serializercntx, itemsNode, enumInfo->pullResultPtr,
			 typeInfo, ep->respName, (char *) ep->data, NULL, 1);
		ws_serializer_free_mem(soapCntx->serializercntx,
			enumInfo->pullResultPtr, typeInfo);
	} else {
		/*
		ws_serialize_str(soapCntx, node, NULL,
			    XML_NS_ENUMERATION, WSENUM_ENUMERATION_CONTEXT, 0);
			    */
		ws_serialize_str(soapCntx->serializercntx,
		    node, NULL, XML_NS_ENUMERATION, WSENUM_END_OF_SEQUENCE, 0);
		remove_locked_enuminfo(soapCntx, enumInfo);
		locked = 0;
		destroy_enuminfo(enumInfo);
	}

DONE:
	if (locked) {
		unlock_enuminfo(soapCntx, enumInfo);
	}
	if (doc) {
		soap_set_op_doc(op, doc, 0);
	}
	return retVal;
}


int
wsenum_pull_direct_stub(SoapOpH op,
		     void *appData,
			void *opaqueData)
{
	WsmanStatus     status;
	WsXmlDocH       doc = NULL;
	SoapH           soap = soap_get_op_soap(op);

	if (soap == NULL) {
		error("soap_get_op_soap failed");
		return -1;
	}

	WsContextH      soapCntx = ws_get_soap_context(soap);
	WsDispatchEndPointInfo *ep = (WsDispatchEndPointInfo *) appData;
#ifdef ENABLE_EVENTING_SUPPORT
	WsNotificationInfoH notificationInfo = NULL;
#endif

	WsEndPointPull  endPoint = (WsEndPointPull) ep->serviceEndPoint;
	int             retVal = 0;
	WsXmlDocH       _doc = soap_get_op_doc(op, 1);
	int locked = 0;
	WsEnumerateInfo *enumInfo;
	WsSubscribeInfo *subsInfo = NULL;
	wsman_status_init(&status);
	enumInfo = get_locked_enuminfo(soapCntx,
	                               _doc, op, WSENUM_PULL, &status);

	if (enumInfo == NULL) {
		subsInfo = search_pull_subs_info(soap, _doc);
		if(subsInfo == NULL) {
			error("Invalid enumeration context...");
			doc = wsman_generate_fault( _doc, status.fault_code, status.fault_detail_code, NULL);
			goto cleanup;
		}
	}
	if (enumInfo) { //pull things from "enumerate" results
		locked = 1;

		if ((retVal = endPoint(ws_create_ep_context(soap, _doc),
						enumInfo, &status, opaqueData))) {
			doc = wsman_generate_fault( _doc, status.fault_code, status.fault_detail_code, NULL);
//			ws_remove_context_val(soapCntx, cntxName);
			goto cleanup;
		}

		enumInfo->index++;
		if (enumInfo->pullResultPtr) {
			WsXmlNodeH      body;
			WsXmlNodeH      response, items;
			doc = enumInfo->pullResultPtr;
			wsman_set_estimated_total(_doc, doc, enumInfo);

			body = ws_xml_get_soap_body(doc);
			response = ws_xml_get_child(body, 0,
					XML_NS_ENUMERATION, WSENUM_PULL_RESP);
			items = ws_xml_get_child(response, 0,
                                        XML_NS_ENUMERATION, WSENUM_ITEMS);
			if (items == NULL) {
				error("Invalid enumeration items...");
				doc = wsman_generate_fault( _doc, WSMAN_INTERNAL_ERROR, WSMAN_DETAIL_INVALID, NULL);
				goto cleanup;
			}
			if (enumInfo->totalItems == 0 || enumInfo->index == enumInfo->totalItems) {
				/*
				   ws_serialize_str(soapCntx, response, NULL,
				   XML_NS_ENUMERATION, WSENUM_ENUMERATION_CONTEXT, 0);
				   */
				ws_serialize_str(soapCntx->serializercntx, response, NULL,
						XML_NS_ENUMERATION, WSENUM_END_OF_SEQUENCE, 0);
				remove_locked_enuminfo(soapCntx, enumInfo);
				locked = 0;
				destroy_enuminfo(enumInfo);
			} else  {
			        /* add Context before Items to comply to WS-Enumeration xsd */
				ws_xml_add_prev_sibling(items, XML_NS_ENUMERATION,
							WSENUM_ENUMERATION_CONTEXT, enumInfo->enumId);
			}
		}
	}
#ifdef ENABLE_EVENTING_SUPPORT
	else { //pull things from notifications
		ws_xml_destroy_doc(doc);
		pthread_mutex_lock(&subsInfo->notificationlock);
		int count = soap->eventpoolOpSet->count(subsInfo->subsId);
		int max_elements = 1;
		if(count > 0) {
			doc = ws_xml_create_envelope();
			WsXmlNodeH docnode = ws_xml_get_soap_body(doc);
			WsXmlNodeH docheader = ws_xml_get_soap_header(doc);
			docnode = ws_xml_add_child(docnode, XML_NS_ENUMERATION, WSENUM_PULL_RESP, NULL);
			if(docnode) {
				ws_xml_add_child_format(docnode, XML_NS_ENUMERATION, WSENUM_ENUMERATION_CONTEXT,
					"uuid:%s", subsInfo->subsId);
			}
			WsXmlDocH notidoc = NULL;
			WsXmlNodeH header = ws_xml_get_soap_header(_doc);
			if (ws_xml_get_child(header, 0,XML_NS_WS_MAN, WSM_REQUEST_TOTAL) != NULL) {
				WsXmlNodeH response_header =ws_xml_get_soap_header(doc);
				response_header = ws_xml_add_child(response_header, XML_NS_WS_MAN,
					WSM_TOTAL_ESTIMATE, NULL);
				if(response_header)
					ws_xml_add_node_attr(response_header, XML_NS_SCHEMA_INSTANCE, XML_SCHEMA_NIL, "true");
			}
			header = ws_xml_get_child(header, 0, XML_NS_ENUMERATION, WSENUM_MAX_ELEMENTS);
			if(header)
				max_elements = atoi(ws_xml_get_node_text(header));
			if(max_elements > 1 && count > 1) {
				docnode = ws_xml_add_child(docnode, XML_NS_ENUMERATION, WSENUM_ITEMS, NULL);
			}
			while(max_elements > 0) {
				if(soap->eventpoolOpSet->remove(subsInfo->subsId, &notificationInfo))
					break;
				ws_xml_add_child(docheader, XML_NS_ADDRESSING, WSA_ACTION, notificationInfo->EventAction);
				notidoc = notificationInfo->EventContent;
				WsXmlNodeH tempnode = ws_xml_get_doc_root(notidoc);
				ws_xml_duplicate_tree(docnode, tempnode);
				delete_notification_info(notificationInfo);
				max_elements--;
			}
		}
		else {
			status.fault_code = WSMAN_TIMED_OUT;
			doc = wsman_generate_fault( _doc, status.fault_code, status.fault_detail_code, NULL);
		}
		pthread_mutex_unlock(&subsInfo->notificationlock);
	}
#endif
cleanup:
	if (locked) {
		unlock_enuminfo(soapCntx, enumInfo);
	}
	if (doc) {
		soap_set_op_doc(op, doc, 0);
	} else {
		error("doc is null");
	}

	return retVal;
}

static list_t *
wsman_get_expired_enuminfos(WsContextH cntx)
{
	list_t *list = NULL;
	hnode_t        *hn;
	hscan_t         hs;
	WsEnumerateInfo *enumInfo;
	struct timeval tv;
	unsigned long mytime;
	unsigned long aeit = cntx->enumIdleTimeout;

	if (aeit == 0) {
		return NULL;
	}
	gettimeofday(&tv, NULL);
	mytime = tv.tv_sec;
	u_lock(cntx->soap);
	if (hash_isempty(cntx->enuminfos)) {
		u_unlock(cntx->soap);
		return NULL;
	}
	hash_scan_begin(&hs, cntx->enuminfos);
	while ((hn = hash_scan_next(&hs))) {
		enumInfo = (WsEnumerateInfo *)hnode_get(hn);
		if (enumInfo->flags & WSMAN_ENUMINFO_INWORK_FLAG) {
			debug("Enum in work: %s", enumInfo->enumId);
			continue;
		}
		if ((enumInfo->timeStamp + aeit > mytime) &&
				((enumInfo->expires == 0) ||
				(enumInfo->expires > mytime))) {
			continue;
		}
		if (list == NULL) {
			list = list_create(LISTCOUNT_T_MAX);
		}
		if (list == NULL) {
			u_unlock(cntx->soap);
			error("could not create list");
			return NULL;
		}
		hash_scan_delfree(cntx->enuminfos, hn);
		list_append(list, lnode_create(enumInfo));
		debug("Enum expired list appended: %s", enumInfo->enumId);
	}
	u_unlock(cntx->soap);
	return list;
}

void
wsman_timeouts_manager(WsContextH cntx, void *opaqueData)
{
	list_t *list = wsman_get_expired_enuminfos(cntx);
	lnode_t *node;
	WsEnumerateInfo *enumInfo;
	WsmanStatus status;

	if (list == NULL) {
		return;
	}
	while ((node = list_del_first(list))) {
		enumInfo = (WsEnumerateInfo *)lnode_get(node);
		debug("EnumContext expired : %s", enumInfo->enumId);
		lnode_destroy(node);
		if (enumInfo->releaseproc) {
			if (enumInfo->releaseproc(cntx, enumInfo, &status, opaqueData)) {
				debug("released with failure: %s",
						enumInfo->enumId);
			} else {
				debug("released: %s", enumInfo->enumId);
			}
		} else {
			debug("no release endpoint: %s", enumInfo->enumId);
		}
		destroy_enuminfo(enumInfo);
		if (list_isempty(list)) {
			list_destroy(list);
			break;
		}
	}
	return;
}


#ifdef ENABLE_EVENTING_SUPPORT
static int destination_reachable(char *url)
{
	int valid = 0;
	u_uri_t *uri = NULL;
	if(strstr(url, "http") == NULL)
		return valid;
	if (u_uri_parse((const char *)url, &uri) == 0) {
		valid = 1;
	}
	u_uri_free(uri);
	return valid;
}

WsEventThreadContextH
ws_create_event_context(SoapH soap, WsSubscribeInfo *subsInfo, WsXmlDocH doc)
{
	WsEventThreadContextH eventcntx = u_malloc(sizeof(*eventcntx));
	eventcntx->soap = soap;
	eventcntx->subsInfo = subsInfo;
	eventcntx->outdoc = doc;
	return eventcntx;
}

static void
destroy_subsinfo(WsSubscribeInfo * subsInfo)
{
	if(subsInfo == NULL) return;
	u_free(subsInfo->uri);
	u_free(subsInfo->auth_data.username);
	u_free(subsInfo->auth_data.password);
	u_free(subsInfo->epr_notifyto);
	u_free(subsInfo->locale);
	u_free(subsInfo->soapNs);
	u_free(subsInfo->contentEncoding);
	u_free(subsInfo->cim_namespace);
	u_free(subsInfo->username);
	u_free(subsInfo->password);
	u_free(subsInfo->certificate_thumbprint);
	if (subsInfo->filter) {
		filter_destroy(subsInfo->filter);
	}
	ws_xml_destroy_doc(subsInfo->bookmarkDoc);
	ws_xml_destroy_doc(subsInfo->templateDoc);
	ws_xml_destroy_doc(subsInfo->heartbeatDoc);
	u_free(subsInfo);
}


static void
create_notification_template(WsXmlDocH indoc, WsSubscribeInfo *subsInfo)
{
	WsXmlDocH notificationDoc = ws_xml_create_envelope();
	WsXmlNodeH temp = NULL;
	WsXmlNodeH node = NULL;
	WsXmlNodeH header = NULL;
	header = ws_xml_get_soap_header(notificationDoc);
	ws_xml_add_child(header, XML_NS_ADDRESSING, WSA_TO, subsInfo->epr_notifyto);
	if(subsInfo->deliveryMode == WS_EVENT_DELIVERY_MODE_EVENTS ||
		subsInfo->deliveryMode == WS_EVENT_DELIVERY_MODE_PUSHWITHACK) {
		ws_xml_add_child(header, XML_NS_WS_MAN, WSM_ACKREQUESTED, NULL);
	}
	node = ws_xml_get_soap_body(indoc);
	node = ws_xml_get_child(node, 0, XML_NS_EVENTING, WSEVENT_SUBSCRIBE);
	node = ws_xml_get_child(node, 0, XML_NS_EVENTING, WSEVENT_DELIVERY);
	node = ws_xml_get_child(node, 0, XML_NS_EVENTING, WSEVENT_NOTIFY_TO);
	temp = ws_xml_get_child(node, 0, XML_NS_ADDRESSING, WSA_REFERENCE_PROPERTIES);
	if(temp == NULL)
		node = ws_xml_get_child(node, 0, XML_NS_ADDRESSING, WSA_REFERENCE_PARAMETERS);
	if(node ) {
		ws_xml_duplicate_children(header, node);
	}
	subsInfo->templateDoc = ws_xml_duplicate_doc(notificationDoc);
	subsInfo->heartbeatDoc = ws_xml_duplicate_doc( notificationDoc);
	temp = ws_xml_get_soap_header(subsInfo->heartbeatDoc);
	temp = ws_xml_add_child(temp, XML_NS_ADDRESSING, WSA_ACTION, WSMAN_ACTION_HEARTBEAT);
	ws_xml_add_node_attr(temp, XML_NS_XML_SCHEMA, SOAP_MUST_UNDERSTAND, "true");
	ws_xml_destroy_doc(notificationDoc);
}


static WsXmlDocH
create_subs_info(SoapOpH op,
		 		WsContextH epcntx,
              	WsXmlDocH indoc,
              	WsSubscribeInfo**sInfo)
{
	WsXmlNodeH  node = ws_xml_get_soap_body(indoc);
	WsXmlNodeH	subNode = ws_xml_get_child(node, 0, XML_NS_EVENTING, WSEVENT_SUBSCRIBE);
	WsXmlNodeH	temp;
	WsXmlDocH outdoc = NULL;
	WsSubscribeInfo *subsInfo;
	WsXmlAttrH attr = NULL;
	op_t *_op = (op_t *) op;
	WsmanMessage   *msg = (WsmanMessage *) _op->data;
	WsmanFaultCodeType fault_code = WSMAN_RC_OK;
	WsmanFaultDetailType fault_detail_code = WSMAN_DETAIL_OK;
	char *str = NULL;
	time_t timeout;
	int r;
	char *soapNs = NULL, *ntext = NULL;


	*sInfo = NULL;
	subsInfo = (WsSubscribeInfo *)u_zalloc(sizeof (WsSubscribeInfo));
	if (subsInfo == NULL) {
		error("No memory");
		fault_code = WSMAN_INTERNAL_ERROR;
		goto DONE;
	}
	if((r = pthread_mutex_init(&subsInfo->notificationlock, NULL)) != 0) {
		fault_code = WSMAN_INTERNAL_ERROR;
		goto DONE;
	}
	subsInfo->uri = u_strdup(wsman_get_resource_uri(epcntx, indoc));
	if(!subNode) {
		message("No subscribe body");
		fault_code = WSE_INVALID_MESSAGE;
		goto DONE;
	}
	soapNs = ws_xml_get_node_name_ns(ws_xml_get_doc_root(indoc));
	subsInfo->soapNs = u_strdup(soapNs);
	node = ws_xml_get_child(subNode, 0, XML_NS_WS_MAN, WSM_SENDBOOKMARKS);
	if(node) {
		subsInfo->bookmarksFlag = 1;
	}
	node = ws_xml_get_child(subNode, 0, XML_NS_WS_MAN, WSM_BOOKMARK);
	if(node) {
		if(ws_xml_get_node_text(node) &&
			!strcmp(ws_xml_get_node_text(node), WSM_DEFAULTBOOKMARK)){
			subsInfo->flags |= WSMAN_SUBSCRIBEINFO_BOOKMARK_DEFAULT;
		}
		else {
			subsInfo->bookmarkDoc = ws_xml_create_doc(XML_NS_WS_MAN, WSM_BOOKMARK);
			temp = ws_xml_get_doc_root(subsInfo->bookmarkDoc);
			ws_xml_duplicate_children(temp, node);
		}
	}
	node = ws_xml_get_child(subNode, 0, XML_NS_EVENTING, WSEVENT_EXPIRES);
	if (node == NULL) {
		debug("No wsen:Expires");
		subsInfo->expires = 0;
	}
	else {
		wsman_set_expiretime(node, &subsInfo->expires, &fault_code);
		if (fault_code != WSMAN_RC_OK) {
			debug("Invalid expiration time!");
			goto DONE;
		}
		if(time_expired(subsInfo->expires)) {
			fault_code = WSE_INVALID_EXPIRATION_TIME;
			debug("Invalid expiration time!");
			goto DONE;
		}
	}
	node = ws_xml_get_child(subNode, 0, XML_NS_EVENTING, WSEVENT_DELIVERY);
	attr = ws_xml_find_node_attr(node, NULL,WSEVENT_DELIVERY_MODE);
	if(attr) {
		str = ws_xml_get_attr_value(attr);
		if (!strcasecmp(str, WSEVENT_DELIVERY_MODE_PUSH)) {
			subsInfo->deliveryMode = WS_EVENT_DELIVERY_MODE_PUSH;
		}
		else if (!strcasecmp(str, WSEVENT_DELIVERY_MODE_PUSHWITHACK)) {
			subsInfo->deliveryMode = WS_EVENT_DELIVERY_MODE_PUSHWITHACK;
		}
		else if (!strcasecmp(str, WSEVENT_DELIVERY_MODE_EVENTS))  {
			subsInfo->deliveryMode = WS_EVENT_DELIVERY_MODE_EVENTS;
		}
		else {
			subsInfo->deliveryMode = WS_EVENT_DELIVERY_MODE_PULL;
		}
	}
	else {
		//"push" is the default delivery mode
		subsInfo->deliveryMode = WS_EVENT_DELIVERY_MODE_PUSH;
	}
	temp = ws_xml_get_child(node, 0, XML_NS_WS_MAN, WSM_CONTENTCODING);
	if(temp){
		str = ws_xml_get_node_text(temp);
		subsInfo->contentEncoding = u_strdup(str);
	}
	temp = ws_xml_get_child(node, 0, XML_NS_WS_MAN, WSM_LOCALE);
	if(temp) {
		attr = ws_xml_find_node_attr(temp, XML_NS_WS_MAN, WSM_LOCALE);
		if(attr)
			subsInfo->locale = u_strdup(ws_xml_get_attr_value(attr));
	}
	temp = ws_xml_get_child(node, 0, XML_NS_WS_MAN, WSM_HEARTBEATS);
	if(temp) {
		str = ws_xml_get_node_text(temp);
		debug("[heartbeat interval = %s]",str);
		if(str[0]=='P') {
			//  xml duration
			if (ws_deserialize_duration(str, &timeout)) {
				fault_code = WSEN_INVALID_EXPIRATION_TIME;
				goto DONE;
			}
			debug("timeout = %d", timeout);
			subsInfo->heartbeatInterval = timeout * 1000;
			subsInfo->heartbeatCountdown = subsInfo->heartbeatInterval;
		}
	}
	if(subsInfo->deliveryMode != WS_EVENT_DELIVERY_MODE_PULL) {
		temp = ws_xml_get_child(node, 0, XML_NS_EVENTING, WSEVENT_NOTIFY_TO);
		if(temp == NULL) {
			message("No notification destination");
			fault_code = WSE_INVALID_MESSAGE;
			goto DONE;
		}
		str = ws_xml_get_node_text(ws_xml_get_child(temp, 0, XML_NS_ADDRESSING, WSA_ADDRESS));
		debug("event sink: %s", str);
		if(str && strcmp(str, "")) {
			subsInfo->epr_notifyto = u_strdup(str);
			if(destination_reachable(str) == 0) {
				fault_code = WSMAN_EVENT_DELIVER_TO_UNUSABLE;
				goto DONE;
			}
		}
		else {
			fault_code = WSE_INVALID_MESSAGE;
			goto DONE;
		}
		temp = ws_xml_get_child(node, 0, XML_NS_WS_MAN, WSM_AUTH);
		if(temp) {
			attr = ws_xml_find_node_attr(temp, NULL, WSM_PROFILE);
			if(attr) {
				str = ws_xml_get_attr_value(attr);
				if(!strcasecmp(str, WSMAN_SECURITY_PROFILE_HTTP_BASIC))
					subsInfo->deliveryAuthType = WSMAN_SECURITY_PROFILE_HTTP_BASIC_TYPE;
				else if(!strcasecmp(str, WSMAN_SECURITY_PROFILE_HTTP_DIGEST))
					subsInfo->deliveryAuthType = WSMAN_SECURITY_PROFILE_HTTP_DIGEST_TYPE;
				else if(!strcasecmp(str, WSMAN_SECURITY_PROFILE_HTTPS_BASIC))
					subsInfo->deliveryAuthType = WSMAN_SECURITY_PROFILE_HTTPS_BASIC_TYPE;
				else if(!strcasecmp(str, WSMAN_SECURITY_PROFILE_HTTPS_DIGEST))
					subsInfo->deliveryAuthType = WSMAN_SECURITY_PROFILE_HTTPS_DIGEST_TYPE;
				else if(!strcasecmp(str, WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL))
					subsInfo->deliveryAuthType = WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL_TYPE;
				else if(!strcasecmp(str, WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL_BASIC))
					subsInfo->deliveryAuthType = WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL_BASIC_TYPE;
				else if(!strcasecmp(str, WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL_DIGEST))
					subsInfo->deliveryAuthType = WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL_DIGEST_TYPE;
				else if(!strcasecmp(str, WSMAN_SECURITY_PROFILE_HTTPS_SPNEGO_KERBEROS))
					subsInfo->deliveryAuthType = WSMAN_SECURITY_PROFILE_HTTPS_SPNEGO_KERBEROS_TYPE;
				else if(!strcasecmp(str, WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL_SPNEGO_KERBEROS))
					subsInfo->deliveryAuthType = WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL_SPNEGO_KERBEROS_TYPE;
				else if(!strcasecmp(str, WSMAN_SECURITY_PROFILE_HTTP_SPNEGO_KERBEROS))
					subsInfo->deliveryAuthType = WSMAN_SECURITY_PROFILE_HTTP_SPNEGO_KERBEROS_TYPE;
				else {
					fault_code = WSMAN_INVALID_OPTIONS;
					fault_detail_code = WSMAN_DETAIL_AUTHERIZATION_MODE;
					goto DONE;
				}
				debug("auth profile type = %d", subsInfo->deliveryAuthType);

			}
		}
	}
	if(wsman_parse_credentials(indoc, subsInfo, &fault_code, &fault_detail_code)) {
		goto DONE;
	}
	if(wsman_parse_event_request(indoc, subsInfo, &fault_code, &fault_detail_code)) {
		goto DONE;
	}
	if (msg->auth_data.username != NULL) {
		subsInfo->auth_data.username =
				u_strdup(msg->auth_data.username);
		subsInfo->auth_data.password =
				u_strdup(msg->auth_data.password);
	} else {
		subsInfo->auth_data.username = NULL;
		subsInfo->auth_data.password = NULL;
	}
	temp = ws_xml_get_soap_header(indoc);
	temp = ws_xml_get_child(temp, 0, XML_NS_OPENWSMAN, "FormerUID");
	ntext = ws_xml_get_node_text(temp);
	if(temp && ntext) { //it is a request from the saved request. So we recover the former UUID
		strncpy(subsInfo->subsId, ntext, EUIDLEN);
		debug("Recover to uuid:%s",subsInfo->subsId);
	}
	else
		generate_uuid(subsInfo->subsId, EUIDLEN, 1);
	if(subsInfo->deliveryMode != WS_EVENT_DELIVERY_MODE_PULL)
		create_notification_template(indoc, subsInfo);
DONE:
	if (fault_code != WSMAN_RC_OK) {
		outdoc = wsman_generate_fault(indoc, fault_code, fault_detail_code, NULL);
		destroy_subsinfo(subsInfo);
	} else {
		*sInfo = subsInfo;
	}
	return outdoc;
}


/**
 * Subscribe Stub for processing subscription requests
 * @param op SOAP operation handler
 * @param appData Application data
 * @return status
 */
int
wse_subscribe_stub(SoapOpH op, void *appData, void *opaqueData)
{
	WsXmlDocH       doc = NULL;
	int             retVal = 0;
	WsSubscribeInfo *subsInfo = NULL;
	WsmanStatus     status;
	WsXmlNodeH      inNode, body, header, temp;
	SoapH           soap = soap_get_op_soap(op);

	if (soap == NULL) {
		error("soap_get_op_soap failed");
		return -1;
	}

	WsContextH soapCntx = ws_get_soap_context(soap);
	int i;
	WsDispatchEndPointInfo *ep = (WsDispatchEndPointInfo *) appData;
	WsEndPointSubscribe endPoint =
			(WsEndPointSubscribe)ep->serviceEndPoint;

	WsXmlDocH       _doc = soap_get_op_doc(op, 1);
	WsContextH      epcntx;
	char *buf = NULL;
	char *expiresstr = NULL;
	int len;
	epcntx = ws_create_ep_context(soap, _doc);
	wsman_status_init(&status);
	doc = create_subs_info(op, epcntx, _doc, &subsInfo);
	if (doc != NULL) {
		goto DONE;
	}
	if (endPoint && (retVal = endPoint(epcntx, subsInfo, &status, opaqueData))) {
                debug("Subscribe fault");
		doc = wsman_generate_fault( _doc, status.fault_code, status.fault_detail_code, status.fault_msg);
		destroy_subsinfo(subsInfo);
		goto DONE;
	}
	doc = wsman_create_response_envelope(_doc, NULL);
	if (!doc)
		goto DONE;

	char str[30];
	wsman_expiretime2xmldatetime(subsInfo->expires, str);
	if(soap->subscriptionOpSet) {
		temp = ws_xml_get_child(ws_xml_get_soap_body(_doc), 0, XML_NS_EVENTING, WSEVENT_SUBSCRIBE);
		temp = ws_xml_get_child(temp, 0, XML_NS_EVENTING, WSEVENT_EXPIRES);
		if(temp) {
			expiresstr = strdup(ws_xml_get_node_text(temp));
			ws_xml_set_node_text(temp, str);
		}
		temp = ws_xml_get_soap_header(_doc);
		inNode = ws_xml_get_child(temp, 0, XML_NS_OPENWSMAN, "FormerUID");
		if(inNode == NULL)
			ws_xml_add_child(temp, XML_NS_OPENWSMAN, "FormerUID", subsInfo->subsId);
		ws_xml_dump_memory_enc(_doc, &buf, &len, "UTF-8");
		if(buf) {
			soap->subscriptionOpSet->save_subscritption(soap->uri_subsRepository, subsInfo->subsId, (unsigned char*)buf);
			u_free(buf);
		}
	}
	lnode_t * sinfo = lnode_create(subsInfo);
	pthread_mutex_lock(&soap->lockSubs);
	list_append(soapCntx->subscriptionMemList, sinfo);
	pthread_mutex_unlock(&soap->lockSubs);
	debug("subscription uuid:%s kept in the memory", subsInfo->subsId);
	header = ws_xml_get_soap_header(doc);
	inNode = ws_xml_get_soap_header(_doc);
	inNode = ws_xml_get_child(inNode, 0, XML_NS_ADDRESSING, WSA_REPLY_TO);
	inNode = ws_xml_get_child(inNode, 0, XML_NS_ADDRESSING, WSA_REFERENCE_PROPERTIES);
	if(inNode == NULL)
		inNode = ws_xml_get_child(inNode, 0, XML_NS_ADDRESSING, WSA_REFERENCE_PARAMETERS);
	if(inNode) {
		for (i = 0;
		     (temp =
		      ws_xml_get_child(inNode, i, NULL, NULL)) != NULL;
		     i++) {
			ws_xml_duplicate_tree(header, temp);
		}
	}
	body = ws_xml_get_soap_body(doc);
	inNode = ws_xml_add_child(body, XML_NS_EVENTING, WSEVENT_SUBSCRIBE_RESP, NULL);
	temp = ws_xml_add_child(inNode, XML_NS_EVENTING, WSEVENT_SUBSCRIPTION_MANAGER, NULL);
	if(subsInfo->expires)
		ws_xml_add_child(inNode, XML_NS_EVENTING, WSEVENT_EXPIRES, expiresstr);
	if(subsInfo->deliveryMode == WS_EVENT_DELIVERY_MODE_PULL)
		ws_xml_add_child_format(inNode, XML_NS_ENUMERATION,
		WSENUM_ENUMERATION_CONTEXT, "uuid:%s", subsInfo->subsId);
	inNode = temp;
	if(inNode){
		temp = ws_xml_get_soap_header(_doc);
		temp = ws_xml_get_child(temp, 0, XML_NS_ADDRESSING, WSA_TO);
		ws_xml_add_child(inNode,XML_NS_ADDRESSING,WSA_ADDRESS,ws_xml_get_node_text(temp));
	}
	temp = ws_xml_add_child(inNode, XML_NS_ADDRESSING, WSA_REFERENCE_PARAMETERS, NULL);
	if(temp)
		ws_xml_add_child_format(temp, XML_NS_EVENTING, WSEVENT_IDENTIFIER, "uuid:%s", subsInfo->subsId);
DONE:
	if (doc) {
		soap_set_op_doc(op, doc, 0);
	}
	u_free(expiresstr);
	ws_serializer_free_all(epcntx->serializercntx);
	ws_destroy_context(epcntx);
	u_free(status.fault_msg);
	return retVal;
}




/**
 * Unsubscribe Stub for processing unsubscription requests
 * @param op SOAP operation handler
 * @param appData Application data
 * @return status
 */
int
wse_unsubscribe_stub(SoapOpH op, void *appData, void *opaqueData)
{
	WsXmlDocH       doc = NULL;
	int             retVal = 0;
	WsSubscribeInfo *subsInfo = NULL;
	WsmanStatus     status;
	WsXmlNodeH      inNode;
	WsXmlNodeH      header;
	SoapH           soap = soap_get_op_soap(op);

	if (soap == NULL) {
		error("soap_get_op_soap failed");
		return -1;
	}

	WsContextH soapCntx = ws_get_soap_context(soap);
	WsDispatchEndPointInfo *ep = (WsDispatchEndPointInfo *) appData;
	WsEndPointSubscribe endPoint =
			(WsEndPointSubscribe)ep->serviceEndPoint;

	WsXmlDocH       _doc = soap_get_op_doc(op, 1);
	WsContextH      epcntx;

	epcntx = ws_create_ep_context(soap, _doc);
	wsman_status_init(&status);
	header = ws_xml_get_soap_header(_doc);
	inNode = ws_xml_get_child(header, 0, XML_NS_EVENTING, WSEVENT_IDENTIFIER);
	if(inNode == NULL) {
		status.fault_code = WSE_INVALID_MESSAGE;
		status.fault_detail_code = WSMAN_DETAIL_INVALID_VALUE;
		goto DONE;
	}
	char *uuid = ws_xml_get_node_text(inNode);
	lnode_t *t = NULL;
	pthread_mutex_lock(&soap->lockSubs);
	if(!list_isempty(soapCntx->subscriptionMemList)) {
		t = list_first(soapCntx->subscriptionMemList);
		subsInfo = (WsSubscribeInfo *)t->list_data;
		while(t && strcasecmp(subsInfo->subsId, uuid+5)) {
			t = list_next(soapCntx->subscriptionMemList, t);
			if(t)
				subsInfo = (WsSubscribeInfo *)t->list_data;
		}
	}
	if(t == NULL) {
		status.fault_code = WSMAN_INVALID_PARAMETER;
		status.fault_detail_code = WSMAN_DETAIL_INVALID_VALUE;
		doc = wsman_generate_fault( _doc,
		 	status.fault_code, status.fault_detail_code, NULL);
		pthread_mutex_unlock(&soap->lockSubs);
		goto DONE;
	}
	pthread_mutex_unlock(&soap->lockSubs);
	if (endPoint && (retVal = endPoint(epcntx, subsInfo, &status, opaqueData))) {
               debug("UnSubscribe fault");
		doc = wsman_generate_fault( _doc, status.fault_code, status.fault_detail_code, status.fault_msg);
		goto DONE;
	}
	pthread_mutex_lock(&subsInfo->notificationlock);
	subsInfo->flags |= WSMAN_SUBSCRIBEINFO_UNSUBSCRIBE;
	pthread_mutex_unlock(&subsInfo->notificationlock);
	debug("subscription %s unsubscribed", uuid);
	doc = wsman_create_response_envelope( _doc, NULL);
	if (!doc)
		goto DONE;
DONE:
	if (doc) {
		soap_set_op_doc(op, doc, 0);
	}
	ws_serializer_free_all(epcntx->serializercntx);
	ws_destroy_context(epcntx);
	u_free(status.fault_msg);
	return retVal;
}


/**
 * Renew Stub for processing renew requests
 * @param op SOAP operation handler
 * @param appData Application data
 * @return status
 */
int
wse_renew_stub(SoapOpH op, void *appData, void *opaqueData)
{
	WsXmlDocH       doc = NULL;
	int             retVal = 0;
	WsSubscribeInfo *subsInfo;
	WsmanStatus     status;
	WsXmlNodeH      inNode;
	WsXmlNodeH      body;
	WsXmlNodeH      header;
	SoapH           soap = soap_get_op_soap(op);

	if (soap == NULL) {
		error("soap_get_op_soap failed");
		return -1;
	}

	WsContextH soapCntx = ws_get_soap_context(soap);
	char * expirestr = NULL;

	WsDispatchEndPointInfo *ep = (WsDispatchEndPointInfo *) appData;
	WsEndPointSubscribe endPoint =
			(WsEndPointSubscribe)ep->serviceEndPoint;

	WsXmlDocH       _doc = soap_get_op_doc(op, 1);
	WsContextH      epcntx;
	epcntx = ws_create_ep_context(soap, _doc);
	wsman_status_init(&status);
	body = ws_xml_get_soap_body(_doc);
	header = ws_xml_get_soap_header(_doc);
	inNode = ws_xml_get_child(header, 0, XML_NS_EVENTING, WSEVENT_IDENTIFIER);
	char *uuid = ws_xml_get_node_text(inNode);
	if(uuid == NULL) {
		status.fault_code = WSE_INVALID_MESSAGE;
		status.fault_detail_code = WSMAN_DETAIL_MISSING_VALUES;
		doc = wsman_generate_fault( _doc, status.fault_code, status.fault_detail_code, NULL);
		goto DONE;
	}
	pthread_mutex_lock(&soap->lockSubs);
	lnode_t *t = NULL;
	if(!list_isempty(soapCntx->subscriptionMemList)) {
		t = list_first(soapCntx->subscriptionMemList);
		subsInfo = (WsSubscribeInfo *)t->list_data;
		while(t && strcasecmp(subsInfo->subsId, uuid+5)) {
			t = list_next(soapCntx->subscriptionMemList, t);
			if(t)
				subsInfo = (WsSubscribeInfo *)t->list_data;
		}
	}
	if(t == NULL) {
		status.fault_code = WSE_UNABLE_TO_RENEW;
		doc = wsman_generate_fault( _doc, status.fault_code, status.fault_detail_code, NULL);
		pthread_mutex_unlock(&soap->lockSubs);
		goto DONE;
	}
	pthread_mutex_unlock(&soap->lockSubs);
	inNode = ws_xml_get_child(body, 0, XML_NS_EVENTING, WSEVENT_RENEW);
	inNode = ws_xml_get_child(inNode, 0, XML_NS_EVENTING ,WSEVENT_EXPIRES);
	pthread_mutex_lock(&subsInfo->notificationlock);
	wsman_set_expiretime(inNode, &subsInfo->expires, &status.fault_code);
	expirestr = ws_xml_get_node_text(inNode);
	pthread_mutex_unlock(&subsInfo->notificationlock);
	if (status.fault_code != WSMAN_RC_OK) {
		status.fault_detail_code = WSMAN_DETAIL_EXPIRATION_TIME;
		pthread_mutex_unlock(&subsInfo->notificationlock);
		goto DONE;
	}
	char str[30];
	wsman_expiretime2xmldatetime(subsInfo->expires, str);
	if(soap->subscriptionOpSet) {
		soap->subscriptionOpSet->update_subscription(soap->uri_subsRepository, uuid+5,
			str);
		debug("subscription %s updated!", uuid);
	}

	if (endPoint && (retVal = endPoint(epcntx, subsInfo, &status, opaqueData))) {
                debug("renew fault in plug-in");
		doc = wsman_generate_fault( _doc, status.fault_code, status.fault_detail_code, status.fault_msg);
		pthread_mutex_unlock(&subsInfo->notificationlock);
		goto DONE;
	}
	doc = wsman_create_response_envelope( _doc, NULL);
	if (!doc)
		goto DONE;
	body = ws_xml_get_soap_body(doc);
	body = ws_xml_add_child(body, XML_NS_EVENTING, WSEVENT_RENEW_RESP, NULL);
	ws_xml_add_child(body, XML_NS_EVENTING, WSEVENT_EXPIRES, expirestr);
DONE:
	if (doc) {
		soap_set_op_doc(op, doc, 0);
	}
	ws_serializer_free_all(epcntx->serializercntx);
	ws_destroy_context(epcntx);
	u_free(status.fault_msg);
	return retVal;
}


void
wsman_heartbeat_generator(WsContextH cntx, void *opaqueData)
{
	SoapH soap = cntx->soap;
	WsSubscribeInfo *subsInfo = NULL;
	WsEventThreadContextH threadcntx = NULL;
	WsContextH soapCntx = ws_get_soap_context(soap);
	pthread_t eventsender;
	pthread_attr_t pattrs;
	int r;
	if ((r = pthread_attr_init(&pattrs)) != 0) {
		debug("pthread_attr_init failed = %d", r);
		return;
	}
	if ((r = pthread_attr_setdetachstate(&pattrs,
					     PTHREAD_CREATE_DETACHED)) !=0) {
		debug("pthread_attr_setdetachstate = %d", r);
		return;
	}
	pthread_mutex_lock(&soap->lockSubs);
	lnode_t *node = list_first(soapCntx->subscriptionMemList);
	while(node) {
		subsInfo = (WsSubscribeInfo *)node->list_data;
		pthread_mutex_lock(&subsInfo->notificationlock);
#if 0
		debug("subscription %s : event sent last time = %d, heartbeat= %ld, heartbeatcountdown = %ld, pending events = %d",
			subsInfo->subsId, 	subsInfo->eventSentLastTime, subsInfo->heartbeatInterval, subsInfo->heartbeatCountdown, subsInfo->flags & WSMAN_SUBSCRIPTION_NOTIFICAITON_PENDING);
#endif
		if(subsInfo->flags & WSMAN_SUBSCRIBEINFO_UNSUBSCRIBE) {
			goto LOOP;
		}
		if(time_expired(subsInfo->expires)) {

			goto LOOP;

		}
		if(subsInfo->heartbeatInterval == 0 || subsInfo->deliveryMode == WS_EVENT_DELIVERY_MODE_PULL) {
			goto LOOP;
		}
		subsInfo->heartbeatCountdown -= 1000;
		if(subsInfo->heartbeatCountdown > 0) {
			goto LOOP;
		}
		if(subsInfo->eventSentLastTime) {
			subsInfo->eventSentLastTime = 0;
		}
		else {
			debug("one heartbeat document created for %s", subsInfo->subsId);
			if((subsInfo->flags & WSMAN_SUBSCRIPTION_NOTIFICAITON_PENDING) == 0) {
				threadcntx = ws_create_event_context(soap, subsInfo, NULL);
				if(pthread_create(&eventsender, &pattrs, wse_heartbeat_sender, threadcntx) == 0)
					subsInfo->flags |= WSMAN_SUBSCRIPTION_NOTIFICAITON_PENDING;
			}
		}
		subsInfo->heartbeatCountdown = subsInfo->heartbeatInterval;
LOOP:
		pthread_mutex_unlock(&subsInfo->notificationlock);
		node = list_next(soapCntx->subscriptionMemList, node);
	}
	pthread_mutex_unlock(&soap->lockSubs);
}

static int wse_send_notification(WsEventThreadContextH cntx, WsXmlDocH outdoc, WsSubscribeInfo *subsInfo, unsigned char acked)
{
	int retVal = 0;
	WsManClient *notificationSender = wsmc_create_from_uri(subsInfo->epr_notifyto);
	if(subsInfo->contentEncoding)
		wsmc_set_encoding(notificationSender, subsInfo->contentEncoding);
	if(subsInfo->username)
		wsman_transport_set_userName(notificationSender, subsInfo->username);
	if(subsInfo->password)
		wsman_transport_set_password(notificationSender, subsInfo->password);
	if(subsInfo->deliveryAuthType ==
		WSMAN_SECURITY_PROFILE_HTTP_BASIC_TYPE) {
	}
	else if(subsInfo->deliveryAuthType ==
		WSMAN_SECURITY_PROFILE_HTTP_DIGEST_TYPE) {
	}
	else if(subsInfo->deliveryAuthType ==
		WSMAN_SECURITY_PROFILE_HTTPS_BASIC_TYPE) {
		wsman_transport_set_verify_peer(notificationSender, 0);
	}
	else if(subsInfo->deliveryAuthType ==
		WSMAN_SECURITY_PROFILE_HTTPS_DIGEST_TYPE) {
		wsman_transport_set_verify_peer(notificationSender, 0);
	}
	else if(subsInfo->deliveryAuthType ==
		WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL_TYPE) {
		wsman_transport_set_verify_peer(notificationSender, 1);
		wsman_transport_set_certhumbprint(notificationSender, subsInfo->certificate_thumbprint);
	}
	else if(subsInfo->deliveryAuthType ==
		WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL_BASIC_TYPE) {
		wsman_transport_set_verify_peer(notificationSender, 1);
		wsman_transport_set_certhumbprint(notificationSender, subsInfo->certificate_thumbprint);
	}
	else if(subsInfo->deliveryAuthType ==
		WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL_DIGEST_TYPE) {
		wsman_transport_set_verify_peer(notificationSender, 1);
		wsman_transport_set_certhumbprint(notificationSender, subsInfo->certificate_thumbprint);
	}
	else if(subsInfo->deliveryAuthType ==
		WSMAN_SECURITY_PROFILE_HTTPS_SPNEGO_KERBEROS_TYPE) {
	}
	else if(subsInfo->deliveryAuthType ==
		WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL_SPNEGO_KERBEROS_TYPE) {
	}
	else { //WSMAN_SECURITY_PROFILE_HTTP_SPNEGO_KERBEROS_TYPE
	}
	wsmc_transport_init(notificationSender, NULL);
	if (wsman_send_request(notificationSender, outdoc)) {
                warning("wse_send_notification: wsman_send_request fails for endpoint %s", subsInfo->epr_notifyto);
                /* FIXME: retVal */
        }
	if(acked) {
		retVal = WSE_NOTIFICATION_NOACK;
		WsXmlDocH ackdoc = wsmc_build_envelope_from_response(notificationSender);
		if(ackdoc) {
			WsXmlNodeH node = ws_xml_get_soap_header(ackdoc);
			WsXmlNodeH srcnode = ws_xml_get_soap_header(outdoc);
			WsXmlNodeH temp = NULL;
			srcnode = ws_xml_get_child(srcnode, 0, XML_NS_ADDRESSING, WSA_MESSAGE_ID);
			if(node) {
				temp = ws_xml_get_child(node, 0, XML_NS_ADDRESSING, WSA_RELATES_TO);
				if(temp) {
					if(!strcasecmp(ws_xml_get_node_text(srcnode),
						ws_xml_get_node_text(temp))) {
						node = ws_xml_get_child(node, 0, XML_NS_ADDRESSING, WSA_ACTION);
						if(!strcasecmp(ws_xml_get_node_text(node), WSMAN_ACTION_ACK))
							retVal = 0;
					}

				}
			}
			ws_xml_destroy_doc(ackdoc);
		}
	}
	wsmc_release(notificationSender);
	return retVal;
}


static void * wse_event_sender(void * thrdcntx, unsigned char flag)
{
	char uuidBuf[50];
	WsXmlNodeH header;
	if(thrdcntx == NULL) return NULL;
	WsEventThreadContextH threadcntx = (WsEventThreadContextH)thrdcntx;
	WsSubscribeInfo * subsInfo = threadcntx->subsInfo;
	if(flag == 1)
		debug("wse_notification_sender for %s started", subsInfo->subsId);
	else
		debug("wse_heartbeat_sender for %s started", subsInfo->subsId);
	WsXmlDocH notificationDoc = NULL;
	pthread_mutex_lock(&subsInfo->notificationlock);
	if(flag == 1)
		subsInfo->eventSentLastTime = 1;
	if(!(subsInfo->flags & WSMAN_SUBSCRIBEINFO_UNSUBSCRIBE) &&
		!time_expired(subsInfo->expires)) {
		if(flag) {
			notificationDoc = threadcntx->outdoc;
		}
		else {
	 		notificationDoc = ws_xml_duplicate_doc(subsInfo->heartbeatDoc);
			header = ws_xml_get_soap_header(notificationDoc);
			generate_uuid(uuidBuf, sizeof(uuidBuf), 0);
			ws_xml_add_child(header, XML_NS_ADDRESSING, WSA_MESSAGE_ID,uuidBuf);
		}
		if (subsInfo->deliveryMode == WS_EVENT_DELIVERY_MODE_EVENTS  ||
			subsInfo->deliveryMode == WS_EVENT_DELIVERY_MODE_PUSHWITHACK){
			if(wse_send_notification(threadcntx, notificationDoc, subsInfo, 1) == WSE_NOTIFICATION_NOACK)
				subsInfo->flags |= WSMAN_SUBSCRIPTION_CANCELLED;
		}
		else
			wse_send_notification(threadcntx, notificationDoc, subsInfo, 0);
	}
	ws_xml_destroy_doc(notificationDoc);
	subsInfo->flags &= ~WSMAN_SUBSCRIPTION_NOTIFICAITON_PENDING;
	debug("[ wse_notification_sender thread for %s quit! ]",subsInfo->subsId);
	pthread_mutex_unlock(&subsInfo->notificationlock);
	u_free(thrdcntx);
	return NULL;
}

void * wse_heartbeat_sender(void *thrdcntx)
{
	return wse_event_sender(thrdcntx, 0);
}

void *wse_notification_sender(void *thrdcntx)
{
	return wse_event_sender(thrdcntx, 1);
}

void wse_notification_manager(void * cntx)
{
	int retVal;
	WsSubscribeInfo * subsInfo = NULL;
	WsXmlDocH notificationDoc =NULL;
	WsXmlNodeH header = NULL;
	WsXmlNodeH body = NULL;
	WsXmlNodeH node = NULL;
	WsXmlNodeH eventnode = NULL;
	WsXmlNodeH temp = NULL;
	lnode_t *subsnode = NULL;
	WsEventThreadContextH threadcntx = NULL;
	WsContextH contex = (WsContextH)cntx;
	SoapH soap = contex->soap;
	WsContextH soapCntx = ws_get_soap_context(soap);
	pthread_t eventsender;
	pthread_attr_t pattrs;
	char uuidBuf[50];
	int r;
	if ((r = pthread_attr_init(&pattrs)) != 0) {
		debug("pthread_attr_init failed = %d", r);
		return;
	}
	if ((r = pthread_attr_setdetachstate(&pattrs,
					     PTHREAD_CREATE_DETACHED)) !=0) {
		debug("pthread_attr_setdetachstate = %d", r);
		return;
	}
	pthread_mutex_lock(&soap->lockSubs);
	subsnode = list_first(soapCntx->subscriptionMemList);
	while(subsnode) {
		subsInfo = (WsSubscribeInfo *)subsnode->list_data;
		pthread_mutex_lock(&subsInfo->notificationlock);
		threadcntx = ws_create_event_context(soap, subsInfo, NULL);
		if(((subsInfo->flags & WSMAN_SUBSCRIBEINFO_UNSUBSCRIBE) ||
			subsInfo->flags & WSMAN_SUBSCRIPTION_CANCELLED ||
			time_expired(subsInfo->expires)) &&
			((subsInfo->flags & WSMAN_SUBSCRIPTION_NOTIFICAITON_PENDING ) == 0)) {
			lnode_t *nodetemp = list_delete2(soapCntx->subscriptionMemList, subsnode);
			soap->subscriptionOpSet->delete_subscription(soap->uri_subsRepository, subsInfo->subsId);
			soap->eventpoolOpSet->clear(subsInfo->subsId, delete_notification_info);
			if(!(subsInfo->flags & WSMAN_SUBSCRIBEINFO_UNSUBSCRIBE) && subsInfo->cancel)
				subsInfo->cancel(threadcntx);
			if(subsInfo->flags & WSMAN_SUBSCRIBEINFO_UNSUBSCRIBE)
				debug("Unsubscribed!uuid:%s deleted", subsInfo->subsId);
			else if(subsInfo->flags & WSMAN_SUBSCRIPTION_CANCELLED)
				debug("Cancelled! uuid:%s deleted", subsInfo->subsId);
			else
				debug("Expired! uuid:%s deleted", subsInfo->subsId);
			destroy_subsinfo(subsInfo);
			lnode_destroy(subsnode);
			u_free(threadcntx);
			subsnode = nodetemp;
			continue;
		}
		if(subsInfo->eventpoll) { //poll the events
			retVal = subsInfo->eventpoll(threadcntx);
			if(retVal == WSE_NOTIFICATION_EVENTS_PENDING) {
				goto LOOP;
			}
		}
		if(subsInfo->deliveryMode == WS_EVENT_DELIVERY_MODE_PULL)
			goto LOOP;
		WsNotificationInfoH notificationInfo = NULL;
		if(soap->eventpoolOpSet->remove(subsInfo->subsId, &notificationInfo) ) // to get the event and delete it from the event source
			goto LOOP;
		if(subsInfo->deliveryMode == WS_EVENT_DELIVERY_MODE_PULL) goto LOOP;
		notificationDoc = ws_xml_duplicate_doc(subsInfo->templateDoc);
		header = ws_xml_get_soap_header(notificationDoc);
		body = ws_xml_get_soap_body(notificationDoc);
		if(notificationInfo->headerOpaqueData) {
			temp = ws_xml_get_doc_root(notificationInfo->headerOpaqueData);
			ws_xml_duplicate_tree(header, temp);
		}
		if(subsInfo->deliveryMode == WS_EVENT_DELIVERY_MODE_EVENTS) {
			ws_xml_add_child(header, XML_NS_ADDRESSING, WSA_ACTION, WSEVENT_DELIVERY_MODE_EVENTS);
			generate_uuid(uuidBuf, sizeof(uuidBuf), 0);
			ws_xml_add_child(header, XML_NS_ADDRESSING, WSA_MESSAGE_ID,uuidBuf);
			eventnode = ws_xml_add_child(body, XML_NS_WS_MAN, WSM_EVENTS, NULL);
			while(notificationInfo) {
				temp = ws_xml_add_child(eventnode, XML_NS_WS_MAN, WSM_EVENT, NULL);
				if(notificationInfo->EventAction)  {
					ws_xml_add_node_attr(temp, XML_NS_WS_MAN, WSM_ACTION, notificationInfo->EventAction);
				}
				else {
					ws_xml_add_node_attr(temp, XML_NS_WS_MAN, WSM_ACTION, WSMAN_ACTION_EVENT);
				}
				if(temp) {
					node = ws_xml_get_doc_root(notificationInfo->EventContent);
					ws_xml_duplicate_children(temp, node);
				}
				delete_notification_info(notificationInfo);
				soap->eventpoolOpSet->remove(subsInfo->subsId, &notificationInfo);
			}
		}
		else{
			generate_uuid(uuidBuf, sizeof(uuidBuf), 0);
			ws_xml_add_child(header, XML_NS_ADDRESSING, WSA_MESSAGE_ID,uuidBuf);
			if(notificationInfo->EventAction)
				ws_xml_add_child(header, XML_NS_WS_MAN, WSM_ACTION, notificationInfo->EventAction);
			else
				ws_xml_add_child(header, XML_NS_WS_MAN, WSM_ACTION, WSMAN_ACTION_EVENT);
			node = ws_xml_get_doc_root(notificationInfo->EventContent);
			ws_xml_duplicate_children(body, node);
			delete_notification_info(notificationInfo);
		}
		if(subsInfo->deliveryMode != WS_EVENT_DELIVERY_MODE_PULL) {
			if((subsInfo->flags & WSMAN_SUBSCRIPTION_NOTIFICAITON_PENDING) == 0) {
				WsEventThreadContextH threadcntx2 = ws_create_event_context(soap, subsInfo, notificationDoc);
				if(pthread_create(&eventsender, &pattrs, wse_notification_sender, threadcntx2) == 0) {
					subsInfo->flags |= WSMAN_SUBSCRIPTION_NOTIFICAITON_PENDING;
				}
				else {
					debug("thread created for %s failed![ %s ]", subsInfo->subsId, strerror(errno));
				}
			}
		}

LOOP:
		if(threadcntx)
			u_free(threadcntx);
		pthread_mutex_unlock(&subsInfo->notificationlock);
		subsnode = list_next(soapCntx->subscriptionMemList, subsnode);
	}
	pthread_mutex_unlock(&soap->lockSubs);
}



#endif


WsContextH
ws_get_soap_context(SoapH soap)
{
	return soap->cntx;
}


int
ws_remove_context_val(WsContextH cntx, char *name)
{
	int retVal = 1;
	if (cntx && name) {
		hnode_t        *hn;
		u_lock(cntx->soap);
		hn = hash_lookup(cntx->entries, name);
		if (hn) {
			debug("Found context entry: %s", name);
			hash_delete_free(cntx->entries, hn);
			retVal = 0;
		}
		u_unlock(cntx->soap);
	}
	return retVal;
}

int
ws_set_context_ulong_val(WsContextH cntx,
			 char *name,
			 unsigned long val)
{
	int retVal = set_context_val(cntx, name, &val, sizeof(unsigned long),
			0, WS_CONTEXT_TYPE_ULONG);
	return retVal;
}


int
ws_set_context_xml_doc_val(WsContextH cntx,
			   char *name,
			   WsXmlDocH val)
{
	cntx->indoc = val;
	return 0;
}

WsContextH
ws_create_ep_context(SoapH soap,
		     WsXmlDocH doc)
{
	WsContextH      cntx = ws_create_context(soap);
	if (cntx)
		ws_set_context_xml_doc_val(cntx, WSFW_INDOC, doc);
	return cntx;
}


int
ws_destroy_context(WsContextH cntx)
{
	int             retVal = 1;
	if (cntx && cntx->owner) {
		ws_clear_context_entries(cntx);
		ws_clear_context_enuminfos(cntx);
		ws_serializer_cleanup(cntx->serializercntx);
		if(cntx->subscriptionMemList) {
			list_destroy_nodes(cntx->subscriptionMemList);
			list_destroy(cntx->subscriptionMemList);
		}
		u_free(cntx);
		retVal = 0;
	}
	return retVal;
}


hnode_t*
create_context_entry(hash_t * h,
		     char *name,
		     void *val)
{
	char           *key = u_strdup(name);
	hnode_t        *hn = hnode_create(val);
	if (!key || !hn) {
		u_free(key);
		u_free(hn);
		return NULL;
	}
	hash_insert(h, hn, (void *) key);
	return hn;
}

SoapH
ws_context_get_runtime(WsContextH cntx)
{
	SoapH soap = NULL;
	if (cntx)
		soap = cntx->soap;
	return soap;
}


const void *
get_context_val(WsContextH cntx, const char *name)
{
	const char *val = NULL;
	if (cntx && name) {
		u_lock(cntx->soap);
		if (cntx->entries) {
			hnode_t *hn = hash_lookup(cntx->entries, name);
			if (hn)
				val = hnode_get(hn);
		}
		u_unlock(cntx->soap);
	}
	return val;
}


const void *
ws_get_context_val(WsContextH cntx, const char *name, int *size)
{
	return get_context_val(cntx, name);
}


unsigned long
ws_get_context_ulong_val(WsContextH cntx, char *name)
{
	const void *ptr = get_context_val(cntx, name);
	if (ptr != NULL)
		return *((unsigned long *) ptr);
	return 0;
}


SoapOpH
soap_create_op(SoapH soap,
	       char *inboundAction,
	       char *outboundAction, //optional
	       char *role,
	       SoapServiceCallback callbackProc,
	       void *callbackData,
	       unsigned long flags)
{
	SoapDispatchH  disp = NULL;
	op_t           *entry = NULL;

	if ((disp = wsman_dispatch_create(soap, inboundAction, outboundAction,
			      NULL, //reserved, must be NULL
			      callbackProc, callbackData, flags)) != NULL) {
		entry = create_op_entry(soap, disp, NULL);
	}
	return (SoapOpH) entry;
}



/**
 * Get Operation Document
 * @param op Operation Handle
 * @param inbound Direction flag
 * @return XML Document
 */
WsXmlDocH
soap_get_op_doc(SoapOpH op,
		int inbound)
{
	WsXmlDocH       doc = NULL;
	if (op) {
		op_t           *e = (op_t *) op;
		doc = (!inbound) ? e->out_doc : e->in_doc;
	}
	return doc;
}

WsXmlDocH
soap_detach_op_doc(SoapOpH op,
		   int inbound)
{
	WsXmlDocH       doc = NULL;
	if (op) {
		op_t           *e = (op_t *) op;
		if (!inbound) {
			doc = e->out_doc;
			e->out_doc = NULL;
		} else {
			doc = e->in_doc;
			e->in_doc = NULL;
		}
	}
	return doc;
}

int
soap_set_op_doc(SoapOpH op,
		WsXmlDocH doc,
		int inbound)
{
	int             retVal = 1;
	if (op) {
		op_t           *e = (op_t *) op;
		if (!inbound)
			e->out_doc = doc;
		else
			e->in_doc = doc;
		retVal = 0;
	}
	return retVal;
}


SoapH
soap_get_op_soap(SoapOpH op)
{
	if (op)
		return (SoapH) ((op_t *) op)->dispatch->soap;

	return NULL;
}

void
soap_destroy_op(SoapOpH op)
{
	destroy_op_entry((op_t *) op);
}


op_t *
create_op_entry(SoapH soap,
		SoapDispatchH dispatch,
		WsmanMessage * data)
{
	op_t *entry = (op_t *) u_zalloc(sizeof(op_t));
	if (entry) {
		entry->dispatch = dispatch;
		entry->cntx = ws_create_context(soap);
		entry->data = data;
		// entry->processed_headers = list_create(LISTCOUNT_T_MAX);
	}
	return entry;
}


void
destroy_op_entry(op_t * entry)
{
	SoapH           soap;
	debug("destroy op");
	if (!entry) {
		debug("nothing to destroy...");
		return;
	}
	soap = entry->dispatch->soap;
	if (soap == NULL) {
		goto NULL_SOAP;
	}
	u_lock(soap);
	if (soap->dispatchList && list_contains(soap->dispatchList, &entry->dispatch->node)) {
		list_delete(soap->dispatchList, &entry->dispatch->node);
	}
	u_unlock(soap);

NULL_SOAP:
	destroy_dispatch_entry(entry->dispatch);
	ws_destroy_context(entry->cntx);
#if 0
	list_destroy_nodes(entry->processed_headers);
	list_destroy(entry->processed_headers);
#endif
	u_free(entry);
}

void
destroy_dispatch_entry(SoapDispatchH entry)
{
	int usageCount;
	list_t *dlist;
	if (!entry) {
		return;
	}

	u_lock(entry->soap);
	entry->usageCount--;
	usageCount = entry->usageCount;
	dlist = entry->soap->dispatchList;
	if (!usageCount && dlist != NULL &&
			list_contains(dlist, &entry->node)) {
		lnode_t *n = list_delete(dlist, &entry->node);
		lnode_destroy(n);
	}
	u_unlock(entry->soap);

	if (!usageCount) {
		if (entry->inboundFilterList) {
			list_destroy_nodes(entry->inboundFilterList);
			list_destroy(entry->inboundFilterList);
		}
		if (entry->outboundFilterList) {
			list_destroy_nodes(entry->outboundFilterList);
			list_destroy(entry->outboundFilterList);
		}

		u_free(entry->inboundAction);
		u_free(entry->outboundAction);

		u_free(entry);
	}
}


void
soap_destroy(SoapH soap)
{
	if (soap == NULL )
		return;

	if (soap->dispatcherProc)
		soap->dispatcherProc(soap->cntx, soap->dispatcherData, NULL);

	if (soap->dispatchList) {
		while (!list_isempty(soap->dispatchList)) {
			destroy_dispatch_entry(
				(SoapDispatchH)list_first(soap->dispatchList));
		}
		list_destroy(soap->dispatchList);
	}

	if (soap->processedMsgIdList) {
		while (!list_isempty(soap->processedMsgIdList)) {
			lnode_t *node = list_del_first(soap->processedMsgIdList);
			u_free(node->list_data);
			lnode_destroy(node);
		}
		list_destroy(soap->processedMsgIdList);
	}


	if (soap->inboundFilterList) {
		list_destroy_nodes(soap->inboundFilterList);
		list_destroy(soap->inboundFilterList);
	}

	if (soap->outboundFilterList) {
		list_destroy_nodes(soap->outboundFilterList);
		list_destroy(soap->outboundFilterList);
	}
	ws_xml_parser_destroy();

	ws_destroy_context(soap->cntx);
	u_free(soap);

	return;
}


void
wsman_status_init(WsmanStatus * status)
{
	status->fault_code = 0;
	status->fault_detail_code = 0;
	status->fault_msg = NULL;
}

int
wsman_check_status(WsmanStatus * status)
{
	return status->fault_code;
}
