/* * Complete an option */ static unsigned char complete_option(char *word, int list) { struct option *o; StringList *words; size_t wordlen; unsigned char rv; words = ftp_sl_init(); wordlen = strlen(word); for (o = optiontab; o->name != NULL; o++) { if (wordlen > strlen(o->name)) continue; if (strncmp(word, o->name, wordlen) == 0) ftp_sl_add(words, ftp_strdup(o->name)); } rv = complete_ambiguous(word, list, words); if (rv == CC_REFRESH) { if (el_insertstr(el, " ") == -1) rv = CC_ERROR; } sl_free(words, 1); return (rv); }
/* * Complete a command */ static unsigned char complete_command(char *word, int list) { struct cmd *c; StringList *words; size_t wordlen; unsigned char rv; words = ftp_sl_init(); wordlen = strlen(word); for (c = cmdtab; c->c_name != NULL; c++) { if (wordlen > strlen(c->c_name)) continue; if (strncmp(word, c->c_name, wordlen) == 0) ftp_sl_add(words, ftp_strdup(c->c_name)); } rv = complete_ambiguous(word, list, words); if (rv == CC_REFRESH) { if (el_insertstr(el, " ") == -1) rv = CC_ERROR; } sl_free(words, 1); return (rv); }
/* * Help/usage command. * Call each command handler with argc == 0 and argv[0] == name. */ void help(int argc, char *argv[]) { struct cmd *c; char *nargv[1], *cmd; const char *p; int isusage; cmd = argv[0]; isusage = (strcmp(cmd, "usage") == 0); if (argc == 0 || (isusage && argc == 1)) { UPRINTF("usage: %s [command [...]]\n", cmd); return; } if (argc == 1) { StringList *buf; buf = ftp_sl_init(); fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n", proxy ? "Proxy c" : "C"); for (c = cmdtab; (p = c->c_name) != NULL; c++) if (!proxy || c->c_proxy) ftp_sl_add(buf, ftp_strdup(p)); list_vertical(buf); sl_free(buf, 1); return; } #define HELPINDENT ((int) sizeof("disconnect")) while (--argc > 0) { char *arg; char cmdbuf[MAX_C_NAME]; arg = *++argv; c = getcmd(arg); if (c == (struct cmd *)-1) fprintf(ttyout, "?Ambiguous %s command `%s'\n", cmd, arg); else if (c == NULL) fprintf(ttyout, "?Invalid %s command `%s'\n", cmd, arg); else { if (isusage) { (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf)); nargv[0] = cmdbuf; (*c->c_handler)(0, nargv); } else fprintf(ttyout, "%-*s\t%s\n", HELPINDENT, c->c_name, c->c_help); } } }
/* * Complete a remote file */ static unsigned char complete_remote(char *word, int list) { static StringList *dirlist; static char lastdir[MAXPATHLEN]; StringList *words; char dir[MAXPATHLEN]; char *file, *cp; size_t i; unsigned char rv; char cmdbuf[MAX_C_NAME]; char *dummyargv[3] = { NULL, NULL, NULL }; (void)strlcpy(cmdbuf, "complete", sizeof(cmdbuf)); dummyargv[0] = cmdbuf; dummyargv[1] = dir; if ((file = strrchr(word, '/')) == NULL) { dir[0] = '\0'; file = word; } else { cp = file; while (*cp == '/' && cp > word) cp--; (void)strlcpy(dir, word, cp - word + 2); file++; } if (dirchange || dirlist == NULL || strcmp(dir, lastdir) != 0) { /* dir not cached */ const char *emesg; if (dirlist != NULL) sl_free(dirlist, 1); dirlist = ftp_sl_init(); mflag = 1; emesg = NULL; while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) { char *tcp; if (!mflag) continue; if (*cp == '\0') { mflag = 0; continue; } tcp = strrchr(cp, '/'); if (tcp) tcp++; else tcp = cp; tcp = ftp_strdup(tcp); ftp_sl_add(dirlist, tcp); } if (emesg != NULL) { fprintf(ttyout, "\n%s\n", emesg); return (CC_REDISPLAY); } (void)strlcpy(lastdir, dir, sizeof(lastdir)); dirchange = 0; } words = ftp_sl_init(); for (i = 0; i < dirlist->sl_cur; i++) { cp = dirlist->sl_str[i]; if (strlen(file) > strlen(cp)) continue; if (strncmp(file, cp, strlen(file)) == 0) ftp_sl_add(words, cp); } rv = complete_ambiguous(file, list, words); sl_free(words, 0); return (rv); }
/* * Complete a local file */ static unsigned char complete_local(char *word, int list) { StringList *words; char dir[MAXPATHLEN]; char *file; DIR *dd; struct dirent *dp; unsigned char rv; size_t len; if ((file = strrchr(word, '/')) == NULL) { dir[0] = '.'; dir[1] = '\0'; file = word; } else { if (file == word) { dir[0] = '/'; dir[1] = '\0'; } else (void)strlcpy(dir, word, file - word + 1); file++; } if (dir[0] == '~') { char *p; if ((p = globulize(dir)) == NULL) return (CC_ERROR); (void)strlcpy(dir, p, sizeof(dir)); free(p); } if ((dd = opendir(dir)) == NULL) return (CC_ERROR); words = ftp_sl_init(); len = strlen(file); for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; #if defined(DIRENT_MISSING_D_NAMLEN) if (len > strlen(dp->d_name)) continue; #else if (len > dp->d_namlen) continue; #endif if (strncmp(file, dp->d_name, len) == 0) { char *tcp; tcp = ftp_strdup(dp->d_name); ftp_sl_add(words, tcp); } } closedir(dd); rv = complete_ambiguous(file, list, words); if (rv == CC_REFRESH) { struct stat sb; char path[MAXPATHLEN]; (void)strlcpy(path, dir, sizeof(path)); (void)strlcat(path, "/", sizeof(path)); (void)strlcat(path, words->sl_str[0], sizeof(path)); if (stat(path, &sb) >= 0) { char suffix[2] = " "; if (S_ISDIR(sb.st_mode)) suffix[0] = '/'; if (el_insertstr(el, suffix) == -1) rv = CC_ERROR; } } sl_free(words, 1); return (rv); }
int main(int volatile argc, char **volatile argv) { int ch, rval; struct passwd *pw; char *cp, *ep, *anonuser, *anonpass, *upload_path, *src_addr; int dumbterm, s, isupload; size_t len; socklen_t slen; tzset(); #if 0 /* tnftp */ /* XXX */ setlocale(LC_ALL, ""); #endif /* tnftp */ setprogname(argv[0]); sigint_raised = 0; ftpport = "ftp"; httpport = "http"; gateport = NULL; cp = getenv("FTPSERVERPORT"); if (cp != NULL) gateport = cp; else gateport = "ftpgate"; doglob = 1; interactive = 1; autologin = 1; passivemode = 1; activefallback = 1; preserve = 1; verbose = 0; progress = 0; gatemode = 0; data = -1; outfile = NULL; restartautofetch = 0; #ifndef NO_EDITCOMPLETE editing = 0; el = NULL; hist = NULL; #endif bytes = 0; mark = HASHBYTES; rate_get = 0; rate_get_incr = DEFAULTINCR; rate_put = 0; rate_put_incr = DEFAULTINCR; #ifdef INET6 epsv4 = 1; #else epsv4 = 0; #endif epsv4bad = 0; src_addr = NULL; upload_path = NULL; isupload = 0; reply_callback = NULL; family = AF_UNSPEC; netrc[0] = '\0'; cp = getenv("NETRC"); if (cp != NULL && strlcpy(netrc, cp, sizeof(netrc)) >= sizeof(netrc)) errx(1, "$NETRC `%s': %s", cp, strerror(ENAMETOOLONG)); /* * Get the default socket buffer sizes if we don't already have them. * It doesn't matter which socket we do this to, because on the first * call no socket buffer sizes will have been modified, so we are * guaranteed to get the system defaults. */ s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) err(1, "Can't create socket to determine default socket sizes"); slen = sizeof(rcvbuf_size); if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf_size, &slen) == -1) err(1, "Unable to get default rcvbuf size"); slen = sizeof(sndbuf_size); if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *)&sndbuf_size, &slen) == -1) err(1, "Unable to get default sndbuf size"); (void)close(s); /* sanity check returned buffer sizes */ if (rcvbuf_size <= 0) rcvbuf_size = 8 * 1024; if (sndbuf_size <= 0) sndbuf_size = 8 * 1024; if (sndbuf_size > 8 * 1024 * 1024) sndbuf_size = 8 * 1024 * 1024; if (rcvbuf_size > 8 * 1024 * 1024) rcvbuf_size = 8 * 1024 * 1024; marg_sl = ftp_sl_init(); if ((tmpdir = getenv("TMPDIR")) == NULL) tmpdir = _PATH_TMP; /* Set default operation mode based on FTPMODE environment variable */ if ((cp = getenv("FTPMODE")) != NULL) { if (strcasecmp(cp, "passive") == 0) { passivemode = 1; activefallback = 0; } else if (strcasecmp(cp, "active") == 0) { passivemode = 0; activefallback = 0; } else if (strcasecmp(cp, "gate") == 0) { gatemode = 1; } else if (strcasecmp(cp, "auto") == 0) { passivemode = 1; activefallback = 1; } else warnx("Unknown $FTPMODE `%s'; using defaults", cp); } if (strcmp(getprogname(), "pftp") == 0) { passivemode = 1; activefallback = 0; } else if (strcmp(getprogname(), "gate-ftp") == 0) gatemode = 1; gateserver = getenv("FTPSERVER"); if (gateserver == NULL || *gateserver == '\0') gateserver = GATE_SERVER; if (gatemode) { if (*gateserver == '\0') { warnx( "Neither $FTPSERVER nor GATE_SERVER is defined; disabling gate-ftp"); gatemode = 0; } } cp = getenv("TERM"); if (cp == NULL || strcmp(cp, "dumb") == 0) dumbterm = 1; else dumbterm = 0; fromatty = isatty(fileno(stdin)); ttyout = stdout; if (isatty(fileno(ttyout))) { verbose = 1; /* verbose if to a tty */ if (! dumbterm) { #ifndef NO_EDITCOMPLETE if (fromatty) /* editing mode on if tty is usable */ editing = 1; #endif #ifndef NO_PROGRESS if (foregroundproc()) progress = 1; /* progress bar on if fg */ #endif } } while ((ch = getopt(argc, argv, "46AadefginN:o:pP:q:r:Rs:tT:u:vV")) != -1) { switch (ch) { case '4': family = AF_INET; break; case '6': #ifdef INET6 family = AF_INET6; #else warnx("INET6 support is not available; ignoring -6"); #endif break; case 'A': activefallback = 0; passivemode = 0; break; case 'a': anonftp = 1; break; case 'd': options |= SO_DEBUG; ftp_debug++; break; case 'e': #ifndef NO_EDITCOMPLETE editing = 0; #endif break; case 'f': flushcache = 1; break; case 'g': doglob = 0; break; case 'i': interactive = 0; break; case 'n': autologin = 0; break; case 'N': if (strlcpy(netrc, optarg, sizeof(netrc)) >= sizeof(netrc)) errx(1, "%s: %s", optarg, strerror(ENAMETOOLONG)); break; case 'o': outfile = optarg; if (strcmp(outfile, "-") == 0) ttyout = stderr; break; case 'p': passivemode = 1; activefallback = 0; break; case 'P': ftpport = optarg; break; case 'q': quit_time = strtol(optarg, &ep, 10); if (quit_time < 1 || *ep != '\0') errx(1, "Bad quit value: %s", optarg); break; case 'r': retry_connect = strtol(optarg, &ep, 10); if (retry_connect < 1 || *ep != '\0') errx(1, "Bad retry value: %s", optarg); break; case 'R': restartautofetch = 1; break; case 's': src_addr = optarg; break; case 't': trace = 1; break; case 'T': { int targc; char *targv[6], *oac; /* look for `dir,max[,incr]' */ targc = 0; targv[targc++] = "-T"; oac = ftp_strdup(optarg); while ((cp = strsep(&oac, ",")) != NULL) { if (*cp == '\0') { warnx("Bad throttle value `%s'", optarg); usage(); /* NOTREACHED */ } targv[targc++] = cp; if (targc >= 5) break; } if (parserate(targc, targv, 1) == -1) usage(); free(oac); break; } case 'u': { isupload = 1; interactive = 0; upload_path = ftp_strdup(optarg); break; } case 'v': progress = verbose = 1; break; case 'V': progress = verbose = 0; break; default: usage(); } } /* set line buffering on ttyout */ setvbuf(ttyout, NULL, _IOLBF, 0); argc -= optind; argv += optind; cpend = 0; /* no pending replies */ proxy = 0; /* proxy not active */ crflag = 1; /* strip c.r. on ascii gets */ sendport = -1; /* not using ports */ if (src_addr != NULL) { struct addrinfo hints; int error; memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; error = getaddrinfo(src_addr, NULL, &hints, &bindai); if (error) { errx(1, "Can't lookup `%s': %s", src_addr, (error == EAI_SYSTEM) ? strerror(errno) : gai_strerror(error)); } } /* * Cache the user name and home directory. */ localhome = NULL; localname = NULL; anonuser = "******"; cp = getenv("HOME"); if (! EMPTYSTRING(cp)) localhome = ftp_strdup(cp); pw = NULL; cp = getlogin(); if (cp != NULL) pw = getpwnam(cp); if (pw == NULL) pw = getpwuid(getuid()); if (pw != NULL) { if (localhome == NULL && !EMPTYSTRING(pw->pw_dir)) localhome = ftp_strdup(pw->pw_dir); localname = ftp_strdup(pw->pw_name); anonuser = localname; } if (netrc[0] == '\0' && localhome != NULL) { if (strlcpy(netrc, localhome, sizeof(netrc)) >= sizeof(netrc) || strlcat(netrc, "/.netrc", sizeof(netrc)) >= sizeof(netrc)) { warnx("%s/.netrc: %s", localhome, strerror(ENAMETOOLONG)); netrc[0] = '\0'; } } if (localhome == NULL) localhome = ftp_strdup("/"); /* * Every anonymous FTP server I've encountered will accept the * string "username@", and will append the hostname itself. We * do this by default since many servers are picky about not * having a FQDN in the anonymous password. * - [email protected] */ len = strlen(anonuser) + 2; anonpass = ftp_malloc(len); (void)strlcpy(anonpass, anonuser, len); (void)strlcat(anonpass, "@", len); /* * set all the defaults for options defined in * struct option optiontab[] declared in cmdtab.c */ setupoption("anonpass", getenv("FTPANONPASS"), anonpass); setupoption("ftp_proxy", getenv(FTP_PROXY), ""); setupoption("http_proxy", getenv(HTTP_PROXY), ""); setupoption("no_proxy", getenv(NO_PROXY), ""); setupoption("pager", getenv("PAGER"), DEFAULTPAGER); setupoption("prompt", getenv("FTPPROMPT"), DEFAULTPROMPT); setupoption("rprompt", getenv("FTPRPROMPT"), DEFAULTRPROMPT); free(anonpass); setttywidth(0); #ifdef SIGINFO (void)xsignal(SIGINFO, psummary); #endif (void)xsignal(SIGQUIT, psummary); (void)xsignal(SIGUSR1, crankrate); (void)xsignal(SIGUSR2, crankrate); (void)xsignal(SIGWINCH, setttywidth); if (argc > 0) { if (isupload) { rval = auto_put(argc, argv, upload_path); sigint_or_rval_exit: if (sigint_raised) { (void)xsignal(SIGINT, SIG_DFL); raise(SIGINT); } exit(rval); } else if (strchr(argv[0], ':') != NULL && ! isipv6addr(argv[0])) { rval = auto_fetch(argc, argv); if (rval >= 0) /* -1 == connected and cd-ed */ goto sigint_or_rval_exit; } else { char *xargv[4], *user, *host; if ((rval = sigsetjmp(toplevel, 1))) goto sigint_or_rval_exit; (void)xsignal(SIGINT, intr); (void)xsignal(SIGPIPE, lostpeer); user = NULL; host = argv[0]; cp = strchr(host, '@'); if (cp) { *cp = '\0'; user = host; host = cp + 1; } /* XXX discards const */ xargv[0] = (char *)getprogname(); xargv[1] = host; xargv[2] = argv[1]; xargv[3] = NULL; do { int oautologin; oautologin = autologin; if (user != NULL) { anonftp = 0; autologin = 0; } setpeer(argc+1, xargv); autologin = oautologin; if (connected == 1 && user != NULL) (void)ftp_login(host, user, NULL); if (!retry_connect) break; if (!connected) { macnum = 0; fprintf(ttyout, "Retrying in %d seconds...\n", retry_connect); sleep(retry_connect); } } while (!connected); retry_connect = 0; /* connected, stop hiding msgs */ } } if (isupload) usage(); #ifndef NO_EDITCOMPLETE controlediting(); #endif /* !NO_EDITCOMPLETE */ (void)sigsetjmp(toplevel, 1); (void)xsignal(SIGINT, intr); (void)xsignal(SIGPIPE, lostpeer); for (;;) cmdscanner(); }