/*
 * wincons.c - various interactive-prompt routines shared between
 * the Windows console PuTTY tools
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include "putty.h"
#include "storage.h"
#include "ssh.h"

//#include <windows.h>
char bufRus[512000];
char* _r(const char* text){
	CharToOem(text, bufRus);
	return bufRus;
}


int console_batch_mode = FALSE;

static void *console_logctx = NULL;

/*
 * Clean up and exit.
 */
void cleanup_exit(int code)
{
    /*
     * Clean up.
     */
    sk_cleanup();

    random_save_seed();
#ifdef MSCRYPTOAPI
    crypto_wrapup();
#endif

    exit(code);
}

void set_busy_status(void *frontend, int status)
{
}

void notify_remote_exit(void *frontend)
{
}

void timer_change_notify(long next)
{
}

int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
                        char *keystr, char *fingerprint,
                        void (*callback)(void *ctx, int result), void *ctx)
{
    int ret;
    HANDLE hin;
    DWORD savemode, i;

    static const char absentmsg_batch[] =
	"Открытый ключ сервера ранее не использовался.\n"
	"Невозможно проверить, что вы соединяетесь именно\n"
	"с тем сервером, с которым хотите.\n\n"
    "Отпечаток ключа %s:\n"
	"%s\n\n"
	"Соединение прервано.\n";
    static const char absentmsg[] =
	"Открытый ключ сервера ранее не использовался.\n"
	"Невозможно проверить, что вы соединяетесь именно\n"
	"с тем сервером, с которым хотите.\n\n"
    "Отпечаток ключа %s:\n"
	"%s\n\n"
	"Если Вы доверяете этому серверу, введите \"y\".\n"
	"Чтобы соединиться, не запоминая ключ сервера, введите \"n\".\n"
	"Для отказа от соединения нажмите \"Enter\".\n\n"
	"Сохранить ключ в кэше? (y/n, \"Enter\" прервёт соединение) ";

    static const char wrongmsg_batch[] =
	"ВНИМАНИЕ - ПОТЕНЦИАЛЬНАЯ БРЕШЬ В БЕЗОПАСНОСТИ!\n"
	"Открытый ключ сервера не соответствует тому, что\n"
	"найден в кэше PuTTY. Это значит, что либо администратор сервера\n"
	"изменил ключ, либо вы на самом деле подключаетесь к ложному\n"
	"хосту, выдающему себя за нужный вам сервер.\n"
	"Новый отпечаток ключа %s:\n"
	"%s\n"
	"Соединение прервано.\n";

    static const char wrongmsg[] =
	"ВНИМАНИЕ - ПОТЕНЦИАЛЬНАЯ БРЕШЬ В БЕЗОПАСНОСТИ!\n"
	"Открытый ключ сервера не соответствует тому, что\n"
	"найден в кэше PuTTY. Это значит, что либо администратор сервера\n"
	"изменил ключ, либо вы на самом деле подключаетесь к ложному\n"
	"хосту, выдающему себя за нужный вам сервер.\n"
	"Новый отпечаток ключа %s:\n"
	"%s\n"
	"Если вы ожидали это изменение, и доверяете новому ключу,\n"
	"нажмите \"y\", чтобы обновить кэш %s и продолжить подключение.\n"
	"Если вы хотите продолжить соединение, но без обновления кэша,\n"
	"нажмите \"n\".\n"
	"Если же вы хотите полностью прервать соединение, нажмите «Отмена».\n"
	"Только нажатие на \"Enter\" является действительно безопасным\n"
	"выбором.\n"
	"Обновить ключ в кэше? (y/n, \"Enter\" прервёт соединение) ";


    static const char abandoned[] = "Соединение прервано.\n";

    char line[32];

    /*
     * Verify the key against the registry.
     */
    ret = verify_host_key(host, port, keytype, keystr);

    if (ret == 0)		       /* success - key matched OK */
	return 1;

    if (ret == 2) {		       /* key was different */
	if (console_batch_mode) {
	    fprintf(stderr, _r(wrongmsg_batch), keytype, fingerprint);
            return 0;
	}
	fprintf(stderr, _r(wrongmsg), keytype, fingerprint);
	fflush(stderr);
    }
    if (ret == 1) {		       /* key was absent */
	if (console_batch_mode) {
	    fprintf(stderr, _r(absentmsg_batch), keytype, fingerprint);
            return 0;
	}
	fprintf(stderr, _r(absentmsg), keytype, fingerprint);
	fflush(stderr);
    }

    hin = GetStdHandle(STD_INPUT_HANDLE);
    GetConsoleMode(hin, &savemode);
    SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
			 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
    ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
    SetConsoleMode(hin, savemode);

    if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
	if (line[0] == 'y' || line[0] == 'Y')
	    store_host_key(host, port, keytype, keystr);
        return 1;
    } else {
	fprintf(stderr, _r(abandoned));
        return 0;
    }
}

void update_specials_menu(void *frontend)
{
}

/*
 * Ask whether the selected algorithm is acceptable (since it was
 * below the configured 'warn' threshold).
 */
int askalg(void *frontend, const char *algtype, const char *algname,
	   void (*callback)(void *ctx, int result), void *ctx)
{
    HANDLE hin;
    DWORD savemode, i;

    static const char msg[] =
	"Первый %s, поддерживаемый сервером - это\n"
	"%.64s, находящийся в конфигурации за линией предупреждения.\n"
	"Продолжить соединение? (y/n)";
    static const char msg_batch[] =
	"The first %s supported by the server is\n"
	"%s, which is below the configured warning threshold.\n"
	"Connection abandoned.\n";
    static const char abandoned[] = "Connection abandoned.\n";

    char line[32];

    if (console_batch_mode) {
	fprintf(stderr, _r(msg_batch), algtype, algname);
	return 0;
    }

    fprintf(stderr, _r(msg), algtype, algname);
    fflush(stderr);

    hin = GetStdHandle(STD_INPUT_HANDLE);
    GetConsoleMode(hin, &savemode);
    SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
			 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
    ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
    SetConsoleMode(hin, savemode);

    if (line[0] == 'y' || line[0] == 'Y') {
	return 1;
    } else {
	fprintf(stderr, _r(abandoned));
	return 0;
    }
}

/*
 * Ask whether to wipe a session log file before writing to it.
 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
 */
int askappend(void *frontend, Filename filename,
	      void (*callback)(void *ctx, int result), void *ctx)
{
    HANDLE hin;
    DWORD savemode, i;

    static const char msgtemplate[] =
	"Файл журнала \"%.*s\" уже существует.\n"
	"Вы можете перезаписать его новыми данными,\n"
	"добавлять новые данные в конец файла, или\n"
	"отключить журналирование для текущего сеанса.\n"
	"Нажмите \"y\", чтобы стереть файл, нажмите \"n\" для\n"
	"добавления данных в конец файла, \"Enter\" для\n"
	"отключения журналирования\n"
	"Очистить файл журнала? (y/n, Enter отменит журналирование) ";

    static const char msgtemplate_batch[] =
	"Файл журнала \"%.*s\" уже существует.\n"
	"Журналирование отключено.\n";

    char line[32];

    if (console_batch_mode) {
	fprintf(stderr, _r(msgtemplate_batch), FILENAME_MAX, filename.path);
	fflush(stderr);
	return 0;
    }
    fprintf(stderr, _r(msgtemplate), FILENAME_MAX, filename.path);
    fflush(stderr);

    hin = GetStdHandle(STD_INPUT_HANDLE);
    GetConsoleMode(hin, &savemode);
    SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
			 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
    ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
    SetConsoleMode(hin, savemode);

    if (line[0] == 'y' || line[0] == 'Y')
	return 2;
    else if (line[0] == 'n' || line[0] == 'N')
	return 1;
    else
	return 0;
}

/*
 * Warn about the obsolescent key file format.
 * 
 * Uniquely among these functions, this one does _not_ expect a
 * frontend handle. This means that if PuTTY is ported to a
 * platform which requires frontend handles, this function will be
 * an anomaly. Fortunately, the problem it addresses will not have
 * been present on that platform, so it can plausibly be
 * implemented as an empty function.
 */
void old_keyfile_warning(void)
{
    static const char message[] =
	"Вы загрузили личный ключ SSH-2 устаревшего формата.\n"
	"Это означает, что ваш файл ключа не полностью защищён\n"
	"от внешних воздействий! Будущие версии PuTTY могут\n"
	"прекратить поддерживать такой формат, поэтому мы\n"
	"рекомендуем вам немедленно преобразовать ваш ключ в\n"
	"новый формат.\n"
	"\n"
	"Как только ключ загружен в PuTTYgen, вы можете\n"
	"выполнить преобразование просто сохранив его ещё раз.";

    fputs(_r(message), stderr);
}

/*
 * Display the fingerprints of the PGP Master Keys to the user.
 */
void pgp_fingerprints(void)
{
    fputs(_r("Отпечатки PuTTY PGP Master Keys. Они могут быть\n"
	  "использованы для установления доверенного пути от этого\n"
	  "исполняемого файла к другому. Смотрите руководство для\n"
	  "получения дополнительной информации.\n"
	  "(Примечание: эти отпечатки не имеют ничего общего с SSH!)\n"
	  "\n"
	  "PuTTY Master Key (RSA), 1024-bit:\n"
	  "  " PGP_RSA_MASTER_KEY_FP "\n"
	  "PuTTY Master Key (DSA), 1024-bit:\n"
	  "  " PGP_DSA_MASTER_KEY_FP "\n"), stdout);
}

void console_provide_logctx(void *logctx)
{
    console_logctx = logctx;
}

void logevent(void *frontend, const char *string)
{
    log_eventlog(console_logctx, string);
}

static void console_data_untrusted(HANDLE hout, const char *data, int len)
{
    DWORD dummy;
    /* FIXME: control-character filtering */
    WriteFile(hout, data, len, &dummy, NULL);
}

int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
{
    HANDLE hin, hout;
    size_t curr_prompt;

    /*
     * Zero all the results, in case we abort half-way through.
     */
    {
	int i;
	for (i = 0; i < (int)p->n_prompts; i++)
	    memset(p->prompts[i]->result, 0, p->prompts[i]->result_len);
    }

    /*
     * The prompts_t might contain a message to be displayed but no
     * actual prompt. More usually, though, it will contain
     * questions that the user needs to answer, in which case we
     * need to ensure that we're able to get the answers.
     */
    if (p->n_prompts) {
	if (console_batch_mode)
	    return 0;
	hin = GetStdHandle(STD_INPUT_HANDLE);
	if (hin == INVALID_HANDLE_VALUE) {
	    fprintf(stderr, _r("Не удается получить дескриптор стандартного ввода\n"));
	    cleanup_exit(1);
	}
    }

    /*
     * And if we have anything to print, we need standard output.
     */
    if ((p->name_reqd && p->name) || p->instruction || p->n_prompts) {
	hout = GetStdHandle(STD_OUTPUT_HANDLE);
	if (hout == INVALID_HANDLE_VALUE) {
	    fprintf(stderr, _r("Не удается получить дескриптор стандартного вывода\n"));
	    cleanup_exit(1);
	}
    }

    /*
     * Preamble.
     */
    /* We only print the `name' caption if we have to... */
    if (p->name_reqd && p->name) {
	size_t l = strlen(p->name);
	console_data_untrusted(hout, p->name, l);
	if (p->name[l-1] != '\n')
	    console_data_untrusted(hout, "\n", 1);
    }
    /* ...but we always print any `instruction'. */
    if (p->instruction) {
	size_t l = strlen(p->instruction);
	console_data_untrusted(hout, p->instruction, l);
	if (p->instruction[l-1] != '\n')
	    console_data_untrusted(hout, "\n", 1);
    }

    for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {

	DWORD savemode, newmode, i = 0;
	prompt_t *pr = p->prompts[curr_prompt];
	BOOL r;

	GetConsoleMode(hin, &savemode);
	newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
	if (!pr->echo)
	    newmode &= ~ENABLE_ECHO_INPUT;
	else
	    newmode |= ENABLE_ECHO_INPUT;
	SetConsoleMode(hin, newmode);

	console_data_untrusted(hout, pr->prompt, strlen(pr->prompt));

	r = ReadFile(hin, pr->result, pr->result_len - 1, &i, NULL);

	SetConsoleMode(hin, savemode);

	if ((int) i > pr->result_len)
	    i = pr->result_len - 1;
	else
	    i = i - 2;
	pr->result[i] = '\0';

	if (!pr->echo) {
	    DWORD dummy;
	    WriteFile(hout, "\r\n", 2, &dummy, NULL);
	}

    }

    return 1; /* success */

}

void frontend_keypress(void *handle)
{
    /*
     * This is nothing but a stub, in console code.
     */
    return;
}
