// Данный тест проверяет работу управляющих команд,
// передаваемых библиотеке функцией C_Ctrl.
// Предполагается, что программный токен
// по умолчанию конфигурирован в последнем слоте,
// поэтому в тесте явно используется библиотека ls11sw.
#include "test_common.h"

CK_UTF8CHAR *new_user_pin = "22222222";
CK_UTF8CHAR *new_so_pin = "88888888";
CK_UTF8CHAR *sopin = NULL;
CK_UTF8CHAR *userpin = NULL;
CK_SLOT_ID_PTR SlotList = NULL;
CK_ULONG SlotCount = 0;
CK_SLOT_ID SlotId;
//CK_UTF8CHAR prompt_and_pin_buf[80];

int
main(int argc, char *argv[]){
	CK_RV rc;
	CK_SLOT_INFO sinfo;
	CK_ULONG i;
	CK_RV  (*pfoo)();
	CK_FLAGS flags = 0;
	CK_SESSION_HANDLE sess;
	Token_Desc_FS tokenDesc = {
		(CK_CHAR *)"G:/lsms11/data/sw_token",
		(CK_CHAR *)"6543210987654321",     // serial number
	};
	Slot_Desc slotDesc = {
    	TOKEN_TYPE_FILE_SYSTEM,
		&tokenDesc,
	};
	CK_UTF8CHAR *label = "Token Label";
	CK_UTF8CHAR label32[32];

#ifdef WIN32
	dll = LoadLibrary("ls11sw");
#else   
	dll = dlopen("libls11sw.so", RTLD_NOW);
#endif   
	if ( dll == NULL ) {
		fprintf(stderr, "Can't load PKCS#11 API library. Check API path.\n"); 
		return CKR_GENERAL_ERROR;
	}
#ifdef WIN32
	pfoo = (CK_C_GetFunctionList)GetProcAddress(dll,"C_GetFunctionList");
#else
	pfoo = (CK_C_GetFunctionList)dlsym(dll,"C_GetFunctionList");
#endif   
	if (pfoo == NULL ) {
		fprintf(stderr, "Couldn't get C_GetFunctionList address\n");
		return CKR_GENERAL_ERROR;
	}
   
	rc = pfoo(&funcs);
	if (rc != CKR_OK) {
		fprintf(stderr, "Error in  C_GetFunctionList 0x%X\n", rc );
		return rc;
	}
#ifdef WIN32
	pctrl = (CK_C_Ctrl)GetProcAddress(dll,"C_Ctrl");
#else
	pctrl = (CK_C_Ctrl)dlsym(dll,"C_Ctrl");
#endif   

	// Конфигурируем путь к файлу сообщений
	rc = pctrl(PKCS11_CTRL_LOG_PATH, 0, 
		(void *)"G:/lsms11/data/log.txt", NULL);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Ctrl failed: 0x%x\n", rc);
		return rc;
	}
	// Конфигурируем путь к начальному значению ГСЧ
	rc = pctrl(PKCS11_CTRL_SEED_PATH, 0, 
		(void *)"G:/lsms11/data/seed.bin", NULL);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Ctrl failed: 0x%x\n", rc);
		return rc;
	}

	// Если конфигурирование последнего слота выполнить до инициализации библиотеки,
	// то умалчиваемый программный токен будет работать не из умалчиваемой,
	// а из заданной папки.
	// Но мы здесь конфигурируем дополнительный токен в слоте 0.
	rc = pctrl(PKCS11_CTRL_OPEN_TOKEN, 0, 
		(void *)&slotDesc, NULL);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Ctrl failed: 0x%x\n", rc);
		return rc;
	}
	// Аналогично можно конфигурировать программные токены и в других слотах.

	rc = funcs->C_Initialize(NULL);
	if (rc != CKR_OK) {
		show_error("C_Initialize", rc);
		return rc;
	}
/*
	// Умалчиваемый программный токен конфигурирован в слоте PKCS11_DEFAULT_SW_TOKEN_ID.
	// "Извлекаем" умалчиваемый программный токен.
	rc = pctrl(PKCS11_CTRL_CLOSE_TOKEN, PKCS11_DEFAULT_SW_TOKEN_ID, 
		NULL, NULL);
	if (rc == CKR_OK) {
		// Конфигурируем путь к программному токену в слоте PKCS11_DEFAULT_SW_TOKEN_ID.
		// Аналогичным образом можно "вставить" программный токен в любой другой слот,
		// если он не предназначен для аппаратного токена.
		// В библиотеке lsms11 программный токен может быть конфигурирован
		// только в последнем слоте, да и то, если библиотека не была собрана
		// с флагом NO_SW_TOKEN.
		// В библиотеке ls11sw все слоты предназначены только
		// для программных токенов.
		rc = pctrl(PKCS11_CTRL_OPEN_TOKEN, PKCS11_DEFAULT_SW_TOKEN_ID, 
			(void *)&slotDesc, NULL);
		if (rc != CKR_OK) {
			fprintf(stderr, "C_Ctrl(PKCS11_CTRL_OPEN_TOKEN) failed: 0x%x\n", rc);
			return rc;
		}
	} else {
		fprintf(stderr, "C_Ctrl(PKCS11_CTRL_CLOSE_TOKEN) failed: 0x%x\n", rc);
	}
*/
	rc = funcs->C_GetSlotList(CK_FALSE, NULL, &SlotCount);
	if (rc != CKR_OK) {
		SlotList = NULL;
		printf("Get slot list result: 0x%x\n", rc);
		return rc;
	}
	printf("%d slots found\n", SlotCount);
	if (SlotCount > 0)
	{   
		SlotList = (CK_SLOT_ID_PTR) malloc(SlotCount * sizeof(CK_SLOT_ID));
		rc = funcs->C_GetSlotList(CK_TRUE, SlotList, &SlotCount);
		if (rc != CKR_OK) {
			SlotList = NULL;
			SlotCount = 0;
			printf("Get slot list result: 0x%x\n", rc);
			return rc;
		}
	} else {
		SlotList = NULL;
		return CKR_OK;
	}
/*
	printf("Cycle through slot list\n");
	for (i = 0; i<SlotCount; i++) 
	{
		// Display the current token and slot info 
		rc = display_slot_info(funcs, SlotList[i]);
		if (rc != CKR_OK) {
			printf("display slot info failed\n");
			return rc;
		}
		rc = funcs->C_GetSlotInfo(SlotList[i], &sinfo);
		if (sinfo.flags & CKF_TOKEN_PRESENT) {
			rc = display_token_info(funcs, SlotList[i]);
			if (rc != CKR_OK) {
				printf("display_token_info failed\n");
				return rc;
			}
			SlotId = SlotList[i];
		}
	}
*/
	userpin = user_pin;
	sopin = so_pin;

	printf("==>> C_InitToken...\n");
	// Если токен не был инициализирован, то SO PIN не проверяется,
	// а впервые назначается, поэтому в любом случае после успешного
	// выполнения данной функции на токене будет установлен заданный sopin,
	// а USER PIN установлен не будет.
	memset(label32, ' ', sizeof(label32));
	memcpy(label32, label, strlen(label));  
	rc = funcs->C_InitToken(SlotId, sopin, strlen(sopin), label32);
	if (rc != CKR_OK) {
		printf("==>> C_InitToken failed\n\n");
		return rc;
	}
//	printf("==>> C_InitToken Ok\n\n");
//	display_token_info(funcs, SlotId);

	rc = funcs->C_GetSlotList(CK_FALSE, NULL, &SlotCount);
	if (rc != CKR_OK) {
		SlotList = NULL;
		printf("Get slot list result: 0x%x\n", rc);
		return rc;
	}
	printf("%d slots found\n", SlotCount);
	if (SlotCount > 0)
	{   
		SlotList = (CK_SLOT_ID_PTR) malloc(SlotCount * sizeof(CK_SLOT_ID));
		rc = funcs->C_GetSlotList(CK_TRUE, SlotList, &SlotCount);
		if (rc != CKR_OK) {
			SlotList = NULL;
			SlotCount = 0;
			printf("Get slot list result: 0x%x\n", rc);
			return rc;
		}
	} else {
		SlotList = NULL;
		return CKR_OK;
	}

	printf("Cycle through slot list\n");
	for (i = 0; i<SlotCount; i++) 
	{
		// Display the current token and slot info 
		rc = display_slot_info(funcs, SlotList[i]);
		if (rc != CKR_OK) {
			printf("display slot info failed\n");
			return rc;
		}
		rc = funcs->C_GetSlotInfo(SlotList[i], &sinfo);
		if (sinfo.flags & CKF_TOKEN_PRESENT) {
			rc = display_token_info(funcs, SlotList[i]);
			if (rc != CKR_OK) {
				printf("display_token_info failed\n");
				return rc;
			}
			SlotId = SlotList[i];
		}
	}

	flags = CKF_SERIAL_SESSION | CKF_RW_SESSION;
// Открытие сессии с указанием колбэка для ввода PIN и буфера для 
// обмена данными между библиотекой и колбэком. Библиотека формирует 
// в буфере запрос на ввод PIN, а колбэк возвращает PIN в этом же буфере. 
// Библиотека сама очищает значение PIN в буфере после его использования.
//	rc = funcs->C_OpenSession( 
//	SlotId, flags, prompt_and_pin_buf, pin_callback, &sess );

	rc = funcs->C_OpenSession( SlotId, flags, NULL, NULL, &sess );
	if (rc != CKR_OK) {
		printf("C_OpenSession failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_OpenSession success\n");

	
	rc = funcs->C_Login(sess, CKU_SO, sopin, strlen(sopin));
	if (rc != CKR_OK) {
		printf("C_Login failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_Login (CKU_SO) success\n");

	rc = funcs->C_InitPIN(sess, new_user_pin, strlen(new_user_pin));
	if (rc != CKR_OK) {
		printf("C_InitPIN failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_InitPIN success\n");

	rc = funcs->C_SetPIN(sess, sopin, strlen(sopin), 
		new_so_pin, strlen(new_so_pin));
	if (rc != CKR_OK) {
		printf("C_SetPIN failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_SetPIN (CKU_SO) success\n");

	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		printf("C_Logout failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_Logout (CKU_SO) success\n");

	rc = funcs->C_Login(sess, CKU_SO, 
		new_so_pin, strlen(new_so_pin));
	if (rc != CKR_OK) {
		printf("C_Login failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_Login (CKU_SO) success\n");

	rc = funcs->C_SetPIN(sess, new_so_pin, strlen(new_so_pin), 
		sopin, strlen(sopin));
	if (rc != CKR_OK) {
		printf("C_SetPIN failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_SetPIN back (CKU_SO) success\n");

	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		printf("C_Logout failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_Logout (CKU_SO) success\n");

	rc = funcs->C_Login(sess, CKU_SO, sopin, strlen(sopin));
	if (rc != CKR_OK) {
		printf("C_Login failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_Login (CKU_SO) success\n");

	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		printf("C_Logout failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_Logout (CKU_SO) success\n");

	// Здесь будет использоваться колбэк для ввода PIN,
	// если он задан в C_OpenSession.
	//	rc = funcs->C_Login(sess, CKU_USER, NULL, 0);

	rc = funcs->C_Login(sess, CKU_USER, 
		new_user_pin, strlen(new_user_pin));
	if (rc != CKR_OK) {
		printf("C_Login failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_Login (CKU_USER) success\n");

	rc = funcs->C_SetPIN(sess, 
		new_user_pin, strlen(new_user_pin), 
		userpin, strlen(userpin));
	if (rc != CKR_OK) {
		printf("C_SetPIN failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_SetPIN (CKU_USER) success\n");

	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		printf("C_Logout failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_Logout (CKU_USER) success\n");

	rc = funcs->C_CloseSession(sess);
	if (rc != CKR_OK) {
		printf("C_CloseSession failed, rc = 0x%x\n", rc );
		return rc;
	}
	printf("C_CloseSession success\n");

	display_token_info(funcs, SlotId);

	if(SlotList){free(SlotList);SlotList=NULL;}
	printf("C_Ctrl test SUCCESS\n");
	return CKR_OK;
}
