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