+++ /dev/null
-/*\r
- * winsftp.c: the Windows-specific parts of PSFTP and PSCP.\r
- */\r
-\r
-#include <assert.h>\r
-\r
-#include "putty.h"\r
-#include "psftp.h"\r
-#include "ssh.h"\r
-#include "int64.h"\r
-\r
-char *get_ttymode(void *frontend, const char *mode) { return NULL; }\r
-\r
-int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
-{\r
- int ret;\r
- ret = cmdline_get_passwd_input(p, in, inlen);\r
- if (ret == -1)\r
- ret = console_get_userpass_input(p, in, inlen);\r
- return ret;\r
-}\r
-\r
-void platform_get_x11_auth(struct X11Display *display, const Config *cfg)\r
-{\r
- /* Do nothing, therefore no auth. */\r
-}\r
-const int platform_uses_x11_unix_by_default = TRUE;\r
-\r
-/* ----------------------------------------------------------------------\r
- * File access abstraction.\r
- */\r
-\r
-/*\r
- * Set local current directory. Returns NULL on success, or else an\r
- * error message which must be freed after printing.\r
- */\r
-char *psftp_lcd(char *dir)\r
-{\r
- char *ret = NULL;\r
-\r
- if (!SetCurrentDirectory(dir)) {\r
- LPVOID message;\r
- int i;\r
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |\r
- FORMAT_MESSAGE_FROM_SYSTEM |\r
- FORMAT_MESSAGE_IGNORE_INSERTS,\r
- NULL, GetLastError(),\r
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
- (LPTSTR)&message, 0, NULL);\r
- i = strcspn((char *)message, "\n");\r
- ret = dupprintf("%.*s", i, (LPCTSTR)message);\r
- LocalFree(message);\r
- }\r
-\r
- return ret;\r
-}\r
-\r
-/*\r
- * Get local current directory. Returns a string which must be\r
- * freed.\r
- */\r
-char *psftp_getcwd(void)\r
-{\r
- char *ret = snewn(256, char);\r
- int len = GetCurrentDirectory(256, ret);\r
- if (len > 256)\r
- ret = sresize(ret, len, char);\r
- GetCurrentDirectory(len, ret);\r
- return ret;\r
-}\r
-\r
-#define TIME_POSIX_TO_WIN(t, ft) do { \\r
- ULARGE_INTEGER uli; \\r
- uli.QuadPart = ((ULONGLONG)(t) + 11644473600ull) * 10000000ull; \\r
- (ft).dwLowDateTime = uli.LowPart; \\r
- (ft).dwHighDateTime = uli.HighPart; \\r
-} while(0)\r
-#define TIME_WIN_TO_POSIX(ft, t) do { \\r
- ULARGE_INTEGER uli; \\r
- uli.LowPart = (ft).dwLowDateTime; \\r
- uli.HighPart = (ft).dwHighDateTime; \\r
- uli.QuadPart = uli.QuadPart / 10000000ull - 11644473600ull; \\r
- (t) = (unsigned long) uli.QuadPart; \\r
-} while(0)\r
-\r
-struct RFile {\r
- HANDLE h;\r
-};\r
-\r
-RFile *open_existing_file(char *name, uint64 *size,\r
- unsigned long *mtime, unsigned long *atime)\r
-{\r
- HANDLE h;\r
- RFile *ret;\r
-\r
- h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL,\r
- OPEN_EXISTING, 0, 0);\r
- if (h == INVALID_HANDLE_VALUE)\r
- return NULL;\r
-\r
- ret = snew(RFile);\r
- ret->h = h;\r
-\r
- if (size)\r
- size->lo=GetFileSize(h, &(size->hi));\r
-\r
- if (mtime || atime) {\r
- FILETIME actime, wrtime;\r
- GetFileTime(h, NULL, &actime, &wrtime);\r
- if (atime)\r
- TIME_WIN_TO_POSIX(actime, *atime);\r
- if (mtime)\r
- TIME_WIN_TO_POSIX(wrtime, *mtime);\r
- }\r
-\r
- return ret;\r
-}\r
-\r
-int read_from_file(RFile *f, void *buffer, int length)\r
-{\r
- int ret;\r
- DWORD read;\r
- ret = ReadFile(f->h, buffer, length, &read, NULL);\r
- if (!ret)\r
- return -1; /* error */\r
- else\r
- return read;\r
-}\r
-\r
-void close_rfile(RFile *f)\r
-{\r
- CloseHandle(f->h);\r
- sfree(f);\r
-}\r
-\r
-struct WFile {\r
- HANDLE h;\r
-};\r
-\r
-WFile *open_new_file(char *name)\r
-{\r
- HANDLE h;\r
- WFile *ret;\r
-\r
- h = CreateFile(name, GENERIC_WRITE, 0, NULL,\r
- CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);\r
- if (h == INVALID_HANDLE_VALUE)\r
- return NULL;\r
-\r
- ret = snew(WFile);\r
- ret->h = h;\r
-\r
- return ret;\r
-}\r
-\r
-WFile *open_existing_wfile(char *name, uint64 *size)\r
-{\r
- HANDLE h;\r
- WFile *ret;\r
-\r
- h = CreateFile(name, GENERIC_WRITE, FILE_SHARE_READ, NULL,\r
- OPEN_EXISTING, 0, 0);\r
- if (h == INVALID_HANDLE_VALUE)\r
- return NULL;\r
-\r
- ret = snew(WFile);\r
- ret->h = h;\r
-\r
- if (size)\r
- size->lo=GetFileSize(h, &(size->hi));\r
-\r
- return ret;\r
-}\r
-\r
-int write_to_file(WFile *f, void *buffer, int length)\r
-{\r
- int ret;\r
- DWORD written;\r
- ret = WriteFile(f->h, buffer, length, &written, NULL);\r
- if (!ret)\r
- return -1; /* error */\r
- else\r
- return written;\r
-}\r
-\r
-void set_file_times(WFile *f, unsigned long mtime, unsigned long atime)\r
-{\r
- FILETIME actime, wrtime;\r
- TIME_POSIX_TO_WIN(atime, actime);\r
- TIME_POSIX_TO_WIN(mtime, wrtime);\r
- SetFileTime(f->h, NULL, &actime, &wrtime);\r
-}\r
-\r
-void close_wfile(WFile *f)\r
-{\r
- CloseHandle(f->h);\r
- sfree(f);\r
-}\r
-\r
-/* Seek offset bytes through file, from whence, where whence is\r
- FROM_START, FROM_CURRENT, or FROM_END */\r
-int seek_file(WFile *f, uint64 offset, int whence)\r
-{\r
- DWORD movemethod;\r
-\r
- switch (whence) {\r
- case FROM_START:\r
- movemethod = FILE_BEGIN;\r
- break;\r
- case FROM_CURRENT:\r
- movemethod = FILE_CURRENT;\r
- break;\r
- case FROM_END:\r
- movemethod = FILE_END;\r
- break;\r
- default:\r
- return -1;\r
- }\r
-\r
- SetFilePointer(f->h, offset.lo, &(offset.hi), movemethod);\r
- \r
- if (GetLastError() != NO_ERROR)\r
- return -1;\r
- else \r
- return 0;\r
-}\r
-\r
-uint64 get_file_posn(WFile *f)\r
-{\r
- uint64 ret;\r
-\r
- ret.hi = 0L;\r
- ret.lo = SetFilePointer(f->h, 0L, &(ret.hi), FILE_CURRENT);\r
-\r
- return ret;\r
-}\r
-\r
-int file_type(char *name)\r
-{\r
- DWORD attr;\r
- attr = GetFileAttributes(name);\r
- /* We know of no `weird' files under Windows. */\r
- if (attr == (DWORD)-1)\r
- return FILE_TYPE_NONEXISTENT;\r
- else if (attr & FILE_ATTRIBUTE_DIRECTORY)\r
- return FILE_TYPE_DIRECTORY;\r
- else\r
- return FILE_TYPE_FILE;\r
-}\r
-\r
-struct DirHandle {\r
- HANDLE h;\r
- char *name;\r
-};\r
-\r
-DirHandle *open_directory(char *name)\r
-{\r
- HANDLE h;\r
- WIN32_FIND_DATA fdat;\r
- char *findfile;\r
- DirHandle *ret;\r
-\r
- /* Enumerate files in dir `foo'. */\r
- findfile = dupcat(name, "/*", NULL);\r
- h = FindFirstFile(findfile, &fdat);\r
- if (h == INVALID_HANDLE_VALUE)\r
- return NULL;\r
- sfree(findfile);\r
-\r
- ret = snew(DirHandle);\r
- ret->h = h;\r
- ret->name = dupstr(fdat.cFileName);\r
- return ret;\r
-}\r
-\r
-char *read_filename(DirHandle *dir)\r
-{\r
- do {\r
-\r
- if (!dir->name) {\r
- WIN32_FIND_DATA fdat;\r
- int ok = FindNextFile(dir->h, &fdat);\r
- if (!ok)\r
- return NULL;\r
- else\r
- dir->name = dupstr(fdat.cFileName);\r
- }\r
-\r
- assert(dir->name);\r
- if (dir->name[0] == '.' &&\r
- (dir->name[1] == '\0' ||\r
- (dir->name[1] == '.' && dir->name[2] == '\0'))) {\r
- sfree(dir->name);\r
- dir->name = NULL;\r
- }\r
-\r
- } while (!dir->name);\r
-\r
- if (dir->name) {\r
- char *ret = dir->name;\r
- dir->name = NULL;\r
- return ret;\r
- } else\r
- return NULL;\r
-}\r
-\r
-void close_directory(DirHandle *dir)\r
-{\r
- FindClose(dir->h);\r
- if (dir->name)\r
- sfree(dir->name);\r
- sfree(dir);\r
-}\r
-\r
-int test_wildcard(char *name, int cmdline)\r
-{\r
- HANDLE fh;\r
- WIN32_FIND_DATA fdat;\r
-\r
- /* First see if the exact name exists. */\r
- if (GetFileAttributes(name) != (DWORD)-1)\r
- return WCTYPE_FILENAME;\r
-\r
- /* Otherwise see if a wildcard match finds anything. */\r
- fh = FindFirstFile(name, &fdat);\r
- if (fh == INVALID_HANDLE_VALUE)\r
- return WCTYPE_NONEXISTENT;\r
-\r
- FindClose(fh);\r
- return WCTYPE_WILDCARD;\r
-}\r
-\r
-struct WildcardMatcher {\r
- HANDLE h;\r
- char *name;\r
- char *srcpath;\r
-};\r
-\r
-/*\r
- * Return a pointer to the portion of str that comes after the last\r
- * slash (or backslash or colon, if `local' is TRUE).\r
- */\r
-static char *stripslashes(char *str, int local)\r
-{\r
- char *p;\r
-\r
- if (local) {\r
- p = strchr(str, ':');\r
- if (p) str = p+1;\r
- }\r
-\r
- p = strrchr(str, '/');\r
- if (p) str = p+1;\r
-\r
- if (local) {\r
- p = strrchr(str, '\\');\r
- if (p) str = p+1;\r
- }\r
-\r
- return str;\r
-}\r
-\r
-WildcardMatcher *begin_wildcard_matching(char *name)\r
-{\r
- HANDLE h;\r
- WIN32_FIND_DATA fdat;\r
- WildcardMatcher *ret;\r
- char *last;\r
-\r
- h = FindFirstFile(name, &fdat);\r
- if (h == INVALID_HANDLE_VALUE)\r
- return NULL;\r
-\r
- ret = snew(WildcardMatcher);\r
- ret->h = h;\r
- ret->srcpath = dupstr(name);\r
- last = stripslashes(ret->srcpath, 1);\r
- *last = '\0';\r
- if (fdat.cFileName[0] == '.' &&\r
- (fdat.cFileName[1] == '\0' ||\r
- (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))\r
- ret->name = NULL;\r
- else\r
- ret->name = dupcat(ret->srcpath, fdat.cFileName, NULL);\r
-\r
- return ret;\r
-}\r
-\r
-char *wildcard_get_filename(WildcardMatcher *dir)\r
-{\r
- while (!dir->name) {\r
- WIN32_FIND_DATA fdat;\r
- int ok = FindNextFile(dir->h, &fdat);\r
-\r
- if (!ok)\r
- return NULL;\r
-\r
- if (fdat.cFileName[0] == '.' &&\r
- (fdat.cFileName[1] == '\0' ||\r
- (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))\r
- dir->name = NULL;\r
- else\r
- dir->name = dupcat(dir->srcpath, fdat.cFileName, NULL);\r
- }\r
-\r
- if (dir->name) {\r
- char *ret = dir->name;\r
- dir->name = NULL;\r
- return ret;\r
- } else\r
- return NULL;\r
-}\r
-\r
-void finish_wildcard_matching(WildcardMatcher *dir)\r
-{\r
- FindClose(dir->h);\r
- if (dir->name)\r
- sfree(dir->name);\r
- sfree(dir->srcpath);\r
- sfree(dir);\r
-}\r
-\r
-int vet_filename(char *name)\r
-{\r
- if (strchr(name, '/') || strchr(name, '\\') || strchr(name, ':'))\r
- return FALSE;\r
-\r
- if (!name[strspn(name, ".")]) /* entirely composed of dots */\r
- return FALSE;\r
-\r
- return TRUE;\r
-}\r
-\r
-int create_directory(char *name)\r
-{\r
- return CreateDirectory(name, NULL) != 0;\r
-}\r
-\r
-char *dir_file_cat(char *dir, char *file)\r
-{\r
- return dupcat(dir, "\\", file, NULL);\r
-}\r
-\r
-/* ----------------------------------------------------------------------\r
- * Platform-specific network handling.\r
- */\r
-\r
-/*\r
- * Be told what socket we're supposed to be using.\r
- */\r
-static SOCKET sftp_ssh_socket = INVALID_SOCKET;\r
-static HANDLE netevent = INVALID_HANDLE_VALUE;\r
-char *do_select(SOCKET skt, int startup)\r
-{\r
- int events;\r
- if (startup)\r
- sftp_ssh_socket = skt;\r
- else\r
- sftp_ssh_socket = INVALID_SOCKET;\r
-\r
- if (p_WSAEventSelect) {\r
- if (startup) {\r
- events = (FD_CONNECT | FD_READ | FD_WRITE |\r
- FD_OOB | FD_CLOSE | FD_ACCEPT);\r
- netevent = CreateEvent(NULL, FALSE, FALSE, NULL);\r
- } else {\r
- events = 0;\r
- }\r
- if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {\r
- switch (p_WSAGetLastError()) {\r
- case WSAENETDOWN:\r
- return "Network is down";\r
- default:\r
- return "WSAEventSelect(): unknown error";\r
- }\r
- }\r
- }\r
- return NULL;\r
-}\r
-extern int select_result(WPARAM, LPARAM);\r
-\r
-int do_eventsel_loop(HANDLE other_event)\r
-{\r
- int n, nhandles, nallhandles, netindex, otherindex;\r
- long next, ticks;\r
- HANDLE *handles;\r
- SOCKET *sklist;\r
- int skcount;\r
- long now = GETTICKCOUNT();\r
-\r
- if (run_timers(now, &next)) {\r
- ticks = next - GETTICKCOUNT();\r
- if (ticks < 0) ticks = 0; /* just in case */\r
- } else {\r
- ticks = INFINITE;\r
- }\r
-\r
- handles = handle_get_events(&nhandles);\r
- handles = sresize(handles, nhandles+2, HANDLE);\r
- nallhandles = nhandles;\r
-\r
- if (netevent != INVALID_HANDLE_VALUE)\r
- handles[netindex = nallhandles++] = netevent;\r
- else\r
- netindex = -1;\r
- if (other_event != INVALID_HANDLE_VALUE)\r
- handles[otherindex = nallhandles++] = other_event;\r
- else\r
- otherindex = -1;\r
-\r
- n = WaitForMultipleObjects(nallhandles, handles, FALSE, ticks);\r
-\r
- if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {\r
- handle_got_event(handles[n - WAIT_OBJECT_0]);\r
- } else if (netindex >= 0 && n == WAIT_OBJECT_0 + netindex) {\r
- WSANETWORKEVENTS things;\r
- SOCKET socket;\r
- extern SOCKET first_socket(int *), next_socket(int *);\r
- extern int select_result(WPARAM, LPARAM);\r
- int i, socketstate;\r
-\r
- /*\r
- * We must not call select_result() for any socket\r
- * until we have finished enumerating within the\r
- * tree. This is because select_result() may close\r
- * the socket and modify the tree.\r
- */\r
- /* Count the active sockets. */\r
- i = 0;\r
- for (socket = first_socket(&socketstate);\r
- socket != INVALID_SOCKET;\r
- socket = next_socket(&socketstate)) i++;\r
-\r
- /* Expand the buffer if necessary. */\r
- sklist = snewn(i, SOCKET);\r
-\r
- /* Retrieve the sockets into sklist. */\r
- skcount = 0;\r
- for (socket = first_socket(&socketstate);\r
- socket != INVALID_SOCKET;\r
- socket = next_socket(&socketstate)) {\r
- sklist[skcount++] = socket;\r
- }\r
-\r
- /* Now we're done enumerating; go through the list. */\r
- for (i = 0; i < skcount; i++) {\r
- WPARAM wp;\r
- socket = sklist[i];\r
- wp = (WPARAM) socket;\r
- if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {\r
- static const struct { int bit, mask; } eventtypes[] = {\r
- {FD_CONNECT_BIT, FD_CONNECT},\r
- {FD_READ_BIT, FD_READ},\r
- {FD_CLOSE_BIT, FD_CLOSE},\r
- {FD_OOB_BIT, FD_OOB},\r
- {FD_WRITE_BIT, FD_WRITE},\r
- {FD_ACCEPT_BIT, FD_ACCEPT},\r
- };\r
- int e;\r
-\r
- noise_ultralight(socket);\r
- noise_ultralight(things.lNetworkEvents);\r
-\r
- for (e = 0; e < lenof(eventtypes); e++)\r
- if (things.lNetworkEvents & eventtypes[e].mask) {\r
- LPARAM lp;\r
- int err = things.iErrorCode[eventtypes[e].bit];\r
- lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);\r
- select_result(wp, lp);\r
- }\r
- }\r
- }\r
-\r
- sfree(sklist);\r
- }\r
-\r
- sfree(handles);\r
-\r
- if (n == WAIT_TIMEOUT) {\r
- now = next;\r
- } else {\r
- now = GETTICKCOUNT();\r
- }\r
-\r
- if (otherindex >= 0 && n == WAIT_OBJECT_0 + otherindex)\r
- return 1;\r
-\r
- return 0;\r
-}\r
-\r
-/*\r
- * Wait for some network data and process it.\r
- *\r
- * We have two variants of this function. One uses select() so that\r
- * it's compatible with WinSock 1. The other uses WSAEventSelect\r
- * and MsgWaitForMultipleObjects, so that we can consistently use\r
- * WSAEventSelect throughout; this enables us to also implement\r
- * ssh_sftp_get_cmdline() using a parallel mechanism.\r
- */\r
-int ssh_sftp_loop_iteration(void)\r
-{\r
- if (p_WSAEventSelect == NULL) {\r
- fd_set readfds;\r
- int ret;\r
- long now = GETTICKCOUNT();\r
-\r
- if (sftp_ssh_socket == INVALID_SOCKET)\r
- return -1; /* doom */\r
-\r
- if (socket_writable(sftp_ssh_socket))\r
- select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_WRITE);\r
-\r
- do {\r
- long next, ticks;\r
- struct timeval tv, *ptv;\r
-\r
- if (run_timers(now, &next)) {\r
- ticks = next - GETTICKCOUNT();\r
- if (ticks <= 0)\r
- ticks = 1; /* just in case */\r
- tv.tv_sec = ticks / 1000;\r
- tv.tv_usec = ticks % 1000 * 1000;\r
- ptv = &tv;\r
- } else {\r
- ptv = NULL;\r
- }\r
-\r
- FD_ZERO(&readfds);\r
- FD_SET(sftp_ssh_socket, &readfds);\r
- ret = p_select(1, &readfds, NULL, NULL, ptv);\r
-\r
- if (ret < 0)\r
- return -1; /* doom */\r
- else if (ret == 0)\r
- now = next;\r
- else\r
- now = GETTICKCOUNT();\r
-\r
- } while (ret == 0);\r
-\r
- select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);\r
-\r
- return 0;\r
- } else {\r
- return do_eventsel_loop(INVALID_HANDLE_VALUE);\r
- }\r
-}\r
-\r
-/*\r
- * Read a command line from standard input.\r
- * \r
- * In the presence of WinSock 2, we can use WSAEventSelect to\r
- * mediate between the socket and stdin, meaning we can send\r
- * keepalives and respond to server events even while waiting at\r
- * the PSFTP command prompt. Without WS2, we fall back to a simple\r
- * fgets.\r
- */\r
-struct command_read_ctx {\r
- HANDLE event;\r
- char *line;\r
-};\r
-\r
-static DWORD WINAPI command_read_thread(void *param)\r
-{\r
- struct command_read_ctx *ctx = (struct command_read_ctx *) param;\r
-\r
- ctx->line = fgetline(stdin);\r
-\r
- SetEvent(ctx->event);\r
-\r
- return 0;\r
-}\r
-\r
-char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok)\r
-{\r
- int ret;\r
- struct command_read_ctx actx, *ctx = &actx;\r
- DWORD threadid;\r
-\r
- fputs(prompt, stdout);\r
- fflush(stdout);\r
-\r
- if ((sftp_ssh_socket == INVALID_SOCKET && no_fds_ok) ||\r
- p_WSAEventSelect == NULL) {\r
- return fgetline(stdin); /* very simple */\r
- }\r
-\r
- /*\r
- * Create a second thread to read from stdin. Process network\r
- * and timing events until it terminates.\r
- */\r
- ctx->event = CreateEvent(NULL, FALSE, FALSE, NULL);\r
- ctx->line = NULL;\r
-\r
- if (!CreateThread(NULL, 0, command_read_thread,\r
- ctx, 0, &threadid)) {\r
- fprintf(stderr, "Unable to create command input thread\n");\r
- cleanup_exit(1);\r
- }\r
-\r
- do {\r
- ret = do_eventsel_loop(ctx->event);\r
-\r
- /* Error return can only occur if netevent==NULL, and it ain't. */\r
- assert(ret >= 0);\r
- } while (ret == 0);\r
-\r
- return ctx->line;\r
-}\r
-\r
-/* ----------------------------------------------------------------------\r
- * Main program. Parse arguments etc.\r
- */\r
-int main(int argc, char *argv[])\r
-{\r
- int ret;\r
-\r
- ret = psftp_main(argc, argv);\r
-\r
- return ret;\r
-}\r