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

/****
 * $Id: hvisdef_exp.c 153041 2017-02-17 06:18:28Z borodin $
 ****/
/*
 * \brief Реализация визуализатора по умолчанию.
 * Проект: визуализатора по умолчанию.
 * Константа: HVDEF_HANDLE
 * Функция: hvisdef_hvis_get_table, hvisdef_hvis
 */

#include "hvisdef_prj.h"
#include "Win32/hvisdef_rc.h"
#include "reader.kit/rdr_ver.h"
#include "reader/support_utf8.h"
#ifdef _WIN32
#include "reader/sup_wnd.h"
#endif /*_WIN32*/

#if defined( SUPPORT_RESOURCE_STD )
extern TSupResourceInstance HVDEF_RESOURCE;
#else
#define HVDEF_RESOURCE HVDEF_DLL_INSTANCE
#endif

#ifndef strcpy_s
#define strcpy_s(x,y,z) strcpy(x,z)
#endif //strcpy_s

#ifdef UNIX
#define _UN _empty_union_.
#else
#define _UN
#endif

#define NEW_LINE L"\r\n"
#define TAB_CHAR L"\t"
#define DOUBLE_TAB_CHAR L"\t\t"

static SUP_INLINE WCHAR * _2wcharstr(char * text)
{
    if (text) {
	size_t destlen = safe_utf8len_inwc(text);
	WCHAR * str = (WCHAR *)malloc(destlen * sizeof(WCHAR));
	if (!str)
	    return NULL;
	safe_utf8stowcs(str, text, destlen, strlen(text) + 1);
	return str;
    }
    return NULL;
}

static SUP_INLINE WCHAR * _2wcharstr_n(char * text_buf, int text_buf_len)
{
    if (text_buf) {
	size_t destlen = safe_utf8len_inwc_n(text_buf, text_buf_len);
	WCHAR * str = (WCHAR *)malloc(destlen * sizeof(WCHAR));
	if (!str)
	    return NULL;
	safe_utf8stowcs(str, text_buf, destlen, text_buf_len);
	return str;
    }
    return NULL;
}

static DWORD hvisdef_init(TSupSysContext *context, TSupSysInfo *info)
{
    THVisFunInit *init = (THVisFunInit*)info;
    THVDefContext *ctx = (THVDefContext*)context;

    SUPSYS_PRE_INFO(info, THVisFunInit);
    if (ctx->ctx_inited)
	return (DWORD)NTE_BAD_DATA;
    ctx->header = _2wcharstr(init->header);
    ctx->contname = _2wcharstr(init->name);
    ctx->language = init->language;
    ctx->parent = init->parent;
    ctx->ctx_inited = TRUE;
    return ERROR_SUCCESS;
}

#define MAX_SYMBOLS 400

DWORD hvisdef_wnd_string_local(WORD language, size_t num, WCHAR **str)
{
    size_t length;
    DWORD code;
    TCHAR * tmp = NULL;
    WCHAR * ret = NULL;
#ifdef _WIN32
#define support_resource_string_call(dest_string) support_resource_string_local(HVDEF_RESOURCE, language, num, dest_string, &length);
#else
    UNUSED(language);
#define support_resource_string_call(dest_string) support_resource_string(HVDEF_RESOURCE, num, dest_string, &length);
#endif

    ret = NULL;
    code = support_resource_string_call(NULL);
    if (code)
	return (DWORD)NTE_PROVIDER_DLL_FAIL;
    tmp = malloc((length + 1) * sizeof(TCHAR));
    if (tmp == NULL)
	return (DWORD)NTE_NO_MEMORY;
    ret = malloc((length + 1) * sizeof(WCHAR));
    if (ret == NULL) {
	free(tmp);
	return (DWORD)NTE_NO_MEMORY;
    }
    code = support_resource_string_call(tmp);
    if (code)
    {
	free(ret);
	free(tmp);
	return (DWORD)NTE_PROVIDER_DLL_FAIL;
    }
    if (!console_t2w(ret, tmp, length + 1)) {
	free(ret);
	free(tmp);
	return (DWORD)NTE_NO_MEMORY;
    }
    free(tmp);
    *str = ret;
    return S_OK;
}

static DWORD add_ttext(WCHAR ** start, WCHAR * middle, WCHAR *second)
{
    WCHAR * tmp = NULL;
    size_t length = 0;
    if (*start)
	length = wcslen(*start);
    if (middle)
	length += wcslen(middle);
    if (second)
	length += wcslen(second);
    if (length > 0) {
	length++;
	tmp = malloc(length * sizeof(WCHAR));
	if (!tmp)
	    return (DWORD)NTE_NO_MEMORY;
	if (*start) {
	    wcscpy(tmp, *start);
	}
	else {
	    if (second || middle)
		tmp[0] = _TEXT('\0');
	}
	if (middle) {
	    wcscat(tmp, middle);
	}
	if (second) {
	    wcscat(tmp, second);
	}
    }
    free(*start);
    *start = tmp;
    return ERROR_SUCCESS;
}

static DWORD add_ctext(WCHAR ** start, WCHAR * middle, char *second, size_t sec_length)
{
    WCHAR * tmp = NULL;
    size_t length = 0;
    if (*start)
	length = wcslen(*start);
    if (middle)
	length += wcslen(middle);
    if (second && sec_length)
	length += strnlen(second, sec_length);
    if (length > 0) {
	length++;
	tmp = malloc(length * sizeof(WCHAR));
	if (!tmp)
	    return (DWORD)NTE_NO_MEMORY;
	if (*start) {
	    wcscpy(tmp, *start);
	}
	else {
	    if ((second && sec_length) || middle)
		tmp[0] = L'\0';
	}
	if (middle) {
	    wcscat(tmp, middle);
	}
	if (second && sec_length) {
	    size_t buffer_len = strnlen(second, sec_length);
	    WCHAR *tsec = (WCHAR *)calloc(buffer_len + 1, sizeof(WCHAR));
	    if (!tsec) {
		free(tmp);
		return (DWORD)NTE_NO_MEMORY;
	    }
	    MultiByteToWideChar(CPRO_CONSOLE_CP, 0, second, (int)sec_length, tsec, (int)buffer_len);
	    wcscat(tmp, tsec);
	    free(tsec);
	}
    }
    free(*start);
    *start = tmp;
    return ERROR_SUCCESS;
}

static const BYTE SimpleTestString[] = {
    0x30, 0x16, 0x04, 0x0a, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x50, 0x72,
    0x6f, 0x31, 0x04, 0x08, 0xa2, 0x2f, 0x6f, 0xfe, 0xe5, 0xe4, 0x9a, 0xff
};

static DWORD hvisdef_internal_cryptopro_test(THVDefContext *ctx, PHS_SIGNING_DATA signing_data, PHS_ADDITIONAL_DATA additional_data)
{
    WCHAR * tmpstr = NULL;

    if (additional_data == NULL && signing_data != NULL) {
	if (signing_data->cbData == sizeof(SimpleTestString) && !memcmp(SimpleTestString, signing_data->pbData, signing_data->cbData)) {
	    DWORD err = hvisdef_wnd_string_local(ctx->language, IDS_INTERNAL_CP_TEST, &tmpstr);
	    if (err)
		return err;
	    free(ctx->frame_header);
	    ctx->frame_header = tmpstr;
	    free(ctx->window_str);
	    ctx->window_str = NULL;
	    return ERROR_SUCCESS;
	}
    }
    return ERROR_BAD_FORMAT;
}

static DWORD hvisdef_internal_null_data(THVDefContext *ctx, PHS_SIGNING_DATA signing_data, PHS_ADDITIONAL_DATA additional_data)
{
    WCHAR * tmpstr = NULL;

    if (additional_data == NULL && signing_data == NULL) {
	DWORD err = hvisdef_wnd_string_local(ctx->language, IDS_HASH_UNKNOWN, &tmpstr);
	if (err)
	    return err;
	free(ctx->frame_header);
	ctx->frame_header = tmpstr;
	free(ctx->window_str);
	ctx->window_str = NULL;
	return ERROR_SUCCESS;
    }
    return ERROR_BAD_FORMAT;
}

static DWORD hvisdef_internal_unknown_additional(THVDefContext *ctx, PHS_SIGNING_DATA signing_data, PHS_ADDITIONAL_DATA additional_data)
{
    if (additional_data != NULL && signing_data != NULL) {
	WCHAR * tmp_header = NULL;
	WCHAR * tmpstr = NULL;
	DWORD err;
	size_t local_dw_data;
	size_t pos = 0;

	err = hvisdef_wnd_string_local(ctx->language, IDS_UNKNOWN_WRAPPED_DATA, &tmp_header);
	if (err)
	    return err;
	local_dw_data = additional_data->documents[0].cbData;
	while (local_dw_data > 0) {
	    size_t delta = strnlen((char*)additional_data->documents[0].pbData + pos, local_dw_data);
	    err = add_ctext(&tmpstr, L" ", (char*)additional_data->documents[0].pbData + pos, delta);
	    if (err) {
		free(tmpstr);
		free(tmp_header);
		return err;
	    }
	    pos += delta + 1;
	    if (delta > 0) {
		if (local_dw_data > delta)
		    local_dw_data -= (delta + 1);
		else
		    local_dw_data = 0;
	    }
	    else
		local_dw_data--;
	}

	free(ctx->frame_header);
	ctx->frame_header = tmp_header;

	free(ctx->window_str);
	ctx->window_str = tmpstr;
	return ERROR_SUCCESS;
    }
    return ERROR_BAD_FORMAT;
}

static DWORD hvisdef_internal_unknown(THVDefContext *ctx, PHS_SIGNING_DATA signing_data, PHS_ADDITIONAL_DATA additional_data)
{
    if (signing_data != NULL && additional_data == NULL) {
	WCHAR * tmp_header = NULL;
	WCHAR * tmpstr = NULL;
	DWORD err;
	size_t local_dw_data;
	size_t pos = 0;

	err = hvisdef_wnd_string_local(ctx->language, IDS_UNKNOWN_DATA, &tmp_header);
	if (err)
	    return err;
	local_dw_data = signing_data->cbData;
	while (local_dw_data > 0) {
	    size_t delta = strnlen((char*)signing_data->pbData + pos, local_dw_data);
	    err = add_ctext(&tmpstr, L" ", (char*)signing_data->pbData + pos, delta);
	    if (err) {
		free(tmpstr);
		free(tmp_header);
		return err;
	    }
	    pos += delta + 1;
	    if (delta > 0) {
		if (local_dw_data > delta)
		    local_dw_data -= (delta + 1);
		else
		    local_dw_data = 0;
	    }
	    else
		local_dw_data--;
	}

	free(ctx->frame_header);
	ctx->frame_header = tmp_header;

	free(ctx->window_str);
	ctx->window_str = tmpstr;
	return ERROR_SUCCESS;
    }
    return ERROR_BAD_FORMAT;
}

static DWORD print_FILETIME(FILETIME * file_time, WCHAR ** time_str)
{
    SYSTEMTIME time_;
    char c_data[256];
    WCHAR * res = NULL;
    if (!FileTimeToSystemTime(file_time, &time_))
	return ERROR_BAD_FORMAT;
    sprintf(c_data, "%02d/%02d/%d  %02d:%02d:%02d", time_.wDay, time_.wMonth, time_.wYear, time_.wHour, time_.wMinute, time_.wSecond);
    res = malloc(sizeof(WCHAR)*(strlen(c_data) + 1));
    if (!res)
	return (DWORD)NTE_NO_MEMORY;
    _2u(res, c_data);
    *time_str = res;
    return ERROR_SUCCESS;
}

static DWORD printHexString(LPBYTE str, DWORD str_len, DWORD space_period, WCHAR ** time_str)
{
    DWORD length = str_len * 2 + (space_period ? str_len / space_period : 0) + 1;
    DWORD i;
    WCHAR * res = malloc((length + 1) * sizeof(WCHAR));
    DWORD space = 0;
    if (!res)
	return (DWORD)NTE_NO_MEMORY;
    res[0] = L'\0';

    for (i = 0; i < str_len; i++) {
	char buffer[3];
	sprintf(buffer, "%02x", str[i]);
	_2u(res + wcslen(res), buffer);
	space++;
	if (space == space_period) {
	    wcscat(res, L" ");
	    space = 0;
	}
    }
    *time_str = res;
    return ERROR_SUCCESS;
}

static DWORD hvisdef_internal_request_cert(THVDefContext *ctx, PHS_SIGNING_DATA signing_data, PHS_ADDITIONAL_DATA additional_data)
{
    WCHAR * tmpstr = NULL;
    WCHAR * tmp_header = NULL;
    PHS_SIGNING_DATA data = NULL;
    DWORD err = ERROR_BAD_FORMAT;
    CERT_REQUEST_INFO * info = NULL;
    DWORD info_len = 0;
    DWORD tmp_len;
    WCHAR * tmpbuffer = NULL;


    if (additional_data == NULL && signing_data != NULL)
	data = signing_data;
    else if (additional_data != NULL && signing_data != NULL)
	data = &additional_data->documents[0];
    else
	return err;
    if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_CERT_REQUEST_TO_BE_SIGNED, data->pbData, data->cbData, 0, NULL, &info_len))
	goto done;
    info = malloc(info_len);
    if (!info)
	return (DWORD)NTE_NO_MEMORY;
    if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_CERT_REQUEST_TO_BE_SIGNED, data->pbData, data->cbData, 0, (void*)info, &info_len))
	goto done;

    if (additional_data) {
	err = hvisdef_wnd_string_local(ctx->language, IDS_WRAPPED_REQUEST_CERT, &tmp_header);
	if (err)
	    goto done;
    }
    else {
	err = hvisdef_wnd_string_local(ctx->language, IDS_REQUEST_CERT, &tmp_header);
	if (err)
	    goto done;
    }

    err = hvisdef_wnd_string_local(ctx->language, IDS_SUBJECT, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, NULL, tmpbuffer);
    if (err)
	goto done;
    free(tmpbuffer);
    tmpbuffer = NULL;

    tmp_len = CertNameToStrW(X509_ASN_ENCODING, &info->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
    if (tmp_len == 0) {
	err = (DWORD)ERROR_BAD_FORMAT;
	goto done;
    }
    tmpbuffer = malloc(sizeof(WCHAR)*tmp_len);
    if (!tmpbuffer) {
	err = (DWORD)NTE_NO_MEMORY;
	goto done;
    }
    tmp_len = CertNameToStrW(X509_ASN_ENCODING, &info->Subject, CERT_SIMPLE_NAME_STR, tmpbuffer, tmp_len);
    if (tmp_len == 0) {
	err = (DWORD)ERROR_BAD_FORMAT;
	goto done;
    }
    err = add_ttext(&tmpstr, DOUBLE_TAB_CHAR, tmpbuffer);
    free(tmpbuffer);
    tmpbuffer = NULL;
    if (err)
	goto done;

    free(ctx->frame_header);
    ctx->frame_header = tmp_header;
    tmp_header = NULL;
    free(ctx->window_str);
    ctx->window_str = tmpstr;
    tmpstr = NULL;
done:
    free(info);
    free(tmpbuffer);
    free(tmpstr);
    free(tmp_header);
    return err;
}

static DWORD print_additional_names(THVDefContext *ctx, CERT_INFO * info, size_t strID, const char * objID, WCHAR **modified_str)
{
    DWORD err = ERROR_SUCCESS;
    WCHAR * tmpbuffer = NULL;
    DWORD alt_names_j;
    WCHAR *tmpstr = *modified_str;
    DWORD cert_it = 0;
    PCERT_ALT_NAME_INFO alt_info = NULL;
    DWORD alt_info_len = 0;

    //Ищем SAN
    for (cert_it = 0; cert_it < info->cExtension; cert_it++) {
	if (info->rgExtension[cert_it].pszObjId != NULL && !strcmp(objID, info->rgExtension[cert_it].pszObjId)) {
	    if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_ALTERNATE_NAME,
		info->rgExtension[cert_it].Value.pbData, info->rgExtension[cert_it].Value.cbData, 0, NULL, &alt_info_len))
	    {
		err = ERROR_BAD_FORMAT;
		goto done;
	    }
	    alt_info = malloc(alt_info_len);
	    if (!alt_info) {
		err = (DWORD)NTE_NO_MEMORY;
		goto done;
	    }
	    if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_ALTERNATE_NAME,
		info->rgExtension[cert_it].Value.pbData, info->rgExtension[cert_it].Value.cbData, 0, (void*)alt_info, &alt_info_len)) {
		err = ERROR_BAD_FORMAT;
		goto done;
	    }
	    err = hvisdef_wnd_string_local(ctx->language, strID, &tmpbuffer);
	    if (err)
		goto done;
	    err = add_ttext(&tmpstr, NEW_LINE, tmpbuffer);
	    if (err)
		goto done;
	    free(tmpbuffer);
	    tmpbuffer = NULL;

	    for (alt_names_j = 0; alt_names_j < alt_info->cAltEntry; alt_names_j++) {
		switch (alt_info->rgAltEntry[alt_names_j].dwAltNameChoice) {
		case CERT_ALT_NAME_OTHER_NAME:
		    tmpbuffer = _2wcharstr(alt_info->rgAltEntry[alt_names_j]._UN pOtherName->pszObjId);
		    if (!tmpbuffer) {
			err = ERROR_BAD_FORMAT;
			goto done;
		    }
		    err = add_ttext(&tmpstr, NEW_LINE TAB_CHAR L"otherName OID = ", tmpbuffer);
		    free(tmpbuffer);
		    tmpbuffer = NULL;
		    if (err)
			goto done;
		    break;

		case CERT_ALT_NAME_RFC822_NAME:
		    err = add_ttext(&tmpstr, NEW_LINE TAB_CHAR L"rfc822Name = ", alt_info->rgAltEntry[alt_names_j]._UN pwszRfc822Name);
		    if (err)
			goto done;
		    break;

		case CERT_ALT_NAME_DNS_NAME:
		    err = add_ttext(&tmpstr, NEW_LINE TAB_CHAR L"dNSName = ", alt_info->rgAltEntry[alt_names_j]._UN pwszDNSName);
		    if (err)
			goto done;
		    break;

		case CERT_ALT_NAME_URL:
		    err = add_ttext(&tmpstr, NEW_LINE TAB_CHAR L"URL = ", alt_info->rgAltEntry[alt_names_j]._UN pwszURL);
		    if (err)
			goto done;
		    break;

		case CERT_ALT_NAME_REGISTERED_ID:
		    tmpbuffer = _2wcharstr(alt_info->rgAltEntry[alt_names_j]._UN pszRegisteredID);
		    if (!tmpbuffer) {
			err = ERROR_BAD_FORMAT;
			goto done;
		    }
		    err = add_ttext(&tmpstr, NEW_LINE TAB_CHAR L"registeredID = ", tmpbuffer);
		    free(tmpbuffer);
		    tmpbuffer = NULL;
		    if (err)
			goto done;
		    break;

		case CERT_ALT_NAME_IP_ADDRESS:
		{
		    char buf[40];
		    if (alt_info->rgAltEntry[alt_names_j]._UN IPAddress.cbData == 4) { /*IPv4*/
			sprintf(buf, "%u.%u.%u.%u", alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[0], alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[1],
			    alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[2], alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[3]);
			tmpbuffer = _2wcharstr(buf);
			if (!tmpbuffer) {
			    err = ERROR_BAD_FORMAT;
			    goto done;
			}
			err = add_ttext(&tmpstr, NEW_LINE TAB_CHAR L"iPv4 address = ", tmpbuffer);
			free(tmpbuffer);
			tmpbuffer = NULL;
			if (err)
			    goto done;
		    }
		    else if (alt_info->rgAltEntry[alt_names_j]._UN IPAddress.cbData == 16) { /*IPv6*/
			sprintf(buf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
			    alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[0], alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[1],
			    alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[2], alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[3],
			    alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[4], alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[5],
			    alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[6], alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[7],
			    alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[8], alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[9],
			    alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[10], alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[11],
			    alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[12], alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[13],
			    alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[14], alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData[15]);
			tmpbuffer = _2wcharstr(buf);
			if (!tmpbuffer) {
			    err = ERROR_BAD_FORMAT;
			    goto done;
			}
			err = add_ttext(&tmpstr, NEW_LINE TAB_CHAR L"iPv6 address = ", tmpbuffer);
			free(tmpbuffer);
			tmpbuffer = NULL;
			if (err)
			    goto done;
		    }
		    else { /*считаем строчкой*/
			tmpbuffer = _2wcharstr_n((char*)alt_info->rgAltEntry[alt_names_j]._UN IPAddress.pbData, (int)alt_info->rgAltEntry[alt_names_j]._UN IPAddress.cbData);
			if (!tmpbuffer) {
			    err = ERROR_BAD_FORMAT;
			    goto done;
			}
			err = add_ttext(&tmpstr, NEW_LINE TAB_CHAR L"iPAddress = ", tmpbuffer);
			free(tmpbuffer);
			tmpbuffer = NULL;
			if (err)
			    goto done;
		    }
		}
		break;
		case CERT_ALT_NAME_X400_ADDRESS:
		    err = add_ttext(&tmpstr, NEW_LINE TAB_CHAR, L"ALTNAME=<X400_ADDRESS>");
		    if (err)
			goto done;
		    break;
		case CERT_ALT_NAME_DIRECTORY_NAME:
		    err = add_ttext(&tmpstr, NEW_LINE TAB_CHAR, L"ALTNAME=<DIRECTORY_NAME>");
		    if (err)
			goto done;
		    break;
		case CERT_ALT_NAME_EDI_PARTY_NAME:
		    err = add_ttext(&tmpstr, NEW_LINE TAB_CHAR, L"ALTNAME=<EDI_PARTY_NAME>");
		    if (err)
			goto done;
		    break;
		default:
		    err = add_ttext(&tmpstr, NEW_LINE TAB_CHAR, L"unknown AltName");
		    if (err)
			goto done;
		    break;
		}
	    }
	}
    }
done:
    free(alt_info);
    free(tmpbuffer);
    *modified_str = tmpstr;
    return err;
}

static DWORD hvisdef_internal_decode_cert(THVDefContext *ctx, PHS_SIGNING_DATA signing_data, PHS_ADDITIONAL_DATA additional_data)
{
    WCHAR * tmpstr = NULL;
    WCHAR * tmp_header = NULL;
    PHS_SIGNING_DATA data = NULL;
    DWORD err = ERROR_BAD_FORMAT;
    CERT_INFO * info = NULL;
    DWORD info_len = 0;
    DWORD tmp_len;
    WCHAR * tmpbuffer = NULL;

    if (additional_data == NULL && signing_data != NULL)
	data = signing_data;
    else if (additional_data != NULL && signing_data != NULL)
	data = &additional_data->documents[0];
    else
	return err;
    if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, data->pbData, data->cbData, 0, NULL, &info_len))
	goto done;
    info = malloc(info_len);
    if (!info)
	return (DWORD)NTE_NO_MEMORY;
    if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, data->pbData, data->cbData, 0, (void*)info, &info_len)) 
	goto done;
    
    if (additional_data) {
	err = hvisdef_wnd_string_local(ctx->language, IDS_WRAPPED_CERTIFICATE, &tmp_header);
	if (err)
	    goto done;
    }
    else {
	err = hvisdef_wnd_string_local(ctx->language, IDS_CERTIFICATE, &tmp_header);
	if (err)
	    goto done;
    }

    err = hvisdef_wnd_string_local(ctx->language, IDS_SUBJECT, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, NULL, tmpbuffer);
    if (err)
	goto done;
    free(tmpbuffer);
    tmpbuffer = NULL;
    
    tmp_len = CertNameToStrW(X509_ASN_ENCODING, &info->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
    if (tmp_len == 0) {
	err = (DWORD)ERROR_BAD_FORMAT;
	goto done;
    }
    tmpbuffer = malloc(sizeof(WCHAR)*tmp_len);
    if (!tmpbuffer) {
	err = (DWORD)NTE_NO_MEMORY;
	goto done;
    }
    tmp_len = CertNameToStrW(X509_ASN_ENCODING, &info->Subject, CERT_SIMPLE_NAME_STR, tmpbuffer, tmp_len);
    if (tmp_len == 0) {
	err = (DWORD)ERROR_BAD_FORMAT;
	goto done;
    }
    err = add_ttext(&tmpstr, DOUBLE_TAB_CHAR, tmpbuffer);
    free(tmpbuffer);
    tmpbuffer = NULL;
    if (err)
	goto done;
    err = print_additional_names(ctx, info, IDS_SAN, szOID_SUBJECT_ALT_NAME2, &tmpstr);
    if (err)
	goto done;
    err = hvisdef_wnd_string_local(ctx->language, IDS_ISSUER, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, NEW_LINE, tmpbuffer);
    if (err)
	goto done;
    free(tmpbuffer);
    tmpbuffer = NULL;
    err = print_additional_names(ctx, info, IDS_IAN, szOID_ISSUER_ALT_NAME2, &tmpstr);
    if (err)
	goto done;
    tmp_len = CertNameToStrW(X509_ASN_ENCODING, &info->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
    if (tmp_len == 0) {
	err = (DWORD)ERROR_BAD_FORMAT;
	goto done;
    }
    tmpbuffer = malloc(sizeof(WCHAR)*tmp_len);
    if (!tmpbuffer) {
	err = (DWORD)NTE_NO_MEMORY;
	goto done;
    }
    tmp_len = CertNameToStrW(X509_ASN_ENCODING, &info->Issuer, CERT_SIMPLE_NAME_STR, tmpbuffer, tmp_len);
    if (tmp_len == 0) {
	err = (DWORD)ERROR_BAD_FORMAT;
	goto done;
    }
    err = add_ttext(&tmpstr, TAB_CHAR, tmpbuffer);
    free(tmpbuffer);
    tmpbuffer = NULL;

    err = hvisdef_wnd_string_local(ctx->language, IDS_VALID_FROM, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, NEW_LINE, tmpbuffer);
    if (err)
	goto done;
    free(tmpbuffer);
    tmpbuffer = NULL;

    err = print_FILETIME(&info->NotBefore, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, TAB_CHAR, tmpbuffer);
    free(tmpbuffer);
    tmpbuffer = NULL;

    err = hvisdef_wnd_string_local(ctx->language, IDS_VALID_TO, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, NEW_LINE, tmpbuffer);
    if (err)
	goto done;
    free(tmpbuffer);
    tmpbuffer = NULL;

    err = print_FILETIME(&info->NotAfter, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, TAB_CHAR, tmpbuffer);
    free(tmpbuffer);
    tmpbuffer = NULL;

    err = hvisdef_wnd_string_local(ctx->language, IDS_SERIAL_NUMBER, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, NEW_LINE, tmpbuffer);
    if (err)
	goto done;
    free(tmpbuffer);
    tmpbuffer = NULL;

    err = printHexString(info->SerialNumber.pbData, info->SerialNumber.cbData, 2, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, TAB_CHAR, tmpbuffer);
    free(tmpbuffer);
    tmpbuffer = NULL;

    free(ctx->frame_header);
    ctx->frame_header = tmp_header;
    tmp_header = NULL;
    free(ctx->window_str);
    ctx->window_str = tmpstr;
    tmpstr = NULL;
    err = ERROR_SUCCESS;
done: 
    free(info);
    free(tmpbuffer);
    free(tmpstr);
    free(tmp_header);
    return err;
}

static DWORD hvisdef_internal_decode_crl(THVDefContext *ctx, PHS_SIGNING_DATA signing_data, PHS_ADDITIONAL_DATA additional_data)
{
    WCHAR * tmpstr = NULL;
    WCHAR * tmp_header = NULL;
    PHS_SIGNING_DATA data = NULL;
    DWORD err = ERROR_BAD_FORMAT;
    CRL_INFO * info = NULL;
    DWORD info_len = 0;
    DWORD tmp_len;
    WCHAR * tmpbuffer = NULL;
    DWORD i;


    if (additional_data == NULL && signing_data != NULL)
	data = signing_data;
    else if (additional_data != NULL && signing_data != NULL)
	data = &additional_data->documents[0];
    else
	return err;
    if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_CERT_CRL_TO_BE_SIGNED, data->pbData, data->cbData, 0, NULL, &info_len))
	goto done;
    info = malloc(info_len);
    if (!info)
	return (DWORD)NTE_NO_MEMORY;
    if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_CERT_CRL_TO_BE_SIGNED, data->pbData, data->cbData, 0, (void*)info, &info_len)) 
	goto done;

    if (additional_data) {
	err = hvisdef_wnd_string_local(ctx->language, IDS_WRAPPED_CERTIFICATE, &tmp_header);
	if (err)
	    goto done;
    }
    else {
	err = hvisdef_wnd_string_local(ctx->language, IDS_CERTIFICATE, &tmp_header);
	if (err)
	    goto done;
    }

    err = hvisdef_wnd_string_local(ctx->language, IDS_SUBJECT, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, NULL, tmpbuffer);
    if (err)
	goto done;
    free(tmpbuffer);
    tmpbuffer = NULL;

    err = hvisdef_wnd_string_local(ctx->language, IDS_ISSUER, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, NULL, tmpbuffer);
    if (err)
	goto done;
    free(tmpbuffer);
    tmpbuffer = NULL;

    tmp_len = CertNameToStrW(X509_ASN_ENCODING, &info->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
    if (tmp_len == 0) {
	err = (DWORD)ERROR_BAD_FORMAT;
	goto done;
    }
    tmpbuffer = malloc(sizeof(WCHAR)*tmp_len);
    if (!tmpbuffer) {
	err = (DWORD)NTE_NO_MEMORY;
	goto done;
    }
    tmp_len = CertNameToStrW(X509_ASN_ENCODING, &info->Issuer, CERT_SIMPLE_NAME_STR, tmpbuffer, tmp_len);
    if (tmp_len == 0) {
	err = (DWORD)ERROR_BAD_FORMAT;
	goto done;
    }
    err = add_ttext(&tmpstr, TAB_CHAR, tmpbuffer);
    free(tmpbuffer);
    tmpbuffer = NULL;

    err = hvisdef_wnd_string_local(ctx->language, IDS_THIS_UPDATE, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, NEW_LINE, tmpbuffer);
    if (err)
	goto done;
    free(tmpbuffer);
    tmpbuffer = NULL;

    err = print_FILETIME(&info->ThisUpdate, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, TAB_CHAR, tmpbuffer);
    free(tmpbuffer);
    tmpbuffer = NULL;

    err = hvisdef_wnd_string_local(ctx->language, IDS_NEXT_UPDATE, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, NEW_LINE, tmpbuffer);
    if (err)
	goto done;
    free(tmpbuffer);
    tmpbuffer = NULL;

    err = print_FILETIME(&info->NextUpdate, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, TAB_CHAR, tmpbuffer);
    free(tmpbuffer);
    tmpbuffer = NULL;

    err = hvisdef_wnd_string_local(ctx->language, IDS_REV_NUMBERS, &tmpbuffer);
    if (err)
	goto done;
    err = add_ttext(&tmpstr, NEW_LINE, tmpbuffer);
    if (err)
	goto done;
    free(tmpbuffer);
    tmpbuffer = NULL;
    err = add_ttext(&tmpstr, NULL, NEW_LINE);
    if (err)
	goto done;
    for (i = 0; i < info->cCRLEntry; i++) {
	err = printHexString(info->rgCRLEntry[i].SerialNumber.pbData, info->rgCRLEntry[i].SerialNumber.cbData, 2, &tmpbuffer);
	if (err)
	    goto done;
	err = add_ttext(&tmpstr, TAB_CHAR, tmpbuffer);
	if (err)
	    goto done;
	free(tmpbuffer);
	tmpbuffer = NULL;
	err = add_ttext(&tmpstr, NULL, NEW_LINE);
	if (err)
	    goto done;
    }

    free(ctx->frame_header);
    ctx->frame_header = tmp_header;
    tmp_header = NULL;
    free(ctx->window_str);
    ctx->window_str = tmpstr;
    tmpstr = NULL;
    err = ERROR_SUCCESS;
done:
    free(info);
    free(tmpbuffer);
    free(tmpstr);
    free(tmp_header);
    return err;
}


#define n2s(c,s)	((s=(((unsigned int)(c[0]))<< 8)| \
			    (((unsigned int)(c[1]))    )),c+=2)

#define n2l3(c,l)	((l =(((unsigned long)(c[0]))<<16)| \
			     (((unsigned long)(c[1]))<< 8)| \
			     (((unsigned long)(c[2]))    )),c+=3)

#include "./ssl3.h"

static DWORD hvisdef_internal_decode_client_hello(THVDefContext *ctx, PHS_SIGNING_DATA signing_data, PHS_ADDITIONAL_DATA additional_data)
{
    WCHAR * tmp_header = NULL;
    HS_SIGNING_DATA data = { 0 };
    PBYTE p = NULL, fin = NULL;
    BYTE session_id_len = 0;
    int cbHandshake = 0, 
	cbSuites = 0, 
	cbCompression = 0,
	cbCertificates = 0,
	cbCertificate = 0;
    DWORD err = ERROR_BAD_FORMAT;
    
    if (additional_data == NULL && signing_data != NULL)
	data = *signing_data;
    else if (additional_data != NULL && signing_data != NULL)
	data = additional_data->documents[0];
    else
	return err;

    p = data.pbData;
    
 //   if (fin - p < 3) {
	////DbTrace(DB_TRACE, (FTEXT(db_ctx, " Incomplete TLS header")));
	//goto done;
 //   }
 //   if (*p++ != SSL3_RT_HANDSHAKE)
	//goto done;
 //   if (*p++ != SSL3_VERSION_MAJOR)
	//goto done;
 //   if (*p++ > 3)
	//goto done;
 //   if (fin - p < 2)
	//goto done;    
 //   n2s(p, cbRec);
 //   if (fin - p < cbRec)
	//goto done;

    /* no record layer*/

    /* get Client Hello  */
    if (data.cbData < 5 )
	goto done;

    if (*p++ != SSL3_MT_CLIENT_HELLO)
	goto done;    
    n2l3(p, cbHandshake);

    if ((DWORD)cbHandshake > data.cbData - 5)
	goto done;

    fin = p + cbHandshake;

    if (fin - p < 2)
	goto done;
    if (*p++ != SSL3_VERSION_MAJOR)
	goto done;
    if (*p++ > 3)
	goto done;
    
    /* get client random */
    if (SSL3_RANDOM_SIZE > fin - p)
    {
	goto done;
    }
    p += SSL3_RANDOM_SIZE;
    /* get session-id */
    if (1 > fin - p)
    {
	goto done;
    }
    session_id_len = *p++;
    if (session_id_len > SSL3_SSL_SESSION_ID_LENGTH)
    {
	goto done;
    }
    if (session_id_len > fin - p)
    {
	goto done;
    }
    p += session_id_len;
    
    /* get cipher suites */
    if (2 > fin - p)
    {
	goto done;
    }
    n2s(p, cbSuites);
    p += cbSuites;

    /* compression */
    if (1 > fin - p)
    {	
	goto done;
    }    
    cbCompression = *p++;
    if (cbCompression > fin - p)
    {
	goto done;
    }
    p += cbCompression;

    /* extensions */
    if (p < fin)
    {
	int cbExtensions = 0;
	if (2 > fin - p)
	{
	    goto done;
	}
	n2s(p, cbExtensions);
	/* we can find SNI extension, but it is absent in renegotiation CH */
	p += cbExtensions;	
    }
    if (p != fin)
    {
	goto done;
    }
    /* Client Hello end */
    data.cbData -= (DWORD)(p - data.pbData);
    data.pbData = p;
    /* get Server Hello */
    if (data.cbData < 5)
	goto done;

    if (*p++ != SSL3_MT_SERVER_HELLO)
	goto done;
    n2l3(p, cbHandshake);

    if ((DWORD)cbHandshake > data.cbData - 5)
	goto done;

    fin = p + cbHandshake;
    /* skip */
    p += cbHandshake;
    
    if (p != fin)
    {
	goto done;
    }
    /* Server Hello end */
    data.cbData -= (DWORD)(p - data.pbData);
    data.pbData = p;
    /* get Server Certificate */
    if (data.cbData < 3)
	goto done;
    if (*p++ != SSL3_MT_CERTIFICATE)
	goto done;
    n2l3(p, cbHandshake);

    if ((DWORD)cbHandshake > data.cbData - 3)
	goto done;
    fin = p + cbHandshake;

    if (3 > fin - p)
    {
	goto done;
    }
    n2l3(p, cbCertificates);
    if (cbCertificates && cbCertificates == fin - p)
    {
	n2l3(p, cbCertificate);
	if (cbCertificate && cbCertificate < cbCertificates)
	{
	    /* get certificate data and display first certificte */
	    data.pbData = p;
	    data.cbData = cbCertificate;
	    err = hvisdef_internal_decode_cert(ctx, &data, NULL);
	    /* replace text */
	    if (err == ERROR_SUCCESS)
	    {
		err = hvisdef_wnd_string_local(ctx->language, IDS_AUTHENTICATION, &tmp_header);
		if (err)
		    goto done;
		free(ctx->frame_header);
		ctx->frame_header = tmp_header;
		tmp_header = NULL;
	    }
	}
    }
    /* Server Certificate end */
done:
    free(tmp_header);
    return err;
}

typedef DWORD (*hvis_decoder) (THVDefContext *ctx, PHS_SIGNING_DATA signing_data, PHS_ADDITIONAL_DATA additional_data);

const hvis_decoder ALL_DECODERS[] = { hvisdef_internal_null_data , hvisdef_internal_cryptopro_test, 
				      hvisdef_internal_decode_cert, hvisdef_internal_request_cert, hvisdef_internal_decode_crl, hvisdef_internal_decode_client_hello,
					/* Два последних обработчика - "неизвестные данные". Умные обработчики добавляются до них. */
					hvisdef_internal_unknown_additional, hvisdef_internal_unknown };
const size_t ALL_DECODERS_LENGTH = sizeof(ALL_DECODERS) / sizeof(hvis_decoder);

static DWORD hvisdef_universal_translate_data(THVDefContext *ctx, PHS_SIGNING_DATA signing_data, PHS_ADDITIONAL_DATA additional_data) 
{
    DWORD err = ERROR_BAD_FORMAT;
    size_t i;

    /* некорректные данные сразу отметем */
    if (signing_data && (signing_data->cbData == 0 || signing_data->pbData == NULL)) 
	return err;
    if (additional_data) {
	if (additional_data->number == 0)
	    return err;
	for (i = 0; i < additional_data->number; i++) {
	    if (additional_data->documents[i].cbData == 0 || additional_data->documents[i].pbData == NULL) 
		return err;
	}
    }

    for (i = 0; i < ALL_DECODERS_LENGTH; i++) {
	err = ALL_DECODERS[i](ctx, signing_data, additional_data);
	if (err != ERROR_BAD_FORMAT)
	    break;
    }
    return err;
}

static DWORD hvisdef_ask(TSupSysContext *context, TSupSysInfo *info)
{
    THVDefContext *ctx = (THVDefContext*)context;
    THvisAskInfo * inf = (THvisAskInfo*)info;
    DWORD err;
    if (!ctx || !inf)
	return (DWORD)ERROR_INVALID_PARAMETER;

    if (inf->silent)
	return (DWORD)NTE_SILENT_CONTEXT;
    if (!ctx->ctx_inited)
	return (DWORD)NTE_BAD_DATA;
    err = hvisdef_universal_translate_data(ctx, inf->signing_data, inf->additional_data);
    if (err)
	return (DWORD)NTE_BAD_DATA;
    return hvisdef_display_question(ctx);
}

/* Функция получения instance. */
static DWORD hvisdef_instance(TSupSysContext *context, TSupSysInfo *info )
{
    TSupResourceInstance *inf = (TSupResourceInstance*)info;

    UNUSED(context);
    SUPSYS_PRE_INFO(info, TSupResourceInstance);
    *inf = HVDEF_RESOURCE;
    return ERROR_SUCCESS;
}
/*функция получения прозвища*/
static DWORD hvisdef_info_nickname(
    TSupSysContext *context, TSupSysInfo *info)
{
    TSupSysInfoNickname *inf = (TSupSysInfoNickname*)info;
    UNUSED(context);
    _tcscpy(inf->nickname, _TEXT("HVISDEF"));
    return ERROR_SUCCESS;
}

static DWORD hvisdef_register(TSupSysContext *context, TSupSysInfo *info)
{
    TSupSysInfoRegister *inf = (TSupSysInfoRegister*)info;
    THVDefContext *ctx = NULL;

    UNUSED(context);
    SUPSYS_PRE_INFO(info, TSupSysInfoRegister);

    support_set_library(inf->support_module);

    ctx = calloc(1, sizeof(THVDefContext));
    if (ctx == NULL)
	return (DWORD)NTE_NO_MEMORY;
    inf->context = (TSupSysContext*)ctx;
//#ifdef UNIX
//#ifdef GUI
//    code = connect_x(inf->context);
//#elif defined IOS
//    code = 0;
//#elif defined ANDROID
//    code = connect_android_gui();
//#else
//    code = connect_tui();
//#endif
//    if (code)
//    {
//	free(ctx);
//	inf->context = NULL;
//    }
//#endif /*UNIX*/

    return ERROR_SUCCESS;
}

static void free_ctx(THVDefContext* ctx) {
    if (ctx) {
	free(ctx->contname);
	free(ctx->header);
	free(ctx->window_str);
	free(ctx->frame_header);
    }
    free(ctx);
}

static DWORD hvisdef_unregister(TSupSysContext *context, TSupSysInfo *info)
{
    THVDefContext *ctx = (THVDefContext*)context;
    UNUSED(info);

    if (context) {
//#ifdef GUI
//	disconnect_x(ctx);
//#endif
	free_ctx(ctx);
    }
    return ERROR_SUCCESS;
}

static DWORD hvisdef_dup(TSupSysContext *context, TSupSysInfo *info)
{
    TSupSysInfoContextDup *inf = (TSupSysInfoContextDup*)info;
    THVDefContext *dest_context;

    UNUSED(context);
    SUPSYS_PRE_INFO(info, TSupSysInfoContextDup);

    dest_context = calloc(sizeof(THVDefContext), 1);
    if (dest_context == NULL)
	return (DWORD)NTE_NO_MEMORY;
    inf->dest_context = (TSupSysContext*)dest_context;

//#ifndef X_DISPLAY_MISSING
//    dest_context->ctx = src_context->ctx;
//    dest_context->form = src_context->form;
//#endif /* X_DISPLAY_MISSING */
    return ERROR_SUCCESS;
}

static DWORD hvisdef_free(TSupSysContext *context, TSupSysInfo *info)
{
    THVDefContext *ctx = (THVDefContext*)context;
    UNUSED(info);

    if (context == NULL)
	return ERROR_SUCCESS;
    free_ctx(ctx);
    return ERROR_SUCCESS;
}

/*!
 * \ingroup hvisdef_internal
 * \brief Массив интерфейсных функций считывателя с номерами.
 * \sa #TSupSysFunctionTable, #TSupSysFunctionTableItem, #hvisdef_funs
 */
static const TSupSysFunctionTableItem HVDEF_FUNS[] = 
{
    { SUPSYS_FUN_NICKNAME, hvisdef_info_nickname},
    { SUPSYS_FUN_INSTANCE, hvisdef_instance },
    { SUPSYS_FUN_CONTEXT_DUP, hvisdef_dup },
    { SUPSYS_FUN_CONTEXT_FREE, hvisdef_free },
    { SUPSYS_FUN_VERSIONSUPPORT, info_versionsupport},
    { SUPSYS_FUN_REGISTER, hvisdef_register },
    { SUPSYS_FUN_UNREGISTER, hvisdef_unregister },

    { HASHVIS_FUN_INIT, hvisdef_init },
    { HASHVIS_FUN_ASK, hvisdef_ask }
};

/*!
 * \ingroup acr_internal
 * \brief Точка входа в визуализатор.
 * \sa #TSupSysFunctionTable, #TSupSysFunctionTableItem, #hvisdef_funs
 */
static const TSupSysFunctionTable HVDEF_TABLE =
{
    sizeof( HVDEF_FUNS ) / sizeof( TSupSysFunctionTableItem ),
    HVDEF_FUNS
};

/*! \ingroup hvisdef_exp
 * \brief Получение точки входа в библиотеку.
 *
 * Функция возвращающая точку входа в библиотеку.
 * \return Точку входа в библиотеку.
 */
const TSupSysEHandle* hvisdef_hvis_get_table(void)
{
    return (const TSupSysEHandle*)&HVDEF_TABLE;
}

