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