/* open.c */ /* $RCSfile: open.c,v $ * $Revision: 1.1 $ * $Date: 93/07/09 11:27:07 $ */ #include "sys.h" #include #include #include #include #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. */ 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, www; 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; /* 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) (void) get(margc, margv); else (void) mget(margc, margv); /* If we were invoked from the command line, quit * after we got this file. */ if (eventnumber == 0L) { (void) quit(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; 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); } 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: continue; /* Try re-dialing. */ } return (NOERR); } /* Open */ /* This stub is called by our command parser. */ int cmdOpen(int argc, char **argv) { OpenOptions openopt; /* 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) || (Open(&openopt) == USAGE)) return USAGE; return NOERR; } /* cmdOpen */ /* eof open.c */