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