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