/*
* Copyright(C) 2000 Проект ИОК
*
* Этот файл содержит информацию, являющуюся
* собственностью компании Крипто Про.
*
* Любая часть этого файла не может быть скопирована,
* исправлена, переведена на другие языки,
* локализована или модифицирована любым способом,
* откомпилирована, передана по сети с или на
* любую компьютерную систему без предварительного
* заключения соглашения с компанией Крипто Про.
*/
#include "rdr_prj.h"


/* установка считывателя по умолчанию */
static const TCHAR RDR_DEFAULT_RDR[] = _TEXT("\\local\\KeyDevices\\default");
/* глобальная установка считывателя по умолчанию */
static const TCHAR RDR_DEFAULT_GLOBAL_RDR[] = _TEXT("\\CONFIG\\KeyDevices\\default");

DWORD rdr_context_default_get(
    const TSupSysEList *list,
    TSupSysEContext **context)
{
    DWORD code;
    size_t length = 0;
    TCHAR *name = NULL;
    const TCHAR *paths[2] = { RDR_DEFAULT_RDR, RDR_DEFAULT_GLOBAL_RDR };
    int i;
    TSupSysEFind *ctx;

    SUPSYS_PRE_WRITE_PTRS(context, sizeof(TSupSysEContext*));
    for (i = 0; i < 2; i++)
    {
	code = support_registry_get_string(paths[i], &length, NULL);
	if (code
	    && code != (DWORD)ERROR_FILE_NOT_FOUND
	    && code != (DWORD)ERROR_FILE_INVALID)
	    LOGRETURN(code);
	if (code)
	    continue;
	name = malloc((length + 1) * sizeof(TCHAR));
	if (name == NULL)
	    LOGRETURN((DWORD)NTE_NO_MEMORY);
	code = support_registry_get_string(RDR_DEFAULT_RDR, &length, name);
	if (code
	    && code != (DWORD)ERROR_FILE_INVALID
	    && code != (DWORD)ERROR_FILE_NOT_FOUND)
	{
	    free(name);
	    LOGRETURN(code);
	}
	if (code)
	{
	    free(name);
	    continue;
	}
	code = supsys_parse_internal_name(list, name, context);
	if (!code)
	{
	    free(name);
	    LOGRETURN(ERROR_SUCCESS);
	}
	free(name);
    }
    code = supsys_find_open(list, &ctx);
    if (code)
	LOGRETURN(code);
    code = supsys_find_next(ctx, context);
    supsys_find_close(ctx);
    if (code == (DWORD)SCARD_E_READER_UNAVAILABLE)
	LOGRETURN((DWORD)NTE_PROVIDER_DLL_FAIL);
    if (code)
	LOGRETURN(code);
    LOGRETURN(ERROR_SUCCESS);
}

DWORD rdr_default_internal_register_name(const TSupSysEContext *context, size_t *length, TCHAR *name, const TCHAR *ssconnect)
{
    LOGRETURN(support_default_internal_register_name(context, length, name, ssconnect));
}

DWORD rdr_context_default_set(const TSupSysEContext *context)
{
    DWORD code;
    TCHAR *str;
    TCHAR *connect = NULL;
    TSupSysNickname nickname;
    size_t length;

    code = supsys_nickname(context, nickname);
    if (code)
	LOGRETURN(code);
    code = supsys_connect_current(context, &length, NULL);
    if (code)
	LOGRETURN(code);
    if (length)
    {
	connect = malloc((length + 1) * sizeof(TCHAR));
	if (!connect)
	    LOGRETURN((DWORD)NTE_NO_MEMORY);
	code = supsys_connect_current(context, &length, connect);
	if (code)
	{
	    free(connect);
	    LOGRETURN(code);
	}
    }
    code = support_default_internal_register_name(context, &length, NULL,
	connect);
    if (code)
    {
	free(connect);
	LOGRETURN(code);
    }
    str = malloc((length + 1) * sizeof(TCHAR));
    if (str == NULL)
    {
	free(connect);
	LOGRETURN((DWORD)NTE_NO_MEMORY);
    }
    code = support_default_internal_register_name(context, &length, str,
	connect);
    free(connect);
    if (code)
    {
	free(str);
	LOGRETURN(code);
    }
    code = support_registry_put_string(RDR_DEFAULT_RDR, str);
    free(str);
    LOGRETURN(code);
}

DWORD rdr_lock(TSupSysEContext *context, const TProvCallCtx * carrier_ctx)
{
    DWORD code;
    code = supsys_call(context, READER_FUN_LOCK, (TSupSysInfo*)carrier_ctx);
    LOGRETURN(code == (DWORD)ERROR_NOT_SUPPORTED ? ERROR_SUCCESS : code);
}

void rdr_unlock(TSupSysEContext *context)
{
    DbTrace(DB_CALL, (FTEXT(db_ctx, "(...)")));
    supsys_call(context, READER_FUN_UNLOCK, NULL);
}

// Структура из одного поля, потому что кастить void* -> TSupSysNickname плохо.
typedef struct TSameCarrierTypeInfo_ {
    TSupSysNickname carrier_type;
} TSameCarrierTypeInfo;

static int is_same_carrier_type(const TSupSysFindInfo* add_info, const TSupSysEContext* item)
{
    TSupSysNickname carrier_type;
    TSameCarrierTypeInfo* inf = (TSameCarrierTypeInfo*)add_info;
    if (inf) {
	// Не все модули содержат нужную информацию - считыватель пусть сам решает, подходит это ему или нет.
	// Поэтому тут одна ошибка, а дальше - другая
	DWORD code = rdr_info_carrier_type(item, carrier_type);
	if (code) {
	    return READER_VALID_FUN_UNDEF;
	}
	if (_tcscmp(inf->carrier_type, carrier_type) != 0) {
	    return READER_VALID_FUN_WRONG;
	}
    }
    return READER_VALID_FUN_OK;
}

typedef enum TShouldFormat_ {
    TShouldFormat_FALSE = 0,
    TShouldFormat_TRUE = 1
} TShouldFormat;

static DWORD rdr_connect_carrier_ex(TSupSysEContext *context, TShouldFormat should_format, unsigned int * applet_count)
{
    TReaderInfoConnectCarrier info = { 0 };
    DWORD code;
    SUPSYS_PRE(applet_count);

    // Некоторые считыватели (pcsc/sable) являются посредниками между CSP и носителем
    // Поэтому им им передаем media_list. К сожалению, по историческим причинам все 
    // регистрации всех считывателей свалены в одно место (KeyCarriers). 
    // На это заложен cpanel, так что так и оставим, чтобы "старые" пользователи не пугались перемен.
    // Чтобы считыватели понимали, для них этот модуль или нет, передаём в помощь колбэк valid_fun.
    // Подразумевается, что считыватель устроит цикл по media_list и будет применять valid_fun(rdr_info, current_item)
    // к каждому из элементов.     
    TSameCarrierTypeInfo valid_info;
    code = rdr_info_carrier_type(context, valid_info.carrier_type);
    if (code == ERROR_SUCCESS) {
	info.valid_fun = &is_same_carrier_type;
	info.rdr_info = (void*)&valid_info;
    }
    info.flags = 0;
    if (should_format == TShouldFormat_TRUE) {
	info.flags |= READER_CONNECT_CARRIER_FLAG_FORMAT;
    }
    info.media_list = (TSupSysEList*)supsys_owner_context(context);
    info.applet_count = 1; //значение по умолчанию для тех, кто забыл установить в READER_FUN_CONNECT_CARRIER    
    code = supsys_call(context, READER_FUN_CONNECT_CARRIER, &info);
    if (code && (code != ERROR_NOT_SUPPORTED))
	LOGRETURN(code);

    if (info.applet_count > 1)
	*applet_count = info.applet_count;
    else
	*applet_count = 1;
    LOGRETURN(ERROR_SUCCESS);
}

DWORD rdr_connect_carrier(TSupSysEContext* context, unsigned int* applet_count)
{
    LOGRETURN(rdr_connect_carrier_ex(context, TShouldFormat_FALSE, applet_count));
}

DWORD rdr_format_carrier(TSupSysEContext* context, unsigned int* applet_count)
{
    LOGRETURN(rdr_connect_carrier_ex(context, TShouldFormat_TRUE, applet_count));
}

DWORD rdr_disconnect_carrier(
    TSupSysEContext *context)
{
    LOGRETURN(supsys_call(context, READER_FUN_DISCONNECT_CARRIER, NULL));
}

DWORD rdr_local_machine(
    TSupSysEContext *context,
    int local_machine)
{
    DWORD code;
    code = supsys_call(context, READER_FUN_LOCAL_MACHINE, &local_machine);
    if (code == (DWORD)ERROR_NOT_SUPPORTED)
	code = ERROR_SUCCESS;
    LOGRETURN(code);
}

DWORD rdr_select_applet(
    TSupSysEContext *context, unsigned applet_number)
{    
    DWORD code;
    code = supsys_call(context, READER_FUN_SET_APP, &applet_number);
    if( code == (DWORD)ERROR_NOT_SUPPORTED )
	code = ERROR_SUCCESS;
    LOGRETURN(code);
}

DWORD rdr_unique_get(
    const TSupSysEContext *context,
    size_t *length,
    TCHAR *unique) {
    TSupSysInfoText info_unqiue_ptr;
    DWORD code;
    if (!context || !length)
	LOGRETURN((DWORD)ERROR_INVALID_PARAMETER);

    info_unqiue_ptr.length = *length;
    info_unqiue_ptr.text = unique;
    code = supsys_call(context, READER_FUN_UNIQUE_GET, &info_unqiue_ptr);
    if (code)
	LOGRETURN(code);
    *length = info_unqiue_ptr.length;
    LOGRETURN(code);
}

DWORD rdr_get_user_defined_name(
    const TSupSysEContext *context,
    size_t *length,
    TCHAR *name) {
    TSupSysInfoText info_name_ptr;
    DWORD code;
    if (!context || !length)
	LOGRETURN((DWORD)ERROR_INVALID_PARAMETER);
    info_name_ptr.length = *length;
    info_name_ptr.text = name;
    code = supsys_call(context, READER_FUN_USER_DEFINED_NAME_GET, &info_name_ptr);
    if (code)
	LOGRETURN(code);
    if (info_name_ptr.length == 0)
	LOGRETURN((DWORD)ERROR_NOT_SUPPORTED);
    *length = info_name_ptr.length;
    LOGRETURN(code);
}

