home *** CD-ROM | disk | FTP | other *** search
Wrap
/* main.c */ #define _main_c_ #include "sys.h" #include <sys/types.h> #include <sys/param.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/time.h> #include <arpa/ftp.h> #include <setjmp.h> #include <signal.h> #include <string.h> #include <errno.h> #include <ctype.h> #include <netdb.h> #include <pwd.h> #ifdef SYSLOG # include <syslog.h> #endif #ifndef NO_UNISTDH # include <unistd.h> #endif #ifdef CURSES # undef HZ /* Collides with HaZeltine ! */ # include <curses.h> #endif /* CURSES */ #include "ftpdefs.h" #include "defaults.h" #include "cmds.h" #include "main.h" #include "ftp.h" #include "ftprc.h" #include "copyright.h" /* main.c globals */ int slrflag; int fromatty; /* input is from a terminal */ char *altarg; /* argv[1] with no shell-like preprocessing */ struct servent *sp; /* service spec for tcp/ftp */ jmp_buf toplevel; /* non-local goto stuff for cmd scanner */ char *line; /* input line buffer */ char *stringbase; /* current scan point in line buffer */ char *argbuf; /* argument storage buffer */ char *argbase; /* current storage point in arg buffer */ int margc; /* count of arguments on input line */ char *margv[20]; /* args parsed from input line */ struct userinfo uinfo; /* a copy of their pwent really */ int ansi_escapes; /* for fancy graphics */ int ignore_rc; /* are we supposed to ignore the netrc */ string progname; /* simple filename */ string prompt, prompt2; /* shell prompt string */ string anon_password; /* most likely your email address */ string pager; /* program to browse text files */ string version = FTP_VERSION; long eventnumber; /* number of commands we've done */ FILE *logf = NULL; /* log user activity */ string logfname; /* name of the logfile */ long logsize = 4096L; /* max log size. 0 == no limit */ int percent_flags; /* "%" in prompt string? */ int at_flags; /* "@" in prompt string? */ string mail_path; /* your mailbox */ int newmail; /* how many new letters you have */ time_t mbox_time; /* last modified time of mbox */ char *tcap_normal = "\033[0m"; /* Default ANSI escapes */ char *tcap_boldface = "\033[1m"; char *tcap_underline = "\033[4m"; char *tcap_reverse = "\033[7m"; #ifdef CURSES static char tcbuf[2048]; #endif /* main.c externs */ extern int debug, verbose, mprompt; extern int options, cpend, data, connected; extern int curtype, macnum; extern FILE *cout; extern struct cmd cmdtab[]; extern str32 curtypename; extern char *macbuf; extern char *reply_string; extern string hostname, cwd, lcwd; extern int Optind; extern char *Optarg; main(int argc, char **argv) { register char *cp; int top, opt, openopts = 0; string tmp, oline; if ((cp = rindex(argv[0], '/'))) cp++; else cp = argv[0]; (void) Strncpy(progname, cp); sp = getservbyname("ftp", "tcp"); if (sp == 0) fatal("ftp/tcp: unknown service"); if (init_arrays()) /* Reserve large blocks of memory now */ fatal("could not reserve large amounts of memory."); /* * Set up defaults for FTP. */ mprompt = dMPROMPT; verbose = dVERBOSE; debug = dDEBUG; (void) Strncpy(curtypename, dTYPESTR); curtype = dTYPE; (void) Strncpy(prompt, dPROMPT); /* Setup our pager variable, before we run through the rc, which may change it. */ set_pager(getenv("PAGER"), 0); #ifdef CURSES ansi_escapes = 1; #else ansi_escapes = 0; if ((cp = getenv("TERM")) != NULL) { if ((*cp == 'v' && cp[1] == 't') /* vt100, vt102, ... */ || (strcmp(cp, "xterm") == 0)) ansi_escapes = 1; } #endif (void) getuserinfo(); newmail = 0; (void) time(&mbox_time); (void) Strncpy(anon_password, uinfo.username); if (getlocalhostname(uinfo.hostname, sizeof(uinfo.hostname)) == 0) { (void) Strncat(anon_password, "@"); (void) Strncat(anon_password, uinfo.hostname); } #if dLOGGING (void) sprintf(logfname, "%s/%s", uinfo.homedir, dLOGNAME); #else *logfname = 0; #endif (void) get_cwd(lcwd, (int) sizeof(lcwd)); #ifdef SYSLOG # ifdef LOG_LOCAL3 openlog ("NcFTP", LOG_PID, LOG_LOCAL3); # else openlog ("NcFTP", LOG_PID); # endif #endif /* SYSLOG */ ignore_rc = 0; (void) strcpy(oline, "open "); while ((opt = Getopt(argc, argv, "DVINRHaiup:rd:g:")) >= 0) { switch(opt) { case 'a': case 'i': case 'u': case 'r': (void) sprintf(tmp, "-%c ", opt); goto cattmp; case 'p': case 'd': case 'g': (void) sprintf(tmp, "-%c %s ", opt, Optarg); cattmp: (void) strcat(oline, tmp); openopts++; break; case 'D': /* options |= SO_DEBUG; done below... */ debug++; break; case 'V': verbose++; break; case 'I': mprompt = !mprompt; break; case 'N': ignore_rc = !ignore_rc; break; case 'H': show_version(0, NULL); exit (0); default: usage: (void) fprintf(stderr, "Usage: %s [program options] [[open options] site.to.open[:path]]\n\ Program Options:\n\ -D : Increase debug level.\n\ -H : Show version and compilation information.\n\ -I : Toggle interactive (mprompt) mode.\n\ -N : Toggle reading of the .netrc/.ncftprc.\n\ -V : Increase verbosity.\n\ Open Options:\n\ -a : Open anonymously (this is the default).\n\ -u : Open, specify user/password.\n\ -i : Ignore machine entry in your .netrc.\n\ -p N : Use port #N for connection.\n\ -r : \"Redial\" until connected.\n\ -d N : Redial, pausing N seconds between tries.\n\ -g N : Redial, giving up after N tries.\n\ :path : Open site, retrieve file \"path,\" then exit.\n\ Examples:\n\ %s ftp.unl.edu:/pub/README\n\ %s -V -u ftp.unl.edu\n\ %s -D -r -d 120 -g 10 ftp.unl.edu\n", progname, progname, progname, progname); exit(1); } } cp = argv[Optind]; /* the site to open. */ if (cp == NULL) { if (openopts) goto usage; } else (void) strcat(oline, cp); if (ignore_rc == 0) (void) thrash_rc(); (void) fix_options(); /* adjust "options" according to "debug" */ fromatty = isatty(fileno(stdin)); cpend = 0; /* no pending replies */ if (*logfname) logf = fopen (logfname, "a"); eventnumber = 0L; /* The user specified a host on the command line. Open it now... */ if (argc > 1 && cp) { if (setjmp(toplevel)) exit(0); (void) signal(SIGINT, intr); (void) signal(SIGPIPE, lostpeer); (void) strcpy(line, oline); makeargv(); (void) setpeer(margc, margv); } (void) init_prompt(); eventnumber = 1L; if (ansi_escapes) { #ifndef CURSES (void) printf("%s%s Ready.%s\n", tcap_boldface, FTP_VERSION, tcap_normal); #else string vis; (void) sprintf(vis, "%s%s Ready.%s\n", tcap_boldface, FTP_VERSION, tcap_normal); tcap_put(vis); #endif /* !CURSES */ } else (void) printf("%s Ready.\n", FTP_VERSION); top = setjmp(toplevel) == 0; if (top) { (void) signal(SIGINT, intr); (void) signal(SIGPIPE, lostpeer); } for (;;) { (void) cmdscanner(top); top = 1; } } /* main */ /*ARGSUSED*/ void intr(int unused) { (void) longjmp(toplevel, 1); } /* intr */ int getuserinfo(void) { register char *cp; struct passwd *pw = NULL; string str; extern char *home; /* for glob.c */ cp = getlogin(); if (cp != NULL) pw = getpwnam(cp); if (pw == NULL) pw = getpwuid(getuid()); if (pw != NULL) { (void) Strncpy(uinfo.username, pw->pw_name); (void) Strncpy(uinfo.shell, pw->pw_shell); (void) Strncpy(uinfo.homedir, pw->pw_dir); uinfo.uid = pw->pw_uid; home = uinfo.homedir; /* for glob.c */ if (((cp = getenv("MAIL")) == NULL) && ((cp = getenv("mail")) == NULL)) { (void) sprintf(str, "/usr/spool/mail/%s", uinfo.username); cp = str; } /* mbox variable may be like MAIL=(28 /usr/mail/me /usr/mail/you), so try to find the first mail path. */ while (*cp != '/') cp++; (void) Strncpy(mail_path, cp); if ((cp = index(mail_path, ' ')) != NULL) *cp = '\0'; return (0); } else { (void) Strncpy(uinfo.username, "unknown"); (void) Strncpy(uinfo.shell, "/bin/sh"); (void) Strncpy(uinfo.homedir, "."); /* current directory */ uinfo.uid = 999; return (-1); } } /* getuserinfo */ int init_arrays(void) { if ((macbuf = (char *) malloc((size_t)(MACBUFLEN))) == NULL) goto barf; if ((line = (char *) malloc((size_t)(CMDLINELEN))) == NULL) goto barf; if ((argbuf = (char *) malloc((size_t)(CMDLINELEN))) == NULL) goto barf; if ((reply_string = (char *) malloc((size_t)(RECEIVEDLINELEN))) == NULL) goto barf; *macbuf = '\0'; init_transfer_buffer(); return (0); barf: return (-1); } /* init_arrays */ #ifndef BUFSIZ #define BUFSIZ 512 #endif void init_transfer_buffer(void) { extern char *xferbuf; extern size_t xferbufsize; /* Make sure we use a multiple of BUFSIZ for efficiency. */ xferbufsize = (MAX_XFER_BUFSIZE / BUFSIZ) * BUFSIZ; while (1) { xferbuf = (char *) malloc (xferbufsize); if (xferbuf != NULL || xferbufsize < 1024) break; xferbufsize >>= 2; } if (xferbuf != NULL) return; fatal("out of memory for transfer buffer."); } /* init_transfer_buffer */ void init_prompt(void) { register char *cp; percent_flags = at_flags = 0; for (cp = prompt; *cp; cp++) { if (*cp == '%') percent_flags = 1; else if (*cp == '@') at_flags = 1; } } /* init_prompt */ /*ARGSUSED*/ void lostpeer(int unused) { if (connected) { close_streams(1); if (data >= 0) { (void) shutdown(data, 1+1); (void) close(data); data = -1; } connected = 0; } if (connected) { close_streams(1); connected = 0; } hostname[0] = cwd[0] = 0; macnum = 0; } /* lostpeer */ /* * Command parser. */ void cmdscanner(int top) { register struct cmd *c; #ifdef CURSES string vis, *vp; #endif if (!top) (void) putchar('\n'); for (;;) { if (fromatty) { #ifndef CURSES (void) printf(strprompt()); #else (void) Strncpy(vis, (strprompt())); tcap_put(vis); #endif /* !CURSES */ (void) fflush(stdout); } if (Gets(line, (size_t)CMDLINELEN) == 0) { if (feof(stdin) || ferror(stdin)) (void) quit(0, NULL); /* control-d */ break; } if (line[0] == 0) /* blank line */ break; eventnumber++; if (debug > 1) (void) printf("---> \"%s\"\n", line); (void) makeargv(); if (margc == 0) { continue; /* blank line... */ } c = getcmd(margv[0]); if (c == (struct cmd *) -1) { (void) printf("?Ambiguous command\n"); continue; } if (c == 0) { if (!implicit_cd(margv[0])) (void) printf("?Invalid command\n"); continue; } if (c->c_conn && !connected) { (void) printf ("Not connected.\n"); continue; } (*c->c_handler)(margc, margv); if (c->c_handler != help) break; } (void) signal(SIGINT, intr); (void) signal(SIGPIPE, lostpeer); } /* cmdscanner */ char *strprompt(void) { time_t tyme; char eventstr[8]; register char *p, *q; string str; #ifdef CURSES static int virgin = 0; if (!virgin++ && ansi_escapes) termcap_init(); #endif /* CURSES */ if (at_flags == 0 && percent_flags == 0) return (prompt); /* But don't overwrite it! */ if (at_flags) { for (p = prompt, q = prompt2, *q = 0; (*p); p++) if (*p == '@') switch (islower(*p) ? (toupper(*++p)) : (*++p)) { case '\0': --p; break; case 'M': if (CheckNewMail() > 0) q = Strpcpy(q, "(Mail) "); break; case 'N': q = Strpcpy(q, "\n"); break; case 'P': /* reset to no bold, no uline, no inverse, etc. */ if (ansi_escapes) q = Strpcpy(q, tcap_normal); break; case 'B': /* toggle boldface */ if (ansi_escapes) q = Strpcpy(q, tcap_boldface); break; case 'U': /* toggle underline */ if (ansi_escapes) q = Strpcpy(q, tcap_underline); break; case 'R': case 'I': /* toggle inverse (reverse) video */ if (ansi_escapes) q = Strpcpy(q, tcap_reverse); break; case 'D': /* insert current directory */ if (cwd != NULL) q = Strpcpy(q, cwd); break; case 'H': /* insert name of connected host */ if (hostname != NULL) q = Strpcpy(q, hostname); break; case '!': case 'E': /* insert event number */ (void) sprintf(eventstr, "%ld", eventnumber); q = Strpcpy(q, eventstr); break; default: *q++ = *p; /* just copy it; unknown switch */ } else *q++ = *p; *q = '\0'; } else (void) strcpy(prompt2, prompt); if (percent_flags) { /* only strftime if the user requested it (with a %something), otherwise don't waste time doing nothing. */ (void) time(&tyme); (void) Strncpy(str, prompt2); (void) strftime(prompt2, sizeof(str), str, localtime(&tyme)); } return (prompt2); } /* strprompt */ char *Strpcpy(char *dst, char *src) { while (*dst++ = *src++) ; return (--dst); /* return current value of dst, NOT original value! */ } /* Strpcpy */ struct cmd *getcmd(char *name) { register char *p, *q; register struct cmd *c, *found; register int nmatches, longest; if (name == NULL) return (NULL); longest = 0; nmatches = 0; found = 0; for (c = cmdtab; p = c->c_name; c++) { for (q = name; *q == *p++; q++) if (*q == 0) /* exact match? */ return (c); if (!*q) { /* the name was a prefix */ 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); } /* getcmd */ /* * Slice a string up into argc/argv. */ void makeargv(void) { char **argp; margc = 0; argp = margv; stringbase = line; /* scan from first of buffer */ argbase = argbuf; /* store from first of buffer */ slrflag = 0; while (*argp++ = slurpstring()) margc++; } /* makeargv */ /* * Parse string into argbuf; * implemented with FSM to * handle quoting and strings */ char *slurpstring(void) { int got_one = 0; register char *sb = stringbase; register char *ap = argbase; char *tmp = argbase; /* will return this if token found */ if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ switch (slrflag) { /* and $ as token for macro invoke */ case 0: slrflag++; stringbase++; return ((*sb == '!') ? "!" : "$"); /* NOTREACHED */ case 1: slrflag++; altarg = stringbase; break; default: break; } } S0: switch (*sb) { case '\0': goto OUT; case ' ': case '\t': case '\n': case '=': 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 '\n': case '=': case '\0': goto OUT; /* end of token */ case '\\': sb++; goto S2; /* slurp next character */ case '"': sb++; goto S3; /* slurp quoted string */ default: *ap++ = *sb++; /* add character to token */ got_one = 1; goto S1; } S2: switch (*sb) { case '\0': goto OUT; default: *ap++ = *sb++; got_one = 1; goto S1; } S3: switch (*sb) { case '\0': goto OUT; case '"': sb++; goto S1; default: *ap++ = *sb++; got_one = 1; goto S3; } OUT: if (got_one) *ap++ = '\0'; argbase = ap; /* update storage pointer */ stringbase = sb; /* update scan pointer */ if (got_one) { return(tmp); } switch (slrflag) { case 0: slrflag++; break; case 1: slrflag++; altarg = (char *) 0; break; default: break; } return((char *)0); } /* slurpstring */ #define HELPINDENT (sizeof ("directory")) /* * Help command. * Call each command handler with argc == 0 and argv[0] == name. */ help(int argc, char **argv) { register struct cmd *c; int i, showall = 0; char *arg; if (argc == 2) showall = strcmp(argv[1], "all") == 0; if (argc == 1 || showall) { (void) printf("Commands may be abbreviated. 'help all' shows aliases,\ninvisible and unsupported commands. 'help <command>' \ngives a brief description of <command>. Commands are:\n"); for (c = cmdtab, i=0; c->c_name != NULL; c++) { if (c->c_hidden && !showall) continue; (void) printf("%-13s", c->c_name); if (++i == 6) { i = 0; putchar('\n'); } } if (i < 6) putchar('\n'); } else while (--argc > 0) { 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); } } /* help */ /* * If the user wants to, s/he can specify the maximum size of the log * file, so it doesn't waste too much disk space. If the log is too * fat, trim the older lines (at the top) until we're under the limit. */ void trim_log(void) { FILE *new, *old; struct stat st; long fat; string tmplogname, str; if (logsize <= 0 || *logfname == 0 || stat(logfname, &st) || (old = fopen(logfname, "r")) == NULL) return; /* never trim, or no log */ fat = st.st_size - logsize; if (fat <= 0L) return; /* log too small yet */ while (fat > 0L) { if (FGets(str, old) == NULL) return; fat -= (long) strlen(str); } /* skip lines until a new site was opened */ while (1) { if (FGets(str, old) == NULL) { (void) fclose(old); (void) unlink(logfname); return; /* nothing left, start anew */ } if (*str != '\t') break; } /* copy the remaining lines in "old" to "new" */ (void) Strncpy(tmplogname, logfname); tmplogname[strlen(tmplogname) - 1] = 'T'; if ((new = fopen(tmplogname, "w")) == NULL) { (void) Perror(tmplogname); return; } (void) fputs(str, new); while (FGets(str, old)) (void) fputs(str, new); (void) fclose(old); (void) fclose(new); if (unlink(logfname) < 0) Perror(logfname); if (rename(tmplogname, logfname) < 0) Perror(tmplogname); } /* trim_log */ int CheckNewMail(void) { struct stat stbuf; if (*mail_path == '\0') return 0; if (stat(mail_path, &stbuf) < 0) { /* cant find mail_path so we'll */ *mail_path = '\0'; /* never check it again */ return 0; } if (stbuf.st_mtime > mbox_time) { newmail++; (void) printf("%s\n", NEWMAILMESSAGE); (void) time(&mbox_time); /* only notify once. */ } return newmail; } /* CheckNewMail */ #ifdef CURSES void termcap_init(void) { static char area[1024]; static char *s = area; char *tgetstr(char *, char **); char *term; if (tgetent(tcbuf,(term = getenv("TERM"))) != 1) { (void) fprintf(stderr,"Can't get termcap entry for terminal [%s]\n", term); } else { if (!(tcap_normal = tgetstr("se", &s))) tcap_normal = ""; if (!(tcap_boldface = tgetstr("md", &s))) tcap_boldface = ""; if (!(tcap_underline = tgetstr("us", &s))) tcap_underline = ""; if (!(tcap_reverse = tgetstr("so", &s))) tcap_reverse = ""; } } /* termcap_init */ static int c_output(int c) { putchar(c); } /* c_output */ void tcap_put(char *cap) { tputs(cap, 0, c_output); } /* tcap_put */ #endif /* CURSES */ /* eof main.c */