/*************************************************************************
* Rutoken                                                                *
* Copyright (c) 2003-2017, CJSC Aktiv-Soft. All rights reserved.         *
* Подробная информация:  http://www.rutoken.ru                           *
*------------------------------------------------------------------------*
* В данном файле содержится реализация следующих вспомогательных         *
* функций для работы с Рутокен при помощи программы Cert2Cont:           *
*  - печати сообщений в командной строке;                                *
*  - анализа параметров, переданных в командной строке;                  *
*  - чтения сертификата из файла;                                        *
*  - освобождения использованных ресурсов.                               *
*************************************************************************/

#include "stdafx.h"
#include "Util_Funcs.h"
#include "Cert2Cont.h"


/************************************************************************
* Распечатать приветствие                                               *
************************************************************************/
void PrintWelcomeInformation()
{
	wprintf(L"\nRutoken project| 2017  (C) Aktiv Co. | www.rutoken.ru\n");
	wprintf(L"Welcome to \"Certificate import to container utility\".\n\n");
}

/************************************************************************
* Распечатать справочную информацию                                     *
************************************************************************/
void PrintHelpInformation()
{
	wprintf(L"\nCert2Cont    [/p <CSP_Name>] [/c <Container_Name>]\n");
	wprintf(L"        [/k AT_KEYEXCHANGE|AT_SIGNATURE] [/f <Der_Cer_File_Name>]\n");
	wprintf(L"        [/?]\n");
	wprintf(L"    /? - print this help;\n");
	wprintf(L"    /p - CSP name, for example \"Aktiv ruToken CSP v1.0\";\n");
	wprintf(L"    /c - CSP key container name, for example\n");
	wprintf(L"        \"{C4169872-D881-4154-8C8B-B1B2B7021BFF}\";\n");
	wprintf(L"    /k - Identifies the key pair to use from the key container.\n");
	wprintf(L"        It can be \"AT_KEYEXCHANGE\" or \"AT_SIGNATURE\".\n");
	wprintf(L"    /f - DER certificate file name.\n\n");
	wprintf(L"!!!Parameters with spaces must be used in quotes.\n\n");
	wprintf(L"Example:\n");
	wprintf(L"C:\\Cert2Cont.exe /p \"Aktiv ruToken CSP v1.0\" /f \"C:\\cert.cer\"\n");
	wprintf(L"    /c {C4169872-D881-4154-8C8B-B1B2B7021BFF} /k AT_KEYEXCHAGE \n\n");
}

/************************************************************************
* Проанализировать параметры, переданные в командной строке             *
************************************************************************/
BOOL ParseCommandLine(IN int argc,
                      IN WCHAR* argv[],
                      OUT LPWSTR* lpszCSP,
                      OUT LPWSTR* lpszContainer,
                      OUT DWORD* dwKeySpec,
                      OUT LPWSTR* lpszCertFileName,
                      OUT BOOL* bPrintHelpAndExit)
{
	BOOL bResult = TRUE;                     // Вспомогательная переменная для хранения результата выполнения функции

	DWORD i, j = 0;                          // Счетчики циклов

	WCHAR lpszCurCmdParam[MAX_PATH] = { 0 }; // Вспомогательная строка для анализа параметров

	DWORD dwParamsMatchCount = 0;            // Вспомогательная переменная для хранения сравнения параметров командной строки

	LPWSTR lpszAllCmdParams[] = { C2C_CMD_ARG_HELP, C2C_CMD_ARG_CSP, C2C_CMD_ARG_CONTAINER, C2C_CMD_ARG_KEYSPEC, C2C_CMD_ARG_CERFILE };

	/************************************************************************
	* Проверка на отсутствие повторяющихся параметров                        *
	************************************************************************/
	for (i = 0; i < ARRAYSIZE(lpszAllCmdParams); ++i) {
		dwParamsMatchCount = 0;

		for (j = 1; j < (DWORD)argc; ++j) {
			if (wcscmp(argv[j], lpszAllCmdParams[i]) == 0) {
				++dwParamsMatchCount;
			}
		}

		if (dwParamsMatchCount > 1) {
			PrintHelpInformation();
			return FALSE;
		}
	}

	*bPrintHelpAndExit = FALSE;

	for (i = 1; i < (DWORD)argc; i += 2) {
		wcscpy(lpszCurCmdParam,
		       argv[i]);
		_wcslwr(lpszCurCmdParam);

		/************************************************************************
		* Проанализировать параметр "/?"                                        *
		************************************************************************/
		if (wcscmp(lpszCurCmdParam,
		           C2C_CMD_ARG_HELP)
		    == 0) {
			PrintHelpInformation();
			*bPrintHelpAndExit = TRUE;
			break;
		}

		/************************************************************************
		* Проанализировать параметр "/p"                                        *
		************************************************************************/
		if (wcscmp(lpszCurCmdParam,
		           C2C_CMD_ARG_CSP)
		    == 0) {
			if (argv[i + 1]) {
				*lpszCSP = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, ((wcslen(argv[i + 1]) + 1) * sizeof(WCHAR)));

				wcscpy(*lpszCSP,
				       argv[i + 1]);
			} else {
				wprintf(L"Error in command line.\n");
				PrintHelpInformation();
				bResult = FALSE;
				break;
			}

			continue;
		}

		/************************************************************************
		* Проанализировать параметр "/c"                                        *
		************************************************************************/
		if (wcscmp(lpszCurCmdParam,
		           C2C_CMD_ARG_CONTAINER)
		    == 0) {
			if (argv[i + 1]) {
				*lpszContainer = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, ((wcslen(argv[i + 1]) + 1) * sizeof(WCHAR)));

				wcscpy(*lpszContainer,
				       argv[i + 1]);
			} else {
				wprintf(L"Error in command line.\n");
				PrintHelpInformation();
				bResult = FALSE;
				break;
			}

			continue;
		}

		/************************************************************************
		* Проанализировать параметр "/k"                                        *
		************************************************************************/
		if (wcscmp(lpszCurCmdParam,
		           C2C_CMD_ARG_KEYSPEC)
		    == 0) {
			*dwKeySpec = 0;

			if (argv[i + 1]) {
				if (wcscmp(argv[i + 1],
				           C2C_CMD_ARG_KEYSPEC_AT_KEYEXCHANGE)
				    == 0) {
					*dwKeySpec = AT_KEYEXCHANGE;
				}

				if (wcscmp(argv[i + 1],
				           C2C_CMD_ARG_KEYSPEC_AT_SIGNATURE)
				    == 0) {
					*dwKeySpec = AT_SIGNATURE;
				}
			} else {
				wprintf(L"Error in command line.\n");
				PrintHelpInformation();
				bResult = FALSE;
				break;
			}

			if (*dwKeySpec == 0) {
				wprintf(L"Error in command line.\n");
				PrintHelpInformation();
				bResult = FALSE;
				break;
			}

			continue;
		}

		/************************************************************************
		* Проанализировать параметр "/f"                                        *
		************************************************************************/
		if (wcscmp(lpszCurCmdParam,
		           C2C_CMD_ARG_CERFILE)
		    == 0) {
			if (argv[i + 1]) {
				*lpszCertFileName = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, ((wcslen(argv[i + 1]) + 1) * sizeof(WCHAR)));

				wcscpy(*lpszCertFileName,
				       argv[i + 1]);
			} else {
				wprintf(L"Error in command line.\n");
				PrintHelpInformation();
				bResult = FALSE;
				break;
			}

			continue;
		}

		wprintf(L"Error in command line.\n");
		PrintHelpInformation();
		bResult = FALSE;
		break;
	}

	return bResult;
}

/************************************************************************
* Считать сертификат из файла                                           *
************************************************************************/
BOOL ReadCertificateFromFile(IN LPCWSTR lpszPreferredFile,
                             OUT LPVOID* lpvCertBuffer,
                             OUT DWORD* dwCertBufferSize)
{
	BOOL bResult = TRUE;                      // Вспомогательная переменная для хранения результата выполнения функции

	HANDLE hCertFile = NULL;                  // Хэндл файла, содержащего сертификат
	WCHAR lpszCertFileName[MAX_PATH] = { 0 }; // Буфер для временного хранения имени файла
	DWORD dwBytesRead = 0;                    // Количество считанных байт

	DWORD dwError = ERROR_SUCCESS;            // Вспомогательная переменная для хранения кода возврата

	if (lpszPreferredFile) {
		wcscpy(lpszCertFileName,
		       lpszPreferredFile);
	} else {
		wprintf(L"Enter der-certificate file name:\n");
		if (wscanf(L"%s", lpszCertFileName) != 1) {
			wprintf(L"Input error.\n");
			bResult = FALSE;
			return bResult;
		}
	}

	/************************************************************************
	* Открыть файл, содержащий сертификат                                   *
	************************************************************************/
	hCertFile = CreateFileW(lpszCertFileName,
	                        GENERIC_READ,
	                        FILE_SHARE_READ,
	                        NULL,
	                        OPEN_EXISTING,
	                        0,
	                        NULL);
	bResult = (hCertFile != INVALID_HANDLE_VALUE);
	dwError = GetLastError();
	if (!bResult) {
		PrintErrorText(L"CreateFileW",
		               dwError);
		return bResult;
	}

	/************************************************************************
	* Получить размер файла, содержащего сертификат                         *
	************************************************************************/
	*dwCertBufferSize = GetFileSize(hCertFile, NULL);
	bResult = *dwCertBufferSize != INVALID_FILE_SIZE;
	if (!bResult) {
		dwError = GetLastError();
		*dwCertBufferSize = 0;
		CloseHandle(hCertFile);
		PrintErrorText(L"GetFileSize",
		               dwError);
		return bResult;
	}

	*lpvCertBuffer = (LPVOID)LocalAlloc(LMEM_ZEROINIT, *dwCertBufferSize);

	/************************************************************************
	* Считать в буфер содержимое файла с сертификатом                       *
	************************************************************************/
	bResult = ReadFile(hCertFile,
	                   *lpvCertBuffer,
	                   *dwCertBufferSize,
	                   &dwBytesRead,
	                   NULL);
	if (!bResult) {
		dwError = GetLastError();
		CloseHandle(hCertFile);
		PrintErrorText(L"ReadFile",
		               dwError);
		return bResult;
	}

	CloseHandle(hCertFile);
	return bResult;
}

/************************************************************************
* Освободить ресурсы в массиве                                          *
************************************************************************/
void FreePointersList(IN LPWSTR* lpszPointersList,
                      IN DWORD dwPointersCount)
{
	DWORD dwIndex; // Счетчик цикла
	for (dwIndex = 0; dwIndex < dwPointersCount; ++dwIndex) {
		LocalFree(lpszPointersList[dwIndex]);
	}

}

/************************************************************************
* Распечатать сообщение об ошибке                                       *
************************************************************************/
void PrintErrorText(IN LPWSTR lpszText,
                    IN DWORD dwError)
{
	LPWSTR lpszError_Text = 0;

	wprintf(L"Error - %s. Error code: 0x%X\n", lpszText, dwError);

	FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
	               NULL,
	               dwError,
	               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
	               (LPWSTR)&lpszError_Text,
	               0,
	               NULL);
	wprintf(L"Error description: %s\n", lpszError_Text);
	LocalFree(lpszError_Text);
}
