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