/*******************************************************************************
 *
 * Copyright (c) 2000-2003 Intel Corporation
 * All rights reserved.
 * Copyright (c) 2012 France Telecom 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 name of Intel Corporation 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 OR
 * 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.
 *
 ******************************************************************************/

/************************************************************************
 * Purpose: This file defines the functions for services. It defines
 * functions for adding and removing services to and from the service table,
 * adding and accessing subscription and other attributes pertaining to the
 * service
 ************************************************************************/

#include "service_table.h"
#include "LinkedList.h"
#include "config.h"
#include "ixml.h"
#include "upnp.h"
#include "upnpdebug.h"
#include "upnpdebug_internal.h"
#include "uri.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>

#ifdef INCLUDE_DEVICE_APIS

	#if EXCLUDE_GENA == 0
/************************************************************************
 *	Function :	copy_subscription
 *
 *	Parameters :
 *		subscription *in ;	Source subscription
 *		subscription *out ;	Destination subscription
 *
 *	Description :	Makes a copy of the subscription
 *
 *	Return : int ;
 *		HTTP_SUCCESS - On success
 *
 *	Note :
 ************************************************************************/
int copy_subscription(subscription *in, subscription *out)
{
	int return_code = HTTP_SUCCESS;

	memcpy(out->sid, in->sid, SID_SIZE);
	out->sid[SID_SIZE] = 0;
	out->ToSendEventKey = in->ToSendEventKey;
	out->expireTime = in->expireTime;
	out->active = in->active;
	return_code = copy_URL_list(&in->DeliveryURLs, &out->DeliveryURLs);
	if (return_code != HTTP_SUCCESS) {
		return return_code;
	}
	ListInit(&out->outgoing, 0, 0);
	out->next = NULL;
	return HTTP_SUCCESS;
}

/************************************************************************
 *	Function :	RemoveSubscriptionSID
 *
 *	Parameters :
 *		Upnp_SID sid ;	subscription ID
 *		service_info * service ;	service object providing the
 *list of subscriptions
 *
 *	Description :	Remove the subscription represented by the
 *		const Upnp_SID sid parameter from the service table and update
 *		the service table.
 *
 *	Return : void ;
 *
 *	Note :
 ************************************************************************/
void RemoveSubscriptionSID(Upnp_SID sid, service_info *service)
{
	subscription *finger = service->subscriptionList;
	subscription *previous = NULL;

	while (finger) {
		if (!strcmp(sid, finger->sid)) {
			if (previous) {
				previous->next = finger->next;
			} else {
				service->subscriptionList = finger->next;
			}
			finger->next = NULL;
			freeSubscriptionList(finger);
			finger = NULL;
			service->TotalSubscriptions--;
		} else {
			previous = finger;
			finger = finger->next;
		}
	}
}

subscription *GetSubscriptionSID(const Upnp_SID sid, service_info *service)
{
	subscription *next = service->subscriptionList;
	subscription *previous = NULL;
	subscription *found = NULL;
	time_t current_time;

	while (next && !found) {
		if (!strcmp(next->sid, sid))
			found = next;
		else {
			previous = next;
			next = next->next;
		}
	}
	if (found) {
		/* get the current_time */
		time(&current_time);
		if (found->expireTime && found->expireTime < current_time) {
			if (previous) {
				previous->next = found->next;
			} else {
				service->subscriptionList = found->next;
			}
			found->next = NULL;
			freeSubscriptionList(found);
			found = NULL;
			service->TotalSubscriptions--;
		}
	}
	return found;
}

subscription *GetNextSubscription(service_info *service, subscription *current)
{
	time_t current_time;
	subscription *next = NULL;
	subscription *previous = NULL;
	int notDone = 1;

	/* get the current_time */
	time(&current_time);
	while (notDone && current) {
		previous = current;
		current = current->next;

		if (!current) {
			notDone = 0;
			next = current;
		} else if (current->expireTime &&
			   current->expireTime < current_time) {
			previous->next = current->next;
			current->next = NULL;
			freeSubscriptionList(current);
			current = previous;
			service->TotalSubscriptions--;
		} else if (current->active) {
			notDone = 0;
			next = current;
		}
	}
	return next;
}

subscription *GetFirstSubscription(service_info *service)
{
	subscription temp;
	subscription *next = NULL;

	temp.next = service->subscriptionList;
	next = GetNextSubscription(service, &temp);
	service->subscriptionList = temp.next;
	/* service->subscriptionList = next; */

	return next;
}

void freeSubscription(subscription *sub)
{
	if (sub) {
		free_URL_list(&sub->DeliveryURLs);
		freeSubscriptionQueuedEvents(sub);
	}
}

/************************************************************************
 *	Function :	freeSubscriptionList
 *
 *	Parameters :
 *		subscription * head ;	head of the subscription list
 *
 *	Description :	Free's memory allocated for all the subscriptions
 *		in the service table.
 *
 *	Return : void ;
 *
 *	Note :
 ************************************************************************/
void freeSubscriptionList(subscription *head)
{
	subscription *next = NULL;

	while (head) {
		next = head->next;
		freeSubscription(head);
		free(head);
		head = next;
	}
}

/*******************************************************************************
 * Function :	FindServiceId
 *
 * Parameters :
 *    service_table *table;  service table
 *    const char *serviceId; string representing the service id to be found
 *                           among those in the
 *    table const char *UDN; string representing the UDN to be found among those
 *                           in the table
 *
 * Description: Traverses through the service table and returns a pointer to the
 *    service node that matches a known service id and a known UDN.
 *
 * Return:
 *     service_info *: pointer to the matching service_info node.
 ******************************************************************************/
service_info *FindServiceId(
	service_table *table, const char *serviceId, const char *UDN)
{
	service_info *finger = NULL;

	if (table) {
		finger = table->serviceList;
		while (finger) {
			if (!strcmp(serviceId, finger->serviceId) &&
				!strcmp(UDN, finger->UDN)) {
				return finger;
			}
			finger = finger->next;
		}
	}

	return NULL;
}

/************************************************************************
 *	Function :	FindServiceEventURLPath
 *
 *	Parameters :
 *		service_table *table ;	service table
 *		char * eventURLPath ;	event URL path used to find a service
 *								from the table
 *
 *	Description :	Traverses the service table and finds the node whose
 *		event URL Path matches a know value
 *
 *	Return : service_info * - pointer to the service list node from the
 *		service table whose event URL matches a known event URL;
 *
 *	Note :
 ************************************************************************/
service_info *FindServiceEventURLPath(
	service_table *table, const char *eventURLPath)
{
	service_info *finger = NULL;
	uri_type parsed_url;
	uri_type parsed_url_in;

	if (!table || !eventURLPath) {
		return NULL;
	}
	if (parse_uri(eventURLPath, strlen(eventURLPath), &parsed_url_in) ==
		HTTP_SUCCESS) {
		finger = table->serviceList;
		while (finger) {
			if (finger->eventURL) {
				if (parse_uri(finger->eventURL,
					    strlen(finger->eventURL),
					    &parsed_url) == HTTP_SUCCESS) {
					if (!token_cmp(&parsed_url.pathquery,
						    &parsed_url_in.pathquery)) {
						return finger;
					}
				}
			}
			finger = finger->next;
		}
	}

	return NULL;
}
	#endif /* EXCLUDE_GENA */

	/***********************************************************************
	 * Function: FindServiceControlURLPath
	 *
	 * Parameters:
	 *    service_table *table; service table
	 *    char *controlURLPath; control URL path used to find a service from
	 *                          the table
	 *
	 * Description: Traverses the service table and finds the node whose
	 *    control URL Path matches a know value
	 *
	 * Return: service_info *:  pointer to the service list node from the
	 *    service table whose control URL Path matches a known value.
	 **********************************************************************/
	#if EXCLUDE_SOAP == 0
service_info *FindServiceControlURLPath(
	service_table *table, const char *controlURLPath)
{
	service_info *finger = NULL;
	uri_type parsed_url;
	uri_type parsed_url_in;

	if (!table || !controlURLPath) {
		return NULL;
	}
	if (parse_uri(controlURLPath, strlen(controlURLPath), &parsed_url_in) ==
		HTTP_SUCCESS) {
		finger = table->serviceList;
		while (finger) {
			if (finger->controlURL) {
				if (parse_uri(finger->controlURL,
					    strlen(finger->controlURL),
					    &parsed_url) == HTTP_SUCCESS) {
					if (!token_cmp(&parsed_url.pathquery,
						    &parsed_url_in.pathquery)) {
						return finger;
					}
				}
			}
			finger = finger->next;
		}
	}

	return NULL;
}
	#endif /* EXCLUDE_SOAP */

	/***********************************************************************
	 * Function: printService
	 *
	 * Parameters:
	 *    service_info *service; Service whose information is to be printed
	 *    Upnp_LogLevel level;  Debug level specified to the print function
	 *    Dbg_Module module;    Debug module specified to the print function
	 *
	 * Description: For debugging purposes prints information from the
	 *    service passed into the function.
	 *
	 * Return: void
	 **********************************************************************/
	#ifdef DEBUG
void printService(service_info *service, Upnp_LogLevel level, Dbg_Module module)
{
	if (service) {
		if (service->serviceType) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"serviceType: %s\n",
				service->serviceType);
		}
		if (service->serviceId) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"serviceId: %s\n",
				service->serviceId);
		}
		if (service->SCPDURL) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"SCPDURL: %s\n",
				service->SCPDURL);
		}
		if (service->controlURL) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"controlURL: %s\n",
				service->controlURL);
		}
		if (service->eventURL) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"eventURL: %s\n",
				service->eventURL);
		}
		if (service->UDN) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"UDN: %s\n\n",
				service->UDN);
		}
		if (service->active) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"Service is active\n");
		} else {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"Service is inactive\n");
		}
	}
}
	#endif

	/************************************************************************
	 * Function: printServiceList
	 *
	 * Parameters:
	 *    service_info *service; Service whose information is to be printed
	 *    Upnp_LogLevel level;   Debug level specified to the print function
	 *    Dbg_Module module;    Debug module specified to the print function
	 *
	 * Description: For debugging purposes prints information of each
	 *    service from the service table passed into the function.
	 *
	 * Return: void
	 ************************************************************************/
	#ifdef DEBUG
void printServiceList(
	service_info *service, Upnp_LogLevel level, Dbg_Module module)
{
	while (service) {
		if (service->serviceType) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"serviceType: %s\n",
				service->serviceType);
		}
		if (service->serviceId) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"serviceId: %s\n",
				service->serviceId);
		}
		if (service->SCPDURL) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"SCPDURL: %s\n",
				service->SCPDURL);
		}
		if (service->controlURL) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"controlURL: %s\n",
				service->controlURL);
		}
		if (service->eventURL) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"eventURL: %s\n",
				service->eventURL);
		}
		if (service->UDN) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"UDN: %s\n\n",
				service->UDN);
		}
		if (service->active) {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"Service is active\n");
		} else {
			UpnpPrintf(level,
				module,
				__FILE__,
				__LINE__,
				"Service is inactive\n");
		}
		service = service->next;
	}
}
	#endif

	/************************************************************************
	 * Function: printServiceTable
	 *
	 * Parameters:
	 *    service_table *table; Service table to be printed
	 *    Upnp_LogLevel level;  Debug level specified to the print function
	 *    Dbg_Module module;    Debug module specified to the print function
	 *
	 * Description: For debugging purposes prints the URL base of the table
	 *    and information of each service from the service table passed into
	 *    the function.
	 *
	 * Return: void
	 ************************************************************************/
	#ifdef DEBUG
void printServiceTable(
	service_table *table, Upnp_LogLevel level, Dbg_Module module)
{
	UpnpPrintf(level,
		module,
		__FILE__,
		__LINE__,
		"URL_BASE: %s\n",
		table->URLBase);
	UpnpPrintf(level, module, __FILE__, __LINE__, "Services: \n");
	printServiceList(table->serviceList, level, module);
}
	#endif

	#if EXCLUDE_GENA == 0
/************************************************************************
 *	Function :	freeService
 *
 *	Parameters :
 *		service_info *in ;	service information that is to be freed
 *
 *	Description :	Free's memory allocated for the various components
 *		of the service entry in the service table.
 *
 *	Return : void ;
 *
 *	Note :
 ************************************************************************/
void freeService(service_info *in)
{
	if (in) {
		if (in->serviceType)
			ixmlFreeDOMString(in->serviceType);

		if (in->serviceId)
			ixmlFreeDOMString(in->serviceId);

		if (in->SCPDURL)
			free(in->SCPDURL);

		if (in->controlURL)
			free(in->controlURL);

		if (in->eventURL)
			free(in->eventURL);

		if (in->UDN)
			ixmlFreeDOMString(in->UDN);

		if (in->subscriptionList)
			freeSubscriptionList(in->subscriptionList);

		in->TotalSubscriptions = 0;
		free(in);
	}
}

/************************************************************************
 *	Function :	freeServiceList
 *
 *	Parameters :
 *		service_info * head ;	Head of the service list to be freed
 *
 *	Description :	Free's memory allocated for the various components
 *		of each service entry in the service table.
 *
 *	Return : void ;
 *
 *	Note :
 ************************************************************************/
void freeServiceList(service_info *head)
{
	service_info *next = NULL;

	while (head) {
		if (head->serviceType)
			ixmlFreeDOMString(head->serviceType);
		if (head->serviceId)
			ixmlFreeDOMString(head->serviceId);
		if (head->SCPDURL)
			free(head->SCPDURL);
		if (head->controlURL)
			free(head->controlURL);
		if (head->eventURL)
			free(head->eventURL);
		if (head->UDN)
			ixmlFreeDOMString(head->UDN);
		if (head->subscriptionList)
			freeSubscriptionList(head->subscriptionList);

		head->TotalSubscriptions = 0;
		next = head->next;
		free(head);
		head = next;
	}
}

/************************************************************************
 *	Function :	freeServiceTable
 *
 *	Parameters :
 *		service_table * table ;	Service table whose memory needs to be
 *								freed
 *
 *	Description : Free's dynamic memory in table.
 *		(does not free table, only memory within the structure)
 *
 *	Return : void ;
 *
 *	Note :
 ************************************************************************/
void freeServiceTable(service_table *table)
{
	ixmlFreeDOMString(table->URLBase);
	freeServiceList(table->serviceList);
	table->serviceList = NULL;
	table->endServiceList = NULL;
}

/*******************************************************************************
 * Function: getElementValue
 *
 * Parameters:
 *    IXML_Node *node; Input node which provides the list of child nodes
 *
 * Description: Returns the clone of the element value
 *
 * Return: DOMString
 *
 * Note: value must be freed with DOMString_free
 ******************************************************************************/
DOMString getElementValue(IXML_Node *node)
{
	IXML_Node *child = (IXML_Node *)ixmlNode_getFirstChild(node);
	const DOMString temp = NULL;

	if (child && ixmlNode_getNodeType(child) == eTEXT_NODE) {
		temp = ixmlNode_getNodeValue(child);

		return ixmlCloneDOMString(temp);
	} else {
		return NULL;
	}
}

/*******************************************************************************
 * Function: getSubElement
 *
 * Parameters:
 *    const char *element_name; sub element name to be searched for
 *    IXML_Node *node;         Input node which provides the list of child nodes
 *    IXML_Node **out;         Ouput node to which the matched child node is
 *                             returned.
 *
 * Description: Traverses through a list of XML nodes to find the node with the
 *              known element name.
 *
 * Return: int
 *    1 - On Success
 *    0 - On Failure
 ******************************************************************************/
int getSubElement(const char *element_name, IXML_Node *node, IXML_Node **out)
{
	const DOMString NodeName = NULL;
	int found = 0;
	IXML_Node *child = (IXML_Node *)ixmlNode_getFirstChild(node);

	(*out) = NULL;
	while (child && !found) {
		switch (ixmlNode_getNodeType(child)) {
		case eELEMENT_NODE:
			NodeName = ixmlNode_getNodeName(child);
			if (!strcmp(NodeName, element_name)) {
				(*out) = child;
				found = 1;
				return found;
			}
			break;
		default:
			break;
		}
		child = (IXML_Node *)ixmlNode_getNextSibling(child);
	}

	return found;
}

/*******************************************************************************
 * Function: getServiceList
 *
 * Parameters:
 *    IXML_Node *node;	  XML node information
 *    service_info **end; service added is returned to the output parameter
 *    char *URLBase;      provides Base URL to resolve relative URL
 *
 * Description: Returns pointer to service info after getting the sub-elements
 *    of the service info.
 *
 * Return: service_info *: pointer to the service info node
 ******************************************************************************/
service_info *getServiceList(IXML_Node *node, service_info **end, char *URLBase)
{
	IXML_Node *serviceList = NULL;
	IXML_Node *current_service = NULL;
	IXML_Node *UDN = NULL;

	IXML_Node *serviceType = NULL;
	IXML_Node *serviceId = NULL;
	IXML_Node *SCPDURL = NULL;
	IXML_Node *controlURL = NULL;
	IXML_Node *eventURL = NULL;
	DOMString tempDOMString = NULL;
	service_info *head = NULL;
	service_info *current = NULL;
	service_info *previous = NULL;
	IXML_NodeList *serviceNodeList = NULL;
	long unsigned int NumOfServices = 0lu;
	long unsigned int i = 0lu;
	int fail = 0;

	if (getSubElement("UDN", node, &UDN) &&
		getSubElement("serviceList", node, &serviceList)) {
		serviceNodeList = ixmlElement_getElementsByTagName(
			(IXML_Element *)serviceList, "service");
		if (serviceNodeList) {
			NumOfServices = ixmlNodeList_length(serviceNodeList);
			for (i = 0lu; i < NumOfServices; i++) {
				current_service =
					ixmlNodeList_item(serviceNodeList, i);
				fail = 0;
				if (current) {
					current->next =
						malloc(sizeof(service_info));
					previous = current;
					current = current->next;
				} else {
					head = malloc(sizeof(service_info));
					current = head;
				}
				if (!current) {
					freeServiceList(head);
					ixmlNodeList_free(serviceNodeList);
					return NULL;
				}
				current->next = NULL;
				current->controlURL = NULL;
				current->eventURL = NULL;
				current->serviceType = NULL;
				current->serviceId = NULL;
				current->SCPDURL = NULL;
				current->active = 1;
				current->subscriptionList = NULL;
				current->TotalSubscriptions = 0;
				if (!(current->UDN = getElementValue(UDN)))
					fail = 1;
				if (!getSubElement("serviceType",
					    current_service,
					    &serviceType) ||
					!(current->serviceType =
							getElementValue(
								serviceType)))
					fail = 1;
				if (!getSubElement("serviceId",
					    current_service,
					    &serviceId) ||
					!(current->serviceId = getElementValue(
						  serviceId)))
					fail = 1;
				if (!getSubElement("SCPDURL",
					    current_service,
					    &SCPDURL) ||
					!(tempDOMString = getElementValue(
						  SCPDURL)) ||
					!(current->SCPDURL = resolve_rel_url(
						  URLBase, tempDOMString)))
					fail = 1;
				ixmlFreeDOMString(tempDOMString);
				tempDOMString = NULL;
				if (!(getSubElement("controlURL",
					    current_service,
					    &controlURL)) ||
					!(tempDOMString = getElementValue(
						  controlURL)) ||
					!(current->controlURL = resolve_rel_url(
						  URLBase, tempDOMString))) {
					UpnpPrintf(UPNP_INFO,
						GENA,
						__FILE__,
						__LINE__,
						"BAD OR MISSING CONTROL URL");
					UpnpPrintf(UPNP_INFO,
						GENA,
						__FILE__,
						__LINE__,
						"CONTROL URL SET TO NULL IN "
						"SERVICE INFO");
					current->controlURL = NULL;
					fail = 0;
				}
				ixmlFreeDOMString(tempDOMString);
				tempDOMString = NULL;
				if (!getSubElement("eventSubURL",
					    current_service,
					    &eventURL) ||
					!(tempDOMString = getElementValue(
						  eventURL)) ||
					!(current->eventURL = resolve_rel_url(
						  URLBase, tempDOMString))) {
					UpnpPrintf(UPNP_INFO,
						GENA,
						__FILE__,
						__LINE__,
						"BAD OR MISSING EVENT URL");
					UpnpPrintf(UPNP_INFO,
						GENA,
						__FILE__,
						__LINE__,
						"EVENT URL SET TO NULL IN "
						"SERVICE INFO");
					current->eventURL = NULL;
					fail = 0;
				}
				ixmlFreeDOMString(tempDOMString);
				tempDOMString = NULL;
				if (fail) {
					freeServiceList(current);
					if (previous)
						previous->next = NULL;
					else
						head = NULL;
					current = previous;
				}
			}
			ixmlNodeList_free(serviceNodeList);
		}
		(*end) = current;
		return head;
	} else {
		(*end) = NULL;
		return NULL;
	}
}

/*******************************************************************************
 * Function: getAllServiceList
 *
 * Parameters:
 *    IXML_Node *node;        XML node information
 *    char *URLBase;          provides Base URL to resolve relative URL
 *    service_info **out_end; service added is returned to the output parameter
 *
 * Description: Returns pointer to service info after getting the sub-elements
 *    of the service info.
 *
 * Return: service_info *
 ******************************************************************************/
service_info *getAllServiceList(
	IXML_Node *node, char *URLBase, service_info **out_end)
{
	service_info *head = NULL;
	service_info *end = NULL;
	service_info *next_end = NULL;
	IXML_NodeList *deviceList = NULL;
	IXML_Node *currentDevice = NULL;

	long unsigned int NumOfDevices = 0lu;
	long unsigned int i = 0lu;

	(*out_end) = NULL;
	deviceList = ixmlElement_getElementsByTagName(
		(IXML_Element *)node, "device");
	if (deviceList) {
		NumOfDevices = ixmlNodeList_length(deviceList);
		for (i = 0lu; i < NumOfDevices; i++) {
			currentDevice = ixmlNodeList_item(deviceList, i);
			if (head) {
				end->next = getServiceList(
					currentDevice, &next_end, URLBase);
				if (next_end)
					end = next_end;
			} else {
				head = getServiceList(
					currentDevice, &end, URLBase);
			}
		}
		ixmlNodeList_free(deviceList);
	}

	(*out_end) = end;
	return head;
}

/*******************************************************************************
 * Function: removeServiceTable
 *
 * Parameters:
 *    IXML_Node *node ;  XML node information
 *    service_table *in; service table from which services will be removed
 *
 * Description: This function assumes that services for a particular root device
 *    are placed linearly in the service table, and in the order in which they
 *    are found in the description document all services for this root device
 *    are removed from the list
 *
 * Return: int
 ******************************************************************************/
int removeServiceTable(IXML_Node *node, service_table *in)
{
	IXML_Node *root = NULL;
	IXML_Node *currentUDN = NULL;
	DOMString UDN = NULL;
	IXML_NodeList *deviceList = NULL;
	service_info *current_service = NULL;
	service_info *start_search = NULL;
	service_info *prev_service = NULL;
	long unsigned int NumOfDevices = 0lu;
	long unsigned int i = 0lu;

	if (getSubElement("root", node, &root)) {
		start_search = in->serviceList;
		deviceList = ixmlElement_getElementsByTagName(
			(IXML_Element *)root, "device");
		if (deviceList) {
			NumOfDevices = ixmlNodeList_length(deviceList);
			for (i = 0lu; i < NumOfDevices; i++) {
				if ((start_search) &&
					((getSubElement(
						 "UDN", node, &currentUDN)) &&
						(UDN = getElementValue(
							 currentUDN)))) {
					current_service = start_search;
					/* Services are put in the service table
					 * in the order in which they appear in
					 * the description document, therefore
					 * we go through the list only once to
					 * remove a particular root device */
					while ((current_service) &&
						(strcmp(current_service->UDN,
							UDN))) {
						current_service =
							current_service->next;
						if (current_service != NULL)
							prev_service =
								current_service
									->next;
					}
					while ((current_service) &&
						(!strcmp(current_service->UDN,
							UDN))) {
						if (prev_service) {
							prev_service->next =
								current_service
									->next;
						} else {
							in->serviceList =
								current_service
									->next;
						}
						if (current_service ==
							in->endServiceList)
							in->endServiceList =
								prev_service;
						start_search =
							current_service->next;
						freeService(current_service);
						current_service = start_search;
					}
					ixmlFreeDOMString(UDN);
					UDN = NULL;
				}
			}

			ixmlNodeList_free(deviceList);
		}
	}
	return 1;
}

/*******************************************************************************
 * Function: addServiceTable
 *
 * Parameters :
 *    IXML_Node *node;            XML node information
 *    service_table *in;          service table that will be initialized with
 *                                services
 *    const char *DefaultURLBase; Default base URL on which the URL will be
 *                                returned to the service list.
 *
 * Description: Add Service to the table.
 *
 * Return: int
 ******************************************************************************/
int addServiceTable(
	IXML_Node *node, service_table *in, const char *DefaultURLBase)
{
	IXML_Node *root = NULL;
	IXML_Node *URLBase = NULL;
	service_info *tempEnd = NULL;

	if (in->URLBase) {
		free(in->URLBase);
		in->URLBase = NULL;
	}
	if (getSubElement("root", node, &root)) {
		if (getSubElement("URLBase", root, &URLBase)) {
			in->URLBase = getElementValue(URLBase);
		} else {
			if (DefaultURLBase) {
				in->URLBase =
					ixmlCloneDOMString(DefaultURLBase);
			} else {
				in->URLBase = ixmlCloneDOMString("");
			}
		}
		if ((in->endServiceList->next = getAllServiceList(
			     root, in->URLBase, &tempEnd))) {
			in->endServiceList = tempEnd;
			return 1;
		}
	}

	return 0;
}

/************************************************************************
 * Function : getServiceTable
 *
 * Parameters :
 * 	IXML_Node *node ;	XML node information
 *	service_table *out ;	output parameter which will contain the
 *				service list and URL
 *	const char *DefaultURLBase ; Default base URL on which the URL
 *				will be returned.
 *
 * Description : Retrieve service from the table
 *
 * Return : int ;
 *
 * Note :
 ************************************************************************/
int getServiceTable(
	IXML_Node *node, service_table *out, const char *DefaultURLBase)
{
	IXML_Node *root = NULL;
	IXML_Node *URLBase = NULL;

	if (getSubElement("root", node, &root)) {
		if (getSubElement("URLBase", root, &URLBase)) {
			out->URLBase = getElementValue(URLBase);
		} else {
			if (DefaultURLBase) {
				out->URLBase =
					ixmlCloneDOMString(DefaultURLBase);
			} else {
				out->URLBase = ixmlCloneDOMString("");
			}
		}
		out->serviceList = getAllServiceList(
			root, out->URLBase, &out->endServiceList);
		if (out->serviceList) {
			return 1;
		}
	}

	return 0;
}
	#endif /* EXCLUDE_GENA */

#endif /* INCLUDE_DEVICE_APIS */
