summaryrefslogtreecommitdiff
path: root/sftp-int.c
diff options
context:
space:
mode:
Diffstat (limited to 'sftp-int.c')
-rw-r--r--sftp-int.c203
1 files changed, 182 insertions, 21 deletions
diff --git a/sftp-int.c b/sftp-int.c
index b13e5da5d..6a2012910 100644
--- a/sftp-int.c
+++ b/sftp-int.c
@@ -22,11 +22,10 @@
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */ 23 */
24 24
25/* XXX: globbed ls */
26/* XXX: recursive operations */ 25/* XXX: recursive operations */
27 26
28#include "includes.h" 27#include "includes.h"
29RCSID("$OpenBSD: sftp-int.c,v 1.47 2002/06/23 09:30:14 deraadt Exp $"); 28RCSID("$OpenBSD: sftp-int.c,v 1.49 2002/09/12 00:13:06 djm Exp $");
30 29
31#include "buffer.h" 30#include "buffer.h"
32#include "xmalloc.h" 31#include "xmalloc.h"
@@ -201,6 +200,25 @@ local_do_ls(const char *args)
201 } 200 }
202} 201}
203 202
203/* Strip one path (usually the pwd) from the start of another */
204static char *
205path_strip(char *path, char *strip)
206{
207 size_t len;
208
209 if (strip == NULL)
210 return (xstrdup(path));
211
212 len = strlen(strip);
213 if (strip != NULL && strncmp(path, strip, len) == 0) {
214 if (strip[len - 1] != '/' && path[len] == '/')
215 len++;
216 return (xstrdup(path + len));
217 }
218
219 return (xstrdup(path));
220}
221
204static char * 222static char *
205path_append(char *p1, char *p2) 223path_append(char *p1, char *p2)
206{ 224{
@@ -209,7 +227,7 @@ path_append(char *p1, char *p2)
209 227
210 ret = xmalloc(len); 228 ret = xmalloc(len);
211 strlcpy(ret, p1, len); 229 strlcpy(ret, p1, len);
212 if (strcmp(p1, "/") != 0) 230 if (p1[strlen(p1) - 1] != '/')
213 strlcat(ret, "/", len); 231 strlcat(ret, "/", len);
214 strlcat(ret, p2, len); 232 strlcat(ret, p2, len);
215 233
@@ -274,6 +292,29 @@ parse_getput_flags(const char **cpp, int *pflag)
274} 292}
275 293
276static int 294static int
295parse_ls_flags(const char **cpp, int *lflag)
296{
297 const char *cp = *cpp;
298
299 /* Check for flags */
300 if (cp++[0] == '-') {
301 for(; strchr(WHITESPACE, *cp) == NULL; cp++) {
302 switch (*cp) {
303 case 'l':
304 *lflag = 1;
305 break;
306 default:
307 error("Invalid flag -%c", *cp);
308 return(-1);
309 }
310 }
311 *cpp = cp + strspn(cp, WHITESPACE);
312 }
313
314 return(0);
315}
316
317static int
277get_pathname(const char **cpp, char **path) 318get_pathname(const char **cpp, char **path)
278{ 319{
279 const char *cp = *cpp, *end; 320 const char *cp = *cpp, *end;
@@ -504,8 +545,129 @@ out:
504} 545}
505 546
506static int 547static int
507parse_args(const char **cpp, int *pflag, unsigned long *n_arg, 548sdirent_comp(const void *aa, const void *bb)
508 char **path1, char **path2) 549{
550 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
551 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
552
553 return (strcmp(a->filename, b->filename));
554}
555
556/* sftp ls.1 replacement for directories */
557static int
558do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
559{
560 int n;
561 SFTP_DIRENT **d;
562
563 if ((n = do_readdir(conn, path, &d)) != 0)
564 return (n);
565
566 /* Count entries for sort */
567 for (n = 0; d[n] != NULL; n++)
568 ;
569
570 qsort(d, n, sizeof(*d), sdirent_comp);
571
572 for (n = 0; d[n] != NULL; n++) {
573 char *tmp, *fname;
574
575 tmp = path_append(path, d[n]->filename);
576 fname = path_strip(tmp, strip_path);
577 xfree(tmp);
578
579 if (lflag) {
580 char *lname;
581 struct stat sb;
582
583 memset(&sb, 0, sizeof(sb));
584 attrib_to_stat(&d[n]->a, &sb);
585 lname = ls_file(fname, &sb, 1);
586 printf("%s\n", lname);
587 xfree(lname);
588 } else {
589 /* XXX - multicolumn display would be nice here */
590 printf("%s\n", fname);
591 }
592
593 xfree(fname);
594 }
595
596 free_sftp_dirents(d);
597 return (0);
598}
599
600/* sftp ls.1 replacement which handles path globs */
601static int
602do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
603 int lflag)
604{
605 glob_t g;
606 int i;
607 Attrib *a;
608 struct stat sb;
609
610 memset(&g, 0, sizeof(g));
611
612 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
613 NULL, &g)) {
614 error("Can't ls: \"%s\" not found", path);
615 return (-1);
616 }
617
618 /*
619 * If the glob returns a single match, which is the same as the
620 * input glob, and it is a directory, then just list its contents
621 */
622 if (g.gl_pathc == 1 &&
623 strncmp(path, g.gl_pathv[0], strlen(g.gl_pathv[0]) - 1) == 0) {
624 if ((a = do_lstat(conn, path, 1)) == NULL) {
625 globfree(&g);
626 return (-1);
627 }
628 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
629 S_ISDIR(a->perm)) {
630 globfree(&g);
631 return (do_ls_dir(conn, path, strip_path, lflag));
632 }
633 }
634
635 for (i = 0; g.gl_pathv[i]; i++) {
636 char *fname, *lname;
637
638 fname = path_strip(g.gl_pathv[i], strip_path);
639
640 if (lflag) {
641 /*
642 * XXX: this is slow - 1 roundtrip per path
643 * A solution to this is to fork glob() and
644 * build a sftp specific version which keeps the
645 * attribs (which currently get thrown away)
646 * that the server returns as well as the filenames.
647 */
648 memset(&sb, 0, sizeof(sb));
649 a = do_lstat(conn, g.gl_pathv[i], 1);
650 if (a != NULL)
651 attrib_to_stat(a, &sb);
652 lname = ls_file(fname, &sb, 1);
653 printf("%s\n", lname);
654 xfree(lname);
655 } else {
656 /* XXX - multicolumn display would be nice here */
657 printf("%s\n", fname);
658 }
659 xfree(fname);
660 }
661
662 if (g.gl_pathc)
663 globfree(&g);
664
665 return (0);
666}
667
668static int
669parse_args(const char **cpp, int *pflag, int *lflag,
670 unsigned long *n_arg, char **path1, char **path2)
509{ 671{
510 const char *cmd, *cp = *cpp; 672 const char *cmd, *cp = *cpp;
511 char *cp2; 673 char *cp2;
@@ -545,7 +707,7 @@ parse_args(const char **cpp, int *pflag, unsigned long *n_arg,
545 } 707 }
546 708
547 /* Get arguments and parse flags */ 709 /* Get arguments and parse flags */
548 *pflag = *n_arg = 0; 710 *lflag = *pflag = *n_arg = 0;
549 *path1 = *path2 = NULL; 711 *path1 = *path2 = NULL;
550 switch (cmdnum) { 712 switch (cmdnum) {
551 case I_GET: 713 case I_GET:
@@ -592,6 +754,8 @@ parse_args(const char **cpp, int *pflag, unsigned long *n_arg,
592 } 754 }
593 break; 755 break;
594 case I_LS: 756 case I_LS:
757 if (parse_ls_flags(&cp, lflag))
758 return(-1);
595 /* Path is optional */ 759 /* Path is optional */
596 if (get_pathname(&cp, path1)) 760 if (get_pathname(&cp, path1))
597 return(-1); 761 return(-1);
@@ -652,7 +816,7 @@ static int
652parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd) 816parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd)
653{ 817{
654 char *path1, *path2, *tmp; 818 char *path1, *path2, *tmp;
655 int pflag, cmdnum, i; 819 int pflag, lflag, cmdnum, i;
656 unsigned long n_arg; 820 unsigned long n_arg;
657 Attrib a, *aa; 821 Attrib a, *aa;
658 char path_buf[MAXPATHLEN]; 822 char path_buf[MAXPATHLEN];
@@ -660,7 +824,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd)
660 glob_t g; 824 glob_t g;
661 825
662 path1 = path2 = NULL; 826 path1 = path2 = NULL;
663 cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2); 827 cmdnum = parse_args(&cmd, &pflag, &lflag, &n_arg,
828 &path1, &path2);
664 829
665 memset(&g, 0, sizeof(g)); 830 memset(&g, 0, sizeof(g));
666 831
@@ -732,22 +897,18 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd)
732 break; 897 break;
733 case I_LS: 898 case I_LS:
734 if (!path1) { 899 if (!path1) {
735 do_ls(conn, *pwd); 900 do_globbed_ls(conn, *pwd, *pwd, lflag);
736 break; 901 break;
737 } 902 }
903
904 /* Strip pwd off beginning of non-absolute paths */
905 tmp = NULL;
906 if (*path1 != '/')
907 tmp = *pwd;
908
738 path1 = make_absolute(path1, *pwd); 909 path1 = make_absolute(path1, *pwd);
739 if ((tmp = do_realpath(conn, path1)) == NULL) 910
740 break; 911 do_globbed_ls(conn, path1, tmp, lflag);
741 xfree(path1);
742 path1 = tmp;
743 if ((aa = do_stat(conn, path1, 0)) == NULL)
744 break;
745 if ((aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
746 !S_ISDIR(aa->perm)) {
747 error("Can't ls: \"%s\" is not a directory", path1);
748 break;
749 }
750 do_ls(conn, path1);
751 break; 912 break;
752 case I_LCHDIR: 913 case I_LCHDIR:
753 if (chdir(path1) == -1) { 914 if (chdir(path1) == -1) {