d6cc41e6 |
1 | /* |
799dfcfa |
2 | * winsftp.c: the Windows-specific parts of PSFTP and PSCP. |
d6cc41e6 |
3 | */ |
4 | |
d6cc41e6 |
5 | #include "putty.h" |
6 | #include "psftp.h" |
7 | |
799dfcfa |
8 | /* ---------------------------------------------------------------------- |
9 | * Interface to GUI driver program. |
d6cc41e6 |
10 | */ |
799dfcfa |
11 | |
12 | /* This is just a base value from which the main message numbers are |
13 | * derived. */ |
14 | #define WM_APP_BASE 0x8000 |
15 | |
16 | /* These two pass a single character value in wParam. They represent |
17 | * the visible output from PSCP. */ |
18 | #define WM_STD_OUT_CHAR ( WM_APP_BASE+400 ) |
19 | #define WM_STD_ERR_CHAR ( WM_APP_BASE+401 ) |
20 | |
21 | /* These pass a transfer status update. WM_STATS_CHAR passes a single |
22 | * character in wParam, and is called repeatedly to pass the name of |
23 | * the file, terminated with "\n". WM_STATS_SIZE passes the size of |
24 | * the file being transferred in wParam. WM_STATS_ELAPSED is called |
25 | * to pass the elapsed time (in seconds) in wParam, and |
26 | * WM_STATS_PERCENT passes the percentage of the transfer which is |
27 | * complete, also in wParam. */ |
28 | #define WM_STATS_CHAR ( WM_APP_BASE+402 ) |
29 | #define WM_STATS_SIZE ( WM_APP_BASE+403 ) |
30 | #define WM_STATS_PERCENT ( WM_APP_BASE+404 ) |
31 | #define WM_STATS_ELAPSED ( WM_APP_BASE+405 ) |
32 | |
33 | /* These are used at the end of a run to pass an error code in |
34 | * wParam: zero means success, nonzero means failure. WM_RET_ERR_CNT |
35 | * is used after a copy, and WM_LS_RET_ERR_CNT is used after a file |
36 | * list operation. */ |
37 | #define WM_RET_ERR_CNT ( WM_APP_BASE+406 ) |
38 | #define WM_LS_RET_ERR_CNT ( WM_APP_BASE+407 ) |
39 | |
40 | /* More transfer status update messages. WM_STATS_DONE passes the |
41 | * number of bytes sent so far in wParam. WM_STATS_ETA passes the |
42 | * estimated time to completion (in seconds). WM_STATS_RATEBS passes |
43 | * the average transfer rate (in bytes per second). */ |
44 | #define WM_STATS_DONE ( WM_APP_BASE+408 ) |
45 | #define WM_STATS_ETA ( WM_APP_BASE+409 ) |
46 | #define WM_STATS_RATEBS ( WM_APP_BASE+410 ) |
47 | |
48 | #define NAME_STR_MAX 2048 |
49 | static char statname[NAME_STR_MAX + 1]; |
50 | static unsigned long statsize = 0; |
51 | static unsigned long statdone = 0; |
52 | static unsigned long stateta = 0; |
53 | static unsigned long statratebs = 0; |
54 | static int statperct = 0; |
55 | static unsigned long statelapsed = 0; |
56 | |
57 | static HWND gui_hwnd = NULL; |
58 | |
59 | static void send_msg(HWND h, UINT message, WPARAM wParam) |
d6cc41e6 |
60 | { |
799dfcfa |
61 | while (!PostMessage(h, message, wParam, 0)) |
62 | SleepEx(1000, TRUE); |
d6cc41e6 |
63 | } |
d6cc41e6 |
64 | |
799dfcfa |
65 | void gui_send_char(int is_stderr, int c) |
d6cc41e6 |
66 | { |
799dfcfa |
67 | unsigned int msg_id = WM_STD_OUT_CHAR; |
68 | if (is_stderr) |
69 | msg_id = WM_STD_ERR_CHAR; |
70 | send_msg(gui_hwnd, msg_id, (WPARAM) c); |
71 | } |
d6cc41e6 |
72 | |
799dfcfa |
73 | void gui_send_errcount(int list, int errs) |
74 | { |
75 | unsigned int msg_id = WM_RET_ERR_CNT; |
76 | if (list) |
77 | msg_id = WM_LS_RET_ERR_CNT; |
78 | while (!PostMessage(gui_hwnd, msg_id, (WPARAM) errs, 0)) |
79 | SleepEx(1000, TRUE); |
80 | } |
81 | |
82 | void gui_update_stats(char *name, unsigned long size, |
83 | int percentage, unsigned long elapsed, |
84 | unsigned long done, unsigned long eta, |
85 | unsigned long ratebs) |
86 | { |
87 | unsigned int i; |
88 | |
89 | if (strcmp(name, statname) != 0) { |
90 | for (i = 0; i < strlen(name); ++i) |
91 | send_msg(gui_hwnd, WM_STATS_CHAR, (WPARAM) name[i]); |
92 | send_msg(gui_hwnd, WM_STATS_CHAR, (WPARAM) '\n'); |
93 | strcpy(statname, name); |
d6cc41e6 |
94 | } |
799dfcfa |
95 | if (statsize != size) { |
96 | send_msg(gui_hwnd, WM_STATS_SIZE, (WPARAM) size); |
97 | statsize = size; |
98 | } |
99 | if (statdone != done) { |
100 | send_msg(gui_hwnd, WM_STATS_DONE, (WPARAM) done); |
101 | statdone = done; |
102 | } |
103 | if (stateta != eta) { |
104 | send_msg(gui_hwnd, WM_STATS_ETA, (WPARAM) eta); |
105 | stateta = eta; |
106 | } |
107 | if (statratebs != ratebs) { |
108 | send_msg(gui_hwnd, WM_STATS_RATEBS, (WPARAM) ratebs); |
109 | statratebs = ratebs; |
d6cc41e6 |
110 | } |
799dfcfa |
111 | if (statelapsed != elapsed) { |
112 | send_msg(gui_hwnd, WM_STATS_ELAPSED, (WPARAM) elapsed); |
113 | statelapsed = elapsed; |
114 | } |
115 | if (statperct != percentage) { |
116 | send_msg(gui_hwnd, WM_STATS_PERCENT, (WPARAM) percentage); |
117 | statperct = percentage; |
118 | } |
119 | } |
120 | |
121 | void gui_enable(char *arg) |
122 | { |
123 | gui_hwnd = (HWND) atoi(arg); |
d6cc41e6 |
124 | } |
125 | |
799dfcfa |
126 | /* ---------------------------------------------------------------------- |
127 | * File access abstraction. |
128 | */ |
129 | |
d6cc41e6 |
130 | /* |
131 | * Set local current directory. Returns NULL on success, or else an |
132 | * error message which must be freed after printing. |
133 | */ |
134 | char *psftp_lcd(char *dir) |
135 | { |
136 | char *ret = NULL; |
137 | |
138 | if (!SetCurrentDirectory(dir)) { |
139 | LPVOID message; |
140 | int i; |
141 | FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
142 | FORMAT_MESSAGE_FROM_SYSTEM | |
143 | FORMAT_MESSAGE_IGNORE_INSERTS, |
144 | NULL, GetLastError(), |
145 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
146 | (LPTSTR)&message, 0, NULL); |
147 | i = strcspn((char *)message, "\n"); |
148 | ret = dupprintf("%.*s", i, (LPCTSTR)message); |
149 | LocalFree(message); |
150 | } |
151 | |
152 | return ret; |
153 | } |
154 | |
155 | /* |
156 | * Get local current directory. Returns a string which must be |
157 | * freed. |
158 | */ |
159 | char *psftp_getcwd(void) |
160 | { |
161 | char *ret = snewn(256, char); |
162 | int len = GetCurrentDirectory(256, ret); |
163 | if (len > 256) |
164 | ret = sresize(ret, len, char); |
165 | GetCurrentDirectory(len, ret); |
166 | return ret; |
167 | } |
168 | |
799dfcfa |
169 | #define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \ |
170 | ((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000) |
171 | #define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \ |
172 | ((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600)) |
173 | |
174 | struct RFile { |
175 | HANDLE h; |
176 | }; |
177 | |
178 | RFile *open_existing_file(char *name, unsigned long *size, |
179 | unsigned long *mtime, unsigned long *atime) |
180 | { |
181 | HANDLE h; |
182 | RFile *ret; |
183 | |
184 | h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL, |
185 | OPEN_EXISTING, 0, 0); |
186 | if (h == INVALID_HANDLE_VALUE) |
187 | return NULL; |
188 | |
189 | ret = snew(RFile); |
190 | ret->h = h; |
191 | |
192 | if (size) |
193 | *size = GetFileSize(h, NULL); |
194 | |
195 | if (mtime || atime) { |
196 | FILETIME actime, wrtime; |
197 | GetFileTime(h, NULL, &actime, &wrtime); |
198 | if (atime) |
199 | TIME_WIN_TO_POSIX(actime, *atime); |
200 | if (mtime) |
201 | TIME_WIN_TO_POSIX(wrtime, *mtime); |
202 | } |
203 | |
204 | return ret; |
205 | } |
206 | |
207 | int read_from_file(RFile *f, void *buffer, int length) |
208 | { |
209 | int ret, read; |
210 | ret = ReadFile(f->h, buffer, length, &read, NULL); |
211 | if (!ret) |
212 | return -1; /* error */ |
213 | else |
214 | return read; |
215 | } |
216 | |
217 | void close_rfile(RFile *f) |
218 | { |
219 | CloseHandle(f->h); |
220 | sfree(f); |
221 | } |
222 | |
223 | struct WFile { |
224 | HANDLE h; |
225 | }; |
226 | |
227 | WFile *open_new_file(char *name) |
228 | { |
229 | HANDLE h; |
230 | WFile *ret; |
231 | |
232 | h = CreateFile(name, GENERIC_WRITE, 0, NULL, |
233 | CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); |
234 | if (h == INVALID_HANDLE_VALUE) |
235 | return NULL; |
236 | |
237 | ret = snew(WFile); |
238 | ret->h = h; |
239 | |
240 | return ret; |
241 | } |
242 | |
243 | int write_to_file(WFile *f, void *buffer, int length) |
244 | { |
245 | int ret, written; |
246 | ret = WriteFile(f->h, buffer, length, &written, NULL); |
247 | if (!ret) |
248 | return -1; /* error */ |
249 | else |
250 | return written; |
251 | } |
252 | |
253 | void set_file_times(WFile *f, unsigned long mtime, unsigned long atime) |
254 | { |
255 | FILETIME actime, wrtime; |
256 | TIME_POSIX_TO_WIN(atime, actime); |
257 | TIME_POSIX_TO_WIN(mtime, wrtime); |
258 | SetFileTime(f->h, NULL, &actime, &wrtime); |
259 | } |
260 | |
261 | void close_wfile(WFile *f) |
262 | { |
263 | CloseHandle(f->h); |
264 | sfree(f); |
265 | } |
266 | |
267 | int file_type(char *name) |
268 | { |
269 | DWORD attr; |
270 | attr = GetFileAttributes(name); |
271 | /* We know of no `weird' files under Windows. */ |
272 | if (attr == (DWORD)-1) |
273 | return FILE_TYPE_NONEXISTENT; |
274 | else if (attr & FILE_ATTRIBUTE_DIRECTORY) |
275 | return FILE_TYPE_DIRECTORY; |
276 | else |
277 | return FILE_TYPE_FILE; |
278 | } |
279 | |
280 | struct DirHandle { |
281 | HANDLE h; |
282 | char *name; |
283 | }; |
284 | |
285 | DirHandle *open_directory(char *name) |
286 | { |
287 | HANDLE h; |
288 | WIN32_FIND_DATA fdat; |
289 | char *findfile; |
290 | DirHandle *ret; |
291 | |
292 | /* To enumerate files in dir `foo', we search for `foo/*'. */ |
293 | findfile = dupcat(name, "/*", NULL); |
294 | h = FindFirstFile(findfile, &fdat); |
295 | if (h == INVALID_HANDLE_VALUE) |
296 | return NULL; |
8c7d710c |
297 | sfree(findfile); |
799dfcfa |
298 | |
299 | ret = snew(DirHandle); |
300 | ret->h = h; |
301 | ret->name = dupstr(fdat.cFileName); |
302 | return ret; |
303 | } |
304 | |
305 | char *read_filename(DirHandle *dir) |
306 | { |
8c7d710c |
307 | while (!dir->name) { |
799dfcfa |
308 | WIN32_FIND_DATA fdat; |
309 | int ok = FindNextFile(dir->h, &fdat); |
310 | |
8c7d710c |
311 | if (!ok) |
312 | return NULL; |
313 | |
314 | if (fdat.cFileName[0] == '.' && |
315 | (fdat.cFileName[1] == '\0' || |
316 | (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0'))) |
317 | dir->name = NULL; |
318 | else |
799dfcfa |
319 | dir->name = dupstr(fdat.cFileName); |
320 | } |
321 | |
322 | if (dir->name) { |
323 | char *ret = dir->name; |
324 | dir->name = NULL; |
325 | return ret; |
326 | } else |
327 | return NULL; |
328 | } |
329 | |
330 | void close_directory(DirHandle *dir) |
331 | { |
332 | FindClose(dir->h); |
333 | if (dir->name) |
334 | sfree(dir->name); |
335 | sfree(dir); |
336 | } |
337 | |
338 | int test_wildcard(char *name, int cmdline) |
339 | { |
340 | HANDLE fh; |
341 | WIN32_FIND_DATA fdat; |
342 | |
343 | /* First see if the exact name exists. */ |
344 | if (GetFileAttributes(name) != (DWORD)-1) |
345 | return WCTYPE_FILENAME; |
346 | |
347 | /* Otherwise see if a wildcard match finds anything. */ |
348 | fh = FindFirstFile(name, &fdat); |
349 | if (fh == INVALID_HANDLE_VALUE) |
350 | return WCTYPE_NONEXISTENT; |
351 | |
352 | FindClose(fh); |
353 | return WCTYPE_WILDCARD; |
354 | } |
355 | |
356 | struct WildcardMatcher { |
357 | HANDLE h; |
358 | char *name; |
359 | char *srcpath; |
360 | }; |
361 | |
362 | /* |
363 | * Return a pointer to the portion of str that comes after the last |
364 | * slash (or backslash or colon, if `local' is TRUE). |
365 | */ |
366 | static char *stripslashes(char *str, int local) |
367 | { |
368 | char *p; |
369 | |
370 | if (local) { |
371 | p = strchr(str, ':'); |
372 | if (p) str = p+1; |
373 | } |
374 | |
375 | p = strrchr(str, '/'); |
376 | if (p) str = p+1; |
377 | |
378 | if (local) { |
379 | p = strrchr(str, '\\'); |
380 | if (p) str = p+1; |
381 | } |
382 | |
383 | return str; |
384 | } |
385 | |
386 | WildcardMatcher *begin_wildcard_matching(char *name) |
387 | { |
388 | HANDLE h; |
389 | WIN32_FIND_DATA fdat; |
390 | WildcardMatcher *ret; |
391 | char *last; |
392 | |
393 | h = FindFirstFile(name, &fdat); |
394 | if (h == INVALID_HANDLE_VALUE) |
395 | return NULL; |
396 | |
397 | ret = snew(WildcardMatcher); |
398 | ret->h = h; |
399 | ret->srcpath = dupstr(name); |
400 | last = stripslashes(ret->srcpath, 1); |
401 | *last = '\0'; |
402 | if (fdat.cFileName[0] == '.' && |
403 | (fdat.cFileName[1] == '\0' || |
404 | (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0'))) |
405 | ret->name = NULL; |
406 | else |
407 | ret->name = dupcat(ret->srcpath, fdat.cFileName, NULL); |
408 | |
409 | return ret; |
410 | } |
411 | |
412 | char *wildcard_get_filename(WildcardMatcher *dir) |
413 | { |
414 | while (!dir->name) { |
415 | WIN32_FIND_DATA fdat; |
416 | int ok = FindNextFile(dir->h, &fdat); |
417 | |
418 | if (!ok) |
419 | return NULL; |
420 | |
421 | if (fdat.cFileName[0] == '.' && |
422 | (fdat.cFileName[1] == '\0' || |
423 | (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0'))) |
424 | dir->name = NULL; |
425 | else |
426 | dir->name = dupcat(dir->srcpath, fdat.cFileName, NULL); |
427 | } |
428 | |
429 | if (dir->name) { |
430 | char *ret = dir->name; |
431 | dir->name = NULL; |
432 | return ret; |
433 | } else |
434 | return NULL; |
435 | } |
436 | |
437 | void finish_wildcard_matching(WildcardMatcher *dir) |
438 | { |
439 | FindClose(dir->h); |
440 | if (dir->name) |
441 | sfree(dir->name); |
442 | sfree(dir->srcpath); |
443 | sfree(dir); |
444 | } |
445 | |
446 | int create_directory(char *name) |
447 | { |
448 | return CreateDirectory(name, NULL) != 0; |
449 | } |
450 | |
8c7d710c |
451 | char *dir_file_cat(char *dir, char *file) |
452 | { |
453 | return dupcat(dir, "\\", file, NULL); |
454 | } |
455 | |
799dfcfa |
456 | /* ---------------------------------------------------------------------- |
457 | * Platform-specific network handling. |
458 | */ |
459 | |
460 | /* |
461 | * Be told what socket we're supposed to be using. |
462 | */ |
463 | static SOCKET sftp_ssh_socket; |
464 | char *do_select(SOCKET skt, int startup) |
465 | { |
466 | if (startup) |
467 | sftp_ssh_socket = skt; |
468 | else |
469 | sftp_ssh_socket = INVALID_SOCKET; |
470 | return NULL; |
471 | } |
472 | extern int select_result(WPARAM, LPARAM); |
473 | |
474 | /* |
d6cc41e6 |
475 | * Wait for some network data and process it. |
476 | */ |
477 | int ssh_sftp_loop_iteration(void) |
478 | { |
479 | fd_set readfds; |
480 | |
481 | if (sftp_ssh_socket == INVALID_SOCKET) |
482 | return -1; /* doom */ |
483 | |
484 | FD_ZERO(&readfds); |
485 | FD_SET(sftp_ssh_socket, &readfds); |
7440fd44 |
486 | if (p_select(1, &readfds, NULL, NULL, NULL) < 0) |
d6cc41e6 |
487 | return -1; /* doom */ |
488 | |
489 | select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ); |
490 | return 0; |
491 | } |
492 | |
799dfcfa |
493 | /* ---------------------------------------------------------------------- |
d6cc41e6 |
494 | * Main program. Parse arguments etc. |
495 | */ |
496 | int main(int argc, char *argv[]) |
497 | { |
498 | int ret; |
499 | |
d6cc41e6 |
500 | ret = psftp_main(argc, argv); |
d6cc41e6 |
501 | |
502 | return ret; |
503 | } |