//this file uses miniupnp

#include "upnp/upnputil.h"

/* protofix() checks if protocol is "UDP" or "TCP" 
 * returns NULL if not */
const char * protofix(const char * proto)
{
	static const char proto_tcp[4] = { 'T', 'C', 'P', 0};
	static const char proto_udp[4] = { 'U', 'D', 'P', 0};
	int i, b;
	for(i=0, b=1; i<4; i++)
		b = b && (   (proto[i] == proto_tcp[i]) 
		          || (proto[i] == (proto_tcp[i] | 32)) );
	if(b)
		return proto_tcp;
	for(i=0, b=1; i<4; i++)
		b = b && (   (proto[i] == proto_udp[i])
		          || (proto[i] == (proto_udp[i] | 32)) );
	if(b)
		return proto_udp;
	return 0;
}

void DisplayInfos(struct UPNPUrls * urls,
                         struct IGDdatas * data)
{
	char externalIPAddress[16];
	char connectionType[64];
	char status[64];
	char lastconnerror[64];
	unsigned int uptime;
	unsigned int brUp, brDown;
	UPNP_GetConnectionTypeInfo(urls->controlURL,
	                           data->servicetype,
							   connectionType);
	if(connectionType[0])
		printf("Connection Type : %s\n", connectionType);
	else
		printf("GetConnectionTypeInfo failed.\n");
	UPNP_GetStatusInfo(urls->controlURL, data->servicetype, status, &uptime, lastconnerror);
	printf("Status : %s, uptime=%u LastConnError %s\n", status, uptime, lastconnerror);
	UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->servicetype_CIF,
			&brDown, &brUp);
	printf("MaxBitRateDown : %u bps   MaxBitRateUp %u bps\n", brDown, brUp);
	UPNP_GetExternalIPAddress(urls->controlURL,
	                          data->servicetype,
							  externalIPAddress);
	if(externalIPAddress[0])
		printf("ExternalIPAddress = %s\n", externalIPAddress);
	else
		printf("GetExternalIPAddress failed.\n");
}

void GetConnectionStatus(struct UPNPUrls * urls,
                               struct IGDdatas * data)
{
	unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
	DisplayInfos(urls, data);
	bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->servicetype_CIF);
	bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->servicetype_CIF);
	packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->servicetype_CIF);
	packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->servicetype_CIF);
	printf("Bytes:   Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
	printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
}

void ListRedirections(struct UPNPUrls * urls,
                             struct IGDdatas * data)
{
	int r;
	int i = 0;
	char index[6];
	char intClient[16];
	char intPort[6];
	char extPort[6];
	char protocol[4];
	char desc[80];
	char enabled[6];
	char rHost[64];
	char duration[16];
	/*unsigned int num=0;
	UPNP_GetPortMappingNumberOfEntries(urls->controlURL, data->servicetype, &num);
	printf("PortMappingNumberOfEntries : %u\n", num);*/
	do {
		snprintf(index, 6, "%d", i);
		rHost[0] = '\0'; enabled[0] = '\0';
		duration[0] = '\0'; desc[0] = '\0';
		extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
		r = UPNP_GetGenericPortMappingEntry(urls->controlURL, data->servicetype,
		                               index,
		                               extPort, intClient, intPort,
									   protocol, desc, enabled,
									   rHost, duration);
		if(r==0)
			printf("%02d - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n"
			       "     desc='%s' rHost='%s'\n",
			       i, protocol, extPort, intClient, intPort,
				   enabled, duration,
				   desc, rHost);
		i++;
	} while(r==0);
}

/* Test function 
 * 1 - get connection type
 * 2 - get extenal ip address
 * 3 - Add port mapping
 * 4 - get this port mapping from the IGD */
int SetRedirectAndTest(struct UPNPUrls * urls,
                               struct IGDdatas * data,
							   const char * iaddr,
							   const char * iport,
							   const char * eport,
                               const char * proto)
{
	char externalIPAddress[16];
	char intClient[16];
	char intPort[6];
//	char leaseDuration[] = "3600"; /* 60 mins */
	int r;
	int ok = 1;

	if(!iaddr || !iport || !eport || !proto)
	{
		fprintf(stderr, "Wrong arguments\n");
		return 0;
	}
	proto = protofix(proto);
	if(!proto)
	{
		fprintf(stderr, "invalid protocol\n");
		return 0;
	}
	
	UPNP_GetExternalIPAddress(urls->controlURL,
	                          data->servicetype,
							  externalIPAddress);
	if(externalIPAddress[0])
		printf("ExternalIPAddress = %s\n", externalIPAddress);
	else
		printf("GetExternalIPAddress failed.\n");

// Unix at the moment!	
#if MINIUPNPC_VERSION >= 13
        /* Starting from miniupnpc version 1.2, lease duration parameter is gone */
	r = UPNP_AddPortMapping(urls->controlURL, data->servicetype,
	                        eport, iport, iaddr, 0, proto, NULL);
#else
   #if MINIUPNPC_VERSION >= 12
        /* Starting from miniupnpc version 1.2, lease duration parameter is gone */
	r = UPNP_AddPortMapping(urls->controlURL, data->servicetype,
	                        eport, iport, iaddr, 0, proto);
   #else
        /* The lease parameter is also gone in minupnpc 1.0 */
	r = UPNP_AddPortMapping(urls->controlURL, data->servicetype,
                                eport, iport, iaddr,0, 0, proto);
   #endif
#endif

//	r = UPNP_AddPortMapping(urls->controlURL, data->servicetype,
//	                        eport, iport, iaddr, 0, leaseDuration, proto);

//	r = UPNP_AddPortMapping(urls->controlURL, data->servicetype,
//	                        eport, iport, iaddr, 0, proto);
	if(r==0)
	{
		printf("AddPortMapping(%s, %s, %s) failed\n", eport, iport, iaddr);
		//this seems to trigger for unknown reasons sometimes.
		//rely on Checking it afterwards...
		//should check IP address then!
		//ok = 0;
	}

	UPNP_GetSpecificPortMappingEntry(urls->controlURL,
	                                 data->servicetype,
    	                             eport, proto,
									 intClient, intPort);
	if(intClient[0])
		printf("InternalIP:Port = %s:%s\n", intClient, intPort);
	else
	{
		printf("GetSpecificPortMappingEntry failed.\n");
		ok = 0;
	}

	if ((strcmp(iaddr, intClient) != 0) || (strcmp(iport, intPort) != 0))
	{
		printf("PortMappingEntry to wrong location! FAILED\n");
		printf("IP1:\"%s\"\n", iaddr);
		printf("IP2:\"%s\"\n", intClient);
		printf("PORT1:\"%s\"\n", iport);
		printf("PORT2:\"%s\"\n", intPort);
		ok = 0;
	}

	printf("external %s:%s is redirected to internal %s:%s\n",
	       externalIPAddress, eport, intClient, intPort);

	if (ok)
	{
		printf("uPnP Forward/Mapping Succeeded\n");
	}
	else
	{
		printf("uPnP Forward/Mapping Failed\n");
	}

	return ok;
}

int TestRedirect(struct UPNPUrls * urls,
                               struct IGDdatas * data,
				const char * iaddr,
				const char * iport,
				const char * eport,
				const char * proto)
{
	char intClient[16];
	char intPort[6];
	int ok = 1;

	if(!iaddr || !iport || !eport || !proto)
	{
		fprintf(stderr, "Wrong arguments\n");
		return 0;
	}
	proto = protofix(proto);
	if(!proto)
	{
		fprintf(stderr, "invalid protocol\n");
		return 0;
	}
	
	UPNP_GetSpecificPortMappingEntry(urls->controlURL,
	                                 data->servicetype,
    	                             eport, proto,
					 intClient, intPort);
	if(intClient[0])
		printf("uPnP Check: InternalIP:Port = %s:%s\n", intClient, intPort);
	else
	{
		printf("GetSpecificPortMappingEntry failed.\n");
		ok = 0;
	}

	printf("uPnP Check: External port %s is redirected to internal %s:%s\n",
	       eport, intClient, intPort);

	if (ok)
	{
		printf("uPnP Check: uPnP Forward/Mapping still Active\n");
	}
	else
	{
		printf("uPnP Check: Forward/Mapping has been Dropped\n");
	}

	return ok;
}



int
RemoveRedirect(struct UPNPUrls * urls,
               struct IGDdatas * data,
			   const char * eport,
			   const char * proto)
{
	if(!proto || !eport)
	{
		fprintf(stderr, "invalid arguments\n");
		return 0;
	}
	proto = protofix(proto);
	if(!proto)
	{
		fprintf(stderr, "protocol invalid\n");
		return 0;
	}
#if MINIUPNPC_VERSION >= 13
	UPNP_DeletePortMapping(urls->controlURL, data->servicetype, eport, proto, NULL);
#else
	UPNP_DeletePortMapping(urls->controlURL, data->servicetype, eport, proto);
#endif

	return 1;
}


/* EOF */
