]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/uxsftp.c
7045c32281f03fa827c580b365f9c231409078ae
[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 vet_filename(char *name)
345 {
346     if (strchr(name, '/'))
347         return FALSE;
348
349     if (name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2])))
350         return FALSE;
351
352     return TRUE;
353 }
354
355 int create_directory(char *name)
356 {
357     return mkdir(name, 0777) == 0;
358 }
359
360 char *dir_file_cat(char *dir, char *file)
361 {
362     return dupcat(dir, "/", file, NULL);
363 }
364
365 /*
366  * Do a select() between all currently active network fds and
367  * optionally stdin.
368  */
369 static int ssh_sftp_do_select(int include_stdin, int no_fds_ok)
370 {
371     fd_set rset, wset, xset;
372     int i, fdcount, fdsize, *fdlist;
373     int fd, fdstate, rwx, ret, maxfd;
374     long now = GETTICKCOUNT();
375
376     fdlist = NULL;
377     fdcount = fdsize = 0;
378
379     do {
380
381         /* Count the currently active fds. */
382         i = 0;
383         for (fd = first_fd(&fdstate, &rwx); fd >= 0;
384              fd = next_fd(&fdstate, &rwx)) i++;
385
386         if (i < 1 && !no_fds_ok)
387             return -1;                 /* doom */
388
389         /* Expand the fdlist buffer if necessary. */
390         if (i > fdsize) {
391             fdsize = i + 16;
392             fdlist = sresize(fdlist, fdsize, int);
393         }
394
395         FD_ZERO(&rset);
396         FD_ZERO(&wset);
397         FD_ZERO(&xset);
398         maxfd = 0;
399
400         /*
401          * Add all currently open fds to the select sets, and store
402          * them in fdlist as well.
403          */
404         fdcount = 0;
405         for (fd = first_fd(&fdstate, &rwx); fd >= 0;
406              fd = next_fd(&fdstate, &rwx)) {
407             fdlist[fdcount++] = fd;
408             if (rwx & 1)
409                 FD_SET_MAX(fd, maxfd, rset);
410             if (rwx & 2)
411                 FD_SET_MAX(fd, maxfd, wset);
412             if (rwx & 4)
413                 FD_SET_MAX(fd, maxfd, xset);
414         }
415
416         if (include_stdin)
417             FD_SET_MAX(0, maxfd, rset);
418
419         do {
420             long next, ticks;
421             struct timeval tv, *ptv;
422
423             if (run_timers(now, &next)) {
424                 ticks = next - GETTICKCOUNT();
425                 if (ticks <= 0)
426                     ticks = 1;         /* just in case */
427                 tv.tv_sec = ticks / 1000;
428                 tv.tv_usec = ticks % 1000 * 1000;
429                 ptv = &tv;
430             } else {
431                 ptv = NULL;
432             }
433             ret = select(maxfd, &rset, &wset, &xset, ptv);
434             if (ret == 0)
435                 now = next;
436             else
437                 now = GETTICKCOUNT();
438         } while (ret < 0 && errno != EINTR);
439     } while (ret == 0);
440
441     if (ret < 0) {
442         perror("select");
443         exit(1);
444     }
445
446     for (i = 0; i < fdcount; i++) {
447         fd = fdlist[i];
448         /*
449          * We must process exceptional notifications before
450          * ordinary readability ones, or we may go straight
451          * past the urgent marker.
452          */
453         if (FD_ISSET(fd, &xset))
454             select_result(fd, 4);
455         if (FD_ISSET(fd, &rset))
456             select_result(fd, 1);
457         if (FD_ISSET(fd, &wset))
458             select_result(fd, 2);
459     }
460
461     sfree(fdlist);
462
463     return FD_ISSET(0, &rset) ? 1 : 0;
464 }
465
466 /*
467  * Wait for some network data and process it.
468  */
469 int ssh_sftp_loop_iteration(void)
470 {
471     return ssh_sftp_do_select(FALSE, FALSE);
472 }
473
474 /*
475  * Read a PSFTP command line from stdin.
476  */
477 char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok)
478 {
479     char *buf;
480     int buflen, bufsize, ret;
481
482     fputs(prompt, stdout);
483     fflush(stdout);
484
485     buf = NULL;
486     buflen = bufsize = 0;
487
488     while (1) {
489         ret = ssh_sftp_do_select(TRUE, no_fds_ok);
490         if (ret < 0) {
491             printf("connection died\n");
492             return NULL;               /* woop woop */
493         }
494         if (ret > 0) {
495             if (buflen >= bufsize) {
496                 bufsize = buflen + 512;
497                 buf = sresize(buf, bufsize, char);
498             }
499             ret = read(0, buf+buflen, 1);
500             if (ret < 0) {
501                 perror("read");
502                 return NULL;
503             }
504             if (ret == 0) {
505                 /* eof on stdin; no error, but no answer either */
506                 return NULL;
507             }
508
509             if (buf[buflen++] == '\n') {
510                 /* we have a full line */
511                 return buf;
512             }
513         }
514     }
515 }
516
517 /*
518  * Main program: do platform-specific initialisation and then call
519  * psftp_main().
520  */
521 int main(int argc, char *argv[])
522 {
523     uxsel_init();
524     return psftp_main(argc, argv);
525 }