text: /* $Id: portlistingparse.c,v 1.10 2016/12/16 08:53:21 nanard Exp $ */
  /* MiniUPnP project
   * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
   * (c) 2011-2016 Thomas Bernard
   * This software is subject to the conditions detailed
   * in the LICENCE file provided within the distribution */
  #include <string.h>
  #include <stdlib.h>
  #ifdef DEBUG
  #include <stdio.h>
  #endif /* DEBUG */
  #include "portlistingparse.h"
  #include "minixml.h"
  
  /* list of the elements */
  static const struct {
  	const portMappingElt code;
  	const char * const str;
  } elements[] = {
  	{ PortMappingEntry, "PortMappingEntry"},
  	{ NewRemoteHost, "NewRemoteHost"},
  	{ NewExternalPort, "NewExternalPort"},
  	{ NewProtocol, "NewProtocol"},
  	{ NewInternalPort, "NewInternalPort"},
  	{ NewInternalClient, "NewInternalClient"},
  	{ NewEnabled, "NewEnabled"},
  	{ NewDescription, "NewDescription"},
  	{ NewLeaseTime, "NewLeaseTime"},
  	{ PortMappingEltNone, NULL}
  };
  
  /* Helper function */
  static UNSIGNED_INTEGER
  atoui(const char * p, int l)
  {
  	UNSIGNED_INTEGER r = 0;
  	while(l > 0 && *p)
  	{
  		if(*p >= '0' && *p <= '9')
  			r = r*10 + (*p - '0');
  		else
  			break;
  		p++;
  		l--;
  	}
  	return r;
  }
  
  /* Start element handler */
  static void
  startelt(void * d, const char * name, int l)
  {
  	int i;
  	struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
  	pdata->curelt = PortMappingEltNone;
  	for(i = 0; elements[i].str; i++)
  	{
  		if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0)
  		{
  			pdata->curelt = elements[i].code;
  			break;
  		}
  	}
  	if(pdata->curelt == PortMappingEntry)
  	{
  		struct PortMapping * pm;
  		pm = calloc(1, sizeof(struct PortMapping));
  		if(pm == NULL)
  		{
  			/* malloc error */
  #ifdef DEBUG
  			fprintf(stderr, "%s: error allocating memory",
  			        "startelt");
  #endif /* DEBUG */
  			return;
  		}
  		pm->l_next = pdata->l_head;	/* insert in list */
  		pdata->l_head = pm;
  	}
  }
  
  /* End element handler */
  static void
  endelt(void * d, const char * name, int l)
  {
  	struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
  	(void)name;
  	(void)l;
  	pdata->curelt = PortMappingEltNone;
  }
  
  /* Data handler */
  static void
  data(void * d, const char * data, int l)
  {
  	struct PortMapping * pm;
  	struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
  	pm = pdata->l_head;
  	if(!pm)
  		return;
  	if(l > 63)
  		l = 63;
  	switch(pdata->curelt)
  	{
  	case NewRemoteHost:
  		memcpy(pm->remoteHost, data, l);
  		pm->remoteHost[l] = '\0';
  		break;
  	case NewExternalPort:
  		pm->externalPort = (unsigned short)atoui(data, l);
  		break;
  	case NewProtocol:
  		if(l > 3)
  			l = 3;
  		memcpy(pm->protocol, data, l);
  		pm->protocol[l] = '\0';
  		break;
  	case NewInternalPort:
  		pm->internalPort = (unsigned short)atoui(data, l);
  		break;
  	case NewInternalClient:
  		memcpy(pm->internalClient, data, l);
  		pm->internalClient[l] = '\0';
  		break;
  	case NewEnabled:
  		pm->enabled = (unsigned char)atoui(data, l);
  		break;
  	case NewDescription:
  		memcpy(pm->description, data, l);
  		pm->description[l] = '\0';
  		break;
  	case NewLeaseTime:
  		pm->leaseTime = atoui(data, l);
  		break;
  	default:
  		break;
  	}
  }
  
  
  /* Parse the PortMappingList XML document for IGD version 2
   */
  void
  ParsePortListing(const char * buffer, int bufsize,
                   struct PortMappingParserData * pdata)
  {
  	struct xmlparser parser;
  
  	memset(pdata, 0, sizeof(struct PortMappingParserData));
  	/* init xmlparser */
  	parser.xmlstart = buffer;
  	parser.xmlsize = bufsize;
  	parser.data = pdata;
  	parser.starteltfunc = startelt;
  	parser.endeltfunc = endelt;
  	parser.datafunc = data;
  	parser.attfunc = 0;
  	parsexml(&parser);
  }
  
  void
  FreePortListing(struct PortMappingParserData * pdata)
  {
  	struct PortMapping * pm;
  	while((pm = pdata->l_head) != NULL)
  	{
  		/* remove from list */
  		pdata->l_head = pm->l_next;
  		free(pm);
  	}
  }
  
  