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