OSDN Git Service

Add VC++ Project files for PuTTY DLL without exported functions.
[ffftp/ffftp.git] / putty / UNIX / UXSFTP.C
1 /*\r
2  * uxsftp.c: the Unix-specific parts of PSFTP and PSCP.\r
3  */\r
4 \r
5 #include <sys/time.h>\r
6 #include <sys/types.h>\r
7 #include <sys/stat.h>\r
8 #include <stdlib.h>\r
9 #include <fcntl.h>\r
10 #include <dirent.h>\r
11 #include <unistd.h>\r
12 #include <utime.h>\r
13 #include <errno.h>\r
14 #include <assert.h>\r
15 #include <glob.h>\r
16 #ifndef HAVE_NO_SYS_SELECT_H\r
17 #include <sys/select.h>\r
18 #endif\r
19 \r
20 #include "putty.h"\r
21 #include "ssh.h"\r
22 #include "psftp.h"\r
23 #include "int64.h"\r
24 \r
25 /*\r
26  * In PSFTP our selects are synchronous, so these functions are\r
27  * empty stubs.\r
28  */\r
29 int uxsel_input_add(int fd, int rwx) { return 0; }\r
30 void uxsel_input_remove(int id) { }\r
31 \r
32 char *x_get_default(const char *key)\r
33 {\r
34     return NULL;                       /* this is a stub */\r
35 }\r
36 \r
37 void platform_get_x11_auth(struct X11Display *display, const Config *cfg)\r
38 {\r
39     /* Do nothing, therefore no auth. */\r
40 }\r
41 const int platform_uses_x11_unix_by_default = TRUE;\r
42 \r
43 /*\r
44  * Default settings that are specific to PSFTP.\r
45  */\r
46 char *platform_default_s(const char *name)\r
47 {\r
48     return NULL;\r
49 }\r
50 \r
51 int platform_default_i(const char *name, int def)\r
52 {\r
53     return def;\r
54 }\r
55 \r
56 FontSpec platform_default_fontspec(const char *name)\r
57 {\r
58     FontSpec ret;\r
59     *ret.name = '\0';\r
60     return ret;\r
61 }\r
62 \r
63 Filename platform_default_filename(const char *name)\r
64 {\r
65     Filename ret;\r
66     if (!strcmp(name, "LogFileName"))\r
67         strcpy(ret.path, "putty.log");\r
68     else\r
69         *ret.path = '\0';\r
70     return ret;\r
71 }\r
72 \r
73 char *get_ttymode(void *frontend, const char *mode) { return NULL; }\r
74 \r
75 int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
76 {\r
77     int ret;\r
78     ret = cmdline_get_passwd_input(p, in, inlen);\r
79     if (ret == -1)\r
80         ret = console_get_userpass_input(p, in, inlen);\r
81     return ret;\r
82 }\r
83 \r
84 /*\r
85  * Set local current directory. Returns NULL on success, or else an\r
86  * error message which must be freed after printing.\r
87  */\r
88 char *psftp_lcd(char *dir)\r
89 {\r
90     if (chdir(dir) < 0)\r
91         return dupprintf("%s: chdir: %s", dir, strerror(errno));\r
92     else\r
93         return NULL;\r
94 }\r
95 \r
96 /*\r
97  * Get local current directory. Returns a string which must be\r
98  * freed.\r
99  */\r
100 char *psftp_getcwd(void)\r
101 {\r
102     char *buffer, *ret;\r
103     int size = 256;\r
104 \r
105     buffer = snewn(size, char);\r
106     while (1) {\r
107         ret = getcwd(buffer, size);\r
108         if (ret != NULL)\r
109             return ret;\r
110         if (errno != ERANGE) {\r
111             sfree(buffer);\r
112             return dupprintf("[cwd unavailable: %s]", strerror(errno));\r
113         }\r
114         /*\r
115          * Otherwise, ERANGE was returned, meaning the buffer\r
116          * wasn't big enough.\r
117          */\r
118         size = size * 3 / 2;\r
119         buffer = sresize(buffer, size, char);\r
120     }\r
121 }\r
122 \r
123 struct RFile {\r
124     int fd;\r
125 };\r
126 \r
127 RFile *open_existing_file(char *name, uint64 *size,\r
128                           unsigned long *mtime, unsigned long *atime)\r
129 {\r
130     int fd;\r
131     RFile *ret;\r
132 \r
133     fd = open(name, O_RDONLY);\r
134     if (fd < 0)\r
135         return NULL;\r
136 \r
137     ret = snew(RFile);\r
138     ret->fd = fd;\r
139 \r
140     if (size || mtime || atime) {\r
141         struct stat statbuf;\r
142         if (fstat(fd, &statbuf) < 0) {\r
143             fprintf(stderr, "%s: stat: %s\n", name, strerror(errno));\r
144             memset(&statbuf, 0, sizeof(statbuf));\r
145         }\r
146 \r
147         if (size)\r
148             *size = uint64_make((statbuf.st_size >> 16) >> 16,\r
149                                 statbuf.st_size);\r
150                 \r
151         if (mtime)\r
152             *mtime = statbuf.st_mtime;\r
153 \r
154         if (atime)\r
155             *atime = statbuf.st_atime;\r
156     }\r
157 \r
158     return ret;\r
159 }\r
160 \r
161 int read_from_file(RFile *f, void *buffer, int length)\r
162 {\r
163     return read(f->fd, buffer, length);\r
164 }\r
165 \r
166 void close_rfile(RFile *f)\r
167 {\r
168     close(f->fd);\r
169     sfree(f);\r
170 }\r
171 \r
172 struct WFile {\r
173     int fd;\r
174     char *name;\r
175 };\r
176 \r
177 WFile *open_new_file(char *name)\r
178 {\r
179     int fd;\r
180     WFile *ret;\r
181 \r
182     fd = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666);\r
183     if (fd < 0)\r
184         return NULL;\r
185 \r
186     ret = snew(WFile);\r
187     ret->fd = fd;\r
188     ret->name = dupstr(name);\r
189 \r
190     return ret;\r
191 }\r
192 \r
193 \r
194 WFile *open_existing_wfile(char *name, uint64 *size)\r
195 {\r
196     int fd;\r
197     WFile *ret;\r
198 \r
199     fd = open(name, O_APPEND | O_WRONLY);\r
200     if (fd < 0)\r
201         return NULL;\r
202 \r
203     ret = snew(WFile);\r
204     ret->fd = fd;\r
205     ret->name = dupstr(name);\r
206 \r
207     if (size) {\r
208         struct stat statbuf;\r
209         if (fstat(fd, &statbuf) < 0) {\r
210             fprintf(stderr, "%s: stat: %s\n", name, strerror(errno));\r
211             memset(&statbuf, 0, sizeof(statbuf));\r
212         }\r
213 \r
214         *size = uint64_make((statbuf.st_size >> 16) >> 16,\r
215                             statbuf.st_size);\r
216     }\r
217 \r
218     return ret;\r
219 }\r
220 \r
221 int write_to_file(WFile *f, void *buffer, int length)\r
222 {\r
223     char *p = (char *)buffer;\r
224     int so_far = 0;\r
225 \r
226     /* Keep trying until we've really written as much as we can. */\r
227     while (length > 0) {\r
228         int ret = write(f->fd, p, length);\r
229 \r
230         if (ret < 0)\r
231             return ret;\r
232 \r
233         if (ret == 0)\r
234             break;\r
235 \r
236         p += ret;\r
237         length -= ret;\r
238         so_far += ret;\r
239     }\r
240 \r
241     return so_far;\r
242 }\r
243 \r
244 void set_file_times(WFile *f, unsigned long mtime, unsigned long atime)\r
245 {\r
246     struct utimbuf ut;\r
247 \r
248     ut.actime = atime;\r
249     ut.modtime = mtime;\r
250 \r
251     utime(f->name, &ut);\r
252 }\r
253 \r
254 /* Closes and frees the WFile */\r
255 void close_wfile(WFile *f)\r
256 {\r
257     close(f->fd);\r
258     sfree(f->name);\r
259     sfree(f);\r
260 }\r
261 \r
262 /* Seek offset bytes through file, from whence, where whence is\r
263    FROM_START, FROM_CURRENT, or FROM_END */\r
264 int seek_file(WFile *f, uint64 offset, int whence)\r
265 {\r
266     off_t fileofft;\r
267     int lseek_whence;\r
268     \r
269     fileofft = (((off_t) offset.hi << 16) << 16) + offset.lo;\r
270 \r
271     switch (whence) {\r
272     case FROM_START:\r
273         lseek_whence = SEEK_SET;\r
274         break;\r
275     case FROM_CURRENT:\r
276         lseek_whence = SEEK_CUR;\r
277         break;\r
278     case FROM_END:\r
279         lseek_whence = SEEK_END;\r
280         break;\r
281     default:\r
282         return -1;\r
283     }\r
284 \r
285     return lseek(f->fd, fileofft, lseek_whence) >= 0 ? 0 : -1;\r
286 }\r
287 \r
288 uint64 get_file_posn(WFile *f)\r
289 {\r
290     off_t fileofft;\r
291     uint64 ret;\r
292 \r
293     fileofft = lseek(f->fd, (off_t) 0, SEEK_CUR);\r
294 \r
295     ret = uint64_make((fileofft >> 16) >> 16, fileofft);\r
296 \r
297     return ret;\r
298 }\r
299 \r
300 int file_type(char *name)\r
301 {\r
302     struct stat statbuf;\r
303 \r
304     if (stat(name, &statbuf) < 0) {\r
305         if (errno != ENOENT)\r
306             fprintf(stderr, "%s: stat: %s\n", name, strerror(errno));\r
307         return FILE_TYPE_NONEXISTENT;\r
308     }\r
309 \r
310     if (S_ISREG(statbuf.st_mode))\r
311         return FILE_TYPE_FILE;\r
312 \r
313     if (S_ISDIR(statbuf.st_mode))\r
314         return FILE_TYPE_DIRECTORY;\r
315 \r
316     return FILE_TYPE_WEIRD;\r
317 }\r
318 \r
319 struct DirHandle {\r
320     DIR *dir;\r
321 };\r
322 \r
323 DirHandle *open_directory(char *name)\r
324 {\r
325     DIR *dir;\r
326     DirHandle *ret;\r
327 \r
328     dir = opendir(name);\r
329     if (!dir)\r
330         return NULL;\r
331 \r
332     ret = snew(DirHandle);\r
333     ret->dir = dir;\r
334     return ret;\r
335 }\r
336 \r
337 char *read_filename(DirHandle *dir)\r
338 {\r
339     struct dirent *de;\r
340 \r
341     do {\r
342         de = readdir(dir->dir);\r
343         if (de == NULL)\r
344             return NULL;\r
345     } while ((de->d_name[0] == '.' &&\r
346               (de->d_name[1] == '\0' ||\r
347                (de->d_name[1] == '.' && de->d_name[2] == '\0'))));\r
348 \r
349     return dupstr(de->d_name);\r
350 }\r
351 \r
352 void close_directory(DirHandle *dir)\r
353 {\r
354     closedir(dir->dir);\r
355     sfree(dir);\r
356 }\r
357 \r
358 int test_wildcard(char *name, int cmdline)\r
359 {\r
360     struct stat statbuf;\r
361 \r
362     if (stat(name, &statbuf) == 0) {\r
363         return WCTYPE_FILENAME;\r
364     } else if (cmdline) {\r
365         /*\r
366          * On Unix, we never need to parse wildcards coming from\r
367          * the command line, because the shell will have expanded\r
368          * them into a filename list already.\r
369          */\r
370         return WCTYPE_NONEXISTENT;\r
371     } else {\r
372         glob_t globbed;\r
373         int ret = WCTYPE_NONEXISTENT;\r
374 \r
375         if (glob(name, GLOB_ERR, NULL, &globbed) == 0) {\r
376             if (globbed.gl_pathc > 0)\r
377                 ret = WCTYPE_WILDCARD;\r
378             globfree(&globbed);\r
379         }\r
380 \r
381         return ret;\r
382     }\r
383 }\r
384 \r
385 /*\r
386  * Actually return matching file names for a local wildcard.\r
387  */\r
388 struct WildcardMatcher {\r
389     glob_t globbed;\r
390     int i;\r
391 };\r
392 WildcardMatcher *begin_wildcard_matching(char *name) {\r
393     WildcardMatcher *ret = snew(WildcardMatcher);\r
394 \r
395     if (glob(name, 0, NULL, &ret->globbed) < 0) {\r
396         sfree(ret);\r
397         return NULL;\r
398     }\r
399 \r
400     ret->i = 0;\r
401 \r
402     return ret;\r
403 }\r
404 char *wildcard_get_filename(WildcardMatcher *dir) {\r
405     if (dir->i < dir->globbed.gl_pathc) {\r
406         return dupstr(dir->globbed.gl_pathv[dir->i++]);\r
407     } else\r
408         return NULL;\r
409 }\r
410 void finish_wildcard_matching(WildcardMatcher *dir) {\r
411     globfree(&dir->globbed);\r
412     sfree(dir);\r
413 }\r
414 \r
415 int vet_filename(char *name)\r
416 {\r
417     if (strchr(name, '/'))\r
418         return FALSE;\r
419 \r
420     if (name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2])))\r
421         return FALSE;\r
422 \r
423     return TRUE;\r
424 }\r
425 \r
426 int create_directory(char *name)\r
427 {\r
428     return mkdir(name, 0777) == 0;\r
429 }\r
430 \r
431 char *dir_file_cat(char *dir, char *file)\r
432 {\r
433     return dupcat(dir, "/", file, NULL);\r
434 }\r
435 \r
436 /*\r
437  * Do a select() between all currently active network fds and\r
438  * optionally stdin.\r
439  */\r
440 static int ssh_sftp_do_select(int include_stdin, int no_fds_ok)\r
441 {\r
442     fd_set rset, wset, xset;\r
443     int i, fdcount, fdsize, *fdlist;\r
444     int fd, fdstate, rwx, ret, maxfd;\r
445     long now = GETTICKCOUNT();\r
446 \r
447     fdlist = NULL;\r
448     fdcount = fdsize = 0;\r
449 \r
450     do {\r
451 \r
452         /* Count the currently active fds. */\r
453         i = 0;\r
454         for (fd = first_fd(&fdstate, &rwx); fd >= 0;\r
455              fd = next_fd(&fdstate, &rwx)) i++;\r
456 \r
457         if (i < 1 && !no_fds_ok)\r
458             return -1;                 /* doom */\r
459 \r
460         /* Expand the fdlist buffer if necessary. */\r
461         if (i > fdsize) {\r
462             fdsize = i + 16;\r
463             fdlist = sresize(fdlist, fdsize, int);\r
464         }\r
465 \r
466         FD_ZERO(&rset);\r
467         FD_ZERO(&wset);\r
468         FD_ZERO(&xset);\r
469         maxfd = 0;\r
470 \r
471         /*\r
472          * Add all currently open fds to the select sets, and store\r
473          * them in fdlist as well.\r
474          */\r
475         fdcount = 0;\r
476         for (fd = first_fd(&fdstate, &rwx); fd >= 0;\r
477              fd = next_fd(&fdstate, &rwx)) {\r
478             fdlist[fdcount++] = fd;\r
479             if (rwx & 1)\r
480                 FD_SET_MAX(fd, maxfd, rset);\r
481             if (rwx & 2)\r
482                 FD_SET_MAX(fd, maxfd, wset);\r
483             if (rwx & 4)\r
484                 FD_SET_MAX(fd, maxfd, xset);\r
485         }\r
486 \r
487         if (include_stdin)\r
488             FD_SET_MAX(0, maxfd, rset);\r
489 \r
490         do {\r
491             long next, ticks;\r
492             struct timeval tv, *ptv;\r
493 \r
494             if (run_timers(now, &next)) {\r
495                 ticks = next - GETTICKCOUNT();\r
496                 if (ticks <= 0)\r
497                     ticks = 1;         /* just in case */\r
498                 tv.tv_sec = ticks / 1000;\r
499                 tv.tv_usec = ticks % 1000 * 1000;\r
500                 ptv = &tv;\r
501             } else {\r
502                 ptv = NULL;\r
503             }\r
504             ret = select(maxfd, &rset, &wset, &xset, ptv);\r
505             if (ret == 0)\r
506                 now = next;\r
507             else {\r
508                 long newnow = GETTICKCOUNT();\r
509                 /*\r
510                  * Check to see whether the system clock has\r
511                  * changed massively during the select.\r
512                  */\r
513                 if (newnow - now < 0 || newnow - now > next - now) {\r
514                     /*\r
515                      * If so, look at the elapsed time in the\r
516                      * select and use it to compute a new\r
517                      * tickcount_offset.\r
518                      */\r
519                     long othernow = now + tv.tv_sec * 1000 + tv.tv_usec / 1000;\r
520                     /* So we'd like GETTICKCOUNT to have returned othernow,\r
521                      * but instead it return newnow. Hence ... */\r
522                     tickcount_offset += othernow - newnow;\r
523                     now = othernow;\r
524                 } else {\r
525                     now = newnow;\r
526                 }\r
527             }\r
528         } while (ret < 0 && errno != EINTR);\r
529     } while (ret == 0);\r
530 \r
531     if (ret < 0) {\r
532         perror("select");\r
533         exit(1);\r
534     }\r
535 \r
536     for (i = 0; i < fdcount; i++) {\r
537         fd = fdlist[i];\r
538         /*\r
539          * We must process exceptional notifications before\r
540          * ordinary readability ones, or we may go straight\r
541          * past the urgent marker.\r
542          */\r
543         if (FD_ISSET(fd, &xset))\r
544             select_result(fd, 4);\r
545         if (FD_ISSET(fd, &rset))\r
546             select_result(fd, 1);\r
547         if (FD_ISSET(fd, &wset))\r
548             select_result(fd, 2);\r
549     }\r
550 \r
551     sfree(fdlist);\r
552 \r
553     return FD_ISSET(0, &rset) ? 1 : 0;\r
554 }\r
555 \r
556 /*\r
557  * Wait for some network data and process it.\r
558  */\r
559 int ssh_sftp_loop_iteration(void)\r
560 {\r
561     return ssh_sftp_do_select(FALSE, FALSE);\r
562 }\r
563 \r
564 /*\r
565  * Read a PSFTP command line from stdin.\r
566  */\r
567 char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok)\r
568 {\r
569     char *buf;\r
570     int buflen, bufsize, ret;\r
571 \r
572     fputs(prompt, stdout);\r
573     fflush(stdout);\r
574 \r
575     buf = NULL;\r
576     buflen = bufsize = 0;\r
577 \r
578     while (1) {\r
579         ret = ssh_sftp_do_select(TRUE, no_fds_ok);\r
580         if (ret < 0) {\r
581             printf("connection died\n");\r
582             return NULL;               /* woop woop */\r
583         }\r
584         if (ret > 0) {\r
585             if (buflen >= bufsize) {\r
586                 bufsize = buflen + 512;\r
587                 buf = sresize(buf, bufsize, char);\r
588             }\r
589             ret = read(0, buf+buflen, 1);\r
590             if (ret < 0) {\r
591                 perror("read");\r
592                 return NULL;\r
593             }\r
594             if (ret == 0) {\r
595                 /* eof on stdin; no error, but no answer either */\r
596                 return NULL;\r
597             }\r
598 \r
599             if (buf[buflen++] == '\n') {\r
600                 /* we have a full line */\r
601                 return buf;\r
602             }\r
603         }\r
604     }\r
605 }\r
606 \r
607 /*\r
608  * Main program: do platform-specific initialisation and then call\r
609  * psftp_main().\r
610  */\r
611 int main(int argc, char *argv[])\r
612 {\r
613     uxsel_init();\r
614     return psftp_main(argc, argv);\r
615 }\r