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