terminal support for the accepting unicode input (#162)
Support the unicode characters as input to the ssh client. PowerShell/Win32-OpenSSH#711 Remove the bAnsi related code changes to the ssh-shellhost.c as that code never executes.. Fixed "long pressing key issue for the slow ssh connections" PowerShell/Win32-OpenSSH#701 fixed warning message in the latestw_all recent commits. Fixed the issue with ssh failures in the latestw_all recent commits.
This commit is contained in:
parent
04ce306973
commit
98eca17c65
|
@ -757,10 +757,7 @@ fileio_stat(const char *path, struct _stat64 *buf)
|
||||||
WIN32_FILE_ATTRIBUTE_DATA attributes = { 0 };
|
WIN32_FILE_ATTRIBUTE_DATA attributes = { 0 };
|
||||||
int ret = -1, len = 0;
|
int ret = -1, len = 0;
|
||||||
|
|
||||||
if ((wpath = utf8_to_utf16(path)) == NULL) {
|
memset(buf, 0, sizeof(struct _stat64));
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Detect root dir */
|
/* Detect root dir */
|
||||||
if (path && strcmp(path, "/") == 0) {
|
if (path && strcmp(path, "/") == 0) {
|
||||||
|
@ -770,7 +767,7 @@ fileio_stat(const char *path, struct _stat64 *buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((wpath = utf8_to_utf16(path)) == NULL) {
|
if ((wpath = utf8_to_utf16(path)) == NULL) {
|
||||||
errno = errno_from_Win32LastError();
|
errno = ENOMEM;
|
||||||
debug3("utf8_to_utf16 failed for file:%s error:%d", path, GetLastError());
|
debug3("utf8_to_utf16 failed for file:%s error:%d", path, GetLastError());
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -781,7 +778,7 @@ fileio_stat(const char *path, struct _stat64 *buf)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = wcslen(wpath);
|
len = (int)wcslen(wpath);
|
||||||
|
|
||||||
buf->st_ino = 0; /* Has no meaning in the FAT, HPFS, or NTFS file systems*/
|
buf->st_ino = 0; /* Has no meaning in the FAT, HPFS, or NTFS file systems*/
|
||||||
buf->st_gid = 0; /* UNIX - specific; has no meaning on windows */
|
buf->st_gid = 0; /* UNIX - specific; has no meaning on windows */
|
||||||
|
|
|
@ -482,10 +482,10 @@ strmode(mode_t mode, char *p)
|
||||||
* As of now we are keeping "*" for everything.
|
* As of now we are keeping "*" for everything.
|
||||||
* TODO - figure out if there is a better option
|
* TODO - figure out if there is a better option
|
||||||
*/
|
*/
|
||||||
const char *permissions = "********* ";
|
const char *permissions = "********* ";
|
||||||
strncpy(p, permissions, strlen(permissions) + 1);
|
for(int i = 0; i < strlen(permissions); i++)
|
||||||
|
*p++ = permissions[i];
|
||||||
p = p + strlen(p);
|
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,11 +535,10 @@ static BOOL
|
||||||
is_root_or_empty(wchar_t * path)
|
is_root_or_empty(wchar_t * path)
|
||||||
{
|
{
|
||||||
wchar_t * path_start;
|
wchar_t * path_start;
|
||||||
BOOL has_drive_letter_and_colon;
|
|
||||||
int len;
|
int len;
|
||||||
if (!path)
|
if (!path)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
len = wcslen(path);
|
len = (int)wcslen(path);
|
||||||
if((len > 1) && __ascii_iswalpha(path[0]) && path[1] == L':')
|
if((len > 1) && __ascii_iswalpha(path[0]) && path[1] == L':')
|
||||||
path_start = path + 2;
|
path_start = path + 2;
|
||||||
else
|
else
|
||||||
|
|
|
@ -29,4 +29,5 @@ void convertToForwardslash(char *str);
|
||||||
#define errno_from_Win32LastError() errno_from_Win32Error(GetLastError())
|
#define errno_from_Win32LastError() errno_from_Win32Error(GetLastError())
|
||||||
int errno_from_Win32Error(int);
|
int errno_from_Win32Error(int);
|
||||||
void unix_time_to_file_time(ULONG, LPFILETIME);
|
void unix_time_to_file_time(ULONG, LPFILETIME);
|
||||||
void file_time_unix_time(const LPFILETIME, time_t *);
|
void file_time_to_unix_time(const LPFILETIME, time_t *);
|
||||||
|
int file_attr_to_st_mode(wchar_t * path, DWORD attributes);
|
|
@ -86,39 +86,41 @@ typedef struct consoleEvent {
|
||||||
} consoleEvent;
|
} consoleEvent;
|
||||||
|
|
||||||
struct key_translation {
|
struct key_translation {
|
||||||
char incoming[6];
|
wchar_t in[6];
|
||||||
int vk;
|
int vk;
|
||||||
char outgoing;
|
wchar_t out;
|
||||||
|
int in_key_len;
|
||||||
} key_translation;
|
} key_translation;
|
||||||
|
|
||||||
|
/* All the substrings (Ex- "\x1b") should be in the end, otherwise ProcessIncomingKeys() will not work as expected */
|
||||||
struct key_translation keys[] = {
|
struct key_translation keys[] = {
|
||||||
{ "\x1b", VK_ESCAPE, '\x1b' },
|
{ L"\r", VK_RETURN, L'\r' , 0},
|
||||||
{ "\r", VK_RETURN, '\r' },
|
{ L"\b", VK_BACK, L'\b' , 0},
|
||||||
{ "\b", VK_BACK, '\b' },
|
{ L"\x7f", VK_BACK, L'\b' , 0},
|
||||||
{ "\x7f", VK_BACK, '\b' },
|
{ L"\t", VK_TAB, L'\t' , 0},
|
||||||
{ "\t", VK_TAB, '\t' },
|
{ L"\x1b[A", VK_UP, 0 , 0},
|
||||||
{ "\x1b[A", VK_UP, 0 },
|
{ L"\x1b[B", VK_DOWN, 0 , 0},
|
||||||
{ "\x1b[B", VK_DOWN, 0 },
|
{ L"\x1b[C", VK_RIGHT, 0 , 0},
|
||||||
{ "\x1b[C", VK_RIGHT, 0 },
|
{ L"\x1b[D", VK_LEFT, 0 , 0},
|
||||||
{ "\x1b[D", VK_LEFT, 0 },
|
{ L"\x1b[1~", VK_HOME, 0 , 0},
|
||||||
{ "\x1b[1~", VK_HOME, 0 },
|
{ L"\x1b[2~", VK_INSERT, 0 , 0},
|
||||||
{ "\x1b[2~", VK_INSERT, 0 },
|
{ L"\x1b[3~", VK_DELETE, 0 , 0},
|
||||||
{ "\x1b[3~", VK_DELETE, 0 },
|
{ L"\x1b[4~", VK_END, 0 , 0},
|
||||||
{ "\x1b[4~", VK_END, 0 },
|
{ L"\x1b[5~", VK_PRIOR, 0 , 0},
|
||||||
{ "\x1b[5~", VK_PRIOR, 0 },
|
{ L"\x1b[6~", VK_NEXT, 0 , 0},
|
||||||
{ "\x1b[6~", VK_NEXT, 0 },
|
{ L"\x1b[11~", VK_F1, 0 , 0},
|
||||||
{ "\x1b[11~", VK_F1, 0 },
|
{ L"\x1b[12~", VK_F2, 0 , 0},
|
||||||
{ "\x1b[12~", VK_F2, 0 },
|
{ L"\x1b[13~", VK_F3, 0 , 0},
|
||||||
{ "\x1b[13~", VK_F3, 0 },
|
{ L"\x1b[14~", VK_F4, 0 , 0},
|
||||||
{ "\x1b[14~", VK_F4, 0 },
|
{ L"\x1b[15~", VK_F5, 0 , 0},
|
||||||
{ "\x1b[15~", VK_F5, 0 },
|
{ L"\x1b[17~", VK_F6, 0 , 0},
|
||||||
{ "\x1b[17~", VK_F6, 0 },
|
{ L"\x1b[18~", VK_F7, 0 , 0},
|
||||||
{ "\x1b[18~", VK_F7, 0 },
|
{ L"\x1b[19~", VK_F8, 0 , 0},
|
||||||
{ "\x1b[19~", VK_F8, 0 },
|
{ L"\x1b[20~", VK_F9, 0 , 0},
|
||||||
{ "\x1b[20~", VK_F9, 0 },
|
{ L"\x1b[21~", VK_F10, 0 , 0},
|
||||||
{ "\x1b[21~", VK_F10, 0 },
|
{ L"\x1b[23~", VK_F11, 0 , 0},
|
||||||
{ "\x1b[23~", VK_F11, 0 },
|
{ L"\x1b[24~", VK_F12, 0 , 0},
|
||||||
{ "\x1b[24~", VK_F12, 0 }
|
{ L"\x1b", VK_ESCAPE, L'\x1b' , 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static SHORT lastX = 0;
|
static SHORT lastX = 0;
|
||||||
|
@ -130,7 +132,6 @@ consoleEvent* tail = NULL;
|
||||||
BOOL bRet = FALSE;
|
BOOL bRet = FALSE;
|
||||||
BOOL bNoScrollRegion = FALSE;
|
BOOL bNoScrollRegion = FALSE;
|
||||||
BOOL bStartup = TRUE;
|
BOOL bStartup = TRUE;
|
||||||
BOOL bAnsi = FALSE;
|
|
||||||
BOOL bHookEvents = FALSE;
|
BOOL bHookEvents = FALSE;
|
||||||
BOOL bFullScreen = FALSE;
|
BOOL bFullScreen = FALSE;
|
||||||
BOOL bUseAnsiEmulation = TRUE;
|
BOOL bUseAnsiEmulation = TRUE;
|
||||||
|
@ -142,13 +143,12 @@ HANDLE pipe_in = INVALID_HANDLE_VALUE;
|
||||||
HANDLE pipe_out = INVALID_HANDLE_VALUE;
|
HANDLE pipe_out = INVALID_HANDLE_VALUE;
|
||||||
HANDLE pipe_err = INVALID_HANDLE_VALUE;
|
HANDLE pipe_err = INVALID_HANDLE_VALUE;
|
||||||
HANDLE child = INVALID_HANDLE_VALUE;
|
HANDLE child = INVALID_HANDLE_VALUE;
|
||||||
DWORD child_exit_code = 0;
|
|
||||||
HANDLE hConsoleBuffer = INVALID_HANDLE_VALUE;
|
HANDLE hConsoleBuffer = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
HANDLE monitor_thread = INVALID_HANDLE_VALUE;
|
HANDLE monitor_thread = INVALID_HANDLE_VALUE;
|
||||||
HANDLE io_thread = INVALID_HANDLE_VALUE;
|
HANDLE io_thread = INVALID_HANDLE_VALUE;
|
||||||
HANDLE ux_thread = INVALID_HANDLE_VALUE;
|
HANDLE ux_thread = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
DWORD child_exit_code = 0;
|
||||||
DWORD hostProcessId = 0;
|
DWORD hostProcessId = 0;
|
||||||
DWORD hostThreadId = 0;
|
DWORD hostThreadId = 0;
|
||||||
DWORD childProcessId = 0;
|
DWORD childProcessId = 0;
|
||||||
|
@ -196,7 +196,7 @@ ConSRWidth()
|
||||||
* This function will handle the console keystrokes.
|
* This function will handle the console keystrokes.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
SendKeyStroke(HANDLE hInput, int keyStroke, char character)
|
SendKeyStroke(HANDLE hInput, int keyStroke, wchar_t character)
|
||||||
{
|
{
|
||||||
DWORD wr = 0;
|
DWORD wr = 0;
|
||||||
INPUT_RECORD ir;
|
INPUT_RECORD ir;
|
||||||
|
@ -207,32 +207,48 @@ SendKeyStroke(HANDLE hInput, int keyStroke, char character)
|
||||||
ir.Event.KeyEvent.wVirtualKeyCode = keyStroke;
|
ir.Event.KeyEvent.wVirtualKeyCode = keyStroke;
|
||||||
ir.Event.KeyEvent.wVirtualScanCode = 0;
|
ir.Event.KeyEvent.wVirtualScanCode = 0;
|
||||||
ir.Event.KeyEvent.dwControlKeyState = 0;
|
ir.Event.KeyEvent.dwControlKeyState = 0;
|
||||||
ir.Event.KeyEvent.uChar.UnicodeChar = 0;
|
ir.Event.KeyEvent.uChar.UnicodeChar = character;
|
||||||
ir.Event.KeyEvent.uChar.AsciiChar = character;
|
|
||||||
|
|
||||||
WriteConsoleInputA(hInput, &ir, 1, &wr);
|
WriteConsoleInputW(hInput, &ir, 1, &wr);
|
||||||
|
|
||||||
ir.Event.KeyEvent.bKeyDown = FALSE;
|
ir.Event.KeyEvent.bKeyDown = FALSE;
|
||||||
WriteConsoleInputA(hInput, &ir, 1, &wr);
|
WriteConsoleInputW(hInput, &ir, 1, &wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
initialize_keylen()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < ARRAYSIZE(keys); i++)
|
||||||
|
keys[i].in_key_len = (int) wcslen(keys[i].in);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ProcessIncomingKeys(char * ansikey)
|
ProcessIncomingKeys(char * ansikey)
|
||||||
{
|
{
|
||||||
int keylen = (int) strlen(ansikey);
|
wchar_t *buf = utf8_to_utf16(ansikey);
|
||||||
|
|
||||||
if (!keylen)
|
if (!buf) {
|
||||||
return;
|
printf("\nFailed to deserialize the client data, error:%d\n", GetLastError());
|
||||||
|
exit(255);
|
||||||
for (int nKey=0; nKey < ARRAYSIZE(keys); nKey++) {
|
|
||||||
if (strcmp(ansikey, keys[nKey].incoming) == 0) {
|
|
||||||
SendKeyStroke(child_in, keys[nKey].vk, keys[nKey].outgoing);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i < keylen; i++)
|
loop:
|
||||||
SendKeyStroke(child_in, 0, ansikey[i]);
|
while (buf && (wcslen(buf) > 0)) {
|
||||||
|
for (int j = 0; j < ARRAYSIZE(keys); j++) {
|
||||||
|
if ( (wcslen(buf) >= keys[j].in_key_len) && (wcsncmp(buf, keys[j].in, keys[j].in_key_len) == 0) ) {
|
||||||
|
SendKeyStroke(child_in, keys[j].vk, keys[j].out);
|
||||||
|
buf += keys[j].in_key_len;
|
||||||
|
goto loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*buf == L'\x3') /*Ctrl+C - Raise Ctrl+C*/
|
||||||
|
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
|
||||||
|
else
|
||||||
|
SendKeyStroke(child_in, 0, *buf);
|
||||||
|
|
||||||
|
buf++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -458,8 +474,7 @@ SizeWindow(HANDLE hInput)
|
||||||
matchingFont.dwFontSize.X = 0;
|
matchingFont.dwFontSize.X = 0;
|
||||||
matchingFont.dwFontSize.Y = 16;
|
matchingFont.dwFontSize.Y = 16;
|
||||||
matchingFont.FontFamily = FF_DONTCARE;
|
matchingFont.FontFamily = FF_DONTCARE;
|
||||||
matchingFont.FontWeight = FW_NORMAL;
|
matchingFont.FontWeight = FW_NORMAL;
|
||||||
//wcscpy(matchingFont.FaceName, L"Consolas");
|
|
||||||
wcscpy_s(matchingFont.FaceName, LF_FACESIZE, L"Consolas");
|
wcscpy_s(matchingFont.FaceName, LF_FACESIZE, L"Consolas");
|
||||||
|
|
||||||
bSuccess = __SetCurrentConsoleFontEx(child_out, FALSE, &matchingFont);
|
bSuccess = __SetCurrentConsoleFontEx(child_out, FALSE, &matchingFont);
|
||||||
|
@ -741,19 +756,6 @@ ProcessEvent(void *p)
|
||||||
DWORD WINAPI
|
DWORD WINAPI
|
||||||
ProcessEventQueue(LPVOID p)
|
ProcessEventQueue(LPVOID p)
|
||||||
{
|
{
|
||||||
if (child_in != INVALID_HANDLE_VALUE && child_in != NULL &&
|
|
||||||
child_out != INVALID_HANDLE_VALUE && child_out != NULL) {
|
|
||||||
DWORD dwInputMode;
|
|
||||||
DWORD dwOutputMode;
|
|
||||||
|
|
||||||
if (GetConsoleMode(child_in, &dwInputMode) && GetConsoleMode(child_out, &dwOutputMode))
|
|
||||||
if (((dwOutputMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == ENABLE_VIRTUAL_TERMINAL_PROCESSING) &&
|
|
||||||
((dwInputMode & ENABLE_VIRTUAL_TERMINAL_INPUT) == ENABLE_VIRTUAL_TERMINAL_INPUT))
|
|
||||||
bAnsi = TRUE;
|
|
||||||
else
|
|
||||||
bAnsi = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
while (head) {
|
while (head) {
|
||||||
EnterCriticalSection(&criticalSection);
|
EnterCriticalSection(&criticalSection);
|
||||||
|
@ -853,32 +855,18 @@ DWORD WINAPI
|
||||||
ProcessPipes(LPVOID p)
|
ProcessPipes(LPVOID p)
|
||||||
{
|
{
|
||||||
BOOL ret;
|
BOOL ret;
|
||||||
DWORD dwStatus;
|
DWORD dwStatus;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
|
|
||||||
/* process data from pipe_in and route appropriately */
|
/* process data from pipe_in and route appropriately */
|
||||||
while (1) {
|
while (1) {
|
||||||
ZeroMemory(buf, sizeof(buf));
|
ZeroMemory(buf, sizeof(buf));
|
||||||
int rd = 0;
|
int rd = 0;
|
||||||
|
|
||||||
GOTO_CLEANUP_ON_FALSE(ReadFile(pipe_in, buf, sizeof(buf) - 1, &rd, NULL)); /* read bufsize-1 */
|
GOTO_CLEANUP_ON_FALSE(ReadFile(pipe_in, buf, sizeof(buf) - 1, &rd, NULL)); /* read bufsize-1 */
|
||||||
bStartup = FALSE;
|
bStartup = FALSE;
|
||||||
for (int i=0; i < rd; i++) {
|
if(rd > 0)
|
||||||
if (buf[i] == 0)
|
ProcessIncomingKeys(buf);
|
||||||
break;
|
|
||||||
|
|
||||||
if (buf[i] == 3) { /*Ctrl+C - Raise Ctrl+C*/
|
|
||||||
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bAnsi)
|
|
||||||
SendKeyStroke(child_in, 0, buf[i]);
|
|
||||||
else {
|
|
||||||
ProcessIncomingKeys(buf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
@ -964,8 +952,8 @@ start_with_pty(wchar_t *command)
|
||||||
HMODULE hm_kernel32 = NULL, hm_user32 = NULL;
|
HMODULE hm_kernel32 = NULL, hm_user32 = NULL;
|
||||||
|
|
||||||
if(cmd == NULL) {
|
if(cmd == NULL) {
|
||||||
printf("ssh-shellhost - out of memory");
|
printf("ssh-shellhost is out of memory");
|
||||||
return -1;
|
exit(255);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((hm_kernel32 = LoadLibraryW(L"kernel32.dll")) == NULL ||
|
if ((hm_kernel32 = LoadLibraryW(L"kernel32.dll")) == NULL ||
|
||||||
|
@ -1047,6 +1035,8 @@ start_with_pty(wchar_t *command)
|
||||||
|
|
||||||
/* disable Ctrl+C hander in this process*/
|
/* disable Ctrl+C hander in this process*/
|
||||||
SetConsoleCtrlHandler(NULL, TRUE);
|
SetConsoleCtrlHandler(NULL, TRUE);
|
||||||
|
|
||||||
|
initialize_keylen();
|
||||||
|
|
||||||
io_thread = CreateThread(NULL, 0, ProcessPipes, NULL, 0, NULL);
|
io_thread = CreateThread(NULL, 0, ProcessPipes, NULL, 0, NULL);
|
||||||
if (IS_INVALID_HANDLE(io_thread))
|
if (IS_INVALID_HANDLE(io_thread))
|
||||||
|
@ -1118,6 +1108,11 @@ start_withno_pty(wchar_t *command)
|
||||||
char buf[128];
|
char buf[128];
|
||||||
DWORD rd = 0, wr = 0, i = 0;
|
DWORD rd = 0, wr = 0, i = 0;
|
||||||
|
|
||||||
|
if (cmd == NULL) {
|
||||||
|
printf("ssh-shellhost is out of memory");
|
||||||
|
exit(255);
|
||||||
|
}
|
||||||
|
|
||||||
pipe_in = GetStdHandle(STD_INPUT_HANDLE);
|
pipe_in = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
pipe_out = GetStdHandle(STD_OUTPUT_HANDLE);
|
pipe_out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
pipe_err = GetStdHandle(STD_ERROR_HANDLE);
|
pipe_err = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
|
|
@ -100,7 +100,6 @@ opendir(const char *name)
|
||||||
DIR *pdir;
|
DIR *pdir;
|
||||||
wchar_t searchstr[PATH_MAX];
|
wchar_t searchstr[PATH_MAX];
|
||||||
wchar_t* wname = NULL;
|
wchar_t* wname = NULL;
|
||||||
int needed;
|
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
/* Detect root dir */
|
/* Detect root dir */
|
||||||
|
|
|
@ -367,9 +367,9 @@ int register_child(void* child, unsigned long pid);
|
||||||
|
|
||||||
int do_exec_windows(Session *s, const char *command, int pty) {
|
int do_exec_windows(Session *s, const char *command, int pty) {
|
||||||
int pipein[2], pipeout[2], pipeerr[2], r;
|
int pipein[2], pipeout[2], pipeerr[2], r;
|
||||||
char *exec_command = NULL, *progdir = w32_programdir(), *cmd = NULL, *shell_host = NULL, *command_b64 = NULL, *argp = NULL;
|
char *exec_command = NULL, *progdir = w32_programdir(), *cmd = NULL, *shell_host = NULL, *command_b64 = NULL;
|
||||||
wchar_t *exec_command_w = NULL, *pw_dir_w;
|
wchar_t *exec_command_w = NULL, *pw_dir_w;
|
||||||
const char *sftp_exe = "sftp-server.exe";
|
const char *sftp_exe = "sftp-server.exe", *argp = NULL;
|
||||||
size_t command_b64_len = 0;
|
size_t command_b64_len = 0;
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
STARTUPINFOW si;
|
STARTUPINFOW si;
|
||||||
|
|
Loading…
Reference in New Issue