]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - psftp.c
Clean up handling of the return value from sftp_find_request. In many
[PuTTY.git] / psftp.c
1 /*
2  * psftp.c: (platform-independent) front end for PSFTP.
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdarg.h>
8 #include <assert.h>
9 #include <limits.h>
10
11 #define PUTTY_DO_GLOBALS
12 #include "putty.h"
13 #include "psftp.h"
14 #include "storage.h"
15 #include "ssh.h"
16 #include "sftp.h"
17 #include "int64.h"
18
19 const char *const appname = "PSFTP";
20
21 /*
22  * Since SFTP is a request-response oriented protocol, it requires
23  * no buffer management: when we send data, we stop and wait for an
24  * acknowledgement _anyway_, and so we can't possibly overfill our
25  * send buffer.
26  */
27
28 static int psftp_connect(char *userhost, char *user, int portnumber);
29 static int do_sftp_init(void);
30 void do_sftp_cleanup();
31
32 /* ----------------------------------------------------------------------
33  * sftp client state.
34  */
35
36 char *pwd, *homedir;
37 static Backend *back;
38 static void *backhandle;
39 static Conf *conf;
40 int sent_eof = FALSE;
41
42 /* ----------------------------------------------------------------------
43  * Manage sending requests and waiting for replies.
44  */
45 struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req)
46 {
47     struct sftp_packet *pktin;
48     struct sftp_request *rreq;
49
50     sftp_register(req);
51     pktin = sftp_recv();
52     if (pktin == NULL)
53         connection_fatal(NULL, "did not receive SFTP response packet "
54                          "from server");
55     rreq = sftp_find_request(pktin);
56     if (rreq != req)
57         connection_fatal(NULL, "unable to understand SFTP response packet "
58                          "from server: %s", fxp_error());
59     return pktin;
60 }
61
62 /* ----------------------------------------------------------------------
63  * Higher-level helper functions used in commands.
64  */
65
66 /*
67  * Attempt to canonify a pathname starting from the pwd. If
68  * canonification fails, at least fall back to returning a _valid_
69  * pathname (though it may be ugly, eg /home/simon/../foobar).
70  */
71 char *canonify(char *name)
72 {
73     char *fullname, *canonname;
74     struct sftp_packet *pktin;
75     struct sftp_request *req;
76
77     if (name[0] == '/') {
78         fullname = dupstr(name);
79     } else {
80         char *slash;
81         if (pwd[strlen(pwd) - 1] == '/')
82             slash = "";
83         else
84             slash = "/";
85         fullname = dupcat(pwd, slash, name, NULL);
86     }
87
88     req = fxp_realpath_send(fullname);
89     pktin = sftp_wait_for_reply(req);
90     canonname = fxp_realpath_recv(pktin, req);
91
92     if (canonname) {
93         sfree(fullname);
94         return canonname;
95     } else {
96         /*
97          * Attempt number 2. Some FXP_REALPATH implementations
98          * (glibc-based ones, in particular) require the _whole_
99          * path to point to something that exists, whereas others
100          * (BSD-based) only require all but the last component to
101          * exist. So if the first call failed, we should strip off
102          * everything from the last slash onwards and try again,
103          * then put the final component back on.
104          * 
105          * Special cases:
106          * 
107          *  - if the last component is "/." or "/..", then we don't
108          *    bother trying this because there's no way it can work.
109          * 
110          *  - if the thing actually ends with a "/", we remove it
111          *    before we start. Except if the string is "/" itself
112          *    (although I can't see why we'd have got here if so,
113          *    because surely "/" would have worked the first
114          *    time?), in which case we don't bother.
115          * 
116          *  - if there's no slash in the string at all, give up in
117          *    confusion (we expect at least one because of the way
118          *    we constructed the string).
119          */
120
121         int i;
122         char *returnname;
123
124         i = strlen(fullname);
125         if (i > 2 && fullname[i - 1] == '/')
126             fullname[--i] = '\0';      /* strip trailing / unless at pos 0 */
127         while (i > 0 && fullname[--i] != '/');
128
129         /*
130          * Give up on special cases.
131          */
132         if (fullname[i] != '/' ||      /* no slash at all */
133             !strcmp(fullname + i, "/.") ||      /* ends in /. */
134             !strcmp(fullname + i, "/..") ||     /* ends in /.. */
135             !strcmp(fullname, "/")) {
136             return fullname;
137         }
138
139         /*
140          * Now i points at the slash. Deal with the final special
141          * case i==0 (ie the whole path was "/nonexistentfile").
142          */
143         fullname[i] = '\0';            /* separate the string */
144         if (i == 0) {
145             req = fxp_realpath_send("/");
146         } else {
147             req = fxp_realpath_send(fullname);
148         }
149         pktin = sftp_wait_for_reply(req);
150         canonname = fxp_realpath_recv(pktin, req);
151
152         if (!canonname) {
153             /* Even that failed. Restore our best guess at the
154              * constructed filename and give up */
155             fullname[i] = '/';  /* restore slash and last component */
156             return fullname;
157         }
158
159         /*
160          * We have a canonical name for all but the last path
161          * component. Concatenate the last component and return.
162          */
163         returnname = dupcat(canonname,
164                             canonname[strlen(canonname) - 1] ==
165                             '/' ? "" : "/", fullname + i + 1, NULL);
166         sfree(fullname);
167         sfree(canonname);
168         return returnname;
169     }
170 }
171
172 /*
173  * Return a pointer to the portion of str that comes after the last
174  * slash (or backslash or colon, if `local' is TRUE).
175  */
176 static char *stripslashes(char *str, int local)
177 {
178     char *p;
179
180     if (local) {
181         p = strchr(str, ':');
182         if (p) str = p+1;
183     }
184
185     p = strrchr(str, '/');
186     if (p) str = p+1;
187
188     if (local) {
189         p = strrchr(str, '\\');
190         if (p) str = p+1;
191     }
192
193     return str;
194 }
195
196 /*
197  * qsort comparison routine for fxp_name structures. Sorts by real
198  * file name.
199  */
200 static int sftp_name_compare(const void *av, const void *bv)
201 {
202     const struct fxp_name *const *a = (const struct fxp_name *const *) av;
203     const struct fxp_name *const *b = (const struct fxp_name *const *) bv;
204     return strcmp((*a)->filename, (*b)->filename);
205 }
206
207 /*
208  * Likewise, but for a bare char *.
209  */
210 static int bare_name_compare(const void *av, const void *bv)
211 {
212     const char **a = (const char **) av;
213     const char **b = (const char **) bv;
214     return strcmp(*a, *b);
215 }
216
217 static void not_connected(void)
218 {
219     printf("psftp: not connected to a host; use \"open host.name\"\n");
220 }
221
222 /* ----------------------------------------------------------------------
223  * The meat of the `get' and `put' commands.
224  */
225 int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
226 {
227     struct fxp_handle *fh;
228     struct sftp_packet *pktin;
229     struct sftp_request *req;
230     struct fxp_xfer *xfer;
231     uint64 offset;
232     WFile *file;
233     int ret, shown_err = FALSE;
234     struct fxp_attrs attrs;
235
236     /*
237      * In recursive mode, see if we're dealing with a directory.
238      * (If we're not in recursive mode, we need not even check: the
239      * subsequent FXP_OPEN will return a usable error message.)
240      */
241     if (recurse) {
242         int result;
243
244         req = fxp_stat_send(fname);
245         pktin = sftp_wait_for_reply(req);
246         result = fxp_stat_recv(pktin, req, &attrs);
247
248         if (result &&
249             (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
250             (attrs.permissions & 0040000)) {
251
252             struct fxp_handle *dirhandle;
253             int nnames, namesize;
254             struct fxp_name **ournames;
255             struct fxp_names *names;
256             int i;
257
258             /*
259              * First, attempt to create the destination directory,
260              * unless it already exists.
261              */
262             if (file_type(outfname) != FILE_TYPE_DIRECTORY &&
263                 !create_directory(outfname)) {
264                 printf("%s: Cannot create directory\n", outfname);
265                 return 0;
266             }
267
268             /*
269              * Now get the list of filenames in the remote
270              * directory.
271              */
272             req = fxp_opendir_send(fname);
273             pktin = sftp_wait_for_reply(req);
274             dirhandle = fxp_opendir_recv(pktin, req);
275
276             if (!dirhandle) {
277                 printf("%s: unable to open directory: %s\n",
278                        fname, fxp_error());
279                 return 0;
280             }
281             nnames = namesize = 0;
282             ournames = NULL;
283             while (1) {
284                 int i;
285
286                 req = fxp_readdir_send(dirhandle);
287                 pktin = sftp_wait_for_reply(req);
288                 names = fxp_readdir_recv(pktin, req);
289
290                 if (names == NULL) {
291                     if (fxp_error_type() == SSH_FX_EOF)
292                         break;
293                     printf("%s: reading directory: %s\n", fname, fxp_error());
294                     sfree(ournames);
295                     return 0;
296                 }
297                 if (names->nnames == 0) {
298                     fxp_free_names(names);
299                     break;
300                 }
301                 if (nnames + names->nnames >= namesize) {
302                     namesize += names->nnames + 128;
303                     ournames = sresize(ournames, namesize, struct fxp_name *);
304                 }
305                 for (i = 0; i < names->nnames; i++)
306                     if (strcmp(names->names[i].filename, ".") &&
307                         strcmp(names->names[i].filename, "..")) {
308                         if (!vet_filename(names->names[i].filename)) {
309                             printf("ignoring potentially dangerous server-"
310                                    "supplied filename '%s'\n",
311                                    names->names[i].filename);
312                         } else {
313                             ournames[nnames++] =
314                                 fxp_dup_name(&names->names[i]);
315                         }
316                     }
317                 fxp_free_names(names);
318             }
319             req = fxp_close_send(dirhandle);
320             pktin = sftp_wait_for_reply(req);
321             fxp_close_recv(pktin, req);
322
323             /*
324              * Sort the names into a clear order. This ought to
325              * make things more predictable when we're doing a
326              * reget of the same directory, just in case two
327              * readdirs on the same remote directory return a
328              * different order.
329              */
330             qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare);
331
332             /*
333              * If we're in restart mode, find the last filename on
334              * this list that already exists. We may have to do a
335              * reget on _that_ file, but shouldn't have to do
336              * anything on the previous files.
337              * 
338              * If none of them exists, of course, we start at 0.
339              */
340             i = 0;
341             if (restart) {
342                 while (i < nnames) {
343                     char *nextoutfname;
344                     int ret;
345                     if (outfname)
346                         nextoutfname = dir_file_cat(outfname,
347                                                     ournames[i]->filename);
348                     else
349                         nextoutfname = dupstr(ournames[i]->filename);
350                     ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT);
351                     sfree(nextoutfname);
352                     if (ret)
353                         break;
354                     i++;
355                 }
356                 if (i > 0)
357                     i--;
358             }
359
360             /*
361              * Now we're ready to recurse. Starting at ournames[i]
362              * and continuing on to the end of the list, we
363              * construct a new source and target file name, and
364              * call sftp_get_file again.
365              */
366             for (; i < nnames; i++) {
367                 char *nextfname, *nextoutfname;
368                 int ret;
369                 
370                 nextfname = dupcat(fname, "/", ournames[i]->filename, NULL);
371                 if (outfname)
372                     nextoutfname = dir_file_cat(outfname,
373                                                 ournames[i]->filename);
374                 else
375                     nextoutfname = dupstr(ournames[i]->filename);
376                 ret = sftp_get_file(nextfname, nextoutfname, recurse, restart);
377                 restart = FALSE;       /* after first partial file, do full */
378                 sfree(nextoutfname);
379                 sfree(nextfname);
380                 if (!ret) {
381                     for (i = 0; i < nnames; i++) {
382                         fxp_free_name(ournames[i]);
383                     }
384                     sfree(ournames);
385                     return 0;
386                 }
387             }
388
389             /*
390              * Done this recursion level. Free everything.
391              */
392             for (i = 0; i < nnames; i++) {
393                 fxp_free_name(ournames[i]);
394             }
395             sfree(ournames);
396
397             return 1;
398         }
399     }
400
401     req = fxp_stat_send(fname);
402     pktin = sftp_wait_for_reply(req);
403     if (!fxp_stat_recv(pktin, req, &attrs))
404         attrs.flags = 0;
405
406     req = fxp_open_send(fname, SSH_FXF_READ, NULL);
407     pktin = sftp_wait_for_reply(req);
408     fh = fxp_open_recv(pktin, req);
409
410     if (!fh) {
411         printf("%s: open for read: %s\n", fname, fxp_error());
412         return 0;
413     }
414
415     if (restart) {
416         file = open_existing_wfile(outfname, NULL);
417     } else {
418         file = open_new_file(outfname, GET_PERMISSIONS(attrs));
419     }
420
421     if (!file) {
422         printf("local: unable to open %s\n", outfname);
423
424         req = fxp_close_send(fh);
425         pktin = sftp_wait_for_reply(req);
426         fxp_close_recv(pktin, req);
427
428         return 0;
429     }
430
431     if (restart) {
432         char decbuf[30];
433         if (seek_file(file, uint64_make(0,0) , FROM_END) == -1) {
434             close_wfile(file);
435             printf("reget: cannot restart %s - file too large\n",
436                    outfname);
437             req = fxp_close_send(fh);
438             pktin = sftp_wait_for_reply(req);
439             fxp_close_recv(pktin, req);
440                 
441             return 0;
442         }
443             
444         offset = get_file_posn(file);
445         uint64_decimal(offset, decbuf);
446         printf("reget: restarting at file position %s\n", decbuf);
447     } else {
448         offset = uint64_make(0, 0);
449     }
450
451     printf("remote:%s => local:%s\n", fname, outfname);
452
453     /*
454      * FIXME: we can use FXP_FSTAT here to get the file size, and
455      * thus put up a progress bar.
456      */
457     ret = 1;
458     xfer = xfer_download_init(fh, offset);
459     while (!xfer_done(xfer)) {
460         void *vbuf;
461         int ret, len;
462         int wpos, wlen;
463
464         xfer_download_queue(xfer);
465         pktin = sftp_recv();
466         ret = xfer_download_gotpkt(xfer, pktin);
467         if (ret <= 0) {
468             if (!shown_err) {
469                 printf("error while reading: %s\n", fxp_error());
470                 shown_err = TRUE;
471             }
472             ret = 0;
473         }
474
475         while (xfer_download_data(xfer, &vbuf, &len)) {
476             unsigned char *buf = (unsigned char *)vbuf;
477
478             wpos = 0;
479             while (wpos < len) {
480                 wlen = write_to_file(file, buf + wpos, len - wpos);
481                 if (wlen <= 0) {
482                     printf("error while writing local file\n");
483                     ret = 0;
484                     xfer_set_error(xfer);
485                     break;
486                 }
487                 wpos += wlen;
488             }
489             if (wpos < len) {          /* we had an error */
490                 ret = 0;
491                 xfer_set_error(xfer);
492             }
493
494             sfree(vbuf);
495         }
496     }
497
498     xfer_cleanup(xfer);
499
500     close_wfile(file);
501
502     req = fxp_close_send(fh);
503     pktin = sftp_wait_for_reply(req);
504     fxp_close_recv(pktin, req);
505
506     return ret;
507 }
508
509 int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
510 {
511     struct fxp_handle *fh;
512     struct fxp_xfer *xfer;
513     struct sftp_packet *pktin;
514     struct sftp_request *req;
515     uint64 offset;
516     RFile *file;
517     int ret, err, eof;
518     struct fxp_attrs attrs;
519     long permissions;
520
521     /*
522      * In recursive mode, see if we're dealing with a directory.
523      * (If we're not in recursive mode, we need not even check: the
524      * subsequent fopen will return an error message.)
525      */
526     if (recurse && file_type(fname) == FILE_TYPE_DIRECTORY) {
527         int result;
528         int nnames, namesize;
529         char *name, **ournames;
530         DirHandle *dh;
531         int i;
532
533         /*
534          * First, attempt to create the destination directory,
535          * unless it already exists.
536          */
537         req = fxp_stat_send(outfname);
538         pktin = sftp_wait_for_reply(req);
539         result = fxp_stat_recv(pktin, req, &attrs);
540         if (!result ||
541             !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||
542             !(attrs.permissions & 0040000)) {
543             req = fxp_mkdir_send(outfname);
544             pktin = sftp_wait_for_reply(req);
545             result = fxp_mkdir_recv(pktin, req);
546
547             if (!result) {
548                 printf("%s: create directory: %s\n",
549                        outfname, fxp_error());
550                 return 0;
551             }
552         }
553
554         /*
555          * Now get the list of filenames in the local directory.
556          */
557         nnames = namesize = 0;
558         ournames = NULL;
559
560         dh = open_directory(fname);
561         if (!dh) {
562             printf("%s: unable to open directory\n", fname);
563             return 0;
564         }
565         while ((name = read_filename(dh)) != NULL) {
566             if (nnames >= namesize) {
567                 namesize += 128;
568                 ournames = sresize(ournames, namesize, char *);
569             }
570             ournames[nnames++] = name;
571         }
572         close_directory(dh);
573
574         /*
575          * Sort the names into a clear order. This ought to make
576          * things more predictable when we're doing a reput of the
577          * same directory, just in case two readdirs on the same
578          * local directory return a different order.
579          */
580         qsort(ournames, nnames, sizeof(*ournames), bare_name_compare);
581
582         /*
583          * If we're in restart mode, find the last filename on this
584          * list that already exists. We may have to do a reput on
585          * _that_ file, but shouldn't have to do anything on the
586          * previous files.
587          *
588          * If none of them exists, of course, we start at 0.
589          */
590         i = 0;
591         if (restart) {
592             while (i < nnames) {
593                 char *nextoutfname;
594                 nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
595                 req = fxp_stat_send(nextoutfname);
596                 pktin = sftp_wait_for_reply(req);
597                 result = fxp_stat_recv(pktin, req, &attrs);
598                 sfree(nextoutfname);
599                 if (!result)
600                     break;
601                 i++;
602             }
603             if (i > 0)
604                 i--;
605         }
606
607         /*
608          * Now we're ready to recurse. Starting at ournames[i]
609          * and continuing on to the end of the list, we
610          * construct a new source and target file name, and
611          * call sftp_put_file again.
612          */
613         for (; i < nnames; i++) {
614             char *nextfname, *nextoutfname;
615             int ret;
616
617             if (fname)
618                 nextfname = dir_file_cat(fname, ournames[i]);
619             else
620                 nextfname = dupstr(ournames[i]);
621             nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
622             ret = sftp_put_file(nextfname, nextoutfname, recurse, restart);
623             restart = FALSE;           /* after first partial file, do full */
624             sfree(nextoutfname);
625             sfree(nextfname);
626             if (!ret) {
627                 for (i = 0; i < nnames; i++) {
628                     sfree(ournames[i]);
629                 }
630                 sfree(ournames);
631                 return 0;
632             }
633         }
634
635         /*
636          * Done this recursion level. Free everything.
637          */
638         for (i = 0; i < nnames; i++) {
639             sfree(ournames[i]);
640         }
641         sfree(ournames);
642
643         return 1;
644     }
645
646     file = open_existing_file(fname, NULL, NULL, NULL, &permissions);
647     if (!file) {
648         printf("local: unable to open %s\n", fname);
649         return 0;
650     }
651     attrs.flags = 0;
652     PUT_PERMISSIONS(attrs, permissions);
653     if (restart) {
654         req = fxp_open_send(outfname, SSH_FXF_WRITE, &attrs);
655     } else {
656         req = fxp_open_send(outfname,
657                             SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC,
658                             &attrs);
659     }
660     pktin = sftp_wait_for_reply(req);
661     fh = fxp_open_recv(pktin, req);
662
663     if (!fh) {
664         close_rfile(file);
665         printf("%s: open for write: %s\n", outfname, fxp_error());
666         return 0;
667     }
668
669     if (restart) {
670         char decbuf[30];
671         struct fxp_attrs attrs;
672         int ret;
673
674         req = fxp_fstat_send(fh);
675         pktin = sftp_wait_for_reply(req);
676         ret = fxp_fstat_recv(pktin, req, &attrs);
677
678         if (!ret) {
679             close_rfile(file);
680             printf("read size of %s: %s\n", outfname, fxp_error());
681             return 0;
682         }
683         if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) {
684             close_rfile(file);
685             printf("read size of %s: size was not given\n", outfname);
686             return 0;
687         }
688         offset = attrs.size;
689         uint64_decimal(offset, decbuf);
690         printf("reput: restarting at file position %s\n", decbuf);
691
692         if (seek_file((WFile *)file, offset, FROM_START) != 0)
693             seek_file((WFile *)file, uint64_make(0,0), FROM_END);    /* *shrug* */
694     } else {
695         offset = uint64_make(0, 0);
696     }
697
698     printf("local:%s => remote:%s\n", fname, outfname);
699
700     /*
701      * FIXME: we can use FXP_FSTAT here to get the file size, and
702      * thus put up a progress bar.
703      */
704     ret = 1;
705     xfer = xfer_upload_init(fh, offset);
706     err = eof = 0;
707     while ((!err && !eof) || !xfer_done(xfer)) {
708         char buffer[4096];
709         int len, ret;
710
711         while (xfer_upload_ready(xfer) && !err && !eof) {
712             len = read_from_file(file, buffer, sizeof(buffer));
713             if (len == -1) {
714                 printf("error while reading local file\n");
715                 err = 1;
716             } else if (len == 0) {
717                 eof = 1;
718             } else {
719                 xfer_upload_data(xfer, buffer, len);
720             }
721         }
722
723         if (!xfer_done(xfer)) {
724             pktin = sftp_recv();
725             ret = xfer_upload_gotpkt(xfer, pktin);
726             if (ret <= 0 && !err) {
727                 printf("error while writing: %s\n", fxp_error());
728                 err = 1;
729             }
730         }
731     }
732
733     xfer_cleanup(xfer);
734
735     req = fxp_close_send(fh);
736     pktin = sftp_wait_for_reply(req);
737     fxp_close_recv(pktin, req);
738
739     close_rfile(file);
740
741     return ret;
742 }
743
744 /* ----------------------------------------------------------------------
745  * A remote wildcard matcher, providing a similar interface to the
746  * local one in psftp.h.
747  */
748
749 typedef struct SftpWildcardMatcher {
750     struct fxp_handle *dirh;
751     struct fxp_names *names;
752     int namepos;
753     char *wildcard, *prefix;
754 } SftpWildcardMatcher;
755
756 SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name)
757 {
758     struct sftp_packet *pktin;
759     struct sftp_request *req;
760     char *wildcard;
761     char *unwcdir, *tmpdir, *cdir;
762     int len, check;
763     SftpWildcardMatcher *swcm;
764     struct fxp_handle *dirh;
765
766     /*
767      * We don't handle multi-level wildcards; so we expect to find
768      * a fully specified directory part, followed by a wildcard
769      * after that.
770      */
771     wildcard = stripslashes(name, 0);
772
773     unwcdir = dupstr(name);
774     len = wildcard - name;
775     unwcdir[len] = '\0';
776     if (len > 0 && unwcdir[len-1] == '/')
777         unwcdir[len-1] = '\0';
778     tmpdir = snewn(1 + len, char);
779     check = wc_unescape(tmpdir, unwcdir);
780     sfree(tmpdir);
781
782     if (!check) {
783         printf("Multiple-level wildcards are not supported\n");
784         sfree(unwcdir);
785         return NULL;
786     }
787
788     cdir = canonify(unwcdir);
789
790     req = fxp_opendir_send(cdir);
791     pktin = sftp_wait_for_reply(req);
792     dirh = fxp_opendir_recv(pktin, req);
793
794     if (dirh) {
795         swcm = snew(SftpWildcardMatcher);
796         swcm->dirh = dirh;
797         swcm->names = NULL;
798         swcm->wildcard = dupstr(wildcard);
799         swcm->prefix = unwcdir;
800     } else {
801         printf("Unable to open %s: %s\n", cdir, fxp_error());
802         swcm = NULL;
803         sfree(unwcdir);
804     }
805
806     sfree(cdir);
807
808     return swcm;
809 }
810
811 char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm)
812 {
813     struct fxp_name *name;
814     struct sftp_packet *pktin;
815     struct sftp_request *req;
816
817     while (1) {
818         if (swcm->names && swcm->namepos >= swcm->names->nnames) {
819             fxp_free_names(swcm->names);
820             swcm->names = NULL;
821         }
822
823         if (!swcm->names) {
824             req = fxp_readdir_send(swcm->dirh);
825             pktin = sftp_wait_for_reply(req);
826             swcm->names = fxp_readdir_recv(pktin, req);
827
828             if (!swcm->names) {
829                 if (fxp_error_type() != SSH_FX_EOF)
830                     printf("%s: reading directory: %s\n", swcm->prefix,
831                            fxp_error());
832                 return NULL;
833             } else if (swcm->names->nnames == 0) {
834                 /*
835                  * Another failure mode which we treat as EOF is if
836                  * the server reports success from FXP_READDIR but
837                  * returns no actual names. This is unusual, since
838                  * from most servers you'd expect at least "." and
839                  * "..", but there's nothing forbidding a server from
840                  * omitting those if it wants to.
841                  */
842                 return NULL;
843             }
844
845             swcm->namepos = 0;
846         }
847
848         assert(swcm->names && swcm->namepos < swcm->names->nnames);
849
850         name = &swcm->names->names[swcm->namepos++];
851
852         if (!strcmp(name->filename, ".") || !strcmp(name->filename, ".."))
853             continue;                  /* expected bad filenames */
854
855         if (!vet_filename(name->filename)) {
856             printf("ignoring potentially dangerous server-"
857                    "supplied filename '%s'\n", name->filename);
858             continue;                  /* unexpected bad filename */
859         }
860
861         if (!wc_match(swcm->wildcard, name->filename))
862             continue;                  /* doesn't match the wildcard */
863
864         /*
865          * We have a working filename. Return it.
866          */
867         return dupprintf("%s%s%s", swcm->prefix,
868                          (!swcm->prefix[0] ||
869                           swcm->prefix[strlen(swcm->prefix)-1]=='/' ?
870                           "" : "/"),
871                          name->filename);
872     }
873 }
874
875 void sftp_finish_wildcard_matching(SftpWildcardMatcher *swcm)
876 {
877     struct sftp_packet *pktin;
878     struct sftp_request *req;
879
880     req = fxp_close_send(swcm->dirh);
881     pktin = sftp_wait_for_reply(req);
882     fxp_close_recv(pktin, req);
883
884     if (swcm->names)
885         fxp_free_names(swcm->names);
886
887     sfree(swcm->prefix);
888     sfree(swcm->wildcard);
889
890     sfree(swcm);
891 }
892
893 /*
894  * General function to match a potential wildcard in a filename
895  * argument and iterate over every matching file. Used in several
896  * PSFTP commands (rmdir, rm, chmod, mv).
897  */
898 int wildcard_iterate(char *filename, int (*func)(void *, char *), void *ctx)
899 {
900     char *unwcfname, *newname, *cname;
901     int is_wc, ret;
902
903     unwcfname = snewn(strlen(filename)+1, char);
904     is_wc = !wc_unescape(unwcfname, filename);
905
906     if (is_wc) {
907         SftpWildcardMatcher *swcm = sftp_begin_wildcard_matching(filename);
908         int matched = FALSE;
909         sfree(unwcfname);
910
911         if (!swcm)
912             return 0;
913
914         ret = 1;
915
916         while ( (newname = sftp_wildcard_get_filename(swcm)) != NULL ) {
917             cname = canonify(newname);
918             if (!cname) {
919                 printf("%s: canonify: %s\n", newname, fxp_error());
920                 ret = 0;
921             }
922             matched = TRUE;
923             ret &= func(ctx, cname);
924             sfree(cname);
925         }
926
927         if (!matched) {
928             /* Politely warn the user that nothing matched. */
929             printf("%s: nothing matched\n", filename);
930         }
931
932         sftp_finish_wildcard_matching(swcm);
933     } else {
934         cname = canonify(unwcfname);
935         if (!cname) {
936             printf("%s: canonify: %s\n", filename, fxp_error());
937             ret = 0;
938         }
939         ret = func(ctx, cname);
940         sfree(cname);
941         sfree(unwcfname);
942     }
943
944     return ret;
945 }
946
947 /*
948  * Handy helper function.
949  */
950 int is_wildcard(char *name)
951 {
952     char *unwcfname = snewn(strlen(name)+1, char);
953     int is_wc = !wc_unescape(unwcfname, name);
954     sfree(unwcfname);
955     return is_wc;
956 }
957
958 /* ----------------------------------------------------------------------
959  * Actual sftp commands.
960  */
961 struct sftp_command {
962     char **words;
963     int nwords, wordssize;
964     int (*obey) (struct sftp_command *);        /* returns <0 to quit */
965 };
966
967 int sftp_cmd_null(struct sftp_command *cmd)
968 {
969     return 1;                          /* success */
970 }
971
972 int sftp_cmd_unknown(struct sftp_command *cmd)
973 {
974     printf("psftp: unknown command \"%s\"\n", cmd->words[0]);
975     return 0;                          /* failure */
976 }
977
978 int sftp_cmd_quit(struct sftp_command *cmd)
979 {
980     return -1;
981 }
982
983 int sftp_cmd_close(struct sftp_command *cmd)
984 {
985     if (back == NULL) {
986         not_connected();
987         return 0;
988     }
989
990     if (back != NULL && back->connected(backhandle)) {
991         char ch;
992         back->special(backhandle, TS_EOF);
993         sent_eof = TRUE;
994         sftp_recvdata(&ch, 1);
995     }
996     do_sftp_cleanup();
997
998     return 0;
999 }
1000
1001 /*
1002  * List a directory. If no arguments are given, list pwd; otherwise
1003  * list the directory given in words[1].
1004  */
1005 int sftp_cmd_ls(struct sftp_command *cmd)
1006 {
1007     struct fxp_handle *dirh;
1008     struct fxp_names *names;
1009     struct fxp_name **ournames;
1010     int nnames, namesize;
1011     char *dir, *cdir, *unwcdir, *wildcard;
1012     struct sftp_packet *pktin;
1013     struct sftp_request *req;
1014     int i;
1015
1016     if (back == NULL) {
1017         not_connected();
1018         return 0;
1019     }
1020
1021     if (cmd->nwords < 2)
1022         dir = ".";
1023     else
1024         dir = cmd->words[1];
1025
1026     unwcdir = snewn(1 + strlen(dir), char);
1027     if (wc_unescape(unwcdir, dir)) {
1028         dir = unwcdir;
1029         wildcard = NULL;
1030     } else {
1031         char *tmpdir;
1032         int len, check;
1033
1034         wildcard = stripslashes(dir, 0);
1035         unwcdir = dupstr(dir);
1036         len = wildcard - dir;
1037         unwcdir[len] = '\0';
1038         if (len > 0 && unwcdir[len-1] == '/')
1039             unwcdir[len-1] = '\0';
1040         tmpdir = snewn(1 + len, char);
1041         check = wc_unescape(tmpdir, unwcdir);
1042         sfree(tmpdir);
1043         if (!check) {
1044             printf("Multiple-level wildcards are not supported\n");
1045             sfree(unwcdir);
1046             return 0;
1047         }
1048         dir = unwcdir;
1049     }
1050
1051     cdir = canonify(dir);
1052     if (!cdir) {
1053         printf("%s: canonify: %s\n", dir, fxp_error());
1054         sfree(unwcdir);
1055         return 0;
1056     }
1057
1058     printf("Listing directory %s\n", cdir);
1059
1060     req = fxp_opendir_send(cdir);
1061     pktin = sftp_wait_for_reply(req);
1062     dirh = fxp_opendir_recv(pktin, req);
1063
1064     if (dirh == NULL) {
1065         printf("Unable to open %s: %s\n", dir, fxp_error());
1066     } else {
1067         nnames = namesize = 0;
1068         ournames = NULL;
1069
1070         while (1) {
1071
1072             req = fxp_readdir_send(dirh);
1073             pktin = sftp_wait_for_reply(req);
1074             names = fxp_readdir_recv(pktin, req);
1075
1076             if (names == NULL) {
1077                 if (fxp_error_type() == SSH_FX_EOF)
1078                     break;
1079                 printf("Reading directory %s: %s\n", dir, fxp_error());
1080                 break;
1081             }
1082             if (names->nnames == 0) {
1083                 fxp_free_names(names);
1084                 break;
1085             }
1086
1087             if (nnames + names->nnames >= namesize) {
1088                 namesize += names->nnames + 128;
1089                 ournames = sresize(ournames, namesize, struct fxp_name *);
1090             }
1091
1092             for (i = 0; i < names->nnames; i++)
1093                 if (!wildcard || wc_match(wildcard, names->names[i].filename))
1094                     ournames[nnames++] = fxp_dup_name(&names->names[i]);
1095
1096             fxp_free_names(names);
1097         }
1098         req = fxp_close_send(dirh);
1099         pktin = sftp_wait_for_reply(req);
1100         fxp_close_recv(pktin, req);
1101
1102         /*
1103          * Now we have our filenames. Sort them by actual file
1104          * name, and then output the longname parts.
1105          */
1106         qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare);
1107
1108         /*
1109          * And print them.
1110          */
1111         for (i = 0; i < nnames; i++) {
1112             printf("%s\n", ournames[i]->longname);
1113             fxp_free_name(ournames[i]);
1114         }
1115         sfree(ournames);
1116     }
1117
1118     sfree(cdir);
1119     sfree(unwcdir);
1120
1121     return 1;
1122 }
1123
1124 /*
1125  * Change directories. We do this by canonifying the new name, then
1126  * trying to OPENDIR it. Only if that succeeds do we set the new pwd.
1127  */
1128 int sftp_cmd_cd(struct sftp_command *cmd)
1129 {
1130     struct fxp_handle *dirh;
1131     struct sftp_packet *pktin;
1132     struct sftp_request *req;
1133     char *dir;
1134
1135     if (back == NULL) {
1136         not_connected();
1137         return 0;
1138     }
1139
1140     if (cmd->nwords < 2)
1141         dir = dupstr(homedir);
1142     else
1143         dir = canonify(cmd->words[1]);
1144
1145     if (!dir) {
1146         printf("%s: canonify: %s\n", dir, fxp_error());
1147         return 0;
1148     }
1149
1150     req = fxp_opendir_send(dir);
1151     pktin = sftp_wait_for_reply(req);
1152     dirh = fxp_opendir_recv(pktin, req);
1153
1154     if (!dirh) {
1155         printf("Directory %s: %s\n", dir, fxp_error());
1156         sfree(dir);
1157         return 0;
1158     }
1159
1160     req = fxp_close_send(dirh);
1161     pktin = sftp_wait_for_reply(req);
1162     fxp_close_recv(pktin, req);
1163
1164     sfree(pwd);
1165     pwd = dir;
1166     printf("Remote directory is now %s\n", pwd);
1167
1168     return 1;
1169 }
1170
1171 /*
1172  * Print current directory. Easy as pie.
1173  */
1174 int sftp_cmd_pwd(struct sftp_command *cmd)
1175 {
1176     if (back == NULL) {
1177         not_connected();
1178         return 0;
1179     }
1180
1181     printf("Remote directory is %s\n", pwd);
1182     return 1;
1183 }
1184
1185 /*
1186  * Get a file and save it at the local end. We have three very
1187  * similar commands here. The basic one is `get'; `reget' differs
1188  * in that it checks for the existence of the destination file and
1189  * starts from where a previous aborted transfer left off; `mget'
1190  * differs in that it interprets all its arguments as files to
1191  * transfer (never as a different local name for a remote file) and
1192  * can handle wildcards.
1193  */
1194 int sftp_general_get(struct sftp_command *cmd, int restart, int multiple)
1195 {
1196     char *fname, *unwcfname, *origfname, *origwfname, *outfname;
1197     int i, ret;
1198     int recurse = FALSE;
1199
1200     if (back == NULL) {
1201         not_connected();
1202         return 0;
1203     }
1204
1205     i = 1;
1206     while (i < cmd->nwords && cmd->words[i][0] == '-') {
1207         if (!strcmp(cmd->words[i], "--")) {
1208             /* finish processing options */
1209             i++;
1210             break;
1211         } else if (!strcmp(cmd->words[i], "-r")) {
1212             recurse = TRUE;
1213         } else {
1214             printf("%s: unrecognised option '%s'\n", cmd->words[0], cmd->words[i]);
1215             return 0;
1216         }
1217         i++;
1218     }
1219
1220     if (i >= cmd->nwords) {
1221         printf("%s: expects a filename\n", cmd->words[0]);
1222         return 0;
1223     }
1224
1225     ret = 1;
1226     do {
1227         SftpWildcardMatcher *swcm;
1228
1229         origfname = cmd->words[i++];
1230         unwcfname = snewn(strlen(origfname)+1, char);
1231
1232         if (multiple && !wc_unescape(unwcfname, origfname)) {
1233             swcm = sftp_begin_wildcard_matching(origfname);
1234             if (!swcm) {
1235                 sfree(unwcfname);
1236                 continue;
1237             }
1238             origwfname = sftp_wildcard_get_filename(swcm);
1239             if (!origwfname) {
1240                 /* Politely warn the user that nothing matched. */
1241                 printf("%s: nothing matched\n", origfname);
1242                 sftp_finish_wildcard_matching(swcm);
1243                 sfree(unwcfname);
1244                 continue;
1245             }
1246         } else {
1247             origwfname = origfname;
1248             swcm = NULL;
1249         }
1250
1251         while (origwfname) {
1252             fname = canonify(origwfname);
1253
1254             if (!fname) {
1255                 printf("%s: canonify: %s\n", origwfname, fxp_error());
1256                 sfree(unwcfname);
1257                 return 0;
1258             }
1259
1260             if (!multiple && i < cmd->nwords)
1261                 outfname = cmd->words[i++];
1262             else
1263                 outfname = stripslashes(origwfname, 0);
1264
1265             ret = sftp_get_file(fname, outfname, recurse, restart);
1266
1267             sfree(fname);
1268
1269             if (swcm) {
1270                 sfree(origwfname);
1271                 origwfname = sftp_wildcard_get_filename(swcm);
1272             } else {
1273                 origwfname = NULL;
1274             }
1275         }
1276         sfree(unwcfname);
1277         if (swcm)
1278             sftp_finish_wildcard_matching(swcm);
1279         if (!ret)
1280             return ret;
1281
1282     } while (multiple && i < cmd->nwords);
1283
1284     return ret;
1285 }
1286 int sftp_cmd_get(struct sftp_command *cmd)
1287 {
1288     return sftp_general_get(cmd, 0, 0);
1289 }
1290 int sftp_cmd_mget(struct sftp_command *cmd)
1291 {
1292     return sftp_general_get(cmd, 0, 1);
1293 }
1294 int sftp_cmd_reget(struct sftp_command *cmd)
1295 {
1296     return sftp_general_get(cmd, 1, 0);
1297 }
1298
1299 /*
1300  * Send a file and store it at the remote end. We have three very
1301  * similar commands here. The basic one is `put'; `reput' differs
1302  * in that it checks for the existence of the destination file and
1303  * starts from where a previous aborted transfer left off; `mput'
1304  * differs in that it interprets all its arguments as files to
1305  * transfer (never as a different remote name for a local file) and
1306  * can handle wildcards.
1307  */
1308 int sftp_general_put(struct sftp_command *cmd, int restart, int multiple)
1309 {
1310     char *fname, *wfname, *origoutfname, *outfname;
1311     int i, ret;
1312     int recurse = FALSE;
1313
1314     if (back == NULL) {
1315         not_connected();
1316         return 0;
1317     }
1318
1319     i = 1;
1320     while (i < cmd->nwords && cmd->words[i][0] == '-') {
1321         if (!strcmp(cmd->words[i], "--")) {
1322             /* finish processing options */
1323             i++;
1324             break;
1325         } else if (!strcmp(cmd->words[i], "-r")) {
1326             recurse = TRUE;
1327         } else {
1328             printf("%s: unrecognised option '%s'\n", cmd->words[0], cmd->words[i]);
1329             return 0;
1330         }
1331         i++;
1332     }
1333
1334     if (i >= cmd->nwords) {
1335         printf("%s: expects a filename\n", cmd->words[0]);
1336         return 0;
1337     }
1338
1339     ret = 1;
1340     do {
1341         WildcardMatcher *wcm;
1342         fname = cmd->words[i++];
1343
1344         if (multiple && test_wildcard(fname, FALSE) == WCTYPE_WILDCARD) {
1345             wcm = begin_wildcard_matching(fname);
1346             wfname = wildcard_get_filename(wcm);
1347             if (!wfname) {
1348                 /* Politely warn the user that nothing matched. */
1349                 printf("%s: nothing matched\n", fname);
1350                 finish_wildcard_matching(wcm);
1351                 continue;
1352             }
1353         } else {
1354             wfname = fname;
1355             wcm = NULL;
1356         }
1357
1358         while (wfname) {
1359             if (!multiple && i < cmd->nwords)
1360                 origoutfname = cmd->words[i++];
1361             else
1362                 origoutfname = stripslashes(wfname, 1);
1363
1364             outfname = canonify(origoutfname);
1365             if (!outfname) {
1366                 printf("%s: canonify: %s\n", origoutfname, fxp_error());
1367                 if (wcm) {
1368                     sfree(wfname);
1369                     finish_wildcard_matching(wcm);
1370                 }
1371                 return 0;
1372             }
1373             ret = sftp_put_file(wfname, outfname, recurse, restart);
1374             sfree(outfname);
1375
1376             if (wcm) {
1377                 sfree(wfname);
1378                 wfname = wildcard_get_filename(wcm);
1379             } else {
1380                 wfname = NULL;
1381             }
1382         }
1383
1384         if (wcm)
1385             finish_wildcard_matching(wcm);
1386
1387         if (!ret)
1388             return ret;
1389
1390     } while (multiple && i < cmd->nwords);
1391
1392     return ret;
1393 }
1394 int sftp_cmd_put(struct sftp_command *cmd)
1395 {
1396     return sftp_general_put(cmd, 0, 0);
1397 }
1398 int sftp_cmd_mput(struct sftp_command *cmd)
1399 {
1400     return sftp_general_put(cmd, 0, 1);
1401 }
1402 int sftp_cmd_reput(struct sftp_command *cmd)
1403 {
1404     return sftp_general_put(cmd, 1, 0);
1405 }
1406
1407 int sftp_cmd_mkdir(struct sftp_command *cmd)
1408 {
1409     char *dir;
1410     struct sftp_packet *pktin;
1411     struct sftp_request *req;
1412     int result;
1413     int i, ret;
1414
1415     if (back == NULL) {
1416         not_connected();
1417         return 0;
1418     }
1419
1420     if (cmd->nwords < 2) {
1421         printf("mkdir: expects a directory\n");
1422         return 0;
1423     }
1424
1425     ret = 1;
1426     for (i = 1; i < cmd->nwords; i++) {
1427         dir = canonify(cmd->words[i]);
1428         if (!dir) {
1429             printf("%s: canonify: %s\n", dir, fxp_error());
1430             return 0;
1431         }
1432
1433         req = fxp_mkdir_send(dir);
1434         pktin = sftp_wait_for_reply(req);
1435         result = fxp_mkdir_recv(pktin, req);
1436
1437         if (!result) {
1438             printf("mkdir %s: %s\n", dir, fxp_error());
1439             ret = 0;
1440         } else
1441             printf("mkdir %s: OK\n", dir);
1442
1443         sfree(dir);
1444     }
1445
1446     return ret;
1447 }
1448
1449 static int sftp_action_rmdir(void *vctx, char *dir)
1450 {
1451     struct sftp_packet *pktin;
1452     struct sftp_request *req;
1453     int result;
1454
1455     req = fxp_rmdir_send(dir);
1456     pktin = sftp_wait_for_reply(req);
1457     result = fxp_rmdir_recv(pktin, req);
1458
1459     if (!result) {
1460         printf("rmdir %s: %s\n", dir, fxp_error());
1461         return 0;
1462     }
1463
1464     printf("rmdir %s: OK\n", dir);
1465
1466     return 1;
1467 }
1468
1469 int sftp_cmd_rmdir(struct sftp_command *cmd)
1470 {
1471     int i, ret;
1472
1473     if (back == NULL) {
1474         not_connected();
1475         return 0;
1476     }
1477
1478     if (cmd->nwords < 2) {
1479         printf("rmdir: expects a directory\n");
1480         return 0;
1481     }
1482
1483     ret = 1;
1484     for (i = 1; i < cmd->nwords; i++)
1485         ret &= wildcard_iterate(cmd->words[i], sftp_action_rmdir, NULL);
1486
1487     return ret;
1488 }
1489
1490 static int sftp_action_rm(void *vctx, char *fname)
1491 {
1492     struct sftp_packet *pktin;
1493     struct sftp_request *req;
1494     int result;
1495
1496     req = fxp_remove_send(fname);
1497     pktin = sftp_wait_for_reply(req);
1498     result = fxp_remove_recv(pktin, req);
1499
1500     if (!result) {
1501         printf("rm %s: %s\n", fname, fxp_error());
1502         return 0;
1503     }
1504
1505     printf("rm %s: OK\n", fname);
1506
1507     return 1;
1508 }
1509
1510 int sftp_cmd_rm(struct sftp_command *cmd)
1511 {
1512     int i, ret;
1513
1514     if (back == NULL) {
1515         not_connected();
1516         return 0;
1517     }
1518
1519     if (cmd->nwords < 2) {
1520         printf("rm: expects a filename\n");
1521         return 0;
1522     }
1523
1524     ret = 1;
1525     for (i = 1; i < cmd->nwords; i++)
1526         ret &= wildcard_iterate(cmd->words[i], sftp_action_rm, NULL);
1527
1528     return ret;
1529 }
1530
1531 static int check_is_dir(char *dstfname)
1532 {
1533     struct sftp_packet *pktin;
1534     struct sftp_request *req;
1535     struct fxp_attrs attrs;
1536     int result;
1537
1538     req = fxp_stat_send(dstfname);
1539     pktin = sftp_wait_for_reply(req);
1540     result = fxp_stat_recv(pktin, req, &attrs);
1541
1542     if (result &&
1543         (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
1544         (attrs.permissions & 0040000))
1545         return TRUE;
1546     else
1547         return FALSE;
1548 }
1549
1550 struct sftp_context_mv {
1551     char *dstfname;
1552     int dest_is_dir;
1553 };
1554
1555 static int sftp_action_mv(void *vctx, char *srcfname)
1556 {
1557     struct sftp_context_mv *ctx = (struct sftp_context_mv *)vctx;
1558     struct sftp_packet *pktin;
1559     struct sftp_request *req;
1560     const char *error;
1561     char *finalfname, *newcanon = NULL;
1562     int ret, result;
1563
1564     if (ctx->dest_is_dir) {
1565         char *p;
1566         char *newname;
1567
1568         p = srcfname + strlen(srcfname);
1569         while (p > srcfname && p[-1] != '/') p--;
1570         newname = dupcat(ctx->dstfname, "/", p, NULL);
1571         newcanon = canonify(newname);
1572         if (!newcanon) {
1573             printf("%s: canonify: %s\n", newname, fxp_error());
1574             sfree(newname);
1575             return 0;
1576         }
1577         sfree(newname);
1578
1579         finalfname = newcanon;
1580     } else {
1581         finalfname = ctx->dstfname;
1582     }
1583
1584     req = fxp_rename_send(srcfname, finalfname);
1585     pktin = sftp_wait_for_reply(req);
1586     result = fxp_rename_recv(pktin, req);
1587
1588     error = result ? NULL : fxp_error();
1589
1590     if (error) {
1591         printf("mv %s %s: %s\n", srcfname, finalfname, error);
1592         ret = 0;
1593     } else {
1594         printf("%s -> %s\n", srcfname, finalfname);
1595         ret = 1;
1596     }
1597
1598     sfree(newcanon);
1599     return ret;
1600 }
1601
1602 int sftp_cmd_mv(struct sftp_command *cmd)
1603 {
1604     struct sftp_context_mv actx, *ctx = &actx;
1605     int i, ret;
1606
1607     if (back == NULL) {
1608         not_connected();
1609         return 0;
1610     }
1611
1612     if (cmd->nwords < 3) {
1613         printf("mv: expects two filenames\n");
1614         return 0;
1615     }
1616
1617     ctx->dstfname = canonify(cmd->words[cmd->nwords-1]);
1618     if (!ctx->dstfname) {
1619         printf("%s: canonify: %s\n", ctx->dstfname, fxp_error());
1620         return 0;
1621     }
1622
1623     /*
1624      * If there's more than one source argument, or one source
1625      * argument which is a wildcard, we _require_ that the
1626      * destination is a directory.
1627      */
1628     ctx->dest_is_dir = check_is_dir(ctx->dstfname);
1629     if ((cmd->nwords > 3 || is_wildcard(cmd->words[1])) && !ctx->dest_is_dir) {
1630         printf("mv: multiple or wildcard arguments require the destination"
1631                " to be a directory\n");
1632         sfree(ctx->dstfname);
1633         return 0;
1634     }
1635
1636     /*
1637      * Now iterate over the source arguments.
1638      */
1639     ret = 1;
1640     for (i = 1; i < cmd->nwords-1; i++)
1641         ret &= wildcard_iterate(cmd->words[i], sftp_action_mv, ctx);
1642
1643     sfree(ctx->dstfname);
1644     return ret;
1645 }
1646
1647 struct sftp_context_chmod {
1648     unsigned attrs_clr, attrs_xor;
1649 };
1650
1651 static int sftp_action_chmod(void *vctx, char *fname)
1652 {
1653     struct fxp_attrs attrs;
1654     struct sftp_packet *pktin;
1655     struct sftp_request *req;
1656     int result;
1657     unsigned oldperms, newperms;
1658     struct sftp_context_chmod *ctx = (struct sftp_context_chmod *)vctx;
1659
1660     req = fxp_stat_send(fname);
1661     pktin = sftp_wait_for_reply(req);
1662     result = fxp_stat_recv(pktin, req, &attrs);
1663
1664     if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) {
1665         printf("get attrs for %s: %s\n", fname,
1666                result ? "file permissions not provided" : fxp_error());
1667         return 0;
1668     }
1669
1670     attrs.flags = SSH_FILEXFER_ATTR_PERMISSIONS;   /* perms _only_ */
1671     oldperms = attrs.permissions & 07777;
1672     attrs.permissions &= ~ctx->attrs_clr;
1673     attrs.permissions ^= ctx->attrs_xor;
1674     newperms = attrs.permissions & 07777;
1675
1676     if (oldperms == newperms)
1677         return 1;                      /* no need to do anything! */
1678
1679     req = fxp_setstat_send(fname, attrs);
1680     pktin = sftp_wait_for_reply(req);
1681     result = fxp_setstat_recv(pktin, req);
1682
1683     if (!result) {
1684         printf("set attrs for %s: %s\n", fname, fxp_error());
1685         return 0;
1686     }
1687
1688     printf("%s: %04o -> %04o\n", fname, oldperms, newperms);
1689
1690     return 1;
1691 }
1692
1693 int sftp_cmd_chmod(struct sftp_command *cmd)
1694 {
1695     char *mode;
1696     int i, ret;
1697     struct sftp_context_chmod actx, *ctx = &actx;
1698
1699     if (back == NULL) {
1700         not_connected();
1701         return 0;
1702     }
1703
1704     if (cmd->nwords < 3) {
1705         printf("chmod: expects a mode specifier and a filename\n");
1706         return 0;
1707     }
1708
1709     /*
1710      * Attempt to parse the mode specifier in cmd->words[1]. We
1711      * don't support the full horror of Unix chmod; instead we
1712      * support a much simpler syntax in which the user can either
1713      * specify an octal number, or a comma-separated sequence of
1714      * [ugoa]*[-+=][rwxst]+. (The initial [ugoa] sequence may
1715      * _only_ be omitted if the only attribute mentioned is t,
1716      * since all others require a user/group/other specification.
1717      * Additionally, the s attribute may not be specified for any
1718      * [ugoa] specifications other than exactly u or exactly g.
1719      */
1720     ctx->attrs_clr = ctx->attrs_xor = 0;
1721     mode = cmd->words[1];
1722     if (mode[0] >= '0' && mode[0] <= '9') {
1723         if (mode[strspn(mode, "01234567")]) {
1724             printf("chmod: numeric file modes should"
1725                    " contain digits 0-7 only\n");
1726             return 0;
1727         }
1728         ctx->attrs_clr = 07777;
1729         sscanf(mode, "%o", &ctx->attrs_xor);
1730         ctx->attrs_xor &= ctx->attrs_clr;
1731     } else {
1732         while (*mode) {
1733             char *modebegin = mode;
1734             unsigned subset, perms;
1735             int action;
1736
1737             subset = 0;
1738             while (*mode && *mode != ',' &&
1739                    *mode != '+' && *mode != '-' && *mode != '=') {
1740                 switch (*mode) {
1741                   case 'u': subset |= 04700; break; /* setuid, user perms */
1742                   case 'g': subset |= 02070; break; /* setgid, group perms */
1743                   case 'o': subset |= 00007; break; /* just other perms */
1744                   case 'a': subset |= 06777; break; /* all of the above */
1745                   default:
1746                     printf("chmod: file mode '%.*s' contains unrecognised"
1747                            " user/group/other specifier '%c'\n",
1748                            (int)strcspn(modebegin, ","), modebegin, *mode);
1749                     return 0;
1750                 }
1751                 mode++;
1752             }
1753             if (!*mode || *mode == ',') {
1754                 printf("chmod: file mode '%.*s' is incomplete\n",
1755                        (int)strcspn(modebegin, ","), modebegin);
1756                 return 0;
1757             }
1758             action = *mode++;
1759             if (!*mode || *mode == ',') {
1760                 printf("chmod: file mode '%.*s' is incomplete\n",
1761                        (int)strcspn(modebegin, ","), modebegin);
1762                 return 0;
1763             }
1764             perms = 0;
1765             while (*mode && *mode != ',') {
1766                 switch (*mode) {
1767                   case 'r': perms |= 00444; break;
1768                   case 'w': perms |= 00222; break;
1769                   case 'x': perms |= 00111; break;
1770                   case 't': perms |= 01000; subset |= 01000; break;
1771                   case 's':
1772                     if ((subset & 06777) != 04700 &&
1773                         (subset & 06777) != 02070) {
1774                         printf("chmod: file mode '%.*s': set[ug]id bit should"
1775                                " be used with exactly one of u or g only\n",
1776                                (int)strcspn(modebegin, ","), modebegin);
1777                         return 0;
1778                     }
1779                     perms |= 06000;
1780                     break;
1781                   default:
1782                     printf("chmod: file mode '%.*s' contains unrecognised"
1783                            " permission specifier '%c'\n",
1784                            (int)strcspn(modebegin, ","), modebegin, *mode);
1785                     return 0;
1786                 }
1787                 mode++;
1788             }
1789             if (!(subset & 06777) && (perms &~ subset)) {
1790                 printf("chmod: file mode '%.*s' contains no user/group/other"
1791                        " specifier and permissions other than 't' \n",
1792                        (int)strcspn(modebegin, ","), modebegin);
1793                 return 0;
1794             }
1795             perms &= subset;
1796             switch (action) {
1797               case '+':
1798                 ctx->attrs_clr |= perms;
1799                 ctx->attrs_xor |= perms;
1800                 break;
1801               case '-':
1802                 ctx->attrs_clr |= perms;
1803                 ctx->attrs_xor &= ~perms;
1804                 break;
1805               case '=':
1806                 ctx->attrs_clr |= subset;
1807                 ctx->attrs_xor |= perms;
1808                 break;
1809             }
1810             if (*mode) mode++;         /* eat comma */
1811         }
1812     }
1813
1814     ret = 1;
1815     for (i = 2; i < cmd->nwords; i++)
1816         ret &= wildcard_iterate(cmd->words[i], sftp_action_chmod, ctx);
1817
1818     return ret;
1819 }
1820
1821 static int sftp_cmd_open(struct sftp_command *cmd)
1822 {
1823     int portnumber;
1824
1825     if (back != NULL) {
1826         printf("psftp: already connected\n");
1827         return 0;
1828     }
1829
1830     if (cmd->nwords < 2) {
1831         printf("open: expects a host name\n");
1832         return 0;
1833     }
1834
1835     if (cmd->nwords > 2) {
1836         portnumber = atoi(cmd->words[2]);
1837         if (portnumber == 0) {
1838             printf("open: invalid port number\n");
1839             return 0;
1840         }
1841     } else
1842         portnumber = 0;
1843
1844     if (psftp_connect(cmd->words[1], NULL, portnumber)) {
1845         back = NULL;                   /* connection is already closed */
1846         return -1;                     /* this is fatal */
1847     }
1848     do_sftp_init();
1849     return 1;
1850 }
1851
1852 static int sftp_cmd_lcd(struct sftp_command *cmd)
1853 {
1854     char *currdir, *errmsg;
1855
1856     if (cmd->nwords < 2) {
1857         printf("lcd: expects a local directory name\n");
1858         return 0;
1859     }
1860
1861     errmsg = psftp_lcd(cmd->words[1]);
1862     if (errmsg) {
1863         printf("lcd: unable to change directory: %s\n", errmsg);
1864         sfree(errmsg);
1865         return 0;
1866     }
1867
1868     currdir = psftp_getcwd();
1869     printf("New local directory is %s\n", currdir);
1870     sfree(currdir);
1871
1872     return 1;
1873 }
1874
1875 static int sftp_cmd_lpwd(struct sftp_command *cmd)
1876 {
1877     char *currdir;
1878
1879     currdir = psftp_getcwd();
1880     printf("Current local directory is %s\n", currdir);
1881     sfree(currdir);
1882
1883     return 1;
1884 }
1885
1886 static int sftp_cmd_pling(struct sftp_command *cmd)
1887 {
1888     int exitcode;
1889
1890     exitcode = system(cmd->words[1]);
1891     return (exitcode == 0);
1892 }
1893
1894 static int sftp_cmd_help(struct sftp_command *cmd);
1895
1896 static struct sftp_cmd_lookup {
1897     char *name;
1898     /*
1899      * For help purposes, there are two kinds of command:
1900      * 
1901      *  - primary commands, in which `longhelp' is non-NULL. In
1902      *    this case `shorthelp' is descriptive text, and `longhelp'
1903      *    is longer descriptive text intended to be printed after
1904      *    the command name.
1905      * 
1906      *  - alias commands, in which `longhelp' is NULL. In this case
1907      *    `shorthelp' is the name of a primary command, which
1908      *    contains the help that should double up for this command.
1909      */
1910     int listed;                        /* do we list this in primary help? */
1911     char *shorthelp;
1912     char *longhelp;
1913     int (*obey) (struct sftp_command *);
1914 } sftp_lookup[] = {
1915     /*
1916      * List of sftp commands. This is binary-searched so it MUST be
1917      * in ASCII order.
1918      */
1919     {
1920         "!", TRUE, "run a local command",
1921             "<command>\n"
1922             /* FIXME: this example is crap for non-Windows. */
1923             "  Runs a local command. For example, \"!del myfile\".\n",
1924             sftp_cmd_pling
1925     },
1926     {
1927         "bye", TRUE, "finish your SFTP session",
1928             "\n"
1929             "  Terminates your SFTP session and quits the PSFTP program.\n",
1930             sftp_cmd_quit
1931     },
1932     {
1933         "cd", TRUE, "change your remote working directory",
1934             " [ <new working directory> ]\n"
1935             "  Change the remote working directory for your SFTP session.\n"
1936             "  If a new working directory is not supplied, you will be\n"
1937             "  returned to your home directory.\n",
1938             sftp_cmd_cd
1939     },
1940     {
1941         "chmod", TRUE, "change file permissions and modes",
1942             " <modes> <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
1943             "  Change the file permissions on one or more remote files or\n"
1944             "  directories.\n"
1945             "  <modes> can be any octal Unix permission specifier.\n"
1946             "  Alternatively, <modes> can include the following modifiers:\n"
1947             "    u+r     make file readable by owning user\n"
1948             "    u+w     make file writable by owning user\n"
1949             "    u+x     make file executable by owning user\n"
1950             "    u-r     make file not readable by owning user\n"
1951             "    [also u-w, u-x]\n"
1952             "    g+r     make file readable by members of owning group\n"
1953             "    [also g+w, g+x, g-r, g-w, g-x]\n"
1954             "    o+r     make file readable by all other users\n"
1955             "    [also o+w, o+x, o-r, o-w, o-x]\n"
1956             "    a+r     make file readable by absolutely everybody\n"
1957             "    [also a+w, a+x, a-r, a-w, a-x]\n"
1958             "    u+s     enable the Unix set-user-ID bit\n"
1959             "    u-s     disable the Unix set-user-ID bit\n"
1960             "    g+s     enable the Unix set-group-ID bit\n"
1961             "    g-s     disable the Unix set-group-ID bit\n"
1962             "    +t      enable the Unix \"sticky bit\"\n"
1963             "  You can give more than one modifier for the same user (\"g-rwx\"), and\n"
1964             "  more than one user for the same modifier (\"ug+w\"). You can\n"
1965             "  use commas to separate different modifiers (\"u+rwx,g+s\").\n",
1966             sftp_cmd_chmod
1967     },
1968     {
1969         "close", TRUE, "finish your SFTP session but do not quit PSFTP",
1970             "\n"
1971             "  Terminates your SFTP session, but does not quit the PSFTP\n"
1972             "  program. You can then use \"open\" to start another SFTP\n"
1973             "  session, to the same server or to a different one.\n",
1974             sftp_cmd_close
1975     },
1976     {
1977         "del", TRUE, "delete files on the remote server",
1978             " <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
1979             "  Delete a file or files from the server.\n",
1980             sftp_cmd_rm
1981     },
1982     {
1983         "delete", FALSE, "del", NULL, sftp_cmd_rm
1984     },
1985     {
1986         "dir", TRUE, "list remote files",
1987             " [ <directory-name> ]/[ <wildcard> ]\n"
1988             "  List the contents of a specified directory on the server.\n"
1989             "  If <directory-name> is not given, the current working directory\n"
1990             "  is assumed.\n"
1991             "  If <wildcard> is given, it is treated as a set of files to\n"
1992             "  list; otherwise, all files are listed.\n",
1993             sftp_cmd_ls
1994     },
1995     {
1996         "exit", TRUE, "bye", NULL, sftp_cmd_quit
1997     },
1998     {
1999         "get", TRUE, "download a file from the server to your local machine",
2000             " [ -r ] [ -- ] <filename> [ <local-filename> ]\n"
2001             "  Downloads a file on the server and stores it locally under\n"
2002             "  the same name, or under a different one if you supply the\n"
2003             "  argument <local-filename>.\n"
2004             "  If -r specified, recursively fetch a directory.\n",
2005             sftp_cmd_get
2006     },
2007     {
2008         "help", TRUE, "give help",
2009             " [ <command> [ <command> ... ] ]\n"
2010             "  Give general help if no commands are specified.\n"
2011             "  If one or more commands are specified, give specific help on\n"
2012             "  those particular commands.\n",
2013             sftp_cmd_help
2014     },
2015     {
2016         "lcd", TRUE, "change local working directory",
2017             " <local-directory-name>\n"
2018             "  Change the local working directory of the PSFTP program (the\n"
2019             "  default location where the \"get\" command will save files).\n",
2020             sftp_cmd_lcd
2021     },
2022     {
2023         "lpwd", TRUE, "print local working directory",
2024             "\n"
2025             "  Print the local working directory of the PSFTP program (the\n"
2026             "  default location where the \"get\" command will save files).\n",
2027             sftp_cmd_lpwd
2028     },
2029     {
2030         "ls", TRUE, "dir", NULL,
2031             sftp_cmd_ls
2032     },
2033     {
2034         "mget", TRUE, "download multiple files at once",
2035             " [ -r ] [ -- ] <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
2036             "  Downloads many files from the server, storing each one under\n"
2037             "  the same name it has on the server side. You can use wildcards\n"
2038             "  such as \"*.c\" to specify lots of files at once.\n"
2039             "  If -r specified, recursively fetch files and directories.\n",
2040             sftp_cmd_mget
2041     },
2042     {
2043         "mkdir", TRUE, "create directories on the remote server",
2044             " <directory-name> [ <directory-name>... ]\n"
2045             "  Creates directories with the given names on the server.\n",
2046             sftp_cmd_mkdir
2047     },
2048     {
2049         "mput", TRUE, "upload multiple files at once",
2050             " [ -r ] [ -- ] <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
2051             "  Uploads many files to the server, storing each one under the\n"
2052             "  same name it has on the client side. You can use wildcards\n"
2053             "  such as \"*.c\" to specify lots of files at once.\n"
2054             "  If -r specified, recursively store files and directories.\n",
2055             sftp_cmd_mput
2056     },
2057     {
2058         "mv", TRUE, "move or rename file(s) on the remote server",
2059             " <source> [ <source>... ] <destination>\n"
2060             "  Moves or renames <source>(s) on the server to <destination>,\n"
2061             "  also on the server.\n"
2062             "  If <destination> specifies an existing directory, then <source>\n"
2063             "  may be a wildcard, and multiple <source>s may be given; all\n"
2064             "  source files are moved into <destination>.\n"
2065             "  Otherwise, <source> must specify a single file, which is moved\n"
2066             "  or renamed so that it is accessible under the name <destination>.\n",
2067             sftp_cmd_mv
2068     },
2069     {
2070         "open", TRUE, "connect to a host",
2071             " [<user>@]<hostname> [<port>]\n"
2072             "  Establishes an SFTP connection to a given host. Only usable\n"
2073             "  when you are not already connected to a server.\n",
2074             sftp_cmd_open
2075     },
2076     {
2077         "put", TRUE, "upload a file from your local machine to the server",
2078             " [ -r ] [ -- ] <filename> [ <remote-filename> ]\n"
2079             "  Uploads a file to the server and stores it there under\n"
2080             "  the same name, or under a different one if you supply the\n"
2081             "  argument <remote-filename>.\n"
2082             "  If -r specified, recursively store a directory.\n",
2083             sftp_cmd_put
2084     },
2085     {
2086         "pwd", TRUE, "print your remote working directory",
2087             "\n"
2088             "  Print the current remote working directory for your SFTP session.\n",
2089             sftp_cmd_pwd
2090     },
2091     {
2092         "quit", TRUE, "bye", NULL,
2093             sftp_cmd_quit
2094     },
2095     {
2096         "reget", TRUE, "continue downloading files",
2097             " [ -r ] [ -- ] <filename> [ <local-filename> ]\n"
2098             "  Works exactly like the \"get\" command, but the local file\n"
2099             "  must already exist. The download will begin at the end of the\n"
2100             "  file. This is for resuming a download that was interrupted.\n"
2101             "  If -r specified, resume interrupted \"get -r\".\n",
2102             sftp_cmd_reget
2103     },
2104     {
2105         "ren", TRUE, "mv", NULL,
2106             sftp_cmd_mv
2107     },
2108     {
2109         "rename", FALSE, "mv", NULL,
2110             sftp_cmd_mv
2111     },
2112     {
2113         "reput", TRUE, "continue uploading files",
2114             " [ -r ] [ -- ] <filename> [ <remote-filename> ]\n"
2115             "  Works exactly like the \"put\" command, but the remote file\n"
2116             "  must already exist. The upload will begin at the end of the\n"
2117             "  file. This is for resuming an upload that was interrupted.\n"
2118             "  If -r specified, resume interrupted \"put -r\".\n",
2119             sftp_cmd_reput
2120     },
2121     {
2122         "rm", TRUE, "del", NULL,
2123             sftp_cmd_rm
2124     },
2125     {
2126         "rmdir", TRUE, "remove directories on the remote server",
2127             " <directory-name> [ <directory-name>... ]\n"
2128             "  Removes the directory with the given name on the server.\n"
2129             "  The directory will not be removed unless it is empty.\n"
2130             "  Wildcards may be used to specify multiple directories.\n",
2131             sftp_cmd_rmdir
2132     }
2133 };
2134
2135 const struct sftp_cmd_lookup *lookup_command(char *name)
2136 {
2137     int i, j, k, cmp;
2138
2139     i = -1;
2140     j = sizeof(sftp_lookup) / sizeof(*sftp_lookup);
2141     while (j - i > 1) {
2142         k = (j + i) / 2;
2143         cmp = strcmp(name, sftp_lookup[k].name);
2144         if (cmp < 0)
2145             j = k;
2146         else if (cmp > 0)
2147             i = k;
2148         else {
2149             return &sftp_lookup[k];
2150         }
2151     }
2152     return NULL;
2153 }
2154
2155 static int sftp_cmd_help(struct sftp_command *cmd)
2156 {
2157     int i;
2158     if (cmd->nwords == 1) {
2159         /*
2160          * Give short help on each command.
2161          */
2162         int maxlen;
2163         maxlen = 0;
2164         for (i = 0; i < sizeof(sftp_lookup) / sizeof(*sftp_lookup); i++) {
2165             int len;
2166             if (!sftp_lookup[i].listed)
2167                 continue;
2168             len = strlen(sftp_lookup[i].name);
2169             if (maxlen < len)
2170                 maxlen = len;
2171         }
2172         for (i = 0; i < sizeof(sftp_lookup) / sizeof(*sftp_lookup); i++) {
2173             const struct sftp_cmd_lookup *lookup;
2174             if (!sftp_lookup[i].listed)
2175                 continue;
2176             lookup = &sftp_lookup[i];
2177             printf("%-*s", maxlen+2, lookup->name);
2178             if (lookup->longhelp == NULL)
2179                 lookup = lookup_command(lookup->shorthelp);
2180             printf("%s\n", lookup->shorthelp);
2181         }
2182     } else {
2183         /*
2184          * Give long help on specific commands.
2185          */
2186         for (i = 1; i < cmd->nwords; i++) {
2187             const struct sftp_cmd_lookup *lookup;
2188             lookup = lookup_command(cmd->words[i]);
2189             if (!lookup) {
2190                 printf("help: %s: command not found\n", cmd->words[i]);
2191             } else {
2192                 printf("%s", lookup->name);
2193                 if (lookup->longhelp == NULL)
2194                     lookup = lookup_command(lookup->shorthelp);
2195                 printf("%s", lookup->longhelp);
2196             }
2197         }
2198     }
2199     return 1;
2200 }
2201
2202 /* ----------------------------------------------------------------------
2203  * Command line reading and parsing.
2204  */
2205 struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
2206 {
2207     char *line;
2208     struct sftp_command *cmd;
2209     char *p, *q, *r;
2210     int quoting;
2211
2212     cmd = snew(struct sftp_command);
2213     cmd->words = NULL;
2214     cmd->nwords = 0;
2215     cmd->wordssize = 0;
2216
2217     line = NULL;
2218
2219     if (fp) {
2220         if (modeflags & 1)
2221             printf("psftp> ");
2222         line = fgetline(fp);
2223     } else {
2224         line = ssh_sftp_get_cmdline("psftp> ", back == NULL);
2225     }
2226
2227     if (!line || !*line) {
2228         cmd->obey = sftp_cmd_quit;
2229         if ((mode == 0) || (modeflags & 1))
2230             printf("quit\n");
2231         return cmd;                    /* eof */
2232     }
2233
2234     line[strcspn(line, "\r\n")] = '\0';
2235
2236     if (modeflags & 1) {
2237         printf("%s\n", line);
2238     }
2239
2240     p = line;
2241     while (*p && (*p == ' ' || *p == '\t'))
2242         p++;
2243
2244     if (*p == '!') {
2245         /*
2246          * Special case: the ! command. This is always parsed as
2247          * exactly two words: one containing the !, and the second
2248          * containing everything else on the line.
2249          */
2250         cmd->nwords = cmd->wordssize = 2;
2251         cmd->words = sresize(cmd->words, cmd->wordssize, char *);
2252         cmd->words[0] = dupstr("!");
2253         cmd->words[1] = dupstr(p+1);
2254     } else if (*p == '#') {
2255         /*
2256          * Special case: comment. Entire line is ignored.
2257          */
2258         cmd->nwords = cmd->wordssize = 0;
2259     } else {
2260
2261         /*
2262          * Parse the command line into words. The syntax is:
2263          *  - double quotes are removed, but cause spaces within to be
2264          *    treated as non-separating.
2265          *  - a double-doublequote pair is a literal double quote, inside
2266          *    _or_ outside quotes. Like this:
2267          *
2268          *      firstword "second word" "this has ""quotes"" in" and""this""
2269          *
2270          * becomes
2271          *
2272          *      >firstword<
2273          *      >second word<
2274          *      >this has "quotes" in<
2275          *      >and"this"<
2276          */
2277         while (1) {
2278             /* skip whitespace */
2279             while (*p && (*p == ' ' || *p == '\t'))
2280                 p++;
2281             /* terminate loop */
2282             if (!*p)
2283                 break;
2284             /* mark start of word */
2285             q = r = p;                 /* q sits at start, r writes word */
2286             quoting = 0;
2287             while (*p) {
2288                 if (!quoting && (*p == ' ' || *p == '\t'))
2289                     break;                     /* reached end of word */
2290                 else if (*p == '"' && p[1] == '"')
2291                     p += 2, *r++ = '"';    /* a literal quote */
2292                 else if (*p == '"')
2293                     p++, quoting = !quoting;
2294                 else
2295                     *r++ = *p++;
2296             }
2297             if (*p)
2298                 p++;                   /* skip over the whitespace */
2299             *r = '\0';
2300             if (cmd->nwords >= cmd->wordssize) {
2301                 cmd->wordssize = cmd->nwords + 16;
2302                 cmd->words = sresize(cmd->words, cmd->wordssize, char *);
2303             }
2304             cmd->words[cmd->nwords++] = dupstr(q);
2305         }
2306     }
2307
2308     sfree(line);
2309
2310     /*
2311      * Now parse the first word and assign a function.
2312      */
2313
2314     if (cmd->nwords == 0)
2315         cmd->obey = sftp_cmd_null;
2316     else {
2317         const struct sftp_cmd_lookup *lookup;
2318         lookup = lookup_command(cmd->words[0]);
2319         if (!lookup)
2320             cmd->obey = sftp_cmd_unknown;
2321         else
2322             cmd->obey = lookup->obey;
2323     }
2324
2325     return cmd;
2326 }
2327
2328 static int do_sftp_init(void)
2329 {
2330     struct sftp_packet *pktin;
2331     struct sftp_request *req;
2332
2333     /*
2334      * Do protocol initialisation. 
2335      */
2336     if (!fxp_init()) {
2337         fprintf(stderr,
2338                 "Fatal: unable to initialise SFTP: %s\n", fxp_error());
2339         return 1;                      /* failure */
2340     }
2341
2342     /*
2343      * Find out where our home directory is.
2344      */
2345     req = fxp_realpath_send(".");
2346     pktin = sftp_wait_for_reply(req);
2347     homedir = fxp_realpath_recv(pktin, req);
2348
2349     if (!homedir) {
2350         fprintf(stderr,
2351                 "Warning: failed to resolve home directory: %s\n",
2352                 fxp_error());
2353         homedir = dupstr(".");
2354     } else {
2355         printf("Remote working directory is %s\n", homedir);
2356     }
2357     pwd = dupstr(homedir);
2358     return 0;
2359 }
2360
2361 void do_sftp_cleanup()
2362 {
2363     char ch;
2364     if (back) {
2365         back->special(backhandle, TS_EOF);
2366         sent_eof = TRUE;
2367         sftp_recvdata(&ch, 1);
2368         back->free(backhandle);
2369         sftp_cleanup_request();
2370         back = NULL;
2371         backhandle = NULL;
2372     }
2373     if (pwd) {
2374         sfree(pwd);
2375         pwd = NULL;
2376     }
2377     if (homedir) {
2378         sfree(homedir);
2379         homedir = NULL;
2380     }
2381 }
2382
2383 void do_sftp(int mode, int modeflags, char *batchfile)
2384 {
2385     FILE *fp;
2386     int ret;
2387
2388     /*
2389      * Batch mode?
2390      */
2391     if (mode == 0) {
2392
2393         /* ------------------------------------------------------------------
2394          * Now we're ready to do Real Stuff.
2395          */
2396         while (1) {
2397             struct sftp_command *cmd;
2398             cmd = sftp_getcmd(NULL, 0, 0);
2399             if (!cmd)
2400                 break;
2401             ret = cmd->obey(cmd);
2402             if (cmd->words) {
2403                 int i;
2404                 for(i = 0; i < cmd->nwords; i++)
2405                     sfree(cmd->words[i]);
2406                 sfree(cmd->words);
2407             }
2408             sfree(cmd);
2409             if (ret < 0)
2410                 break;
2411         }
2412     } else {
2413         fp = fopen(batchfile, "r");
2414         if (!fp) {
2415             printf("Fatal: unable to open %s\n", batchfile);
2416             return;
2417         }
2418         while (1) {
2419             struct sftp_command *cmd;
2420             cmd = sftp_getcmd(fp, mode, modeflags);
2421             if (!cmd)
2422                 break;
2423             ret = cmd->obey(cmd);
2424             if (ret < 0)
2425                 break;
2426             if (ret == 0) {
2427                 if (!(modeflags & 2))
2428                     break;
2429             }
2430         }
2431         fclose(fp);
2432
2433     }
2434 }
2435
2436 /* ----------------------------------------------------------------------
2437  * Dirty bits: integration with PuTTY.
2438  */
2439
2440 static int verbose = 0;
2441
2442 /*
2443  *  Print an error message and perform a fatal exit.
2444  */
2445 void fatalbox(char *fmt, ...)
2446 {
2447     char *str, *str2;
2448     va_list ap;
2449     va_start(ap, fmt);
2450     str = dupvprintf(fmt, ap);
2451     str2 = dupcat("Fatal: ", str, "\n", NULL);
2452     sfree(str);
2453     va_end(ap);
2454     fputs(str2, stderr);
2455     sfree(str2);
2456
2457     cleanup_exit(1);
2458 }
2459 void modalfatalbox(char *fmt, ...)
2460 {
2461     char *str, *str2;
2462     va_list ap;
2463     va_start(ap, fmt);
2464     str = dupvprintf(fmt, ap);
2465     str2 = dupcat("Fatal: ", str, "\n", NULL);
2466     sfree(str);
2467     va_end(ap);
2468     fputs(str2, stderr);
2469     sfree(str2);
2470
2471     cleanup_exit(1);
2472 }
2473 void connection_fatal(void *frontend, char *fmt, ...)
2474 {
2475     char *str, *str2;
2476     va_list ap;
2477     va_start(ap, fmt);
2478     str = dupvprintf(fmt, ap);
2479     str2 = dupcat("Fatal: ", str, "\n", NULL);
2480     sfree(str);
2481     va_end(ap);
2482     fputs(str2, stderr);
2483     sfree(str2);
2484
2485     cleanup_exit(1);
2486 }
2487
2488 void ldisc_send(void *handle, char *buf, int len, int interactive)
2489 {
2490     /*
2491      * This is only here because of the calls to ldisc_send(NULL,
2492      * 0) in ssh.c. Nothing in PSFTP actually needs to use the
2493      * ldisc as an ldisc. So if we get called with any real data, I
2494      * want to know about it.
2495      */
2496     assert(len == 0);
2497 }
2498
2499 /*
2500  * In psftp, all agent requests should be synchronous, so this is a
2501  * never-called stub.
2502  */
2503 void agent_schedule_callback(void (*callback)(void *, void *, int),
2504                              void *callback_ctx, void *data, int len)
2505 {
2506     assert(!"We shouldn't be here");
2507 }
2508
2509 /*
2510  * Receive a block of data from the SSH link. Block until all data
2511  * is available.
2512  *
2513  * To do this, we repeatedly call the SSH protocol module, with our
2514  * own trap in from_backend() to catch the data that comes back. We
2515  * do this until we have enough data.
2516  */
2517
2518 static unsigned char *outptr;          /* where to put the data */
2519 static unsigned outlen;                /* how much data required */
2520 static unsigned char *pending = NULL;  /* any spare data */
2521 static unsigned pendlen = 0, pendsize = 0;      /* length and phys. size of buffer */
2522 int from_backend(void *frontend, int is_stderr, const char *data, int datalen)
2523 {
2524     unsigned char *p = (unsigned char *) data;
2525     unsigned len = (unsigned) datalen;
2526
2527     /*
2528      * stderr data is just spouted to local stderr and otherwise
2529      * ignored.
2530      */
2531     if (is_stderr) {
2532         if (len > 0)
2533             if (fwrite(data, 1, len, stderr) < len)
2534                 /* oh well */;
2535         return 0;
2536     }
2537
2538     /*
2539      * If this is before the real session begins, just return.
2540      */
2541     if (!outptr)
2542         return 0;
2543
2544     if ((outlen > 0) && (len > 0)) {
2545         unsigned used = outlen;
2546         if (used > len)
2547             used = len;
2548         memcpy(outptr, p, used);
2549         outptr += used;
2550         outlen -= used;
2551         p += used;
2552         len -= used;
2553     }
2554
2555     if (len > 0) {
2556         if (pendsize < pendlen + len) {
2557             pendsize = pendlen + len + 4096;
2558             pending = sresize(pending, pendsize, unsigned char);
2559         }
2560         memcpy(pending + pendlen, p, len);
2561         pendlen += len;
2562     }
2563
2564     return 0;
2565 }
2566 int from_backend_untrusted(void *frontend_handle, const char *data, int len)
2567 {
2568     /*
2569      * No "untrusted" output should get here (the way the code is
2570      * currently, it's all diverted by FLAG_STDERR).
2571      */
2572     assert(!"Unexpected call to from_backend_untrusted()");
2573     return 0; /* not reached */
2574 }
2575 int from_backend_eof(void *frontend)
2576 {
2577     /*
2578      * We expect to be the party deciding when to close the
2579      * connection, so if we see EOF before we sent it ourselves, we
2580      * should panic.
2581      */
2582     if (!sent_eof) {
2583         connection_fatal(frontend,
2584                          "Received unexpected end-of-file from SFTP server");
2585     }
2586     return FALSE;
2587 }
2588 int sftp_recvdata(char *buf, int len)
2589 {
2590     outptr = (unsigned char *) buf;
2591     outlen = len;
2592
2593     /*
2594      * See if the pending-input block contains some of what we
2595      * need.
2596      */
2597     if (pendlen > 0) {
2598         unsigned pendused = pendlen;
2599         if (pendused > outlen)
2600             pendused = outlen;
2601         memcpy(outptr, pending, pendused);
2602         memmove(pending, pending + pendused, pendlen - pendused);
2603         outptr += pendused;
2604         outlen -= pendused;
2605         pendlen -= pendused;
2606         if (pendlen == 0) {
2607             pendsize = 0;
2608             sfree(pending);
2609             pending = NULL;
2610         }
2611         if (outlen == 0)
2612             return 1;
2613     }
2614
2615     while (outlen > 0) {
2616         if (back->exitcode(backhandle) >= 0 || ssh_sftp_loop_iteration() < 0)
2617             return 0;                  /* doom */
2618     }
2619
2620     return 1;
2621 }
2622 int sftp_senddata(char *buf, int len)
2623 {
2624     back->send(backhandle, buf, len);
2625     return 1;
2626 }
2627
2628 /*
2629  *  Short description of parameters.
2630  */
2631 static void usage(void)
2632 {
2633     printf("PuTTY Secure File Transfer (SFTP) client\n");
2634     printf("%s\n", ver);
2635     printf("Usage: psftp [options] [user@]host\n");
2636     printf("Options:\n");
2637     printf("  -V        print version information and exit\n");
2638     printf("  -pgpfp    print PGP key fingerprints and exit\n");
2639     printf("  -b file   use specified batchfile\n");
2640     printf("  -bc       output batchfile commands\n");
2641     printf("  -be       don't stop batchfile processing if errors\n");
2642     printf("  -v        show verbose messages\n");
2643     printf("  -load sessname  Load settings from saved session\n");
2644     printf("  -l user   connect with specified username\n");
2645     printf("  -P port   connect to specified port\n");
2646     printf("  -pw passw login with specified password\n");
2647     printf("  -1 -2     force use of particular SSH protocol version\n");
2648     printf("  -4 -6     force use of IPv4 or IPv6\n");
2649     printf("  -C        enable compression\n");
2650     printf("  -i key    private key file for authentication\n");
2651     printf("  -noagent  disable use of Pageant\n");
2652     printf("  -agent    enable use of Pageant\n");
2653     printf("  -batch    disable all interactive prompts\n");
2654     cleanup_exit(1);
2655 }
2656
2657 static void version(void)
2658 {
2659   printf("psftp: %s\n", ver);
2660   cleanup_exit(1);
2661 }
2662
2663 /*
2664  * Connect to a host.
2665  */
2666 static int psftp_connect(char *userhost, char *user, int portnumber)
2667 {
2668     char *host, *realhost;
2669     const char *err;
2670     void *logctx;
2671
2672     /* Separate host and username */
2673     host = userhost;
2674     host = strrchr(host, '@');
2675     if (host == NULL) {
2676         host = userhost;
2677     } else {
2678         *host++ = '\0';
2679         if (user) {
2680             printf("psftp: multiple usernames specified; using \"%s\"\n",
2681                    user);
2682         } else
2683             user = userhost;
2684     }
2685
2686     /*
2687      * If we haven't loaded session details already (e.g., from -load),
2688      * try looking for a session called "host".
2689      */
2690     if (!loaded_session) {
2691         /* Try to load settings for `host' into a temporary config */
2692         Conf *conf2 = conf_new();
2693         conf_set_str(conf2, CONF_host, "");
2694         do_defaults(host, conf2);
2695         if (conf_get_str(conf2, CONF_host)[0] != '\0') {
2696             /* Settings present and include hostname */
2697             /* Re-load data into the real config. */
2698             do_defaults(host, conf);
2699         } else {
2700             /* Session doesn't exist or mention a hostname. */
2701             /* Use `host' as a bare hostname. */
2702             conf_set_str(conf, CONF_host, host);
2703         }
2704     } else {
2705         /* Patch in hostname `host' to session details. */
2706         conf_set_str(conf, CONF_host, host);
2707     }
2708
2709     /*
2710      * Force use of SSH. (If they got the protocol wrong we assume the
2711      * port is useless too.)
2712      */
2713     if (conf_get_int(conf, CONF_protocol) != PROT_SSH) {
2714         conf_set_int(conf, CONF_protocol, PROT_SSH);
2715         conf_set_int(conf, CONF_port, 22);
2716     }
2717
2718     /*
2719      * If saved session / Default Settings says SSH-1 (`1 only' or `1'),
2720      * then change it to SSH-2, on the grounds that that's more likely to
2721      * work for SFTP. (Can be overridden with `-1' option.)
2722      * But if it says `2 only' or `2', respect which.
2723      */
2724     if ((conf_get_int(conf, CONF_sshprot) & ~1) != 2)   /* is it 2 or 3? */
2725         conf_set_int(conf, CONF_sshprot, 2);
2726
2727     /*
2728      * Enact command-line overrides.
2729      */
2730     cmdline_run_saved(conf);
2731
2732     /*
2733      * Muck about with the hostname in various ways.
2734      */
2735     {
2736         char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
2737         char *host = hostbuf;
2738         char *p, *q;
2739
2740         /*
2741          * Trim leading whitespace.
2742          */
2743         host += strspn(host, " \t");
2744
2745         /*
2746          * See if host is of the form user@host, and separate out
2747          * the username if so.
2748          */
2749         if (host[0] != '\0') {
2750             char *atsign = strrchr(host, '@');
2751             if (atsign) {
2752                 *atsign = '\0';
2753                 conf_set_str(conf, CONF_username, host);
2754                 host = atsign + 1;
2755             }
2756         }
2757
2758         /*
2759          * Remove any remaining whitespace.
2760          */
2761         p = hostbuf;
2762         q = host;
2763         while (*q) {
2764             if (*q != ' ' && *q != '\t')
2765                 *p++ = *q;
2766             q++;
2767         }
2768         *p = '\0';
2769
2770         conf_set_str(conf, CONF_host, hostbuf);
2771         sfree(hostbuf);
2772     }
2773
2774     /* Set username */
2775     if (user != NULL && user[0] != '\0') {
2776         conf_set_str(conf, CONF_username, user);
2777     }
2778
2779     if (portnumber)
2780         conf_set_int(conf, CONF_port, portnumber);
2781
2782     /*
2783      * Disable scary things which shouldn't be enabled for simple
2784      * things like SCP and SFTP: agent forwarding, port forwarding,
2785      * X forwarding.
2786      */
2787     conf_set_int(conf, CONF_x11_forward, 0);
2788     conf_set_int(conf, CONF_agentfwd, 0);
2789     conf_set_int(conf, CONF_ssh_simple, TRUE);
2790     {
2791         char *key;
2792         while ((key = conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) != NULL)
2793             conf_del_str_str(conf, CONF_portfwd, key);
2794     }
2795
2796     /* Set up subsystem name. */
2797     conf_set_str(conf, CONF_remote_cmd, "sftp");
2798     conf_set_int(conf, CONF_ssh_subsys, TRUE);
2799     conf_set_int(conf, CONF_nopty, TRUE);
2800
2801     /*
2802      * Set up fallback option, for SSH-1 servers or servers with the
2803      * sftp subsystem not enabled but the server binary installed
2804      * in the usual place. We only support fallback on Unix
2805      * systems, and we use a kludgy piece of shellery which should
2806      * try to find sftp-server in various places (the obvious
2807      * systemwide spots /usr/lib and /usr/local/lib, and then the
2808      * user's PATH) and finally give up.
2809      * 
2810      *   test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server
2811      *   test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server
2812      *   exec sftp-server
2813      * 
2814      * the idea being that this will attempt to use either of the
2815      * obvious pathnames and then give up, and when it does give up
2816      * it will print the preferred pathname in the error messages.
2817      */
2818     conf_set_str(conf, CONF_remote_cmd2,
2819                  "test -x /usr/lib/sftp-server &&"
2820                  " exec /usr/lib/sftp-server\n"
2821                  "test -x /usr/local/lib/sftp-server &&"
2822                  " exec /usr/local/lib/sftp-server\n"
2823                  "exec sftp-server");
2824     conf_set_int(conf, CONF_ssh_subsys2, FALSE);
2825
2826     back = &ssh_backend;
2827
2828     err = back->init(NULL, &backhandle, conf,
2829                      conf_get_str(conf, CONF_host),
2830                      conf_get_int(conf, CONF_port),
2831                      &realhost, 0,
2832                      conf_get_int(conf, CONF_tcp_keepalives));
2833     if (err != NULL) {
2834         fprintf(stderr, "ssh_init: %s\n", err);
2835         return 1;
2836     }
2837     logctx = log_init(NULL, conf);
2838     back->provide_logctx(backhandle, logctx);
2839     console_provide_logctx(logctx);
2840     while (!back->sendok(backhandle)) {
2841         if (back->exitcode(backhandle) >= 0)
2842             return 1;
2843         if (ssh_sftp_loop_iteration() < 0) {
2844             fprintf(stderr, "ssh_init: error during SSH connection setup\n");
2845             return 1;
2846         }
2847     }
2848     if (verbose && realhost != NULL)
2849         printf("Connected to %s\n", realhost);
2850     if (realhost != NULL)
2851         sfree(realhost);
2852     return 0;
2853 }
2854
2855 void cmdline_error(char *p, ...)
2856 {
2857     va_list ap;
2858     fprintf(stderr, "psftp: ");
2859     va_start(ap, p);
2860     vfprintf(stderr, p, ap);
2861     va_end(ap);
2862     fprintf(stderr, "\n       try typing \"psftp -h\" for help\n");
2863     exit(1);
2864 }
2865
2866 /*
2867  * Main program. Parse arguments etc.
2868  */
2869 int psftp_main(int argc, char *argv[])
2870 {
2871     int i;
2872     int portnumber = 0;
2873     char *userhost, *user;
2874     int mode = 0;
2875     int modeflags = 0;
2876     char *batchfile = NULL;
2877
2878     flags = FLAG_STDERR | FLAG_INTERACTIVE
2879 #ifdef FLAG_SYNCAGENT
2880         | FLAG_SYNCAGENT
2881 #endif
2882         ;
2883     cmdline_tooltype = TOOLTYPE_FILETRANSFER;
2884     sk_init();
2885
2886     userhost = user = NULL;
2887
2888     /* Load Default Settings before doing anything else. */
2889     conf = conf_new();
2890     do_defaults(NULL, conf);
2891     loaded_session = FALSE;
2892
2893     for (i = 1; i < argc; i++) {
2894         int ret;
2895         if (argv[i][0] != '-') {
2896             if (userhost)
2897                 usage();
2898             else
2899                 userhost = dupstr(argv[i]);
2900             continue;
2901         }
2902         ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, conf);
2903         if (ret == -2) {
2904             cmdline_error("option \"%s\" requires an argument", argv[i]);
2905         } else if (ret == 2) {
2906             i++;               /* skip next argument */
2907         } else if (ret == 1) {
2908             /* We have our own verbosity in addition to `flags'. */
2909             if (flags & FLAG_VERBOSE)
2910                 verbose = 1;
2911         } else if (strcmp(argv[i], "-h") == 0 ||
2912                    strcmp(argv[i], "-?") == 0 ||
2913                    strcmp(argv[i], "--help") == 0) {
2914             usage();
2915         } else if (strcmp(argv[i], "-pgpfp") == 0) {
2916             pgp_fingerprints();
2917             return 1;
2918         } else if (strcmp(argv[i], "-V") == 0 ||
2919                    strcmp(argv[i], "--version") == 0) {
2920             version();
2921         } else if (strcmp(argv[i], "-batch") == 0) {
2922             console_batch_mode = 1;
2923         } else if (strcmp(argv[i], "-b") == 0 && i + 1 < argc) {
2924             mode = 1;
2925             batchfile = argv[++i];
2926         } else if (strcmp(argv[i], "-bc") == 0) {
2927             modeflags = modeflags | 1;
2928         } else if (strcmp(argv[i], "-be") == 0) {
2929             modeflags = modeflags | 2;
2930         } else if (strcmp(argv[i], "--") == 0) {
2931             i++;
2932             break;
2933         } else {
2934             cmdline_error("unknown option \"%s\"", argv[i]);
2935         }
2936     }
2937     argc -= i;
2938     argv += i;
2939     back = NULL;
2940
2941     /*
2942      * If the loaded session provides a hostname, and a hostname has not
2943      * otherwise been specified, pop it in `userhost' so that
2944      * `psftp -load sessname' is sufficient to start a session.
2945      */
2946     if (!userhost && conf_get_str(conf, CONF_host)[0] != '\0') {
2947         userhost = dupstr(conf_get_str(conf, CONF_host));
2948     }
2949
2950     /*
2951      * If a user@host string has already been provided, connect to
2952      * it now.
2953      */
2954     if (userhost) {
2955         int ret;
2956         ret = psftp_connect(userhost, user, portnumber);
2957         sfree(userhost);
2958         if (ret)
2959             return 1;
2960         if (do_sftp_init())
2961             return 1;
2962     } else {
2963         printf("psftp: no hostname specified; use \"open host.name\""
2964                " to connect\n");
2965     }
2966
2967     do_sftp(mode, modeflags, batchfile);
2968
2969     if (back != NULL && back->connected(backhandle)) {
2970         char ch;
2971         back->special(backhandle, TS_EOF);
2972         sent_eof = TRUE;
2973         sftp_recvdata(&ch, 1);
2974     }
2975     do_sftp_cleanup();
2976     random_save_seed();
2977     cmdline_cleanup();
2978     console_provide_logctx(NULL);
2979     sk_cleanup();
2980
2981     return 0;
2982 }