LibNcFTP: FTP Client Library

  • What is this?
  • System Requirements
  • Downloading and Extracting the LibNcFTP Package File
  • Building and Installing LibNcFTP for UNIX
  • Building and Installing LibNcFTP for Windows
  • Tutorial
  • Library Data Structures
  • Function reference
  • Questions & Answers

  • What is this?

    The LibNcFTP FTP Client Library (or simply, "LibNcFTP", or "the library") is used to build custom applications that use the File Transfer Protocol (FTP) to exchange files between machines on a TCP/IP network.  LibNcFTP provides an Application Programming Interface (API) in the "C" language.  LibNcFTP is most often used as a quick way to add FTP functionality to an existing application, but can be used to build custom middleware, or even dedicated FTP client browser programs.

    The rest of the document provides information on how to install and use the library.  This document assumes you have a good working knowledge of how the FTP protocol works; we suggest you read "An Overview of the File Transfer Protocol," which explains how FTP works without getting into too many technical details.

    For more information about LibNcFTP itself, visit the NcFTP Software page at http://www.ncftp.com/libncftp/ .

     

    System Requirements

    LibNcFTP can be built and used on a variety of UNIX platforms, as well as Microsoft Windows and Mac OS X.  The library is provided only in source code form, which means you need a C compiler installed on the machine that LibNcFTP itself is to be installed on.  Most vendors' proprietary C compilers can be used, as well as the GNU "gcc" compiler.  For Microsoft Windows, Visual Studio 6 or later is required.  For Mac OS X, the Developer Tools package must be installed.

    The library itself will only need about 10 megabytes of disk space to hold all the source code, object code, and sample program files.

    Programs using the library can support any of the platforms that the library can be compiled on, which means your programs can run on the same wide variety of UNIX platforms, Microsoft Windows, or Mac OS X.  With a little work, you can write code that can be compiled by all of these platforms.

    Programs using the library will use approximately 100 kB of memory for library variables, internal buffers, etc.  Likewise, your programs' executable will increase in size by approximately 100 kB, but this can vary wildly depending on what type of optimization and debugging compiler options used, as well as which library functions your program uses.

     

    Downloading and Extracting the LibNcFTP Package File

    The library source code is provided in two package formats, a gzipped TAR file (.tar.gz) and ZIP (.zip) file.  Before proceeding, you should check the official download page at http://www.ncftp.com/download/ to see if updated packages are available.

    UNIX users can extract the downloaded package file by typing the following from a shell prompt, where X.Y.Z denotes the library version:

    $ gzip -d -c libncftp-X.Y.Z-src.tar.gz | tar xf -
    $ cd libncftp-X.Y.Z

    The package contains 4 subdirectories: doc, Strn, sio, and libncftp.  The doc directory contains documentation and READMEs.  The Strn and sio directories contain the source code for the Strn string utility library and sio socket utility library respectively.

    Windows users should use their favorite ZIP file utility to extract the libncftp-X.Y.Z-src.zip file, and then skip the following section.

     

    Building and Installing LibNcFTP for UNIX

    From a shell prompt, change to the libncftp subdirectory. There is a script you must run which will checks your system for certain features, so that the library can be compiled on a variety of UNIX systems.  If you've built GNU or other open source software before, you're probably familiar with the "./configure ; make ; make install" sequence.

    First, decide on which C compiler to use, if you have more than one installed.  It's common to have both a vendor's compiler as well as gcc available on the same machine.  You can set the "CC" environment variable to the path to your preferred compiler, for example using the Bourne shell:

    $ CC=/usr/local/gcc/bin/gcc
    $ export CC

    You can also set a CFLAGS environment variable, but you do not need to do this since the configure script will choose the library's recommended set of compiler flags automatically, depending on the C compiler in use and the host operating system.

    You are now ready to run the configuration script:

    $ sh ./configure --prefix=/usr/local

    This will produce several pages of output, and if all goes well, will create all the necessary Makefiles for you.  If you like, you can inspect the Makefiles and config.h files, but you probably won't need to do that.  The "--prefix=/usr/local" option you passed to the configure script tells the script to have the library files (.a files, .so files) set to install in /usr/local/lib and the header files (.h files) set to install in /usr/local/include.  If you don't want to use /usr/local, you can use a different directory using the --prefix option.

    You are now ready to build the libraries and sample programs.  Type:

    $ make

    This should build everything, including the code in the Strn and sio subdirectories, as well as the sample programs in the libncftp subdirectory.  Your build should say "Done" at the end, like this:

    ...
    Compiling simpleget: [OK] 
    -rwx------ 1 gleason gleason  77492 Dec 7 19:41 simpleget
    Done.
    $

    If the build did not succeed, send us e-mail and we should be able to help you build it, since we support almost all of the major UNIX variants.

    Finally, copy the finished libraries and headers to a permanent location.  The easiest way to do that is to do this while logged in as root:

    $ make install

    That will copy the libraries into /usr/local/lib, and the header files into /usr/local/include.  You can also "make install" while not logged in as root, but you'll need to make sure that /usr/local/lib and /usr/local/include have been created and that the user you are logged in as has write permission to those directories.

    You are now ready to build your own programs using LibNcFTP.  You will need to ensure that your compiler can find the library files and header files.  You may need to add "-I/usr/local/include" when compiling and "-L/usr/local/lib" when linking, if your compiler does not search those directories by default.  When linking, your programs will need to link with all three of the installed libraries; an easy way to do that is to add the flags "-lncftp -lsio -lStrn" when linking.

     

    Building and Installing LibNcFTP for Windows

    You will need Visual C++ 6.0 or greater to build the library and sample programs. This version includes two supplementary libraries which you must build and link with your applications: a string utility library (Strn) and a Winsock utility library (sio).

    Keep the source hierarchy intact, so that the samples and libraries build without problems. Each project directory contains a visual studio project (.dsp and .dsw files), but you don't need to manually build each project. Instead, open the "LibNcFTP_ALL.dsw" workspace file in the libncftp directory, and then "Rebuild All" to build all the libraries and all the samples.

    To build your own applications using LibNcFTP, you'll need to make sure you configure your project to find the header files and library files.

    Your application may not need to use the sio or Strn libraries directly, but you still need to link with them. For example, the "simpleget" sample uses "..\..\Debug,..\..\..\Strn\Debug,..\..\..\sio\Debug" in the project option for additional library paths for the linker, and "kernel32.lib user32.lib gdi32.lib advapi32.lib shell32.lib ws2_32.lib Strn.lib sio.lib libncftp.lib" for the list of libraries to link with. Note that LibNcFTP uses advapi32.lib and shell32.lib, in addition to the usual "kernel32.lib user32.lib gdi32.lib". Of course, it also needs to link with Winsock (ws2_32.lib).

    Similarly, you'll need to make sure one of your additional include directories points to the LibNcFTP directory containing ncftp.h. The "simpleget" sample uses "..\.." since it is in a subdirectory of the library itself. If you actually use functions from Strn or sio (as some of the samples do), you'll need to have your project look in their directories for their headers as well.

    About Winsock2: This version of the library was designed for use with Winsock version 2. Note that older versions of Windows 95 do not include Winsock version 2, but can be upgraded by getting the updater from Microsoft.

    However, the library should also work with Winsock 1.1. That is left as an exercise to the coder to change the Winsock initialization to use 1.1 and to link with the 1.1 library (wsock32.lib).


    Tutorial

    First, you'll need to include the library header, ncftp.h.  This header file includes ncftp_errno.h, which is the library's equivalent to <errno.h>.  It also includes many system headers, including <stdio.h>, <time.h>, <sys/types.h>, etc..  Therefore, for our simple example, we only need to:
    #include <ncftp.h>

    The library avoids using global variables. However, you give each library routine a pointer to a structure with the library's state data. This state data must be maintained between calls, and you must pass a pointer to the same structure for each library function. For simplicity, this example puts the two library structures you pass around in two global variables:

        FTPLibraryInfo li;
        FTPConnectionInfo ci;
    Somewhere early in your program, before you try any FTP, you must initialize the library. Do that like this:
        FTPInitLibrary(&li);
    Then, when you want to open a host, you initialize a session structure. Do that by calling FTPInitConnectionInfo() to initialize to the default values, and then you set the fields that define the session:
        FTPInitConnectionInfo(&li, &ci, kDefaultFTPBufSize);
    For this example, we will try a non-anonymous login to a server named ftp.cs.unl.edu with a username of mgleason and a password of au29X-b7.
        ci.debugLog = myDebugFile;
        strcpy(ci.user, "mgleason");
        strcpy(ci.pass, "au29X-b7");
        strcpy(ci.host, "ftp.cs.unl.edu");
    I recommend that you use the optional debugLog parameter while you are testing. That is a FILE * pointer, and you are responsible for opening and closing it. You may want to use just stdout.

    Then open the host:

        if ((result = FTPOpenHost(&ci)) < 0) {
            fprintf(stderr, "Cannot open host: %s.\n", FTPStrError(result));
            exit(1);
        }
    Now try some downloads.  Note that our example no longer checks the return code (result) from the library functions, but you should check for errors by checking if the return code is negative.
        FTPChdir(&ci, "/pub/foobar/downloads/April2001");		/* remote directory */
        globPattern = "log.????2000";
        dstDir = "/usr/log/junk";			/* local directory */
        result = FTPGetFiles3(&ci, globPattern, dstDir, kRecursiveNo, kGlobYes,
    			kTypeBinary, kResumeNo, kAppendNo, kDeleteNo,
    			kTarNo, kNoFTPConfirmResumeDownloadProc, 0
    			);
    Make a few directories, setting the recursive parameter to kRecursiveYes to simulate "mkdir -p":
        newDir = "/pub/foobar/uploads/May2001";	/* remote directory */
        result = FTPMkdir(&ci, newDir, kRecursiveYes);
    Upload some files into the directory we just created:
        result = FTPPutFiles3(&ci, "/usr/logs/*.05??2001", newDir,
                        kRecursiveNo, kGlobYes, kTypeBinary, kAppendNo,
                        NULL, NULL, kResumeNo, kDeleteNo,
                        kNoFTPConfirmResumeUploadProc, 0
                        );
    Finally, close the connection. If your FTPOpenHost succeeded, you should make a good effort to make sure you close it:
        FTPCloseHost(&ci);
    To see a simple example in entirety, see the simpleget.c source file included with the sample code. You should be able to compile that program and run it, to see how things work.  This sample is located in the samples/simpleget subdirectory.


    Library Data Structures

    FTPLibraryInfo

    Each program that uses the library should have one of these structures. This needs to be referenced by all parts of your code that call a FTP library routine, so declare this a global variable or local variable in scope of all subroutines that use FTP.
    typedef struct FTPLibraryInfo {
            char magic[16];
            ...
            char defaultAnonPassword[80];
    } FTPLibraryInfo, *FTPLIPtr;
    The magic field is used internally by the library to make sure the programmer (that would be you :-) isn't using an invalid structure with the library. The library routines check this field against a well-known value to ensure validity.

    The defaultAnonPassword field can be changed after the library has been initialized. You may need to set this manually if the library does not calculate a valid password for use with anonymous logins.

    There are other fields in the structure, but they are used internally and should not be modified or relied upon.

    FTPConnectionInfo

    Each program that uses the library may have one or more of these structures in use at a time. Like the FTPLibraryInfo structure, you should declare these as global variables or local variables that maintain scope throughout use of your FTP operations, because you need to pass a pointer to this structure to each library function.

    This structure is a mixture of mostly internal-use variables and configurable options.  The structure is documented below, but some fields which are used internally have been omitted.  The structure is large, but in practice you'll only use a handful of the fields and let the library use appropriate default values for the others.

    typedef struct FTPConnectionInfo {
    	char magic[16];
    
    	char host[64];
    	char user[64];
    	char pass[64];
    	char acct[64];
    	unsigned int port;
    
    	int errNo;
    	char lastFTPCmdResultStr[128];
    	FTPLineList lastFTPCmdResultLL;
    	int lastFTPCmdResultNum;
    
    	FILE *debugLog;
    	FTPLogProc debugLogProc;
    
    	unsigned int xferTimeout;
    	unsigned int connTimeout;
    	unsigned int ctrlTimeout;
    	unsigned int abortTimeout;
    
    	int maxDials;
    	int redialDelay;
    
    	int dataPortMode;
    
    	int firewallType;
    	char firewallHost[64];
    	char firewallUser[64];
    	char firewallPass[64];
    	unsigned int firewallPort;
    
    	size_t ctrlSocketRBufSize;
    	size_t ctrlSocketSBufSize;
    	size_t dataSocketRBufSize;
    	size_t dataSocketSBufSize;
    
    	const char *asciiFilenameExtensions;
    
    	unsigned short ephemLo;
    	unsigned short ephemHi;
    
    	FTPConnectMessageProc onConnectMsgProc;
    	FTPRedialStatusProc redialStatusProc;
    	FTPPrintResponseProc printResponseProc;
    	FTPLoginMessageProc onLoginMsgProc;
    
    	FTPGetPassphraseProc passphraseProc;
    
    	FTPProgressMeterProc progress;
    	longest_int bytesTransferred;
    	int useProgressMeter;
    	struct timeval t0;
    	double sec;
    	double secLeft;
    	double kBytesPerSec;
    	double percentCompleted;
    	longest_int expectedSize;
    	time_t mdtm;
    	time_t nextProgressUpdate;
    	const char *rname;
    	const char *lname;
    	int stalled;
    	int dataTimedOut;
    	int cancelXfer;
    
    	char actualHost[64];
    	char ip[32];
    
    	char *startingWorkingDirectory;
    	...
    
    	int iUser;
    	void *pUser;
    	longest_int llUser;
    	...
    } FTPConnectionInfo;
    The magic field is used internally by the library to make sure the programmer isn't using an invalid structure with the library. The library routines check this field against a well-known value to ensure validity.  Do not modify this field!

    User authentication fields

    Before connecting, you will always set the host field, which is the hostname or IP address of the remote FTP server to connect to. If the server is running on a non-standard port number (not 21), you may set the port field.

    If you are logging in non-anonymously, you will need to use username and password authentication, which requires that you need to set the user and pass fields (and very rarely, the acct field). Otherwise, you can leave the user field unset to indicate you wish to login anonymously.

    Error detection fields

    The most interesting field is the errNo field. If you an error occurs within a library function, a negative result code is returned and the errNo field is set to the error number, which you can find listed in <ncftp_errno.h>.

    If you get an error, you may also want to inspect the lastFTPCmdResultStr field. This will contain the first line of the most recent reply from the FTP server (i.e. "550 Permission Denied"). Similarly, the lastFTPCmdResultNum contains the numeric FTP protocol result code that came along with it (i.e. 550), and the lastFTPCmdResultLL is the FTPLineList containing the complete reply.

    Logging fields

    For debugging purposes you may want to use the debugLog field which you can set to stdio FILE * pointers. The debugLog writes the whole conversation that took place on the control connection, and would be what you would get had you done an FTP manually.

    Instead of having the library write to files directly, you may wish to use your own logging function. You can use the debugLogProc field to point to a custom logging function. The function should be of this type:

        typedef void (*FTPLogProc)(const FTPCIPtr, char *);
    The first argument is a pointer to the FTPConnectionInfo structure, and the second is the string produced for logging.

    Redialing fields

    The library can "redial" automatically, so if the connection or login attempt failed, it can retry. To do this, you set the maxDials field to 2 or more. The related field redialDelay can be set to the number of seconds to wait between each attempt.

    Timeout fields

    There are several timeout fields you can set if you want certain FTP operations to abort after a period of time. These fields are xferTimeout, connTimeout, ctrlTimeout, and abortTimeout, and each value is in seconds. If you set a timeout field, library functions may return an error code when the timeout occurs.

    The xferTimeout refers to the amount of time to wait on an data transfer read or write; The connTimeout refers to the amount of time to wait before establishing a successful control (FTP conversation) connection; The ctrlTimeout refers to the amount of time to wait for a response from the server on an established control connection; And the abortTimeout refers to a special case of the above when an abort transfer has been requested. (Usually that is used when the user hits ^C and is intending to quit the program as soon as possible, so the time is much less than a regular control connection response.)

    You would use the connTimeout field when you are first trying to connect to a remote FTP server and want to place a reasonable time limit for the connection and login procedure.

    Once you have established a session, you could use the ctrlTimeout to prevent a particular command from blocking forever, since the server may become unresponsive (or time you out!) at any time after the session has been established.

    Once a file transfer connection has been established, the xferTimeout is used to prevent uploads or downloads from stalling forever.  Because of network congestion, it is not uncommon for FTP data transfers to timeout despite having previously transferred hundreds of kilobytes of data.  Note that the connTimeout is used when first trying to establish the data connection, but once the data transfer has started the xferTimeout is used.  The reason for this is that it is usually best to have a relatively short timeout to see if you can connect to the remote host, and then to use a longer timeout for the actual data blocks so that the sending and receiving hosts have extra leeway to survive transient network congestion.

    The abortTimeout is used in one special case.  When a client wants to abort a transfer, the client is supposed to send an abort request (ABOR).  This timeout is used to place a timeout on the response to the abort request; typically this is very short (3 seconds) as a courtesy to the server before assuming the server can't abort the transfer (which happens frequently).  The alternative is to just close the data connection, but the protocol specification implies that is not good behavior.  The abortTimeout is available so that the ctrlTimeout would not need to be used for this, since the ctrlTimeout is typically much longer.  You don't want to wait around for an abort request to finish when you will most likely be closing the session anyway.

    Data connection type fields ("PASV" or "PORT")

    The dataPortMode may be set to one of kSendPortMode, kPassiveMode, or kFallBackToSendPortMode. If you want to try passive FTP, you can use kPassiveMode. You can also use kFallBackToSendPortMode which means the library will try passive FTP first, and if the server does not support it, it will then try regular port FTP.

    FTP proxy fields

    The library provides a means to work with FTP proxies.  These proxies are traditionally older firewall implementations which do not implement automatic and transparent FTP with network address translation.  Hopefully it will not be necessary to use this feature, but if your network administrator says you have to "login to the firewall" in order to use FTP, you will need to use these fields and get the necessary details on how your firewall is accessed.

    Note that the library does not support HTTP proxies which handle FTP!  You would need to connect to the proxy server using the HTTP protocol and issue a "GET" request with a FTP URL, and LibNcFTP does not support HTTP.  Examples of proxy servers which are not supported: Microsoft Proxy Server, Netscape Proxy Server, and Squid.

    The firewallType field can be set to a value from 0 to 7.  Each of the permutations that the library supports are listed below.

    The firewallHost field must be set to the hostname or IP address string of the firewall machine.  You may need to use an IP address string (i.e. "192.168.200.250" if Domain Name Service is not available.

    The firewallPort field may be set to a TCP port number, if the firewall requires you to connect to the firewall on a port other than the default port (21).

    The firewallUser and firewallPass fields is available if your firewall requires you to login with a username and password (fwuser and fwpass as noted above).

    Socket buffer size fields

    The socket buffers used for the control connection and data connections can be tuned if you want to use something other than the default values (which vary by platform and individual kernel tuning).  Each of the options below can be set to the number of bytes you wish to use for the buffer.  The library will then attempt to set the socket option SO_SNDBUF or SO_RCVBUF as needed.

    If the data connection socket buffers are set by you, the library will also attempt to negotiate TCP Large Window support with the server by using SITE commands (SITE RETRBUFSIZE, RBUFSIZ, RBUFSZ, BUFSIZE, STORBUFSIZE, SBUFSIZ, SBUFSZ).  This could also significantly increase performance depending on the type of links (i.e. satellite) between client and server.  The downside to this is that not many FTP servers support the needed SITE commands to enable this feature.

    Note that it is usually not a good idea to set the buffers for the control connection.  The default sizes are already more than large enough, so setting the sizes larger will not increase performance.

    Automatic type selection fields

    Functions such as FTPGetFiles and FTPPutFiles transfer groups of files at a time.  The asciiFilenameExtensions field gives you an opportunity to automatically have files in these groups which end in certain extensions be transferred in ASCII rather than binary.  This field requires a special format, which is best illustrated by an example:

    cip->asciiFilenameExtensions = "|.txt|.asc|.html|.htm|";

    The extensions are delimited by pipe characters ('|') and the string must both begin and end in a pipe character.

    Ephemeral port selection fields

    If you set both the ephemLo and ephemHi fields, this range of ports will be used when selecting port numbers to use for active data connections (i.e. what we use with PORT).  For example, you could set ephemLo to 50000 and ephemHi to 60000 to have PORT only use ports between 50000 and 60000.

    Message callback fields

    The library provides a few hooks so that if your program wants to update its user interface with FTP status information it can do so.  The following callbacks are defined:

    typedef void (*FTPConnectMessageProc)(const FTPCIPtr, ResponsePtr);
    typedef void (*FTPLoginMessageProc)(const FTPCIPtr, ResponsePtr);
    typedef void (*FTPRedialStatusProc)(const FTPCIPtr, int mode, int value);
    typedef void (*FTPPrintResponseProc)(const FTPCIPtr, ResponsePtr);

    Some of the callbacks pass you a pointer to a Response structure, which looks like this:

    typedef struct Response {
    	FTPLineList msg;
    	int codeType;
    	int code;
    	int printMode;
    	int eofOkay;
    	int hadEof;
    } Response, *ResponsePtr;

    The only thing you should need to access in the structure is the msg field, which contains a FTPLineList with the text from the FTP server.

    The onConnectMsgProc and onLoginMsgProc callbacks provide a way for you to display the connect and login messages, respectively.  When you FTP to a remote server, the first thing the server does is send the connect message (also known as a welcome message).  The server then expects you to login with a username and password.  Once you have logged in, it sends a login message in reply.  Neither the connect nor the login message are guaranteed to contain anything except a terse FTP reply message, but popular servers often have useful information in one or both.

    The redialStatusProc is called to indicate what the library is doing.  The mode parameter will be either kRedialStatusDialing or kRedialStatusSleeping.  If it is kRedialStatusDialing, then the value parameter will contain how many connection attempts to the server (number of "dials") and after your function is called the library will immediately attempt to connect.  If it is kRedialStatusSleeping, value will be how many seconds the library is delaying until the next attempt.

    The printResponseProc is called whenever the library would be printing a Response structure to the debug log.  It could be used to modify the printMode field of the Response structure to determine if the Response should be printed, but generally you should be filtering that information from a debugLogProc instead.

    Password request callback fields

    When attempting a user (not anonymous) login, you may leave the pass field blank (set to an empty C-string, "") and set passphraseProc to a:

    typedef void (*FTPGetPassphraseProc)(const FTPCIPtr, FTPLineListPtr pwPrompt, char *pass, size_t dsize);

    When the library needs the password, it will call your function to allow you to present a prompt to an end-user.  As parameters your function will receive the message from the remote FTP server (useful for one-time password systems which provide information to the user) in the pwPrompt parameter, a pointer to a C string in pass, and the size of the string in dsize (i.e., do not write more than dsize - 1 bytes to pass).

    Progress meter fields

    The library computes statistics for each data transfer. If you are interested in those, you can inspect the bytesTransferred, sec, and kBytesPerSec fields for the results. Those correspond to the size of the file, how long it took in seconds to transfer, and how fast it was in kilobytes per second.

    You may also want to implement a progress meter, which updates while the transfer is in progress. To do that you set the progress field to a:

        typedef void (*FTPProgressMeterProc)(const FTPCIPtr, int);
    The second argument tells you which state the transfer is in. You will get a kPrInitMsg message before the first block of data transferred, and an kPrEndMsg at the end. In addition, you get a kPrUpdateMsg for each block transferred. The sample source code included with the package shows how to use progress meters.

    During the transfer (and when your progress meter function is called) the following additional fields are valid.

    There is one more important field to mention, the cancelXfer field.  This can be set during a transfer (from within your progress meter function) to non-zero to indicate that the current transfer should be aborted.  Note that aborting a transfer will often cause the remote server to not only stop sending over the data connection, but it may also disconnect your FTP session altogether.

    Besides providing a way for you to abort a transfer in progress, progress meter functions also provide a way for you to have your code do something else while the transfer proceeds.  This can be something related to the transfer, such as drawing a bar graph on screen, or something totally unrelated.

    DNS information fields

    When the library connects to the FTP server you designate with the host field, the library does a DNS lookup and stores the actual DNS hostname for the host in actualHost, and an IP address string in ip.

    Starting directory fields

    One of the first things the library does after successfully logging in to a remote server is determine the current working directory. The startingWorkingDirectory is set as a result. It may be NULL if the login failed.  This is often the root directory if you are performing an anonymous login.  For user logins, it is often the home directory of the user.

    Piggyback fields

    Near the end of the structure the library provides a few fields for private use by the programmer.  A common use for these fields is to set one to contain a pointer to another structure which you want to associate with the library structure without having to use a global variable.

    The iUser is an integer, the pUser is a void pointer, and the llUser is a longest_int field.

    Working with FTPLineLists

    Some of the library routines work with a FTPLineList structure. This is simply a linked-list of dynamically allocated C-strings.
    typedef struct FTPLine *FTPLinePtr;
    typedef struct FTPLine {
            FTPLinePtr prev, next;
            char *line;
    } FTPLine;
    
    typedef struct FTPLineList {
            FTPLinePtr first, last;
            int nLines;
    } FTPLineList, *FTPLineListPtr;
    Here is an example use that shows how to print the contents of a remote wildcard match:
    FTPLineList fileList;
    FTPLinePtr lp;
    int i, err;
    
    err = FTPRemoteGlob(cip, &fileList, "*.c", &fileList, kGlobYes);
    if (err == kNoErr) {
        for (lp = fileList.first, i=0; lp != NULL; lp = lp->next) {
            ++i;
            printf("item #%d: %s\n", i, lp->line);
        }
    }
    
    DisposeLineListContents(&list);

     

    Working with longest_ints

    Some of the library routines work with longest_int variables.  The longest_int is actually a #define to the largest integral type your compiler supports -- you can see this by browsing the <ncftp.h> header file.  When you built LibNcFTP from the source code, the configure script ran a test and modified your copy of the ncftp.h header file to reflect the correct definition of longest_int.

    Hopefully your longest_int will be at least a 64-bit value, such as "long long", otherwise it will fallback to being a 32-bit value, "long".  If longest_int is only 32-bit, then Large File Support will be unavailable on your system which will effect your capability to work with files larger than 2 gigabytes.

    Use longest_int and longest_uint variables just as you would with int or unsigned int variables.  Be cognizant when casting to and from longest_int variables, since this could result in truncation similar to the effect of miscasting shorts and longs.  In effect, you can do:

    int x = 1234567;
    longest_int y;
    
    y = (longest_int) x;

    But you cannot do:

    int x;
    longest_int y = 990000000003LL;
    
    x = (int) y;

    You also must take care when passing longest_ints to the C library functions in the printf() and scanf() family.  You will need to know your operating system's method of printing and scanning 64-bit values.  Usually for UNIX systems you use "%lld", and on Microsoft Windows you use "%I64d".  If the manual pages for printf() and scanf() aren't helpful, you can look at the libncftp/config.h file in your extracted and configured LibNcFTP source code -- look for PRINTF_LONG_LONG and SCANF_LONG_LONG.

     

    FtwInfo

    The "file tree walk" (Ftw) family of functions work with a FtwInfo structure, which looks like this:
    typedef struct FtwInfo {
            unsigned int init;
            FtwProc proc;
            char *curPath;
            size_t curPathLen;
            size_t curPathAllocSize;
            size_t startPathLen;
            char *curFile;
            size_t curFileLen;
            int curType;
            struct Stat curStat;
            int noAutoMallocAndFree;
            int dirSeparator;
            char rootDir[4];
            int autoGrow;
            size_t depth;
            size_t maxDepth;
            size_t numDirs;
            size_t numFiles;
            size_t numLinks;
            int reserved;
            void *cip;
            void *userdata;
    } FtwInfo;

    Do not use the init, proc, curPathAllocSize, noAutoMallocAndFree, dirSeparator, rootDir, or reserved fields.

    The proc field is of type FtwProc, which is:

    typedef int (*FtwProc)(const FtwInfoPtr ftwip);

    Your FtwProc is called during Ftw functions, and it should return (-1) if they should abort directory traversal, or 0 if they should continue to iterate through the directory tree.

    You may use the following fields only from within your FtwProc callback function:

    The curPath field is a C-string which contains the complete or relative pathname of the file or directory.  Important: treat this field as read-only!

    curPathLen is the length of the string (i.e. strlen(curPath)).

    startPathLen is the length of the starting directory. An easy way to compute pathnames relative to the starting directory is to simply use curPath + startPathLen + 1.

    curFile is the filename-only portion of the pathname (also known as the basename of the path).  Note that curFile is really an offset into curPath (so do not modify curFile).

    curFileLen is the length of curFile.

    curType will be set to 'd' if the pathname is a directory, '-' for a regular file, or 'l' for a symbolic link.  This corresponds to the first character in the lines from UNIX "ls -l".

    curStat contains useful information such as the modification timestamp, file size, etc.

    depth is the current subdirectory recursion level, i.e., how deep into the directory tree you are.  You may want to monitor the depth from your FtwProc and have your function return an error if the depth gets too deep.

    cip is a pointer to your FTPConnectionInfo structure, but only if your FtwProc was called by FTPFtw.

    userdata is a variable given to you for private use, so you can set it to whatever you like.  This can be useful if you need to access other parts of your program from within your FtwProc without having to declare a global variable.

    The following fields are kept as statistics and may be monitored from within your FtwProc callback function, and also after you call Ftw or FTPFtw.  You will need to cast it to a FTPCIPtr before using it.

    maxDepth is the deepest level reached into the directory tree.

    numDirs is the number of items that were directories.

    numFiles is the number of items that were regular files.

    numLinks is the number of items that were symbolic links.


    Function reference


    FTP functions

    FTPAbortDataTransfer

    FTPChdir

    FTPChdirAndGetCWD

    FTPChdir3

    FTPChdirList

    FTPChmod

    FTPCloseHost

    FTPCmd

    FTPDecodeURL

    FTPDelete

    FTPFileExists

    FTPFileModificationTime

    FTPFileSize

    FTPFileSizeAndModificationTime

    FTPFileType

    int FTPFileType(const FTPCIPtr cip, const char *const file, int *const ftype);

    This function can be used to determine if a particular pathname is a directory or regular file.  It returns 'd' in the ftype parameter if the item was a directory, or '-' if it was a regular file.  The ftype parameter return result should only be used if the function returns 0 (kNoErr).  If the item exists but the type could not be determined, the error code kErrFileExistsButCannotDetermineType is returned.  Other types of errors are returned as negative error codes.

    This function calls FTPFileExists, which may be an expensive call.

    Example:

        FTPConnectionInfo ci;
        int ftype, result;
    
        if ((result = FTPFileType(&ci, "/pub/linux", &ftype)) == kNoErr) {
            if (ftype == 'd')
                printf("It was a directory.\n");
            else
                printf("It was a file.\n");
        } else {
                printf("An error occurred (%d).\n", result);
        }

    FTPFtw

    FTPGetCWD

    FTPGetFiles, FTPGetFiles2

    FTPGetFiles3

    FTPGetOneFile, FTPGetOneFile2

    FTPGetOneFile3

    FTPInitConnectionInfo

    FTPInitLibrary

    FTPIsDir

    FTPIsRegularFile

    FTPList

    FTPListToMemory

    FTPListToMemory2

    FTPLocalGlob

    FTPLoginHost

    FTPMkdir

    FTPOpenHost

    FTPOpenHostNoLogin

    FTPPerror

    FTPPutFiles, FTPPutFiles2

    FTPPutFiles3

    FTPPutOneFile, FTPPutOneFile2

    FTPPutOneFile3

    FTPRemoteGlob

    FTPRename

    FTPRmdir

    FTPShutdownHost

    FTPStrError

    FTPStrError2

    FTPSymlink

    FTPUmask

    FTPUtime


    FTPLineList functions

    CopyLineList

    DisposeLineListContents

    InitLineList

    RemoveLine

    AddLine


    Ftw functions

    FtwInit

    FtwDispose

    Ftw

      int Ftw(FtwInfo *const ftwip, const char *const dir, FtwProc proc);

      This can be used like the C library function ftw(), if your C library has one.  "Ftw" stands for "file tree walk" and like the ftw() function, this function provides you an opportunity to recurse through an entire directory on the local machine and process each file within it.  Our implementation has the following features:

      The dir parameter specifies the remote directory tree to walk.

      The proc parameter is a callback to a custom function which you provide.  This function will be called by Ftw for file or directory in the tree.  A proc is of the FtwProc type, which is:

      typedef int (*FtwProc)(const FtwInfoPtr ftwip);

      When your FtwProc is called by Ftw it should inspect the FtwInfo structure, and return (-1) if directory traversal should stop, or return 0 if the traversal should continue.

      Example:  Traverse "/home/joeuser" on the local machine, and attempt to remove any "core" files found.  This is an abridged example, but the library includes a more detailed example, ncftpftw.c, in the samples directory (which also can use FTPFtw to do remote Ftw traversals on remote FTP servers).

      static int
      MyLocalFtwProc(const FtwInfoPtr ftwip)
      {
      	longest_int fSize;
      	time_t fTime;
      
      	if (ftwip->curType == 'd') {
      		printf("Directory: %s\n", ftwip->curPath);
      	} else if (ftwip->curType == 'l') {
      		printf("Symlink: %s\n", ftwip->curPath);
      	} else {
      		fSize = ftwip->curStat.st_size;
      		fTime = ftwip->curStat.st_mtime;
      		printf("File: %s (size=%lld, mtime=%u)\n",
      			ftwip->curPath, fSize, fTime);
      
      		if (strcmp(ftwip->curFile, "core") == 0)
      			(void) unlinkk(ftwip->curPath);
      	}
      
      	return (0);	/* continue traversal */
      }	/* MyFtwProc */
      
      /* ... */
      {
      	FtwInfo ftwi;
      	int rc;
      
      	FtwInit(&ftwi);
      	if ((rc = Ftw(&ftwi, "/home/joeuser", MyLocalFtwProc)) != 0) {
      		/* Traversal failed */
      		/* ... */
      	}
      
      	(void) printf("Stats: rc=%d #dirs=%u #files=%u #links=%u maxdepth=%u\n",
      		rc, ftwi.numDirs, ftwi.numFiles, ftwi.numLinks, ftwi.maxDepth);
      
      	FtwDispose(&ftwi);
      }

    FtwSetBuf


    Questions & Answers

    1. How do I get the library to use passive FTP?
     
    The dataPortMode field of your FTPConnectionInfo structure may be set to one of kSendPortMode, kPassiveMode, or kFallBackToSendPortMode. If you want to try passive FTP, you can use kPassiveMode. You can also use kFallBackToSendPortMode which means the library will try passive FTP first, and if the server does not support it, it will then try regular port FTP.
     
     
    2. How can I do non-blocking FTP library calls?
     
    Technically, you can't. The library isn't coded for non-blocking I/O and it would be ugly it was. But you can have an operation timeout after a number of seconds so you don't hang forever on a slow or unresponsive server.

    To do that, the xferTimeout field of your FTPConnectionInfo structure can be set to a positive integer and the library function will return an error when the timer expires.

     
     
    3. How do I debug an application using the library?
     
    The easiest is to take advantage of the debugLog field in your FTPConnectionInfo structure (i.e. set it to stdout), then look at the log. You should be able to see the whole FTP conversation, and what errors the remote server reported, if any. You can then repeat that same conversation using an FTP client to investigate possible problems with the server you are communicating with.
     
     
    4. Does the library support SFTP/SSH/SSL?
     
    No, sorry!
     
     
    5. Is the library thread safe?
     
    Currently our test suite nor any of the sample programs use multi-threading on platforms that support it.  Therefore, we can't say we've explicitly tested it in a multi-threading environment.  However, some daring customers are using the library in multi-threaded applications with apparent success.

    The general guidelines would be:

    • Only one thread should own and use a FTPConnectionInfo structure (and thus a FTP session).  If you can't adhere to that, you absolutely must not allow multiple threads to call library functions using the same FTPConnectionInfo structure at the same time.

    • The library relies upon standard C library functions such as malloc() and snprintf(), and will try to use reentrant versions of C library functions (such as strtok_r) if they are available.  When you compile the library, you must compile it with any necessary compiler options that enable multi-threading and reentrancy (for example, -D_REENTRANT).  For example, thread-safe versions of C library functions such as malloc() and snprintf() must be used!

    • Library functions must be allowed to finish.  Signal handlers, longjmp()ing, or killing threads which are in the middle of library functions may leave the library in an unknown state.

    • You are responsible for ensuring that only one thread is accessing a particular local file at any given time.

     
    6. How can I contact the library maintainers?
     
    For technical support, send us mail.
     
     
    7. Is SOCKS supported?
     
    Not officially, since we do not use SOCKS, but you can try it.  When the library is built, the configure script must be told to look for SOCKS, as in ./configure --enable-socks5.