//
// Copyright (c) ANSCENTER. All rights reserved.
//

#include "precomp.h"

#include "anslicensing.h"
#include "template.h"
#include "base64.h"
#include "except.h"
#include "uniconv.h"
#include "tinyxml2.h"
#include "picojson.h"

using namespace std;

const unsigned char LicenseTemplateImpl::m_demoPublicKeys[10][23] = {
	{8, 0x67, 0xC9, 0x0C, 0xB9, 0xF9, 0x7D, 0x1C, 0x9C},
	{11, 0x76, 0x8C, 0x73, 0xC8, 0xC3, 0x6E, 0xEE, 0x7E, 0x3A, 0xC2, 0x13},
	{12, 0xC2, 0x8F, 0x03, 0x24, 0x4B, 0xAF, 0xC7, 0x94, 0x20, 0x3D, 0x46, 0x03},
	{13, 0xC8, 0xC3, 0x75, 0x6A, 0x9E, 0x74, 0x5F, 0xC9, 0x18, 0xCF, 0xA0, 0xD4, 0xA9},
	{14, 0x75, 0xD5, 0x7F, 0xDC, 0x08, 0x48, 0x93, 0x55, 0xDF, 0x63, 0xF9, 0xC4, 0x2C, 0x48},
	{14, 0xE1, 0x5A, 0xA2, 0xE0, 0xB7, 0x33, 0xFF, 0x88, 0x20, 0xFF, 0x1F, 0x91, 0xC9, 0x77},
	{17, 0xCC, 0x50, 0x01, 0x33, 0x11, 0x20, 0xFF, 0xA8, 0xED, 0x4F, 0xF2, 0x0A, 0xD7, 0xCB, 0xD0, 0xC3, 0x14},
	{18, 0x5A, 0xCC, 0xFB, 0xF5, 0x00, 0x62, 0xB5, 0x6B, 0xEE, 0x28, 0xE3, 0x92, 0x56, 0x2C, 0xAD, 0x76, 0xB8, 0xAE},
	{20, 0x4B, 0x36, 0x14, 0x2C, 0x29, 0xF0, 0x0C, 0xCD, 0x2F, 0xC2, 0xE0, 0x24, 0x4A, 0x6E, 0xCA, 0x83, 0x7B, 0xA7, 0xB1, 0x05},
	{22, 0x6B, 0x3C, 0x1D, 0xD7, 0x6F, 0xAC, 0x59, 0x37, 0xAA, 0xCC, 0x52, 0xF9, 0xBB, 0x7B, 0x25, 0x34, 0x93, 0x40, 0x7D, 0xE0, 0x90, 0x07}
};

const unsigned char LicenseTemplateImpl::m_demoPrivateKeys[10][23] = {
	{7, 0x5B, 0x3C, 0x97, 0xDC, 0x58, 0x4C, 0x37},
	{9, 0xC3, 0xD3, 0xB3, 0xE7, 0x13, 0x0E, 0xC2, 0x46, 0x2F},
	{10, 0x25, 0x72, 0x41, 0x0A, 0x22, 0x8E, 0x88, 0x25, 0x1C, 0x01},
	{12, 0x23, 0x5F, 0xA9, 0x40, 0x61, 0x5A, 0xB6, 0xC9, 0x07, 0x86, 0x1E, 0x01},
	{12, 0x24, 0x2F, 0x29, 0xA7, 0xE1, 0xAF, 0xC1, 0x42, 0x41, 0x82, 0xAF, 0x67},
	{13, 0xA8, 0x89, 0x6A, 0x64, 0x85, 0x9D, 0x00, 0xBC, 0x06, 0x92, 0xF0, 0xF9, 0x08},
	{15, 0x00, 0x8F, 0x7E, 0x9F, 0x61, 0x38, 0x8B, 0xB3, 0x9D, 0x2D, 0x68, 0x17, 0x7E, 0x7D, 0x19},
	{17, 0xBE, 0x53, 0x27, 0xF2, 0xB4, 0xD6, 0xA0, 0xA8, 0xF9, 0x10, 0x67, 0x94, 0x61, 0xD9, 0x8B, 0x4B, 0x03},
	{18, 0x0E, 0x15, 0xCB, 0xFC, 0x8D, 0x6C, 0x93, 0xA3, 0x1F, 0x70, 0xB8, 0xFB, 0x25, 0xB4, 0xB8, 0xE1, 0xED, 0x04},
	{20, 0x66, 0x8C, 0xE8, 0x03, 0x44, 0x4E, 0x5C, 0x81, 0x15, 0xBF, 0x01, 0x07, 0x6A, 0xA3, 0x59, 0xF6, 0xE0, 0x97, 0x76, 0x45}
};

#define TEMPLATE  "LicenseKeyTemplate"
#define FORMAT    "LicenseKey"
#define DATA      "Data"
#define FIELD     "Field"
#define SIGNATURE "Signature"
#define SIGNATURE_PUBLIC_KEY  "SignaturePublicKey"
#define SIGNATURE_PRIVATE_KEY "SignaturePrivateKey"
#define SIGNING_SERVICE_URL   "SigningServiceUrl"
#define SIGNING_SERVICE_TEMPLATE_ID "SigningServiceTemplateId"
#define DATA_FIELDS           "DataFields"
#define VALIDATION_FIELDS     "ValidationFields"

void LicenseTemplateImpl::LoadXml(const char * xmlTemplate)
{
	tinyxml2::XMLDocument xml;

	if (xml.Parse(xmlTemplate) != tinyxml2::XML_NO_ERROR)
		throw new LicensingException(STATUS_OUT_OF_MEMORY);

	std::string rootElementName;
	const char * attr;
	tinyxml2::XMLElement * element, * rootElement;
	
	if ((element = rootElement = xml.FirstChildElement()) == NULL) throw new LicensingException(STATUS_OUT_OF_MEMORY);
	
	rootElementName = rootElement->Name();

	int templateVersion;

	if ((attr = element->Attribute("version")) == NULL)
		templateVersion = 1;
	else
		templateVersion = atoi(attr);

	if (templateVersion < 1 && templateVersion > 3)
		throw new LicensingException(STATUS_GENERIC_ERROR, "unsupported template version");

	if ((element = element->FirstChildElement("LicenseKey")) == NULL) throw new LicensingException(STATUS_OUT_OF_MEMORY);

	m_keyEncoding = ENCODING_BASE32X;
	if ((attr = element->Attribute("encoding")) != NULL)
		if (_stricmp(attr, "base64") == 0)
			m_keyEncoding = ENCODING_BASE64X;

	if ((attr = element->Attribute("characterGroups")) == NULL)
		throw new LicensingException(STATUS_OUT_OF_MEMORY);

	m_numGroups = atoi(attr);

	if ((attr = element->Attribute("charactersPerGroup")) == NULL)
		throw new LicensingException(STATUS_OUT_OF_MEMORY);

	m_charsPerGroup = atoi(attr);

	m_groupSeparator = "-";

	if ((attr = element->Attribute("groupSeparator")) != NULL)
		m_groupSeparator = attr;

	m_header = "";

	if ((attr = element->Attribute("header")) != NULL)
		m_header = attr;

	m_footer = "";

	if ((attr = element->Attribute("footer")) != NULL)
		m_footer = attr;
	
	int type, offset, size;

	if ((element = xml.FirstChildElement()) == NULL) throw new LicensingException(STATUS_GENERIC_ERROR);
	if ((element = element->FirstChildElement("LicenseKey")) == NULL) throw new LicensingException(STATUS_GENERIC_ERROR);
	if ((element = element->FirstChildElement("Data")) == NULL) throw new LicensingException(STATUS_GENERIC_ERROR);
	if ((element = element->FirstChildElement("DataFields")) == NULL) throw new LicensingException(STATUS_GENERIC_ERROR);

	for (element = element->FirstChildElement("Field"); element != NULL; element = element->NextSiblingElement())
	{
		attr = element->Attribute("type");

		if ( _stricmp( attr, "raw" ) == 0 )
			type = FIELD_TYPE_RAW;
		else
		if ( _stricmp( attr, "integer" ) == 0 )
			type = FIELD_TYPE_INTEGER;
		else
		if ( _stricmp( attr, "string" ) == 0 )
			type = FIELD_TYPE_STRING;
		else
		if ( _stricmp( attr, "date13") == 0 )
			type = FIELD_TYPE_DATE13;
		else
		if ( _stricmp( attr, "date14" ) == 0 )
			type = FIELD_TYPE_DATE14;
		else
		if ( _stricmp( attr, "date16" ) == 0 )
			type = FIELD_TYPE_DATE16;
		else
			throw new LicensingException(STATUS_GENERIC_ERROR);

		offset = 0;

		if ((attr = element->Attribute("offset")) != NULL )
		{
			offset = atoi(attr);
		}

		if ((attr = element->Attribute("size")) == NULL )
			throw new LicensingException(STATUS_GENERIC_ERROR);

		size = atoi(attr);

		if ((attr = element->Attribute("name")) == NULL)
			throw new LicensingException(STATUS_GENERIC_ERROR);

		m_dataFields.emplace(piecewise_construct, forward_as_tuple(attr), forward_as_tuple(type, size, offset));
	}

	if ((element = xml.FirstChildElement()) == NULL) throw new LicensingException(STATUS_GENERIC_ERROR);
	if ((element = element->FirstChildElement("LicenseKey")) == NULL) throw new LicensingException(STATUS_GENERIC_ERROR);
	if ((element = element->FirstChildElement("Data")) == NULL) throw new LicensingException(STATUS_GENERIC_ERROR);
	if ((element = element->FirstChildElement("ValidationFields")) == NULL) throw new LicensingException(STATUS_GENERIC_ERROR);

	m_validationDataSize = 0;

	for (element = element->FirstChildElement("Field"); element != NULL; element = element->NextSiblingElement())
	{
		attr = element->Attribute("type");

		if ( _stricmp( attr, "raw" ) == 0 )
			type = FIELD_TYPE_RAW;
		else
		if ( _stricmp( attr, "integer" ) == 0 )
			type = FIELD_TYPE_INTEGER;
		else
		if ( _stricmp( attr, "string" ) == 0 )
			type = FIELD_TYPE_STRING;
		else
		if ( _stricmp ( attr, "date13" ) == 0 )
			type = FIELD_TYPE_DATE13;
		else
		if ( _stricmp( attr, "date14" ) == 0 )
			type = FIELD_TYPE_DATE14;
		else
		if ( _stricmp( attr, "date16" ) == 0 )
			type = FIELD_TYPE_DATE16;
		else
			throw new LicensingException(STATUS_GENERIC_ERROR);

		offset = 0;

		if ((attr = element->Attribute("offset")) != NULL )
		{
			offset = atoi(attr);
		}

		if ((attr = element->Attribute("size")) == NULL )
			throw new LicensingException(STATUS_GENERIC_ERROR);

		size = atoi(attr);

		if ((attr = element->Attribute("name")) == NULL)
			throw new LicensingException(STATUS_GENERIC_ERROR);

		m_validationFields.emplace(piecewise_construct, forward_as_tuple(attr), forward_as_tuple(type, size, offset));

		if (m_validationDataSize < offset + size)
				m_validationDataSize = offset + size;
	}

	if ((element = xml.FirstChildElement()) == NULL) throw new LicensingException(STATUS_GENERIC_ERROR);
	if ((element = element->FirstChildElement("LicenseKey")) == NULL) throw new LicensingException(STATUS_GENERIC_ERROR);
	if ((element = element->FirstChildElement("Signature")) == NULL) throw new LicensingException(STATUS_GENERIC_ERROR);

	if ((attr = element->Attribute("size")) == NULL)
		throw new LicensingException(STATUS_GENERIC_ERROR, "signature size not provided in template");

	int signatureSize = atoi(attr);
	if (signatureSize <= 0)
		throw new LicensingException(STATUS_GENERIC_ERROR, "invalid signature size");

	SetSignatureSize(signatureSize);

	SetDataSize((m_numGroups * m_charsPerGroup * m_keyEncoding) - signatureSize);

	tinyxml2::XMLElement * element2;
	
	if ((element2 = element->FirstChildElement("SignaturePrivateKey")) != NULL)
	{
		if (templateVersion == 1)
		{
			m_signingKey = make_unique<Key>(element2->GetText(), m_signatureKeyType);
			
			int len = 1024;
			void * buf = _alloca(1024);

			if (!m_signingKey->Store(buf, &len))
				throw new LicensingException(STATUS_GENERIC_ERROR, "invalid private key");

			ValidatePrivateKey(buf, len);
		} else 
		if (templateVersion <= 3)
			SetPrivateKey(element2->GetText());
		else
			throw new LicensingException(STATUS_GENERIC_ERROR, "unsupported template version");
	}

	if ((element2 = element->FirstChildElement("SignaturePublicKey")) != NULL)
	{
		if (templateVersion == 1)
		{
			m_verificationKey = make_unique<Key>(element2->GetText(), m_signatureKeyType);
			m_certificate.reset(Certificate::Generate(signatureSize, m_signingKey.get(), m_verificationKey.get(), NULL, NULL, NULL, 0));
			ValidateCertificate();
		}
		else
		if (templateVersion <= 3)
			SetPublicKeyCertificate(element2->GetText());
		else
			throw new LicensingException(STATUS_GENERIC_ERROR, "unsupported template version");
	}

	if ((element2 = element->FirstChildElement("SigningServiceUrl")) != NULL)
		SetLicensingServiceUrl(element2->GetText());

	if ((element2 = element->FirstChildElement("SigningServiceTemplateId")) != NULL)
		SetTemplateId(element2->GetText());

	if (templateVersion < 3)
		return;

	if ((element = rootElement->FirstChildElement("Properties")) != NULL)
	{
		m_properties.LoadXml(element);
	}
}

#ifndef MAX_XML_BUFFER_SIZE
#define MAX_XML_BUFFER_SIZE 4096
#endif

const char * LicenseTemplateImpl::SaveXml(bool savePrivateKey)
{
	
	BASE64 base64;
	char buffer[ MAX_XML_BUFFER_SIZE ];

	m_xmlTemplate.resize(0);

	snprintf(buffer, MAX_XML_BUFFER_SIZE, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");m_xmlTemplate.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "<LicenseTemplate version=\"%d\">\n", m_version);m_xmlTemplate.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t<LicenseKey\n"
							"\t\tencoding=\"%s\"\n"
							"\t\tcharacterGroups=\"%d\"\n"
							"\t\tcharactersPerGroup=\"%d\"\n"
							"\t\tgroupSeparator=\"%s\"\n"
							"\t\theader=\"%s\"\n"
							"\t\tfooter=\"%s\">\n", (m_keyEncoding == ENCODING_BASE64X) ? "BASE64" : "BASE32X", m_numGroups, m_charsPerGroup, m_groupSeparator.c_str(), m_header.c_str(), m_footer.c_str());m_xmlTemplate.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t<Data>\n");m_xmlTemplate.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t\t<DataFields size=\"%d\">\n", m_dataSize);m_xmlTemplate.append(buffer);

	const char * name;
	BitStruct::FIELD_TYPE type;
	int size, offset;
	string fieldType;

	for (const auto& field : m_dataFields)
	{
		name = field.first.c_str();
		type = field.second.type;
		size = field.second.size;
		offset = field.second.offset;

		switch ((int)type)
		{
			case FIELD_TYPE_INTEGER:
				fieldType = "Integer";
				break;

			case FIELD_TYPE_STRING:
				fieldType = "String";
				break;

			case FIELD_TYPE_RAW:
				fieldType = "Raw";
				break;

			case FIELD_TYPE_DATE13:
				fieldType = "Date13";
				break;

			case FIELD_TYPE_DATE14:
				fieldType = "Date14";
				break;

			case FIELD_TYPE_DATE16:
				fieldType = "Date16";
				break;

			default:
				fieldType = "Raw";
		}

		snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t\t\t<Field name=\"%s\" type=\"%s\" size=\"%d\" offset=\"%d\" />\n", name, fieldType.c_str(), size, offset); 
		m_xmlTemplate.append(buffer);
	}

	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t\t</DataFields>\n");m_xmlTemplate.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t\t<ValidationFields>\n");m_xmlTemplate.append(buffer);

	for (const auto& field : m_validationFields)
	{
		name = field.first.c_str();
		type = field.second.type;
		size = field.second.size;
		offset = field.second.offset;

		switch ((int)type)
		{
			case FIELD_TYPE_INTEGER:
				fieldType = "Integer";
				break;

			case FIELD_TYPE_STRING:
				fieldType = "String";
				break;

			case FIELD_TYPE_RAW:
				fieldType = "Raw";
				break;

			case FIELD_TYPE_DATE13:
				fieldType = "Date13";
				break;

			case FIELD_TYPE_DATE14:
				fieldType = "Date14";
				break;

			case FIELD_TYPE_DATE16:
				fieldType = "Date16";
				break;

			default:
				fieldType = "Raw";
		}

		snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t\t\t<Field name=\"%s\" type=\"%s\" size=\"%d\" offset=\"%d\" />\n", name, fieldType.c_str(), size, offset); 
		m_xmlTemplate.append(buffer);
	}

	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t\t</ValidationFields>\n");m_xmlTemplate.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t</Data>\n");m_xmlTemplate.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t<Signature size=\"%d\">\n", m_signatureSize);m_xmlTemplate.append(buffer);

	if (m_certificate)
	{
		snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t\t<SignaturePublicKey>%s</SignaturePublicKey>\n", m_certificate->ToString() );m_xmlTemplate.append(buffer);

		if (savePrivateKey && m_signingKey)
		{
			snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t\t<SignaturePrivateKey>%s</SignaturePrivateKey>\n", m_signingKey->ToString() );m_xmlTemplate.append(buffer);
		}
	}

	if (m_licensingServiceUrl.length() > 0)
	{
		snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t\t<SigningServiceUrl>%s</SigningServiceUrl>\n", m_licensingServiceUrl.c_str() );m_xmlTemplate.append(buffer);
	}

	if (!m_templateId.empty())
	{
		snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t\t<SigningServiceTemplateId>%s</SigningServiceTemplateId>\n", m_templateId.c_str() );m_xmlTemplate.append(buffer);
	}

	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t\t</Signature>\n");m_xmlTemplate.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t</LicenseKey>\n");m_xmlTemplate.append(buffer);
	
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t<Properties>\n");m_xmlTemplate.append(buffer);
		m_properties.SaveXml(m_xmlTemplate);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t</Properties>\n");m_xmlTemplate.append(buffer);
	
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "</LicenseTemplate>");m_xmlTemplate.append(buffer);

	return m_xmlTemplate.c_str();
}

void LicenseTemplateImpl::LoadJson(const char * xmlTemplate)
{
	
	picojson::value json;

	try {
		std::string err = picojson::parse(json, xmlTemplate);

		if (!err.empty())
			throw new LicensingException(STATUS_GENERIC_ERROR, err.c_str());

		if (!json.is<picojson::object>())
			throw new LicensingException(STATUS_GENERIC_ERROR, "invalid license template");
		
		picojson::object obj = json.get<picojson::object>();

		int templateVersion = 1;
		
		try { templateVersion = obj["version"].get<double>(); } catch (std::out_of_range &) {}

		if (templateVersion < 1 && templateVersion > 3)
			throw new LicensingException(STATUS_GENERIC_ERROR, "unsupported template version");

		m_keyEncoding = ENCODING_BASE32X;
		
		try {
			if (obj["encoding"].is<std::string>() && obj["encoding"].get<std::string>().compare("base64") == 0) 
				m_keyEncoding = ENCODING_BASE64X;
		} catch (std::out_of_range &) {}

		m_numGroups = obj["char_groups"].get<double>();

		m_charsPerGroup = obj["chars_per_group"].get<double>();

		m_groupSeparator = obj["group_sep"].get<std::string>().c_str();

		m_header = obj["header"].get<std::string>().c_str();

		m_footer = obj["footer"].get<std::string>().c_str();

		std::string name; int type, size, offset;

		picojson::object field;
		picojson::array fieldList = obj["data_fields"].get<picojson::array>();

		for (picojson::array::iterator iter = fieldList.begin(); iter != fieldList.end(); iter++) {

			field = iter->get<picojson::object>();

			name = field["type"].get<std::string>();

			if ((name.compare("raw") == 0) || (name.compare("Raw") == 0))
				type = FIELD_TYPE_RAW;
			else
				if ((name.compare("int") == 0) || (name.compare("integer") == 0) || (name.compare("Integer") == 0))
					type = FIELD_TYPE_INTEGER;
				else
					if ((name.compare("string") == 0) || (name.compare("String") == 0))
						type = FIELD_TYPE_STRING;
					else
						if ((name.compare("date13") == 0) || (name.compare("Date13") == 0))
							type = FIELD_TYPE_DATE13;
						else
							if ((name.compare("date14") == 0) || (name.compare("Date14") == 0))
								type = FIELD_TYPE_DATE14;
							else
								if ((name.compare("date16") == 0) || (name.compare("Date16") == 0))
									type = FIELD_TYPE_DATE16;
								else
									throw new LicensingException(STATUS_GENERIC_ERROR);

			name = field["name"].get<std::string>();
			size = field["size"].get<double>();
			offset = 0;

			try { offset = field["offset"].get<double>(); } catch (std::out_of_range &) {}

			m_dataFields.emplace(piecewise_construct, forward_as_tuple(name.c_str()), forward_as_tuple(type, size, offset));
		}

		m_validationDataSize = 0;

		if (obj["validation_fields"].is<picojson::array>()) {

			fieldList = obj["validation_fields"].get<picojson::array>();

			for (picojson::array::iterator iter = fieldList.begin(); iter != fieldList.end(); iter++) {

				field = iter->get<picojson::object>();

				name = field["type"].get<std::string>();

				if ((name.compare("raw") == 0) || (name.compare("Raw") == 0))
					type = FIELD_TYPE_RAW;
				else
					if ((name.compare("int") == 0) || (name.compare("integer") == 0) || (name.compare("Integer") == 0))
						type = FIELD_TYPE_INTEGER;
					else
						if ((name.compare("string") == 0) || (name.compare("String") == 0))
							type = FIELD_TYPE_STRING;
						else
							if ((name.compare("date13") == 0) || (name.compare("Date13") == 0))
								type = FIELD_TYPE_DATE13;
							else
								if ((name.compare("date14") == 0) || (name.compare("Date14") == 0))
									type = FIELD_TYPE_DATE14;
								else
									if ((name.compare("date16") == 0) || (name.compare("Date16") == 0))
										type = FIELD_TYPE_DATE16;
									else
										throw new LicensingException(STATUS_GENERIC_ERROR);

				name = field["name"].get<std::string>();
				size = field["size"].get<double>();
				offset = 0;
				try { offset = field["offset"].get<double>(); }
				catch (std::out_of_range &) {}

				m_validationFields.emplace(piecewise_construct, forward_as_tuple(name.c_str()), forward_as_tuple(type, size, offset));

				if (m_validationDataSize < offset + size)
					m_validationDataSize = offset + size;
			}
		}

		SetSignatureSize(obj["signature_size"].get<double>());

		SetDataSize((m_numGroups * m_charsPerGroup * m_keyEncoding) - m_signatureSize);

		if (obj["private_key"].is<picojson::object>())
			SetPrivateKey(obj["private_key"].get<picojson::object>()["k"].get<std::string>().c_str());

		if (obj["public_cert"].is<picojson::object>())
			SetPublicKeyCertificate(obj["public_cert"].get<picojson::object>()["p"].get<std::string>().c_str());

		if (obj["licensing_service_url"].is<std::string>())
			SetLicensingServiceUrl(obj["licensing_service_url"].get<std::string>().c_str());

		if (obj["licensing_service_template_id"].is<std::string>())
			SetTemplateId(obj["licensing_service_template_id"].get<std::string>().c_str());

		if (obj["properties"].is<picojson::object>())
			m_properties.LoadJson(obj["properties"].get<picojson::object>());
	}
	catch (std::exception & e) {
		throw new LicensingException(STATUS_GENERIC_ERROR, "error loading JSON license template");
	}
}

#ifndef MAX_JSON_BUFFER_SIZE
#define MAX_JSON_BUFFER_SIZE 4096
#endif

const char * LicenseTemplateImpl::SaveJson(bool savePrivateKey)
{
	
	BASE64 base64;
	char buffer[MAX_XML_BUFFER_SIZE];

	m_xmlTemplate.resize(0);

	snprintf(buffer, MAX_XML_BUFFER_SIZE, "{"
		"\n\t\"version\":%d"
		",\n\t\"encoding\":\"%s\""
		",\n\t\"char_groups\":%d"
		",\n\t\"chars_per_group\":%d"
		",\n\t\"group_sep\":\"%s\""
		",\n\t\"header\":\"%s\""
		",\n\t\"footer\":\"%s\""
		",\n\t\"data_size\":%d", m_version, (m_keyEncoding == ENCODING_BASE64X) ? "BASE64" : "BASE32X", m_numGroups, m_charsPerGroup, m_groupSeparator.c_str(), m_header.c_str(), m_footer.c_str(), m_dataSize); m_xmlTemplate.append(buffer);

	snprintf(buffer, MAX_XML_BUFFER_SIZE, ",\n\t\"data_fields\":["); m_xmlTemplate.append(buffer);

	const char * name;
	BitStruct::FIELD_TYPE type;
	int size, offset;
	string fieldType;
	bool first = true;

	for (const auto& field : m_dataFields)
	{
		name = field.first.c_str();
		type = field.second.type;
		size = field.second.size;
		offset = field.second.offset;

		if (!first)	m_xmlTemplate.append(",");

		switch ((int)type)
		{
		case FIELD_TYPE_INTEGER:
			fieldType = "int";
			break;

		case FIELD_TYPE_STRING:
			fieldType = "string";
			break;

		case FIELD_TYPE_RAW:
			fieldType = "raw";
			break;

		case FIELD_TYPE_DATE13:
			fieldType = "date13";
			break;

		case FIELD_TYPE_DATE14:
			fieldType = "date14";
			break;

		case FIELD_TYPE_DATE16:
			fieldType = "date16";
			break;

		default:
			fieldType = "raw";
		}

		snprintf(buffer, MAX_XML_BUFFER_SIZE, "\n\t\t{ \"name\":\"%s\", \"type\":\"%s\", \"size\":%d, \"offset\":%d }", name, fieldType.c_str(), size, offset);
		m_xmlTemplate.append(buffer);

		first = false;
	}

	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\n\t]"); m_xmlTemplate.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, ",\n\t\"validation_data_size\":%d", m_validationDataSize); m_xmlTemplate.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, ",\n\t\"validation_fields\":["); m_xmlTemplate.append(buffer);

	first = true;

	for (const auto& field : m_validationFields)
	{
		name = field.first.c_str();
		type = field.second.type;
		size = field.second.size;
		offset = field.second.offset;

		if (!first)	m_xmlTemplate.append(",");

		switch ((int)type)
		{
		case FIELD_TYPE_INTEGER:
			fieldType = "int";
			break;

		case FIELD_TYPE_STRING:
			fieldType = "string";
			break;

		case FIELD_TYPE_RAW:
			fieldType = "raw";
			break;

		case FIELD_TYPE_DATE13:
			fieldType = "date13";
			break;

		case FIELD_TYPE_DATE14:
			fieldType = "date14";
			break;

		case FIELD_TYPE_DATE16:
			fieldType = "date16";
			break;

		default:
			fieldType = "raw";
		}

		snprintf(buffer, MAX_XML_BUFFER_SIZE, "\n\t\t{ \"name\":\"%s\", \"type\":\"%s\", \"size\":%d, \"offset\":%d }", name, fieldType.c_str(), size, offset);
		m_xmlTemplate.append(buffer);

		first = false;
	}

	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\n\t]"); m_xmlTemplate.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, ",\n\t\"signature_size\":%d", m_signatureSize); m_xmlTemplate.append(buffer);

	if (m_certificate)
	{
		snprintf(buffer, MAX_XML_BUFFER_SIZE, ",\n\t\"public_cert\": {\"kty\":\"softactivate_ec_v1\", \"p\":\"%s\"}", m_certificate->ToString()); m_xmlTemplate.append(buffer);

		if (savePrivateKey && m_signingKey)
		{
			snprintf(buffer, MAX_XML_BUFFER_SIZE, ",\n\t\"private_key\": {\"kty\":\"softactivate_ec_v1\", \"k\":\"%s\"}", m_signingKey->ToString()); m_xmlTemplate.append(buffer);
		}
	}

	if (m_licensingServiceUrl.length() > 0)
	{
		snprintf(buffer, MAX_XML_BUFFER_SIZE, ",\n\t\"licensing_service_url\":\"%s\"", m_licensingServiceUrl.c_str()); m_xmlTemplate.append(buffer);
	}

	if (!m_templateId.empty())
	{
		snprintf(buffer, MAX_XML_BUFFER_SIZE, ",\n\t\"licensing_service_template_id\":\"%s\"", m_templateId.c_str()); m_xmlTemplate.append(buffer);
	}

	if (m_properties.GetSize() > 0)
	{
		snprintf(buffer, MAX_XML_BUFFER_SIZE, ",\n\t\"properties\":"); m_xmlTemplate.append(buffer);
		m_properties.SaveJson(m_xmlTemplate, "\t");
	}

	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\n}"); m_xmlTemplate.append(buffer);

	return m_xmlTemplate.c_str();
}

namespace ANSCENTER {
	namespace Licensing {

		template<>
		LicenseTemplateT<char>::LicenseTemplateT():
		m_Impl(*(new LicenseTemplateImpl()))
		{

		}

		template<>
		LicenseTemplateT<wchar_t>::LicenseTemplateT():
		m_Impl(*(new LicenseTemplateImpl()))
		{

		}

		template<>
		LicenseTemplateT<char>::~LicenseTemplateT()
		{
			delete & m_Impl;
		}

		template<>
		LicenseTemplateT<wchar_t>::~LicenseTemplateT()
		{
			delete & m_Impl;
		}

		template<>
		LicenseTemplateT<char> * LicenseTemplateT<char>::Create()
		{
			return new LicenseTemplateT<char>();
		}

		template<>
		LicenseTemplateT<wchar_t> * LicenseTemplateT<wchar_t>::Create()
		{
			return new LicenseTemplateT<wchar_t>();
		}

		template<>
		void LicenseTemplateT<char>::Destroy(LicenseTemplateT<char> * obj)
		{
			delete obj;
		}

		template<>
		void LicenseTemplateT<wchar_t>::Destroy(LicenseTemplateT<wchar_t> * obj)
		{
			delete obj;
		}

		template<>
		void LicenseTemplateT<char>::SetNumberOfGroups(int numGroups)
		{
			m_Impl.SetNumberOfGroups(numGroups);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetNumberOfGroups(int numGroups)
		{
			m_Impl.SetNumberOfGroups(numGroups);
		}

		template<>
		unsigned LicenseTemplateT<char>::GetNumberOfGroups()
		{
			return m_Impl.GetNumberOfGroups();
		}

		template<>
		unsigned LicenseTemplateT<wchar_t>::GetNumberOfGroups()
		{
			return m_Impl.GetNumberOfGroups();
		}

		template<>
		void LicenseTemplateT<char>::SetCharactersPerGroup(int charsPerGroup)
		{
			m_Impl.SetCharactersPerGroup(charsPerGroup);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetCharactersPerGroup(int charsPerGroup)
		{
			m_Impl.SetCharactersPerGroup(charsPerGroup);
		}

		template<>
		unsigned LicenseTemplateT<char>::GetCharactersPerGroup()
		{
			return m_Impl.GetCharactersPerGroup();
		}

		template<>
		unsigned LicenseTemplateT<wchar_t>::GetCharactersPerGroup()
		{
			return m_Impl.GetCharactersPerGroup();
		}

		template<>
		void LicenseTemplateT<char>::SetGroupSeparator(const char * groupSep)
		{
			m_Impl.SetGroupSeparator(groupSep);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetGroupSeparator(const wchar_t * groupSep)
		{
			m_Impl.SetGroupSeparator(w2s(groupSep).c_str());
		}

		template<>
		const char * LicenseTemplateT<char>::GetGroupSeparator()
		{
			return m_Impl.GetGroupSeparator();
		}

		template<>
		const wchar_t * LicenseTemplateT<wchar_t>::GetGroupSeparator()
		{
			static wstring wstr1;
			
			wstr1 = s2w(m_Impl.GetGroupSeparator());
			return wstr1.c_str();
		}

		template<>
		void LicenseTemplateT<char>::SetEncoding(int encoding)
		{
			m_Impl.SetEncoding(encoding);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetEncoding(int encoding)
		{
			m_Impl.SetEncoding(encoding);
		}

		template<>
		int LicenseTemplateT<char>::GetEncoding()
		{
			return m_Impl.GetEncoding();
		}

		template<>
		int LicenseTemplateT<wchar_t>::GetEncoding()
		{
			return m_Impl.GetEncoding();
		}

		template<>
		void LicenseTemplateT<char>::SetHeader(const char * header)
		{
			m_Impl.SetHeader(header);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetHeader(const wchar_t * header)
		{
			m_Impl.SetHeader(w2s(header).c_str());
		}

		template<>
		const char * LicenseTemplateT<char>::GetHeader()
		{
			return m_Impl.GetHeader();
		}

		template<>
		const wchar_t * LicenseTemplateT<wchar_t>::GetHeader()
		{
			static wstring wstr1;
			
			wstr1 = s2w(m_Impl.GetHeader());
			return wstr1.c_str();
		}

		template<>
		void LicenseTemplateT<char>::SetFooter(const char * footer)
		{
			m_Impl.SetFooter(footer);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetFooter(const wchar_t * footer)
		{
			m_Impl.SetFooter(w2s(footer).c_str());
		}

		template<>
		const char * LicenseTemplateT<char>::GetFooter()
		{
			return m_Impl.GetFooter();
		}

		template<>
		const wchar_t * LicenseTemplateT<wchar_t>::GetFooter()
		{
			static wstring wstr1;
			
			wstr1 = s2w(m_Impl.GetFooter());
			return wstr1.c_str();
		}

		template<>
		void LicenseTemplateT<char>::SetDataSize(int sizeInBits)
		{
			m_Impl.SetDataSize(sizeInBits);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetDataSize(int sizeInBits)
		{
			m_Impl.SetDataSize(sizeInBits);
		}

		template<>
		unsigned LicenseTemplateT<char>::GetDataSize()
		{
			return m_Impl.GetDataSize();
		}

		template<>
		unsigned LicenseTemplateT<wchar_t>::GetDataSize()
		{
			return m_Impl.GetDataSize();
		}

		template<>
		void LicenseTemplateT<char>::AddDataField(const char * fieldName, int fieldType, int fieldBitSize, int offset)
		{
			
			m_Impl.AddDataField(fieldName, fieldType, fieldBitSize, offset);
		}

		template<>
		void LicenseTemplateT<wchar_t>::AddDataField(const wchar_t * fieldName, int fieldType, int fieldBitSize, int offset)
		{
			m_Impl.AddDataField(w2s(fieldName).c_str(), fieldType, fieldBitSize, offset);
		}

		template<>
		bool LicenseTemplateT<wchar_t>::GetDataField(const wchar_t * fieldName, int * fieldType, int * fieldBitSize, int * offset)
		{
			return m_Impl.GetDataField(w2s(fieldName).c_str(), fieldType, fieldBitSize, offset);
		}

		template<>
		bool LicenseTemplateT<char>::GetDataField(const char * fieldName, int * fieldType, int * fieldBitSize, int * offset)
		{
			return m_Impl.GetDataField(fieldName, fieldType, fieldBitSize, offset);
		}

		template<>
		bool LicenseTemplateT<char>::EnumDataFields(void **enumHandle, const char **fieldName, int * fieldType, int * fieldSize, int * offset)
		{
			return m_Impl.EnumDataFields(enumHandle, fieldName, fieldType, fieldSize, offset);
		}

		template<>
		bool LicenseTemplateT<wchar_t>::EnumDataFields(void **enumHandle, const wchar_t **fieldName, int * fieldType, int * fieldSize, int * offset)
		{		
			const char * name;
			static std::wstring staticName;
			bool result = m_Impl.EnumDataFields(enumHandle, &name, fieldType, fieldSize, offset);
			if (result)
			{
				staticName = s2w(name);
				*fieldName = staticName.c_str();
			}

			return result;
		}

		template<>
		void LicenseTemplateT<char>::SetValidationDataSize(int sizeInBits)
		{
			m_Impl.SetValidationDataSize(sizeInBits);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetValidationDataSize(int sizeInBits)
		{
			m_Impl.SetValidationDataSize(sizeInBits);
		}

		template<>
		unsigned LicenseTemplateT<char>::GetValidationDataSize()
		{
			return m_Impl.GetValidationDataSize();
		}

		template<>
		unsigned LicenseTemplateT<wchar_t>::GetValidationDataSize()
		{
			return m_Impl.GetValidationDataSize();
		}

		template<>
		void LicenseTemplateT<char>::AddValidationField(const char * fieldName, int fieldType, int fieldBitSize, int offset)
		{
			m_Impl.AddValidationField(fieldName, fieldType, fieldBitSize, offset);
		}

		template<>
		void LicenseTemplateT<wchar_t>::AddValidationField(const wchar_t * fieldName, int fieldType, int fieldBitSize, int offset)
		{
			m_Impl.AddValidationField(w2s(fieldName).c_str(), fieldType, fieldBitSize, offset);
		}

		template<>
		bool LicenseTemplateT<char>::EnumValidationFields(void **enumHandle, const char **fieldName, int * fieldType, int * fieldSize, int * offset)
		{
			return m_Impl.EnumValidationFields(enumHandle, fieldName, fieldType, fieldSize, offset);
		}

		template<>
		bool LicenseTemplateT<wchar_t>::EnumValidationFields(void **enumHandle, const wchar_t **fieldName, int * fieldType, int * fieldSize, int * offset)
		{		
			const char * name;
			static std::wstring staticName;
			bool result = m_Impl.EnumValidationFields(enumHandle, &name, fieldType, fieldSize, offset);
			
			if (result)
			{
				staticName = s2w(name);
				*fieldName = staticName.c_str();
			}

			return result;
		}

		template<>
		bool LicenseTemplateT<wchar_t>::GetValidationField(const wchar_t * fieldName, int * fieldType, int * fieldBitSize, int * offset)
		{
			return m_Impl.GetValidationField(w2s(fieldName).c_str(), fieldType, fieldBitSize, offset);
		}

		template<>
		bool LicenseTemplateT<char>::GetValidationField(const char * fieldName, int * fieldType, int * fieldBitSize, int * offset)
		{
			
			return m_Impl.GetValidationField(fieldName, fieldType, fieldBitSize, offset);
		}

		template<>
		void LicenseTemplateT<char>::SetSignatureSize(int signatureSize)
		{
			m_Impl.SetSignatureSize(signatureSize);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetSignatureSize(int signatureSize)
		{
			m_Impl.SetSignatureSize(signatureSize);
		}

		template<>
		unsigned LicenseTemplateT<char>::GetSignatureSize()
		{
			return m_Impl.GetSignatureSize();
		}

		template<>
		unsigned LicenseTemplateT<wchar_t>::GetSignatureSize()
		{
			return m_Impl.GetSignatureSize();
		}

		template<>
		void LicenseTemplateT<char>::SetVersion(int version)
		{
			m_Impl.SetVersion(version);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetVersion(int version)
		{
			m_Impl.SetVersion(version);
		}

		template<>
		unsigned LicenseTemplateT<char>::GetVersion()
		{
			return m_Impl.GetVersion();
		}

		template<>
		void LicenseTemplateT<char>::SetTemplateId(const char * templateId)
		{
			m_Impl.SetTemplateId(templateId);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetTemplateId(const wchar_t * templateId)
		{
			m_Impl.SetTemplateId(w2s(templateId).c_str());
		}

		template<>
		unsigned LicenseTemplateT<wchar_t>::GetVersion()
		{
			return m_Impl.GetVersion();
		}

		template<>
		void LicenseTemplateT<char>::LoadXml(const char * xmlTemplate)
		{
			m_Impl.LoadXml(xmlTemplate);
		}

		template<>
		void LicenseTemplateT<wchar_t>::LoadXml(const char * xmlTemplate)
		{
			m_Impl.LoadXml(xmlTemplate);
		}

		template<>
		const char * LicenseTemplateT<char>::SaveXml(bool savePrivateKey)
		{
			return m_Impl.SaveXml(savePrivateKey);
		}

		template<>
		const char * LicenseTemplateT<wchar_t>::SaveXml(bool savePrivateKey)
		{
			return m_Impl.SaveXml(savePrivateKey);
		}

		template<>
		void LicenseTemplateT<char>::LoadJson(const char * jsonTemplate)
		{
			m_Impl.LoadJson(jsonTemplate);
		}

		template<>
		void LicenseTemplateT<wchar_t>::LoadJson(const char * jsonTemplate)
		{
			m_Impl.LoadJson(jsonTemplate);
		}

		template<>
		const char * LicenseTemplateT<char>::SaveJson(bool savePrivateKey)
		{
			return m_Impl.SaveJson(savePrivateKey);
		}

		template<>
		const char * LicenseTemplateT<wchar_t>::SaveJson(bool savePrivateKey)
		{
			return m_Impl.SaveJson(savePrivateKey);
		}

		template<>
		void LicenseTemplateT<char>::SetLicensingServiceUrl(const char * url)
		{
			m_Impl.SetLicensingServiceUrl(url);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetLicensingServiceUrl(const wchar_t * url)
		{
			m_Impl.SetLicensingServiceUrl(w2s(url).c_str());
		}

		template<>
		const char * LicenseTemplateT<char>::GetLicensingServiceUrl()
		{
			return m_Impl.GetLicensingServiceUrl();
		}

		template<>
		const wchar_t * LicenseTemplateT<wchar_t>::GetLicensingServiceUrl()
		{
			static wstring wstr1;
			
			wstr1 = s2w(m_Impl.GetLicensingServiceUrl());
			return wstr1.c_str();
		}

		template<>
		void LicenseTemplateT<char>::SetPublicKeyCertificate(const char * base64Certificate)
		{
			m_Impl.SetPublicKeyCertificate(base64Certificate);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetPublicKeyCertificate(const wchar_t * base64Certificate)
		{
			m_Impl.SetPublicKeyCertificate(w2s(base64Certificate).c_str());
		}

		template<>
		const char * LicenseTemplateT<char>::GetPublicKeyCertificate()
		{
			return m_Impl.GetPublicKeyCertificate();
		}

		template<>
		const wchar_t * LicenseTemplateT<wchar_t>::GetPublicKeyCertificate()
		{
			static wstring wstr1;
			
			wstr1 = s2w(m_Impl.GetPublicKeyCertificate());
			return wstr1.c_str();
		}

		template<>
		void LicenseTemplateT<char>::SetPrivateKey(const char * base64Key)
		{
			m_Impl.SetPrivateKey(base64Key);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetPrivateKey(const wchar_t * base64Key)
		{
			m_Impl.SetPrivateKey(w2s(base64Key).c_str());
		}

		template<>
		const char * LicenseTemplateT<char>::GetPrivateKey()
		{
			return m_Impl.GetPrivateKey();
		}

		template<>
		const wchar_t * LicenseTemplateT<wchar_t>::GetPrivateKey()
		{
			static wstring wstr1;
			
			wstr1 = s2w(m_Impl.GetPrivateKey());
			return wstr1.c_str();
		}

		template<>
		void LicenseTemplateT<char>::SetProperty(const char * path, const char * name, const char * value)
		{
			m_Impl.SetProperty(path, (name) ? name : nullptr, value);
		}

		template<>
		void LicenseTemplateT<wchar_t>::SetProperty(const wchar_t * path, const wchar_t * name, const wchar_t * value)
		{
			m_Impl.SetProperty(w2s(path).c_str(), (name) ? w2s(name).c_str() : nullptr, w2s(value).c_str());
		}

		template<>
		const char * LicenseTemplateT<char>::GetProperty(const char * path, const char * name)
		{
			return m_Impl.GetProperty(path, name);
		}

		template<>
		const wchar_t * LicenseTemplateT<wchar_t>::GetProperty(const wchar_t * path, const wchar_t * name)
		{
			static wstring wstr1;

			wstr1 = s2w(m_Impl.GetProperty(w2s(path).c_str(), w2s(name).c_str()));
			return wstr1.c_str();
		}

		template<>
		void LicenseTemplateT<char>::GenerateSigningKeyPair()
		{
			m_Impl.GenerateSigningKeyPair();
		}

		template<>
		void LicenseTemplateT<wchar_t>::GenerateSigningKeyPair()
		{
			m_Impl.GenerateSigningKeyPair();
		}
	};
};
