home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume40 / ncftp / part03 / open.c < prev   
Encoding:
C/C++ Source or Header  |  1993-11-02  |  16.2 KB  |  597 lines

  1. /* open.c */
  2.  
  3. /*  $RCSfile: open.c,v $
  4.  *  $Revision: 1.1 $
  5.  *  $Date: 93/07/09 11:27:07 $
  6.  */
  7.  
  8. #include "sys.h"
  9.  
  10. #include <netdb.h>
  11. #include <netinet/in.h>
  12. #include <arpa/ftp.h>
  13.  
  14. #include <errno.h>
  15.  
  16. #include "util.h"
  17. #include "open.h"
  18. #include "cmds.h"
  19. #include "ftp.h"
  20. #include "ftprc.h"
  21. #include "main.h"
  22. #include "defaults.h"
  23. #include "copyright.h"
  24.  
  25. /* open.c globals */
  26. int                    remote_is_unix;        /* TRUE if remote host is unix. */
  27. int                    auto_binary = dAUTOBINARY;
  28. int                    anon_open = dANONOPEN;
  29.                                         /* Anonymous logins by default? */
  30. int                    connected = 0;        /* TRUE if connected to server */
  31.                                         /* If TRUE, set binary each connection. */
  32. Hostname            hostname;            /* Name of current host */
  33. #ifdef GATEWAY
  34. string                gateway;            /* node name of firewall gateway */
  35. string                gate_login;            /* login at firewall gateway */
  36. #endif
  37.  
  38. /* open.c externs */
  39. extern char                    *reply_string, *line, *Optarg, *margv[];
  40. extern int                    Optind, margc, verbose, macnum;
  41. extern long                    eventnumber;
  42. extern struct servent        serv;
  43. extern FILE                    *cout;
  44. extern string                anon_password;
  45.  
  46. /* Given a pointer to an OpenOptions (structure containing all variables
  47.  * that can be set from the command line), this routine makes sure all
  48.  * the variables have valid values by setting them to their defaults.
  49.  */
  50.  
  51. void InitOpenOptions(OpenOptions *openopt)
  52. {
  53.     /* How do you want to open a site if neither -a or -u are given?
  54.      * anon_open is true (default to anonymous login), unless
  55.      * defaults.h was edited to set dANONOPEN to 0 instead.
  56.      */
  57.     openopt->openmode = anon_open ? openImplicitAnon : openImplicitUser;
  58.  
  59.     /* Normally you don't want to ignore the entry in your netrc. */
  60.     openopt->ignore_rc = 0;
  61.  
  62.     /* Set the default delay if the user specifies redial mode without
  63.      * specifying the redial delay.
  64.      */
  65.     openopt->redial_delay = dREDIALDELAY;
  66.  
  67.     /* Normally, you only want to try once. If you specify redial mode,
  68.      * this is changed.
  69.      */
  70.     openopt->max_dials = 1;
  71.     
  72.     /* You don't want to cat the file to stdout by default. */
  73.     openopt->ftpcat = NO_FTPCAT;
  74.  
  75.     /* Setup the port number to try. */
  76. #ifdef dFTP_PORT
  77.     /* If dFTP_PORT is defined, we use a different port number by default
  78.      * than the one supplied in the servent structure.
  79.      */
  80.     openopt->port = dFTP_PORT;
  81.     /* Make sure the correct byte order is supplied! */
  82.     openopt->port = htons(openopt->port);
  83. #else
  84.     /* Use the port number supplied by the operating system's servent
  85.      * structure.
  86.      */
  87.     openopt->port = serv.s_port;
  88. #endif
  89.  
  90.     /* We are not in colon-mode (yet). */
  91.     openopt->colonmodepath[0] = 0;
  92.  
  93.     /* Set the hostname to a null string, since there is no default host. */
  94.     openopt->hostname[0] = 0;
  95.     
  96.     /* Set the opening directory path to a null string. */
  97.     openopt->cdpath[0] = 0;
  98. }    /* InitOpenOptions */
  99.  
  100.  
  101.  
  102.  
  103. /* This is responsible for parsing the command line and setting variables
  104.  * in the OpenOptions structure according to the user's flags.
  105.  */
  106.  
  107. int GetOpenOptions(int argc, char **argv, OpenOptions *openopt)
  108. {
  109.     int                    opt;
  110.     char                *cp;
  111.  
  112.     /* First setup the openopt variables. */
  113.     InitOpenOptions(openopt);
  114.  
  115.     /* Tell Getopt() that we want to start over with a new command. */
  116.     Getopt_Reset();
  117.     while ((opt = Getopt(argc, argv, "aiup:rd:g:cm")) >= 0) {
  118.         switch (opt) {        
  119.             case 'a':
  120.                 /* User wants to open anonymously. */
  121.                 openopt->openmode = openExplicitAnon;
  122.                 break;
  123.                 
  124.             case 'u':
  125.                 /* User wants to open with a login and password. */
  126.                 openopt->openmode = openExplicitUser;
  127.                 break;
  128.                 
  129.             case 'i':
  130.                 /* User wants to ignore the entry in the netrc. */
  131.                 openopt->ignore_rc = 1;
  132.                 break;
  133.                 
  134.             case 'p':
  135.                 /* User supplied a port number different from the default
  136.                  * ftp port.
  137.                  */
  138.                 openopt->port = atoi(Optarg);
  139.                 if (openopt->port <= 0) {
  140.                     /* Probably never happen, but just in case. */
  141.                     (void) printf("%s: bad port number (%s).\n", argv[0], Optarg);
  142.                     goto usage;
  143.                 }
  144.                 /* Must ensure that the port is in the correct byte order! */
  145.                 openopt->port = htons(openopt->port);
  146.                 break;
  147.                 
  148.             case 'd':
  149.                 /* User supplied a delay (in seconds) that differs from
  150.                  * the default.
  151.                  */
  152.                 openopt->redial_delay = atoi(Optarg);
  153.                 break;
  154.                 
  155.             case 'g':
  156.                 /* User supplied an upper-bound on the number of redials
  157.                  * to try.
  158.                  */
  159.                 openopt->max_dials = atoi(Optarg);
  160.                 break;
  161.  
  162.             case 'r':
  163.                 openopt->max_dials = -1;
  164.                 break;
  165.  
  166.             case 'm':
  167.                 /* ftpcat mode is only available from your shell command-line,
  168.                  * not from the ncftp shell.  Do that yourself with 'more zz'.
  169.                  */
  170.                 if (eventnumber == 0L) {
  171.                     /* If eventnumber is zero, then we were called directly
  172.                      * from main(), and before the ftp shell has started.
  173.                      */
  174.                     openopt->ftpcat = FTPMORE;
  175.                     /* ftpcat mode is really ftpmore mode. */
  176.                     break;
  177.                 } else {
  178.                     fprintf(stderr,
  179. "You can only use this form of colon-mode (-m) from your shell command line.\n\
  180. Try 'ncftp -m wuarchive.wustl.edu:/README'\n");
  181.                     goto usage;
  182.                 }
  183.                 break;
  184.  
  185.             case 'c':
  186.                 /* ftpcat mode is only available from your shell command-line,
  187.                  * not from the ncftp shell.  Do that yourself with 'get zz -'.
  188.                  */
  189.                 if (eventnumber == 0L) {
  190.                     /* If eventnumber is zero, then we were called directly
  191.                      * from main(), and before the ftp shell has started.
  192.                      */
  193.                     openopt->ftpcat = FTPCAT;
  194.                     break;
  195.                 } else {
  196.                     fprintf(stderr,
  197. "You can only use ftpcat/colon-mode from your shell command line.\n\
  198. Try 'ncftp -c wuarchive.wustl.edu:/README > file.'\n");
  199.                     goto usage;
  200.                 }
  201.                 break;
  202.                 
  203.             default:
  204.             usage:
  205.                 return USAGE;
  206.         }
  207.     }
  208.  
  209.     if (argv[Optind] == NULL) {
  210.         /* No host was supplied.  Print out the list of sites we know
  211.          * about and ask the user for one.
  212.          */
  213.         PrintSiteList();
  214.         (void) Gets("(site to open) ", openopt->hostname, sizeof(openopt->hostname));
  215.         /* Make sure the user just didn't hit return, in which case we
  216.          * just give up and go home.
  217.          */
  218.         if (openopt->hostname[0] == 0)
  219.             goto usage;
  220.     } else {
  221.         /* The user gave us a host to open.
  222.          *
  223.          * First, check to see if they gave us a colon-mode path
  224.          * along with the hostname.
  225.          */
  226.         if ((cp = index(argv[Optind], ':')) != NULL) {
  227.             *cp++ = 0;
  228.             (void) Strncpy(openopt->colonmodepath, cp);
  229.         }    
  230.         (void) Strncpy(openopt->hostname, argv[Optind]);
  231.     }
  232.     return NOERR;
  233. }    /* GetOpenOptions */
  234.  
  235.  
  236.  
  237.  
  238. /* This examines the format of the string stored in the hostname
  239.  * field of the OpenOptions, and sees if has to strip out a colon-mode
  240.  * pathname (to store in the colonmodepath field).  Since colon-mode
  241.  * is run quietly (without any output being generated), we init the
  242.  * login_verbosity variable here to quiet if we are running colon-mode.
  243.  */
  244. int CheckForColonMode(OpenOptions *openopt, int *login_verbosity)
  245. {
  246.     /* Usually the user doesn't supply hostname in colon-mode format,
  247.      * and wants to interactively browse the remote host, so set the
  248.      * login_verbosity to whatever it is set to now.
  249.      */
  250.     *login_verbosity = verbose;
  251.  
  252.     if (openopt->colonmodepath[0] != 0) {
  253.         /* But if the user does use colon-mode, we want to do our business
  254.          * and leave, without all the login messages, etc., so set
  255.          * login_verbosity to quiet so we won't print anything until
  256.          * we finish.  Colon-mode can be specified from the shell command
  257.          * line, so we would like to be able to execute ncftp as a one
  258.          * line command from the shell without spewing gobs of output.
  259.          */
  260.         *login_verbosity = V_QUIET;
  261.     } else if (openopt->ftpcat != 0) {
  262.         /* User specified ftpcat mode, but didn't supply the host:file. */
  263.         (void) fprintf(stderr, "You didn't use colon mode correctly.\n\
  264. If you use -c or -m, you need to do something like this:\n\
  265.     ncftp -c wuarchive.wustl.edu:/pub/README (to cat this file to stdout).\n");
  266.         return USAGE;
  267.     }
  268.     return NOERR;
  269. }    /* CheckForColonMode */
  270.  
  271.  
  272.  
  273.  
  274. /* All this short routine does is to hookup a socket to either the
  275.  * remote host or the firewall gateway host.
  276.  */
  277. int HookupToRemote(OpenOptions *openopt)
  278. {
  279.     int hErr;
  280.  
  281. #ifdef GATEWAY
  282.     /* Try connecting to the gateway host. */
  283.     if (*gateway) {
  284.         hErr = hookup(gateway, openopt->port);
  285.         (void) Strncpy(hostname, openopt->hostname);
  286.     } else
  287. #endif
  288.         hErr = hookup(openopt->hostname, openopt->port);
  289.     
  290.     return hErr;
  291. }    /* HookupToRemote */
  292.  
  293.  
  294.  
  295.  
  296. void CheckRemoteSystemType(int force_binary)
  297. {
  298.     int tmpverbose;
  299.     char *cp, c;
  300.  
  301.     /* As of this writing, UNIX is pretty much standard. */
  302.     remote_is_unix = 1;
  303.  
  304.     /* Do a SYSTem command quietly. */
  305.     tmpverbose = verbose;
  306.     verbose = V_QUIET;
  307.     if (command("SYST") == COMPLETE) {
  308.         if (tmpverbose == V_VERBOSE) {        
  309.             /* Find the system type embedded in the reply_string,
  310.              * and separate it from the rest of the junk.
  311.              */
  312.             cp = index(reply_string+4, ' ');
  313.             if (cp == NULL)
  314.                 cp = index(reply_string+4, '\r');
  315.             if (cp) {
  316.                 if (cp[-1] == '.')
  317.                     cp--;
  318.                 c = *cp;
  319.                 *cp = '\0';
  320.             }
  321.  
  322.             (void) printf("Remote system type is %s.\n",
  323.                 reply_string+4);
  324.             if (cp)
  325.                 *cp = c;
  326.         }
  327.         remote_is_unix = !strncmp(reply_string + 4, "UNIX", (size_t) 4);
  328.     }
  329.  
  330.     /* Set to binary mode if any of the following are true:
  331.      * (a) The user has auto-binary set;
  332.      * (b) The user is using colon-mode (force_binary);
  333.      * (c) The reply-string from SYST said it was UNIX with 8-bit chars.
  334.      */
  335.     if (auto_binary || force_binary
  336.         || !strncmp(reply_string, "215 UNIX Type: L8", (size_t) 17)) {
  337.         (void) _settype("binary");
  338.         if (tmpverbose > V_TERSE)
  339.             (void) printf("Using binary mode to transfer files.\n");
  340.     }
  341.  
  342.     /* Print a warning for that (extremely) rare Tenex machine. */
  343.     if (tmpverbose >= V_ERRS && 
  344.         !strncmp(reply_string, "215 TOPS20", (size_t) 10)) {
  345.         (void) _settype("tenex");
  346.         (void) printf("Using tenex mode to transfer files.\n");
  347.     }
  348.     verbose = tmpverbose;
  349. }    /* CheckRemoteSystemType */
  350.  
  351.  
  352.  
  353. /* This is called if the user opened the host with a file appended to
  354.  * the host's name, like "wuarchive.wustl.edu:/pub/readme," or
  355.  * "wuarchive.wustl.edu:/pub."  In the former case, we open wuarchive,
  356.  * and fetch "readme."  In the latter case, we open wuarchive, then set
  357.  * the current remote directory to "/pub."  If we are fetching a file,
  358.  * we can do some other tricks if "ftpcat mode" is enabled.  This mode
  359.  * must be selected from your shell's command line, and this allows you
  360.  * to use the program as a one-liner to pipe a remote file into something,
  361.  * like "ncftp -c wu:/pub/README | wc."  If the user uses ftpcat mode,
  362.  * the program immediately quits instead of going into it's own command
  363.  * shell.
  364.  */
  365. void ColonMode(OpenOptions *openopt)
  366. {
  367.     int tmpverbose;
  368.  
  369.     /* How do we tell if colonmodepath is a file or a directory?
  370.      * We first try cd'ing to the path first.  If we can, then it
  371.      * was a directory.  If we could not, we'll assume it was a file.
  372.      */
  373.  
  374.     /* Shut up, so cd won't print 'foobar: Not a directory.' */
  375.     tmpverbose = verbose;
  376.     verbose = V_QUIET;
  377.  
  378.     /* If we are using ftpcat|more mode, or we couldn't cd to the
  379.      * colon-mode path (then it must be a file to fetch), then
  380.      * we need to fetch a file.
  381.      */
  382.     if (openopt->ftpcat || ! _cd(openopt->colonmodepath)) {
  383.         /* We call the appropriate fetching routine, so we have to
  384.          * have the argc and argv set up correctly.  To do this,
  385.          * we just make an entire command line, then let makeargv()
  386.          * convert it to argv/argc.
  387.          */
  388.         if (openopt->ftpcat == FTPCAT)
  389.             (void) sprintf(line, "get %s -", openopt->colonmodepath);
  390.         else if (openopt->ftpcat == FTPMORE)
  391.             (void) sprintf(line, "more %s", openopt->colonmodepath);
  392.         else {
  393.             /* Regular colon-mode, where we fetch the file, putting the
  394.              * copy in the current local directory.
  395.              */
  396.             (void) sprintf(line, "mget %s", openopt->colonmodepath);
  397.         }
  398.         makeargv();
  399.  
  400.         /* Turn on messaging if we aren't catting. */
  401.         if (openopt->ftpcat == 0)
  402.             verbose = tmpverbose;
  403.         
  404.         /* get() also handles 'more'. */
  405.         if (openopt->ftpcat)
  406.             (void) get(margc, margv);
  407.         else
  408.             (void) mget(margc, margv);
  409.  
  410.         /* If we were invoked from the command line, quit
  411.          * after we got this file.
  412.          */
  413.         if (eventnumber == 0L) {
  414.             (void) quit(0, NULL);
  415.         }
  416.     }
  417.     verbose = tmpverbose;
  418. }    /* ColonMode */
  419.  
  420.  
  421.  
  422.  
  423. /* Given a properly set up OpenOptions, we try connecting to the site,
  424.  * redialing if necessary, and do some initialization steps so the user
  425.  * can send commands.
  426.  */
  427. int Open(OpenOptions *openopt)
  428. {
  429.     int                    hErr;
  430.     int                    dials;
  431.     char                *ruser, *rpass, *racct;
  432.     int                    siteInRC;
  433.     char                *user, *pass, *acct;    
  434.     int                    login_verbosity, oldv;
  435.  
  436.     macnum = 0;     /* Reset macros. */
  437.  
  438.     /* If the hostname supplied is in the form host.name.str:/path/file,
  439.      * then colon mode was used, and we need to fix the hostname to be
  440.      * just the hostname, copy the /path/file to colonmode path, and init
  441.      * the login_verbosity variable.
  442.      */
  443.     if (CheckForColonMode(openopt, &login_verbosity) == USAGE)
  444.         return USAGE;
  445.  
  446.     /* If the hostname supplied was an abbreviation, such as just
  447.      * "wu" (wuarchive.wustl.edu), look through the list of sites
  448.      * we know about and get the whole name.  We also would like
  449.      * the path we want to start out in, if it is available.
  450.      */
  451.     GetFullSiteName(openopt->hostname, openopt->cdpath);
  452.  
  453. #ifdef GATEWAY
  454.     /* Make sure the gateway host name is a full name and not an
  455.      * abbreviation.
  456.      */
  457.     if (*gateway)
  458.         GetFullSiteName(gateway, NULL);
  459. #endif
  460.  
  461.     ruser = rpass = racct = NULL;
  462.     /* This also loads the init macro. */
  463.     siteInRC = ruserpass2(openopt->hostname, &ruser, &rpass, &racct);
  464.     if (ISANONOPEN(openopt->openmode)) {
  465.         user = "anonymous";
  466.         pass = anon_password;
  467.     } else {
  468.         user = NULL;
  469.         pass = NULL;
  470.     }
  471.     acct = NULL;
  472.     
  473.     if (siteInRC && !openopt->ignore_rc) {
  474.         acct = racct;
  475.         if (ruser != NULL) {
  476.             /* We were given a username.  If we were given explicit
  477.              * instructions from the command line, follow those and
  478.              * ignore what the RC had.  Otherwise if no -a or -u
  479.              * was specified, we use whatever was in the RC.
  480.              */
  481.             if (ISIMPLICITOPEN(openopt->openmode)) {
  482.                 user = ruser;
  483.                 pass = rpass;
  484.             }
  485.         }        
  486.     }
  487.  
  488.     for (
  489.             dials = 0;
  490.             openopt->max_dials < 0 || dials < openopt->max_dials;
  491.             dials++)
  492.     {
  493.         if (dials > 0) {
  494.             /* If this is the second dial or higher, sleep a bit. */
  495.             (void) sleep(openopt->redial_delay);
  496.             (void) fprintf(stderr, "Retry Number: %d\n", dials + 1);
  497.         }
  498.  
  499.         if ((hErr = HookupToRemote(openopt)) == -2)    
  500.             /* Recoverable, so we can try re-dialing. */
  501.             continue;
  502.         else if (hErr == NOERR) {
  503.             /* We were hookup'd successfully. */
  504.             connected = 1;
  505.  
  506.         oldv = verbose;  verbose = login_verbosity;
  507.         
  508. #ifdef GATEWAY
  509.             if (*gateway) {
  510.                 if ((Login(
  511.                     user,
  512.                     pass,
  513.                     acct,
  514.                     (!openopt->ignore_rc && !openopt->colonmodepath[0])
  515.                 ) != NOERR) || cout == NULL)
  516.                     goto nextdial;        /* error! */
  517.             }
  518. #endif
  519.  
  520. #ifdef GATEWAY
  521.             if (!*gateway) {
  522. #endif
  523.                 /* We don't want to run the init macro for colon-mode. */
  524.                 if ((Login(
  525.                         user,
  526.                         pass,
  527.                         acct,
  528.                         (!openopt->ignore_rc && !openopt->colonmodepath[0])
  529.                     ) != NOERR) || cout == NULL)
  530.                 {
  531.                     goto nextdial;        /* error! */
  532.                 }
  533. #ifdef GATEWAY
  534.             }
  535. #endif
  536.  
  537.             verbose = oldv;
  538.  
  539.             /* We need to check for unix and see if we should set binary
  540.              * mode automatically.
  541.              */
  542.             CheckRemoteSystemType(openopt->colonmodepath[0] != (char)0);
  543.  
  544.             if (openopt->colonmodepath[0]) {
  545.                 ColonMode(openopt);
  546.             } else if (openopt->cdpath[0]) {
  547.                 /* If we didn't have a colon-mode path, we try setting
  548.                  * the current remote directory to cdpath.  cdpath is
  549.                  * usually the last directory we were in the previous
  550.                  * time we called this site.
  551.                  */
  552.                 (void) _cd(openopt->cdpath);
  553.             } else {
  554.                 /* Freshen 'cwd' variable for the prompt. 
  555.                  * We have to do atleast one 'cd' so our variable
  556.                  * cwd (which is saved by _cd()) is set to something
  557.                  * valid.
  558.                  */
  559.                 (void) _cd(NULL);
  560.             }
  561.             break;    /* we are connected, so break the redial loop. */
  562.             /* end if we are connected */
  563.         } else {
  564.             /* Irrecoverable error, so don't bother redialing. */
  565.             /* The error message should have already been printed
  566.              * from Hookup().
  567.              */
  568.             break;
  569.         }
  570. nextdial: continue;    /* Try re-dialing. */
  571.     }
  572.     return (NOERR);
  573. }    /* Open */
  574.  
  575.  
  576.  
  577. /* This stub is called by our command parser. */
  578. int cmdOpen(int argc, char **argv)
  579. {
  580.     OpenOptions            openopt;
  581.  
  582.     /* If there is already a site open, close that one so we can
  583.      * open a new one.
  584.      */
  585.     if (connected && NOT_VQUIET && hostname[0]) {
  586.         (void) printf("Closing %s...\n", hostname);
  587.         (void) disconnect(0, NULL);
  588.     }
  589.  
  590.     if ((GetOpenOptions(argc, argv, &openopt) == USAGE) ||
  591.         (Open(&openopt) == USAGE))
  592.         return USAGE;
  593.     return NOERR;
  594. }    /* cmdOpen */
  595.  
  596. /* eof open.c */
  597.