/* main.c */ #define _main_c_ #define FTP_VERSION "1.9.4 (April 15, 1995)" /* #define BETA 1 */ /* If defined, it prints a little warning message. */ #include "sys.h" #include #include #include #include #include #include #include #include #ifdef SYSLOG # include #endif #if defined(CURSES) && !defined(NO_CURSES_H) # undef HZ /* Collides with HaZeltine ! */ # include # ifdef TERMH # include # endif #endif /* CURSES */ #include "util.h" #include "cmds.h" #include "main.h" #include "ftp.h" #include "ftprc.h" #include "open.h" #include "set.h" #include "defaults.h" #include "copyright.h" /* main.c globals */ int slrflag; int fromatty; /* input is from a terminal */ int toatty; /* output is to a terminal */ int doing_script; /* is a file being = 0) { switch(opt) { case 'a': case 'c': case 'i': case 'm': 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': debug = atoi(Optarg); break; case 'V': set_verbose(Optarg, 0); break; case 'I': mprompt = !mprompt; break; case 'N': ++ignore_rc; break; case 'P': passivemode = !passivemode; break; case 'H': (void) 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 x : Set debugging level to x (a number).\n\ -H : Show version and compilation information.\n\ -I : Toggle interactive (mprompt) mode.\n\ -N : Toggle reading of the .netrc/.ncftprc.\n\ -P : Toggle passive mode ftp (for use behind firewalls).\n\ -V x : Set verbosity to level x (-1,0,1,2).\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 : ``Colon-mode:'' If \"path\" is a file, it opens site, retrieves\n\ file \"path,\" then exits; if \"path\" is a remote directory,\n\ it opens site then starts you in that directory..\n\ -c : If you're using colon-mode with a file path, this will cat the\n\ file to stdout instead of storing on disk.\n\ -m : Just like -c, only it pipes the file to your $PAGER.\n\ Examples:\n\ ncftp ftp.unl.edu:/pub/README (just fetches README then quits)\n\ ncftp (just enters ncftp command shell)\n\ ncftp -V -u ftp.unl.edu\n\ ncftp -c ftp.unl.edu:/pub/README (cats README to stdout then quits)\n\ ncftp -D -r -d 120 -g 10 ftp.unl.edu\n", 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(); if (ignore_rc <= 1) ReadRecentSitesFile(); (void) fix_options(); /* adjust "options" according to "debug" */ fromatty = doing_script = isatty(0); toatty = isatty(1); (void) UserLoggedIn(); /* Init parent-death detection. */ cpend = 0; /* no pending replies */ if (*logfname) logf = fopen (logfname, "a"); /* The user specified a host, maybe in 'colon-mode', 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(); /* setpeer uses this to tell if it was called from the cmd-line. */ eventnumber = 0L; if (cmdOpen(margc, margv) != NOERR) { exit(1); } } eventnumber = 1L; (void) init_prompt(); if (startup_msg) { /* TAR */ if (ansi_escapes) { #ifdef BETA # define BETA_MSG "\n\ For testing purposes only. Do not re-distribute or subject to novice users." #else # define BETA_MSG "" #endif #ifndef CURSES (void) printf("%sNcFTP %s by Mike Gleason, NCEMRSoft.%s%s%s%s\n", tcap_boldface, FTP_VERSION, tcap_normal, tcap_reverse, BETA_MSG, tcap_normal ); #else char vis[256]; (void) sprintf(vis, "%sNcFTP %s by Mike Gleason, NCEMRSoft.%s%s%s%s\n", tcap_boldface, FTP_VERSION, tcap_normal, tcap_reverse, BETA_MSG, tcap_normal ); tcap_put(vis); #endif /* !CURSES */ } else (void) printf("%s%s\n", FTP_VERSION, BETA_MSG); } /* TAR */ if (NOT_VQUIET) PrintTip(); top = setjmp(toplevel) == 0; if (top) { (void) Signal(SIGINT, intr); (void) Signal(SIGPIPE, lostpeer); } for (;;) { if (cmdscanner(top) && !fromatty) exit(1); top = 1; } } /* main */ /*ARGSUSED*/ void intr SIG_PARAMS { dbprintf("intr()\n"); (void) Signal(SIGINT, intr); (void) longjmp(toplevel, 1); } /* intr */ int getuserinfo(void) { register char *cp; struct passwd *pw; string str; extern char *home; /* for glob.c */ home = uinfo.homedir; /* for glob.c */ pw = NULL; #ifdef USE_GETPWUID /* Try to use getpwuid(), but if we have to, fall back to getpwnam(). */ pw = getpwuid(getuid()); if (pw == NULL) { /* Oh well, try getpwnam() then. */ cp = getlogin(); if (cp == NULL) { cp = getenv("LOGNAME"); if (cp == NULL) cp = getenv("USER"); } if (cp != NULL) pw = getpwnam(cp); } #else /* Try to use getpwnam(), but if we have to, fall back to getpwuid(). */ cp = getlogin(); if (cp == NULL) { cp = getenv("LOGNAME"); if (cp == NULL) cp = getenv("USER"); } if (cp != NULL) pw = getpwnam(cp); if (pw == NULL) { /* Oh well, try getpwuid() then. */ pw = getpwuid(getuid()); } #endif if (pw != NULL) { uinfo.uid = pw->pw_uid; (void) Strncpy(uinfo.username, pw->pw_name); (void) Strncpy(uinfo.shell, pw->pw_shell); if ((cp = getenv("HOME")) != NULL) (void) Strncpy(uinfo.homedir, cp); else (void) Strncpy(uinfo.homedir, pw->pw_dir); cp = getenv("MAIL"); if (cp != NULL) { (void) Strncpy(str, cp); } else { /* Check a few typical mail directories. * If we don't find it, too bad. Checking for new mail * isn't very important anyway. */ (void) sprintf(str, "/usr/spool/mail/%s", uinfo.username); if (access(str, 0) < 0) { (void) sprintf(str, "/var/mail/%s", uinfo.username); } } cp = str; if (access(cp, 0) < 0) { mail_path[0] = 0; } else { /* * mbox variable may be like MAIL=(28 /usr/mail/me /usr/mail/you), * so try to find the first mail path. */ while ((*cp != '/') && (*cp != 0)) cp++; (void) Strncpy(mail_path, cp); if ((cp = index(mail_path, ' ')) != NULL) *cp = '\0'; } return (0); } else { PERROR("getuserinfo", "Could not get your passwd entry!"); (void) Strncpy(uinfo.shell, "/bin/sh"); (void) Strncpy(uinfo.homedir, "."); /* current directory */ uinfo.uid = 999; if ((cp = getenv("HOME")) != NULL) (void) Strncpy(uinfo.homedir, cp); mail_path[0] = 0; 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 SIG_PARAMS { 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; logged_in = macnum = 0; } /* lostpeer */ /* * Command parser. */ int cmdscanner(int top) { register struct cmd *c; int cmd_status, rcode = 0; if (!top) (void) putchar('\n'); for (;;) { if (!doing_script && !UserLoggedIn()) (void) quit(0, NULL); if (Gets(strprompt(), line, (size_t)CMDLINELEN) == NULL) { (void) quit(0, NULL); /* control-d */ } eventnumber++; dbprintf("\"%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; } cmd_status = (*c->c_handler)(margc, margv); if (cmd_status == USAGE) cmd_usage(c); else if (cmd_status == CMDERR) rcode = 1; if (c->c_handler != help) break; } (void) Signal(SIGINT, intr); (void) Signal(SIGPIPE, lostpeer); return rcode; } /* cmdscanner */ char *strprompt(void) { time_t tyme; char eventstr[8]; char *dname, *lastlinestart; register char *p, *q; string str; int flag; if (at_flags == 0 && percent_flags == 0) { epromptlen = strlen(prompt); return (prompt); /* But don't overwrite it! */ } epromptlen = 0; lastlinestart = prompt2; if (at_flags) { for (p = prompt, q = prompt2, *q = 0; (*p); p++) { if (*p == '@') switch (flag = *++p) { case '\0': --p; break; case 'M': if (CheckNewMail() > 0) q = Strpcpy(q, "(Mail) "); break; case 'N': q = Strpcpy(q, "\n"); lastlinestart = q; epromptlen = 0; break; case 'P': /* reset to no bold, no uline, no inverse, etc. */ if (ansi_escapes) { q = Strpcpy(q, tcap_normal); epromptlen += tcl_normal; } break; case 'B': /* toggle boldface */ if (ansi_escapes) { q = Strpcpy(q, tcap_boldface); epromptlen += tcl_bold; } break; case 'U': /* toggle underline */ if (ansi_escapes) { q = Strpcpy(q, tcap_underline); epromptlen += tcl_uline; } break; case 'R': case 'I': /* toggle inverse (reverse) video */ if (ansi_escapes) { q = Strpcpy(q, tcap_reverse); epromptlen += tcl_rev; } break; case 'D': /* insert current directory */ case 'J': if ((flag == 'J') && (remote_is_unix)) { /* Not the whole path, just the dir name. */ dname = rindex(cwd, '/'); if (dname == NULL) dname = cwd; else if ((dname != cwd) && (dname[1])) ++dname; } else dname = cwd; if (dname[0]) { q = Strpcpy(q, dname); q = Strpcpy(q, " "); } break; case 'H': /* insert name of connected host */ if (logged_in) { (void) sprintf(str, "%s ", hostname); q = Strpcpy(q, str); } break; case 'C': /* Insert host:path (colon-mode format. */ if (logged_in) { (void) sprintf(str, "%s:%s ", hostname, cwd); q = Strpcpy(q, str); } else q = Strpcpy(q, "(not connected)"); break; case 'c': if (logged_in) { (void) sprintf(str, "%s:%s\n", hostname, cwd); q = Strpcpy(q, str); lastlinestart = q; /* there is a \n at the end. */ epromptlen = 0; } 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); #ifndef NO_STRFTIME 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)); } #endif epromptlen = (size_t) ((long) strlen(lastlinestart) - (long) epromptlen); return (prompt2); } /* strprompt */ /* * 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()) != 0) 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 */ /* * Help command. * Call each command handler with argc == 0 and argv[0] == name. */ int help(int argc, char **argv) { register struct cmd *c; int showall = 0, helpall = 0; char *arg; int i, j, k; int nRows, nCols; int nCmds2Print; int screenColumns; int len, widestName; char *cp, **cmdnames, spec[16]; if (argc == 2) { showall = (strcmp(argv[1], "showall") == 0); helpall = (strcmp(argv[1], "helpall") == 0); } if (argc == 1 || showall) { (void) printf("\ Commands may be abbreviated. 'help showall' shows aliases, invisible and\n\ unsupported commands. 'help ' gives a brief description of .\n\n"); for (c = cmdtab, nCmds2Print=0; c->c_name != NULL; c++) if (!c->c_hidden || showall) nCmds2Print++; if ((cmdnames = (char **) malloc(sizeof(char *) * nCmds2Print)) == NULL) fatal("out of memory!"); for (c = cmdtab, i=0, widestName=0; c->c_name != NULL; c++) { if (!c->c_hidden || showall) { cmdnames[i++] = c->c_name; len = (int) strlen(c->c_name); if (len > widestName) widestName = len; } } if ((cp = getenv("COLUMNS")) == NULL) screenColumns = 80; else screenColumns = atoi(cp); widestName += 2; /* leave room for white-space in between cols. */ nCols = screenColumns / widestName; /* if ((screenColumns % widestName) > 0) nCols++; */ nRows = nCmds2Print / nCols; if ((nCmds2Print % nCols) > 0) nRows++; (void) sprintf(spec, "%%-%ds", widestName); for (i=0; ic_name != NULL; c++) { cmd_help(c); cmd_usage(c); } } 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 { cmd_help(c); cmd_usage(c); } } return NOERR; } /* 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("trim_log", 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("trim_log", logfname); if (rename(tmplogname, logfname) < 0) PERROR("trim_log", 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; } /* * Check if the size is non-zero and the access time is less than * the modify time -- this indicates unread mail. */ if ((stbuf.st_size != 0) && (stbuf.st_atime <= stbuf.st_mtime)) { if (stbuf.st_mtime > mbox_time) { (void) printf("%s\n", NEWMAILMESSAGE); mbox_time = stbuf.st_mtime; } return 1; } return 0; } /* CheckNewMail */ #ifdef CURSES int termcap_get(char **dest, char *attr) { static char area[1024]; static char *s = area; char *buf, *cp; int i, result = -1; int len = 0; *dest = NULL; while (*attr != '\0') { buf = tgetstr(attr, &s); if (buf != NULL && buf[0] != '\0') { for (i = 0; (buf[i] <= '9') && (buf[i] >= '0'); ) i++; /* Get rid of the terminal delays, like "$<2>". */ if ((cp = strstr(&(buf[i]), "$<")) != NULL) *cp = 0; if (*dest == NULL) *dest = (char *)malloc(strlen(&(buf[i])) + 1); else *dest = (char *)realloc(*dest, len + strlen(&(buf[i])) + 1); if (*dest == NULL) break; (void) strcpy(*dest + len, &(buf[i])); len += strlen (&(buf[i])); } attr += 2; } if (*dest == NULL) *dest = ""; else result = 0; return (result); } /* termcap_get */ void termcap_init(void) { char *term; if ((term = getenv("TERM")) == NULL) { term = "dumb"; /* TAR */ ansi_escapes = 0; } if (tgetent(tcbuf,term) != 1) { (void) fprintf(stderr,"Can't get termcap entry for terminal [%s]\n", term); } else { (void) termcap_get(&tcap_normal, "meuese"); if (termcap_get(&tcap_boldface, "md") < 0) { /* Dim-mode is better than nothing... */ (void) termcap_get(&tcap_boldface, "mh"); } (void) termcap_get(&tcap_underline, "us"); (void) termcap_get(&tcap_reverse, "so"); tcl_normal = strlen(tcap_normal); tcl_bold = strlen(tcap_boldface); tcl_uline = strlen(tcap_underline); tcl_rev = strlen(tcap_reverse); } } /* termcap_init */ static int c_output(int c) { return (putchar(c)); } /* c_output */ void tcap_put(char *cap) { tputs(cap, 0, c_output); } /* tcap_put */ #endif /* CURSES */ /* eof main.c */