192eb0aa1SDag-Erling Smørgrav /* $OpenBSD: sftp.c,v 1.93 2006/09/30 17:48:22 ray Exp $ */ 21e8db6e2SBrian Feldman /* 3efcad6b7SDag-Erling Smørgrav * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 41e8db6e2SBrian Feldman * 5efcad6b7SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 6efcad6b7SDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 7efcad6b7SDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 81e8db6e2SBrian Feldman * 9efcad6b7SDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10efcad6b7SDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11efcad6b7SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12efcad6b7SDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13efcad6b7SDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14efcad6b7SDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15efcad6b7SDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 161e8db6e2SBrian Feldman */ 171e8db6e2SBrian Feldman 181e8db6e2SBrian Feldman #include "includes.h" 191e8db6e2SBrian Feldman 20761efaa7SDag-Erling Smørgrav #include <sys/types.h> 21761efaa7SDag-Erling Smørgrav #include <sys/ioctl.h> 22761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_STAT_H 23761efaa7SDag-Erling Smørgrav # include <sys/stat.h> 24761efaa7SDag-Erling Smørgrav #endif 25761efaa7SDag-Erling Smørgrav #include <sys/param.h> 26761efaa7SDag-Erling Smørgrav #include <sys/socket.h> 27761efaa7SDag-Erling Smørgrav #include <sys/wait.h> 285e8dbd04SDag-Erling Smørgrav 29761efaa7SDag-Erling Smørgrav #include <errno.h> 30761efaa7SDag-Erling Smørgrav 31761efaa7SDag-Erling Smørgrav #ifdef HAVE_PATHS_H 32761efaa7SDag-Erling Smørgrav # include <paths.h> 33761efaa7SDag-Erling Smørgrav #endif 345e8dbd04SDag-Erling Smørgrav #ifdef USE_LIBEDIT 355e8dbd04SDag-Erling Smørgrav #include <histedit.h> 365e8dbd04SDag-Erling Smørgrav #else 375e8dbd04SDag-Erling Smørgrav typedef void EditLine; 385e8dbd04SDag-Erling Smørgrav #endif 39761efaa7SDag-Erling Smørgrav #include <signal.h> 40761efaa7SDag-Erling Smørgrav #include <stdlib.h> 41761efaa7SDag-Erling Smørgrav #include <stdio.h> 42761efaa7SDag-Erling Smørgrav #include <string.h> 43761efaa7SDag-Erling Smørgrav #include <unistd.h> 44761efaa7SDag-Erling Smørgrav #include <stdarg.h> 451e8db6e2SBrian Feldman 461e8db6e2SBrian Feldman #include "xmalloc.h" 471e8db6e2SBrian Feldman #include "log.h" 481e8db6e2SBrian Feldman #include "pathnames.h" 49ae1f160dSDag-Erling Smørgrav #include "misc.h" 501e8db6e2SBrian Feldman 511e8db6e2SBrian Feldman #include "sftp.h" 52761efaa7SDag-Erling Smørgrav #include "buffer.h" 531e8db6e2SBrian Feldman #include "sftp-common.h" 541e8db6e2SBrian Feldman #include "sftp-client.h" 55efcad6b7SDag-Erling Smørgrav 56efcad6b7SDag-Erling Smørgrav /* File to read commands from */ 57efcad6b7SDag-Erling Smørgrav FILE* infile; 58efcad6b7SDag-Erling Smørgrav 59efcad6b7SDag-Erling Smørgrav /* Are we in batchfile mode? */ 60efcad6b7SDag-Erling Smørgrav int batchmode = 0; 61efcad6b7SDag-Erling Smørgrav 62efcad6b7SDag-Erling Smørgrav /* Size of buffer used when copying files */ 63efcad6b7SDag-Erling Smørgrav size_t copy_buffer_len = 32768; 64efcad6b7SDag-Erling Smørgrav 65efcad6b7SDag-Erling Smørgrav /* Number of concurrent outstanding requests */ 66efcad6b7SDag-Erling Smørgrav size_t num_requests = 16; 67efcad6b7SDag-Erling Smørgrav 68efcad6b7SDag-Erling Smørgrav /* PID of ssh transport process */ 69efcad6b7SDag-Erling Smørgrav static pid_t sshpid = -1; 70efcad6b7SDag-Erling Smørgrav 71efcad6b7SDag-Erling Smørgrav /* This is set to 0 if the progressmeter is not desired. */ 7252028650SDag-Erling Smørgrav int showprogress = 1; 73efcad6b7SDag-Erling Smørgrav 74d74d50a8SDag-Erling Smørgrav /* SIGINT received during command processing */ 75d74d50a8SDag-Erling Smørgrav volatile sig_atomic_t interrupted = 0; 76d74d50a8SDag-Erling Smørgrav 77d74d50a8SDag-Erling Smørgrav /* I wish qsort() took a separate ctx for the comparison function...*/ 78d74d50a8SDag-Erling Smørgrav int sort_flag; 79d74d50a8SDag-Erling Smørgrav 80efcad6b7SDag-Erling Smørgrav int remote_glob(struct sftp_conn *, const char *, int, 81efcad6b7SDag-Erling Smørgrav int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 821e8db6e2SBrian Feldman 8383d2307dSDag-Erling Smørgrav extern char *__progname; 8483d2307dSDag-Erling Smørgrav 85efcad6b7SDag-Erling Smørgrav /* Separators for interactive commands */ 86efcad6b7SDag-Erling Smørgrav #define WHITESPACE " \t\r\n" 871e8db6e2SBrian Feldman 88d74d50a8SDag-Erling Smørgrav /* ls flags */ 89d74d50a8SDag-Erling Smørgrav #define LS_LONG_VIEW 0x01 /* Full view ala ls -l */ 90d74d50a8SDag-Erling Smørgrav #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */ 91d74d50a8SDag-Erling Smørgrav #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */ 92d74d50a8SDag-Erling Smørgrav #define LS_NAME_SORT 0x08 /* Sort by name (default) */ 93d74d50a8SDag-Erling Smørgrav #define LS_TIME_SORT 0x10 /* Sort by mtime */ 94d74d50a8SDag-Erling Smørgrav #define LS_SIZE_SORT 0x20 /* Sort by file size */ 95d74d50a8SDag-Erling Smørgrav #define LS_REVERSE_SORT 0x40 /* Reverse sort order */ 96d74d50a8SDag-Erling Smørgrav #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */ 97d74d50a8SDag-Erling Smørgrav 98d74d50a8SDag-Erling Smørgrav #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW) 99d74d50a8SDag-Erling Smørgrav #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) 100efcad6b7SDag-Erling Smørgrav 101efcad6b7SDag-Erling Smørgrav /* Commands for interactive mode */ 102efcad6b7SDag-Erling Smørgrav #define I_CHDIR 1 103efcad6b7SDag-Erling Smørgrav #define I_CHGRP 2 104efcad6b7SDag-Erling Smørgrav #define I_CHMOD 3 105efcad6b7SDag-Erling Smørgrav #define I_CHOWN 4 106efcad6b7SDag-Erling Smørgrav #define I_GET 5 107efcad6b7SDag-Erling Smørgrav #define I_HELP 6 108efcad6b7SDag-Erling Smørgrav #define I_LCHDIR 7 109efcad6b7SDag-Erling Smørgrav #define I_LLS 8 110efcad6b7SDag-Erling Smørgrav #define I_LMKDIR 9 111efcad6b7SDag-Erling Smørgrav #define I_LPWD 10 112efcad6b7SDag-Erling Smørgrav #define I_LS 11 113efcad6b7SDag-Erling Smørgrav #define I_LUMASK 12 114efcad6b7SDag-Erling Smørgrav #define I_MKDIR 13 115efcad6b7SDag-Erling Smørgrav #define I_PUT 14 116efcad6b7SDag-Erling Smørgrav #define I_PWD 15 117efcad6b7SDag-Erling Smørgrav #define I_QUIT 16 118efcad6b7SDag-Erling Smørgrav #define I_RENAME 17 119efcad6b7SDag-Erling Smørgrav #define I_RM 18 120efcad6b7SDag-Erling Smørgrav #define I_RMDIR 19 121efcad6b7SDag-Erling Smørgrav #define I_SHELL 20 122efcad6b7SDag-Erling Smørgrav #define I_SYMLINK 21 123efcad6b7SDag-Erling Smørgrav #define I_VERSION 22 124efcad6b7SDag-Erling Smørgrav #define I_PROGRESS 23 125efcad6b7SDag-Erling Smørgrav 126efcad6b7SDag-Erling Smørgrav struct CMD { 127efcad6b7SDag-Erling Smørgrav const char *c; 128efcad6b7SDag-Erling Smørgrav const int n; 129efcad6b7SDag-Erling Smørgrav }; 130efcad6b7SDag-Erling Smørgrav 131efcad6b7SDag-Erling Smørgrav static const struct CMD cmds[] = { 132efcad6b7SDag-Erling Smørgrav { "bye", I_QUIT }, 133efcad6b7SDag-Erling Smørgrav { "cd", I_CHDIR }, 134efcad6b7SDag-Erling Smørgrav { "chdir", I_CHDIR }, 135efcad6b7SDag-Erling Smørgrav { "chgrp", I_CHGRP }, 136efcad6b7SDag-Erling Smørgrav { "chmod", I_CHMOD }, 137efcad6b7SDag-Erling Smørgrav { "chown", I_CHOWN }, 138efcad6b7SDag-Erling Smørgrav { "dir", I_LS }, 139efcad6b7SDag-Erling Smørgrav { "exit", I_QUIT }, 140efcad6b7SDag-Erling Smørgrav { "get", I_GET }, 141efcad6b7SDag-Erling Smørgrav { "mget", I_GET }, 142efcad6b7SDag-Erling Smørgrav { "help", I_HELP }, 143efcad6b7SDag-Erling Smørgrav { "lcd", I_LCHDIR }, 144efcad6b7SDag-Erling Smørgrav { "lchdir", I_LCHDIR }, 145efcad6b7SDag-Erling Smørgrav { "lls", I_LLS }, 146efcad6b7SDag-Erling Smørgrav { "lmkdir", I_LMKDIR }, 147efcad6b7SDag-Erling Smørgrav { "ln", I_SYMLINK }, 148efcad6b7SDag-Erling Smørgrav { "lpwd", I_LPWD }, 149efcad6b7SDag-Erling Smørgrav { "ls", I_LS }, 150efcad6b7SDag-Erling Smørgrav { "lumask", I_LUMASK }, 151efcad6b7SDag-Erling Smørgrav { "mkdir", I_MKDIR }, 152efcad6b7SDag-Erling Smørgrav { "progress", I_PROGRESS }, 153efcad6b7SDag-Erling Smørgrav { "put", I_PUT }, 154efcad6b7SDag-Erling Smørgrav { "mput", I_PUT }, 155efcad6b7SDag-Erling Smørgrav { "pwd", I_PWD }, 156efcad6b7SDag-Erling Smørgrav { "quit", I_QUIT }, 157efcad6b7SDag-Erling Smørgrav { "rename", I_RENAME }, 158efcad6b7SDag-Erling Smørgrav { "rm", I_RM }, 159efcad6b7SDag-Erling Smørgrav { "rmdir", I_RMDIR }, 160efcad6b7SDag-Erling Smørgrav { "symlink", I_SYMLINK }, 161efcad6b7SDag-Erling Smørgrav { "version", I_VERSION }, 162efcad6b7SDag-Erling Smørgrav { "!", I_SHELL }, 163efcad6b7SDag-Erling Smørgrav { "?", I_HELP }, 164efcad6b7SDag-Erling Smørgrav { NULL, -1} 165efcad6b7SDag-Erling Smørgrav }; 166efcad6b7SDag-Erling Smørgrav 167efcad6b7SDag-Erling Smørgrav int interactive_loop(int fd_in, int fd_out, char *file1, char *file2); 168efcad6b7SDag-Erling Smørgrav 169efcad6b7SDag-Erling Smørgrav static void 170d74d50a8SDag-Erling Smørgrav killchild(int signo) 171d74d50a8SDag-Erling Smørgrav { 1725e8dbd04SDag-Erling Smørgrav if (sshpid > 1) { 173d74d50a8SDag-Erling Smørgrav kill(sshpid, SIGTERM); 1745e8dbd04SDag-Erling Smørgrav waitpid(sshpid, NULL, 0); 1755e8dbd04SDag-Erling Smørgrav } 176d74d50a8SDag-Erling Smørgrav 177d74d50a8SDag-Erling Smørgrav _exit(1); 178d74d50a8SDag-Erling Smørgrav } 179d74d50a8SDag-Erling Smørgrav 180d74d50a8SDag-Erling Smørgrav static void 181d74d50a8SDag-Erling Smørgrav cmd_interrupt(int signo) 182d74d50a8SDag-Erling Smørgrav { 183d74d50a8SDag-Erling Smørgrav const char msg[] = "\rInterrupt \n"; 1845e8dbd04SDag-Erling Smørgrav int olderrno = errno; 185d74d50a8SDag-Erling Smørgrav 186d74d50a8SDag-Erling Smørgrav write(STDERR_FILENO, msg, sizeof(msg) - 1); 187d74d50a8SDag-Erling Smørgrav interrupted = 1; 1885e8dbd04SDag-Erling Smørgrav errno = olderrno; 189d74d50a8SDag-Erling Smørgrav } 190d74d50a8SDag-Erling Smørgrav 191d74d50a8SDag-Erling Smørgrav static void 192efcad6b7SDag-Erling Smørgrav help(void) 193efcad6b7SDag-Erling Smørgrav { 194efcad6b7SDag-Erling Smørgrav printf("Available commands:\n"); 195efcad6b7SDag-Erling Smørgrav printf("cd path Change remote directory to 'path'\n"); 196efcad6b7SDag-Erling Smørgrav printf("lcd path Change local directory to 'path'\n"); 197efcad6b7SDag-Erling Smørgrav printf("chgrp grp path Change group of file 'path' to 'grp'\n"); 198efcad6b7SDag-Erling Smørgrav printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); 199efcad6b7SDag-Erling Smørgrav printf("chown own path Change owner of file 'path' to 'own'\n"); 200efcad6b7SDag-Erling Smørgrav printf("help Display this help text\n"); 201efcad6b7SDag-Erling Smørgrav printf("get remote-path [local-path] Download file\n"); 202efcad6b7SDag-Erling Smørgrav printf("lls [ls-options [path]] Display local directory listing\n"); 203efcad6b7SDag-Erling Smørgrav printf("ln oldpath newpath Symlink remote file\n"); 204efcad6b7SDag-Erling Smørgrav printf("lmkdir path Create local directory\n"); 205efcad6b7SDag-Erling Smørgrav printf("lpwd Print local working directory\n"); 206efcad6b7SDag-Erling Smørgrav printf("ls [path] Display remote directory listing\n"); 207efcad6b7SDag-Erling Smørgrav printf("lumask umask Set local umask to 'umask'\n"); 208efcad6b7SDag-Erling Smørgrav printf("mkdir path Create remote directory\n"); 209efcad6b7SDag-Erling Smørgrav printf("progress Toggle display of progress meter\n"); 210efcad6b7SDag-Erling Smørgrav printf("put local-path [remote-path] Upload file\n"); 211efcad6b7SDag-Erling Smørgrav printf("pwd Display remote working directory\n"); 212efcad6b7SDag-Erling Smørgrav printf("exit Quit sftp\n"); 213efcad6b7SDag-Erling Smørgrav printf("quit Quit sftp\n"); 214efcad6b7SDag-Erling Smørgrav printf("rename oldpath newpath Rename remote file\n"); 215efcad6b7SDag-Erling Smørgrav printf("rmdir path Remove remote directory\n"); 216efcad6b7SDag-Erling Smørgrav printf("rm path Delete remote file\n"); 217efcad6b7SDag-Erling Smørgrav printf("symlink oldpath newpath Symlink remote file\n"); 218efcad6b7SDag-Erling Smørgrav printf("version Show SFTP version\n"); 219efcad6b7SDag-Erling Smørgrav printf("!command Execute 'command' in local shell\n"); 220efcad6b7SDag-Erling Smørgrav printf("! Escape to local shell\n"); 221efcad6b7SDag-Erling Smørgrav printf("? Synonym for help\n"); 222efcad6b7SDag-Erling Smørgrav } 223efcad6b7SDag-Erling Smørgrav 224efcad6b7SDag-Erling Smørgrav static void 225efcad6b7SDag-Erling Smørgrav local_do_shell(const char *args) 226efcad6b7SDag-Erling Smørgrav { 227efcad6b7SDag-Erling Smørgrav int status; 228efcad6b7SDag-Erling Smørgrav char *shell; 229efcad6b7SDag-Erling Smørgrav pid_t pid; 230efcad6b7SDag-Erling Smørgrav 231efcad6b7SDag-Erling Smørgrav if (!*args) 232efcad6b7SDag-Erling Smørgrav args = NULL; 233efcad6b7SDag-Erling Smørgrav 234efcad6b7SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL) 235efcad6b7SDag-Erling Smørgrav shell = _PATH_BSHELL; 236efcad6b7SDag-Erling Smørgrav 237efcad6b7SDag-Erling Smørgrav if ((pid = fork()) == -1) 238efcad6b7SDag-Erling Smørgrav fatal("Couldn't fork: %s", strerror(errno)); 239efcad6b7SDag-Erling Smørgrav 240efcad6b7SDag-Erling Smørgrav if (pid == 0) { 241efcad6b7SDag-Erling Smørgrav /* XXX: child has pipe fds to ssh subproc open - issue? */ 242efcad6b7SDag-Erling Smørgrav if (args) { 243efcad6b7SDag-Erling Smørgrav debug3("Executing %s -c \"%s\"", shell, args); 244efcad6b7SDag-Erling Smørgrav execl(shell, shell, "-c", args, (char *)NULL); 245efcad6b7SDag-Erling Smørgrav } else { 246efcad6b7SDag-Erling Smørgrav debug3("Executing %s", shell); 247efcad6b7SDag-Erling Smørgrav execl(shell, shell, (char *)NULL); 248efcad6b7SDag-Erling Smørgrav } 249efcad6b7SDag-Erling Smørgrav fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, 250efcad6b7SDag-Erling Smørgrav strerror(errno)); 251efcad6b7SDag-Erling Smørgrav _exit(1); 252efcad6b7SDag-Erling Smørgrav } 253efcad6b7SDag-Erling Smørgrav while (waitpid(pid, &status, 0) == -1) 254efcad6b7SDag-Erling Smørgrav if (errno != EINTR) 255efcad6b7SDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 256efcad6b7SDag-Erling Smørgrav if (!WIFEXITED(status)) 257761efaa7SDag-Erling Smørgrav error("Shell exited abnormally"); 258efcad6b7SDag-Erling Smørgrav else if (WEXITSTATUS(status)) 259efcad6b7SDag-Erling Smørgrav error("Shell exited with status %d", WEXITSTATUS(status)); 260efcad6b7SDag-Erling Smørgrav } 261efcad6b7SDag-Erling Smørgrav 262efcad6b7SDag-Erling Smørgrav static void 263efcad6b7SDag-Erling Smørgrav local_do_ls(const char *args) 264efcad6b7SDag-Erling Smørgrav { 265efcad6b7SDag-Erling Smørgrav if (!args || !*args) 266efcad6b7SDag-Erling Smørgrav local_do_shell(_PATH_LS); 267efcad6b7SDag-Erling Smørgrav else { 268efcad6b7SDag-Erling Smørgrav int len = strlen(_PATH_LS " ") + strlen(args) + 1; 269efcad6b7SDag-Erling Smørgrav char *buf = xmalloc(len); 270efcad6b7SDag-Erling Smørgrav 271efcad6b7SDag-Erling Smørgrav /* XXX: quoting - rip quoting code from ftp? */ 272efcad6b7SDag-Erling Smørgrav snprintf(buf, len, _PATH_LS " %s", args); 273efcad6b7SDag-Erling Smørgrav local_do_shell(buf); 274efcad6b7SDag-Erling Smørgrav xfree(buf); 275efcad6b7SDag-Erling Smørgrav } 276efcad6b7SDag-Erling Smørgrav } 277efcad6b7SDag-Erling Smørgrav 278efcad6b7SDag-Erling Smørgrav /* Strip one path (usually the pwd) from the start of another */ 279efcad6b7SDag-Erling Smørgrav static char * 280efcad6b7SDag-Erling Smørgrav path_strip(char *path, char *strip) 281efcad6b7SDag-Erling Smørgrav { 282efcad6b7SDag-Erling Smørgrav size_t len; 283efcad6b7SDag-Erling Smørgrav 284efcad6b7SDag-Erling Smørgrav if (strip == NULL) 285efcad6b7SDag-Erling Smørgrav return (xstrdup(path)); 286efcad6b7SDag-Erling Smørgrav 287efcad6b7SDag-Erling Smørgrav len = strlen(strip); 2885e8dbd04SDag-Erling Smørgrav if (strncmp(path, strip, len) == 0) { 289efcad6b7SDag-Erling Smørgrav if (strip[len - 1] != '/' && path[len] == '/') 290efcad6b7SDag-Erling Smørgrav len++; 291efcad6b7SDag-Erling Smørgrav return (xstrdup(path + len)); 292efcad6b7SDag-Erling Smørgrav } 293efcad6b7SDag-Erling Smørgrav 294efcad6b7SDag-Erling Smørgrav return (xstrdup(path)); 295efcad6b7SDag-Erling Smørgrav } 296efcad6b7SDag-Erling Smørgrav 297efcad6b7SDag-Erling Smørgrav static char * 298efcad6b7SDag-Erling Smørgrav path_append(char *p1, char *p2) 299efcad6b7SDag-Erling Smørgrav { 300efcad6b7SDag-Erling Smørgrav char *ret; 301efcad6b7SDag-Erling Smørgrav int len = strlen(p1) + strlen(p2) + 2; 302efcad6b7SDag-Erling Smørgrav 303efcad6b7SDag-Erling Smørgrav ret = xmalloc(len); 304efcad6b7SDag-Erling Smørgrav strlcpy(ret, p1, len); 305efcad6b7SDag-Erling Smørgrav if (p1[strlen(p1) - 1] != '/') 306efcad6b7SDag-Erling Smørgrav strlcat(ret, "/", len); 307efcad6b7SDag-Erling Smørgrav strlcat(ret, p2, len); 308efcad6b7SDag-Erling Smørgrav 309efcad6b7SDag-Erling Smørgrav return(ret); 310efcad6b7SDag-Erling Smørgrav } 311efcad6b7SDag-Erling Smørgrav 312efcad6b7SDag-Erling Smørgrav static char * 313efcad6b7SDag-Erling Smørgrav make_absolute(char *p, char *pwd) 314efcad6b7SDag-Erling Smørgrav { 315d74d50a8SDag-Erling Smørgrav char *abs_str; 316efcad6b7SDag-Erling Smørgrav 317efcad6b7SDag-Erling Smørgrav /* Derelativise */ 318efcad6b7SDag-Erling Smørgrav if (p && p[0] != '/') { 319d74d50a8SDag-Erling Smørgrav abs_str = path_append(pwd, p); 320efcad6b7SDag-Erling Smørgrav xfree(p); 321d74d50a8SDag-Erling Smørgrav return(abs_str); 322efcad6b7SDag-Erling Smørgrav } else 323efcad6b7SDag-Erling Smørgrav return(p); 324efcad6b7SDag-Erling Smørgrav } 325efcad6b7SDag-Erling Smørgrav 326efcad6b7SDag-Erling Smørgrav static int 327efcad6b7SDag-Erling Smørgrav infer_path(const char *p, char **ifp) 328efcad6b7SDag-Erling Smørgrav { 329efcad6b7SDag-Erling Smørgrav char *cp; 330efcad6b7SDag-Erling Smørgrav 331efcad6b7SDag-Erling Smørgrav cp = strrchr(p, '/'); 332efcad6b7SDag-Erling Smørgrav if (cp == NULL) { 333efcad6b7SDag-Erling Smørgrav *ifp = xstrdup(p); 334efcad6b7SDag-Erling Smørgrav return(0); 335efcad6b7SDag-Erling Smørgrav } 336efcad6b7SDag-Erling Smørgrav 337efcad6b7SDag-Erling Smørgrav if (!cp[1]) { 338efcad6b7SDag-Erling Smørgrav error("Invalid path"); 339efcad6b7SDag-Erling Smørgrav return(-1); 340efcad6b7SDag-Erling Smørgrav } 341efcad6b7SDag-Erling Smørgrav 342efcad6b7SDag-Erling Smørgrav *ifp = xstrdup(cp + 1); 343efcad6b7SDag-Erling Smørgrav return(0); 344efcad6b7SDag-Erling Smørgrav } 345efcad6b7SDag-Erling Smørgrav 346efcad6b7SDag-Erling Smørgrav static int 347efcad6b7SDag-Erling Smørgrav parse_getput_flags(const char **cpp, int *pflag) 348efcad6b7SDag-Erling Smørgrav { 349efcad6b7SDag-Erling Smørgrav const char *cp = *cpp; 350efcad6b7SDag-Erling Smørgrav 351efcad6b7SDag-Erling Smørgrav /* Check for flags */ 352efcad6b7SDag-Erling Smørgrav if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { 353efcad6b7SDag-Erling Smørgrav switch (cp[1]) { 354efcad6b7SDag-Erling Smørgrav case 'p': 355efcad6b7SDag-Erling Smørgrav case 'P': 356efcad6b7SDag-Erling Smørgrav *pflag = 1; 357efcad6b7SDag-Erling Smørgrav break; 358efcad6b7SDag-Erling Smørgrav default: 359efcad6b7SDag-Erling Smørgrav error("Invalid flag -%c", cp[1]); 360efcad6b7SDag-Erling Smørgrav return(-1); 361efcad6b7SDag-Erling Smørgrav } 362efcad6b7SDag-Erling Smørgrav cp += 2; 363efcad6b7SDag-Erling Smørgrav *cpp = cp + strspn(cp, WHITESPACE); 364efcad6b7SDag-Erling Smørgrav } 365efcad6b7SDag-Erling Smørgrav 366efcad6b7SDag-Erling Smørgrav return(0); 367efcad6b7SDag-Erling Smørgrav } 368efcad6b7SDag-Erling Smørgrav 369efcad6b7SDag-Erling Smørgrav static int 370efcad6b7SDag-Erling Smørgrav parse_ls_flags(const char **cpp, int *lflag) 371efcad6b7SDag-Erling Smørgrav { 372efcad6b7SDag-Erling Smørgrav const char *cp = *cpp; 373efcad6b7SDag-Erling Smørgrav 374d74d50a8SDag-Erling Smørgrav /* Defaults */ 375d74d50a8SDag-Erling Smørgrav *lflag = LS_NAME_SORT; 376d74d50a8SDag-Erling Smørgrav 377efcad6b7SDag-Erling Smørgrav /* Check for flags */ 378efcad6b7SDag-Erling Smørgrav if (cp++[0] == '-') { 379efcad6b7SDag-Erling Smørgrav for (; strchr(WHITESPACE, *cp) == NULL; cp++) { 380efcad6b7SDag-Erling Smørgrav switch (*cp) { 381efcad6b7SDag-Erling Smørgrav case 'l': 382d74d50a8SDag-Erling Smørgrav *lflag &= ~VIEW_FLAGS; 383d74d50a8SDag-Erling Smørgrav *lflag |= LS_LONG_VIEW; 384efcad6b7SDag-Erling Smørgrav break; 385efcad6b7SDag-Erling Smørgrav case '1': 386d74d50a8SDag-Erling Smørgrav *lflag &= ~VIEW_FLAGS; 387d74d50a8SDag-Erling Smørgrav *lflag |= LS_SHORT_VIEW; 388d74d50a8SDag-Erling Smørgrav break; 389d74d50a8SDag-Erling Smørgrav case 'n': 390d74d50a8SDag-Erling Smørgrav *lflag &= ~VIEW_FLAGS; 391d74d50a8SDag-Erling Smørgrav *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; 392d74d50a8SDag-Erling Smørgrav break; 393d74d50a8SDag-Erling Smørgrav case 'S': 394d74d50a8SDag-Erling Smørgrav *lflag &= ~SORT_FLAGS; 395d74d50a8SDag-Erling Smørgrav *lflag |= LS_SIZE_SORT; 396d74d50a8SDag-Erling Smørgrav break; 397d74d50a8SDag-Erling Smørgrav case 't': 398d74d50a8SDag-Erling Smørgrav *lflag &= ~SORT_FLAGS; 399d74d50a8SDag-Erling Smørgrav *lflag |= LS_TIME_SORT; 400d74d50a8SDag-Erling Smørgrav break; 401d74d50a8SDag-Erling Smørgrav case 'r': 402d74d50a8SDag-Erling Smørgrav *lflag |= LS_REVERSE_SORT; 403d74d50a8SDag-Erling Smørgrav break; 404d74d50a8SDag-Erling Smørgrav case 'f': 405d74d50a8SDag-Erling Smørgrav *lflag &= ~SORT_FLAGS; 406d74d50a8SDag-Erling Smørgrav break; 407d74d50a8SDag-Erling Smørgrav case 'a': 408d74d50a8SDag-Erling Smørgrav *lflag |= LS_SHOW_ALL; 409efcad6b7SDag-Erling Smørgrav break; 410efcad6b7SDag-Erling Smørgrav default: 411efcad6b7SDag-Erling Smørgrav error("Invalid flag -%c", *cp); 412efcad6b7SDag-Erling Smørgrav return(-1); 413efcad6b7SDag-Erling Smørgrav } 414efcad6b7SDag-Erling Smørgrav } 415efcad6b7SDag-Erling Smørgrav *cpp = cp + strspn(cp, WHITESPACE); 416efcad6b7SDag-Erling Smørgrav } 417efcad6b7SDag-Erling Smørgrav 418efcad6b7SDag-Erling Smørgrav return(0); 419efcad6b7SDag-Erling Smørgrav } 420efcad6b7SDag-Erling Smørgrav 421efcad6b7SDag-Erling Smørgrav static int 422efcad6b7SDag-Erling Smørgrav get_pathname(const char **cpp, char **path) 423efcad6b7SDag-Erling Smørgrav { 424efcad6b7SDag-Erling Smørgrav const char *cp = *cpp, *end; 425efcad6b7SDag-Erling Smørgrav char quot; 426043840dfSDag-Erling Smørgrav u_int i, j; 427efcad6b7SDag-Erling Smørgrav 428efcad6b7SDag-Erling Smørgrav cp += strspn(cp, WHITESPACE); 429efcad6b7SDag-Erling Smørgrav if (!*cp) { 430efcad6b7SDag-Erling Smørgrav *cpp = cp; 431efcad6b7SDag-Erling Smørgrav *path = NULL; 432efcad6b7SDag-Erling Smørgrav return (0); 433efcad6b7SDag-Erling Smørgrav } 434efcad6b7SDag-Erling Smørgrav 435efcad6b7SDag-Erling Smørgrav *path = xmalloc(strlen(cp) + 1); 436efcad6b7SDag-Erling Smørgrav 437efcad6b7SDag-Erling Smørgrav /* Check for quoted filenames */ 438efcad6b7SDag-Erling Smørgrav if (*cp == '\"' || *cp == '\'') { 439efcad6b7SDag-Erling Smørgrav quot = *cp++; 440efcad6b7SDag-Erling Smørgrav 441efcad6b7SDag-Erling Smørgrav /* Search for terminating quote, unescape some chars */ 442efcad6b7SDag-Erling Smørgrav for (i = j = 0; i <= strlen(cp); i++) { 443efcad6b7SDag-Erling Smørgrav if (cp[i] == quot) { /* Found quote */ 444efcad6b7SDag-Erling Smørgrav i++; 445efcad6b7SDag-Erling Smørgrav (*path)[j] = '\0'; 446efcad6b7SDag-Erling Smørgrav break; 447efcad6b7SDag-Erling Smørgrav } 448efcad6b7SDag-Erling Smørgrav if (cp[i] == '\0') { /* End of string */ 449efcad6b7SDag-Erling Smørgrav error("Unterminated quote"); 450efcad6b7SDag-Erling Smørgrav goto fail; 451efcad6b7SDag-Erling Smørgrav } 452efcad6b7SDag-Erling Smørgrav if (cp[i] == '\\') { /* Escaped characters */ 453efcad6b7SDag-Erling Smørgrav i++; 454efcad6b7SDag-Erling Smørgrav if (cp[i] != '\'' && cp[i] != '\"' && 455efcad6b7SDag-Erling Smørgrav cp[i] != '\\') { 456d74d50a8SDag-Erling Smørgrav error("Bad escaped character '\\%c'", 457efcad6b7SDag-Erling Smørgrav cp[i]); 458efcad6b7SDag-Erling Smørgrav goto fail; 459efcad6b7SDag-Erling Smørgrav } 460efcad6b7SDag-Erling Smørgrav } 461efcad6b7SDag-Erling Smørgrav (*path)[j++] = cp[i]; 462efcad6b7SDag-Erling Smørgrav } 463efcad6b7SDag-Erling Smørgrav 464efcad6b7SDag-Erling Smørgrav if (j == 0) { 465efcad6b7SDag-Erling Smørgrav error("Empty quotes"); 466efcad6b7SDag-Erling Smørgrav goto fail; 467efcad6b7SDag-Erling Smørgrav } 468efcad6b7SDag-Erling Smørgrav *cpp = cp + i + strspn(cp + i, WHITESPACE); 469efcad6b7SDag-Erling Smørgrav } else { 470efcad6b7SDag-Erling Smørgrav /* Read to end of filename */ 471efcad6b7SDag-Erling Smørgrav end = strpbrk(cp, WHITESPACE); 472efcad6b7SDag-Erling Smørgrav if (end == NULL) 473efcad6b7SDag-Erling Smørgrav end = strchr(cp, '\0'); 474efcad6b7SDag-Erling Smørgrav *cpp = end + strspn(end, WHITESPACE); 475efcad6b7SDag-Erling Smørgrav 476efcad6b7SDag-Erling Smørgrav memcpy(*path, cp, end - cp); 477efcad6b7SDag-Erling Smørgrav (*path)[end - cp] = '\0'; 478efcad6b7SDag-Erling Smørgrav } 479efcad6b7SDag-Erling Smørgrav return (0); 480efcad6b7SDag-Erling Smørgrav 481efcad6b7SDag-Erling Smørgrav fail: 482efcad6b7SDag-Erling Smørgrav xfree(*path); 483efcad6b7SDag-Erling Smørgrav *path = NULL; 484efcad6b7SDag-Erling Smørgrav return (-1); 485efcad6b7SDag-Erling Smørgrav } 486efcad6b7SDag-Erling Smørgrav 487efcad6b7SDag-Erling Smørgrav static int 488efcad6b7SDag-Erling Smørgrav is_dir(char *path) 489efcad6b7SDag-Erling Smørgrav { 490efcad6b7SDag-Erling Smørgrav struct stat sb; 491efcad6b7SDag-Erling Smørgrav 492efcad6b7SDag-Erling Smørgrav /* XXX: report errors? */ 493efcad6b7SDag-Erling Smørgrav if (stat(path, &sb) == -1) 494efcad6b7SDag-Erling Smørgrav return(0); 495efcad6b7SDag-Erling Smørgrav 496761efaa7SDag-Erling Smørgrav return(S_ISDIR(sb.st_mode)); 497efcad6b7SDag-Erling Smørgrav } 498efcad6b7SDag-Erling Smørgrav 499efcad6b7SDag-Erling Smørgrav static int 500efcad6b7SDag-Erling Smørgrav is_reg(char *path) 501efcad6b7SDag-Erling Smørgrav { 502efcad6b7SDag-Erling Smørgrav struct stat sb; 503efcad6b7SDag-Erling Smørgrav 504efcad6b7SDag-Erling Smørgrav if (stat(path, &sb) == -1) 505efcad6b7SDag-Erling Smørgrav fatal("stat %s: %s", path, strerror(errno)); 506efcad6b7SDag-Erling Smørgrav 507efcad6b7SDag-Erling Smørgrav return(S_ISREG(sb.st_mode)); 508efcad6b7SDag-Erling Smørgrav } 509efcad6b7SDag-Erling Smørgrav 510efcad6b7SDag-Erling Smørgrav static int 511efcad6b7SDag-Erling Smørgrav remote_is_dir(struct sftp_conn *conn, char *path) 512efcad6b7SDag-Erling Smørgrav { 513efcad6b7SDag-Erling Smørgrav Attrib *a; 514efcad6b7SDag-Erling Smørgrav 515efcad6b7SDag-Erling Smørgrav /* XXX: report errors? */ 516efcad6b7SDag-Erling Smørgrav if ((a = do_stat(conn, path, 1)) == NULL) 517efcad6b7SDag-Erling Smørgrav return(0); 518efcad6b7SDag-Erling Smørgrav if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) 519efcad6b7SDag-Erling Smørgrav return(0); 520761efaa7SDag-Erling Smørgrav return(S_ISDIR(a->perm)); 521efcad6b7SDag-Erling Smørgrav } 522efcad6b7SDag-Erling Smørgrav 523efcad6b7SDag-Erling Smørgrav static int 524efcad6b7SDag-Erling Smørgrav process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 525efcad6b7SDag-Erling Smørgrav { 526efcad6b7SDag-Erling Smørgrav char *abs_src = NULL; 527efcad6b7SDag-Erling Smørgrav char *abs_dst = NULL; 528efcad6b7SDag-Erling Smørgrav char *tmp; 529efcad6b7SDag-Erling Smørgrav glob_t g; 530efcad6b7SDag-Erling Smørgrav int err = 0; 531efcad6b7SDag-Erling Smørgrav int i; 532efcad6b7SDag-Erling Smørgrav 533efcad6b7SDag-Erling Smørgrav abs_src = xstrdup(src); 534efcad6b7SDag-Erling Smørgrav abs_src = make_absolute(abs_src, pwd); 535efcad6b7SDag-Erling Smørgrav 536efcad6b7SDag-Erling Smørgrav memset(&g, 0, sizeof(g)); 537efcad6b7SDag-Erling Smørgrav debug3("Looking up %s", abs_src); 538efcad6b7SDag-Erling Smørgrav if (remote_glob(conn, abs_src, 0, NULL, &g)) { 539efcad6b7SDag-Erling Smørgrav error("File \"%s\" not found.", abs_src); 540efcad6b7SDag-Erling Smørgrav err = -1; 541efcad6b7SDag-Erling Smørgrav goto out; 542efcad6b7SDag-Erling Smørgrav } 543efcad6b7SDag-Erling Smørgrav 544efcad6b7SDag-Erling Smørgrav /* If multiple matches, dst must be a directory or unspecified */ 545efcad6b7SDag-Erling Smørgrav if (g.gl_matchc > 1 && dst && !is_dir(dst)) { 546efcad6b7SDag-Erling Smørgrav error("Multiple files match, but \"%s\" is not a directory", 547efcad6b7SDag-Erling Smørgrav dst); 548efcad6b7SDag-Erling Smørgrav err = -1; 549efcad6b7SDag-Erling Smørgrav goto out; 550efcad6b7SDag-Erling Smørgrav } 551efcad6b7SDag-Erling Smørgrav 552d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 553efcad6b7SDag-Erling Smørgrav if (infer_path(g.gl_pathv[i], &tmp)) { 554efcad6b7SDag-Erling Smørgrav err = -1; 555efcad6b7SDag-Erling Smørgrav goto out; 556efcad6b7SDag-Erling Smørgrav } 557efcad6b7SDag-Erling Smørgrav 558efcad6b7SDag-Erling Smørgrav if (g.gl_matchc == 1 && dst) { 559efcad6b7SDag-Erling Smørgrav /* If directory specified, append filename */ 560761efaa7SDag-Erling Smørgrav xfree(tmp); 561efcad6b7SDag-Erling Smørgrav if (is_dir(dst)) { 562efcad6b7SDag-Erling Smørgrav if (infer_path(g.gl_pathv[0], &tmp)) { 563efcad6b7SDag-Erling Smørgrav err = 1; 564efcad6b7SDag-Erling Smørgrav goto out; 565efcad6b7SDag-Erling Smørgrav } 566efcad6b7SDag-Erling Smørgrav abs_dst = path_append(dst, tmp); 567efcad6b7SDag-Erling Smørgrav xfree(tmp); 568efcad6b7SDag-Erling Smørgrav } else 569efcad6b7SDag-Erling Smørgrav abs_dst = xstrdup(dst); 570efcad6b7SDag-Erling Smørgrav } else if (dst) { 571efcad6b7SDag-Erling Smørgrav abs_dst = path_append(dst, tmp); 572efcad6b7SDag-Erling Smørgrav xfree(tmp); 573efcad6b7SDag-Erling Smørgrav } else 574efcad6b7SDag-Erling Smørgrav abs_dst = tmp; 575efcad6b7SDag-Erling Smørgrav 576efcad6b7SDag-Erling Smørgrav printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 577efcad6b7SDag-Erling Smørgrav if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 578efcad6b7SDag-Erling Smørgrav err = -1; 579efcad6b7SDag-Erling Smørgrav xfree(abs_dst); 580efcad6b7SDag-Erling Smørgrav abs_dst = NULL; 581efcad6b7SDag-Erling Smørgrav } 582efcad6b7SDag-Erling Smørgrav 583efcad6b7SDag-Erling Smørgrav out: 584efcad6b7SDag-Erling Smørgrav xfree(abs_src); 585efcad6b7SDag-Erling Smørgrav globfree(&g); 586efcad6b7SDag-Erling Smørgrav return(err); 587efcad6b7SDag-Erling Smørgrav } 588efcad6b7SDag-Erling Smørgrav 589efcad6b7SDag-Erling Smørgrav static int 590efcad6b7SDag-Erling Smørgrav process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 591efcad6b7SDag-Erling Smørgrav { 592efcad6b7SDag-Erling Smørgrav char *tmp_dst = NULL; 593efcad6b7SDag-Erling Smørgrav char *abs_dst = NULL; 594efcad6b7SDag-Erling Smørgrav char *tmp; 595efcad6b7SDag-Erling Smørgrav glob_t g; 596efcad6b7SDag-Erling Smørgrav int err = 0; 597efcad6b7SDag-Erling Smørgrav int i; 598efcad6b7SDag-Erling Smørgrav 599efcad6b7SDag-Erling Smørgrav if (dst) { 600efcad6b7SDag-Erling Smørgrav tmp_dst = xstrdup(dst); 601efcad6b7SDag-Erling Smørgrav tmp_dst = make_absolute(tmp_dst, pwd); 602efcad6b7SDag-Erling Smørgrav } 603efcad6b7SDag-Erling Smørgrav 604efcad6b7SDag-Erling Smørgrav memset(&g, 0, sizeof(g)); 605efcad6b7SDag-Erling Smørgrav debug3("Looking up %s", src); 606efcad6b7SDag-Erling Smørgrav if (glob(src, 0, NULL, &g)) { 607efcad6b7SDag-Erling Smørgrav error("File \"%s\" not found.", src); 608efcad6b7SDag-Erling Smørgrav err = -1; 609efcad6b7SDag-Erling Smørgrav goto out; 610efcad6b7SDag-Erling Smørgrav } 611efcad6b7SDag-Erling Smørgrav 612efcad6b7SDag-Erling Smørgrav /* If multiple matches, dst may be directory or unspecified */ 613efcad6b7SDag-Erling Smørgrav if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { 614efcad6b7SDag-Erling Smørgrav error("Multiple files match, but \"%s\" is not a directory", 615efcad6b7SDag-Erling Smørgrav tmp_dst); 616efcad6b7SDag-Erling Smørgrav err = -1; 617efcad6b7SDag-Erling Smørgrav goto out; 618efcad6b7SDag-Erling Smørgrav } 619efcad6b7SDag-Erling Smørgrav 620d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 621efcad6b7SDag-Erling Smørgrav if (!is_reg(g.gl_pathv[i])) { 622efcad6b7SDag-Erling Smørgrav error("skipping non-regular file %s", 623efcad6b7SDag-Erling Smørgrav g.gl_pathv[i]); 624efcad6b7SDag-Erling Smørgrav continue; 625efcad6b7SDag-Erling Smørgrav } 626efcad6b7SDag-Erling Smørgrav if (infer_path(g.gl_pathv[i], &tmp)) { 627efcad6b7SDag-Erling Smørgrav err = -1; 628efcad6b7SDag-Erling Smørgrav goto out; 629efcad6b7SDag-Erling Smørgrav } 630efcad6b7SDag-Erling Smørgrav 631efcad6b7SDag-Erling Smørgrav if (g.gl_matchc == 1 && tmp_dst) { 632efcad6b7SDag-Erling Smørgrav /* If directory specified, append filename */ 633efcad6b7SDag-Erling Smørgrav if (remote_is_dir(conn, tmp_dst)) { 634efcad6b7SDag-Erling Smørgrav if (infer_path(g.gl_pathv[0], &tmp)) { 635efcad6b7SDag-Erling Smørgrav err = 1; 636efcad6b7SDag-Erling Smørgrav goto out; 637efcad6b7SDag-Erling Smørgrav } 638efcad6b7SDag-Erling Smørgrav abs_dst = path_append(tmp_dst, tmp); 639efcad6b7SDag-Erling Smørgrav xfree(tmp); 640efcad6b7SDag-Erling Smørgrav } else 641efcad6b7SDag-Erling Smørgrav abs_dst = xstrdup(tmp_dst); 642efcad6b7SDag-Erling Smørgrav 643efcad6b7SDag-Erling Smørgrav } else if (tmp_dst) { 644efcad6b7SDag-Erling Smørgrav abs_dst = path_append(tmp_dst, tmp); 645efcad6b7SDag-Erling Smørgrav xfree(tmp); 646efcad6b7SDag-Erling Smørgrav } else 647efcad6b7SDag-Erling Smørgrav abs_dst = make_absolute(tmp, pwd); 648efcad6b7SDag-Erling Smørgrav 649efcad6b7SDag-Erling Smørgrav printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 650efcad6b7SDag-Erling Smørgrav if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 651efcad6b7SDag-Erling Smørgrav err = -1; 652efcad6b7SDag-Erling Smørgrav } 653efcad6b7SDag-Erling Smørgrav 654efcad6b7SDag-Erling Smørgrav out: 655efcad6b7SDag-Erling Smørgrav if (abs_dst) 656efcad6b7SDag-Erling Smørgrav xfree(abs_dst); 657efcad6b7SDag-Erling Smørgrav if (tmp_dst) 658efcad6b7SDag-Erling Smørgrav xfree(tmp_dst); 659efcad6b7SDag-Erling Smørgrav globfree(&g); 660efcad6b7SDag-Erling Smørgrav return(err); 661efcad6b7SDag-Erling Smørgrav } 662efcad6b7SDag-Erling Smørgrav 663efcad6b7SDag-Erling Smørgrav static int 664efcad6b7SDag-Erling Smørgrav sdirent_comp(const void *aa, const void *bb) 665efcad6b7SDag-Erling Smørgrav { 666efcad6b7SDag-Erling Smørgrav SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; 667efcad6b7SDag-Erling Smørgrav SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; 668d74d50a8SDag-Erling Smørgrav int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; 669efcad6b7SDag-Erling Smørgrav 670d74d50a8SDag-Erling Smørgrav #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) 671d74d50a8SDag-Erling Smørgrav if (sort_flag & LS_NAME_SORT) 672d74d50a8SDag-Erling Smørgrav return (rmul * strcmp(a->filename, b->filename)); 673d74d50a8SDag-Erling Smørgrav else if (sort_flag & LS_TIME_SORT) 674d74d50a8SDag-Erling Smørgrav return (rmul * NCMP(a->a.mtime, b->a.mtime)); 675d74d50a8SDag-Erling Smørgrav else if (sort_flag & LS_SIZE_SORT) 676d74d50a8SDag-Erling Smørgrav return (rmul * NCMP(a->a.size, b->a.size)); 677d74d50a8SDag-Erling Smørgrav 678d74d50a8SDag-Erling Smørgrav fatal("Unknown ls sort type"); 679efcad6b7SDag-Erling Smørgrav } 680efcad6b7SDag-Erling Smørgrav 681efcad6b7SDag-Erling Smørgrav /* sftp ls.1 replacement for directories */ 682efcad6b7SDag-Erling Smørgrav static int 683efcad6b7SDag-Erling Smørgrav do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) 684efcad6b7SDag-Erling Smørgrav { 685043840dfSDag-Erling Smørgrav int n; 686043840dfSDag-Erling Smørgrav u_int c = 1, colspace = 0, columns = 1; 687efcad6b7SDag-Erling Smørgrav SFTP_DIRENT **d; 688efcad6b7SDag-Erling Smørgrav 689efcad6b7SDag-Erling Smørgrav if ((n = do_readdir(conn, path, &d)) != 0) 690efcad6b7SDag-Erling Smørgrav return (n); 691efcad6b7SDag-Erling Smørgrav 692d74d50a8SDag-Erling Smørgrav if (!(lflag & LS_SHORT_VIEW)) { 693043840dfSDag-Erling Smørgrav u_int m = 0, width = 80; 694efcad6b7SDag-Erling Smørgrav struct winsize ws; 695efcad6b7SDag-Erling Smørgrav char *tmp; 696efcad6b7SDag-Erling Smørgrav 697efcad6b7SDag-Erling Smørgrav /* Count entries for sort and find longest filename */ 698d74d50a8SDag-Erling Smørgrav for (n = 0; d[n] != NULL; n++) { 699d74d50a8SDag-Erling Smørgrav if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL)) 700efcad6b7SDag-Erling Smørgrav m = MAX(m, strlen(d[n]->filename)); 701d74d50a8SDag-Erling Smørgrav } 702efcad6b7SDag-Erling Smørgrav 703efcad6b7SDag-Erling Smørgrav /* Add any subpath that also needs to be counted */ 704efcad6b7SDag-Erling Smørgrav tmp = path_strip(path, strip_path); 705efcad6b7SDag-Erling Smørgrav m += strlen(tmp); 706efcad6b7SDag-Erling Smørgrav xfree(tmp); 707efcad6b7SDag-Erling Smørgrav 708efcad6b7SDag-Erling Smørgrav if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 709efcad6b7SDag-Erling Smørgrav width = ws.ws_col; 710efcad6b7SDag-Erling Smørgrav 711efcad6b7SDag-Erling Smørgrav columns = width / (m + 2); 712efcad6b7SDag-Erling Smørgrav columns = MAX(columns, 1); 713efcad6b7SDag-Erling Smørgrav colspace = width / columns; 714efcad6b7SDag-Erling Smørgrav colspace = MIN(colspace, width); 715efcad6b7SDag-Erling Smørgrav } 716efcad6b7SDag-Erling Smørgrav 717d74d50a8SDag-Erling Smørgrav if (lflag & SORT_FLAGS) { 718021d409fSDag-Erling Smørgrav for (n = 0; d[n] != NULL; n++) 719021d409fSDag-Erling Smørgrav ; /* count entries */ 720d74d50a8SDag-Erling Smørgrav sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); 721efcad6b7SDag-Erling Smørgrav qsort(d, n, sizeof(*d), sdirent_comp); 722d74d50a8SDag-Erling Smørgrav } 723efcad6b7SDag-Erling Smørgrav 724d74d50a8SDag-Erling Smørgrav for (n = 0; d[n] != NULL && !interrupted; n++) { 725efcad6b7SDag-Erling Smørgrav char *tmp, *fname; 726efcad6b7SDag-Erling Smørgrav 727d74d50a8SDag-Erling Smørgrav if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL)) 728d74d50a8SDag-Erling Smørgrav continue; 729d74d50a8SDag-Erling Smørgrav 730efcad6b7SDag-Erling Smørgrav tmp = path_append(path, d[n]->filename); 731efcad6b7SDag-Erling Smørgrav fname = path_strip(tmp, strip_path); 732efcad6b7SDag-Erling Smørgrav xfree(tmp); 733efcad6b7SDag-Erling Smørgrav 734d74d50a8SDag-Erling Smørgrav if (lflag & LS_LONG_VIEW) { 735d74d50a8SDag-Erling Smørgrav if (lflag & LS_NUMERIC_VIEW) { 736efcad6b7SDag-Erling Smørgrav char *lname; 737efcad6b7SDag-Erling Smørgrav struct stat sb; 738efcad6b7SDag-Erling Smørgrav 739efcad6b7SDag-Erling Smørgrav memset(&sb, 0, sizeof(sb)); 740efcad6b7SDag-Erling Smørgrav attrib_to_stat(&d[n]->a, &sb); 741efcad6b7SDag-Erling Smørgrav lname = ls_file(fname, &sb, 1); 742efcad6b7SDag-Erling Smørgrav printf("%s\n", lname); 743efcad6b7SDag-Erling Smørgrav xfree(lname); 744d74d50a8SDag-Erling Smørgrav } else 745d74d50a8SDag-Erling Smørgrav printf("%s\n", d[n]->longname); 746efcad6b7SDag-Erling Smørgrav } else { 747efcad6b7SDag-Erling Smørgrav printf("%-*s", colspace, fname); 748efcad6b7SDag-Erling Smørgrav if (c >= columns) { 749efcad6b7SDag-Erling Smørgrav printf("\n"); 750efcad6b7SDag-Erling Smørgrav c = 1; 751efcad6b7SDag-Erling Smørgrav } else 752efcad6b7SDag-Erling Smørgrav c++; 753efcad6b7SDag-Erling Smørgrav } 754efcad6b7SDag-Erling Smørgrav 755efcad6b7SDag-Erling Smørgrav xfree(fname); 756efcad6b7SDag-Erling Smørgrav } 757efcad6b7SDag-Erling Smørgrav 758d74d50a8SDag-Erling Smørgrav if (!(lflag & LS_LONG_VIEW) && (c != 1)) 759efcad6b7SDag-Erling Smørgrav printf("\n"); 760efcad6b7SDag-Erling Smørgrav 761efcad6b7SDag-Erling Smørgrav free_sftp_dirents(d); 762efcad6b7SDag-Erling Smørgrav return (0); 763efcad6b7SDag-Erling Smørgrav } 764efcad6b7SDag-Erling Smørgrav 765efcad6b7SDag-Erling Smørgrav /* sftp ls.1 replacement which handles path globs */ 766efcad6b7SDag-Erling Smørgrav static int 767efcad6b7SDag-Erling Smørgrav do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, 768efcad6b7SDag-Erling Smørgrav int lflag) 769efcad6b7SDag-Erling Smørgrav { 770efcad6b7SDag-Erling Smørgrav glob_t g; 771043840dfSDag-Erling Smørgrav u_int i, c = 1, colspace = 0, columns = 1; 7725e8dbd04SDag-Erling Smørgrav Attrib *a = NULL; 773efcad6b7SDag-Erling Smørgrav 774efcad6b7SDag-Erling Smørgrav memset(&g, 0, sizeof(g)); 775efcad6b7SDag-Erling Smørgrav 776efcad6b7SDag-Erling Smørgrav if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, 7775e8dbd04SDag-Erling Smørgrav NULL, &g) || (g.gl_pathc && !g.gl_matchc)) { 7785e8dbd04SDag-Erling Smørgrav if (g.gl_pathc) 7795e8dbd04SDag-Erling Smørgrav globfree(&g); 780efcad6b7SDag-Erling Smørgrav error("Can't ls: \"%s\" not found", path); 781efcad6b7SDag-Erling Smørgrav return (-1); 782efcad6b7SDag-Erling Smørgrav } 783efcad6b7SDag-Erling Smørgrav 784d74d50a8SDag-Erling Smørgrav if (interrupted) 785d74d50a8SDag-Erling Smørgrav goto out; 786d74d50a8SDag-Erling Smørgrav 787efcad6b7SDag-Erling Smørgrav /* 7885e8dbd04SDag-Erling Smørgrav * If the glob returns a single match and it is a directory, 7895e8dbd04SDag-Erling Smørgrav * then just list its contents. 790efcad6b7SDag-Erling Smørgrav */ 7915e8dbd04SDag-Erling Smørgrav if (g.gl_matchc == 1) { 7925e8dbd04SDag-Erling Smørgrav if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) { 793efcad6b7SDag-Erling Smørgrav globfree(&g); 794efcad6b7SDag-Erling Smørgrav return (-1); 795efcad6b7SDag-Erling Smørgrav } 796efcad6b7SDag-Erling Smørgrav if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 797efcad6b7SDag-Erling Smørgrav S_ISDIR(a->perm)) { 7985e8dbd04SDag-Erling Smørgrav int err; 7995e8dbd04SDag-Erling Smørgrav 8005e8dbd04SDag-Erling Smørgrav err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); 801efcad6b7SDag-Erling Smørgrav globfree(&g); 8025e8dbd04SDag-Erling Smørgrav return (err); 803efcad6b7SDag-Erling Smørgrav } 804efcad6b7SDag-Erling Smørgrav } 805efcad6b7SDag-Erling Smørgrav 806d74d50a8SDag-Erling Smørgrav if (!(lflag & LS_SHORT_VIEW)) { 807043840dfSDag-Erling Smørgrav u_int m = 0, width = 80; 808efcad6b7SDag-Erling Smørgrav struct winsize ws; 809efcad6b7SDag-Erling Smørgrav 810efcad6b7SDag-Erling Smørgrav /* Count entries for sort and find longest filename */ 811efcad6b7SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i]; i++) 812efcad6b7SDag-Erling Smørgrav m = MAX(m, strlen(g.gl_pathv[i])); 813efcad6b7SDag-Erling Smørgrav 814efcad6b7SDag-Erling Smørgrav if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 815efcad6b7SDag-Erling Smørgrav width = ws.ws_col; 816efcad6b7SDag-Erling Smørgrav 817efcad6b7SDag-Erling Smørgrav columns = width / (m + 2); 818efcad6b7SDag-Erling Smørgrav columns = MAX(columns, 1); 819efcad6b7SDag-Erling Smørgrav colspace = width / columns; 820efcad6b7SDag-Erling Smørgrav } 821efcad6b7SDag-Erling Smørgrav 8225e8dbd04SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) { 823efcad6b7SDag-Erling Smørgrav char *fname; 824efcad6b7SDag-Erling Smørgrav 825efcad6b7SDag-Erling Smørgrav fname = path_strip(g.gl_pathv[i], strip_path); 826efcad6b7SDag-Erling Smørgrav 827d74d50a8SDag-Erling Smørgrav if (lflag & LS_LONG_VIEW) { 828efcad6b7SDag-Erling Smørgrav char *lname; 829efcad6b7SDag-Erling Smørgrav struct stat sb; 830efcad6b7SDag-Erling Smørgrav 831efcad6b7SDag-Erling Smørgrav /* 832efcad6b7SDag-Erling Smørgrav * XXX: this is slow - 1 roundtrip per path 833efcad6b7SDag-Erling Smørgrav * A solution to this is to fork glob() and 834efcad6b7SDag-Erling Smørgrav * build a sftp specific version which keeps the 835efcad6b7SDag-Erling Smørgrav * attribs (which currently get thrown away) 836efcad6b7SDag-Erling Smørgrav * that the server returns as well as the filenames. 837efcad6b7SDag-Erling Smørgrav */ 838efcad6b7SDag-Erling Smørgrav memset(&sb, 0, sizeof(sb)); 8395e8dbd04SDag-Erling Smørgrav if (a == NULL) 840efcad6b7SDag-Erling Smørgrav a = do_lstat(conn, g.gl_pathv[i], 1); 841efcad6b7SDag-Erling Smørgrav if (a != NULL) 842efcad6b7SDag-Erling Smørgrav attrib_to_stat(a, &sb); 843efcad6b7SDag-Erling Smørgrav lname = ls_file(fname, &sb, 1); 844efcad6b7SDag-Erling Smørgrav printf("%s\n", lname); 845efcad6b7SDag-Erling Smørgrav xfree(lname); 846efcad6b7SDag-Erling Smørgrav } else { 847efcad6b7SDag-Erling Smørgrav printf("%-*s", colspace, fname); 848efcad6b7SDag-Erling Smørgrav if (c >= columns) { 849efcad6b7SDag-Erling Smørgrav printf("\n"); 850efcad6b7SDag-Erling Smørgrav c = 1; 851efcad6b7SDag-Erling Smørgrav } else 852efcad6b7SDag-Erling Smørgrav c++; 853efcad6b7SDag-Erling Smørgrav } 854efcad6b7SDag-Erling Smørgrav xfree(fname); 855efcad6b7SDag-Erling Smørgrav } 856efcad6b7SDag-Erling Smørgrav 857d74d50a8SDag-Erling Smørgrav if (!(lflag & LS_LONG_VIEW) && (c != 1)) 858efcad6b7SDag-Erling Smørgrav printf("\n"); 859efcad6b7SDag-Erling Smørgrav 860d74d50a8SDag-Erling Smørgrav out: 861efcad6b7SDag-Erling Smørgrav if (g.gl_pathc) 862efcad6b7SDag-Erling Smørgrav globfree(&g); 863efcad6b7SDag-Erling Smørgrav 864efcad6b7SDag-Erling Smørgrav return (0); 865efcad6b7SDag-Erling Smørgrav } 866efcad6b7SDag-Erling Smørgrav 867efcad6b7SDag-Erling Smørgrav static int 868efcad6b7SDag-Erling Smørgrav parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, 869efcad6b7SDag-Erling Smørgrav unsigned long *n_arg, char **path1, char **path2) 870efcad6b7SDag-Erling Smørgrav { 871efcad6b7SDag-Erling Smørgrav const char *cmd, *cp = *cpp; 872efcad6b7SDag-Erling Smørgrav char *cp2; 873efcad6b7SDag-Erling Smørgrav int base = 0; 874efcad6b7SDag-Erling Smørgrav long l; 875efcad6b7SDag-Erling Smørgrav int i, cmdnum; 876efcad6b7SDag-Erling Smørgrav 877efcad6b7SDag-Erling Smørgrav /* Skip leading whitespace */ 878efcad6b7SDag-Erling Smørgrav cp = cp + strspn(cp, WHITESPACE); 879efcad6b7SDag-Erling Smørgrav 880efcad6b7SDag-Erling Smørgrav /* Ignore blank lines and lines which begin with comment '#' char */ 881efcad6b7SDag-Erling Smørgrav if (*cp == '\0' || *cp == '#') 882efcad6b7SDag-Erling Smørgrav return (0); 883efcad6b7SDag-Erling Smørgrav 884efcad6b7SDag-Erling Smørgrav /* Check for leading '-' (disable error processing) */ 885efcad6b7SDag-Erling Smørgrav *iflag = 0; 886efcad6b7SDag-Erling Smørgrav if (*cp == '-') { 887efcad6b7SDag-Erling Smørgrav *iflag = 1; 888efcad6b7SDag-Erling Smørgrav cp++; 889efcad6b7SDag-Erling Smørgrav } 890efcad6b7SDag-Erling Smørgrav 891efcad6b7SDag-Erling Smørgrav /* Figure out which command we have */ 892efcad6b7SDag-Erling Smørgrav for (i = 0; cmds[i].c; i++) { 893efcad6b7SDag-Erling Smørgrav int cmdlen = strlen(cmds[i].c); 894efcad6b7SDag-Erling Smørgrav 895efcad6b7SDag-Erling Smørgrav /* Check for command followed by whitespace */ 896efcad6b7SDag-Erling Smørgrav if (!strncasecmp(cp, cmds[i].c, cmdlen) && 897efcad6b7SDag-Erling Smørgrav strchr(WHITESPACE, cp[cmdlen])) { 898efcad6b7SDag-Erling Smørgrav cp += cmdlen; 899efcad6b7SDag-Erling Smørgrav cp = cp + strspn(cp, WHITESPACE); 900efcad6b7SDag-Erling Smørgrav break; 901efcad6b7SDag-Erling Smørgrav } 902efcad6b7SDag-Erling Smørgrav } 903efcad6b7SDag-Erling Smørgrav cmdnum = cmds[i].n; 904efcad6b7SDag-Erling Smørgrav cmd = cmds[i].c; 905efcad6b7SDag-Erling Smørgrav 906efcad6b7SDag-Erling Smørgrav /* Special case */ 907efcad6b7SDag-Erling Smørgrav if (*cp == '!') { 908efcad6b7SDag-Erling Smørgrav cp++; 909efcad6b7SDag-Erling Smørgrav cmdnum = I_SHELL; 910efcad6b7SDag-Erling Smørgrav } else if (cmdnum == -1) { 911efcad6b7SDag-Erling Smørgrav error("Invalid command."); 912efcad6b7SDag-Erling Smørgrav return (-1); 913efcad6b7SDag-Erling Smørgrav } 914efcad6b7SDag-Erling Smørgrav 915efcad6b7SDag-Erling Smørgrav /* Get arguments and parse flags */ 916efcad6b7SDag-Erling Smørgrav *lflag = *pflag = *n_arg = 0; 917efcad6b7SDag-Erling Smørgrav *path1 = *path2 = NULL; 918efcad6b7SDag-Erling Smørgrav switch (cmdnum) { 919efcad6b7SDag-Erling Smørgrav case I_GET: 920efcad6b7SDag-Erling Smørgrav case I_PUT: 921efcad6b7SDag-Erling Smørgrav if (parse_getput_flags(&cp, pflag)) 922efcad6b7SDag-Erling Smørgrav return(-1); 923efcad6b7SDag-Erling Smørgrav /* Get first pathname (mandatory) */ 924efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 925efcad6b7SDag-Erling Smørgrav return(-1); 926efcad6b7SDag-Erling Smørgrav if (*path1 == NULL) { 927efcad6b7SDag-Erling Smørgrav error("You must specify at least one path after a " 928efcad6b7SDag-Erling Smørgrav "%s command.", cmd); 929efcad6b7SDag-Erling Smørgrav return(-1); 930efcad6b7SDag-Erling Smørgrav } 931efcad6b7SDag-Erling Smørgrav /* Try to get second pathname (optional) */ 932efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path2)) 933efcad6b7SDag-Erling Smørgrav return(-1); 934efcad6b7SDag-Erling Smørgrav break; 935efcad6b7SDag-Erling Smørgrav case I_RENAME: 936efcad6b7SDag-Erling Smørgrav case I_SYMLINK: 937efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 938efcad6b7SDag-Erling Smørgrav return(-1); 939efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path2)) 940efcad6b7SDag-Erling Smørgrav return(-1); 941efcad6b7SDag-Erling Smørgrav if (!*path1 || !*path2) { 942efcad6b7SDag-Erling Smørgrav error("You must specify two paths after a %s " 943efcad6b7SDag-Erling Smørgrav "command.", cmd); 944efcad6b7SDag-Erling Smørgrav return(-1); 945efcad6b7SDag-Erling Smørgrav } 946efcad6b7SDag-Erling Smørgrav break; 947efcad6b7SDag-Erling Smørgrav case I_RM: 948efcad6b7SDag-Erling Smørgrav case I_MKDIR: 949efcad6b7SDag-Erling Smørgrav case I_RMDIR: 950efcad6b7SDag-Erling Smørgrav case I_CHDIR: 951efcad6b7SDag-Erling Smørgrav case I_LCHDIR: 952efcad6b7SDag-Erling Smørgrav case I_LMKDIR: 953efcad6b7SDag-Erling Smørgrav /* Get pathname (mandatory) */ 954efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 955efcad6b7SDag-Erling Smørgrav return(-1); 956efcad6b7SDag-Erling Smørgrav if (*path1 == NULL) { 957efcad6b7SDag-Erling Smørgrav error("You must specify a path after a %s command.", 958efcad6b7SDag-Erling Smørgrav cmd); 959efcad6b7SDag-Erling Smørgrav return(-1); 960efcad6b7SDag-Erling Smørgrav } 961efcad6b7SDag-Erling Smørgrav break; 962efcad6b7SDag-Erling Smørgrav case I_LS: 963efcad6b7SDag-Erling Smørgrav if (parse_ls_flags(&cp, lflag)) 964efcad6b7SDag-Erling Smørgrav return(-1); 965efcad6b7SDag-Erling Smørgrav /* Path is optional */ 966efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 967efcad6b7SDag-Erling Smørgrav return(-1); 968efcad6b7SDag-Erling Smørgrav break; 969efcad6b7SDag-Erling Smørgrav case I_LLS: 970efcad6b7SDag-Erling Smørgrav case I_SHELL: 971efcad6b7SDag-Erling Smørgrav /* Uses the rest of the line */ 972efcad6b7SDag-Erling Smørgrav break; 973efcad6b7SDag-Erling Smørgrav case I_LUMASK: 974efcad6b7SDag-Erling Smørgrav base = 8; 975efcad6b7SDag-Erling Smørgrav case I_CHMOD: 976efcad6b7SDag-Erling Smørgrav base = 8; 977efcad6b7SDag-Erling Smørgrav case I_CHOWN: 978efcad6b7SDag-Erling Smørgrav case I_CHGRP: 979efcad6b7SDag-Erling Smørgrav /* Get numeric arg (mandatory) */ 98092eb0aa1SDag-Erling Smørgrav errno = 0; 981efcad6b7SDag-Erling Smørgrav l = strtol(cp, &cp2, base); 982efcad6b7SDag-Erling Smørgrav if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) && 983efcad6b7SDag-Erling Smørgrav errno == ERANGE) || l < 0) { 984efcad6b7SDag-Erling Smørgrav error("You must supply a numeric argument " 985efcad6b7SDag-Erling Smørgrav "to the %s command.", cmd); 986efcad6b7SDag-Erling Smørgrav return(-1); 987efcad6b7SDag-Erling Smørgrav } 988efcad6b7SDag-Erling Smørgrav cp = cp2; 989efcad6b7SDag-Erling Smørgrav *n_arg = l; 990efcad6b7SDag-Erling Smørgrav if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp)) 991efcad6b7SDag-Erling Smørgrav break; 992efcad6b7SDag-Erling Smørgrav if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) { 993efcad6b7SDag-Erling Smørgrav error("You must supply a numeric argument " 994efcad6b7SDag-Erling Smørgrav "to the %s command.", cmd); 995efcad6b7SDag-Erling Smørgrav return(-1); 996efcad6b7SDag-Erling Smørgrav } 997efcad6b7SDag-Erling Smørgrav cp += strspn(cp, WHITESPACE); 998efcad6b7SDag-Erling Smørgrav 999efcad6b7SDag-Erling Smørgrav /* Get pathname (mandatory) */ 1000efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 1001efcad6b7SDag-Erling Smørgrav return(-1); 1002efcad6b7SDag-Erling Smørgrav if (*path1 == NULL) { 1003efcad6b7SDag-Erling Smørgrav error("You must specify a path after a %s command.", 1004efcad6b7SDag-Erling Smørgrav cmd); 1005efcad6b7SDag-Erling Smørgrav return(-1); 1006efcad6b7SDag-Erling Smørgrav } 1007efcad6b7SDag-Erling Smørgrav break; 1008efcad6b7SDag-Erling Smørgrav case I_QUIT: 1009efcad6b7SDag-Erling Smørgrav case I_PWD: 1010efcad6b7SDag-Erling Smørgrav case I_LPWD: 1011efcad6b7SDag-Erling Smørgrav case I_HELP: 1012efcad6b7SDag-Erling Smørgrav case I_VERSION: 1013efcad6b7SDag-Erling Smørgrav case I_PROGRESS: 1014efcad6b7SDag-Erling Smørgrav break; 1015efcad6b7SDag-Erling Smørgrav default: 1016efcad6b7SDag-Erling Smørgrav fatal("Command not implemented"); 1017efcad6b7SDag-Erling Smørgrav } 1018efcad6b7SDag-Erling Smørgrav 1019efcad6b7SDag-Erling Smørgrav *cpp = cp; 1020efcad6b7SDag-Erling Smørgrav return(cmdnum); 1021efcad6b7SDag-Erling Smørgrav } 1022efcad6b7SDag-Erling Smørgrav 1023efcad6b7SDag-Erling Smørgrav static int 1024efcad6b7SDag-Erling Smørgrav parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 1025efcad6b7SDag-Erling Smørgrav int err_abort) 1026efcad6b7SDag-Erling Smørgrav { 1027efcad6b7SDag-Erling Smørgrav char *path1, *path2, *tmp; 1028efcad6b7SDag-Erling Smørgrav int pflag, lflag, iflag, cmdnum, i; 1029efcad6b7SDag-Erling Smørgrav unsigned long n_arg; 1030efcad6b7SDag-Erling Smørgrav Attrib a, *aa; 1031efcad6b7SDag-Erling Smørgrav char path_buf[MAXPATHLEN]; 1032efcad6b7SDag-Erling Smørgrav int err = 0; 1033efcad6b7SDag-Erling Smørgrav glob_t g; 1034efcad6b7SDag-Erling Smørgrav 1035efcad6b7SDag-Erling Smørgrav path1 = path2 = NULL; 1036efcad6b7SDag-Erling Smørgrav cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg, 1037efcad6b7SDag-Erling Smørgrav &path1, &path2); 1038efcad6b7SDag-Erling Smørgrav 1039efcad6b7SDag-Erling Smørgrav if (iflag != 0) 1040efcad6b7SDag-Erling Smørgrav err_abort = 0; 1041efcad6b7SDag-Erling Smørgrav 1042efcad6b7SDag-Erling Smørgrav memset(&g, 0, sizeof(g)); 1043efcad6b7SDag-Erling Smørgrav 1044efcad6b7SDag-Erling Smørgrav /* Perform command */ 1045efcad6b7SDag-Erling Smørgrav switch (cmdnum) { 1046efcad6b7SDag-Erling Smørgrav case 0: 1047efcad6b7SDag-Erling Smørgrav /* Blank line */ 1048efcad6b7SDag-Erling Smørgrav break; 1049efcad6b7SDag-Erling Smørgrav case -1: 1050efcad6b7SDag-Erling Smørgrav /* Unrecognized command */ 1051efcad6b7SDag-Erling Smørgrav err = -1; 1052efcad6b7SDag-Erling Smørgrav break; 1053efcad6b7SDag-Erling Smørgrav case I_GET: 1054efcad6b7SDag-Erling Smørgrav err = process_get(conn, path1, path2, *pwd, pflag); 1055efcad6b7SDag-Erling Smørgrav break; 1056efcad6b7SDag-Erling Smørgrav case I_PUT: 1057efcad6b7SDag-Erling Smørgrav err = process_put(conn, path1, path2, *pwd, pflag); 1058efcad6b7SDag-Erling Smørgrav break; 1059efcad6b7SDag-Erling Smørgrav case I_RENAME: 1060efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1061efcad6b7SDag-Erling Smørgrav path2 = make_absolute(path2, *pwd); 1062efcad6b7SDag-Erling Smørgrav err = do_rename(conn, path1, path2); 1063efcad6b7SDag-Erling Smørgrav break; 1064efcad6b7SDag-Erling Smørgrav case I_SYMLINK: 1065efcad6b7SDag-Erling Smørgrav path2 = make_absolute(path2, *pwd); 1066efcad6b7SDag-Erling Smørgrav err = do_symlink(conn, path1, path2); 1067efcad6b7SDag-Erling Smørgrav break; 1068efcad6b7SDag-Erling Smørgrav case I_RM: 1069efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1070efcad6b7SDag-Erling Smørgrav remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1071d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1072efcad6b7SDag-Erling Smørgrav printf("Removing %s\n", g.gl_pathv[i]); 1073efcad6b7SDag-Erling Smørgrav err = do_rm(conn, g.gl_pathv[i]); 1074efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1075efcad6b7SDag-Erling Smørgrav break; 1076efcad6b7SDag-Erling Smørgrav } 1077efcad6b7SDag-Erling Smørgrav break; 1078efcad6b7SDag-Erling Smørgrav case I_MKDIR: 1079efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1080efcad6b7SDag-Erling Smørgrav attrib_clear(&a); 1081efcad6b7SDag-Erling Smørgrav a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1082efcad6b7SDag-Erling Smørgrav a.perm = 0777; 1083efcad6b7SDag-Erling Smørgrav err = do_mkdir(conn, path1, &a); 1084efcad6b7SDag-Erling Smørgrav break; 1085efcad6b7SDag-Erling Smørgrav case I_RMDIR: 1086efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1087efcad6b7SDag-Erling Smørgrav err = do_rmdir(conn, path1); 1088efcad6b7SDag-Erling Smørgrav break; 1089efcad6b7SDag-Erling Smørgrav case I_CHDIR: 1090efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1091efcad6b7SDag-Erling Smørgrav if ((tmp = do_realpath(conn, path1)) == NULL) { 1092efcad6b7SDag-Erling Smørgrav err = 1; 1093efcad6b7SDag-Erling Smørgrav break; 1094efcad6b7SDag-Erling Smørgrav } 1095efcad6b7SDag-Erling Smørgrav if ((aa = do_stat(conn, tmp, 0)) == NULL) { 1096efcad6b7SDag-Erling Smørgrav xfree(tmp); 1097efcad6b7SDag-Erling Smørgrav err = 1; 1098efcad6b7SDag-Erling Smørgrav break; 1099efcad6b7SDag-Erling Smørgrav } 1100efcad6b7SDag-Erling Smørgrav if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { 1101efcad6b7SDag-Erling Smørgrav error("Can't change directory: Can't check target"); 1102efcad6b7SDag-Erling Smørgrav xfree(tmp); 1103efcad6b7SDag-Erling Smørgrav err = 1; 1104efcad6b7SDag-Erling Smørgrav break; 1105efcad6b7SDag-Erling Smørgrav } 1106efcad6b7SDag-Erling Smørgrav if (!S_ISDIR(aa->perm)) { 1107efcad6b7SDag-Erling Smørgrav error("Can't change directory: \"%s\" is not " 1108efcad6b7SDag-Erling Smørgrav "a directory", tmp); 1109efcad6b7SDag-Erling Smørgrav xfree(tmp); 1110efcad6b7SDag-Erling Smørgrav err = 1; 1111efcad6b7SDag-Erling Smørgrav break; 1112efcad6b7SDag-Erling Smørgrav } 1113efcad6b7SDag-Erling Smørgrav xfree(*pwd); 1114efcad6b7SDag-Erling Smørgrav *pwd = tmp; 1115efcad6b7SDag-Erling Smørgrav break; 1116efcad6b7SDag-Erling Smørgrav case I_LS: 1117efcad6b7SDag-Erling Smørgrav if (!path1) { 1118efcad6b7SDag-Erling Smørgrav do_globbed_ls(conn, *pwd, *pwd, lflag); 1119efcad6b7SDag-Erling Smørgrav break; 1120efcad6b7SDag-Erling Smørgrav } 1121efcad6b7SDag-Erling Smørgrav 1122efcad6b7SDag-Erling Smørgrav /* Strip pwd off beginning of non-absolute paths */ 1123efcad6b7SDag-Erling Smørgrav tmp = NULL; 1124efcad6b7SDag-Erling Smørgrav if (*path1 != '/') 1125efcad6b7SDag-Erling Smørgrav tmp = *pwd; 1126efcad6b7SDag-Erling Smørgrav 1127efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1128efcad6b7SDag-Erling Smørgrav err = do_globbed_ls(conn, path1, tmp, lflag); 1129efcad6b7SDag-Erling Smørgrav break; 1130efcad6b7SDag-Erling Smørgrav case I_LCHDIR: 1131efcad6b7SDag-Erling Smørgrav if (chdir(path1) == -1) { 1132efcad6b7SDag-Erling Smørgrav error("Couldn't change local directory to " 1133efcad6b7SDag-Erling Smørgrav "\"%s\": %s", path1, strerror(errno)); 1134efcad6b7SDag-Erling Smørgrav err = 1; 1135efcad6b7SDag-Erling Smørgrav } 1136efcad6b7SDag-Erling Smørgrav break; 1137efcad6b7SDag-Erling Smørgrav case I_LMKDIR: 1138efcad6b7SDag-Erling Smørgrav if (mkdir(path1, 0777) == -1) { 1139efcad6b7SDag-Erling Smørgrav error("Couldn't create local directory " 1140efcad6b7SDag-Erling Smørgrav "\"%s\": %s", path1, strerror(errno)); 1141efcad6b7SDag-Erling Smørgrav err = 1; 1142efcad6b7SDag-Erling Smørgrav } 1143efcad6b7SDag-Erling Smørgrav break; 1144efcad6b7SDag-Erling Smørgrav case I_LLS: 1145efcad6b7SDag-Erling Smørgrav local_do_ls(cmd); 1146efcad6b7SDag-Erling Smørgrav break; 1147efcad6b7SDag-Erling Smørgrav case I_SHELL: 1148efcad6b7SDag-Erling Smørgrav local_do_shell(cmd); 1149efcad6b7SDag-Erling Smørgrav break; 1150efcad6b7SDag-Erling Smørgrav case I_LUMASK: 1151efcad6b7SDag-Erling Smørgrav umask(n_arg); 1152efcad6b7SDag-Erling Smørgrav printf("Local umask: %03lo\n", n_arg); 1153efcad6b7SDag-Erling Smørgrav break; 1154efcad6b7SDag-Erling Smørgrav case I_CHMOD: 1155efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1156efcad6b7SDag-Erling Smørgrav attrib_clear(&a); 1157efcad6b7SDag-Erling Smørgrav a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1158efcad6b7SDag-Erling Smørgrav a.perm = n_arg; 1159efcad6b7SDag-Erling Smørgrav remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1160d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1161efcad6b7SDag-Erling Smørgrav printf("Changing mode on %s\n", g.gl_pathv[i]); 1162efcad6b7SDag-Erling Smørgrav err = do_setstat(conn, g.gl_pathv[i], &a); 1163efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1164efcad6b7SDag-Erling Smørgrav break; 1165efcad6b7SDag-Erling Smørgrav } 1166efcad6b7SDag-Erling Smørgrav break; 1167efcad6b7SDag-Erling Smørgrav case I_CHOWN: 1168efcad6b7SDag-Erling Smørgrav case I_CHGRP: 1169efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1170efcad6b7SDag-Erling Smørgrav remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1171d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1172efcad6b7SDag-Erling Smørgrav if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1173efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1174efcad6b7SDag-Erling Smørgrav break; 1175efcad6b7SDag-Erling Smørgrav else 1176efcad6b7SDag-Erling Smørgrav continue; 1177efcad6b7SDag-Erling Smørgrav } 1178efcad6b7SDag-Erling Smørgrav if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1179efcad6b7SDag-Erling Smørgrav error("Can't get current ownership of " 1180efcad6b7SDag-Erling Smørgrav "remote file \"%s\"", g.gl_pathv[i]); 1181efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1182efcad6b7SDag-Erling Smørgrav break; 1183efcad6b7SDag-Erling Smørgrav else 1184efcad6b7SDag-Erling Smørgrav continue; 1185efcad6b7SDag-Erling Smørgrav } 1186efcad6b7SDag-Erling Smørgrav aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1187efcad6b7SDag-Erling Smørgrav if (cmdnum == I_CHOWN) { 1188efcad6b7SDag-Erling Smørgrav printf("Changing owner on %s\n", g.gl_pathv[i]); 1189efcad6b7SDag-Erling Smørgrav aa->uid = n_arg; 1190efcad6b7SDag-Erling Smørgrav } else { 1191efcad6b7SDag-Erling Smørgrav printf("Changing group on %s\n", g.gl_pathv[i]); 1192efcad6b7SDag-Erling Smørgrav aa->gid = n_arg; 1193efcad6b7SDag-Erling Smørgrav } 1194efcad6b7SDag-Erling Smørgrav err = do_setstat(conn, g.gl_pathv[i], aa); 1195efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1196efcad6b7SDag-Erling Smørgrav break; 1197efcad6b7SDag-Erling Smørgrav } 1198efcad6b7SDag-Erling Smørgrav break; 1199efcad6b7SDag-Erling Smørgrav case I_PWD: 1200efcad6b7SDag-Erling Smørgrav printf("Remote working directory: %s\n", *pwd); 1201efcad6b7SDag-Erling Smørgrav break; 1202efcad6b7SDag-Erling Smørgrav case I_LPWD: 1203efcad6b7SDag-Erling Smørgrav if (!getcwd(path_buf, sizeof(path_buf))) { 1204efcad6b7SDag-Erling Smørgrav error("Couldn't get local cwd: %s", strerror(errno)); 1205efcad6b7SDag-Erling Smørgrav err = -1; 1206efcad6b7SDag-Erling Smørgrav break; 1207efcad6b7SDag-Erling Smørgrav } 1208efcad6b7SDag-Erling Smørgrav printf("Local working directory: %s\n", path_buf); 1209efcad6b7SDag-Erling Smørgrav break; 1210efcad6b7SDag-Erling Smørgrav case I_QUIT: 1211efcad6b7SDag-Erling Smørgrav /* Processed below */ 1212efcad6b7SDag-Erling Smørgrav break; 1213efcad6b7SDag-Erling Smørgrav case I_HELP: 1214efcad6b7SDag-Erling Smørgrav help(); 1215efcad6b7SDag-Erling Smørgrav break; 1216efcad6b7SDag-Erling Smørgrav case I_VERSION: 1217efcad6b7SDag-Erling Smørgrav printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1218efcad6b7SDag-Erling Smørgrav break; 1219efcad6b7SDag-Erling Smørgrav case I_PROGRESS: 1220efcad6b7SDag-Erling Smørgrav showprogress = !showprogress; 1221efcad6b7SDag-Erling Smørgrav if (showprogress) 1222efcad6b7SDag-Erling Smørgrav printf("Progress meter enabled\n"); 1223efcad6b7SDag-Erling Smørgrav else 1224efcad6b7SDag-Erling Smørgrav printf("Progress meter disabled\n"); 1225efcad6b7SDag-Erling Smørgrav break; 1226efcad6b7SDag-Erling Smørgrav default: 1227efcad6b7SDag-Erling Smørgrav fatal("%d is not implemented", cmdnum); 1228efcad6b7SDag-Erling Smørgrav } 1229efcad6b7SDag-Erling Smørgrav 1230efcad6b7SDag-Erling Smørgrav if (g.gl_pathc) 1231efcad6b7SDag-Erling Smørgrav globfree(&g); 1232efcad6b7SDag-Erling Smørgrav if (path1) 1233efcad6b7SDag-Erling Smørgrav xfree(path1); 1234efcad6b7SDag-Erling Smørgrav if (path2) 1235efcad6b7SDag-Erling Smørgrav xfree(path2); 1236efcad6b7SDag-Erling Smørgrav 1237efcad6b7SDag-Erling Smørgrav /* If an unignored error occurs in batch mode we should abort. */ 1238efcad6b7SDag-Erling Smørgrav if (err_abort && err != 0) 1239efcad6b7SDag-Erling Smørgrav return (-1); 1240efcad6b7SDag-Erling Smørgrav else if (cmdnum == I_QUIT) 1241efcad6b7SDag-Erling Smørgrav return (1); 1242efcad6b7SDag-Erling Smørgrav 1243efcad6b7SDag-Erling Smørgrav return (0); 1244efcad6b7SDag-Erling Smørgrav } 1245efcad6b7SDag-Erling Smørgrav 12465e8dbd04SDag-Erling Smørgrav #ifdef USE_LIBEDIT 12475e8dbd04SDag-Erling Smørgrav static char * 12485e8dbd04SDag-Erling Smørgrav prompt(EditLine *el) 12495e8dbd04SDag-Erling Smørgrav { 12505e8dbd04SDag-Erling Smørgrav return ("sftp> "); 12515e8dbd04SDag-Erling Smørgrav } 12525e8dbd04SDag-Erling Smørgrav #endif 12535e8dbd04SDag-Erling Smørgrav 1254efcad6b7SDag-Erling Smørgrav int 1255efcad6b7SDag-Erling Smørgrav interactive_loop(int fd_in, int fd_out, char *file1, char *file2) 1256efcad6b7SDag-Erling Smørgrav { 1257efcad6b7SDag-Erling Smørgrav char *pwd; 1258efcad6b7SDag-Erling Smørgrav char *dir = NULL; 1259efcad6b7SDag-Erling Smørgrav char cmd[2048]; 1260efcad6b7SDag-Erling Smørgrav struct sftp_conn *conn; 1261043840dfSDag-Erling Smørgrav int err, interactive; 12625e8dbd04SDag-Erling Smørgrav EditLine *el = NULL; 12635e8dbd04SDag-Erling Smørgrav #ifdef USE_LIBEDIT 12645e8dbd04SDag-Erling Smørgrav History *hl = NULL; 12655e8dbd04SDag-Erling Smørgrav HistEvent hev; 12665e8dbd04SDag-Erling Smørgrav extern char *__progname; 12675e8dbd04SDag-Erling Smørgrav 12685e8dbd04SDag-Erling Smørgrav if (!batchmode && isatty(STDIN_FILENO)) { 12695e8dbd04SDag-Erling Smørgrav if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) 12705e8dbd04SDag-Erling Smørgrav fatal("Couldn't initialise editline"); 12715e8dbd04SDag-Erling Smørgrav if ((hl = history_init()) == NULL) 12725e8dbd04SDag-Erling Smørgrav fatal("Couldn't initialise editline history"); 12735e8dbd04SDag-Erling Smørgrav history(hl, &hev, H_SETSIZE, 100); 12745e8dbd04SDag-Erling Smørgrav el_set(el, EL_HIST, history, hl); 12755e8dbd04SDag-Erling Smørgrav 12765e8dbd04SDag-Erling Smørgrav el_set(el, EL_PROMPT, prompt); 12775e8dbd04SDag-Erling Smørgrav el_set(el, EL_EDITOR, "emacs"); 12785e8dbd04SDag-Erling Smørgrav el_set(el, EL_TERMINAL, NULL); 12795e8dbd04SDag-Erling Smørgrav el_set(el, EL_SIGNAL, 1); 12805e8dbd04SDag-Erling Smørgrav el_source(el, NULL); 12815e8dbd04SDag-Erling Smørgrav } 12825e8dbd04SDag-Erling Smørgrav #endif /* USE_LIBEDIT */ 1283efcad6b7SDag-Erling Smørgrav 1284efcad6b7SDag-Erling Smørgrav conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); 1285efcad6b7SDag-Erling Smørgrav if (conn == NULL) 1286efcad6b7SDag-Erling Smørgrav fatal("Couldn't initialise connection to server"); 1287efcad6b7SDag-Erling Smørgrav 1288efcad6b7SDag-Erling Smørgrav pwd = do_realpath(conn, "."); 1289efcad6b7SDag-Erling Smørgrav if (pwd == NULL) 1290efcad6b7SDag-Erling Smørgrav fatal("Need cwd"); 1291efcad6b7SDag-Erling Smørgrav 1292efcad6b7SDag-Erling Smørgrav if (file1 != NULL) { 1293efcad6b7SDag-Erling Smørgrav dir = xstrdup(file1); 1294efcad6b7SDag-Erling Smørgrav dir = make_absolute(dir, pwd); 1295efcad6b7SDag-Erling Smørgrav 1296efcad6b7SDag-Erling Smørgrav if (remote_is_dir(conn, dir) && file2 == NULL) { 1297efcad6b7SDag-Erling Smørgrav printf("Changing to: %s\n", dir); 1298efcad6b7SDag-Erling Smørgrav snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 12995e8dbd04SDag-Erling Smørgrav if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) { 13005e8dbd04SDag-Erling Smørgrav xfree(dir); 13015e8dbd04SDag-Erling Smørgrav xfree(pwd); 1302761efaa7SDag-Erling Smørgrav xfree(conn); 1303efcad6b7SDag-Erling Smørgrav return (-1); 13045e8dbd04SDag-Erling Smørgrav } 1305efcad6b7SDag-Erling Smørgrav } else { 1306efcad6b7SDag-Erling Smørgrav if (file2 == NULL) 1307efcad6b7SDag-Erling Smørgrav snprintf(cmd, sizeof cmd, "get %s", dir); 1308efcad6b7SDag-Erling Smørgrav else 1309efcad6b7SDag-Erling Smørgrav snprintf(cmd, sizeof cmd, "get %s %s", dir, 1310efcad6b7SDag-Erling Smørgrav file2); 1311efcad6b7SDag-Erling Smørgrav 1312efcad6b7SDag-Erling Smørgrav err = parse_dispatch_command(conn, cmd, &pwd, 1); 1313efcad6b7SDag-Erling Smørgrav xfree(dir); 1314efcad6b7SDag-Erling Smørgrav xfree(pwd); 1315761efaa7SDag-Erling Smørgrav xfree(conn); 1316efcad6b7SDag-Erling Smørgrav return (err); 1317efcad6b7SDag-Erling Smørgrav } 1318efcad6b7SDag-Erling Smørgrav xfree(dir); 1319efcad6b7SDag-Erling Smørgrav } 1320efcad6b7SDag-Erling Smørgrav 1321043840dfSDag-Erling Smørgrav #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF) 1322efcad6b7SDag-Erling Smørgrav setvbuf(stdout, NULL, _IOLBF, 0); 1323efcad6b7SDag-Erling Smørgrav setvbuf(infile, NULL, _IOLBF, 0); 1324efcad6b7SDag-Erling Smørgrav #else 1325efcad6b7SDag-Erling Smørgrav setlinebuf(stdout); 1326efcad6b7SDag-Erling Smørgrav setlinebuf(infile); 1327efcad6b7SDag-Erling Smørgrav #endif 1328efcad6b7SDag-Erling Smørgrav 1329043840dfSDag-Erling Smørgrav interactive = !batchmode && isatty(STDIN_FILENO); 1330efcad6b7SDag-Erling Smørgrav err = 0; 1331efcad6b7SDag-Erling Smørgrav for (;;) { 1332efcad6b7SDag-Erling Smørgrav char *cp; 1333efcad6b7SDag-Erling Smørgrav 1334d74d50a8SDag-Erling Smørgrav signal(SIGINT, SIG_IGN); 1335d74d50a8SDag-Erling Smørgrav 13365e8dbd04SDag-Erling Smørgrav if (el == NULL) { 1337043840dfSDag-Erling Smørgrav if (interactive) 1338efcad6b7SDag-Erling Smørgrav printf("sftp> "); 1339efcad6b7SDag-Erling Smørgrav if (fgets(cmd, sizeof(cmd), infile) == NULL) { 1340043840dfSDag-Erling Smørgrav if (interactive) 1341efcad6b7SDag-Erling Smørgrav printf("\n"); 1342efcad6b7SDag-Erling Smørgrav break; 1343efcad6b7SDag-Erling Smørgrav } 1344043840dfSDag-Erling Smørgrav if (!interactive) { /* Echo command */ 1345043840dfSDag-Erling Smørgrav printf("sftp> %s", cmd); 1346043840dfSDag-Erling Smørgrav if (strlen(cmd) > 0 && 1347043840dfSDag-Erling Smørgrav cmd[strlen(cmd) - 1] != '\n') 1348043840dfSDag-Erling Smørgrav printf("\n"); 1349043840dfSDag-Erling Smørgrav } 13505e8dbd04SDag-Erling Smørgrav } else { 13515e8dbd04SDag-Erling Smørgrav #ifdef USE_LIBEDIT 13525e8dbd04SDag-Erling Smørgrav const char *line; 13535e8dbd04SDag-Erling Smørgrav int count = 0; 13545e8dbd04SDag-Erling Smørgrav 1355043840dfSDag-Erling Smørgrav if ((line = el_gets(el, &count)) == NULL || count <= 0) { 1356043840dfSDag-Erling Smørgrav printf("\n"); 13575e8dbd04SDag-Erling Smørgrav break; 1358043840dfSDag-Erling Smørgrav } 13595e8dbd04SDag-Erling Smørgrav history(hl, &hev, H_ENTER, line); 13605e8dbd04SDag-Erling Smørgrav if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { 13615e8dbd04SDag-Erling Smørgrav fprintf(stderr, "Error: input line too long\n"); 13625e8dbd04SDag-Erling Smørgrav continue; 13635e8dbd04SDag-Erling Smørgrav } 13645e8dbd04SDag-Erling Smørgrav #endif /* USE_LIBEDIT */ 13655e8dbd04SDag-Erling Smørgrav } 1366efcad6b7SDag-Erling Smørgrav 1367efcad6b7SDag-Erling Smørgrav cp = strrchr(cmd, '\n'); 1368efcad6b7SDag-Erling Smørgrav if (cp) 1369efcad6b7SDag-Erling Smørgrav *cp = '\0'; 1370efcad6b7SDag-Erling Smørgrav 1371d74d50a8SDag-Erling Smørgrav /* Handle user interrupts gracefully during commands */ 1372d74d50a8SDag-Erling Smørgrav interrupted = 0; 1373d74d50a8SDag-Erling Smørgrav signal(SIGINT, cmd_interrupt); 1374d74d50a8SDag-Erling Smørgrav 1375efcad6b7SDag-Erling Smørgrav err = parse_dispatch_command(conn, cmd, &pwd, batchmode); 1376efcad6b7SDag-Erling Smørgrav if (err != 0) 1377efcad6b7SDag-Erling Smørgrav break; 1378efcad6b7SDag-Erling Smørgrav } 1379efcad6b7SDag-Erling Smørgrav xfree(pwd); 1380761efaa7SDag-Erling Smørgrav xfree(conn); 1381efcad6b7SDag-Erling Smørgrav 1382043840dfSDag-Erling Smørgrav #ifdef USE_LIBEDIT 1383043840dfSDag-Erling Smørgrav if (el != NULL) 1384043840dfSDag-Erling Smørgrav el_end(el); 1385043840dfSDag-Erling Smørgrav #endif /* USE_LIBEDIT */ 1386043840dfSDag-Erling Smørgrav 1387efcad6b7SDag-Erling Smørgrav /* err == 1 signifies normal "quit" exit */ 1388efcad6b7SDag-Erling Smørgrav return (err >= 0 ? 0 : -1); 1389efcad6b7SDag-Erling Smørgrav } 1390d0c8c0bcSDag-Erling Smørgrav 1391ae1f160dSDag-Erling Smørgrav static void 1392d95e11bfSDag-Erling Smørgrav connect_to_server(char *path, char **args, int *in, int *out) 13931e8db6e2SBrian Feldman { 13941e8db6e2SBrian Feldman int c_in, c_out; 1395ee21a45fSDag-Erling Smørgrav 13961e8db6e2SBrian Feldman #ifdef USE_PIPES 13971e8db6e2SBrian Feldman int pin[2], pout[2]; 1398ee21a45fSDag-Erling Smørgrav 13991e8db6e2SBrian Feldman if ((pipe(pin) == -1) || (pipe(pout) == -1)) 14001e8db6e2SBrian Feldman fatal("pipe: %s", strerror(errno)); 14011e8db6e2SBrian Feldman *in = pin[0]; 14021e8db6e2SBrian Feldman *out = pout[1]; 14031e8db6e2SBrian Feldman c_in = pout[0]; 14041e8db6e2SBrian Feldman c_out = pin[1]; 14051e8db6e2SBrian Feldman #else /* USE_PIPES */ 14061e8db6e2SBrian Feldman int inout[2]; 1407ee21a45fSDag-Erling Smørgrav 14081e8db6e2SBrian Feldman if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 14091e8db6e2SBrian Feldman fatal("socketpair: %s", strerror(errno)); 14101e8db6e2SBrian Feldman *in = *out = inout[0]; 14111e8db6e2SBrian Feldman c_in = c_out = inout[1]; 14121e8db6e2SBrian Feldman #endif /* USE_PIPES */ 14131e8db6e2SBrian Feldman 1414d95e11bfSDag-Erling Smørgrav if ((sshpid = fork()) == -1) 14151e8db6e2SBrian Feldman fatal("fork: %s", strerror(errno)); 1416d95e11bfSDag-Erling Smørgrav else if (sshpid == 0) { 14171e8db6e2SBrian Feldman if ((dup2(c_in, STDIN_FILENO) == -1) || 14181e8db6e2SBrian Feldman (dup2(c_out, STDOUT_FILENO) == -1)) { 14191e8db6e2SBrian Feldman fprintf(stderr, "dup2: %s\n", strerror(errno)); 1420d74d50a8SDag-Erling Smørgrav _exit(1); 14211e8db6e2SBrian Feldman } 14221e8db6e2SBrian Feldman close(*in); 14231e8db6e2SBrian Feldman close(*out); 14241e8db6e2SBrian Feldman close(c_in); 14251e8db6e2SBrian Feldman close(c_out); 1426d74d50a8SDag-Erling Smørgrav 1427d74d50a8SDag-Erling Smørgrav /* 1428d74d50a8SDag-Erling Smørgrav * The underlying ssh is in the same process group, so we must 1429d74d50a8SDag-Erling Smørgrav * ignore SIGINT if we want to gracefully abort commands, 1430d74d50a8SDag-Erling Smørgrav * otherwise the signal will make it to the ssh process and 1431d74d50a8SDag-Erling Smørgrav * kill it too 1432d74d50a8SDag-Erling Smørgrav */ 1433d74d50a8SDag-Erling Smørgrav signal(SIGINT, SIG_IGN); 1434d74d50a8SDag-Erling Smørgrav execvp(path, args); 1435ae1f160dSDag-Erling Smørgrav fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 1436d74d50a8SDag-Erling Smørgrav _exit(1); 14371e8db6e2SBrian Feldman } 14381e8db6e2SBrian Feldman 1439d95e11bfSDag-Erling Smørgrav signal(SIGTERM, killchild); 1440d95e11bfSDag-Erling Smørgrav signal(SIGINT, killchild); 1441d95e11bfSDag-Erling Smørgrav signal(SIGHUP, killchild); 14421e8db6e2SBrian Feldman close(c_in); 14431e8db6e2SBrian Feldman close(c_out); 14441e8db6e2SBrian Feldman } 14451e8db6e2SBrian Feldman 1446ae1f160dSDag-Erling Smørgrav static void 14471e8db6e2SBrian Feldman usage(void) 14481e8db6e2SBrian Feldman { 1449ae1f160dSDag-Erling Smørgrav extern char *__progname; 1450ae1f160dSDag-Erling Smørgrav 1451ae1f160dSDag-Erling Smørgrav fprintf(stderr, 1452efcad6b7SDag-Erling Smørgrav "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" 1453efcad6b7SDag-Erling Smørgrav " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" 1454efcad6b7SDag-Erling Smørgrav " [-S program] [-s subsystem | sftp_server] host\n" 1455efcad6b7SDag-Erling Smørgrav " %s [[user@]host[:file [file]]]\n" 1456efcad6b7SDag-Erling Smørgrav " %s [[user@]host[:dir[/]]]\n" 1457efcad6b7SDag-Erling Smørgrav " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); 14581e8db6e2SBrian Feldman exit(1); 14591e8db6e2SBrian Feldman } 14601e8db6e2SBrian Feldman 14611e8db6e2SBrian Feldman int 14621e8db6e2SBrian Feldman main(int argc, char **argv) 14631e8db6e2SBrian Feldman { 1464d0c8c0bcSDag-Erling Smørgrav int in, out, ch, err; 1465d74d50a8SDag-Erling Smørgrav char *host, *userhost, *cp, *file2 = NULL; 1466ae1f160dSDag-Erling Smørgrav int debug_level = 0, sshver = 2; 1467ae1f160dSDag-Erling Smørgrav char *file1 = NULL, *sftp_server = NULL; 1468ae1f160dSDag-Erling Smørgrav char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 1469ae1f160dSDag-Erling Smørgrav LogLevel ll = SYSLOG_LEVEL_INFO; 1470ae1f160dSDag-Erling Smørgrav arglist args; 14711e8db6e2SBrian Feldman extern int optind; 14721e8db6e2SBrian Feldman extern char *optarg; 14731e8db6e2SBrian Feldman 1474021d409fSDag-Erling Smørgrav /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1475021d409fSDag-Erling Smørgrav sanitise_stdfd(); 1476021d409fSDag-Erling Smørgrav 1477d95e11bfSDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 1478021d409fSDag-Erling Smørgrav memset(&args, '\0', sizeof(args)); 1479ae1f160dSDag-Erling Smørgrav args.list = NULL; 1480761efaa7SDag-Erling Smørgrav addargs(&args, "%s", ssh_program); 1481ae1f160dSDag-Erling Smørgrav addargs(&args, "-oForwardX11 no"); 1482ae1f160dSDag-Erling Smørgrav addargs(&args, "-oForwardAgent no"); 1483021d409fSDag-Erling Smørgrav addargs(&args, "-oPermitLocalCommand no"); 1484ae1f160dSDag-Erling Smørgrav addargs(&args, "-oClearAllForwardings yes"); 1485efcad6b7SDag-Erling Smørgrav 1486ae1f160dSDag-Erling Smørgrav ll = SYSLOG_LEVEL_INFO; 1487efcad6b7SDag-Erling Smørgrav infile = stdin; 14881e8db6e2SBrian Feldman 1489ae1f160dSDag-Erling Smørgrav while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { 14901e8db6e2SBrian Feldman switch (ch) { 14911e8db6e2SBrian Feldman case 'C': 1492ae1f160dSDag-Erling Smørgrav addargs(&args, "-C"); 14931e8db6e2SBrian Feldman break; 14941e8db6e2SBrian Feldman case 'v': 1495ae1f160dSDag-Erling Smørgrav if (debug_level < 3) { 1496ae1f160dSDag-Erling Smørgrav addargs(&args, "-v"); 1497ae1f160dSDag-Erling Smørgrav ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 1498ae1f160dSDag-Erling Smørgrav } 1499ae1f160dSDag-Erling Smørgrav debug_level++; 15001e8db6e2SBrian Feldman break; 1501ae1f160dSDag-Erling Smørgrav case 'F': 15021e8db6e2SBrian Feldman case 'o': 1503ae1f160dSDag-Erling Smørgrav addargs(&args, "-%c%s", ch, optarg); 15041e8db6e2SBrian Feldman break; 15051e8db6e2SBrian Feldman case '1': 1506ae1f160dSDag-Erling Smørgrav sshver = 1; 15071e8db6e2SBrian Feldman if (sftp_server == NULL) 15081e8db6e2SBrian Feldman sftp_server = _PATH_SFTP_SERVER; 15091e8db6e2SBrian Feldman break; 15101e8db6e2SBrian Feldman case 's': 15111e8db6e2SBrian Feldman sftp_server = optarg; 15121e8db6e2SBrian Feldman break; 15131e8db6e2SBrian Feldman case 'S': 15141e8db6e2SBrian Feldman ssh_program = optarg; 1515021d409fSDag-Erling Smørgrav replacearg(&args, 0, "%s", ssh_program); 15161e8db6e2SBrian Feldman break; 15171e8db6e2SBrian Feldman case 'b': 1518efcad6b7SDag-Erling Smørgrav if (batchmode) 1519efcad6b7SDag-Erling Smørgrav fatal("Batch file already specified."); 1520efcad6b7SDag-Erling Smørgrav 1521efcad6b7SDag-Erling Smørgrav /* Allow "-" as stdin */ 1522efcad6b7SDag-Erling Smørgrav if (strcmp(optarg, "-") != 0 && 1523efcad6b7SDag-Erling Smørgrav (infile = fopen(optarg, "r")) == NULL) 15241e8db6e2SBrian Feldman fatal("%s (%s).", strerror(errno), optarg); 1525d0c8c0bcSDag-Erling Smørgrav showprogress = 0; 1526efcad6b7SDag-Erling Smørgrav batchmode = 1; 15275e8dbd04SDag-Erling Smørgrav addargs(&args, "-obatchmode yes"); 15281e8db6e2SBrian Feldman break; 1529ae1f160dSDag-Erling Smørgrav case 'P': 1530ae1f160dSDag-Erling Smørgrav sftp_direct = optarg; 1531ae1f160dSDag-Erling Smørgrav break; 1532ae1f160dSDag-Erling Smørgrav case 'B': 1533ae1f160dSDag-Erling Smørgrav copy_buffer_len = strtol(optarg, &cp, 10); 1534ae1f160dSDag-Erling Smørgrav if (copy_buffer_len == 0 || *cp != '\0') 1535ae1f160dSDag-Erling Smørgrav fatal("Invalid buffer size \"%s\"", optarg); 1536ae1f160dSDag-Erling Smørgrav break; 1537ae1f160dSDag-Erling Smørgrav case 'R': 1538ae1f160dSDag-Erling Smørgrav num_requests = strtol(optarg, &cp, 10); 1539ae1f160dSDag-Erling Smørgrav if (num_requests == 0 || *cp != '\0') 1540ae1f160dSDag-Erling Smørgrav fatal("Invalid number of requests \"%s\"", 1541ae1f160dSDag-Erling Smørgrav optarg); 1542ae1f160dSDag-Erling Smørgrav break; 15431e8db6e2SBrian Feldman case 'h': 15441e8db6e2SBrian Feldman default: 15451e8db6e2SBrian Feldman usage(); 15461e8db6e2SBrian Feldman } 15471e8db6e2SBrian Feldman } 15481e8db6e2SBrian Feldman 154952028650SDag-Erling Smørgrav if (!isatty(STDERR_FILENO)) 155052028650SDag-Erling Smørgrav showprogress = 0; 155152028650SDag-Erling Smørgrav 1552545d5ecaSDag-Erling Smørgrav log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 1553545d5ecaSDag-Erling Smørgrav 1554ae1f160dSDag-Erling Smørgrav if (sftp_direct == NULL) { 15551e8db6e2SBrian Feldman if (optind == argc || argc > (optind + 2)) 15561e8db6e2SBrian Feldman usage(); 15571e8db6e2SBrian Feldman 15581e8db6e2SBrian Feldman userhost = xstrdup(argv[optind]); 15591e8db6e2SBrian Feldman file2 = argv[optind+1]; 15601e8db6e2SBrian Feldman 1561d0c8c0bcSDag-Erling Smørgrav if ((host = strrchr(userhost, '@')) == NULL) 15621e8db6e2SBrian Feldman host = userhost; 15631e8db6e2SBrian Feldman else { 15641e8db6e2SBrian Feldman *host++ = '\0'; 15651e8db6e2SBrian Feldman if (!userhost[0]) { 15661e8db6e2SBrian Feldman fprintf(stderr, "Missing username\n"); 15671e8db6e2SBrian Feldman usage(); 15681e8db6e2SBrian Feldman } 1569ae1f160dSDag-Erling Smørgrav addargs(&args, "-l%s",userhost); 15701e8db6e2SBrian Feldman } 15711e8db6e2SBrian Feldman 1572efcad6b7SDag-Erling Smørgrav if ((cp = colon(host)) != NULL) { 1573efcad6b7SDag-Erling Smørgrav *cp++ = '\0'; 1574efcad6b7SDag-Erling Smørgrav file1 = cp; 1575efcad6b7SDag-Erling Smørgrav } 1576efcad6b7SDag-Erling Smørgrav 15771e8db6e2SBrian Feldman host = cleanhostname(host); 15781e8db6e2SBrian Feldman if (!*host) { 15791e8db6e2SBrian Feldman fprintf(stderr, "Missing hostname\n"); 15801e8db6e2SBrian Feldman usage(); 15811e8db6e2SBrian Feldman } 15821e8db6e2SBrian Feldman 1583ae1f160dSDag-Erling Smørgrav addargs(&args, "-oProtocol %d", sshver); 15841e8db6e2SBrian Feldman 1585ae1f160dSDag-Erling Smørgrav /* no subsystem if the server-spec contains a '/' */ 1586ae1f160dSDag-Erling Smørgrav if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 1587ae1f160dSDag-Erling Smørgrav addargs(&args, "-s"); 1588ae1f160dSDag-Erling Smørgrav 1589ae1f160dSDag-Erling Smørgrav addargs(&args, "%s", host); 1590ae1f160dSDag-Erling Smørgrav addargs(&args, "%s", (sftp_server != NULL ? 1591ae1f160dSDag-Erling Smørgrav sftp_server : "sftp")); 15921e8db6e2SBrian Feldman 1593efcad6b7SDag-Erling Smørgrav if (!batchmode) 15941e8db6e2SBrian Feldman fprintf(stderr, "Connecting to %s...\n", host); 1595d95e11bfSDag-Erling Smørgrav connect_to_server(ssh_program, args.list, &in, &out); 1596ae1f160dSDag-Erling Smørgrav } else { 1597ae1f160dSDag-Erling Smørgrav args.list = NULL; 1598ae1f160dSDag-Erling Smørgrav addargs(&args, "sftp-server"); 15991e8db6e2SBrian Feldman 1600efcad6b7SDag-Erling Smørgrav if (!batchmode) 1601ae1f160dSDag-Erling Smørgrav fprintf(stderr, "Attaching to %s...\n", sftp_direct); 1602d95e11bfSDag-Erling Smørgrav connect_to_server(sftp_direct, args.list, &in, &out); 1603ae1f160dSDag-Erling Smørgrav } 1604021d409fSDag-Erling Smørgrav freeargs(&args); 16051e8db6e2SBrian Feldman 1606d0c8c0bcSDag-Erling Smørgrav err = interactive_loop(in, out, file1, file2); 16071e8db6e2SBrian Feldman 160883d2307dSDag-Erling Smørgrav #if !defined(USE_PIPES) 160983d2307dSDag-Erling Smørgrav shutdown(in, SHUT_RDWR); 161083d2307dSDag-Erling Smørgrav shutdown(out, SHUT_RDWR); 161183d2307dSDag-Erling Smørgrav #endif 161283d2307dSDag-Erling Smørgrav 16131e8db6e2SBrian Feldman close(in); 16141e8db6e2SBrian Feldman close(out); 1615efcad6b7SDag-Erling Smørgrav if (batchmode) 16161e8db6e2SBrian Feldman fclose(infile); 16171e8db6e2SBrian Feldman 1618545d5ecaSDag-Erling Smørgrav while (waitpid(sshpid, NULL, 0) == -1) 1619545d5ecaSDag-Erling Smørgrav if (errno != EINTR) 1620545d5ecaSDag-Erling Smørgrav fatal("Couldn't wait for ssh process: %s", 1621545d5ecaSDag-Erling Smørgrav strerror(errno)); 16221e8db6e2SBrian Feldman 1623d0c8c0bcSDag-Erling Smørgrav exit(err == 0 ? 0 : 1); 16241e8db6e2SBrian Feldman } 1625