]> andersk Git - openssh.git/blob - sftp-int.c
- (djm) OpenBSD CVS Sync
[openssh.git] / sftp-int.c
1 /*
2  * Copyright (c) 2001,2002 Damien Miller.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 /* XXX: recursive operations */
26
27 #include "includes.h"
28 RCSID("$OpenBSD: sftp-int.c,v 1.67 2004/01/23 17:57:48 mouring Exp $");
29
30 #include "buffer.h"
31 #include "xmalloc.h"
32 #include "log.h"
33 #include "pathnames.h"
34
35 #include "sftp.h"
36 #include "sftp-common.h"
37 #include "sftp-glob.h"
38 #include "sftp-client.h"
39 #include "sftp-int.h"
40
41 /* File to read commands from */
42 extern FILE *infile;
43
44 /* Are we in batchfile mode? */
45 extern int batchmode;
46
47 /* Size of buffer used when copying files */
48 extern size_t copy_buffer_len;
49
50 /* Number of concurrent outstanding requests */
51 extern int num_requests;
52
53 /* This is set to 0 if the progressmeter is not desired. */
54 int showprogress = 1;
55
56 /* Separators for interactive commands */
57 #define WHITESPACE " \t\r\n"
58
59 /* Define what type of ls view (0 - multi-column) */
60 #define LONG_VIEW 1             /* Full view ala ls -l */
61 #define SHORT_VIEW 2            /* Single row view ala ls -1 */
62
63 /* Commands for interactive mode */
64 #define I_CHDIR         1
65 #define I_CHGRP         2
66 #define I_CHMOD         3
67 #define I_CHOWN         4
68 #define I_GET           5
69 #define I_HELP          6
70 #define I_LCHDIR        7
71 #define I_LLS           8
72 #define I_LMKDIR        9
73 #define I_LPWD          10
74 #define I_LS            11
75 #define I_LUMASK        12
76 #define I_MKDIR         13
77 #define I_PUT           14
78 #define I_PWD           15
79 #define I_QUIT          16
80 #define I_RENAME        17
81 #define I_RM            18
82 #define I_RMDIR         19
83 #define I_SHELL         20
84 #define I_SYMLINK       21
85 #define I_VERSION       22
86 #define I_PROGRESS      23
87
88 struct CMD {
89         const char *c;
90         const int n;
91 };
92
93 static const struct CMD cmds[] = {
94         { "bye",        I_QUIT },
95         { "cd",         I_CHDIR },
96         { "chdir",      I_CHDIR },
97         { "chgrp",      I_CHGRP },
98         { "chmod",      I_CHMOD },
99         { "chown",      I_CHOWN },
100         { "dir",        I_LS },
101         { "exit",       I_QUIT },
102         { "get",        I_GET },
103         { "mget",       I_GET },
104         { "help",       I_HELP },
105         { "lcd",        I_LCHDIR },
106         { "lchdir",     I_LCHDIR },
107         { "lls",        I_LLS },
108         { "lmkdir",     I_LMKDIR },
109         { "ln",         I_SYMLINK },
110         { "lpwd",       I_LPWD },
111         { "ls",         I_LS },
112         { "lumask",     I_LUMASK },
113         { "mkdir",      I_MKDIR },
114         { "progress",   I_PROGRESS },
115         { "put",        I_PUT },
116         { "mput",       I_PUT },
117         { "pwd",        I_PWD },
118         { "quit",       I_QUIT },
119         { "rename",     I_RENAME },
120         { "rm",         I_RM },
121         { "rmdir",      I_RMDIR },
122         { "symlink",    I_SYMLINK },
123         { "version",    I_VERSION },
124         { "!",          I_SHELL },
125         { "?",          I_HELP },
126         { NULL,                 -1}
127 };
128
129 static void
130 help(void)
131 {
132         printf("Available commands:\n");
133         printf("cd path                       Change remote directory to 'path'\n");
134         printf("lcd path                      Change local directory to 'path'\n");
135         printf("chgrp grp path                Change group of file 'path' to 'grp'\n");
136         printf("chmod mode path               Change permissions of file 'path' to 'mode'\n");
137         printf("chown own path                Change owner of file 'path' to 'own'\n");
138         printf("help                          Display this help text\n");
139         printf("get remote-path [local-path]  Download file\n");
140         printf("lls [ls-options [path]]       Display local directory listing\n");
141         printf("ln oldpath newpath            Symlink remote file\n");
142         printf("lmkdir path                   Create local directory\n");
143         printf("lpwd                          Print local working directory\n");
144         printf("ls [path]                     Display remote directory listing\n");
145         printf("lumask umask                  Set local umask to 'umask'\n");
146         printf("mkdir path                    Create remote directory\n");
147         printf("progress                      Toggle display of progress meter\n");
148         printf("put local-path [remote-path]  Upload file\n");
149         printf("pwd                           Display remote working directory\n");
150         printf("exit                          Quit sftp\n");
151         printf("quit                          Quit sftp\n");
152         printf("rename oldpath newpath        Rename remote file\n");
153         printf("rmdir path                    Remove remote directory\n");
154         printf("rm path                       Delete remote file\n");
155         printf("symlink oldpath newpath       Symlink remote file\n");
156         printf("version                       Show SFTP version\n");
157         printf("!command                      Execute 'command' in local shell\n");
158         printf("!                             Escape to local shell\n");
159         printf("?                             Synonym for help\n");
160 }
161
162 static void
163 local_do_shell(const char *args)
164 {
165         int status;
166         char *shell;
167         pid_t pid;
168
169         if (!*args)
170                 args = NULL;
171
172         if ((shell = getenv("SHELL")) == NULL)
173                 shell = _PATH_BSHELL;
174
175         if ((pid = fork()) == -1)
176                 fatal("Couldn't fork: %s", strerror(errno));
177
178         if (pid == 0) {
179                 /* XXX: child has pipe fds to ssh subproc open - issue? */
180                 if (args) {
181                         debug3("Executing %s -c \"%s\"", shell, args);
182                         execl(shell, shell, "-c", args, (char *)NULL);
183                 } else {
184                         debug3("Executing %s", shell);
185                         execl(shell, shell, (char *)NULL);
186                 }
187                 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
188                     strerror(errno));
189                 _exit(1);
190         }
191         while (waitpid(pid, &status, 0) == -1)
192                 if (errno != EINTR)
193                         fatal("Couldn't wait for child: %s", strerror(errno));
194         if (!WIFEXITED(status))
195                 error("Shell exited abormally");
196         else if (WEXITSTATUS(status))
197                 error("Shell exited with status %d", WEXITSTATUS(status));
198 }
199
200 static void
201 local_do_ls(const char *args)
202 {
203         if (!args || !*args)
204                 local_do_shell(_PATH_LS);
205         else {
206                 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
207                 char *buf = xmalloc(len);
208
209                 /* XXX: quoting - rip quoting code from ftp? */
210                 snprintf(buf, len, _PATH_LS " %s", args);
211                 local_do_shell(buf);
212                 xfree(buf);
213         }
214 }
215
216 /* Strip one path (usually the pwd) from the start of another */
217 static char *
218 path_strip(char *path, char *strip)
219 {
220         size_t len;
221
222         if (strip == NULL)
223                 return (xstrdup(path));
224
225         len = strlen(strip);
226         if (strip != NULL && strncmp(path, strip, len) == 0) {
227                 if (strip[len - 1] != '/' && path[len] == '/')
228                         len++;
229                 return (xstrdup(path + len));
230         }
231
232         return (xstrdup(path));
233 }
234
235 static char *
236 path_append(char *p1, char *p2)
237 {
238         char *ret;
239         int len = strlen(p1) + strlen(p2) + 2;
240
241         ret = xmalloc(len);
242         strlcpy(ret, p1, len);
243         if (p1[strlen(p1) - 1] != '/')
244                 strlcat(ret, "/", len);
245         strlcat(ret, p2, len);
246
247         return(ret);
248 }
249
250 static char *
251 make_absolute(char *p, char *pwd)
252 {
253         char *abs;
254
255         /* Derelativise */
256         if (p && p[0] != '/') {
257                 abs = path_append(pwd, p);
258                 xfree(p);
259                 return(abs);
260         } else
261                 return(p);
262 }
263
264 static int
265 infer_path(const char *p, char **ifp)
266 {
267         char *cp;
268
269         cp = strrchr(p, '/');
270         if (cp == NULL) {
271                 *ifp = xstrdup(p);
272                 return(0);
273         }
274
275         if (!cp[1]) {
276                 error("Invalid path");
277                 return(-1);
278         }
279
280         *ifp = xstrdup(cp + 1);
281         return(0);
282 }
283
284 static int
285 parse_getput_flags(const char **cpp, int *pflag)
286 {
287         const char *cp = *cpp;
288
289         /* Check for flags */
290         if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
291                 switch (cp[1]) {
292                 case 'p':
293                 case 'P':
294                         *pflag = 1;
295                         break;
296                 default:
297                         error("Invalid flag -%c", cp[1]);
298                         return(-1);
299                 }
300                 cp += 2;
301                 *cpp = cp + strspn(cp, WHITESPACE);
302         }
303
304         return(0);
305 }
306
307 static int
308 parse_ls_flags(const char **cpp, int *lflag)
309 {
310         const char *cp = *cpp;
311
312         /* Check for flags */
313         if (cp++[0] == '-') {
314                 for(; strchr(WHITESPACE, *cp) == NULL; cp++) {
315                         switch (*cp) {
316                         case 'l':
317                                 *lflag = LONG_VIEW;
318                                 break;
319                         case '1':
320                                 *lflag = SHORT_VIEW;
321                                 break;
322                         default:
323                                 error("Invalid flag -%c", *cp);
324                                 return(-1);
325                         }
326                 }
327                 *cpp = cp + strspn(cp, WHITESPACE);
328         }
329
330         return(0);
331 }
332
333 static int
334 get_pathname(const char **cpp, char **path)
335 {
336         const char *cp = *cpp, *end;
337         char quot;
338         int i, j;
339
340         cp += strspn(cp, WHITESPACE);
341         if (!*cp) {
342                 *cpp = cp;
343                 *path = NULL;
344                 return (0);
345         }
346
347         *path = xmalloc(strlen(cp) + 1);
348
349         /* Check for quoted filenames */
350         if (*cp == '\"' || *cp == '\'') {
351                 quot = *cp++;
352
353                 /* Search for terminating quote, unescape some chars */
354                 for (i = j = 0; i <= strlen(cp); i++) {
355                         if (cp[i] == quot) {    /* Found quote */
356                                 i++;
357                                 (*path)[j] = '\0';
358                                 break;
359                         }
360                         if (cp[i] == '\0') {    /* End of string */
361                                 error("Unterminated quote");
362                                 goto fail;
363                         }
364                         if (cp[i] == '\\') {    /* Escaped characters */
365                                 i++;
366                                 if (cp[i] != '\'' && cp[i] != '\"' &&
367                                     cp[i] != '\\') {
368                                         error("Bad escaped character '\%c'",
369                                             cp[i]);
370                                         goto fail;
371                                 }
372                         }
373                         (*path)[j++] = cp[i];
374                 }
375
376                 if (j == 0) {
377                         error("Empty quotes");
378                         goto fail;
379                 }
380                 *cpp = cp + i + strspn(cp + i, WHITESPACE);
381         } else {
382                 /* Read to end of filename */
383                 end = strpbrk(cp, WHITESPACE);
384                 if (end == NULL)
385                         end = strchr(cp, '\0');
386                 *cpp = end + strspn(end, WHITESPACE);
387
388                 memcpy(*path, cp, end - cp);
389                 (*path)[end - cp] = '\0';
390         }
391         return (0);
392
393  fail:
394         xfree(*path);
395         *path = NULL;
396         return (-1);
397 }
398
399 static int
400 is_dir(char *path)
401 {
402         struct stat sb;
403
404         /* XXX: report errors? */
405         if (stat(path, &sb) == -1)
406                 return(0);
407
408         return(sb.st_mode & S_IFDIR);
409 }
410
411 static int
412 is_reg(char *path)
413 {
414         struct stat sb;
415
416         if (stat(path, &sb) == -1)
417                 fatal("stat %s: %s", path, strerror(errno));
418
419         return(S_ISREG(sb.st_mode));
420 }
421
422 static int
423 remote_is_dir(struct sftp_conn *conn, char *path)
424 {
425         Attrib *a;
426
427         /* XXX: report errors? */
428         if ((a = do_stat(conn, path, 1)) == NULL)
429                 return(0);
430         if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
431                 return(0);
432         return(a->perm & S_IFDIR);
433 }
434
435 static int
436 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
437 {
438         char *abs_src = NULL;
439         char *abs_dst = NULL;
440         char *tmp;
441         glob_t g;
442         int err = 0;
443         int i;
444
445         abs_src = xstrdup(src);
446         abs_src = make_absolute(abs_src, pwd);
447
448         memset(&g, 0, sizeof(g));
449         debug3("Looking up %s", abs_src);
450         if (remote_glob(conn, abs_src, 0, NULL, &g)) {
451                 error("File \"%s\" not found.", abs_src);
452                 err = -1;
453                 goto out;
454         }
455
456         /* If multiple matches, dst must be a directory or unspecified */
457         if (g.gl_matchc > 1 && dst && !is_dir(dst)) {
458                 error("Multiple files match, but \"%s\" is not a directory",
459                     dst);
460                 err = -1;
461                 goto out;
462         }
463
464         for (i = 0; g.gl_pathv[i]; i++) {
465                 if (infer_path(g.gl_pathv[i], &tmp)) {
466                         err = -1;
467                         goto out;
468                 }
469
470                 if (g.gl_matchc == 1 && dst) {
471                         /* If directory specified, append filename */
472                         if (is_dir(dst)) {
473                                 if (infer_path(g.gl_pathv[0], &tmp)) {
474                                         err = 1;
475                                         goto out;
476                                 }
477                                 abs_dst = path_append(dst, tmp);
478                                 xfree(tmp);
479                         } else
480                                 abs_dst = xstrdup(dst);
481                 } else if (dst) {
482                         abs_dst = path_append(dst, tmp);
483                         xfree(tmp);
484                 } else
485                         abs_dst = tmp;
486
487                 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
488                 if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
489                         err = -1;
490                 xfree(abs_dst);
491                 abs_dst = NULL;
492         }
493
494 out:
495         xfree(abs_src);
496         if (abs_dst)
497                 xfree(abs_dst);
498         globfree(&g);
499         return(err);
500 }
501
502 static int
503 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
504 {
505         char *tmp_dst = NULL;
506         char *abs_dst = NULL;
507         char *tmp;
508         glob_t g;
509         int err = 0;
510         int i;
511
512         if (dst) {
513                 tmp_dst = xstrdup(dst);
514                 tmp_dst = make_absolute(tmp_dst, pwd);
515         }
516
517         memset(&g, 0, sizeof(g));
518         debug3("Looking up %s", src);
519         if (glob(src, 0, NULL, &g)) {
520                 error("File \"%s\" not found.", src);
521                 err = -1;
522                 goto out;
523         }
524
525         /* If multiple matches, dst may be directory or unspecified */
526         if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) {
527                 error("Multiple files match, but \"%s\" is not a directory",
528                     tmp_dst);
529                 err = -1;
530                 goto out;
531         }
532
533         for (i = 0; g.gl_pathv[i]; i++) {
534                 if (!is_reg(g.gl_pathv[i])) {
535                         error("skipping non-regular file %s",
536                             g.gl_pathv[i]);
537                         continue;
538                 }
539                 if (infer_path(g.gl_pathv[i], &tmp)) {
540                         err = -1;
541                         goto out;
542                 }
543
544                 if (g.gl_matchc == 1 && tmp_dst) {
545                         /* If directory specified, append filename */
546                         if (remote_is_dir(conn, tmp_dst)) {
547                                 if (infer_path(g.gl_pathv[0], &tmp)) {
548                                         err = 1;
549                                         goto out;
550                                 }
551                                 abs_dst = path_append(tmp_dst, tmp);
552                                 xfree(tmp);
553                         } else
554                                 abs_dst = xstrdup(tmp_dst);
555
556                 } else if (tmp_dst) {
557                         abs_dst = path_append(tmp_dst, tmp);
558                         xfree(tmp);
559                 } else
560                         abs_dst = make_absolute(tmp, pwd);
561
562                 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
563                 if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
564                         err = -1;
565         }
566
567 out:
568         if (abs_dst)
569                 xfree(abs_dst);
570         if (tmp_dst)
571                 xfree(tmp_dst);
572         globfree(&g);
573         return(err);
574 }
575
576 static int
577 sdirent_comp(const void *aa, const void *bb)
578 {
579         SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
580         SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
581
582         return (strcmp(a->filename, b->filename));
583 }
584
585 /* sftp ls.1 replacement for directories */
586 static int
587 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
588 {
589         int n, c = 1, colspace = 0, columns = 1;
590         SFTP_DIRENT **d;
591
592         if ((n = do_readdir(conn, path, &d)) != 0)
593                 return (n);
594
595         if (!(lflag & SHORT_VIEW)) {
596                 int m = 0, width = 80;
597                 struct winsize ws;
598                 char *tmp;
599
600                 /* Count entries for sort and find longest filename */
601                 for (n = 0; d[n] != NULL; n++)
602                         m = MAX(m, strlen(d[n]->filename));
603
604                 /* Add any subpath that also needs to be counted */
605                 tmp = path_strip(path, strip_path);
606                 m += strlen(tmp);
607                 xfree(tmp);
608
609                 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
610                         width = ws.ws_col;
611
612                 columns = width / (m + 2);
613                 columns = MAX(columns, 1);
614                 colspace = width / columns;
615                 colspace = MIN(colspace, width);
616         }
617
618         qsort(d, n, sizeof(*d), sdirent_comp);
619
620         for (n = 0; d[n] != NULL; n++) {
621                 char *tmp, *fname;
622
623                 tmp = path_append(path, d[n]->filename);
624                 fname = path_strip(tmp, strip_path);
625                 xfree(tmp);
626
627                 if (lflag & LONG_VIEW) {
628                         char *lname;
629                         struct stat sb;
630
631                         memset(&sb, 0, sizeof(sb));
632                         attrib_to_stat(&d[n]->a, &sb);
633                         lname = ls_file(fname, &sb, 1);
634                         printf("%s\n", lname);
635                         xfree(lname);
636                 } else {
637                         printf("%-*s", colspace, fname);
638                         if (c >= columns) {
639                                 printf("\n");
640                                 c = 1;
641                         } else
642                                 c++;
643                 }
644
645                 xfree(fname);
646         }
647
648         if (!(lflag & LONG_VIEW) && (c != 1))
649                 printf("\n");
650
651         free_sftp_dirents(d);
652         return (0);
653 }
654
655 /* sftp ls.1 replacement which handles path globs */
656 static int
657 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
658     int lflag)
659 {
660         glob_t g;
661         int i, c = 1, colspace = 0, columns = 1;
662         Attrib *a;
663
664         memset(&g, 0, sizeof(g));
665
666         if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
667             NULL, &g)) {
668                 error("Can't ls: \"%s\" not found", path);
669                 return (-1);
670         }
671
672         /*
673          * If the glob returns a single match, which is the same as the
674          * input glob, and it is a directory, then just list its contents
675          */
676         if (g.gl_pathc == 1 &&
677             strncmp(path, g.gl_pathv[0], strlen(g.gl_pathv[0]) - 1) == 0) {
678                 if ((a = do_lstat(conn, path, 1)) == NULL) {
679                         globfree(&g);
680                         return (-1);
681                 }
682                 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
683                     S_ISDIR(a->perm)) {
684                         globfree(&g);
685                         return (do_ls_dir(conn, path, strip_path, lflag));
686                 }
687         }
688
689         if (!(lflag & SHORT_VIEW)) {
690                 int m = 0, width = 80;
691                 struct winsize ws;
692
693                 /* Count entries for sort and find longest filename */
694                 for (i = 0; g.gl_pathv[i]; i++)
695                         m = MAX(m, strlen(g.gl_pathv[i]));
696
697                 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
698                         width = ws.ws_col;
699
700                 columns = width / (m + 2);
701                 columns = MAX(columns, 1);
702                 colspace = width / columns;
703         }
704
705         for (i = 0; g.gl_pathv[i]; i++) {
706                 char *fname;
707
708                 fname = path_strip(g.gl_pathv[i], strip_path);
709
710                 if (lflag & LONG_VIEW) {
711                         char *lname;
712                         struct stat sb;
713
714                         /*
715                          * XXX: this is slow - 1 roundtrip per path
716                          * A solution to this is to fork glob() and
717                          * build a sftp specific version which keeps the
718                          * attribs (which currently get thrown away)
719                          * that the server returns as well as the filenames.
720                          */
721                         memset(&sb, 0, sizeof(sb));
722                         a = do_lstat(conn, g.gl_pathv[i], 1);
723                         if (a != NULL)
724                                 attrib_to_stat(a, &sb);
725                         lname = ls_file(fname, &sb, 1);
726                         printf("%s\n", lname);
727                         xfree(lname);
728                 } else {
729                         printf("%-*s", colspace, fname);
730                         if (c >= columns) {
731                                 printf("\n");
732                                 c = 1;
733                         } else
734                                 c++;
735                 }
736                 xfree(fname);
737         }
738
739         if (!(lflag & LONG_VIEW) && (c != 1))
740                 printf("\n");
741
742         if (g.gl_pathc)
743                 globfree(&g);
744
745         return (0);
746 }
747
748 static int
749 parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,
750     unsigned long *n_arg, char **path1, char **path2)
751 {
752         const char *cmd, *cp = *cpp;
753         char *cp2;
754         int base = 0;
755         long l;
756         int i, cmdnum;
757
758         /* Skip leading whitespace */
759         cp = cp + strspn(cp, WHITESPACE);
760
761         /* Ignore blank lines and lines which begin with comment '#' char */
762         if (*cp == '\0' || *cp == '#')
763                 return (0);
764
765         /* Check for leading '-' (disable error processing) */
766         *iflag = 0;
767         if (*cp == '-') {
768                 *iflag = 1;
769                 cp++;
770         }
771
772         /* Figure out which command we have */
773         for (i = 0; cmds[i].c; i++) {
774                 int cmdlen = strlen(cmds[i].c);
775
776                 /* Check for command followed by whitespace */
777                 if (!strncasecmp(cp, cmds[i].c, cmdlen) &&
778                     strchr(WHITESPACE, cp[cmdlen])) {
779                         cp += cmdlen;
780                         cp = cp + strspn(cp, WHITESPACE);
781                         break;
782                 }
783         }
784         cmdnum = cmds[i].n;
785         cmd = cmds[i].c;
786
787         /* Special case */
788         if (*cp == '!') {
789                 cp++;
790                 cmdnum = I_SHELL;
791         } else if (cmdnum == -1) {
792                 error("Invalid command.");
793                 return (-1);
794         }
795
796         /* Get arguments and parse flags */
797         *lflag = *pflag = *n_arg = 0;
798         *path1 = *path2 = NULL;
799         switch (cmdnum) {
800         case I_GET:
801         case I_PUT:
802                 if (parse_getput_flags(&cp, pflag))
803                         return(-1);
804                 /* Get first pathname (mandatory) */
805                 if (get_pathname(&cp, path1))
806                         return(-1);
807                 if (*path1 == NULL) {
808                         error("You must specify at least one path after a "
809                             "%s command.", cmd);
810                         return(-1);
811                 }
812                 /* Try to get second pathname (optional) */
813                 if (get_pathname(&cp, path2))
814                         return(-1);
815                 break;
816         case I_RENAME:
817         case I_SYMLINK:
818                 if (get_pathname(&cp, path1))
819                         return(-1);
820                 if (get_pathname(&cp, path2))
821                         return(-1);
822                 if (!*path1 || !*path2) {
823                         error("You must specify two paths after a %s "
824                             "command.", cmd);
825                         return(-1);
826                 }
827                 break;
828         case I_RM:
829         case I_MKDIR:
830         case I_RMDIR:
831         case I_CHDIR:
832         case I_LCHDIR:
833         case I_LMKDIR:
834                 /* Get pathname (mandatory) */
835                 if (get_pathname(&cp, path1))
836                         return(-1);
837                 if (*path1 == NULL) {
838                         error("You must specify a path after a %s command.",
839                             cmd);
840                         return(-1);
841                 }
842                 break;
843         case I_LS:
844                 if (parse_ls_flags(&cp, lflag))
845                         return(-1);
846                 /* Path is optional */
847                 if (get_pathname(&cp, path1))
848                         return(-1);
849                 break;
850         case I_LLS:
851         case I_SHELL:
852                 /* Uses the rest of the line */
853                 break;
854         case I_LUMASK:
855                 base = 8;
856         case I_CHMOD:
857                 base = 8;
858         case I_CHOWN:
859         case I_CHGRP:
860                 /* Get numeric arg (mandatory) */
861                 l = strtol(cp, &cp2, base);
862                 if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) &&
863                     errno == ERANGE) || l < 0) {
864                         error("You must supply a numeric argument "
865                             "to the %s command.", cmd);
866                         return(-1);
867                 }
868                 cp = cp2;
869                 *n_arg = l;
870                 if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp))
871                         break;
872                 if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {
873                         error("You must supply a numeric argument "
874                             "to the %s command.", cmd);
875                         return(-1);
876                 }
877                 cp += strspn(cp, WHITESPACE);
878
879                 /* Get pathname (mandatory) */
880                 if (get_pathname(&cp, path1))
881                         return(-1);
882                 if (*path1 == NULL) {
883                         error("You must specify a path after a %s command.",
884                             cmd);
885                         return(-1);
886                 }
887                 break;
888         case I_QUIT:
889         case I_PWD:
890         case I_LPWD:
891         case I_HELP:
892         case I_VERSION:
893         case I_PROGRESS:
894                 break;
895         default:
896                 fatal("Command not implemented");
897         }
898
899         *cpp = cp;
900         return(cmdnum);
901 }
902
903 static int
904 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
905     int err_abort)
906 {
907         char *path1, *path2, *tmp;
908         int pflag, lflag, iflag, cmdnum, i;
909         unsigned long n_arg;
910         Attrib a, *aa;
911         char path_buf[MAXPATHLEN];
912         int err = 0;
913         glob_t g;
914
915         path1 = path2 = NULL;
916         cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg,
917             &path1, &path2);
918
919         if (iflag != 0)
920                 err_abort = 0;
921
922         memset(&g, 0, sizeof(g));
923
924         /* Perform command */
925         switch (cmdnum) {
926         case 0:
927                 /* Blank line */
928                 break;
929         case -1:
930                 /* Unrecognized command */
931                 err = -1;
932                 break;
933         case I_GET:
934                 err = process_get(conn, path1, path2, *pwd, pflag);
935                 break;
936         case I_PUT:
937                 err = process_put(conn, path1, path2, *pwd, pflag);
938                 break;
939         case I_RENAME:
940                 path1 = make_absolute(path1, *pwd);
941                 path2 = make_absolute(path2, *pwd);
942                 err = do_rename(conn, path1, path2);
943                 break;
944         case I_SYMLINK:
945                 path2 = make_absolute(path2, *pwd);
946                 err = do_symlink(conn, path1, path2);
947                 break;
948         case I_RM:
949                 path1 = make_absolute(path1, *pwd);
950                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
951                 for (i = 0; g.gl_pathv[i]; i++) {
952                         printf("Removing %s\n", g.gl_pathv[i]);
953                         err = do_rm(conn, g.gl_pathv[i]);
954                         if (err != 0 && err_abort)
955                                 break;
956                 }
957                 break;
958         case I_MKDIR:
959                 path1 = make_absolute(path1, *pwd);
960                 attrib_clear(&a);
961                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
962                 a.perm = 0777;
963                 err = do_mkdir(conn, path1, &a);
964                 break;
965         case I_RMDIR:
966                 path1 = make_absolute(path1, *pwd);
967                 err = do_rmdir(conn, path1);
968                 break;
969         case I_CHDIR:
970                 path1 = make_absolute(path1, *pwd);
971                 if ((tmp = do_realpath(conn, path1)) == NULL) {
972                         err = 1;
973                         break;
974                 }
975                 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
976                         xfree(tmp);
977                         err = 1;
978                         break;
979                 }
980                 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
981                         error("Can't change directory: Can't check target");
982                         xfree(tmp);
983                         err = 1;
984                         break;
985                 }
986                 if (!S_ISDIR(aa->perm)) {
987                         error("Can't change directory: \"%s\" is not "
988                             "a directory", tmp);
989                         xfree(tmp);
990                         err = 1;
991                         break;
992                 }
993                 xfree(*pwd);
994                 *pwd = tmp;
995                 break;
996         case I_LS:
997                 if (!path1) {
998                         do_globbed_ls(conn, *pwd, *pwd, lflag);
999                         break;
1000                 }
1001
1002                 /* Strip pwd off beginning of non-absolute paths */
1003                 tmp = NULL;
1004                 if (*path1 != '/')
1005                         tmp = *pwd;
1006
1007                 path1 = make_absolute(path1, *pwd);
1008                 err = do_globbed_ls(conn, path1, tmp, lflag);
1009                 break;
1010         case I_LCHDIR:
1011                 if (chdir(path1) == -1) {
1012                         error("Couldn't change local directory to "
1013                             "\"%s\": %s", path1, strerror(errno));
1014                         err = 1;
1015                 }
1016                 break;
1017         case I_LMKDIR:
1018                 if (mkdir(path1, 0777) == -1) {
1019                         error("Couldn't create local directory "
1020                             "\"%s\": %s", path1, strerror(errno));
1021                         err = 1;
1022                 }
1023                 break;
1024         case I_LLS:
1025                 local_do_ls(cmd);
1026                 break;
1027         case I_SHELL:
1028                 local_do_shell(cmd);
1029                 break;
1030         case I_LUMASK:
1031                 umask(n_arg);
1032                 printf("Local umask: %03lo\n", n_arg);
1033                 break;
1034         case I_CHMOD:
1035                 path1 = make_absolute(path1, *pwd);
1036                 attrib_clear(&a);
1037                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1038                 a.perm = n_arg;
1039                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1040                 for (i = 0; g.gl_pathv[i]; i++) {
1041                         printf("Changing mode on %s\n", g.gl_pathv[i]);
1042                         err = do_setstat(conn, g.gl_pathv[i], &a);
1043                         if (err != 0 && err_abort)
1044                                 break;
1045                 }
1046                 break;
1047         case I_CHOWN:
1048         case I_CHGRP:
1049                 path1 = make_absolute(path1, *pwd);
1050                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1051                 for (i = 0; g.gl_pathv[i]; i++) {
1052                         if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1053                                 if (err != 0 && err_abort)
1054                                         break;
1055                                 else
1056                                         continue;
1057                         }
1058                         if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1059                                 error("Can't get current ownership of "
1060                                     "remote file \"%s\"", g.gl_pathv[i]);
1061                                 if (err != 0 && err_abort)
1062                                         break;
1063                                 else
1064                                         continue;
1065                         }
1066                         aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1067                         if (cmdnum == I_CHOWN) {
1068                                 printf("Changing owner on %s\n", g.gl_pathv[i]);
1069                                 aa->uid = n_arg;
1070                         } else {
1071                                 printf("Changing group on %s\n", g.gl_pathv[i]);
1072                                 aa->gid = n_arg;
1073                         }
1074                         err = do_setstat(conn, g.gl_pathv[i], aa);
1075                         if (err != 0 && err_abort)
1076                                 break;
1077                 }
1078                 break;
1079         case I_PWD:
1080                 printf("Remote working directory: %s\n", *pwd);
1081                 break;
1082         case I_LPWD:
1083                 if (!getcwd(path_buf, sizeof(path_buf))) {
1084                         error("Couldn't get local cwd: %s", strerror(errno));
1085                         err = -1;
1086                         break;
1087                 }
1088                 printf("Local working directory: %s\n", path_buf);
1089                 break;
1090         case I_QUIT:
1091                 /* Processed below */
1092                 break;
1093         case I_HELP:
1094                 help();
1095                 break;
1096         case I_VERSION:
1097                 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1098                 break;
1099         case I_PROGRESS:
1100                 showprogress = !showprogress;
1101                 if (showprogress)
1102                         printf("Progress meter enabled\n");
1103                 else
1104                         printf("Progress meter disabled\n");
1105                 break;
1106         default:
1107                 fatal("%d is not implemented", cmdnum);
1108         }
1109
1110         if (g.gl_pathc)
1111                 globfree(&g);
1112         if (path1)
1113                 xfree(path1);
1114         if (path2)
1115                 xfree(path2);
1116
1117         /* If an unignored error occurs in batch mode we should abort. */
1118         if (err_abort && err != 0)
1119                 return (-1);
1120         else if (cmdnum == I_QUIT)
1121                 return (1);
1122
1123         return (0);
1124 }
1125
1126 int
1127 interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1128 {
1129         char *pwd;
1130         char *dir = NULL;
1131         char cmd[2048];
1132         struct sftp_conn *conn;
1133         int err;
1134
1135         conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests);
1136         if (conn == NULL)
1137                 fatal("Couldn't initialise connection to server");
1138
1139         pwd = do_realpath(conn, ".");
1140         if (pwd == NULL)
1141                 fatal("Need cwd");
1142
1143         if (file1 != NULL) {
1144                 dir = xstrdup(file1);
1145                 dir = make_absolute(dir, pwd);
1146
1147                 if (remote_is_dir(conn, dir) && file2 == NULL) {
1148                         printf("Changing to: %s\n", dir);
1149                         snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1150                         if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0)
1151                                 return (-1);
1152                 } else {
1153                         if (file2 == NULL)
1154                                 snprintf(cmd, sizeof cmd, "get %s", dir);
1155                         else
1156                                 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1157                                     file2);
1158
1159                         err = parse_dispatch_command(conn, cmd, &pwd, 1);
1160                         xfree(dir);
1161                         xfree(pwd);
1162                         return (err);
1163                 }
1164                 xfree(dir);
1165         }
1166
1167 #if HAVE_SETVBUF
1168         setvbuf(stdout, NULL, _IOLBF, 0);
1169         setvbuf(infile, NULL, _IOLBF, 0);
1170 #else
1171         setlinebuf(stdout);
1172         setlinebuf(infile);
1173 #endif
1174
1175         err = 0;
1176         for (;;) {
1177                 char *cp;
1178
1179                 printf("sftp> ");
1180
1181                 /* XXX: use libedit */
1182                 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1183                         printf("\n");
1184                         break;
1185                 }
1186
1187                 if (batchmode) /* Echo command */
1188                         printf("%s", cmd);
1189
1190                 cp = strrchr(cmd, '\n');
1191                 if (cp)
1192                         *cp = '\0';
1193
1194                 err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
1195                 if (err != 0)
1196                         break;
1197         }
1198         xfree(pwd);
1199
1200         /* err == 1 signifies normal "quit" exit */
1201         return (err >= 0 ? 0 : -1);
1202 }
1203
This page took 0.58322 seconds and 5 git commands to generate.