X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/b51259f6a879f69bec5348bddb604d9b3d499941..799dfcfab944cd604ee0850f37e0dcb07f02d84d:/winsftp.c diff --git a/winsftp.c b/winsftp.c index 677bf024..a4dafd4a 100644 --- a/winsftp.c +++ b/winsftp.c @@ -1,45 +1,141 @@ /* - * winsftp.c: the Windows-specific parts of PSFTP. + * winsftp.c: the Windows-specific parts of PSFTP and PSCP. */ #include +#ifndef AUTO_WINSOCK +#ifdef WINSOCK_TWO +#include +#else +#include +#endif +#endif #include "putty.h" #include "psftp.h" -/* - * Be told what socket we're supposed to be using. +/* ---------------------------------------------------------------------- + * Interface to GUI driver program. */ -static SOCKET sftp_ssh_socket; -char *do_select(SOCKET skt, int startup) + +/* This is just a base value from which the main message numbers are + * derived. */ +#define WM_APP_BASE 0x8000 + +/* These two pass a single character value in wParam. They represent + * the visible output from PSCP. */ +#define WM_STD_OUT_CHAR ( WM_APP_BASE+400 ) +#define WM_STD_ERR_CHAR ( WM_APP_BASE+401 ) + +/* These pass a transfer status update. WM_STATS_CHAR passes a single + * character in wParam, and is called repeatedly to pass the name of + * the file, terminated with "\n". WM_STATS_SIZE passes the size of + * the file being transferred in wParam. WM_STATS_ELAPSED is called + * to pass the elapsed time (in seconds) in wParam, and + * WM_STATS_PERCENT passes the percentage of the transfer which is + * complete, also in wParam. */ +#define WM_STATS_CHAR ( WM_APP_BASE+402 ) +#define WM_STATS_SIZE ( WM_APP_BASE+403 ) +#define WM_STATS_PERCENT ( WM_APP_BASE+404 ) +#define WM_STATS_ELAPSED ( WM_APP_BASE+405 ) + +/* These are used at the end of a run to pass an error code in + * wParam: zero means success, nonzero means failure. WM_RET_ERR_CNT + * is used after a copy, and WM_LS_RET_ERR_CNT is used after a file + * list operation. */ +#define WM_RET_ERR_CNT ( WM_APP_BASE+406 ) +#define WM_LS_RET_ERR_CNT ( WM_APP_BASE+407 ) + +/* More transfer status update messages. WM_STATS_DONE passes the + * number of bytes sent so far in wParam. WM_STATS_ETA passes the + * estimated time to completion (in seconds). WM_STATS_RATEBS passes + * the average transfer rate (in bytes per second). */ +#define WM_STATS_DONE ( WM_APP_BASE+408 ) +#define WM_STATS_ETA ( WM_APP_BASE+409 ) +#define WM_STATS_RATEBS ( WM_APP_BASE+410 ) + +#define NAME_STR_MAX 2048 +static char statname[NAME_STR_MAX + 1]; +static unsigned long statsize = 0; +static unsigned long statdone = 0; +static unsigned long stateta = 0; +static unsigned long statratebs = 0; +static int statperct = 0; +static unsigned long statelapsed = 0; + +static HWND gui_hwnd = NULL; + +static void send_msg(HWND h, UINT message, WPARAM wParam) { - if (startup) - sftp_ssh_socket = skt; - else - sftp_ssh_socket = INVALID_SOCKET; - return NULL; + while (!PostMessage(h, message, wParam, 0)) + SleepEx(1000, TRUE); } -extern int select_result(WPARAM, LPARAM); -/* - * Initialize the WinSock driver. - */ -static void init_winsock(void) +void gui_send_char(int is_stderr, int c) { - WORD winsock_ver; - WSADATA wsadata; + unsigned int msg_id = WM_STD_OUT_CHAR; + if (is_stderr) + msg_id = WM_STD_ERR_CHAR; + send_msg(gui_hwnd, msg_id, (WPARAM) c); +} - winsock_ver = MAKEWORD(1, 1); - if (WSAStartup(winsock_ver, &wsadata)) { - fprintf(stderr, "Unable to initialise WinSock"); - cleanup_exit(1); +void gui_send_errcount(int list, int errs) +{ + unsigned int msg_id = WM_RET_ERR_CNT; + if (list) + msg_id = WM_LS_RET_ERR_CNT; + while (!PostMessage(gui_hwnd, msg_id, (WPARAM) errs, 0)) + SleepEx(1000, TRUE); +} + +void gui_update_stats(char *name, unsigned long size, + int percentage, unsigned long elapsed, + unsigned long done, unsigned long eta, + unsigned long ratebs) +{ + unsigned int i; + + if (strcmp(name, statname) != 0) { + for (i = 0; i < strlen(name); ++i) + send_msg(gui_hwnd, WM_STATS_CHAR, (WPARAM) name[i]); + send_msg(gui_hwnd, WM_STATS_CHAR, (WPARAM) '\n'); + strcpy(statname, name); } - if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) { - fprintf(stderr, "WinSock version is incompatible with 1.1"); - cleanup_exit(1); + if (statsize != size) { + send_msg(gui_hwnd, WM_STATS_SIZE, (WPARAM) size); + statsize = size; + } + if (statdone != done) { + send_msg(gui_hwnd, WM_STATS_DONE, (WPARAM) done); + statdone = done; + } + if (stateta != eta) { + send_msg(gui_hwnd, WM_STATS_ETA, (WPARAM) eta); + stateta = eta; + } + if (statratebs != ratebs) { + send_msg(gui_hwnd, WM_STATS_RATEBS, (WPARAM) ratebs); + statratebs = ratebs; } + if (statelapsed != elapsed) { + send_msg(gui_hwnd, WM_STATS_ELAPSED, (WPARAM) elapsed); + statelapsed = elapsed; + } + if (statperct != percentage) { + send_msg(gui_hwnd, WM_STATS_PERCENT, (WPARAM) percentage); + statperct = percentage; + } +} + +void gui_enable(char *arg) +{ + gui_hwnd = (HWND) atoi(arg); } +/* ---------------------------------------------------------------------- + * File access abstraction. + */ + /* * Set local current directory. Returns NULL on success, or else an * error message which must be freed after printing. @@ -79,6 +175,317 @@ char *psftp_getcwd(void) return ret; } +#define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \ + ((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000) +#define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \ + ((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600)) + +struct RFile { + HANDLE h; +}; + +RFile *open_existing_file(char *name, unsigned long *size, + unsigned long *mtime, unsigned long *atime) +{ + HANDLE h; + RFile *ret; + + h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, 0, 0); + if (h == INVALID_HANDLE_VALUE) + return NULL; + + ret = snew(RFile); + ret->h = h; + + if (size) + *size = GetFileSize(h, NULL); + + if (mtime || atime) { + FILETIME actime, wrtime; + GetFileTime(h, NULL, &actime, &wrtime); + if (atime) + TIME_WIN_TO_POSIX(actime, *atime); + if (mtime) + TIME_WIN_TO_POSIX(wrtime, *mtime); + } + + return ret; +} + +int read_from_file(RFile *f, void *buffer, int length) +{ + int ret, read; + ret = ReadFile(f->h, buffer, length, &read, NULL); + if (!ret) + return -1; /* error */ + else + return read; +} + +void close_rfile(RFile *f) +{ + CloseHandle(f->h); + sfree(f); +} + +struct WFile { + HANDLE h; +}; + +WFile *open_new_file(char *name) +{ + HANDLE h; + WFile *ret; + + h = CreateFile(name, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (h == INVALID_HANDLE_VALUE) + return NULL; + + ret = snew(WFile); + ret->h = h; + + return ret; +} + +int write_to_file(WFile *f, void *buffer, int length) +{ + int ret, written; + ret = WriteFile(f->h, buffer, length, &written, NULL); + if (!ret) + return -1; /* error */ + else + return written; +} + +void set_file_times(WFile *f, unsigned long mtime, unsigned long atime) +{ + FILETIME actime, wrtime; + TIME_POSIX_TO_WIN(atime, actime); + TIME_POSIX_TO_WIN(mtime, wrtime); + SetFileTime(f->h, NULL, &actime, &wrtime); +} + +void close_wfile(WFile *f) +{ + CloseHandle(f->h); + sfree(f); +} + +int file_type(char *name) +{ + DWORD attr; + attr = GetFileAttributes(name); + /* We know of no `weird' files under Windows. */ + if (attr == (DWORD)-1) + return FILE_TYPE_NONEXISTENT; + else if (attr & FILE_ATTRIBUTE_DIRECTORY) + return FILE_TYPE_DIRECTORY; + else + return FILE_TYPE_FILE; +} + +struct DirHandle { + HANDLE h; + char *name; +}; + +DirHandle *open_directory(char *name) +{ + HANDLE h; + WIN32_FIND_DATA fdat; + char *findfile; + DirHandle *ret; + + /* To enumerate files in dir `foo', we search for `foo/*'. */ + findfile = dupcat(name, "/*", NULL); + h = FindFirstFile(findfile, &fdat); + if (h == INVALID_HANDLE_VALUE) + return NULL; + + ret = snew(DirHandle); + ret->h = h; + ret->name = dupstr(fdat.cFileName); + return ret; +} + +char *read_filename(DirHandle *dir) +{ + if (!dir->name) { + WIN32_FIND_DATA fdat; + int ok = FindNextFile(dir->h, &fdat); + + if (ok) + dir->name = dupstr(fdat.cFileName); + } + + if (dir->name) { + char *ret = dir->name; + dir->name = NULL; + return ret; + } else + return NULL; +} + +void close_directory(DirHandle *dir) +{ + FindClose(dir->h); + if (dir->name) + sfree(dir->name); + sfree(dir); +} + +int test_wildcard(char *name, int cmdline) +{ + HANDLE fh; + WIN32_FIND_DATA fdat; + + /* First see if the exact name exists. */ + if (GetFileAttributes(name) != (DWORD)-1) + return WCTYPE_FILENAME; + + /* Otherwise see if a wildcard match finds anything. */ + fh = FindFirstFile(name, &fdat); + if (fh == INVALID_HANDLE_VALUE) + return WCTYPE_NONEXISTENT; + + FindClose(fh); + return WCTYPE_WILDCARD; +} + +struct WildcardMatcher { + HANDLE h; + char *name; + char *srcpath; +}; + +/* + * Return a pointer to the portion of str that comes after the last + * slash (or backslash or colon, if `local' is TRUE). + */ +static char *stripslashes(char *str, int local) +{ + char *p; + + if (local) { + p = strchr(str, ':'); + if (p) str = p+1; + } + + p = strrchr(str, '/'); + if (p) str = p+1; + + if (local) { + p = strrchr(str, '\\'); + if (p) str = p+1; + } + + return str; +} + +WildcardMatcher *begin_wildcard_matching(char *name) +{ + HANDLE h; + WIN32_FIND_DATA fdat; + WildcardMatcher *ret; + char *last; + + h = FindFirstFile(name, &fdat); + if (h == INVALID_HANDLE_VALUE) + return NULL; + + ret = snew(WildcardMatcher); + ret->h = h; + ret->srcpath = dupstr(name); + last = stripslashes(ret->srcpath, 1); + *last = '\0'; + if (fdat.cFileName[0] == '.' && + (fdat.cFileName[1] == '\0' || + (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0'))) + ret->name = NULL; + else + ret->name = dupcat(ret->srcpath, fdat.cFileName, NULL); + + return ret; +} + +char *wildcard_get_filename(WildcardMatcher *dir) +{ + while (!dir->name) { + WIN32_FIND_DATA fdat; + int ok = FindNextFile(dir->h, &fdat); + + if (!ok) + return NULL; + + if (fdat.cFileName[0] == '.' && + (fdat.cFileName[1] == '\0' || + (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0'))) + dir->name = NULL; + else + dir->name = dupcat(dir->srcpath, fdat.cFileName, NULL); + } + + if (dir->name) { + char *ret = dir->name; + dir->name = NULL; + return ret; + } else + return NULL; +} + +void finish_wildcard_matching(WildcardMatcher *dir) +{ + FindClose(dir->h); + if (dir->name) + sfree(dir->name); + sfree(dir->srcpath); + sfree(dir); +} + +int create_directory(char *name) +{ + return CreateDirectory(name, NULL) != 0; +} + +/* ---------------------------------------------------------------------- + * Platform-specific network handling. + */ + +/* + * Be told what socket we're supposed to be using. + */ +static SOCKET sftp_ssh_socket; +char *do_select(SOCKET skt, int startup) +{ + if (startup) + sftp_ssh_socket = skt; + else + sftp_ssh_socket = INVALID_SOCKET; + return NULL; +} +extern int select_result(WPARAM, LPARAM); + +/* + * Initialize the WinSock driver. + */ +static void init_winsock(void) +{ + WORD winsock_ver; + WSADATA wsadata; + + winsock_ver = MAKEWORD(1, 1); + if (WSAStartup(winsock_ver, &wsadata)) { + fprintf(stderr, "Unable to initialise WinSock"); + cleanup_exit(1); + } + if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) { + fprintf(stderr, "WinSock version is incompatible with 1.1"); + cleanup_exit(1); + } +} + /* * Wait for some network data and process it. */ @@ -98,7 +505,7 @@ int ssh_sftp_loop_iteration(void) return 0; } -/* +/* ---------------------------------------------------------------------- * Main program. Parse arguments etc. */ int main(int argc, char *argv[])