EstEID_Map EstEID_mapClone(EstEID_Map map) {
	EstEID_Map result;
	if (!map) return NULL;
	result = EstEID_mapPut(NULL, map->key, map->value);
	result->next = EstEID_mapClone(map->next);
	return result;
}
int main(void) {
	assertStringEquals("foo", EstEID_jsonString("foo"));
	assertStringEquals("foo\\\"", EstEID_jsonString("foo\""));
	assertStringEquals("foo\\\\", EstEID_jsonString("foo\\"));
	assertStringEquals("\\n", EstEID_jsonString("\n"));
	assertStringEquals("foo\\\\bar\\r\\n\\\"test\\\"", EstEID_jsonString("foo\\bar\r\n\"test\""));

	EstEID_Map map = NULL;
	assertStringEquals("{}", EstEID_mapToJson(map));
	map = EstEID_mapPut(map, "foo", "bar");
	assertStringEquals("{\"foo\": \"bar\"}", EstEID_mapToJson(map));

	map = EstEID_mapPut(map, "a\nb", "\"123\"");
	assertStringEquals("{\"foo\": \"bar\", \"a\\nb\": \"\\\"123\\\"\"}", EstEID_mapToJson(map));

	EstEID_mapFree(map);

	return 0;
}
EstEID_Map EstEID_createCertMap(CK_TOKEN_INFO tokenInfo) {
	char *label = EstEID_createString(tokenInfo.label, sizeof(tokenInfo.label));	
	EstEID_Map cert = EstEID_mapPutNoAlloc(NULL, strdup("label"), label);

	char pinLen[8];
	memset(pinLen, 0x0, 8);
	sprintf(pinLen, "%lu", tokenInfo.ulMinPinLen);
	EstEID_mapPut(cert, "minPinLen", pinLen);

	return cert;
}
EstEID_Map EstEID_mapPutNoAlloc(EstEID_Map map, const char *key, const char *value) {
	if (!map) {
		map = (EstEID_Map)malloc(sizeof(struct EstEID_MapEntry));
		map->key = key;
		map->value = value;
		map->next = NULL;
	}
	else if (!strcmp(map->key, key)) {
		free((void *)map->key);
		free((void *)map->value);
		map->key = key;
		map->value = value;
	}
	else {
		map->next = EstEID_mapPut(map->next, key, value);
	}
	return map;
}
int main(void) {

	EstEID_Map map = NULL;
	assertIntEquals(0, EstEID_mapSize(map));

	map = EstEID_mapPut(map, "foo", "bar");
	assertNotNull(map);
	assertIntEquals(1, EstEID_mapSize(map));

	map = EstEID_mapPut(map, "foo", "FOO");
	assertIntEquals(1, EstEID_mapSize(map));

	char *k = strdup("key");
	char *v = strdup("value");
	map = EstEID_mapPut(map, k, v);
	free(k);
	free(v);
	assertIntEquals(2, EstEID_mapSize(map));
	assertStringEquals("value", EstEID_mapGet(map, "key"));

	assertNull(EstEID_mapGet(map, "xyz"));

	EstEID_mapFree(map);


	EstEID_Map m = EstEID_mapPut(NULL, "a", "A");
	EstEID_mapPut(m, "b", "B");
	EstEID_mapPut(m, "c", "C");

	printf("map:\n");
	EstEID_mapPrint(stdout, m);

	EstEID_Map c = EstEID_mapClone(m);
	EstEID_mapFree(m);

	printf("clone:\n");
	EstEID_mapPrint(stdout, c);
	EstEID_mapFree(c);

}
int EstEID_loadCertInfoEntries(EstEID_Certs *certs, int index) {
	EstEID_Map cert = certs->certs[index];
	CK_SLOT_ID slotID = certs->slotIDs[index];

	CK_SESSION_HANDLE session;
	FAIL_IF(EstEID_CK_failure("C_OpenSession", fl->C_OpenSession(slotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &session)));

	CK_OBJECT_CLASS objectClass = CKO_CERTIFICATE;
	CK_ATTRIBUTE searchAttribute = {CKA_CLASS, &objectClass, sizeof(objectClass)};
	if (EstEID_CK_failure("C_FindObjectsInit", fl->C_FindObjectsInit(session, &searchAttribute, 1))) return FAILURE;

	CK_OBJECT_HANDLE objectHandle;
	CK_ULONG objectCount;
	if (EstEID_CK_failure("C_FindObjects", fl->C_FindObjects(session, &objectHandle, 1, &objectCount))) return FAILURE;

	if (objectCount == 0) return SUCCESS;

	CK_ATTRIBUTE attribute = {CKA_VALUE, NULL_PTR, 0};
	if (EstEID_CK_failure("C_GetAttributeValue", fl->C_GetAttributeValue(session, objectHandle, &attribute, 1))) return FAILURE;

	CK_ULONG certificateLength = attribute.ulValueLen;
	CK_BYTE_PTR certificate = (CK_BYTE_PTR)malloc(certificateLength);
	attribute.pValue = certificate;
	if (EstEID_CK_failure("C_GetAttributeValue", fl->C_GetAttributeValue(session, objectHandle, &attribute, 1))) return FAILURE;

	EstEID_mapPutNoAlloc(cert, strdup("certificateAsHex"), EstEID_bin2hex((char *)certificate, certificateLength));

	const unsigned char *p = certificate;
	X509 *x509 = d2i_X509(NULL, &p, certificateLength);
	
	char *certMD5;
	certMD5 = EstEID_getCertHash((char*)certificate);
	FAIL_IF(EstEID_md5_failure(certMD5));
	EstEID_mapPutNoAlloc(cert, strdup("certHash"), certMD5);
	free(certificate);
// todo: error handling of all openssl functions

	EstEID_mapPutNoAlloc(cert, strdup("validTo"), EstEID_ASN1_TIME_toString(X509_get_notAfter(x509)));
	EstEID_mapPutNoAlloc(cert, strdup("validFrom"), EstEID_ASN1_TIME_toString(X509_get_notBefore(x509)));

	unsigned long keyUsage;
	ASN1_BIT_STRING *usage = (ASN1_BIT_STRING *)X509_get_ext_d2i(x509, NID_key_usage, NULL, NULL);
	if (usage->length > 0) keyUsage = usage->data[0];
	ASN1_BIT_STRING_free(usage);

	if (keyUsage & X509v3_KU_DIGITAL_SIGNATURE) EstEID_mapPut(cert, "usageDigitalSignature", "TRUE");
	if (keyUsage & X509v3_KU_NON_REPUDIATION) {
		EstEID_mapPut(cert, "usageNonRepudiation", "TRUE");
		EstEID_mapPut(cert, "keyUsage", "Non-Repudiation");  // for compatibility with older plugin
	}

	EstEID_loadCertEntries(cert, "", X509_get_subject_name(x509));

	char *certSerialNumber = (char*)malloc(33);
	snprintf(certSerialNumber, 32, "%lX", ASN1_INTEGER_get(X509_get_serialNumber(x509)));
	EstEID_mapPutNoAlloc(cert, strdup("certSerialNumber"), certSerialNumber);
	EstEID_loadCertEntries(cert, "issuer.", X509_get_issuer_name(x509));

	BIO *bio = BIO_new(BIO_s_mem());
	if (!PEM_write_bio_X509(bio, x509)) printf("Cannot create PEM\n");
	char *b;
	int len = BIO_get_mem_data(bio, &b);
	char *pem = (char *)malloc(len + 1);
	strncpy(pem, b, len);
	pem[len] = 0;
	BIO_free(bio);
	EstEID_mapPutNoAlloc(cert, strdup("certificateAsPEM"), pem);

	FAIL_IF(EstEID_CK_failure("C_CloseSession", fl->C_CloseSession(session)));
	return SUCCESS;
}