home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Networking / ncftp-2.4.2-MIHS / src / Open.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-18  |  23.3 KB  |  888 lines

  1. /* Open.c */
  2.  
  3. #include "Sys.h"
  4.  
  5. #ifdef SYSLOG
  6. #    include <syslog.h>
  7. #endif
  8.  
  9. #include "Util.h"
  10. #include "Open.h"
  11. #include "GetPass.h"
  12. #include "Cmds.h"
  13. #include "RCmd.h"
  14. #include "Bookmark.h"
  15. #include "FTP.h"
  16. #include "Get.h"
  17. #include "Getopt.h"
  18. #include "Macro.h"
  19. #include "Hostwin.h"
  20. #include "Main.h"
  21. #include "Complete.h"
  22.  
  23. /* This is a preference setting specifying whether we use anonymous logins
  24.  * by default (as opposed to user/passwd logins).
  25.  */
  26. int gAnonOpen = (UOPEN == 0);
  27.  
  28. /* A structure containing some extra information we've learned about
  29.  * the remote host, so we don't need to find out the hard way each
  30.  * time we do something.
  31.  */
  32. extern Bookmark gRmtInfo;
  33.  
  34. /* Name of the host we're connected to. */
  35. string gHost;
  36.  
  37. /* We keep track if we've logged in to the host yet.  It's possible to
  38.  * be connected but not authorized (authenticated, whatever) to do anything.
  39.  */
  40. int gLoggedIn = 0;
  41.  
  42. /* Need to know if we are trying to connect and login, so if we get
  43.  * hangup during that, we will come back to the open without longjmp'ing
  44.  * out.
  45.  */
  46. int gAttemptingConnection = 0;
  47.  
  48. /* Need to know if they did a 'open -r' so we don't use the built-in pager
  49.  * for long site-connect messages.  Otherwise the program would stop to
  50.  * wait for the user to hit <space> each time we connected to that site
  51.  * which would sort of defeat the purpose of redialing.
  52.  */
  53. int gRedialModeEnabled = 0;
  54.  
  55. /* If we connect successfully, we'll want to save this port number
  56.  * when we add a host file entry.
  57.  */
  58. int gPortNumberUsed;
  59.  
  60. int gSavePasswords = 0;
  61.  
  62. /* Open.c externs */
  63. extern char *gOptArg;
  64. extern int gOptInd, gIsToTTY;
  65. extern unsigned int gFTPPort;
  66. extern long gEventNumber;
  67. extern char gRemoteCWD[256];
  68. extern char gIPStr[32];
  69. extern int gTransferType, gCurType, gRemoteServerType;
  70. extern int gRmtInfoIsNew, gWantRmtInfoSaved;
  71. extern int gConnected, gHasPASV, gVerbosity;
  72. extern int gDoneApplication, gWinInit, gMode;
  73. extern string gEmailAddress, gAnonPassword, gPager;
  74. extern UserInfo gUserInfo;
  75. extern FILE *gLogFile;
  76. extern LineList gRedir;
  77. extern void GetRemoteCWD(char *cdstub, ResponsePtr cwdrp);
  78.  
  79.  
  80. /* This is used primarily for non-anonymous logins.  We'll have to ask
  81.  * the user some questions, like their username, password, etc.
  82.  */
  83. int LoginQuestion(
  84.     char *prompt,
  85.     char *answer,
  86.     size_t siz,
  87.     char *defanswer,
  88.     int noEcho)
  89. {
  90.     string prompt2;
  91.     
  92.     /* Only do this if we have an empty string as the answer. */
  93.     if (*answer == '\0') {
  94.         if (defanswer != NULL)
  95.             sprintf(prompt2, "%s [%s]: ", prompt, defanswer);
  96.         else
  97.             sprintf(prompt2, "%s: ", prompt);
  98.         GetAnswer(prompt2, answer, siz, noEcho);
  99.         if ((*answer == '\0') && (defanswer != NULL))
  100.             Strncpy(answer, defanswer, siz);
  101.     }
  102.     return (*answer == '\0' ? (-1) : 0);
  103. }    /* LoginQuestion */
  104.  
  105.  
  106.  
  107.  
  108. int Login(char *u, char *p, char *a)
  109. {
  110.     string u2, p2, a2;
  111.     ResponsePtr rp;
  112.     int result = -1;
  113.     int isAnonLogin = 1;
  114.  
  115.     STRNCPY(u2, u);
  116.     STRNCPY(p2, p);
  117.     STRNCPY(a2, a);
  118.         
  119.     rp = InitResponse();
  120.     if (LoginQuestion("User", u2, sizeof(u2), "anonymous", 0) < 0)
  121.         goto done;
  122.     RCmd(rp, "USER %s", u2);
  123.  
  124.     for (;;) {
  125.         /* Here's a mini finite-automaton for the login process.
  126.          *
  127.          * Originally, the FTP protocol was designed to be entirely
  128.          * implementable from a FA.  It could be done, but I don't think
  129.          * it's something an interactive process could be the most
  130.          * effective with.
  131.          */
  132.         switch (rp->code) {
  133.             case 230:    /* 230 User logged in, proceed. */
  134.             case 202:    /* Command not implemented, superfluous at this site. */
  135.                 goto okay;
  136.  
  137.             case 421:    /* 421 Service not available, closing control connection. */
  138.                 goto done;
  139.                 
  140.             case 331:    /* 331 User name okay, need password. */
  141.                 isAnonLogin =    STREQ("anonymous", u2) ||
  142.                                 STREQ("ftp", u2);
  143.                 if (isAnonLogin)
  144.                     rp->printMode = kDontPrint;
  145.  
  146.                 ReInitResponse(rp);
  147.                 (void) LoginQuestion(
  148.                     "Password",
  149.                     p2,
  150.                     sizeof(p2),
  151.                     isAnonLogin ? gAnonPassword : NULL,
  152.                     1
  153.                 );
  154.                 RCmd(rp, "PASS %s", p2);
  155.                 break;
  156.  
  157.              case 332:    /* 332 Need account for login. */
  158.              case 532:     /* 532 Need account for storing files. */
  159.                 ReInitResponse(rp);
  160.                 (void) LoginQuestion("Account", a2, sizeof(a2), NULL, 1);
  161.                 RCmd(rp, "ACCT %s", a2);
  162.                 break;
  163.  
  164.             case 501:    /* Syntax error in parameters or arguments. */
  165.             case 503:    /* Bad sequence of commands. */
  166.             case 530:    /* Not logged in. */
  167.             case 550:    /* Can't set guest privileges. */
  168.                 goto done;
  169.                 
  170.             default:
  171.                 if (rp->msg.first == NULL) {
  172.                     Error(kDontPerror, "Lost connection during login.\n");
  173.                 } else {
  174.                     Error(kDontPerror, "Unexpected response: %s\n",
  175.                         rp->msg.first->line
  176.                     );
  177.                 }
  178.                 goto done;
  179.         }
  180.     }
  181. okay:
  182.     if (isAnonLogin) {
  183.         gRmtInfo.user[0] = '\0';
  184.         gRmtInfo.pass[0] = '\0';
  185.         STRNCPY(gRmtInfo.acct, a2);
  186.     } else {
  187.         STRNCPY(gRmtInfo.user, u2);
  188.         if (gSavePasswords) {
  189.             STRNCPY(gRmtInfo.pass, p2);
  190.             STRNCPY(gRmtInfo.acct, a2);
  191.         }
  192.     }
  193.  
  194.     result = 0;
  195. done:
  196.     DoneWithResponse(rp);
  197.     return result;
  198. }    /* Login */
  199.  
  200.  
  201.  
  202.  
  203. /* After closing a site, set or restore some things to their original
  204.  * states.
  205.  */
  206. void PostCloseStuff(void)
  207. {
  208. #ifdef SYSLOG
  209.     syslog (LOG_INFO, "%s disconnected from %s.", gUserInfo.userName, gHost);
  210. #endif
  211.     if (gLoggedIn) {
  212.         gRmtInfo.hasPASV = gHasPASV;
  213.         gRmtInfo.port = gPortNumberUsed;
  214.         gRmtInfo.nCalls++;
  215.         time((time_t *) &gRmtInfo.lastCall);
  216.         STRNCPY(gRmtInfo.lastIP, gIPStr);
  217.         if (gEventNumber > 0L) {
  218.             /* Only do these if we were not in batch mode (colon-mode). */
  219.             if (gRmtInfo.noSaveDir == 0)
  220.                     STRNCPY(gRmtInfo.dir, gRemoteCWD);
  221.             (void) RunPrefixedMacro("close.", gRmtInfo.bookmarkName);
  222.             (void) RunPrefixedMacro("close.", "any");
  223.         } else {
  224.             /* Only do these if we are running colon mode. */
  225.             (void) RunPrefixedMacro("colon.close.", gRmtInfo.bookmarkName);
  226.             (void) RunPrefixedMacro("colon.close.", "any");
  227.         }
  228.         SaveCurHostBookmark(NULL);
  229.     }
  230.     gLoggedIn = 0;
  231.     gRemoteCWD[0] = gHost[0] = '\0';
  232. }    /* PostCloseStuff */
  233.  
  234.  
  235.  
  236.  
  237. /* Close the connection to the remote host and cleanup. */
  238. void DoClose(int tryQUIT)
  239. {
  240.     ResponsePtr rp;
  241.     
  242.     if (tryQUIT != 0) {
  243.         rp = InitResponse();
  244.         rp->eofOkay = 1;    /* We are expecting EOF after this cmd. */
  245.         RCmd(rp, "QUIT");
  246.         DoneWithResponse(rp);
  247.     }
  248.     
  249.     CloseControlConnection();
  250.     CloseDataConnection(1);
  251.     PostCloseStuff();
  252. }    /* DoClose */
  253.  
  254.  
  255.  
  256.  
  257. int CloseCmd(void)
  258. {
  259.     DoClose(gConnected);
  260.     if (gWinInit == 0)
  261.             SetScreenInfo();    /* Need for line mode. */
  262.     return kNoErr;
  263. }    /* CloseCmd */
  264.  
  265.  
  266.  
  267.  
  268. /* Given a pointer to an OpenOptions (structure containing all variables
  269.  * that can be set from the command line), this routine makes sure all
  270.  * the variables have valid values by setting them to their defaults.
  271.  */
  272.  
  273. void InitOpenOptions(OpenOptions *openopt)
  274. {
  275.     /* How do you want to open a site if neither -a or -u are given?
  276.      * gAnonOpen is true (default to anonymous login), unless
  277.      * Config.h was edited to set UOPEN to 0 instead.
  278.      */
  279.     openopt->openmode = gAnonOpen ? kOpenImplicitAnon : kOpenImplicitUser;
  280.  
  281.     /* Normally you don't want to ignore the entry in your netrc. */
  282.     openopt->ignoreRC = 0;
  283.  
  284.     /* Set the default delay if the user specifies redial mode without
  285.      * specifying the redial delay.
  286.      */
  287.     openopt->redialDelay = kRedialDelay;
  288.  
  289.     /* Normally, you only want to try once. If you specify redial mode,
  290.      * this is changed.
  291.      */
  292.     openopt->maxDials = 1;
  293.     
  294.     /* You don't want to dump the file to stdout by default. */
  295.     openopt->ftpcat = kNoFTPCat;
  296.  
  297.     /* We'll set this later, but we'll use 0 to mean that the port
  298.      * is explicitly not set by the user.
  299.      */
  300.     openopt->port = kPortUnset;
  301.  
  302.     /* We are not in colon-mode (yet). */
  303.     openopt->colonModePath[0] = 0;
  304.  
  305.     /* Set the hostname to a null string, since there is no default host. */
  306.     openopt->hostname[0] = 0;
  307.     
  308.     /* Set the opening directory path to a null string. */
  309.     openopt->cdpath[0] = 0;
  310.  
  311.     openopt->interactiveColonMode = 0;
  312.  
  313.     /* Since we're opening with a possible colon-mode item, we have to
  314.      * track options for the GetCmd too.
  315.      */
  316.     InitGetOptions(&openopt->gopt);
  317. }    /* InitOpenOptions */
  318.  
  319.  
  320.  
  321.  
  322.  
  323. /* This is responsible for parsing the command line and setting variables
  324.  * in the OpenOptions structure according to the user's flags.
  325.  */
  326.  
  327. int GetOpenOptions(int argc, char **argv, OpenOptions *openopt, int fromMain)
  328. {
  329.     int                    opt, www;
  330.     char                *cp, *hostp, *cpath;
  331.  
  332.     /* First setup the openopt variables. */
  333.     InitOpenOptions(openopt);
  334.  
  335.     /* Tell Getopt() that we want to start over with a new command. */
  336.     GetoptReset();
  337.     while ((opt = Getopt(argc, argv, "aiup:rd:g:cmCfGRn:")) >= 0) {
  338.         switch (opt) {        
  339.             case 'a':
  340.                 /* User wants to open anonymously. */
  341.                 openopt->openmode = kOpenExplicitAnon;
  342.                 break;
  343.                 
  344.             case 'u':
  345.                 /* User wants to open with a login and password. */
  346.                 openopt->openmode = kOpenExplicitUser;
  347.                 break;
  348.  
  349.             case 'i':
  350.                 /* User wants to ignore the entry in the netrc. */
  351.                 openopt->ignoreRC = 1;
  352.                 break;
  353.  
  354.             case 'p':
  355.                 /* User supplied a port number different from the default
  356.                  * ftp port.
  357.                  */
  358.                 openopt->port = atoi(gOptArg);
  359.                 if (openopt->port <= 0) {
  360.                     /* Probably never happen, but just in case. */
  361.                     (void) PrintF("%s: bad port number (%s).\n", argv[0],
  362.                         gOptArg);
  363.                     goto usage;
  364.                 }
  365.                 break;
  366.                 
  367.             case 'd':
  368.                 /* User supplied a delay (in seconds) that differs from
  369.                  * the default.
  370.                  */
  371.                 openopt->redialDelay = atoi(gOptArg);
  372.                 break;
  373.                 
  374.             case 'g':
  375.                 /* User supplied an upper-bound on the number of redials
  376.                  * to try.
  377.                  */
  378.                 openopt->maxDials = atoi(gOptArg);
  379.                 break;
  380.  
  381.             case 'r':
  382.                 openopt->maxDials = -1;
  383.                 break;
  384.  
  385.             case 'm':
  386.                 /* ftpcat mode is only available from your shell command-line,
  387.                  * not from the ncftp shell.  Do that yourself with 'more zz'.
  388.                  */
  389.                 if (gEventNumber == 0L) {
  390.                     /* If gEventNumber is zero, then we were called directly
  391.                      * from main(), and before the ftp shell has started.
  392.                      */
  393.                     openopt->ftpcat = kFTPMore;
  394.                     /* ftpcat mode is really ftpmore mode. */
  395.                     break;
  396.                 } else {
  397.                     PrintF(
  398. "You can only use this form of colon-mode (-m) from your shell command line.\n\
  399. Try 'ncftp -m wuarchive.wustl.edu:/README'\n");
  400.                 }
  401.                 goto usage;
  402.  
  403.             case 'c':
  404.                 /* ftpcat mode is only available from your shell command-line,
  405.                  * not from the ncftp shell.  Do that yourself with 'get zz -'.
  406.                  */
  407.                 if (gEventNumber == 0L) {
  408.                     /* If gEventNumber is zero, then we were called directly
  409.                      * from main(), and before the ftp shell has started.
  410.                      */
  411.                     openopt->ftpcat = kFTPCat;
  412.                     break;
  413.                 } else {
  414.                     PrintF(
  415. "You can only use ftpcat/colon-mode from your shell command line.\n\
  416. Try 'ncftp -c wuarchive.wustl.edu:/README > file.'\n");
  417.                 }
  418.                 goto usage;
  419.  
  420.             /* These are options that can be passed to get. */
  421.             case 'C':
  422.             case 'f':
  423.             case 'G':
  424.             case 'R':
  425.             /* case 'z':  Note that we can't handle the rename here. */
  426.             case 'n':
  427.                 if (fromMain) {
  428.                     (void) SetGetOption(&openopt->gopt, opt, gOptArg);
  429.                     break;
  430.                 }
  431.                 goto usage;
  432.  
  433.             default:
  434.                 if (fromMain)
  435.                     break;
  436.                     
  437.             usage:
  438.                 return kUsageErr;
  439.         }
  440.     }
  441.  
  442.     if (argv[gOptInd] == NULL) {
  443.         if (!fromMain) {
  444.             if (openopt->hostname[0] == 0)
  445.                 goto usage;
  446.         }
  447.     } else {
  448.         /* The user gave us a host to open.
  449.          *
  450.          * First, check to see if they gave us a colon-mode path
  451.          * along with the hostname.  We also understand a WWW path,
  452.          * like "ftp://bang.nta.no/pub/fm2html.v.0.8.4.tar.Z".
  453.          */
  454.         hostp = argv[gOptInd];
  455.         cpath = NULL;
  456.         if ((cp = strchr(hostp, ':')) != NULL) {
  457.             if (fromMain) {
  458.                 *cp++ = '\0';
  459.                 cpath = cp;
  460.                 www = 0;    /* Is 0 or 1, depending on the type of path. */
  461.                 if ((*cp == '/') && (cp[1] == '/')) {
  462.                     /* First make sure the path was intended to be used
  463.                      * with ftp and not one of the other URLs.
  464.                      */
  465.                     if (!ISTREQ(argv[gOptInd], "ftp")) {
  466.                         PrintF(
  467.                             "Bad URL '%s' -- WWW paths must be prefixed by 'ftp://'.\n",
  468.                             argv[gOptInd]
  469.                         );
  470.                         goto usage;
  471.                     }
  472.     
  473.                     cp += 2;
  474.                     hostp = cp;
  475.                     cpath = NULL;    /* It could have been ftp://hostname only. */
  476.     
  477.                     if ((cp = strchr(hostp, '/')) != NULL) {
  478.                         *cp++ = '\0';
  479.                         cpath = cp;
  480.                     }
  481.                     www = 1;
  482.                 }
  483.                 if (cpath != NULL) {
  484.                     (void) STRNCPY(openopt->colonModePath, www ? "/" : "");
  485.                     (void) STRNCAT(openopt->colonModePath, cpath);
  486.                     cp = openopt->colonModePath +
  487.                         strlen(openopt->colonModePath) - 1;
  488.                     if (*cp == '/') {
  489.                         /* Colon-mode path ended in a slash, so you said it
  490.                          * was a directory.  That means we should start from
  491.                          * this directory when we open this site.
  492.                          */
  493.                         *cp = '\0';
  494.                         openopt->interactiveColonMode = 1;
  495.                     }
  496.                     DebugMsg("Colon-Mode Path = '%s'\n", openopt->colonModePath);
  497.                 }
  498.  
  499.             } else {
  500.                 /* I may lift this restriction later. */
  501.                 EPrintF("You can't use colon mode in the command shell.\n");
  502.                 return kUsageErr;
  503.             }
  504.         }
  505.         (void) STRNCPY(openopt->hostname, hostp);
  506.  
  507.         if ((openopt->colonModePath[0] == '\0') &&
  508.             (openopt->ftpcat != kNoFTPCat))
  509.         {
  510.             /* User specified ftpcat mode, but didn't supply
  511.              * the host:file.
  512.              */
  513.             EPrintF("You didn't use colon mode correctly.\n\
  514. If you use -c or -m, you need to do something like this:\n\
  515. ncftp -c wuarchive.wustl.edu:/pub/README (to cat this file to stdout).\n");
  516.             return kUsageErr;
  517.         }
  518.  
  519.         GetBookmark(openopt->hostname, sizeof(openopt->hostname));
  520.         if (openopt->port == kPortUnset) {
  521.             /* No port specified, so use same port as last time. */
  522.             if (gRmtInfo.port == kPortUnset)
  523.                 openopt->port = gFTPPort;
  524.             else
  525.                 openopt->port = gRmtInfo.port;
  526.         }
  527.     }
  528.     return kNoErr;
  529. }    /* GetOpenOptions */
  530.  
  531.  
  532.  
  533.  
  534. void CheckRemoteSystemType(int force_binary)
  535. {
  536.     string remoteSys;
  537.  
  538.     if (gRmtInfoIsNew) {
  539.         /* If first time here, check somethings while we can. */
  540.         if (DoSystem(remoteSys, sizeof(remoteSys)) == 0)
  541.             gRmtInfo.isUnix = !strncmp(remoteSys, "UNIX", (size_t) 4);
  542.     
  543.         /* Set to binary mode if any of the following are true:
  544.          * (a) The user is using colon-mode (force_binary; below);
  545.          * (b) The reply-string from SYST said it was UNIX with 8-bit chars.
  546.          */
  547.         if (!strncmp(remoteSys, "UNIX Type: L8", (size_t) 13))
  548.         {
  549.             gRmtInfo.xferType = 'I';
  550.         }
  551.     
  552.         /* Print a warning for that (extremely) rare Tenex machine. */
  553.         if (!strncmp(remoteSys, "TOPS20", (size_t) 6)) {
  554.             gRmtInfo.xferType = 'T';
  555.             (void) PrintF("Using tenex mode to transfer files.\n");
  556.         }
  557.  
  558.         if (gRemoteServerType == kNcFTPd) {
  559.             /* This server correctly implements
  560.              * block transfer mode, so turn it
  561.              * on by default.
  562.              *
  563.              * Unfortunately, most servers don't
  564.              * support it, and those that do
  565.              * (AIX ftpd) don't do it correctly.
  566.              */
  567.             gRmtInfo.xferMode = 'B';
  568.         }
  569.  
  570.     }
  571.     gTransferType = gRmtInfo.xferType;
  572.     
  573.     /* Use binary mode if this site was last set using ascii mode,
  574.      * and we are using colon-mode.
  575.      */
  576.     if ((force_binary) && (gTransferType == 'A'))
  577.         gTransferType = 'I';
  578.  
  579.     if (SetMode(gRmtInfo.xferMode) < 0) {
  580.         gRmtInfo.xferMode = gMode;
  581.     }
  582. }    /* CheckRemoteSystemType */
  583.  
  584.  
  585.  
  586. /* This is called if the user opened the host with a file appended to
  587.  * the host's name, like "wuarchive.wustl.edu:/pub/readme," or
  588.  * "wuarchive.wustl.edu:/pub."  In the former case, we open wuarchive,
  589.  * and fetch "readme."  In the latter case, we open wuarchive, then set
  590.  * the current remote directory to "/pub."  If we are fetching a file,
  591.  * we can do some other tricks if "ftpcat mode" is enabled.  This mode
  592.  * must be selected from your shell's command line, and this allows you
  593.  * to use the program as a one-liner to pipe a remote file into something,
  594.  * like "ncftp -c wu:/pub/README | wc."  If the user uses ftpcat mode,
  595.  * the program immediately quits instead of going into its own command
  596.  * shell.
  597.  */
  598. void ColonMode(OpenOptions *openopt)
  599. {
  600.     int result;
  601.  
  602.     /* Check for FTP-cat mode, so we call the appropriate
  603.      * fetching routine.
  604.      */
  605.     if (openopt->ftpcat == kFTPCat) {
  606.         InitGetOutputMode(&openopt->gopt, kDumpToStdout);
  607.         openopt->gopt.rName = openopt->colonModePath;
  608.         openopt->gopt.doReports = 0;
  609.         result = DoGet(&openopt->gopt);
  610.     } else if (openopt->ftpcat == kFTPMore) {
  611.         result = DoPage(openopt->colonModePath);
  612.     } else {
  613.         /* Regular colon-mode, where we fetch the file, putting the
  614.          * copy in the current local directory.
  615.          */
  616.         InitGetOutputMode(&openopt->gopt, kSaveToDisk);
  617.         openopt->gopt.doReports = 0;
  618.         openopt->gopt.rName = openopt->colonModePath;
  619.         result = DoGetWithGlobbingAndRecursion(&openopt->gopt);
  620.     }
  621.     DoQuit(result == 0 ? result : kExitColonModeFail);
  622.     /*NOTREACHED*/
  623. }    /* ColonMode */
  624.  
  625.  
  626.  
  627.  
  628. /* Now that we're logged in, do some other stuff prior to letting the
  629.  * user type away at the shell.
  630.  */
  631. void PostLoginStuff(OpenOptions *openopt)
  632. {
  633.     time_t now;
  634.     int wasColonMode;
  635.     
  636.     gLoggedIn = 1;
  637.  
  638.     /* Clear out the old redir buffer, since we are on a new site. */
  639.     DisposeLineListContents(&gRedir);
  640.  
  641.     /* Since we're logged in okay, we know what we've collected so far
  642.      * should be saved for next time.
  643.      */
  644.     gWantRmtInfoSaved = 1;
  645.     
  646.     /* The FTP module keeps its own note of whether the site has PASV
  647.      * or not, and has already initialized it to true.  If the gRmtInfo
  648.      * was fetched from our host file, we can tell FTP.c for sure if
  649.      * PASV should even be attempted.  If this was a new gRmtInfo, we
  650.      * will just be a little redundant, since new entries also assume
  651.      * PASV is supported.
  652.      */
  653.     gHasPASV = gRmtInfo.hasPASV;
  654.     
  655.     /* Since we connected okay, save this port number for later. */
  656.     gPortNumberUsed = openopt->port;
  657.  
  658.     /* When a new site is opened, ASCII type is assumed (by protocol),
  659.      * as well as stream transfer mode.
  660.      */
  661.     gCurType = 'A';
  662.     gMode = 'S';
  663.  
  664.     STRNCPY(gHost, openopt->hostname);
  665. #ifdef SYSLOG
  666.     syslog (LOG_INFO, "%s logged into %s.", gUserInfo.userName, gHost);
  667. #endif
  668.     if (gLogFile != NULL) {
  669.         (void) time(&now);
  670.         fprintf(gLogFile, "%s at %s", gHost, ctime(&now));
  671.     }
  672.  
  673.     wasColonMode = openopt->colonModePath[0] != (char)0;
  674.  
  675.     /* We need to check for unix and see if we should set binary
  676.      * mode automatically.
  677.      */
  678.     CheckRemoteSystemType(wasColonMode);
  679.  
  680.     if (wasColonMode) {
  681.         if (openopt->interactiveColonMode) {
  682.             /* Interactive colon mode simply means that the thing they
  683.              * gave us was a directory, and we should just cd to that
  684.              * directory when we start up.
  685.              */
  686.             (void) DoChdir(openopt->colonModePath);
  687.         } else {
  688.             /* Regular colon-mode is fetching a file by specifying the
  689.              * pathname of the file on the shell command line.
  690.              */
  691.             (void) GetRemoteCWD(kDidNotChdir, NULL);
  692.             ColonMode(openopt);        /* Does not return... */
  693.         }
  694.     } else if (gRmtInfo.dir[0]) {
  695.         /* If we didn't have a colon-mode path, we try setting
  696.          * the current remote directory to cdpath.  The .dir field is
  697.          * (usually) the last directory we were in the previous
  698.          * time we called this site.
  699.          */
  700.         (void) DoChdir(gRmtInfo.dir);
  701.     } else {
  702.         /* Freshen 'cwd' variable for the prompt. */
  703.         (void) GetRemoteCWD(kDidNotChdir, NULL);
  704.     }
  705.  
  706.     if (wasColonMode) {
  707.         /* Run separate sets of macros for colon-mode opens and regular,
  708.          * interactive opens.
  709.          */
  710.         (void) RunPrefixedMacro("colon.open.", "any");
  711.         (void) RunPrefixedMacro("colon.open.", gRmtInfo.bookmarkName);
  712.     } else {
  713.         (void) RunPrefixedMacro("open.", "any");
  714.         (void) RunPrefixedMacro("open.", gRmtInfo.bookmarkName);
  715.         if (gWinInit == 0)
  716.                 SetScreenInfo();    /* Need for line mode. */
  717.         ClearDirCache();
  718.     }
  719. }    /* PostLoginStuff */
  720.  
  721.  
  722.  
  723.  
  724.  
  725. /* Given a properly set up OpenOptions, we try connecting to the site,
  726.  * redialing if necessary, and do some initialization steps so the user
  727.  * can send commands.
  728.  */
  729. int Open(OpenOptions *openopt)
  730. {
  731.     int                    hErr;
  732.     int                    dials;
  733.     char                *user, *pass, *r_acct;    
  734.     int                    loginResult;
  735.     int                    openMode;
  736.  
  737.     gRedialModeEnabled = (openopt->maxDials != 1);
  738.     if (ISEXPLICITOPEN(openopt->openmode)) {
  739.         /* User specified an open mode explictly, like open -u,
  740.          * so do what the user said in spite of what we may have had
  741.          * in the gRmtInfo.
  742.          */
  743.         openMode = (ISANONOPEN(openopt->openmode)) ? 'a' : 'u';
  744.     } else {
  745.         if (gRmtInfoIsNew == 1) {
  746.             /* First time opening this site.  Open it anonymously
  747.              * unless you said not to with a -option.
  748.              */
  749.             openMode = (ISANONOPEN(openopt->openmode)) ? 'a' : 'u';
  750.         } else {
  751.             /* We've been here before, so use what we had last time. */
  752.             openMode = 'r';
  753.         }
  754.     }
  755.  
  756.     if ((openMode == 'r') && (gRmtInfo.user[0] == '\0'))
  757.         openMode = 'a';
  758.     if (openMode == 'a') {
  759.         user = "anonymous";
  760.         pass = strchr(gRmtInfo.pass, '@') ? gRmtInfo.pass : gAnonPassword;
  761.         r_acct = "";
  762.     } else if (openMode == 'u') {
  763.         user = "";
  764.         pass = "";
  765.         r_acct = "";
  766.     } else {
  767.         user = gRmtInfo.user;
  768.         pass = gRmtInfo.pass;
  769.         r_acct = gRmtInfo.acct;
  770.     }
  771.  
  772.     if ((openopt->colonModePath[0]) && (!openopt->interactiveColonMode)) {
  773.         /* If we're running from a shell script (or whatever)
  774.          * don't dump any output.  If the user is doing this from
  775.          * the shell, we will at least print the error messages.
  776.          * We also don't want to "pollute" ftpcat mode, but since
  777.          * error messages are printed to stderr, and we weren't
  778.          * going to print anything but error messages anyway,
  779.          * we're okay by using kErrorsOnly.
  780.          */
  781.         if (gIsToTTY != 0)
  782.             (void) SetVerbose(kErrorsOnly);
  783.         else
  784.             (void) SetVerbose(kQuiet);
  785.     } else {
  786.         /* If we haven't already setup our interactive shell, which
  787.          * would happen if you gave a host on the command line, then
  788.          * we need to do that now because we want the remote host's
  789.          * startup information to be displayed.
  790.          */
  791.         Startup();
  792.     }
  793.  
  794.     
  795.     PrintF("Trying to connect to %s...\n", openopt->hostname);
  796.     for (
  797.             dials = 0;
  798.             openopt->maxDials < 0 || dials < openopt->maxDials;
  799.             dials++)
  800.     {
  801.         if (dials > 0) {
  802.             /* If this is the second dial or higher, sleep a bit. */
  803.             PrintF("Sleeping %u seconds...  ",
  804.                 (unsigned) openopt->redialDelay);
  805.             FlushListWindow();
  806.             (void) sleep((unsigned) openopt->redialDelay);
  807.             PrintF("Retry Number: %d\n", dials + 1);
  808.         }
  809.         FlushListWindow();
  810.  
  811.         SetBar(NULL, "CONNECTING", NULL, 1, 1);
  812.         gAttemptingConnection = 1;
  813.         hErr = OpenControlConnection(openopt->hostname, openopt->port);
  814.         if (hErr == kConnectErrFatal) {
  815.             /* Irrecoverable error, so don't bother redialing.
  816.              * The error message should have already been printed
  817.              * from OpenControlConnection().
  818.              */
  819.             DebugMsg("Cannot recover from open error %d.\n", hErr);
  820.             break;
  821.         } else if (hErr == kConnectNoErr) {
  822.             /* We were hooked up successfully. */
  823.             
  824.             gRemoteCWD[0] = '\0';
  825.     
  826.             /* This updates the status bar (for visual mode). */
  827.             SetBar(NULL, "LOGGING IN", NULL, 1, 1);
  828.             SetPostHangupOnServerProc(PostCloseStuff);
  829.             loginResult = Login(user, pass, r_acct);
  830.             
  831.             if (loginResult == 0) {
  832.                 PostLoginStuff(openopt);
  833.                 if (openopt->maxDials != 1) {
  834.                     /* If they selected redial mode, beep at the user
  835.                      * to get their attention.
  836.                      */
  837.                     Beep(1);
  838.                 }
  839.                 gAttemptingConnection = 0;    /* We are connected. */
  840.                 gRedialModeEnabled = 0;        /* Not dialing any more. */
  841.                 return(kNoErr);    /* Login okay, so done. */
  842.             }
  843.             /* Otherwise, an error during login occurred, so try again. */
  844.         } else /* (hErr == kConnectErrReTryable), so redial. */ {
  845.             /* Display error now. */
  846.             FlushListWindow();
  847.         }
  848.         
  849.         DoClose(gConnected);
  850.         gAttemptingConnection = 0;
  851.     }
  852.  
  853.     /* Display error now. */
  854.     FlushListWindow();
  855.  
  856.     if ((openopt->colonModePath[0]) && (!openopt->interactiveColonMode)) {
  857.         /* If we get here, we we're colon-moding and got a non-redialable
  858.          * error or we ran out of attempts.
  859.          */
  860.         DoQuit(kExitColonModeFail);
  861.     }
  862.     return (kCmdErr);
  863. }    /* Open */
  864.  
  865.  
  866.  
  867.  
  868. int OpenCmd(int argc, char **argv)
  869. {
  870.     OpenOptions            openopt;
  871.     int result;
  872.  
  873.     /* If there is already a site open, close that one so we can
  874.      * open a new one.
  875.      */
  876.     DoClose(gConnected);
  877.  
  878.     if (argc < 2) {
  879.         result = HostWindow();
  880.     } else {
  881.         if ((result = GetOpenOptions(argc, argv, &openopt, 0)) == kNoErr)
  882.             result = Open(&openopt);
  883.     }
  884.     return result;
  885. }    /* OpenCmd */
  886.  
  887. /* Open.c */
  888.