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