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