home *** CD-ROM | disk | FTP | other *** search
/ ftp.ncftp.com / ftp.ncftp.com.zip / ftp.ncftp.com / ncftp / older_versions / ncftp-3.2.2-src.tar.bz2 / ncftp-3.2.2-src.tar / ncftp-3.2.2 / libncftp / open.c < prev    next >
C/C++ Source or Header  |  2008-08-18  |  30KB  |  1,081 lines

  1. /* open.c
  2.  *
  3.  * Copyright (c) 1996-2005 Mike Gleason, NcFTP Software.
  4.  * All rights reserved.
  5.  *
  6.  */
  7.  
  8. #include "syshdrs.h"
  9. #ifdef PRAGMA_HDRSTOP
  10. #    pragma hdrstop
  11. #endif
  12.  
  13. void
  14. FTPResetStatusVariables(const FTPCIPtr cip)
  15. {
  16.     cip->errNo = 0;
  17.     memset(&cip->lastFTPCmdResultStr, 0, sizeof(cip->lastFTPCmdResultStr));
  18.     cip->lastFTPCmdResultNum = 0;
  19.     cip->sec = 0;
  20.     cip->secLeft = 0;
  21.     cip->kBytesPerSec = 0;
  22.     cip->percentCompleted = 0;
  23.     cip->expectedSize = 0;
  24.     cip->mdtm = 0;
  25.     cip->nextProgressUpdate = 0;
  26.     cip->stalled = 0;
  27.     cip->dataTimedOut = 0;
  28.     cip->cancelXfer = 0;
  29.     cip->canceling = 0;
  30.     cip->canceled = 0;
  31.     cip->connected = 0;
  32.     cip->loggedIn = 0;
  33.     cip->bytesTransferred = 0;
  34.     cip->curTransferType = 0;
  35.     cip->startPoint = 0;
  36.     cip->dataSocketConnected = 0;
  37.     cip->totalDials = 0;
  38.     memset(&cip->connectTime, 0, sizeof(cip->connectTime));
  39.     memset(&cip->loginTime, 0, sizeof(cip->loginTime));
  40.     memset(&cip->disconnectTime, 0, sizeof(cip->disconnectTime));
  41.     memset(&cip->lastCmdStart, 0, sizeof(cip->lastCmdStart));
  42.     memset(&cip->lastCmdFinish, 0, sizeof(cip->lastCmdFinish));
  43.     cip->numDownloads = 0;
  44.     cip->numUploads = 0;
  45.     cip->numListings = 0;
  46. }    /* FTPResetStatusVariables */
  47.  
  48.  
  49.  
  50.  
  51. void
  52. FTPDeallocateHost(const FTPCIPtr cip)
  53. {
  54.     /* Requires the cip->bufSize field set,
  55.      * and the cip->buf set if the
  56.      * buffer is allocated.
  57.      */
  58.     if (cip->buf != NULL) {
  59.         (void) memset(cip->buf, 0, cip->bufSize);
  60.         if (cip->doAllocBuf != 0) {
  61.             free(cip->buf);
  62.             cip->buf = NULL;
  63.         }
  64.     }
  65.  
  66.     if (cip->startingWorkingDirectory != NULL) {
  67.         free(cip->startingWorkingDirectory);
  68.         cip->startingWorkingDirectory = NULL;
  69.     }
  70.  
  71. #if USE_SIO
  72.     DisposeSReadlineInfo(&cip->ctrlSrl);
  73. #endif
  74.     DisposeLineListContents(&cip->lastFTPCmdResultLL);
  75.  
  76. }    /* FTPDeallocateHost */
  77.  
  78.  
  79.  
  80.  
  81. int
  82. FTPAllocateHost(const FTPCIPtr cip)
  83. {
  84.     char *buf;
  85.  
  86.     /* Requires the cip->bufSize field set,
  87.      * and the cip->buf cleared if the
  88.      * buffer is not allocated.
  89.      */
  90.     if (cip->buf == NULL) {
  91.         if (cip->doAllocBuf == 0) {
  92.             /* User must supply buffer! */
  93.             cip->errNo = kErrBadParameter;
  94.             return (kErrBadParameter);
  95.         } else {
  96.             buf = (char *) calloc((size_t) 1, cip->bufSize);
  97.             if (buf == NULL) {
  98.                 FTPLogError(cip, kDontPerror, "Malloc failed.\n");
  99.                 cip->errNo = kErrMallocFailed;
  100.                 return (kErrMallocFailed);
  101.             }
  102.             cip->buf = buf;
  103.         }
  104.     } else {
  105.         (void) memset(cip->buf, 0, cip->bufSize);
  106.     }
  107.     return (kNoErr);
  108. }    /* FTPAllocateHost */
  109.  
  110.  
  111.  
  112.  
  113. void
  114. FTPInitializeAnonPassword(const FTPLIPtr lip)
  115. {
  116.     if (lip == NULL)
  117.         return;
  118.     if (strcmp(lip->magic, kLibraryMagic))
  119.         return;
  120.  
  121.     if (lip->defaultAnonPassword[0] == '\0')
  122.         (void) STRNCPY(lip->defaultAnonPassword, "NcFTP@");
  123. }    /* FTPInitializeAnonPassword */
  124.  
  125.  
  126.  
  127.  
  128. int
  129. FTPLoginHost(const FTPCIPtr cip)
  130. {
  131.     ResponsePtr rp;
  132.     int result = kErrLoginFailed;
  133.     int anonLogin;
  134.     int sentpass = 0, fwsentpass = 0;
  135.     int fwloggedin;
  136.     int firstTime;
  137.     char cwd[512];
  138.  
  139.     if (cip == NULL)
  140.         return (kErrBadParameter);
  141.     if ((cip->firewallType < kFirewallNotInUse) || (cip->firewallType > kFirewallLastType))
  142.         return (kErrBadParameter);
  143.  
  144.     if (strcmp(cip->magic, kLibraryMagic))
  145.         return (kErrBadMagic);
  146.  
  147.     anonLogin = 0;
  148.     if (cip->user[0] == '\0')
  149.         (void) STRNCPY(cip->user, "anonymous");
  150.     if ((strcmp(cip->user, "anonymous") == 0) || (strcmp(cip->user, "ftp") == 0)) {
  151.         anonLogin = 1;
  152.         /* Try to get the email address if you didn't specify
  153.          * a password when the user is anonymous.
  154.          */
  155.         if ((cip->pass[0] == '\0') && (cip->passIsEmpty == 0)) {
  156.             FTPInitializeAnonPassword(cip->lip);
  157.             (void) STRNCPY(cip->pass, cip->lip->defaultAnonPassword);
  158.         }
  159.     }
  160.  
  161.     rp = InitResponse();
  162.     if (rp == NULL) {
  163.         result = kErrMallocFailed;
  164.         cip->errNo = kErrMallocFailed;
  165.         goto done2;
  166.     }
  167.  
  168.     for (firstTime = 1, fwloggedin = 0; ; ) {
  169.         /* Here's a mini finite-automaton for the login process.
  170.          *
  171.          * Originally, the FTP protocol was designed to be entirely
  172.          * implementable from a FA.  It could be done, but I don't think
  173.          * it's something an interactive process could be the most
  174.          * effective with.
  175.          */
  176.  
  177.         if (firstTime != 0) {
  178.             rp->code = 220;
  179.             firstTime = 0;
  180.         } else if (result < 0) {
  181.             goto done;
  182.         }
  183.  
  184.         switch (rp->code) {
  185.             case 220:    /* Welcome, ready for new user. */
  186.                 if ((cip->firewallType == kFirewallNotInUse) || (fwloggedin != 0) || (fwsentpass != 0)) {
  187.                     ReInitResponse(cip, rp);
  188.                     result = RCmd(cip, rp, "USER %s", cip->user);
  189.                 } else if (cip->firewallType == kFirewallUserAtSite) {
  190.                     ReInitResponse(cip, rp);
  191.                     result = RCmd(cip, rp, "USER %s@%s", cip->user, cip->host);
  192.                 } else if (cip->firewallType == kFirewallUserAtSitePort) {
  193.                     ReInitResponse(cip, rp);
  194.                     result = RCmd(cip, rp, "USER %s@%s:%u", cip->user, cip->host, cip->port);
  195.                 } else if (cip->firewallType == kFirewallUserAtSitePort2) {
  196.                     ReInitResponse(cip, rp);
  197.                     result = RCmd(cip, rp, "USER %s@%s %u", cip->user, cip->host, cip->port);
  198.                 } else if (cip->firewallType == kFirewallUserAtUserPassAtPass) {
  199.                     ReInitResponse(cip, rp);
  200.                     result = RCmd(cip, rp, "USER %s@%s@%s", cip->user, cip->firewallUser, cip->host);
  201.                 } else if (cip->firewallType == kFirewallUserAtSiteFwuPassFwp) {
  202.                     ReInitResponse(cip, rp);
  203.                     result = RCmd(cip, rp, "USER %s@%s %s", cip->user, cip->host, cip->firewallUser);
  204.                 } else if (cip->firewallType == kFirewallFwuAtSiteFwpUserPass) {
  205.                     /* only reached when !fwloggedin */
  206.                     ReInitResponse(cip, rp);
  207.                     result = RCmd(cip, rp, "USER %s@%s", cip->firewallUser, cip->host);
  208.                 } else if (cip->firewallType > kFirewallNotInUse) {
  209.                     ReInitResponse(cip, rp);
  210.                     result = RCmd(cip, rp, "USER %s", cip->firewallUser);
  211.                 } else {
  212.                     goto unknown;
  213.                 }
  214.                 break;
  215.  
  216.             case 230:    /* 230 User logged in, proceed. */
  217.             case 231:    /* User name accepted. */
  218.             case 202:    /* Command not implemented, superfluous at this site. */
  219.                 if ((cip->firewallType == kFirewallNotInUse) || (fwloggedin != 0))
  220.                     goto okay;
  221.  
  222.                 /* Now logged in to the firewall. */
  223.                 fwloggedin++;
  224.  
  225.                 if (cip->firewallType == kFirewallLoginThenUserAtSite) {
  226.                     ReInitResponse(cip, rp);
  227.                     result = RCmd(cip, rp, "USER %s@%s", cip->user, cip->host);
  228.                 } else if (cip->firewallType == kFirewallUserAtUserPassAtPass) {
  229.                     goto okay;
  230.                 } else if (cip->firewallType == kFirewallOpenSite) {
  231.                     ReInitResponse(cip, rp);
  232.                     result = RCmd(cip, rp, "OPEN %s", cip->host);
  233.                 } else if (cip->firewallType == kFirewallSiteSite) {
  234.                     ReInitResponse(cip, rp);
  235.                     result = RCmd(cip, rp, "SITE %s", cip->host);
  236.                 } else if (cip->firewallType == kFirewallFwuAtSiteFwpUserPass) {
  237.                     /* only reached when !fwloggedin */
  238.                     ReInitResponse(cip, rp);
  239.                     result = RCmd(cip, rp, "USER %s", cip->user);
  240.                 } else /* kFirewallUserAtSite[Port[2]] */ {
  241.                     goto okay;
  242.                 }
  243.                 break;
  244.  
  245.             case 421:    /* 421 Service not available, closing control connection. */
  246.                 result = kErrHostDisconnectedDuringLogin;
  247.                 goto done;
  248.                 
  249.             case 331:    /* 331 User name okay, need password. */
  250.                 if ((cip->firewallType == kFirewallNotInUse) || (fwloggedin != 0) || (fwsentpass != 0)) {
  251.                     if ((cip->pass[0] == '\0') && (cip->passIsEmpty == 0) && (cip->passphraseProc != kNoFTPGetPassphraseProc))
  252.                         (*cip->passphraseProc)(cip, &rp->msg, cip->pass, sizeof(cip->pass));
  253.                     ReInitResponse(cip, rp);
  254.                     result = RCmd(cip, rp, "PASS %s", cip->pass);
  255.                     sentpass++;
  256.                 } else if ((cip->firewallType == kFirewallUserAtSite) || (cip->firewallType == kFirewallUserAtSitePort) || (cip->firewallType == kFirewallUserAtSitePort2)) {
  257.                     ReInitResponse(cip, rp);
  258.                     result = RCmd(cip, rp, "PASS %s", cip->pass);
  259.                     sentpass++;
  260.                 } else if (cip->firewallType == kFirewallUserAtUserPassAtPass) {
  261.                     ReInitResponse(cip, rp);
  262.                     result = RCmd(cip, rp, "PASS %s@%s", cip->pass, cip->firewallPass);
  263.                     fwsentpass++;
  264.                 } else if (cip->firewallType == kFirewallUserAtSiteFwuPassFwp) {
  265.                     ReInitResponse(cip, rp);
  266.                     result = RCmd(cip, rp, "PASS %s", cip->pass);
  267.                     sentpass++;
  268.                 } else if (cip->firewallType == kFirewallFwuAtSiteFwpUserPass) {
  269.                     /* only reached when !fwloggedin */
  270.                     ReInitResponse(cip, rp);
  271.                     result = RCmd(cip, rp, "PASS %s", cip->firewallPass);
  272.                     fwsentpass++;
  273.                 } else if (cip->firewallType > kFirewallNotInUse) {
  274.                     ReInitResponse(cip, rp);
  275.                     result = RCmd(cip, rp, "PASS %s", cip->firewallPass);
  276.                     fwsentpass++;
  277.                 } else {
  278.                     goto unknown;
  279.                 }
  280.                 break;
  281.  
  282.             case 332:    /* 332 Need account for login. */
  283.             case 532:     /* 532 Need account for storing files. */
  284.                 ReInitResponse(cip, rp);
  285.                 result = RCmd(cip, rp, "ACCT %s", (cip->acct[0] != '\0') ? cip->acct : cip->firewallPass);
  286.                 break;
  287.  
  288.             case 530:    /* Not logged in. */
  289.                 result = (sentpass != 0) ? kErrBadRemoteUserOrPassword : kErrBadRemoteUser;
  290.                 goto done;
  291.  
  292.             case 501:    /* Syntax error in parameters or arguments. */
  293.             case 503:    /* Bad sequence of commands. */
  294.             case 550:    /* Can't set guest privileges. */
  295.                 goto done;
  296.                 
  297.             default:
  298.             unknown:
  299.                 if (rp->msg.first == NULL) {
  300.                     FTPLogError(cip, kDontPerror, "Lost connection during login.\n");
  301.                 } else {
  302.                     FTPLogError(cip, kDontPerror, "Unexpected response: %s\n",
  303.                         rp->msg.first->line
  304.                     );
  305.                 }
  306.                 goto done;
  307.         }
  308.     }
  309.  
  310. okay:
  311.     /* Do the application's connect message callback, if present. */
  312.     if (cip->onLoginMsgProc != 0)
  313.         (*cip->onLoginMsgProc)(cip, rp);
  314.     DoneWithResponse(cip, rp);
  315.     result = kNoErr;
  316.     cip->loggedIn = 1;
  317.  
  318.     /* Make a note of what our root directory is.
  319.      * This is often different from "/" when not
  320.      * logged in anonymously.
  321.      */
  322.     if (cip->startingWorkingDirectory != NULL) {
  323.         free(cip->startingWorkingDirectory);
  324.         cip->startingWorkingDirectory = NULL;
  325.     }
  326.     if ((cip->doNotGetStartingWorkingDirectory == 0) &&
  327.         (FTPGetCWD(cip, cwd, sizeof(cwd)) == kNoErr))
  328.     {
  329.         cip->startingWorkingDirectory = StrDup(cwd);
  330.     }
  331.  
  332.     /* When a new site is opened, ASCII mode is assumed (by protocol). */
  333.     cip->curTransferType = 'A';
  334.     PrintF(cip, "Logged in to %s as %s.\n", cip->host, cip->user);
  335.  
  336.     /* Don't leave cleartext password in memory, since we
  337.      * are logged in and do not need it any more.
  338.      */
  339.     if ((anonLogin == 0) && (cip->leavePass == 0))
  340.         (void) memset(cip->pass, '*', sizeof(cip->pass) - 1);
  341.  
  342.     (void) gettimeofday(&cip->loginTime, NULL);
  343.     return result;    /* kNoErr */
  344.  
  345. done:
  346.     DoneWithResponse(cip, rp);
  347.  
  348. done2:
  349.     if ((anonLogin == 0) && (cip->leavePass == 0)) {
  350.         switch (result) {
  351.             case kErrConnectRetryableErr:
  352.             case kErrConnectRefused:
  353.             case kErrRemoteHostClosedConnection:
  354.             case kErrHostDisconnectedDuringLogin:
  355.                 break;
  356.             default:
  357.                 /* Don't leave cleartext password in memory,
  358.                  * since we won't be redialing.
  359.                  */
  360.                 (void) memset(cip->pass, '*', sizeof(cip->pass) - 1);
  361.         }
  362.     }
  363.     if (result > 0) {
  364.         result = cip->errNo = kErrLoginHostMiscErr;
  365.     }
  366.     if (result < 0) {
  367.         cip->errNo = result;
  368.     } else {
  369.         (void) gettimeofday(&cip->loginTime, NULL);
  370.     }
  371.     return result;
  372. }    /* FTPLoginHost */
  373.  
  374.  
  375.  
  376.  
  377. static void
  378. FTPExamineMlstFeatures(const FTPCIPtr cip, const char *features)
  379. {
  380.     char buf[256], *feat;
  381.     int flags;
  382.     char *ctext;
  383.  
  384.     flags = 0;
  385.     STRNCPY(buf, features);
  386.     ctext = NULL;
  387.     feat = strtokc(buf, ";*", &ctext);
  388.     while (feat != NULL) {
  389.         if (ISTRNEQ(feat, "OS.", 3))
  390.             feat += 3;
  391.         if (ISTREQ(feat, "type")) {
  392.             flags |= kMlsOptType;
  393.         } else if (ISTREQ(feat, "size")) {
  394.             flags |= kMlsOptSize;
  395.         } else if (ISTREQ(feat, "modify")) {
  396.             flags |= kMlsOptModify;
  397.         } else if (ISTREQ(feat, "UNIX.mode")) {
  398.             flags |= kMlsOptUNIXmode;
  399.         } else if (ISTREQ(feat, "UNIX.owner")) {
  400.             flags |= kMlsOptUNIXowner;
  401.         } else if (ISTREQ(feat, "UNIX.group")) {
  402.             flags |= kMlsOptUNIXgroup;
  403.         } else if (ISTREQ(feat, "perm")) {
  404.             flags |= kMlsOptPerm;
  405.         } else if (ISTREQ(feat, "UNIX.uid")) {
  406.             flags |= kMlsOptUNIXuid;
  407.         } else if (ISTREQ(feat, "UNIX.gid")) {
  408.             flags |= kMlsOptUNIXgid;
  409.         } else if (ISTREQ(feat, "UNIX.gid")) {
  410.             flags |= kMlsOptUnique;
  411.         }
  412.         feat = strtokc(NULL, ";*", &ctext);
  413.     }
  414.  
  415.     cip->mlsFeatures = flags;
  416. }    /* FTPExamineMlstFeatures */
  417.  
  418.  
  419.  
  420.  
  421. int
  422. FTPQueryFeatures(const FTPCIPtr cip)
  423. {
  424.     ResponsePtr rp;
  425.     int result;
  426.     FTPLinePtr lp;
  427.     char *cp, *p;
  428.  
  429.     if (cip == NULL)
  430.         return (kErrBadParameter);
  431.     if (strcmp(cip->magic, kLibraryMagic))
  432.         return (kErrBadMagic);
  433.  
  434.     if (cip->serverType == kServerTypeNetWareFTP) {
  435.         /* NetWare 5.00 server freaks out when
  436.          * you give it a command it doesn't
  437.          * recognize, so cheat here and return.
  438.          */
  439.         cip->hasPASV = kCommandAvailable;
  440.         cip->hasSIZE = kCommandNotAvailable;
  441.         cip->hasMDTM = kCommandNotAvailable;
  442.         cip->hasMDTM_set = kCommandNotAvailable;
  443.         cip->hasREST = kCommandNotAvailable;
  444.         cip->NLSTfileParamWorks = kCommandAvailable;
  445.         cip->hasCLNT = kCommandNotAvailable;
  446.         cip->hasMLST = kCommandNotAvailable;
  447.         cip->hasMLSD = kCommandNotAvailable;
  448.         cip->hasSITE_UTIME = kCommandNotAvailable;
  449.         cip->hasHELP_SITE = kCommandNotAvailable;
  450.         return (kNoErr);
  451.     }
  452.  
  453.     if (cip->serverType == kServerTypeProFTPD) {
  454.         /* They won't fix a bug where "NLST -a" outputs as "LIST -a" */
  455.         cip->hasNLST_a = kCommandNotAvailable;
  456.     }
  457.     
  458.     /* Older ftpd implementations have problems. */
  459.     if (    (cip->serverType == kServerTypeDguxFTP) ||
  460.         (cip->serverType == kServerTypePyramid) ||
  461.         (cip->serverType == kServerTypeIBMFTPCS))
  462.     {
  463.         cip->hasCLNT = kCommandNotAvailable;
  464.         cip->hasMLST = kCommandNotAvailable;
  465.         cip->hasMLSD = kCommandNotAvailable;
  466.         cip->hasSITE_UTIME = kCommandNotAvailable;
  467.         cip->hasHELP_SITE = kCommandNotAvailable;
  468.     }
  469.     
  470.     rp = InitResponse();
  471.     if (rp == NULL) {
  472.         cip->errNo = kErrMallocFailed;
  473.         result = cip->errNo;
  474.     } else if (cip->hasFEAT != kCommandNotAvailable) {
  475.         rp->printMode = (kResponseNoPrint|kResponseNoSave);
  476.         result = RCmd(cip, rp, "FEAT");
  477.         if (result < kNoErr) {
  478.             DoneWithResponse(cip, rp);
  479.             return (result);
  480.         } else if (result != 2) {
  481.             /* We cheat here and pre-populate some
  482.              * fields when the server is wu-ftpd.
  483.              * This server is very common and we
  484.              * know it has always had these.
  485.              */
  486.              if (cip->serverType == kServerTypeWuFTPd) {
  487.                 cip->hasPASV = kCommandAvailable;
  488.                 cip->hasSIZE = kCommandAvailable;
  489.                 cip->hasMDTM = kCommandAvailable;
  490.                 cip->hasMDTM_set = kCommandAvailable;
  491.                 cip->hasREST = kCommandAvailable;
  492.                 cip->NLSTfileParamWorks = kCommandAvailable;
  493.             } else if (cip->serverType == kServerTypeNcFTPd) {
  494.                 cip->hasPASV = kCommandAvailable;
  495.                 cip->hasSIZE = kCommandAvailable;
  496.                 cip->hasMDTM = kCommandAvailable;
  497.                 cip->hasREST = kCommandAvailable;
  498.                 cip->NLSTfileParamWorks = kCommandAvailable;
  499.             }
  500.  
  501.             /* Newer commands are only shown in FEAT,
  502.              * so we don't have to do the "try it,
  503.              * then save that it didn't work" thing.
  504.              */
  505.             cip->hasMLST = kCommandNotAvailable;
  506.             cip->hasMLSD = kCommandNotAvailable;
  507.         } else {
  508.             cip->hasFEAT = kCommandAvailable;
  509.  
  510.             for (lp = rp->msg.first; lp != NULL; lp = lp->next) {
  511.                 /* If first character was not a space it is
  512.                  * either:
  513.                  *
  514.                  * (a) The header line in the response;
  515.                  * (b) The trailer line in the response;
  516.                  * (c) A protocol violation.
  517.                  */
  518.                 cp = lp->line;
  519.                 while ((*cp != '\0') && (isspace(*cp)))
  520.                     cp++;
  521.                 if (*cp == '\0')
  522.                     continue;
  523.                 if (ISTRNCMP(cp, "PASV", 4) == 0) {
  524.                     cip->hasPASV = kCommandAvailable;
  525.                 } else if (ISTRNCMP(cp, "SIZE", 4) == 0) {
  526.                     cip->hasSIZE = kCommandAvailable;
  527.                 } else if (ISTRNCMP(cp, "MDTM", 4) == 0) {
  528.                     cip->hasMDTM = kCommandAvailable;
  529.                 } else if (ISTRNCMP(cp, "REST", 4) == 0) {
  530.                     cip->hasREST = kCommandAvailable;
  531.                 } else if (ISTRNCMP(cp, "UTIME", 5) == 0) {
  532.                     cip->hasSITE_UTIME = kCommandAvailable;
  533.                 } else if (ISTRNCMP(cp, "MLST", 4) == 0) {
  534.                     cip->hasMLST = kCommandAvailable;
  535.                     cip->hasMLSD = kCommandAvailable;
  536.                     FTPExamineMlstFeatures(cip, cp + 5);
  537.                 } else if (ISTRNCMP(cp, "CLNT", 4) == 0) {
  538.                     cip->hasCLNT = kCommandAvailable;
  539.                 } else if (ISTRNCMP(cp, "Compliance Level: ", 18) == 0) {
  540.                     /* Probably only NcFTPd will ever implement this.
  541.                      * But we use it internally to differentiate
  542.                      * between different NcFTPd implementations of
  543.                      * IETF extensions.
  544.                      */
  545.                     cip->ietfCompatLevel = atoi(cp + 18);
  546.                 }
  547.             }
  548.         }
  549.  
  550.         /* You can set cip->hasHELP_SITE to kCommandNotAvailable
  551.          * if your host chokes (i.e. IBM Mainframes) when you
  552.          * do "HELP SITE".
  553.          */
  554.         ReInitResponse(cip, rp);
  555.         result = (cip->hasHELP_SITE == kCommandNotAvailable) ? (-1) : RCmd(cip, rp, "HELP SITE");
  556.         if (result == 2) {
  557.             cip->hasHELP_SITE = kCommandAvailable;
  558.             for (lp = rp->msg.first; lp != NULL; lp = lp->next) {
  559.                 cp = lp->line;
  560.                 if (strstr(cp, "RETRBUFSIZE") != NULL)
  561.                     cip->hasSITE_RETRBUFSIZE = kCommandAvailable;
  562.                 if (strstr(cp, "RBUFSZ") != NULL)
  563.                     cip->hasSITE_RBUFSZ = kCommandAvailable;
  564.                 /* See if RBUFSIZ matches (but not STORBUFSIZE) */
  565.                 if (
  566.                     ((p = strstr(cp, "RBUFSIZ")) != NULL) &&
  567.                     (
  568.                          (p == cp) ||
  569.                         ((p > cp) && (!isupper((int) p[-1])))
  570.                     )
  571.                 )
  572.                     cip->hasSITE_RBUFSIZ = kCommandAvailable;
  573.                 if (strstr(cp, "STORBUFSIZE") != NULL)
  574.                     cip->hasSITE_STORBUFSIZE = kCommandAvailable;
  575.                 if (strstr(cp, "SBUFSIZ") != NULL)
  576.                     cip->hasSITE_SBUFSIZ = kCommandAvailable;
  577.                 if (strstr(cp, "SBUFSZ") != NULL)
  578.                     cip->hasSITE_SBUFSZ = kCommandAvailable;
  579.                 if (strstr(cp, "BUFSIZE") != NULL)
  580.                     cip->hasSITE_BUFSIZE = kCommandAvailable;
  581.             }
  582.         }
  583.         DoneWithResponse(cip, rp);
  584.     }
  585.  
  586.     return (kNoErr);
  587. }    /* FTPQueryFeatures */
  588.  
  589.  
  590.  
  591. int
  592. FTPCloseHost(const FTPCIPtr cip)
  593. {
  594.     ResponsePtr rp;
  595.     int result;
  596.  
  597.     if (cip == NULL)
  598.         return (kErrBadParameter);
  599.     if (strcmp(cip->magic, kLibraryMagic))
  600.         return (kErrBadMagic);
  601.  
  602.     /* Data connection shouldn't be open normally. */
  603.     if (cip->dataSocket != kClosedFileDescriptor)
  604.         FTPAbortDataTransfer(cip);
  605.  
  606.     result = kNoErr;
  607.     if (cip->connected != 0) {
  608.         rp = InitResponse();
  609.         if (rp == NULL) {
  610.             cip->errNo = kErrMallocFailed;
  611.             result = cip->errNo;
  612.         } else {
  613.             rp->eofOkay = 1;    /* We are expecting EOF after this cmd. */
  614.             cip->eofOkay = 1;
  615.             (void) RCmd(cip, rp, "QUIT");
  616.             DoneWithResponse(cip, rp);
  617.         }
  618.     }
  619.     
  620.     FTPCloseControlConnection(cip);
  621.  
  622.     /* Dispose dynamic data structures, so you won't leak
  623.      * if you OpenHost with this again.
  624.      */
  625.     FTPDeallocateHost(cip);
  626.  
  627.     if (cip->disconnectTime.tv_sec == 0)
  628.         (void) gettimeofday(&cip->disconnectTime, NULL);
  629.     return (result);
  630. }    /* FTPCloseHost */
  631.  
  632.  
  633.  
  634.  
  635. void
  636. FTPInitialLogEntry(const FTPCIPtr cip)
  637. {
  638. #if defined(HAVE_UNAME) && defined(HAVE_SYS_UTSNAME_H)
  639.     struct utsname u;
  640. #endif
  641.  
  642.     if (cip->startTime.tv_sec == 0) {
  643.         (void) gettimeofday(&cip->startTime, NULL);
  644.  
  645.         /* Log some headers for debugging.
  646.          *
  647.          * Note that this is the soonest we can do this,
  648.          * since the user may not have set the debugLog
  649.          * fields right away.
  650.          */
  651.         PrintF(cip, "%s compiled for %s\n",
  652.             gLibNcFTPVersion + 5,
  653. #ifdef O_S
  654.             O_S
  655. #elif (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__)
  656.             "Windows"
  657. #elif defined(__CYGWIN__)
  658.             "Cygwin"
  659. #else
  660.             "UNIX"
  661. #endif
  662.         );
  663.     
  664. #if defined(HAVE_UNAME) && defined(HAVE_SYS_UTSNAME_H)
  665.         memset(&u, 0, sizeof(u));
  666.         if (uname(&u) == 0) {
  667.             PrintF(cip, "Uname: %s|%s|%s|%s|%s\n",
  668.                 u.sysname,
  669.                 u.nodename,
  670.                 u.release,
  671.                 u.version,
  672.                 u.machine
  673.             );
  674.         }
  675. #endif
  676.  
  677. #if defined(SOLARIS) || defined(AIX)
  678.         {
  679.             FILE *fp;
  680.             char line[256], *cp;
  681.  
  682. #if defined(SOLARIS)
  683.             fp = fopen("/etc/release", "r");
  684. #elif defined(AIX)
  685.             fp = fopen("/usr/lpp/bos/aix_release.level", "r");
  686. #endif
  687.             if (fp != NULL) {
  688.                 memset(line, 0, sizeof(line));
  689.                 (void) fgets(line, sizeof(line) - 1, fp);
  690.                 cp = line + strlen(line) - 1;
  691.                 while (cp > line) {
  692.                     if (! isspace((int) *cp))
  693.                         break;
  694.                     --cp;
  695.                 }
  696.                 cp[1] = '\0';
  697.                 cp = line;
  698.                 while ((*cp != '\0') && (isspace((int) *cp)))
  699.                     cp++;
  700.                 fclose(fp);
  701.                 PrintF(cip, "Release: %s\n", cp);
  702.             }
  703.         }
  704. #endif
  705.  
  706.  
  707. #if defined(HAVE_SYSINFO) && defined(SI_ARCHITECTURE) && defined(SI_ISALIST) && defined(SI_PLATFORM)
  708.         {
  709.             char si_platform[64];
  710.             char si_arch[32];
  711.             char si_isalist[256];
  712.  
  713.             memset(si_platform, 0, sizeof(si_platform));
  714.             memset(si_arch, 0, sizeof(si_arch));
  715.             memset(si_isalist, 0, sizeof(si_isalist));
  716.  
  717.             (void) sysinfo(SI_PLATFORM, si_platform, sizeof(si_platform) - 1);
  718.             (void) sysinfo(SI_ARCHITECTURE, si_arch, sizeof(si_arch) - 1);
  719.             (void) sysinfo(SI_ISALIST, si_isalist, sizeof(si_isalist) - 1);
  720.             PrintF(cip, "Sysinfo: %s|%s|%s\n",
  721.                 si_platform,
  722.                 si_arch,
  723.                 si_isalist
  724.             );
  725.         }
  726. #endif
  727.  
  728. #ifdef LINUX
  729.     {
  730.         FILE *etc_fp;
  731.         const char **etc_fn;
  732.         char etc_line[256], *etc_cp;
  733.  
  734.         const char *etc_fnames[] = {
  735.             "/etc/yellowdog-release",
  736.             "/etc/gentoo-release",
  737.             "/etc/turbolinux-release",
  738.             "/etc/slackware-release",
  739.             "/etc/slackware-version",
  740.             "/etc/mandrake-release",
  741.             "/etc/debian_version",
  742.             "/etc/conectiva-release",
  743.             "/etc/SuSE-release",
  744.             "/etc/fedora-release",
  745.             "/etc/redhat-release",    /* Works for CentOS, too. */
  746.             "/etc/issue",
  747.             NULL,
  748.         };
  749.  
  750.         for (etc_fn = etc_fnames; *etc_fn != NULL; etc_fn++) {
  751.             etc_fp = fopen(*etc_fn, "r");
  752.             if (etc_fp != NULL) {
  753.                 PrintF(cip, "Contents of %.127s:\n", *etc_fn);
  754.                 memset(etc_line, 0, sizeof(etc_line));
  755.                 while (fgets(etc_line, sizeof(etc_line) - 1, etc_fp) != NULL) {
  756.                     etc_cp = etc_line + strlen(etc_line) - 1;
  757.                     while ((etc_cp >= etc_line) && (isspace((int) *etc_cp)))
  758.                         etc_cp--;
  759.                     ++etc_cp;
  760.                     *etc_cp = '\0';
  761.                     if (etc_line[0] != '\0') {
  762.                         PrintF(cip, "  %.127s\n", etc_line);
  763.                     }
  764.                 }
  765.                 (void) fclose(etc_fp);
  766.             }
  767.         }
  768.     }
  769. #endif    /* LINUX */
  770.  
  771. #if defined(HAVE_GNU_GET_LIBC_VERSION) && defined(HAVE_GNU_GET_LIBC_RELEASE)
  772.         PrintF(cip, "Glibc: %s (%s)\n",
  773.             gnu_get_libc_version(),
  774.             gnu_get_libc_release()
  775.         );
  776. #endif    /* GLIBC */
  777.  
  778. #ifdef MACOSX
  779. /*
  780. <?xml version="1.0" encoding="UTF-8"?>
  781. <!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
  782. <plist version="0.9">
  783. <dict>
  784.     <key>ProductBuildVersion</key>
  785.     <string>5S66</string>
  786.     <key>ProductName</key>
  787.     <string>Mac OS X</string>
  788.     <key>ProductVersion</key>
  789.     <string>10.1.5</string>
  790. </dict>
  791. </plist>
  792. */
  793.         {
  794.             char osx_ver[32], osx_build[32];
  795.             char line[256], *cp, *cp2; 
  796.             FILE *fp;
  797.  
  798.             memset(osx_ver, 0, sizeof(osx_ver));
  799.             memset(osx_build, 0, sizeof(osx_build));
  800.  
  801.             fp = fopen("/System/Library/CoreServices/SystemVersion.plist", "r");
  802.  
  803.             if (fp != NULL) {
  804.                 memset(line, 0, sizeof(line));
  805.                 while (fgets(line, sizeof(line) - 1, fp) != NULL) {
  806.                     cp = strstr(line, "<key>ProductVersion</key>");
  807.                     if (cp != NULL) {
  808.                         memset(line, 0, sizeof(line));
  809.                         if (fgets(line, sizeof(line) - 2, fp) != NULL) {
  810.                             for (cp = line; ((*cp != '\0') && (! isdigit(*cp))); ) cp++;
  811.                             for (cp2 = cp; ((*cp2 != '\0') && (! isspace(*cp2)) && (*cp2 != '<') && (*cp2 != '>')); ) cp2++;
  812.                             cp2[0] = '\0';
  813.                             STRNCPY(osx_ver, cp);
  814.                         }
  815.                     }
  816.  
  817.                     cp = strstr(line, "<key>ProductBuildVersion</key>");
  818.                     if (cp != NULL) {
  819.                         memset(line, 0, sizeof(line));
  820.                         if (fgets(line, sizeof(line) - 2, fp) != NULL) {
  821.                             for (cp = line; ((*cp != '\0') && (! isdigit(*cp))); ) cp++;
  822.                             for (cp2 = cp; ((*cp2 != '\0') && (! isspace(*cp2)) && (*cp2 != '<') && (*cp2 != '>')); ) cp2++;
  823.                             cp2[0] = '\0';
  824.                             STRNCPY(osx_build, cp);
  825.                         }
  826.                     }
  827.                 }
  828.                 fclose(fp);
  829.             }
  830.             PrintF(cip, "Sysinfo: Mac OS X %s (Build %s)\n",
  831.                 osx_ver,
  832.                 osx_build
  833.             );
  834.         }
  835. #endif    /* MACOSX */
  836.  
  837.     } else {
  838.         /* Starting a new set of opens, but simply update
  839.          * the timestamp rather than re-log the headers.
  840.          */
  841.         (void) gettimeofday(&cip->startTime, NULL);
  842.     }
  843. }    /* FTPInitialLogEntry */
  844.  
  845.  
  846.  
  847. int
  848. FTPOpenHost(const FTPCIPtr cip)
  849. {
  850.     int result;
  851.     time_t t0, t1;
  852.     int elapsed;
  853.  
  854.     if (cip == NULL)
  855.         return (kErrBadParameter);
  856.     if (strcmp(cip->magic, kLibraryMagic))
  857.         return (kErrBadMagic);
  858.  
  859.     if (cip->host[0] == '\0') {
  860.         cip->errNo = kErrNoHostSpecified;
  861.         return (kErrNoHostSpecified);
  862.     }
  863.  
  864.     FTPResetStatusVariables(cip);
  865.     FTPManualOverrideFeatures(cip);
  866.     FTPInitialLogEntry(cip);
  867.  
  868.     for (    result = kErrConnectMiscErr, cip->numDials = 0;
  869.         cip->maxDials < 0 || cip->numDials < cip->maxDials;
  870.     )    
  871.     {
  872.         /* Allocate (or if the host was closed, reallocate)
  873.          * the transfer data buffer.
  874.          */
  875.         result = FTPAllocateHost(cip);
  876.         if (result < 0)
  877.             return (result);
  878.  
  879.         memset(&cip->connectTime, 0, sizeof(cip->connectTime));
  880.         memset(&cip->loginTime, 0, sizeof(cip->loginTime));
  881.         memset(&cip->disconnectTime, 0, sizeof(cip->disconnectTime));
  882.  
  883.         cip->totalDials++;
  884.         cip->numDials++;
  885.         if (cip->numDials > 1)
  886.             PrintF(cip, "Retry Number: %d\n", cip->numDials - 1);
  887.         if (cip->redialStatusProc != 0)
  888.             (*cip->redialStatusProc)(cip, kRedialStatusDialing, cip->numDials - 1);
  889.         (void) time(&t0);
  890.         result = OpenControlConnection(cip, cip->host, cip->port);
  891.         (void) time(&t1);
  892.         if (result == kNoErr) {
  893.             /* We were hooked up successfully. */
  894.             (void) gettimeofday(&cip->connectTime, NULL);
  895.             PrintF(cip, "Connected to %s.\n", cip->host);
  896.  
  897.             result = FTPLoginHost(cip);
  898.             if (result == kNoErr) {
  899.                 (void) FTPQueryFeatures(cip);
  900.                 /* Do this again in case QueryFeatures
  901.                  * changed anything that the user wants
  902.                  * to use.
  903.                  */
  904.                 FTPManualOverrideFeatures(cip);
  905.                 break;
  906.             }
  907.  
  908.             /* Close and try again. */
  909.             (void) FTPCloseHost(cip);
  910.  
  911.             /* Originally we also stopped retyring if
  912.              * we got kErrBadRemoteUser and non-anonymous,
  913.              * but some FTP servers apparently do their
  914.              * max user check after the username is sent.
  915.              */
  916.             if (result == kErrBadRemoteUserOrPassword /* || (result == kErrBadRemoteUser) */) {
  917.                 if (strcmp(cip->user, "anonymous") != 0) {
  918.                     /* Non-anonymous login was denied, and
  919.                      * retrying is not going to help.
  920.                      */
  921.                     break;
  922.                 }
  923.             }
  924.         } else if ((result != kErrConnectRetryableErr) && (result != kErrConnectRefused) && (result != kErrRemoteHostClosedConnection) && (result != kErrHostDisconnectedDuringLogin)) {
  925.             /* Irrecoverable error, so don't bother redialing.
  926.              * The error message should have already been printed
  927.              * from OpenControlConnection().
  928.              */
  929.             PrintF(cip, "Cannot recover from miscellaneous open error %d.\n", result);
  930.             if (result > 0)
  931.                 result = kErrOpenHostMiscErr;
  932.             FTPDeallocateHost(cip);
  933.             return result;
  934.         }
  935.  
  936.         /* Retryable error, wait and then redial. */
  937.         if (cip->redialDelay > 0) {
  938.             /* But don't sleep if this is the last loop. */
  939.             if ((cip->maxDials < 0) || (cip->numDials < (cip->maxDials))) {
  940.                 elapsed = (int) (t1 - t0);
  941.                 if (elapsed < cip->redialDelay) {
  942.                     PrintF(cip, "Sleeping %u seconds.\n",
  943.                         (unsigned) cip->redialDelay - elapsed);
  944.                     if (cip->redialStatusProc != 0)
  945.                         (*cip->redialStatusProc)(cip, kRedialStatusSleeping, cip->redialDelay - elapsed);
  946.                     (void) sleep((unsigned) cip->redialDelay - elapsed);
  947.                 }
  948.             }
  949.         }
  950.     }
  951.     if (result > 0)
  952.         result = kErrOpenHostMiscErr;
  953.     if (result != kNoErr)
  954.         FTPDeallocateHost(cip);
  955.     return (result);
  956. }    /* FTPOpenHost */
  957.  
  958.  
  959.  
  960.  
  961. int
  962. FTPInitConnectionInfo2(const FTPLIPtr lip, const FTPCIPtr cip, char *const buf, size_t bufSize)
  963. {
  964.     size_t siz;
  965.  
  966.     if ((lip == NULL) || (cip == NULL) || (bufSize == 0))
  967.         return (kErrBadParameter);
  968.  
  969.     siz = sizeof(FTPConnectionInfo);
  970.     (void) memset(cip, 0, siz);
  971.  
  972.     if (strcmp(lip->magic, kLibraryMagic))
  973.         return (kErrBadMagic);
  974.  
  975.     cip->bufSize = bufSize;
  976.     if (buf == NULL) {
  977.         /* We're responsible for allocating the buffer. */
  978.         cip->buf = NULL;    /* denote that it needs to be allocated. */
  979.         cip->doAllocBuf = 1;
  980.     } else {
  981.         /* The application is passing us a buffer to use. */
  982.         cip->buf = buf;
  983.         cip->doAllocBuf = 0;
  984.     }
  985.     cip->port = lip->defaultPort;
  986.     cip->firewallPort = lip->defaultPort;
  987.     cip->maxDials = kDefaultMaxDials;
  988.     cip->redialDelay = kDefaultRedialDelay;
  989.     cip->xferTimeout = kDefaultXferTimeout;
  990.     cip->connTimeout = kDefaultConnTimeout;
  991.     cip->ctrlTimeout = kDefaultCtrlTimeout;
  992.     cip->abortTimeout = kDefaultAbortTimeout;
  993.     cip->ctrlSocketR = kClosedFileDescriptor;
  994.     cip->ctrlSocketW = kClosedFileDescriptor;
  995.     cip->dataPortMode = kDefaultDataPortMode;
  996.     cip->maxNumberOfSuccessivePASVAttempts = kDefaultMaxNumberOfSuccessivePASVAttempts;
  997.     cip->dataSocket = kClosedFileDescriptor;
  998.     cip->lip = lip;
  999.     cip->hasPASV = kCommandAvailabilityUnknown;
  1000.     cip->hasSIZE = kCommandAvailabilityUnknown;
  1001.     cip->hasMDTM = kCommandAvailabilityUnknown;
  1002.     cip->hasMDTM_set = kCommandAvailabilityUnknown;
  1003.     cip->hasREST = kCommandAvailabilityUnknown;
  1004.     cip->hasNLST_a = kCommandAvailabilityUnknown;
  1005.     cip->hasNLST_d = kCommandAvailabilityUnknown;
  1006.     cip->hasFEAT = kCommandAvailabilityUnknown;
  1007.     cip->hasMLSD = kCommandAvailabilityUnknown;
  1008.     cip->hasMLST = kCommandAvailabilityUnknown;
  1009.     cip->hasCLNT = kCommandAvailabilityUnknown;
  1010.     cip->hasHELP_SITE = kCommandAvailabilityUnknown;
  1011.     cip->hasSITE_UTIME = kCommandAvailabilityUnknown;
  1012.     cip->hasSITE_RETRBUFSIZE = kCommandAvailabilityUnknown;
  1013.     cip->hasSITE_RBUFSIZ = kCommandAvailabilityUnknown;
  1014.     cip->hasSITE_RBUFSZ = kCommandAvailabilityUnknown;
  1015.     cip->hasSITE_STORBUFSIZE = kCommandAvailabilityUnknown;
  1016.     cip->hasSITE_SBUFSIZ = kCommandAvailabilityUnknown;
  1017.     cip->hasSITE_SBUFSZ = kCommandAvailabilityUnknown;
  1018.     cip->STATfileParamWorks = kCommandAvailabilityUnknown;
  1019.     cip->NLSTfileParamWorks = kCommandAvailabilityUnknown;
  1020.     cip->firewallType = kFirewallNotInUse;
  1021.     cip->startingWorkingDirectory = NULL;
  1022.     cip->shutdownUnusedSideOfSockets = 0;
  1023.     cip->asciiTranslationMode = kAsciiTranslationModeDefault;
  1024.     
  1025. #ifdef MACOSX
  1026.     /* For Mac OS 9 compatibility you could set this to '\r' */
  1027.     cip->textEOLN[0] = '\n';
  1028. #elif (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__)
  1029.     cip->textEOLN[0] = '\r';
  1030.     cip->textEOLN[1] = '\n';
  1031. #else    /* UNIX */
  1032.     cip->textEOLN[0] = '\n';
  1033. #endif
  1034.  
  1035.     InitLineList(&cip->lastFTPCmdResultLL);
  1036.     (void) STRNCPY(cip->magic, kLibraryMagic);
  1037.     (void) STRNCPY(cip->user, "anonymous");
  1038.     (void) gettimeofday(&cip->initTime, NULL);
  1039.     return (kNoErr);
  1040. }    /* FTPInitConnectionInfo2 */
  1041.  
  1042.  
  1043.  
  1044.  
  1045. int
  1046. FTPInitConnectionInfo(const FTPLIPtr lip, const FTPCIPtr cip, size_t bufSize)
  1047. {
  1048.     return (FTPInitConnectionInfo2(lip, cip, NULL, bufSize));
  1049. }    /* FTPInitConnectionInfo */
  1050.  
  1051.  
  1052.  
  1053.  
  1054. int
  1055. FTPInitLibrary(const FTPLIPtr lip)
  1056. {
  1057.     if (lip == NULL)
  1058.         return (kErrBadParameter);
  1059.  
  1060.     (void) memset(lip, 0, sizeof(FTPLibraryInfo));
  1061.  
  1062.     lip->defaultPort = ServiceNameToPortNumber("ftp", 't');
  1063.     if (lip->defaultPort == 0)
  1064.         lip->defaultPort = (unsigned int) kDefaultFTPPort;
  1065.  
  1066.     lip->init = 1;
  1067.     (void) STRNCPY(lip->magic, kLibraryMagic);
  1068.  
  1069.     /* We'll initialize the defaultAnonPassword field
  1070.      * later when we try the first anon ftp connection.
  1071.      */
  1072.  
  1073. #ifdef HAVE_LIBSOCKS
  1074.     SOCKSinit("libncftp");
  1075.     lip->socksInit = 1;
  1076. #endif
  1077.     return (kNoErr);
  1078. }    /* FTPInitLibrary */
  1079.  
  1080. /* Open.c */
  1081.