/*
 * winproxy.c: Windows implementation of platform_new_connection(),
 * supporting an OpenSSH-like proxy command via the winhandl.c
 * mechanism.
 */

#include <stdio.h>
#include <assert.h>

#define DEFINE_PLUG_METHOD_MACROS
#include "tree234.h"
#include "putty.h"
#include "network.h"
#include "proxy.h"

Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
                          Plug plug, int overlapped);

Socket platform_new_connection(SockAddr addr, const char *hostname,
			       int port, int privport,
			       int oobinline, int nodelay, int keepalive,
			       Plug plug, Conf *conf)
{
    char *cmd;
    HANDLE us_to_cmd, cmd_from_us;
    HANDLE us_from_cmd, cmd_to_us;
    HANDLE us_from_cmd_err, cmd_err_to_us;
    SECURITY_ATTRIBUTES sa;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD)
	return NULL;

    cmd = format_telnet_command(addr, port, conf);

    /* We are responsible for this and don't need it any more */
    sk_addr_free(addr);

    {
	char *msg = dupprintf("Starting local proxy command: %s", cmd);
	plug_log(plug, 2, NULL, 0, msg, 0);
	sfree(msg);
    }

    /*
     * Create the pipes to the proxy command, and spawn the proxy
     * command process.
     */
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;    /* default */
    sa.bInheritHandle = TRUE;
    if (!CreatePipe(&us_from_cmd, &cmd_to_us, &sa, 0)) {
	Socket ret =
            new_error_socket("Unable to create pipes for proxy command", plug);
        sfree(cmd);
	return ret;
    }

    if (!CreatePipe(&cmd_from_us, &us_to_cmd, &sa, 0)) {
	Socket ret =
            new_error_socket("Unable to create pipes for proxy command", plug);
        sfree(cmd);
	CloseHandle(us_from_cmd);
	CloseHandle(cmd_to_us);
	return ret;
    }

    if (flags & FLAG_STDERR) {
        /* If we have a sensible stderr, the proxy command can send
         * its own standard error there, so we won't interfere. */
        us_from_cmd_err = cmd_err_to_us = NULL;
    } else {
        /* If we don't have a sensible stderr, we should catch the
         * proxy command's standard error to put in our event log. */
        if (!CreatePipe(&us_from_cmd_err, &cmd_err_to_us, &sa, 0)) {
            Socket ret = new_error_socket
                ("Unable to create pipes for proxy command", plug);
            sfree(cmd);
            CloseHandle(us_from_cmd);
            CloseHandle(cmd_to_us);
            CloseHandle(us_to_cmd);
            CloseHandle(cmd_from_us);
            return ret;
        }
    }

    SetHandleInformation(us_to_cmd, HANDLE_FLAG_INHERIT, 0);
    SetHandleInformation(us_from_cmd, HANDLE_FLAG_INHERIT, 0);
    if (us_from_cmd_err != NULL)
        SetHandleInformation(us_from_cmd_err, HANDLE_FLAG_INHERIT, 0);

    si.cb = sizeof(si);
    si.lpReserved = NULL;
    si.lpDesktop = NULL;
    si.lpTitle = NULL;
    si.dwFlags = STARTF_USESTDHANDLES;
    si.cbReserved2 = 0;
    si.lpReserved2 = NULL;
    si.hStdInput = cmd_from_us;
    si.hStdOutput = cmd_to_us;
    si.hStdError = cmd_err_to_us;
    CreateProcess(NULL, cmd, NULL, NULL, TRUE,
		  CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS,
		  NULL, NULL, &si, &pi);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    sfree(cmd);

    CloseHandle(cmd_from_us);
    CloseHandle(cmd_to_us);

    if (cmd_err_to_us != NULL)
        CloseHandle(cmd_err_to_us);

    return make_handle_socket(us_to_cmd, us_from_cmd, us_from_cmd_err,
                              plug, FALSE);
}
