diff --git a/Recipe b/Recipe index 4422441b..a9b0ec9d 100644 --- a/Recipe +++ b/Recipe @@ -139,7 +139,8 @@ putty : [G] GUITERM NONSSH WINSSH be_all WINMISC win_res.res LIBS1 puttytel : [G] GUITERM NONSSH be_nossh WINMISC win_res.res LIBS1 plink : [C] plink console NONSSH WINSSH be_all logging WINMISC + plink.res LIBS2 -pscp : [C] scp console WINSSH be_none SFTP wildcard WINMISC scp.res LIBS1 +pscp : [C] scp winsftp console WINSSH be_none SFTP wildcard WINMISC + + scp.res LIBS1 psftp : [C] psftp winsftp console WINSSH be_none SFTP WINMISC scp.res LIBS1 pageant : [G] pageant sshrsa sshpubk sshdes sshbn sshmd5 version tree234 @@ -159,6 +160,7 @@ puttytel : [X] UXTERM uxmisc misc ldisc settings pty uxsel be_nossh uxstore plink : [U] uxplink uxcons NONSSH UXSSH be_all logging UXMISC signal ux_x11 +pscp : [U] scp uxsftp uxcons UXSSH be_none SFTP wildcard UXMISC psftp : [U] psftp uxsftp uxcons UXSSH be_none SFTP UXMISC PuTTY : [M] terminal wcwidth ldiscucs logging be_all mac macdlg macevlog diff --git a/psftp.h b/psftp.h index 30fbd881..8eb11877 100644 --- a/psftp.h +++ b/psftp.h @@ -1,6 +1,6 @@ /* - * psftp.h: interface between psftp.c and each platform-specific - * SFTP module. + * psftp.h: interface between psftp.c / scp.c and each + * platform-specific SFTP module. */ #ifndef PUTTY_PSFTP_H @@ -19,6 +19,13 @@ char *psftp_getcwd(void); */ char *psftp_lcd(char *newdir); +/* + * Retrieve file times on a local file. Must return two unsigned + * longs in POSIX time_t format. + */ +void get_file_times(char *filename, unsigned long *mtime, + unsigned long *atime); + /* * One iteration of the PSFTP event loop: wait for network data and * process it, once. @@ -31,4 +38,107 @@ int ssh_sftp_loop_iteration(void); */ int psftp_main(int argc, char *argv[]); +/* + * These functions are used by PSCP to transmit progress updates + * and error information to a GUI window managing it. This will + * probably only ever be supported on Windows, so these functions + * can safely be stubs on all other platforms. + */ +void gui_update_stats(char *name, unsigned long size, + int percentage, unsigned long elapsed, + unsigned long done, unsigned long eta, + unsigned long ratebs); +void gui_send_errcount(int list, int errs); +void gui_send_char(int is_stderr, int c); +void gui_enable(char *arg); + +/* + * It's likely that a given platform's implementation of file + * transfer utilities is going to want to do things with them that + * aren't present in stdio. Hence we supply an alternative + * abstraction for file access functions. + * + * This abstraction tells you the size and access times when you + * open an existing file (platforms may choose the meaning of the + * file times if it's not clear; whatever they choose will be what + * PSCP sends to the server as mtime and atime), and lets you set + * the times when saving a new file. + * + * On the other hand, the abstraction is pretty simple: it supports + * only opening a file and reading it, or creating a file and + * writing it. (FIXME: to use this in PSFTP it will also need to + * support seeking to a starting point for restarted transfers.) + * None of this read-and-write, seeking-back-and-forth stuff. + */ +typedef struct RFile RFile; +typedef struct WFile WFile; +/* Output params size, mtime and atime can all be NULL if desired */ +RFile *open_existing_file(char *name, unsigned long *size, + unsigned long *mtime, unsigned long *atime); +/* Returns <0 on error, 0 on eof, or number of bytes read, as usual */ +int read_from_file(RFile *f, void *buffer, int length); +/* Closes and frees the RFile */ +void close_rfile(RFile *f); +WFile *open_new_file(char *name); +/* Returns <0 on error, 0 on eof, or number of bytes written, as usual */ +int write_to_file(WFile *f, void *buffer, int length); +void set_file_times(WFile *f, unsigned long mtime, unsigned long atime); +/* Closes and frees the WFile */ +void close_wfile(WFile *f); + +/* + * Determine the type of a file: nonexistent, file, directory or + * weird. `weird' covers anything else - named pipes, Unix sockets, + * device files, fish, badgers, you name it. Things marked `weird' + * will be skipped over in recursive file transfers, so the only + * real reason for not lumping them in with `nonexistent' is that + * it allows a slightly more sane error message. + */ +enum { + FILE_TYPE_NONEXISTENT, FILE_TYPE_FILE, FILE_TYPE_DIRECTORY, FILE_TYPE_WEIRD +}; +int file_type(char *name); + +/* + * Read all the file names out of a directory. + */ +typedef struct DirHandle DirHandle; +DirHandle *open_directory(char *name); +/* The string returned from this will need freeing if not NULL */ +char *read_filename(DirHandle *dir); +void close_directory(DirHandle *dir); + +/* + * Test a filespec to see whether it's a local wildcard or not. + * Return values: + * + * - WCTYPE_WILDCARD (this is a wildcard). + * - WCTYPE_FILENAME (this is a single file name). + * - WCTYPE_NONEXISTENT (whichever it was, nothing of that name exists). + * + * Some platforms may choose not to support local wildcards when + * they come from the command line; in this case they simply never + * return WCTYPE_WILDCARD, but still test the file's existence. + * (However, all platforms will probably want to support wildcards + * inside the PSFTP CLI.) + */ +enum { + WCTYPE_NONEXISTENT, WCTYPE_FILENAME, WCTYPE_WILDCARD +}; +int test_wildcard(char *name, int cmdline); + +/* + * Actually return matching file names for a local wildcard. + */ +typedef struct WildcardMatcher WildcardMatcher; +WildcardMatcher *begin_wildcard_matching(char *name); +/* The string returned from this will need freeing if not NULL */ +char *wildcard_get_filename(WildcardMatcher *dir); +void finish_wildcard_matching(WildcardMatcher *dir); + +/* + * Create a directory. Returns 0 on error, !=0 on success. + */ +int create_directory(char *name); + #endif /* PUTTY_PSFTP_H */ diff --git a/putty.h b/putty.h index 9a35849e..026448b7 100644 --- a/putty.h +++ b/putty.h @@ -845,5 +845,6 @@ Filename filename_from_str(const char *string); const char *filename_to_str(const Filename *fn); int filename_equal(Filename f1, Filename f2); int filename_is_null(Filename fn); +char *get_username(void); /* return value needs freeing */ #endif diff --git a/scp.c b/scp.c index 8c05c973..53cb60ab 100644 --- a/scp.c +++ b/scp.c @@ -12,14 +12,6 @@ * to licensing issues.) */ -#include -#ifndef AUTO_WINSOCK -#ifdef WINSOCK_TWO -#include -#else -#include -#endif -#endif #include #include #include @@ -29,54 +21,11 @@ #define PUTTY_DO_GLOBALS #include "putty.h" +#include "psftp.h" #include "ssh.h" #include "sftp.h" -#include "winstuff.h" #include "storage.h" -#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)) - -/* GUI Adaptation - Sept 2000 */ - -/* 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 ) - static int list = 0; static int verbose = 0; static int recursive = 0; @@ -86,17 +35,7 @@ static int statistics = 1; static int prev_stats_len = 0; static int scp_unsafe_mode = 0; static int errs = 0; -/* GUI Adaptation - Sept 2000 */ -#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 int gui_mode = 0; -static char *gui_hwnd = NULL; static int using_sftp = 0; static Backend *back; @@ -106,14 +45,6 @@ static Config cfg; static void source(char *src); static void rsource(char *src); static void sink(char *targ, char *src); -/* GUI Adaptation - Sept 2000 */ -static void tell_char(FILE * stream, char c); -static void tell_str(FILE * stream, char *str); -static void tell_user(FILE * stream, char *fmt, ...); -static void gui_update_stats(char *name, unsigned long size, - int percentage, unsigned long elapsed, - unsigned long done, unsigned long eta, - unsigned long ratebs); /* * The maximum amount of queued data we accept before we stop and @@ -132,23 +63,12 @@ void ldisc_send(void *handle, char *buf, int len, int interactive) assert(len == 0); } -/* GUI Adaptation - Sept 2000 */ -static void send_msg(HWND h, UINT message, WPARAM wParam) -{ - while (!PostMessage(h, message, wParam, 0)) - SleepEx(1000, TRUE); -} - static void tell_char(FILE * stream, char c) { if (!gui_mode) fputc(c, stream); - else { - unsigned int msg_id = WM_STD_OUT_CHAR; - if (stream == stderr) - msg_id = WM_STD_ERR_CHAR; - send_msg((HWND) atoi(gui_hwnd), msg_id, (WPARAM) c); - } + else + gui_send_char(stream == stderr, c); } static void tell_str(FILE * stream, char *str) @@ -172,48 +92,6 @@ static void tell_user(FILE * stream, char *fmt, ...) sfree(str2); } -static 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((HWND) atoi(gui_hwnd), WM_STATS_CHAR, - (WPARAM) name[i]); - send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM) '\n'); - strcpy(statname, name); - } - if (statsize != size) { - send_msg((HWND) atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM) size); - statsize = size; - } - if (statdone != done) { - send_msg((HWND) atoi(gui_hwnd), WM_STATS_DONE, (WPARAM) done); - statdone = done; - } - if (stateta != eta) { - send_msg((HWND) atoi(gui_hwnd), WM_STATS_ETA, (WPARAM) eta); - stateta = eta; - } - if (statratebs != ratebs) { - send_msg((HWND) atoi(gui_hwnd), WM_STATS_RATEBS, (WPARAM) ratebs); - statratebs = ratebs; - } - if (statelapsed != elapsed) { - send_msg((HWND) atoi(gui_hwnd), WM_STATS_ELAPSED, - (WPARAM) elapsed); - statelapsed = elapsed; - } - if (statperct != percentage) { - send_msg((HWND) atoi(gui_hwnd), WM_STATS_PERCENT, - (WPARAM) percentage); - statperct = percentage; - } -} - /* * Print an error message and perform a fatal exit. */ @@ -230,14 +108,8 @@ void fatalbox(char *fmt, ...) sfree(str2); errs++; - if (gui_mode) { - unsigned int msg_id = WM_RET_ERR_CNT; - if (list) - msg_id = WM_LS_RET_ERR_CNT; - while (!PostMessage - ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs, - 0 /*lParam */ ))SleepEx(1000, TRUE); - } + if (gui_mode) + gui_send_errcount(list, errs); cleanup_exit(1); } @@ -254,14 +126,8 @@ void modalfatalbox(char *fmt, ...) sfree(str2); errs++; - if (gui_mode) { - unsigned int msg_id = WM_RET_ERR_CNT; - if (list) - msg_id = WM_LS_RET_ERR_CNT; - while (!PostMessage - ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs, - 0 /*lParam */ ))SleepEx(1000, TRUE); - } + if (gui_mode) + gui_send_errcount(list, errs); cleanup_exit(1); } @@ -278,32 +144,12 @@ void connection_fatal(void *frontend, char *fmt, ...) sfree(str2); errs++; - if (gui_mode) { - unsigned int msg_id = WM_RET_ERR_CNT; - if (list) - msg_id = WM_LS_RET_ERR_CNT; - while (!PostMessage - ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs, - 0 /*lParam */ ))SleepEx(1000, TRUE); - } + if (gui_mode) + gui_send_errcount(list, errs); cleanup_exit(1); } -/* - * Be told what socket we're supposed to be using. - */ -static SOCKET scp_ssh_socket; -char *do_select(SOCKET skt, int startup) -{ - if (startup) - scp_ssh_socket = skt; - else - scp_ssh_socket = INVALID_SOCKET; - return NULL; -} -extern int select_result(WPARAM, LPARAM); - /* * In pscp, all agent requests should be synchronous, so this is a * never-called stub. @@ -373,17 +219,6 @@ int from_backend(void *frontend, int is_stderr, const char *data, int datalen) return 0; } -static int scp_process_network_event(void) -{ - fd_set readfds; - - FD_ZERO(&readfds); - FD_SET(scp_ssh_socket, &readfds); - if (select(1, &readfds, NULL, NULL, NULL) < 0) - return 0; /* doom */ - select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ); - return 1; -} static int ssh_scp_recv(unsigned char *buf, int len) { outptr = buf; @@ -412,7 +247,7 @@ static int ssh_scp_recv(unsigned char *buf, int len) } while (outlen > 0) { - if (!scp_process_network_event()) + if (ssh_sftp_loop_iteration() < 0) return 0; /* doom */ } @@ -424,15 +259,9 @@ static int ssh_scp_recv(unsigned char *buf, int len) */ static void ssh_scp_init(void) { - if (scp_ssh_socket == INVALID_SOCKET) - return; while (!back->sendok(backhandle)) { - fd_set readfds; - FD_ZERO(&readfds); - FD_SET(scp_ssh_socket, &readfds); - if (select(1, &readfds, NULL, NULL, NULL) < 0) + if (ssh_sftp_loop_iteration() < 0) return; /* doom */ - select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ); } using_sftp = !ssh_fallback_cmd(backhandle); if (verbose) { @@ -465,14 +294,8 @@ static void bump(char *fmt, ...) ssh_scp_recv(&ch, 1); } - if (gui_mode) { - unsigned int msg_id = WM_RET_ERR_CNT; - if (list) - msg_id = WM_LS_RET_ERR_CNT; - while (!PostMessage - ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs, - 0 /*lParam */ ))SleepEx(1000, TRUE); - } + if (gui_mode) + gui_send_errcount(list, errs); cleanup_exit(1); } @@ -484,7 +307,7 @@ static void do_cmd(char *host, char *user, char *cmd) { const char *err; char *realhost; - DWORD namelen; + void *logctx; if (host == NULL || host[0] == '\0') bump("Empty host name"); @@ -558,16 +381,16 @@ static void do_cmd(char *host, char *user, char *cmd) strncpy(cfg.username, user, sizeof(cfg.username) - 1); cfg.username[sizeof(cfg.username) - 1] = '\0'; } else if (cfg.username[0] == '\0') { - namelen = 0; - if (GetUserName(user, &namelen) == FALSE) + user = get_username(); + if (!user) bump("Empty user name"); - user = snewn(namelen, char); - GetUserName(user, &namelen); - if (verbose) - tell_user(stderr, "Guessing user name: %s", user); - strncpy(cfg.username, user, sizeof(cfg.username) - 1); - cfg.username[sizeof(cfg.username) - 1] = '\0'; - free(user); + else { + if (verbose) + tell_user(stderr, "Guessing user name: %s", user); + strncpy(cfg.username, user, sizeof(cfg.username) - 1); + cfg.username[sizeof(cfg.username) - 1] = '\0'; + sfree(user); + } } /* @@ -632,11 +455,10 @@ static void print_stats(char *name, unsigned long size, unsigned long done, pct = (int) (100 * (done * 1.0 / size)); - if (gui_mode) - /* GUI Adaptation - Sept 2000 */ + if (gui_mode) { gui_update_stats(name, size, pct, elap, done, eta, (unsigned long) ratebs); - else { + } else { len = printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%", name, done / 1024, ratebs / 1024.0, etastr, pct); if (len < prev_stats_len) @@ -991,7 +813,7 @@ int scp_send_filedata(char *data, int len) * we have space in the buffer again. */ while (bufsize > MAX_SCP_BUFSIZE) { - if (!scp_process_network_event()) + if (ssh_sftp_loop_iteration() < 0) return 1; bufsize = back->sendbuffer(backhandle); } @@ -1645,20 +1467,23 @@ static void run_err(const char *fmt, ...) static void source(char *src) { unsigned long size; + unsigned long mtime, atime; char *last; - HANDLE f; - DWORD attr; + RFile *f; + int attr; unsigned long i; unsigned long stat_bytes; time_t stat_starttime, stat_lasttime; - attr = GetFileAttributes(src); - if (attr == (DWORD) - 1) { - run_err("%s: No such file or directory", src); + attr = file_type(src); + if (attr == FILE_TYPE_NONEXISTENT || + attr == FILE_TYPE_WEIRD) { + run_err("%s: %s file or directory", src, + (attr == FILE_TYPE_WEIRD ? "Not a" : "No such")); return; } - if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + if (attr == FILE_TYPE_DIRECTORY) { if (recursive) { /* * Avoid . and .. directories. @@ -1690,24 +1515,16 @@ static void source(char *src) if (last == src && strchr(src, ':') != NULL) last = strchr(src, ':') + 1; - f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, 0, 0); - if (f == INVALID_HANDLE_VALUE) { + f = open_existing_file(src, &size, &mtime, &atime); + if (f == NULL) { run_err("%s: Cannot open file", src); return; } - if (preserve) { - FILETIME actime, wrtime; - unsigned long mtime, atime; - GetFileTime(f, NULL, &actime, &wrtime); - TIME_WIN_TO_POSIX(actime, atime); - TIME_WIN_TO_POSIX(wrtime, mtime); if (scp_send_filetimes(mtime, atime)) return; } - size = GetFileSize(f, NULL); if (verbose) tell_user(stderr, "Sending file %s, size=%lu", last, size); if (scp_send_filename(last, size, 0644)) @@ -1719,11 +1536,11 @@ static void source(char *src) for (i = 0; i < size; i += 4096) { char transbuf[4096]; - DWORD j, k = 4096; + int j, k = 4096; if (i + k > size) k = size - i; - if (!ReadFile(f, transbuf, k, &j, NULL) || j != k) { + if ((j = read_from_file(f, transbuf, k)) != k) { if (statistics) printf("\n"); bump("%s: Read error", src); @@ -1741,7 +1558,7 @@ static void source(char *src) } } - CloseHandle(f); + close_rfile(f); (void) scp_send_finish(); } @@ -1751,11 +1568,9 @@ static void source(char *src) */ static void rsource(char *src) { - char *last, *findfile; + char *last; char *save_target; - HANDLE dir; - WIN32_FIND_DATA fdat; - int ok; + DirHandle *dir; if ((last = strrchr(src, '/')) == NULL) last = src; @@ -1775,22 +1590,17 @@ static void rsource(char *src) if (scp_send_dirname(last, 0755)) return; - findfile = dupcat(src, "/*", NULL); - dir = FindFirstFile(findfile, &fdat); - ok = (dir != INVALID_HANDLE_VALUE); - while (ok) { - if (strcmp(fdat.cFileName, ".") == 0 || - strcmp(fdat.cFileName, "..") == 0) { - /* ignore . and .. */ - } else { - char *foundfile = dupcat(src, "/", fdat.cFileName, NULL); + dir = open_directory(src); + if (dir != NULL) { + char *filename; + while ((filename = read_filename(dir)) != NULL) { + char *foundfile = dupcat(src, "/", filename, NULL); source(foundfile); sfree(foundfile); + sfree(filename); } - ok = FindNextFile(dir, &fdat); } - FindClose(dir); - sfree(findfile); + close_directory(dir); (void) scp_send_enddir(); @@ -1805,16 +1615,16 @@ static void sink(char *targ, char *src) char *destfname; int targisdir = 0; int exists; - DWORD attr; - HANDLE f; + int attr; + WFile *f; unsigned long received; int wrerror = 0; unsigned long stat_bytes; time_t stat_starttime, stat_lasttime; char *stat_name; - attr = GetFileAttributes(targ); - if (attr != (DWORD) - 1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + attr = file_type(targ); + if (attr == FILE_TYPE_DIRECTORY) targisdir = 1; if (targetshouldbedirectory && !targisdir) @@ -1912,16 +1722,16 @@ static void sink(char *targ, char *src) */ destfname = dupstr(targ); } - attr = GetFileAttributes(destfname); - exists = (attr != (DWORD) - 1); + attr = file_type(destfname); + exists = (attr != FILE_TYPE_NONEXISTENT); if (act.action == SCP_SINK_DIR) { - if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) { + if (exists && attr != FILE_TYPE_DIRECTORY) { run_err("%s: Not a directory", destfname); continue; } if (!exists) { - if (!CreateDirectory(destfname, NULL)) { + if (!create_directory(destfname)) { run_err("%s: Cannot create directory", destfname); continue; } @@ -1931,9 +1741,8 @@ static void sink(char *targ, char *src) continue; } - f = CreateFile(destfname, GENERIC_WRITE, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); - if (f == INVALID_HANDLE_VALUE) { + f = open_new_file(destfname); + if (f == NULL) { run_err("%s: Cannot create file", destfname); continue; } @@ -1949,17 +1758,16 @@ static void sink(char *targ, char *src) received = 0; while (received < act.size) { char transbuf[4096]; - DWORD blksize, read, written; + int blksize, read; blksize = 4096; - if (blksize > act.size - received) + if (blksize > (int)(act.size - received)) blksize = act.size - received; read = scp_recv_filedata(transbuf, blksize); if (read <= 0) bump("Lost connection"); if (wrerror) continue; - if (!WriteFile(f, transbuf, read, &written, NULL) || - written != read) { + if (write_to_file(f, transbuf, read) != (int)read) { wrerror = 1; /* FIXME: in sftp we can actually abort the transfer */ if (statistics) @@ -1980,13 +1788,10 @@ static void sink(char *targ, char *src) received += read; } if (act.settime) { - FILETIME actime, wrtime; - TIME_POSIX_TO_WIN(act.atime, actime); - TIME_POSIX_TO_WIN(act.mtime, wrtime); - SetFileTime(f, NULL, &actime, &wrtime); + set_file_times(f, act.mtime, act.atime); } - CloseHandle(f); + close_wfile(f); if (wrerror) { run_err("%s: Write error", destfname); continue; @@ -2004,7 +1809,7 @@ static void toremote(int argc, char *argv[]) { char *src, *targ, *host, *user; char *cmd; - int i; + int i, wc_type; targ = argv[argc - 1]; @@ -2031,18 +1836,14 @@ static void toremote(int argc, char *argv[]) } if (argc == 2) { - /* Find out if the source filespec covers multiple files - if so, we should set the targetshouldbedirectory flag */ - HANDLE fh; - WIN32_FIND_DATA fdat; if (colon(argv[0]) != NULL) bump("%s: Remote to remote not supported", argv[0]); - fh = FindFirstFile(argv[0], &fdat); - if (fh == INVALID_HANDLE_VALUE) + + wc_type = test_wildcard(argv[0], 1); + if (wc_type == WCTYPE_NONEXISTENT) bump("%s: No such file or directory\n", argv[0]); - if (FindNextFile(fh, &fdat)) + else if (wc_type == WCTYPE_WILDCARD) targetshouldbedirectory = 1; - FindClose(fh); } cmd = dupprintf("scp%s%s%s%s -t %s", @@ -2056,9 +1857,6 @@ static void toremote(int argc, char *argv[]) scp_source_setup(targ, targetshouldbedirectory); for (i = 0; i < argc - 1; i++) { - char *srcpath, *last; - HANDLE dir; - WIN32_FIND_DATA fdat; src = argv[i]; if (colon(src) != NULL) { tell_user(stderr, "%s: Remote to remote not supported\n", src); @@ -2066,49 +1864,30 @@ static void toremote(int argc, char *argv[]) continue; } - /* - * Trim off the last pathname component of `src', to - * provide the base pathname which will be prepended to - * filenames returned from Find{First,Next}File. - */ - srcpath = dupstr(src); - last = stripslashes(srcpath, 1); - *last = '\0'; - - dir = FindFirstFile(src, &fdat); - if (dir == INVALID_HANDLE_VALUE) { + wc_type = test_wildcard(src, 1); + if (wc_type == WCTYPE_NONEXISTENT) { run_err("%s: No such file or directory", src); continue; - } - do { + } else if (wc_type == WCTYPE_FILENAME) { + source(src); + continue; + } else { + WildcardMatcher *wc; char *filename; - /* - * Ensure that . and .. are never matched by wildcards, - * but only by deliberate action. - */ - if (!strcmp(fdat.cFileName, ".") || - !strcmp(fdat.cFileName, "..")) { - /* - * Find*File has returned a special dir. We require - * that _either_ `src' ends in a backslash followed - * by that string, _or_ `src' is precisely that - * string. - */ - int len = strlen(src), dlen = strlen(fdat.cFileName); - if (len == dlen && !strcmp(src, fdat.cFileName)) { - /* ok */ ; - } else if (len > dlen + 1 && src[len - dlen - 1] == '\\' && - !strcmp(src + len - dlen, fdat.cFileName)) { - /* ok */ ; - } else - continue; /* ignore this one */ + + wc = begin_wildcard_matching(src); + if (wc == NULL) { + run_err("%s: No such file or directory", src); + continue; } - filename = dupcat(srcpath, fdat.cFileName, NULL); - source(filename); - sfree(filename); - } while (FindNextFile(dir, &fdat)); - FindClose(dir); - sfree(srcpath); + + while ((filename = wildcard_get_filename(wc)) != NULL) { + source(filename); + sfree(filename); + } + + finish_wildcard_matching(wc); + } } } @@ -2222,21 +2001,6 @@ static void get_dir_list(int argc, char *argv[]) } } -/* - * Initialize the Win$ock driver. - */ -static void init_winsock(void) -{ - WORD winsock_ver; - WSADATA wsadata; - - winsock_ver = MAKEWORD(1, 1); - if (WSAStartup(winsock_ver, &wsadata)) - bump("Unable to initialise WinSock"); - if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) - bump("WinSock version is incompatible with 1.1"); -} - /* * Short description of parameters. */ @@ -2288,18 +2052,22 @@ void cmdline_error(char *p, ...) } /* - * Main program (no, really?) + * Main program. (Called `psftp_main' because it gets called from + * *sftp.c; bit silly, I know, but it had to be called _something_.) */ -int main(int argc, char *argv[]) +int psftp_main(int argc, char *argv[]) { int i; default_protocol = PROT_TELNET; - flags = FLAG_STDERR | FLAG_SYNCAGENT; + flags = FLAG_STDERR +#ifdef FLAG_SYNCAGENT + | FLAG_SYNCAGENT +#endif + ; cmdline_tooltype = TOOLTYPE_FILETRANSFER; ssh_get_line = &console_get_line; - init_winsock(); sk_init(); for (i = 1; i < argc; i++) { @@ -2324,7 +2092,7 @@ int main(int argc, char *argv[]) } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0) { usage(); } else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) { - gui_hwnd = argv[++i]; + gui_enable(argv[++i]); gui_mode = 1; console_batch_mode = TRUE; } else if (strcmp(argv[i], "-ls") == 0) { @@ -2367,18 +2135,11 @@ int main(int argc, char *argv[]) back->special(backhandle, TS_EOF); ssh_scp_recv(&ch, 1); } - WSACleanup(); random_save_seed(); - /* GUI Adaptation - August 2000 */ - if (gui_mode) { - unsigned int msg_id = WM_RET_ERR_CNT; - if (list) - msg_id = WM_LS_RET_ERR_CNT; - while (!PostMessage - ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs, - 0 /*lParam */ ))SleepEx(1000, TRUE); - } + if (gui_mode) + gui_send_errcount(list, errs); + return (errs == 0 ? 0 : 1); } diff --git a/settings.c b/settings.c index be43f31c..d5e0d265 100644 --- a/settings.c +++ b/settings.c @@ -29,6 +29,7 @@ static void gpps(void *handle, const char *name, const char *def, pdef = platform_default_s(name); if (pdef) { strncpy(val, pdef, len); + sfree(pdef); } else { strncpy(val, def, len); } diff --git a/unix/uxmisc.c b/unix/uxmisc.c index 6889bcaf..d1afa44a 100644 --- a/unix/uxmisc.c +++ b/unix/uxmisc.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "putty.h" @@ -19,6 +21,8 @@ unsigned long getticks(void) return tv.tv_sec * 1000000 + tv.tv_usec; } + + Filename filename_from_str(const char *str) { Filename ret; @@ -57,3 +61,43 @@ void dputs(char *buf) fflush(debug_fp); } #endif + +char *get_username(void) +{ + struct passwd *p; + uid_t uid = getuid(); + char *user, *ret = NULL; + + /* + * First, find who we think we are using getlogin. If this + * agrees with our uid, we'll go along with it. This should + * allow sharing of uids between several login names whilst + * coping correctly with people who have su'ed. + */ + user = getlogin(); + setpwent(); + if (user) + p = getpwnam(user); + else + p = NULL; + if (p && p->pw_uid == uid) { + /* + * The result of getlogin() really does correspond to + * our uid. Fine. + */ + ret = user; + } else { + /* + * If that didn't work, for whatever reason, we'll do + * the simpler version: look up our uid in the password + * file and map it straight to a name. + */ + p = getpwuid(uid); + if (!p) + return NULL; + ret = p->pw_name; + } + endpwent(); + + return dupstr(ret); +} diff --git a/unix/uxplink.c b/unix/uxplink.c index e8b616eb..8d0e169a 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -74,48 +74,11 @@ static Config cfg; char *platform_default_s(const char *name) { if (!strcmp(name, "X11Display")) - return getenv("DISPLAY"); + return dupstr(getenv("DISPLAY")); if (!strcmp(name, "TermType")) - return getenv("TERM"); - if (!strcmp(name, "UserName")) { - /* - * Remote login username will default to the local username. - */ - struct passwd *p; - uid_t uid = getuid(); - char *user, *ret = NULL; - - /* - * First, find who we think we are using getlogin. If this - * agrees with our uid, we'll go along with it. This should - * allow sharing of uids between several login names whilst - * coping correctly with people who have su'ed. - */ - user = getlogin(); - setpwent(); - if (user) - p = getpwnam(user); - else - p = NULL; - if (p && p->pw_uid == uid) { - /* - * The result of getlogin() really does correspond to - * our uid. Fine. - */ - ret = user; - } else { - /* - * If that didn't work, for whatever reason, we'll do - * the simpler version: look up our uid in the password - * file and map it straight to a name. - */ - p = getpwuid(uid); - ret = p->pw_name; - } - endpwent(); - - return ret; - } + return dupstr(getenv("TERM")); + if (!strcmp(name, "UserName")) + return get_username(); return NULL; } diff --git a/unix/uxsftp.c b/unix/uxsftp.c index e9c9f6e6..d1989f83 100644 --- a/unix/uxsftp.c +++ b/unix/uxsftp.c @@ -1,12 +1,11 @@ /* - * uxsftp.c: the Unix-specific parts of PSFTP. + * uxsftp.c: the Unix-specific parts of PSFTP and PSCP. */ #include #include #include #include -#include #include "putty.h" #include "psftp.h" @@ -34,45 +33,6 @@ void platform_get_x11_auth(char *display, int *protocol, */ char *platform_default_s(const char *name) { - if (!strcmp(name, "UserName")) { - /* - * Remote login username will default to the local username. - */ - struct passwd *p; - uid_t uid = getuid(); - char *user, *ret = NULL; - - /* - * First, find who we think we are using getlogin. If this - * agrees with our uid, we'll go along with it. This should - * allow sharing of uids between several login names whilst - * coping correctly with people who have su'ed. - */ - user = getlogin(); - setpwent(); - if (user) - p = getpwnam(user); - else - p = NULL; - if (p && p->pw_uid == uid) { - /* - * The result of getlogin() really does correspond to - * our uid. Fine. - */ - ret = user; - } else { - /* - * If that didn't work, for whatever reason, we'll do - * the simpler version: look up our uid in the password - * file and map it straight to a name. - */ - p = getpwuid(uid); - ret = p->pw_name; - } - endpwent(); - - return ret; - } return NULL; } @@ -98,6 +58,18 @@ Filename platform_default_filename(const char *name) return ret; } +/* + * Stubs for the GUI feedback mechanism in Windows PSCP. + */ +void gui_update_stats(char *name, unsigned long size, + int percentage, unsigned long elapsed, + unsigned long done, unsigned long eta, + unsigned long ratebs) {} +void gui_send_errcount(int list, int errs) {} +void gui_send_char(int is_stderr, int c) {} +void gui_enable(char *arg) {} + + /* * Set local current directory. Returns NULL on success, or else an * error message which must be freed after printing. diff --git a/winmisc.c b/winmisc.c index 10c9fe36..a524b96f 100644 --- a/winmisc.c +++ b/winmisc.c @@ -39,6 +39,21 @@ int filename_is_null(Filename fn) return !*fn.path; } +char *get_username(void) +{ + DWORD namelen; + char *user; + + namelen = 0; + if (GetUserName(NULL, &namelen) == FALSE) + return NULL; + + user = snewn(namelen, char); + GetUserName(user, &namelen); + + return user; +} + int SaneDialogBox(HINSTANCE hinst, LPCTSTR tmpl, HWND hwndparent, 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[])