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