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 / c_exists.c < prev    next >
C/C++ Source or Header  |  2005-01-01  |  14KB  |  459 lines

  1. /* c_exists.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. /* We only use STAT to see if files or directories exist.
  14.  * But since it is so rarely used in the wild, we need to
  15.  * make sure the server supports the use where we pass
  16.  * a pathname as a parameter.
  17.  */
  18. int
  19. FTPFileExistsStat(const FTPCIPtr cip, const char *const file)
  20. {
  21.     int result;
  22.     ResponsePtr rp;
  23.     FTPLineList fileList;
  24.     char savedCwd[512];
  25.  
  26.     if (cip == NULL)
  27.         return (kErrBadParameter);
  28.     if (strcmp(cip->magic, kLibraryMagic))
  29.         return (kErrBadMagic);
  30.  
  31.     if (file == NULL)
  32.         return (kErrBadParameter);
  33.  
  34.     if (cip->STATfileParamWorks == kCommandNotAvailable) {
  35.         cip->errNo = result = kErrSTATwithFileNotAvailable;
  36.         return (result);
  37.     }
  38.  
  39.     if (cip->STATfileParamWorks == kCommandAvailabilityUnknown) {
  40.         rp = InitResponse();
  41.         if (rp == NULL) {
  42.             result = kErrMallocFailed;
  43.             cip->errNo = kErrMallocFailed;
  44.             FTPLogError(cip, kDontPerror, "Malloc failed.\n");
  45.             return (result);
  46.  
  47.         }
  48.  
  49.         /* First, make sure that when we STAT a pathname
  50.          * that does not exist, that we get an error back.
  51.          *
  52.          * We also assume that a valid STAT response has
  53.          * at least 3 lines of response text, typically
  54.          * a "start" line, intermediate data, and then
  55.          * a trailing line.
  56.          *
  57.          * We also can see a one-line case.
  58.          */
  59.         result = RCmd(cip, rp, "STAT %s", "NoSuchFile");
  60.         if ((result == 2) && ((rp->msg.nLines >= 3) || (rp->msg.nLines == 1))) {
  61.             /* Hmmm.... it gave back a positive
  62.              * response.  So STAT <file> does not
  63.              * work correctly.
  64.              */
  65.             if (
  66.                 (rp->msg.first->next != NULL) &&
  67.                 (rp->msg.first->next->line != NULL) &&
  68.                 (
  69.                     (strstr(rp->msg.first->next->line, "o such file") != NULL) ||
  70.                     (strstr(rp->msg.first->next->line, "ot found") != NULL)
  71.                 )
  72.             ) {
  73.                 /* OK, while we didn't want a 200
  74.                  * level response, some servers,
  75.                  * like wu-ftpd print an error
  76.                  * message "No such file or
  77.                  * directory" which we can special
  78.                  * case.
  79.                  */
  80.                 result = kNoErr;
  81.             } else {
  82.                 cip->STATfileParamWorks = kCommandNotAvailable;
  83.                 cip->errNo = result = kErrSTATwithFileNotAvailable;
  84.                 DoneWithResponse(cip, rp);
  85.                 return (result);
  86.             }
  87.         }
  88.         DoneWithResponse(cip, rp);
  89.  
  90.         /* We can't assume that we can simply say STAT rootdir/firstfile,
  91.          * since the remote host may not be using / as a directory
  92.          * delimiter.  So we have to change to the root directory
  93.          * and then do the STAT on that file.
  94.          */
  95.         if (
  96.             (FTPGetCWD(cip, savedCwd, sizeof(savedCwd)) != kNoErr) ||
  97.             (FTPChdir(cip, cip->startingWorkingDirectory) != kNoErr)
  98.         ) {
  99.             return (cip->errNo);
  100.         }
  101.  
  102.         /* OK, we get an error when we stat
  103.          * a non-existant file, but now we need to
  104.          * see if we get a positive reply when
  105.          * we stat a file that does exist.
  106.          *
  107.          * To do this, we list the root directory,
  108.          * which we assume has one or more items.
  109.          * If it doesn't, the user can't do anything
  110.          * anyway.  Then we stat the first item
  111.          * we found to see if STAT says it exists.
  112.          */
  113.         if (
  114.             ((result = FTPListToMemory2(cip, "", &fileList, "", 0, (int *) 0)) < 0) ||
  115.             (fileList.last == NULL) ||
  116.             (fileList.last->line == NULL)
  117.         ) {
  118.             /* Hmmm... well, in any case we can't use STAT. */
  119.             cip->STATfileParamWorks = kCommandNotAvailable;
  120.             cip->errNo = result = kErrSTATwithFileNotAvailable;
  121.             DisposeLineListContents(&fileList);
  122.             (void) FTPChdir(cip, savedCwd);
  123.             return (result);
  124.         }
  125.  
  126.         rp = InitResponse();
  127.         if (rp == NULL) {
  128.             result = kErrMallocFailed;
  129.             cip->errNo = kErrMallocFailed;
  130.             FTPLogError(cip, kDontPerror, "Malloc failed.\n");
  131.             DisposeLineListContents(&fileList);
  132.             (void) FTPChdir(cip, savedCwd);
  133.             return (result);
  134.  
  135.         }
  136.  
  137.         result = RCmd(cip, rp, "STAT %s", fileList.last->line);
  138.         DisposeLineListContents(&fileList);
  139.  
  140.         if ((result != 2) || (rp->msg.nLines == 2)) {
  141.             /* Hmmm.... it gave back a negative
  142.              * response.  So STAT <file> does not
  143.              * work correctly.
  144.              */
  145.             cip->STATfileParamWorks = kCommandNotAvailable;
  146.             cip->errNo = result = kErrSTATwithFileNotAvailable;
  147.             DoneWithResponse(cip, rp);
  148.             (void) FTPChdir(cip, savedCwd);
  149.             return (result);
  150.         } else if (
  151.                 (rp->msg.first->next != NULL) &&
  152.                 (rp->msg.first->next->line != NULL) &&
  153.                 (
  154.                     (strstr(rp->msg.first->next->line, "o such file") != NULL) ||
  155.                     (strstr(rp->msg.first->next->line, "ot found") != NULL)
  156.                 )
  157.         ) {
  158.             /* Same special-case of the second line of STAT response. */
  159.             cip->STATfileParamWorks = kCommandNotAvailable;
  160.             cip->errNo = result = kErrSTATwithFileNotAvailable;
  161.             DoneWithResponse(cip, rp);
  162.             (void) FTPChdir(cip, savedCwd);
  163.             return (result);
  164.         }
  165.         DoneWithResponse(cip, rp);
  166.         cip->STATfileParamWorks = kCommandAvailable;
  167.  
  168.         /* Don't forget to change back to the original directory. */
  169.         (void) FTPChdir(cip, savedCwd);
  170.     }
  171.  
  172.     rp = InitResponse();
  173.     if (rp == NULL) {
  174.         result = kErrMallocFailed;
  175.         cip->errNo = kErrMallocFailed;
  176.         FTPLogError(cip, kDontPerror, "Malloc failed.\n");
  177.         return (result);
  178.     }
  179.  
  180.     result = RCmd(cip, rp, "STAT %s", file);
  181.     if (result == 2) {
  182.         result = kNoErr;
  183.         if (((rp->msg.nLines >= 3) || (rp->msg.nLines == 1))) {
  184.             if (
  185.                 (rp->msg.first->next != NULL) &&
  186.                 (rp->msg.first->next->line != NULL) &&
  187.                 (
  188.                     (strstr(rp->msg.first->next->line, "o such file") != NULL) ||
  189.                     (strstr(rp->msg.first->next->line, "ot found") != NULL)
  190.                 )
  191.             ) {
  192.                 cip->errNo = kErrSTATFailed;
  193.                 result = kErrSTATFailed;
  194.             } else {
  195.                 result = kNoErr;
  196.             }
  197.         } else if (rp->msg.nLines == 2) {
  198.             cip->errNo = kErrSTATFailed;
  199.             result = kErrSTATFailed;
  200.         } else {
  201.             result = kNoErr;
  202.         }
  203.     } else {
  204.         cip->errNo = kErrSTATFailed;
  205.         result = kErrSTATFailed;
  206.     }
  207.     DoneWithResponse(cip, rp);
  208.     return (result);
  209. }    /* FTPFileExistsStat */
  210.  
  211.  
  212.  
  213.  
  214. /* We only use STAT to see if files or directories exist.
  215.  * But since it is so rarely used in the wild, we need to
  216.  * make sure the server supports the use where we pass
  217.  * a pathname as a parameter.
  218.  */
  219. int
  220. FTPFileExistsNlst(const FTPCIPtr cip, const char *const file)
  221. {
  222.     int result;
  223.     FTPLineList fileList, rootFileList;
  224.     char savedCwd[512];
  225.     int createdTempFile;
  226. #define kFTPFileExistsNlstTestMessage "This file was created by an FTP client program using the LibNcFTP toolkit.  A temporary file needed to be created to ensure that this directory was not empty, so that the directory could be listed with the guarantee of at least one file in the listing.\r\n\r\nYou may delete this file manually if your FTP client program does not delete it for you.\r\n"
  227.     const char *testFileMessage = kFTPFileExistsNlstTestMessage; 
  228. #undef kFTPFileExistsNlstTestMessage
  229.     const char *testFileName = "testfile.ftp";
  230.  
  231.     if (cip == NULL)
  232.         return (kErrBadParameter);
  233.     if (strcmp(cip->magic, kLibraryMagic))
  234.         return (kErrBadMagic);
  235.  
  236.     if (file == NULL)
  237.         return (kErrBadParameter);
  238.  
  239.     if (cip->NLSTfileParamWorks == kCommandNotAvailable) {
  240.         cip->errNo = result = kErrNLSTwithFileNotAvailable;
  241.         return (result);
  242.     }
  243.  
  244.     if (cip->NLSTfileParamWorks == kCommandAvailabilityUnknown) {
  245.         /* First, make sure that when we NLST a pathname
  246.          * that does not exist, that we get an error back.
  247.          *
  248.          * We also assume that a valid NLST response has
  249.          * at least 3 lines of response text, typically
  250.          * a "start" line, intermediate data, and then
  251.          * a trailing line.
  252.          *
  253.          * We also can see a one-line case.
  254.          */
  255.         if (
  256.             ((FTPListToMemory2(cip, "NoSuchFile", &fileList, "", 0, (int *) 0)) == kNoErr) &&
  257.             (fileList.nLines >= 1) &&
  258.             (strstr(fileList.last->line, "o such file") == NULL) &&
  259.             (strstr(fileList.last->line, "ot found") == NULL) &&
  260.             (strstr(fileList.last->line, "o Such File") == NULL) &&
  261.             (strstr(fileList.last->line, "ot Found") == NULL)
  262.  
  263.         ) {
  264.             cip->NLSTfileParamWorks = kCommandNotAvailable;
  265.             cip->errNo = result = kErrNLSTwithFileNotAvailable;
  266.             DisposeLineListContents(&fileList);
  267.             return (result);
  268.         }
  269.         DisposeLineListContents(&fileList);
  270.  
  271.         /* We can't assume that we can simply say NLST rootdir/firstfile,
  272.          * since the remote host may not be using / as a directory
  273.          * delimiter.  So we have to change to the root directory
  274.          * and then do the NLST on that file.
  275.          */
  276.         if (
  277.             (FTPGetCWD(cip, savedCwd, sizeof(savedCwd)) != kNoErr) ||
  278.             (FTPChdir(cip, cip->startingWorkingDirectory) != kNoErr)
  279.         ) {
  280.             return (cip->errNo);
  281.         }
  282.  
  283.         /* OK, we get an error when we list
  284.          * a non-existant file, but now we need to
  285.          * see if we get a positive reply when
  286.          * we stat a file that does exist.
  287.          *
  288.          * To do this, we list the root directory,
  289.          * which we assume has one or more items.
  290.          * If it doesn't, the user can't do anything
  291.          * anyway.  Then we do the first item
  292.          * we found to see if NLST says it exists.
  293.          */
  294.         createdTempFile = 0;
  295.         if (
  296.             ((result = FTPListToMemory2(cip, "", &rootFileList, "", 0, (int *) 0)) < 0) ||
  297.             (rootFileList.last == NULL) ||
  298.             (rootFileList.last->line == NULL)
  299.         ) {
  300.             if (AddLine(&rootFileList, testFileName) != NULL) {
  301.                 /* Hate to do this, but if the directory
  302.                  * is empty but writable (i.e. a $HOME)
  303.                  * then we can still continue.
  304.                  */
  305.                 if (FTPPutFileFromMemory(cip, testFileName, testFileMessage, strlen(testFileMessage), kAppendNo) == kNoErr) {
  306.                     createdTempFile = 1;
  307.                 }
  308.             }
  309.             if (createdTempFile == 0) {
  310.                 /* Hmmm... well, in any case we can't use NLST. */
  311.                 cip->NLSTfileParamWorks = kCommandNotAvailable;
  312.                 cip->errNo = result = kErrNLSTwithFileNotAvailable;
  313.                 DisposeLineListContents(&rootFileList);
  314.                 (void) FTPChdir(cip, savedCwd);
  315.                 return (result);
  316.             }
  317.         }
  318.  
  319.         if (
  320.             ((FTPListToMemory2(cip, rootFileList.last->line, &fileList, "", 0, (int *) 0)) == kNoErr) &&
  321.             (fileList.nLines >= 1) &&
  322.             (strstr(fileList.last->line, "o such file") == NULL) &&
  323.             (strstr(fileList.last->line, "ot found") == NULL) &&
  324.             (strstr(fileList.last->line, "o Such File") == NULL) &&
  325.             (strstr(fileList.last->line, "ot Found") == NULL)
  326.  
  327.         ) {
  328.             /* Good.  We listed the item. */
  329.             if (createdTempFile != 0)
  330.                 (void) FTPDelete(cip, testFileName, kRecursiveNo, kGlobNo);
  331.             DisposeLineListContents(&fileList);
  332.             DisposeLineListContents(&rootFileList);
  333.             cip->NLSTfileParamWorks = kCommandAvailable;
  334.  
  335.             /* Don't forget to change back to the original directory. */
  336.             (void) FTPChdir(cip, savedCwd);
  337.         } else {
  338.             if (createdTempFile != 0)
  339.                 (void) FTPDelete(cip, testFileName, kRecursiveNo, kGlobNo);
  340.             cip->NLSTfileParamWorks = kCommandNotAvailable;
  341.             cip->errNo = result = kErrNLSTwithFileNotAvailable;
  342.             DisposeLineListContents(&fileList);
  343.             DisposeLineListContents(&rootFileList);
  344.             (void) FTPChdir(cip, savedCwd);
  345.             return (result);
  346.         }
  347.     }
  348.  
  349.     /* Check the requested item. */
  350.     InitLineList(&fileList);
  351.     if (
  352.         ((FTPListToMemory2(cip, file, &fileList, "", 0, (int *) 0)) == kNoErr) &&
  353.         (fileList.nLines >= 1) &&
  354.         (strstr(fileList.last->line, "o such file") == NULL) &&
  355.         (strstr(fileList.last->line, "ot found") == NULL) &&
  356.         (strstr(fileList.last->line, "o Such File") == NULL) &&
  357.         (strstr(fileList.last->line, "ot Found") == NULL)
  358.  
  359.     ) {
  360.         /* The item existed. */
  361.         result = kNoErr;
  362.     } else {
  363.         cip->errNo = kErrNLSTFailed;
  364.         result = kErrNLSTFailed;
  365.     }
  366.  
  367.     DisposeLineListContents(&fileList);
  368.     return (result);
  369. }    /* FTPFileExistsNlst*/
  370.  
  371.  
  372.  
  373.  
  374. /* This functions goes to a great deal of trouble to try and determine if the
  375.  * remote file specified exists.  Newer servers support commands that make
  376.  * it relatively inexpensive to find the answer, but older servers do not
  377.  * provide a standard way.  This means we may try a whole bunch of things,
  378.  * but the good news is that the library saves information about which things
  379.  * worked so if you do this again it uses the methods that work.
  380.  */
  381. int
  382. FTPFileExists2(const FTPCIPtr cip, const char *const file, const int tryMDTM, const int trySIZE, const int tryMLST, const int trySTAT, const int tryNLST)
  383. {
  384.     int result;
  385.     time_t mdtm;
  386.     longest_int size;
  387.     MLstItem mlsInfo;
  388.  
  389.     if (tryMDTM != 0) {
  390.         result = FTPFileModificationTime(cip, file, &mdtm);
  391.         if (result == kNoErr)
  392.             return (kNoErr);
  393.         if (result == kErrMDTMFailed) {
  394.             cip->errNo = kErrNoSuchFileOrDirectory;
  395.             return (kErrNoSuchFileOrDirectory);
  396.         }
  397.         /* else keep going */    
  398.     }
  399.  
  400.     if (trySIZE != 0) {
  401.         result = FTPFileSize(cip, file, &size, kTypeBinary);
  402.         if (result == kNoErr)
  403.             return (kNoErr);
  404.         /* SIZE could fail if the server does
  405.          * not support it for directories.
  406.          *
  407.          * if (result == kErrSIZEFailed)
  408.          *    return (kErrNoSuchFileOrDirectory);
  409.          */
  410.         /* else keep going */    
  411.     }
  412.  
  413.  
  414.     if (tryMLST != 0) {
  415.         result = FTPMListOneFile(cip, file, &mlsInfo);
  416.         if (result == kNoErr)
  417.             return (kNoErr);
  418.         if (result == kErrMLSTFailed) {
  419.             cip->errNo = kErrNoSuchFileOrDirectory;
  420.             return (kErrNoSuchFileOrDirectory);
  421.         }
  422.         /* else keep going */    
  423.     }
  424.  
  425.     if (trySTAT != 0) {
  426.         result = FTPFileExistsStat(cip, file);
  427.         if (result == kNoErr)
  428.             return (kNoErr);
  429.         if (result == kErrSTATFailed) {
  430.             cip->errNo = kErrNoSuchFileOrDirectory;
  431.             return (kErrNoSuchFileOrDirectory);
  432.         }
  433.         /* else keep going */    
  434.     }
  435.  
  436.     if (tryNLST != 0) {
  437.         result = FTPFileExistsNlst(cip, file);
  438.         if (result == kNoErr)
  439.             return (kNoErr);
  440.         if (result == kErrNLSTFailed) {
  441.             cip->errNo = kErrNoSuchFileOrDirectory;
  442.             return (kErrNoSuchFileOrDirectory);
  443.         }
  444.         /* else keep going */    
  445.     }
  446.  
  447.     cip->errNo = kErrCantTellIfFileExists;
  448.     return (kErrCantTellIfFileExists);
  449. }    /* FTPFileExists2 */
  450.  
  451.  
  452.  
  453.  
  454. int
  455. FTPFileExists(const FTPCIPtr cip, const char *const file)
  456. {
  457.     return (FTPFileExists2(cip, file, 1, 1, 1, 1, 1));
  458. }    /* FTPFileExists */
  459.