freebsd-skq/usr.bin/ncftp/open.c
1995-11-23 20:19:56 +00:00

645 lines
18 KiB
C

/* open.c */
/* $RCSfile: open.c,v $
* $Revision: 1.1 $
* $Date: 93/07/09 11:27:07 $
*/
#include "sys.h"
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/ftp.h>
#include <errno.h>
#include "util.h"
#include "open.h"
#include "cmds.h"
#include "ftp.h"
#include "ftprc.h"
#include "main.h"
#include "defaults.h"
#include "copyright.h"
/* open.c globals */
int remote_is_unix; /* TRUE if remote host is unix. */
int auto_binary = dAUTOBINARY;
int anon_open = dANONOPEN;
/* Anonymous logins by default? */
int connected = 0; /* TRUE if connected to server */
/* If TRUE, set binary each connection. */
int www = 0; /* TRUE if use URL */
Hostname hostname; /* Name of current host */
RemoteSiteInfo gRmtInfo;
#ifdef GATEWAY
string gateway; /* node name of firewall gateway */
string gate_login; /* login at firewall gateway */
#endif
/* open.c externs */
extern char *reply_string, *line, *Optarg, *margv[];
extern int Optind, margc, verbose, macnum;
extern long eventnumber;
extern struct servent serv;
extern FILE *cout;
extern string anon_password;
/* Given a pointer to an OpenOptions (structure containing all variables
* that can be set from the command line), this routine makes sure all
* the variables have valid values by setting them to their defaults.
*/
void InitOpenOptions(OpenOptions *openopt)
{
/* How do you want to open a site if neither -a or -u are given?
* anon_open is true (default to anonymous login), unless
* defaults.h was edited to set dANONOPEN to 0 instead.
*/
openopt->openmode = anon_open ? openImplicitAnon : openImplicitUser;
/* Normally you don't want to ignore the entry in your netrc. */
openopt->ignore_rc = 0;
/* Set the default delay if the user specifies redial mode without
* specifying the redial delay.
*/
openopt->redial_delay = dREDIALDELAY;
/* Normally, you only want to try once. If you specify redial mode,
* this is changed.
*/
openopt->max_dials = 1;
/* You don't want to cat the file to stdout by default. */
openopt->ftpcat = NO_FTPCAT;
/* Setup the port number to try. */
#ifdef dFTP_PORT
/* If dFTP_PORT is defined, we use a different port number by default
* than the one supplied in the servent structure.
*/
openopt->port = dFTP_PORT;
/* Make sure the correct byte order is supplied! */
openopt->port = htons(openopt->port);
#else
/* Use the port number supplied by the operating system's servent
* structure.
*/
openopt->port = serv.s_port;
#endif
/* We are not in colon-mode (yet). */
openopt->colonmodepath[0] = 0;
/* Set the hostname to a null string, since there is no default host. */
openopt->hostname[0] = 0;
/* Set the opening directory path to a null string. */
openopt->cdpath[0] = 0;
} /* InitOpenOptions */
/* This is responsible for parsing the command line and setting variables
* in the OpenOptions structure according to the user's flags.
*/
int GetOpenOptions(int argc, char **argv, OpenOptions *openopt)
{
int opt;
char *cp, *hostp, *cpath;
/* First setup the openopt variables. */
InitOpenOptions(openopt);
/* Tell Getopt() that we want to start over with a new command. */
Getopt_Reset();
while ((opt = Getopt(argc, argv, "aiup:rd:g:cm")) >= 0) {
switch (opt) {
case 'a':
/* User wants to open anonymously. */
openopt->openmode = openExplicitAnon;
break;
case 'u':
/* User wants to open with a login and password. */
openopt->openmode = openExplicitUser;
break;
case 'i':
/* User wants to ignore the entry in the netrc. */
openopt->ignore_rc = 1;
break;
case 'p':
/* User supplied a port number different from the default
* ftp port.
*/
openopt->port = atoi(Optarg);
if (openopt->port <= 0) {
/* Probably never happen, but just in case. */
(void) printf("%s: bad port number (%s).\n", argv[0], Optarg);
goto usage;
}
/* Must ensure that the port is in the correct byte order! */
openopt->port = htons(openopt->port);
break;
case 'd':
/* User supplied a delay (in seconds) that differs from
* the default.
*/
openopt->redial_delay = atoi(Optarg);
break;
case 'g':
/* User supplied an upper-bound on the number of redials
* to try.
*/
openopt->max_dials = atoi(Optarg);
break;
case 'r':
openopt->max_dials = -1;
break;
case 'm':
/* ftpcat mode is only available from your shell command-line,
* not from the ncftp shell. Do that yourself with 'more zz'.
*/
if (eventnumber == 0L) {
/* If eventnumber is zero, then we were called directly
* from main(), and before the ftp shell has started.
*/
openopt->ftpcat = FTPMORE;
/* ftpcat mode is really ftpmore mode. */
break;
} else {
fprintf(stderr,
"You can only use this form of colon-mode (-m) from your shell command line.\n\
Try 'ncftp -m wuarchive.wustl.edu:/README'\n");
goto usage;
}
/* break; */
case 'c':
/* ftpcat mode is only available from your shell command-line,
* not from the ncftp shell. Do that yourself with 'get zz -'.
*/
if (eventnumber == 0L) {
/* If eventnumber is zero, then we were called directly
* from main(), and before the ftp shell has started.
*/
openopt->ftpcat = FTPCAT;
break;
} else {
fprintf(stderr,
"You can only use ftpcat/colon-mode from your shell command line.\n\
Try 'ncftp -c wuarchive.wustl.edu:/README > file.'\n");
goto usage;
}
/* break; */
default:
usage:
return USAGE;
}
}
if (argv[Optind] == NULL) {
/* No host was supplied. Print out the list of sites we know
* about and ask the user for one.
*/
PrintSiteList();
(void) Gets("(site to open) ", openopt->hostname, sizeof(openopt->hostname));
/* Make sure the user just didn't hit return, in which case we
* just give up and go home.
*/
if (openopt->hostname[0] == 0)
goto usage;
} else {
/* The user gave us a host to open.
*
* First, check to see if they gave us a colon-mode path
* along with the hostname. We also understand a WWW path,
* like "ftp://bang.nta.no/pub/fm2html.v.0.8.4.tar.Z".
*/
hostp = argv[Optind];
cpath = NULL;
if ((cp = index(hostp, ':')) != NULL) {
*cp++ = '\0';
cpath = cp;
www = 0; /* Is 0 or 1, depending on the type of path. */
if ((*cp == '/') && (cp[1] == '/')) {
/* First make sure the path was intended to be used
* with ftp and not one of the other URLs.
*/
if (strcmp(argv[Optind], "ftp")) {
fprintf(
stderr,
"Bad URL '%s' -- WWW paths must be prefixed by 'ftp://'.\n",
argv[Optind]
);
goto usage;
}
cp += 2;
hostp = cp;
cpath = NULL; /* It could have been ftp://hostname only. */
if ((cp = index(hostp, '/')) != NULL) {
*cp++ = '\0';
cpath = cp;
}
www = 1;
}
if (cpath != NULL) {
(void) Strncpy(openopt->colonmodepath, www ? "/" : "");
(void) Strncat(openopt->colonmodepath, cpath);
dbprintf("Colon-Mode Path = '%s'\n", openopt->colonmodepath);
}
}
(void) Strncpy(openopt->hostname, hostp);
dbprintf("Host = '%s'\n", hostp);
}
return NOERR;
} /* GetOpenOptions */
/* This examines the format of the string stored in the hostname
* field of the OpenOptions, and sees if has to strip out a colon-mode
* pathname (to store in the colonmodepath field). Since colon-mode
* is run quietly (without any output being generated), we init the
* login_verbosity variable here to quiet if we are running colon-mode.
*/
int CheckForColonMode(OpenOptions *openopt, int *login_verbosity)
{
/* Usually the user doesn't supply hostname in colon-mode format,
* and wants to interactively browse the remote host, so set the
* login_verbosity to whatever it is set to now.
*/
*login_verbosity = verbose;
if (openopt->colonmodepath[0] != 0) {
/* But if the user does use colon-mode, we want to do our business
* and leave, without all the login messages, etc., so set
* login_verbosity to quiet so we won't print anything until
* we finish. Colon-mode can be specified from the shell command
* line, so we would like to be able to execute ncftp as a one
* line command from the shell without spewing gobs of output.
*/
*login_verbosity = V_QUIET;
} else if (openopt->ftpcat != 0) {
/* User specified ftpcat mode, but didn't supply the host:file. */
(void) fprintf(stderr, "You didn't use colon mode correctly.\n\
If you use -c or -m, you need to do something like this:\n\
ncftp -c wuarchive.wustl.edu:/pub/README (to cat this file to stdout).\n");
return USAGE;
}
return NOERR;
} /* CheckForColonMode */
/* All this short routine does is to hookup a socket to either the
* remote host or the firewall gateway host.
*/
int HookupToRemote(OpenOptions *openopt)
{
int hErr;
#ifdef GATEWAY
/* Try connecting to the gateway host. */
if (*gateway) {
hErr = hookup(gateway, openopt->port);
(void) Strncpy(hostname, openopt->hostname);
} else
#endif
hErr = hookup(openopt->hostname, openopt->port);
return hErr;
} /* HookupToRemote */
void CheckRemoteSystemType(int force_binary)
{
int tmpverbose;
char *cp, c;
/* As of this writing, UNIX is pretty much standard. */
remote_is_unix = 1;
/* Do a SYSTem command quietly. */
tmpverbose = verbose;
verbose = V_QUIET;
if (command("SYST") == COMPLETE) {
if (tmpverbose == V_VERBOSE) {
/* Find the system type embedded in the reply_string,
* and separate it from the rest of the junk.
*/
cp = index(reply_string+4, ' ');
if (cp == NULL)
cp = index(reply_string+4, '\r');
if (cp) {
if (cp[-1] == '.')
cp--;
c = *cp;
*cp = '\0';
}
(void) printf("Remote system type is %s.\n",
reply_string+4);
if (cp)
*cp = c;
}
remote_is_unix = !strncmp(reply_string + 4, "UNIX", (size_t) 4);
}
/* Set to binary mode if any of the following are true:
* (a) The user has auto-binary set;
* (b) The user is using colon-mode (force_binary);
* (c) The reply-string from SYST said it was UNIX with 8-bit chars.
*/
if (auto_binary || force_binary
|| !strncmp(reply_string, "215 UNIX Type: L8", (size_t) 17)) {
(void) _settype("binary");
if (tmpverbose > V_TERSE)
(void) printf("Using binary mode to transfer files.\n");
}
/* Print a warning for that (extremely) rare Tenex machine. */
if (tmpverbose >= V_ERRS &&
!strncmp(reply_string, "215 TOPS20", (size_t) 10)) {
(void) _settype("tenex");
(void) printf("Using tenex mode to transfer files.\n");
}
verbose = tmpverbose;
} /* CheckRemoteSystemType */
/* This is called if the user opened the host with a file appended to
* the host's name, like "wuarchive.wustl.edu:/pub/readme," or
* "wuarchive.wustl.edu:/pub." In the former case, we open wuarchive,
* and fetch "readme." In the latter case, we open wuarchive, then set
* the current remote directory to "/pub." If we are fetching a file,
* we can do some other tricks if "ftpcat mode" is enabled. This mode
* must be selected from your shell's command line, and this allows you
* to use the program as a one-liner to pipe a remote file into something,
* like "ncftp -c wu:/pub/README | wc." If the user uses ftpcat mode,
* the program immediately quits instead of going into it's own command
* shell.
*/
void ColonMode(OpenOptions *openopt)
{
int tmpverbose;
int cmdstatus;
/* How do we tell if colonmodepath is a file or a directory?
* We first try cd'ing to the path first. If we can, then it
* was a directory. If we could not, we'll assume it was a file.
*/
/* Shut up, so cd won't print 'foobar: Not a directory.' */
tmpverbose = verbose;
verbose = V_QUIET;
/* If we are using ftpcat|more mode, or we couldn't cd to the
* colon-mode path (then it must be a file to fetch), then
* we need to fetch a file.
*/
if (openopt->ftpcat || ! _cd(openopt->colonmodepath)) {
/* We call the appropriate fetching routine, so we have to
* have the argc and argv set up correctly. To do this,
* we just make an entire command line, then let makeargv()
* convert it to argv/argc.
*/
if (openopt->ftpcat == FTPCAT)
(void) sprintf(line, "get %s -", openopt->colonmodepath);
else if (openopt->ftpcat == FTPMORE)
(void) sprintf(line, "more %s", openopt->colonmodepath);
else {
/* Regular colon-mode, where we fetch the file, putting the
* copy in the current local directory.
*/
(void) sprintf(line, "mget %s", openopt->colonmodepath);
}
makeargv();
/* Turn on messaging if we aren't catting. */
if (openopt->ftpcat == 0)
verbose = tmpverbose;
/* get() also handles 'more'. */
if (openopt->ftpcat)
cmdstatus = get(margc, margv);
else
cmdstatus = mget(margc, margv);
/* If we were invoked from the command line, quit
* after we got this file.
*/
if (eventnumber == 0L) {
(void) quit(cmdstatus == CMDERR ? -1 : 0, NULL);
}
}
verbose = tmpverbose;
} /* ColonMode */
/* Given a properly set up OpenOptions, we try connecting to the site,
* redialing if necessary, and do some initialization steps so the user
* can send commands.
*/
int Open(OpenOptions *openopt)
{
int hErr;
int dials;
char *ruser, *rpass, *racct;
int siteInRC;
char *user, *pass, *acct;
int login_verbosity, oldv;
int result = CMDERR;
macnum = 0; /* Reset macros. */
/* If the hostname supplied is in the form host.name.str:/path/file,
* then colon mode was used, and we need to fix the hostname to be
* just the hostname, copy the /path/file to colonmode path, and init
* the login_verbosity variable.
*/
if (CheckForColonMode(openopt, &login_verbosity) == USAGE)
return USAGE;
/* If the hostname supplied was an abbreviation, such as just
* "wu" (wuarchive.wustl.edu), look through the list of sites
* we know about and get the whole name. We also would like
* the path we want to start out in, if it is available.
*/
GetFullSiteName(openopt->hostname, openopt->cdpath);
#ifdef GATEWAY
/* Make sure the gateway host name is a full name and not an
* abbreviation.
*/
if (*gateway)
GetFullSiteName(gateway, NULL);
#endif
ruser = rpass = racct = NULL;
/* This also loads the init macro. */
siteInRC = ruserpass2(openopt->hostname, &ruser, &rpass, &racct);
if (ISANONOPEN(openopt->openmode)) {
user = "anonymous";
pass = anon_password;
} else {
user = NULL;
pass = NULL;
}
acct = NULL;
if (siteInRC && !openopt->ignore_rc) {
acct = racct;
if (ruser != NULL) {
/* We were given a username. If we were given explicit
* instructions from the command line, follow those and
* ignore what the RC had. Otherwise if no -a or -u
* was specified, we use whatever was in the RC.
*/
if (ISIMPLICITOPEN(openopt->openmode)) {
user = ruser;
pass = rpass;
}
}
}
for (
dials = 0;
openopt->max_dials < 0 || dials < openopt->max_dials;
dials++)
{
if (dials > 0) {
/* If this is the second dial or higher, sleep a bit. */
(void) sleep(openopt->redial_delay);
(void) fprintf(stderr, "Retry Number: %d\n", dials + 1);
}
if ((hErr = HookupToRemote(openopt)) == -2)
/* Recoverable, so we can try re-dialing. */
continue;
else if (hErr == NOERR) {
/* We were hookup'd successfully. */
connected = 1;
oldv = verbose; verbose = login_verbosity;
#ifdef GATEWAY
if (*gateway) {
if ((Login(
user,
pass,
acct,
(!openopt->ignore_rc && !openopt->colonmodepath[0])
) != NOERR) || cout == NULL)
goto nextdial; /* error! */
}
#endif
#ifdef GATEWAY
if (!*gateway) {
#endif
/* We don't want to run the init macro for colon-mode. */
if ((Login(
user,
pass,
acct,
(!openopt->ignore_rc && !openopt->colonmodepath[0])
) != NOERR) || cout == NULL)
{
goto nextdial; /* error! */
}
#ifdef GATEWAY
}
#endif
verbose = oldv;
/* We need to check for unix and see if we should set binary
* mode automatically.
*/
CheckRemoteSystemType(openopt->colonmodepath[0] != (char)0);
if (openopt->colonmodepath[0]) {
ColonMode(openopt);
} else if (openopt->cdpath[0]) {
/* If we didn't have a colon-mode path, we try setting
* the current remote directory to cdpath. cdpath is
* usually the last directory we were in the previous
* time we called this site.
*/
(void) _cd(openopt->cdpath);
} else {
/* Freshen 'cwd' variable for the prompt.
* We have to do atleast one 'cd' so our variable
* cwd (which is saved by _cd()) is set to something
* valid.
*/
(void) _cd(NULL);
}
result = NOERR;
break; /* we are connected, so break the redial loop. */
/* end if we are connected */
} else {
/* Irrecoverable error, so don't bother redialing. */
/* The error message should have already been printed
* from Hookup().
*/
break;
}
nextdial:
disconnect(0, NULL);
continue; /* Try re-dialing. */
}
return (result);
} /* Open */
/* This stub is called by our command parser. */
int cmdOpen(int argc, char **argv)
{
OpenOptions openopt;
int result = NOERR;
/* If there is already a site open, close that one so we can
* open a new one.
*/
if (connected && NOT_VQUIET && hostname[0]) {
(void) printf("Closing %s...\n", hostname);
(void) disconnect(0, NULL);
}
/* Reset the remote info structure for the new site we want to open.
* Assume we have these properties until we discover otherwise.
*/
gRmtInfo.hasSIZE = 1;
gRmtInfo.hasMDTM = 1;
if ((GetOpenOptions(argc, argv, &openopt) == USAGE) ||
((result = Open(&openopt)) == USAGE))
return USAGE;
/* Return an error if colon-mode/URL didn't work. */
return (openopt.colonmodepath[0] != '\0' ? result : NOERR);
} /* cmdOpen */
/* eof open.c */