#include "ftp_var.h"
#include <deflt.h>
int trace;
int hash;
int sendport;
int verbose;
int connected;
int fromatty;
int interactive;
int debug;
int bell;
int doglob;
int autologin;
int proxy;
int proxflag;
int sunique;
int runique;
int mcase;
int ntflag;
int mapflag;
int code;
int crflag;
char pasv[64];
char *altarg;
char ntin[17];
char ntout[17];
char mapin[MAXPATHLEN];
char mapout[MAXPATHLEN];
char typename[32];
int type;
char structname[32];
int stru;
char formname[32];
int form;
char modename[32];
int mode;
char bytename[32];
int bytesize;
int passivemode;
off_t restart_point;
int tcpwindowsize;
boolean_t ls_invokes_NLST;
char *hostname;
char *home;
char *globerr;
struct sockaddr_in6 myctladdr;
struct sockaddr_in6 remctladdr;
int clevel;
int dlevel;
int autoauth;
int auth_error;
int autoencrypt;
int fflag;
boolean_t goteof;
int skipsyst;
char mechstr[MECH_SZ];
char *buf;
jmp_buf toplevel;
char line[BUFSIZE];
char *stringbase;
char argbuf[BUFSIZE];
char *argbase;
int margc;
char **margv;
int cpend;
int mflag;
char reply_buf[FTPBUFSIZ];
char *reply_ptr;
int options;
int timeout;
int timeoutms;
jmp_buf timeralarm;
int macnum;
struct macel macros[16];
char macbuf[4096];
static void usage(void);
static void timeout_sig(int sig);
static void cmdscanner(int top);
static void intr(int sig);
static char *slurpstring(void);
extern int use_eprt;
boolean_t ls_invokes_NLST = B_TRUE;
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_ext.h>
#define GETOPT_STR "dginpstvET:axfm:"
#define USAGE_STR "[-adfginpstvx] [-m mech] [-T timeout] " \
"[hostname [port]]"
int
main(int argc, char *argv[])
{
char *cp;
int c, top;
struct passwd *pw = NULL;
char homedir[MAXPATHLEN];
char *temp_string = NULL;
(void) setlocale(LC_ALL, "");
buf = (char *)memalign(getpagesize(), FTPBUFSIZ);
if (buf == NULL) {
(void) fprintf(stderr, "ftp: memory allocation failed\n");
return (1);
}
timeoutms = timeout = 0;
doglob = 1;
interactive = 1;
autologin = 1;
autoauth = 0;
skipsyst = 0;
fflag = 0;
autoencrypt = 0;
goteof = 0;
mechstr[0] = '\0';
sendport = -1;
passivemode = 0;
while ((c = getopt(argc, argv, GETOPT_STR)) != EOF) {
switch (c) {
case 'd':
options |= SO_DEBUG;
debug++;
break;
case 'g':
doglob = 0;
break;
case 'i':
interactive = 0;
break;
case 'n':
autologin = 0;
break;
case 'p':
passivemode = 1;
break;
case 't':
trace++;
break;
case 'v':
verbose++;
break;
case 'E':
use_eprt = 1;
break;
case 'T':
if (!isdigit(*optarg)) {
(void) fprintf(stderr,
"ftp: bad timeout: \"%s\"\n", optarg);
break;
}
timeout = atoi(optarg);
timeoutms = timeout * MILLISEC;
break;
case 'a':
autoauth = 1;
break;
case 'f':
autoauth = 1;
fflag = 1;
break;
case 'm':
autoauth = 1;
call(setmech, "ftp", optarg, 0);
if (code != 0)
exit(1);
break;
case 'x':
autoauth = 1;
autoencrypt = 1;
break;
case 's':
skipsyst = 1;
break;
case '?':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc > 2)
usage();
fromatty = isatty(fileno(stdin));
temp_string = getenv("FTP_LS_SENDS_NLST");
if (temp_string == NULL) {
if (defopen(DEFAULTFTPFILE) == 0) {
int flags = defcntl(DC_GETFLAGS, 0);
TURNOFF(flags, DC_CASE);
(void) defcntl(DC_SETFLAGS, flags);
temp_string = defread("FTP_LS_SENDS_NLST=");
(void) defopen(NULL);
}
}
if (temp_string != NULL &&
strncasecmp(temp_string, "n", 1) == 0)
ls_invokes_NLST = B_FALSE;
(void) strcpy(typename, "ascii"), type = TYPE_A;
(void) strcpy(formname, "non-print"), form = FORM_N;
(void) strcpy(modename, "stream"), mode = MODE_S;
(void) strcpy(structname, "file"), stru = STRU_F;
(void) strcpy(bytename, "8"), bytesize = 8;
if (fromatty)
verbose++;
cpend = 0;
proxy = 0;
crflag = 1;
if (mechstr[0] == '\0') {
strlcpy(mechstr, FTP_DEF_MECH, MECH_SZ);
}
cp = getlogin();
if (cp != NULL) {
pw = getpwnam(cp);
}
if (pw == NULL)
pw = getpwuid(getuid());
if (pw != NULL) {
home = homedir;
(void) strcpy(home, pw->pw_dir);
}
if (setjmp(timeralarm)) {
(void) fflush(stdout);
(void) printf("Connection timeout\n");
exit(1);
}
(void) signal(SIGALRM, timeout_sig);
reset_timer();
if (argc > 0) {
int nargc = 0;
char *nargv[4];
if (setjmp(toplevel))
return (0);
(void) signal(SIGINT, intr);
(void) signal(SIGPIPE, lostpeer);
nargv[nargc++] = "ftp";
nargv[nargc++] = argv[0];
if (argc > 1)
nargv[nargc++] = argv[1];
nargv[nargc] = NULL;
setpeer(nargc, nargv);
}
top = setjmp(toplevel) == 0;
if (top) {
(void) signal(SIGINT, intr);
(void) signal(SIGPIPE, lostpeer);
}
for (;;) {
cmdscanner(top);
top = 1;
}
}
static void
usage(void)
{
(void) fprintf(stderr, "usage: ftp %s\n", USAGE_STR);
exit(1);
}
void
reset_timer()
{
if (timeout)
alarm(timeout);
}
void
stop_timer()
{
if (timeout)
alarm(0);
}
static void
timeout_sig(int sig)
{
longjmp(timeralarm, 1);
}
static void
intr(int sig)
{
longjmp(toplevel, 1);
}
void
lostpeer(int sig)
{
extern FILE *ctrl_out;
extern int data;
if (connected) {
if (ctrl_out != NULL) {
(void) shutdown(fileno(ctrl_out), 1+1);
(void) fclose(ctrl_out);
ctrl_out = NULL;
}
if (data >= 0) {
(void) shutdown(data, 1+1);
(void) close(data);
data = -1;
}
connected = 0;
auth_type = AUTHTYPE_NONE;
clevel = dlevel = PROT_C;
goteof = 0;
}
pswitch(1);
if (connected) {
if (ctrl_out != NULL) {
(void) shutdown(fileno(ctrl_out), 1+1);
(void) fclose(ctrl_out);
ctrl_out = NULL;
}
connected = 0;
auth_type = AUTHTYPE_NONE;
clevel = dlevel = PROT_C;
goteof = 0;
}
proxflag = 0;
pswitch(0);
}
static void
cmdscanner(int top)
{
struct cmd *c;
if (!top)
(void) putchar('\n');
for (;;) {
stop_timer();
if (fromatty) {
(void) printf("ftp> ");
(void) fflush(stdout);
}
if (fgets(line, sizeof (line), stdin) == 0) {
if (feof(stdin) || ferror(stdin))
quit(0, NULL);
break;
}
if (line[0] == 0)
break;
if (line[strlen(line)-1] != '\n') {
while (fgetc(stdin) != '\n' && !feof(stdin) &&
!ferror(stdin))
;
(void) printf("Line too long\n");
continue;
} else
line[strlen(line)-1] = 0;
makeargv();
if (margc == 0) {
continue;
}
c = getcmd(margv[0]);
if (c == (struct cmd *)-1) {
(void) printf("?Ambiguous command\n");
continue;
}
if (c == 0) {
(void) printf("?Invalid command\n");
continue;
}
if (c->c_conn && !connected) {
(void) printf("Not connected.\n");
continue;
}
reset_timer();
(*c->c_handler)(margc, margv);
#ifndef CTRL
#define CTRL(c) ((c)&037)
#endif
stop_timer();
if (bell && c->c_bell)
(void) putchar(CTRL('g'));
if (c->c_handler != help)
break;
}
(void) signal(SIGINT, intr);
(void) signal(SIGPIPE, lostpeer);
}
struct cmd *
getcmd(char *name)
{
char *p, *q;
struct cmd *c, *found;
int nmatches, longest;
extern struct cmd cmdtab[];
if (name == NULL)
return (0);
longest = 0;
nmatches = 0;
found = 0;
for (c = cmdtab; (p = c->c_name) != NULL; c++) {
for (q = name; *q == *p++; q++)
if (*q == 0)
return (c);
if (!*q) {
if (q - name > longest) {
longest = q - name;
nmatches = 1;
found = c;
} else if (q - name == longest)
nmatches++;
}
}
if (nmatches > 1)
return ((struct cmd *)-1);
return (found);
}
static int slrflag;
#define MARGV_INC 20
void
makeargv(void)
{
char **argp;
static int margv_size;
margc = 0;
stringbase = line;
argbase = argbuf;
slrflag = 0;
if (!margv) {
margv_size = MARGV_INC;
if ((margv = malloc(margv_size * sizeof (char *))) == NULL)
fatal("Out of memory");
}
argp = margv;
while (*argp++ = slurpstring()) {
margc++;
if (margc == margv_size) {
margv_size += MARGV_INC;
if ((margv = realloc(margv,
margv_size * sizeof (char *))) == NULL)
fatal("Out of memory");
argp = margv + margc;
}
}
}
static char *
slurpstring(void)
{
int got_one = 0;
char *sb = stringbase;
char *ap = argbase;
char *tmp = argbase;
int len;
if (*sb == '!' || *sb == '$') {
switch (slrflag) {
case 0:
slrflag++;
stringbase++;
return ((*sb == '!') ? "!" : "$");
case 1:
slrflag++;
altarg = stringbase;
break;
default:
break;
}
}
S0:
switch (*sb) {
case '\0':
goto OUT;
case ' ':
case '\t':
sb++; goto S0;
default:
switch (slrflag) {
case 0:
slrflag++;
break;
case 1:
slrflag++;
altarg = sb;
break;
default:
break;
}
goto S1;
}
S1:
switch (*sb) {
case ' ':
case '\t':
case '\0':
goto OUT;
case '\\':
sb++; goto S2;
case '"':
sb++; goto S3;
default:
if ((len = mblen(sb, MB_CUR_MAX)) <= 0)
len = 1;
memcpy(ap, sb, len);
ap += len;
sb += len;
got_one = 1;
goto S1;
}
S2:
switch (*sb) {
case '\0':
goto OUT;
default:
if ((len = mblen(sb, MB_CUR_MAX)) <= 0)
len = 1;
memcpy(ap, sb, len);
ap += len;
sb += len;
got_one = 1;
goto S1;
}
S3:
switch (*sb) {
case '\0':
goto OUT;
case '"':
sb++; goto S1;
default:
if ((len = mblen(sb, MB_CUR_MAX)) <= 0)
len = 1;
memcpy(ap, sb, len);
ap += len;
sb += len;
got_one = 1;
goto S3;
}
OUT:
if (got_one)
*ap++ = '\0';
argbase = ap;
stringbase = sb;
if (got_one) {
return (tmp);
}
switch (slrflag) {
case 0:
slrflag++;
break;
case 1:
slrflag++;
altarg = (char *)0;
break;
default:
break;
}
return ((char *)0);
}
#define HELPINDENT (sizeof ("directory"))
void
help(int argc, char *argv[])
{
struct cmd *c;
extern struct cmd cmdtab[];
if (argc == 1) {
int i, j, w, k;
int columns, width = 0, lines;
extern int NCMDS;
(void) printf(
"Commands may be abbreviated. Commands are:\n\n");
for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
int len = strlen(c->c_name);
if (len > width)
width = len;
}
width = (width + 8) &~ 7;
columns = 80 / width;
if (columns == 0)
columns = 1;
lines = (NCMDS + columns - 1) / columns;
for (i = 0; i < lines; i++) {
for (j = 0; j < columns; j++) {
c = cmdtab + j * lines + i;
if (c->c_name && (!proxy || c->c_proxy)) {
(void) printf("%s", c->c_name);
} else if (c->c_name) {
for (k = 0; k < strlen(c->c_name);
k++) {
(void) putchar(' ');
}
}
if (c + lines >= &cmdtab[NCMDS]) {
(void) printf("\n");
break;
}
w = strlen(c->c_name);
while (w < width) {
w = (w + 8) &~ 7;
(void) putchar('\t');
}
}
}
return;
}
while (--argc > 0) {
char *arg;
arg = *++argv;
c = getcmd(arg);
if (c == (struct cmd *)-1)
(void) printf("?Ambiguous help command %s\n", arg);
else if (c == (struct cmd *)0)
(void) printf("?Invalid help command %s\n", arg);
else
(void) printf("%-*s\t%s\n", HELPINDENT,
c->c_name, c->c_help);
}
}
void
call(void (*routine)(int argc, char *argv[]), ...)
{
va_list ap;
char *argv[10];
int argc = 0;
va_start(ap, routine);
while ((argv[argc] = va_arg(ap, char *)) != (char *)0)
argc++;
va_end(ap);
(*routine)(argc, argv);
}