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