]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winsftp.c
We weren't correctly discounting "." and ".." when they came from
[PuTTY.git] / windows / winsftp.c
1 /*
2  * winsftp.c: the Windows-specific parts of PSFTP and PSCP.
3  */
4
5 #include <assert.h>
6
7 #include "putty.h"
8 #include "psftp.h"
9
10 /* ----------------------------------------------------------------------
11  * Interface to GUI driver program.
12  */
13
14 /* This is just a base value from which the main message numbers are
15  * derived. */
16 #define   WM_APP_BASE           0x8000
17
18 /* These two pass a single character value in wParam. They represent
19  * the visible output from PSCP. */
20 #define   WM_STD_OUT_CHAR       ( WM_APP_BASE+400 )
21 #define   WM_STD_ERR_CHAR       ( WM_APP_BASE+401 )
22
23 /* These pass a transfer status update. WM_STATS_CHAR passes a single
24  * character in wParam, and is called repeatedly to pass the name of
25  * the file, terminated with "\n". WM_STATS_SIZE passes the size of
26  * the file being transferred in wParam. WM_STATS_ELAPSED is called
27  * to pass the elapsed time (in seconds) in wParam, and
28  * WM_STATS_PERCENT passes the percentage of the transfer which is
29  * complete, also in wParam. */
30 #define   WM_STATS_CHAR         ( WM_APP_BASE+402 )
31 #define   WM_STATS_SIZE         ( WM_APP_BASE+403 )
32 #define   WM_STATS_PERCENT      ( WM_APP_BASE+404 )
33 #define   WM_STATS_ELAPSED      ( WM_APP_BASE+405 )
34
35 /* These are used at the end of a run to pass an error code in
36  * wParam: zero means success, nonzero means failure. WM_RET_ERR_CNT
37  * is used after a copy, and WM_LS_RET_ERR_CNT is used after a file
38  * list operation. */
39 #define   WM_RET_ERR_CNT        ( WM_APP_BASE+406 )
40 #define   WM_LS_RET_ERR_CNT     ( WM_APP_BASE+407 )
41
42 /* More transfer status update messages. WM_STATS_DONE passes the
43  * number of bytes sent so far in wParam. WM_STATS_ETA passes the
44  * estimated time to completion (in seconds). WM_STATS_RATEBS passes
45  * the average transfer rate (in bytes per second). */
46 #define   WM_STATS_DONE         ( WM_APP_BASE+408 )
47 #define   WM_STATS_ETA          ( WM_APP_BASE+409 )
48 #define   WM_STATS_RATEBS       ( WM_APP_BASE+410 )
49
50 #define NAME_STR_MAX 2048
51 static char statname[NAME_STR_MAX + 1];
52 static unsigned long statsize = 0;
53 static unsigned long statdone = 0;
54 static unsigned long stateta = 0;
55 static unsigned long statratebs = 0;
56 static int statperct = 0;
57 static unsigned long statelapsed = 0;
58
59 static HWND gui_hwnd = NULL;
60
61 static void send_msg(HWND h, UINT message, WPARAM wParam)
62 {
63     while (!PostMessage(h, message, wParam, 0))
64         SleepEx(1000, TRUE);
65 }
66
67 void gui_send_char(int is_stderr, int c)
68 {
69     unsigned int msg_id = WM_STD_OUT_CHAR;
70     if (is_stderr)
71         msg_id = WM_STD_ERR_CHAR;
72     send_msg(gui_hwnd, msg_id, (WPARAM) c);
73 }
74
75 void gui_send_errcount(int list, int errs)
76 {
77     unsigned int msg_id = WM_RET_ERR_CNT;
78     if (list)
79         msg_id = WM_LS_RET_ERR_CNT;
80     while (!PostMessage(gui_hwnd, msg_id, (WPARAM) errs, 0))
81         SleepEx(1000, TRUE);
82 }
83
84 void gui_update_stats(char *name, unsigned long size,
85                       int percentage, unsigned long elapsed,
86                       unsigned long done, unsigned long eta,
87                       unsigned long ratebs)
88 {
89     unsigned int i;
90
91     if (strcmp(name, statname) != 0) {
92         for (i = 0; i < strlen(name); ++i)
93             send_msg(gui_hwnd, WM_STATS_CHAR, (WPARAM) name[i]);
94         send_msg(gui_hwnd, WM_STATS_CHAR, (WPARAM) '\n');
95         strcpy(statname, name);
96     }
97     if (statsize != size) {
98         send_msg(gui_hwnd, WM_STATS_SIZE, (WPARAM) size);
99         statsize = size;
100     }
101     if (statdone != done) {
102         send_msg(gui_hwnd, WM_STATS_DONE, (WPARAM) done);
103         statdone = done;
104     }
105     if (stateta != eta) {
106         send_msg(gui_hwnd, WM_STATS_ETA, (WPARAM) eta);
107         stateta = eta;
108     }
109     if (statratebs != ratebs) {
110         send_msg(gui_hwnd, WM_STATS_RATEBS, (WPARAM) ratebs);
111         statratebs = ratebs;
112     }
113     if (statelapsed != elapsed) {
114         send_msg(gui_hwnd, WM_STATS_ELAPSED, (WPARAM) elapsed);
115         statelapsed = elapsed;
116     }
117     if (statperct != percentage) {
118         send_msg(gui_hwnd, WM_STATS_PERCENT, (WPARAM) percentage);
119         statperct = percentage;
120     }
121 }
122
123 void gui_enable(char *arg)
124 {
125     gui_hwnd = (HWND) atoi(arg);
126 }
127
128 /* ----------------------------------------------------------------------
129  * File access abstraction.
130  */
131
132 /*
133  * Set local current directory. Returns NULL on success, or else an
134  * error message which must be freed after printing.
135  */
136 char *psftp_lcd(char *dir)
137 {
138     char *ret = NULL;
139
140     if (!SetCurrentDirectory(dir)) {
141         LPVOID message;
142         int i;
143         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
144                       FORMAT_MESSAGE_FROM_SYSTEM |
145                       FORMAT_MESSAGE_IGNORE_INSERTS,
146                       NULL, GetLastError(),
147                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
148                       (LPTSTR)&message, 0, NULL);
149         i = strcspn((char *)message, "\n");
150         ret = dupprintf("%.*s", i, (LPCTSTR)message);
151         LocalFree(message);
152     }
153
154     return ret;
155 }
156
157 /*
158  * Get local current directory. Returns a string which must be
159  * freed.
160  */
161 char *psftp_getcwd(void)
162 {
163     char *ret = snewn(256, char);
164     int len = GetCurrentDirectory(256, ret);
165     if (len > 256)
166         ret = sresize(ret, len, char);
167     GetCurrentDirectory(len, ret);
168     return ret;
169 }
170
171 #define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \
172         ((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000)
173 #define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \
174         ((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600))
175
176 struct RFile {
177     HANDLE h;
178 };
179
180 RFile *open_existing_file(char *name, unsigned long *size,
181                           unsigned long *mtime, unsigned long *atime)
182 {
183     HANDLE h;
184     RFile *ret;
185
186     h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL,
187                    OPEN_EXISTING, 0, 0);
188     if (h == INVALID_HANDLE_VALUE)
189         return NULL;
190
191     ret = snew(RFile);
192     ret->h = h;
193
194     if (size)
195         *size = GetFileSize(h, NULL);
196
197     if (mtime || atime) {
198         FILETIME actime, wrtime;
199         GetFileTime(h, NULL, &actime, &wrtime);
200         if (atime)
201             TIME_WIN_TO_POSIX(actime, *atime);
202         if (mtime)
203             TIME_WIN_TO_POSIX(wrtime, *mtime);
204     }
205
206     return ret;
207 }
208
209 int read_from_file(RFile *f, void *buffer, int length)
210 {
211     int ret, read;
212     ret = ReadFile(f->h, buffer, length, &read, NULL);
213     if (!ret)
214         return -1;                     /* error */
215     else
216         return read;
217 }
218
219 void close_rfile(RFile *f)
220 {
221     CloseHandle(f->h);
222     sfree(f);
223 }
224
225 struct WFile {
226     HANDLE h;
227 };
228
229 WFile *open_new_file(char *name)
230 {
231     HANDLE h;
232     WFile *ret;
233
234     h = CreateFile(name, GENERIC_WRITE, 0, NULL,
235                    CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
236     if (h == INVALID_HANDLE_VALUE)
237         return NULL;
238
239     ret = snew(WFile);
240     ret->h = h;
241
242     return ret;
243 }
244
245 int write_to_file(WFile *f, void *buffer, int length)
246 {
247     int ret, written;
248     ret = WriteFile(f->h, buffer, length, &written, NULL);
249     if (!ret)
250         return -1;                     /* error */
251     else
252         return written;
253 }
254
255 void set_file_times(WFile *f, unsigned long mtime, unsigned long atime)
256 {
257     FILETIME actime, wrtime;
258     TIME_POSIX_TO_WIN(atime, actime);
259     TIME_POSIX_TO_WIN(mtime, wrtime);
260     SetFileTime(f->h, NULL, &actime, &wrtime);
261 }
262
263 void close_wfile(WFile *f)
264 {
265     CloseHandle(f->h);
266     sfree(f);
267 }
268
269 int file_type(char *name)
270 {
271     DWORD attr;
272     attr = GetFileAttributes(name);
273     /* We know of no `weird' files under Windows. */
274     if (attr == (DWORD)-1)
275         return FILE_TYPE_NONEXISTENT;
276     else if (attr & FILE_ATTRIBUTE_DIRECTORY)
277         return FILE_TYPE_DIRECTORY;
278     else
279         return FILE_TYPE_FILE;
280 }
281
282 struct DirHandle {
283     HANDLE h;
284     char *name;
285 };
286
287 DirHandle *open_directory(char *name)
288 {
289     HANDLE h;
290     WIN32_FIND_DATA fdat;
291     char *findfile;
292     DirHandle *ret;
293
294     /* Enumerate files in dir `foo'. */
295     findfile = dupcat(name, "/*", NULL);
296     h = FindFirstFile(findfile, &fdat);
297     if (h == INVALID_HANDLE_VALUE)
298         return NULL;
299     sfree(findfile);
300
301     ret = snew(DirHandle);
302     ret->h = h;
303     ret->name = dupstr(fdat.cFileName);
304     return ret;
305 }
306
307 char *read_filename(DirHandle *dir)
308 {
309     do {
310
311         if (!dir->name) {
312             WIN32_FIND_DATA fdat;
313             int ok = FindNextFile(dir->h, &fdat);
314             if (!ok)
315                 return NULL;
316             else
317                 dir->name = dupstr(fdat.cFileName);
318         }
319
320         assert(dir->name);
321         if (dir->name[0] == '.' &&
322             (dir->name[1] == '\0' ||
323              (dir->name[1] == '.' && dir->name[2] == '\0'))) {
324             sfree(dir->name);
325             dir->name = NULL;
326         }
327
328     } while (!dir->name);
329
330     if (dir->name) {
331         char *ret = dir->name;
332         dir->name = NULL;
333         return ret;
334     } else
335         return NULL;
336 }
337
338 void close_directory(DirHandle *dir)
339 {
340     FindClose(dir->h);
341     if (dir->name)
342         sfree(dir->name);
343     sfree(dir);
344 }
345
346 int test_wildcard(char *name, int cmdline)
347 {
348     HANDLE fh;
349     WIN32_FIND_DATA fdat;
350
351     /* First see if the exact name exists. */
352     if (GetFileAttributes(name) != (DWORD)-1)
353         return WCTYPE_FILENAME;
354
355     /* Otherwise see if a wildcard match finds anything. */
356     fh = FindFirstFile(name, &fdat);
357     if (fh == INVALID_HANDLE_VALUE)
358         return WCTYPE_NONEXISTENT;
359
360     FindClose(fh);
361     return WCTYPE_WILDCARD;
362 }
363
364 struct WildcardMatcher {
365     HANDLE h;
366     char *name;
367     char *srcpath;
368 };
369
370 /*
371  * Return a pointer to the portion of str that comes after the last
372  * slash (or backslash or colon, if `local' is TRUE).
373  */
374 static char *stripslashes(char *str, int local)
375 {
376     char *p;
377
378     if (local) {
379         p = strchr(str, ':');
380         if (p) str = p+1;
381     }
382
383     p = strrchr(str, '/');
384     if (p) str = p+1;
385
386     if (local) {
387         p = strrchr(str, '\\');
388         if (p) str = p+1;
389     }
390
391     return str;
392 }
393
394 WildcardMatcher *begin_wildcard_matching(char *name)
395 {
396     HANDLE h;
397     WIN32_FIND_DATA fdat;
398     WildcardMatcher *ret;
399     char *last;
400
401     h = FindFirstFile(name, &fdat);
402     if (h == INVALID_HANDLE_VALUE)
403         return NULL;
404
405     ret = snew(WildcardMatcher);
406     ret->h = h;
407     ret->srcpath = dupstr(name);
408     last = stripslashes(ret->srcpath, 1);
409     *last = '\0';
410     if (fdat.cFileName[0] == '.' &&
411         (fdat.cFileName[1] == '\0' ||
412          (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))
413         ret->name = NULL;
414     else
415         ret->name = dupcat(ret->srcpath, fdat.cFileName, NULL);
416
417     return ret;
418 }
419
420 char *wildcard_get_filename(WildcardMatcher *dir)
421 {
422     while (!dir->name) {
423         WIN32_FIND_DATA fdat;
424         int ok = FindNextFile(dir->h, &fdat);
425
426         if (!ok)
427             return NULL;
428
429         if (fdat.cFileName[0] == '.' &&
430             (fdat.cFileName[1] == '\0' ||
431              (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))
432             dir->name = NULL;
433         else
434             dir->name = dupcat(dir->srcpath, fdat.cFileName, NULL);
435     }
436
437     if (dir->name) {
438         char *ret = dir->name;
439         dir->name = NULL;
440         return ret;
441     } else
442         return NULL;
443 }
444
445 void finish_wildcard_matching(WildcardMatcher *dir)
446 {
447     FindClose(dir->h);
448     if (dir->name)
449         sfree(dir->name);
450     sfree(dir->srcpath);
451     sfree(dir);
452 }
453
454 int vet_filename(char *name)
455 {
456     if (strchr(name, '/') || strchr(name, '\\') || strchr(name, ':'))
457         return FALSE;
458
459     if (!name[strspn(name, ".")])      /* entirely composed of dots */
460         return FALSE;
461
462     return TRUE;
463 }
464
465 int create_directory(char *name)
466 {
467     return CreateDirectory(name, NULL) != 0;
468 }
469
470 char *dir_file_cat(char *dir, char *file)
471 {
472     return dupcat(dir, "\\", file, NULL);
473 }
474
475 /* ----------------------------------------------------------------------
476  * Platform-specific network handling.
477  */
478
479 /*
480  * Be told what socket we're supposed to be using.
481  */
482 static SOCKET sftp_ssh_socket = INVALID_SOCKET;
483 static HANDLE netevent = NULL;
484 char *do_select(SOCKET skt, int startup)
485 {
486     int events;
487     if (startup)
488         sftp_ssh_socket = skt;
489     else
490         sftp_ssh_socket = INVALID_SOCKET;
491
492     if (p_WSAEventSelect) {
493         if (startup) {
494             events = (FD_CONNECT | FD_READ | FD_WRITE |
495                       FD_OOB | FD_CLOSE | FD_ACCEPT);
496             netevent = CreateEvent(NULL, FALSE, FALSE, NULL);
497         } else {
498             events = 0;
499         }
500         if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {
501             switch (p_WSAGetLastError()) {
502               case WSAENETDOWN:
503                 return "Network is down";
504               default:
505                 return "WSAEventSelect(): unknown error";
506             }
507         }
508     }
509     return NULL;
510 }
511 extern int select_result(WPARAM, LPARAM);
512
513 int do_eventsel_loop(HANDLE other_event)
514 {
515     int n;
516     long next, ticks;
517     HANDLE handles[2];
518     SOCKET *sklist;
519     int skcount;
520     long now = GETTICKCOUNT();
521
522     if (!netevent) {
523         return -1;                     /* doom */
524     }
525
526     handles[0] = netevent;
527     handles[1] = other_event;
528
529     if (run_timers(now, &next)) {
530         ticks = next - GETTICKCOUNT();
531         if (ticks < 0) ticks = 0;  /* just in case */
532     } else {
533         ticks = INFINITE;
534     }
535
536     n = MsgWaitForMultipleObjects(other_event ? 2 : 1, handles, FALSE, ticks,
537                                   QS_POSTMESSAGE);
538
539     if (n == WAIT_OBJECT_0 + 0) {
540         WSANETWORKEVENTS things;
541         SOCKET socket;
542         extern SOCKET first_socket(int *), next_socket(int *);
543         extern int select_result(WPARAM, LPARAM);
544         int i, socketstate;
545
546         /*
547          * We must not call select_result() for any socket
548          * until we have finished enumerating within the
549          * tree. This is because select_result() may close
550          * the socket and modify the tree.
551          */
552         /* Count the active sockets. */
553         i = 0;
554         for (socket = first_socket(&socketstate);
555              socket != INVALID_SOCKET;
556              socket = next_socket(&socketstate)) i++;
557
558         /* Expand the buffer if necessary. */
559         sklist = snewn(i, SOCKET);
560
561         /* Retrieve the sockets into sklist. */
562         skcount = 0;
563         for (socket = first_socket(&socketstate);
564              socket != INVALID_SOCKET;
565              socket = next_socket(&socketstate)) {
566             sklist[skcount++] = socket;
567         }
568
569         /* Now we're done enumerating; go through the list. */
570         for (i = 0; i < skcount; i++) {
571             WPARAM wp;
572             socket = sklist[i];
573             wp = (WPARAM) socket;
574             if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {
575                 static const struct { int bit, mask; } eventtypes[] = {
576                     {FD_CONNECT_BIT, FD_CONNECT},
577                     {FD_READ_BIT, FD_READ},
578                     {FD_CLOSE_BIT, FD_CLOSE},
579                     {FD_OOB_BIT, FD_OOB},
580                     {FD_WRITE_BIT, FD_WRITE},
581                     {FD_ACCEPT_BIT, FD_ACCEPT},
582                 };
583                 int e;
584
585                 noise_ultralight(socket);
586                 noise_ultralight(things.lNetworkEvents);
587
588                 for (e = 0; e < lenof(eventtypes); e++)
589                     if (things.lNetworkEvents & eventtypes[e].mask) {
590                         LPARAM lp;
591                         int err = things.iErrorCode[eventtypes[e].bit];
592                         lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);
593                         select_result(wp, lp);
594                     }
595             }
596         }
597
598         sfree(sklist);
599     }
600
601     if (n == WAIT_TIMEOUT) {
602         now = next;
603     } else {
604         now = GETTICKCOUNT();
605     }
606
607     if (other_event && n == WAIT_OBJECT_0 + 1)
608         return 1;
609
610     return 0;
611 }
612
613 /*
614  * Wait for some network data and process it.
615  *
616  * We have two variants of this function. One uses select() so that
617  * it's compatible with WinSock 1. The other uses WSAEventSelect
618  * and MsgWaitForMultipleObjects, so that we can consistently use
619  * WSAEventSelect throughout; this enables us to also implement
620  * ssh_sftp_get_cmdline() using a parallel mechanism.
621  */
622 int ssh_sftp_loop_iteration(void)
623 {
624     if (sftp_ssh_socket == INVALID_SOCKET)
625         return -1;                     /* doom */
626
627     if (p_WSAEventSelect == NULL) {
628         fd_set readfds;
629         int ret;
630         long now = GETTICKCOUNT();
631
632         if (socket_writable(sftp_ssh_socket))
633             select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_WRITE);
634
635         do {
636             long next, ticks;
637             struct timeval tv, *ptv;
638
639             if (run_timers(now, &next)) {
640                 ticks = next - GETTICKCOUNT();
641                 if (ticks <= 0)
642                     ticks = 1;         /* just in case */
643                 tv.tv_sec = ticks / 1000;
644                 tv.tv_usec = ticks % 1000 * 1000;
645                 ptv = &tv;
646             } else {
647                 ptv = NULL;
648             }
649
650             FD_ZERO(&readfds);
651             FD_SET(sftp_ssh_socket, &readfds);
652             ret = p_select(1, &readfds, NULL, NULL, ptv);
653
654             if (ret < 0)
655                 return -1;                     /* doom */
656             else if (ret == 0)
657                 now = next;
658             else
659                 now = GETTICKCOUNT();
660
661         } while (ret == 0);
662
663         select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
664
665         return 0;
666     } else {
667         return do_eventsel_loop(NULL);
668     }
669 }
670
671 /*
672  * Read a command line from standard input.
673  * 
674  * In the presence of WinSock 2, we can use WSAEventSelect to
675  * mediate between the socket and stdin, meaning we can send
676  * keepalives and respond to server events even while waiting at
677  * the PSFTP command prompt. Without WS2, we fall back to a simple
678  * fgets.
679  */
680 struct command_read_ctx {
681     HANDLE event;
682     char *line;
683 };
684
685 static DWORD WINAPI command_read_thread(void *param)
686 {
687     struct command_read_ctx *ctx = (struct command_read_ctx *) param;
688
689     ctx->line = fgetline(stdin);
690
691     SetEvent(ctx->event);
692
693     return 0;
694 }
695
696 char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok)
697 {
698     int ret;
699     struct command_read_ctx actx, *ctx = &actx;
700     DWORD threadid;
701
702     fputs(prompt, stdout);
703     fflush(stdout);
704
705     if ((sftp_ssh_socket == INVALID_SOCKET && no_fds_ok) ||
706         p_WSAEventSelect == NULL) {
707         return fgetline(stdin);        /* very simple */
708     }
709
710     /*
711      * Create a second thread to read from stdin. Process network
712      * and timing events until it terminates.
713      */
714     ctx->event = CreateEvent(NULL, FALSE, FALSE, NULL);
715     ctx->line = NULL;
716
717     if (!CreateThread(NULL, 0, command_read_thread,
718                       ctx, 0, &threadid)) {
719         fprintf(stderr, "Unable to create command input thread\n");
720         cleanup_exit(1);
721     }
722
723     do {
724         ret = do_eventsel_loop(ctx->event);
725
726         /* Error return can only occur if netevent==NULL, and it ain't. */
727         assert(ret >= 0);
728     } while (ret == 0);
729
730     return ctx->line;
731 }
732
733 /* ----------------------------------------------------------------------
734  * Main program. Parse arguments etc.
735  */
736 int main(int argc, char *argv[])
737 {
738     int ret;
739
740     ret = psftp_main(argc, argv);
741
742     return ret;
743 }