LibNcFTP: FTP library for UNIX

  • Installing the library
  • Compiling with the library
  • Tutorial
  • Library Data Structures
  • Function reference
  • Questions & Answers

  • Installing the library

    If you have the binary distribution, the package will include some library files (libncftp.a and libStrn.a) and some header files (ncftp.h, ncftp_errno.h, and Strn.h).

    Copy the library files to your system's library directory, most likely /usr/lib, and the header files to the header directory, most likely /usr/include. You may of course install those in other directories as long as you know how to tell your compiler how to find them.

    If you have the source distribution, you will need to compile the libraries before you can install them.

    First, unpack the archive. If the package arrived as a '.tar.gz' or '.tgz' file, you need to run gunzip, and then tar to extract the source files. You can do that in one shot by doing "gunzip -c libncftp.tgz | tar xvf -". If the package you have is a '.tar' file you can use "tar xvf libncftp.tar". If the package you have is a '.zip' file you can use "unzip libncftp.zip" or "pkunzip libncftp.zip".

    Now go to the "libncftp" directory you just made. 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. Run this script by typing "./Configure" in that directory. After that, you can look at the Makefile it made if you like, and then you run "make" to create the "libncftp.a" and "libStrn.a" library files.

    Finally, install the libraries and headers. You can manually copy the files as stated above for the binary distribution, or you can run "make install" to copy the files for you. If you choose to "make install" you may want to edit the Makefile if you do not want to install to the /usr/local tree.


    Compiling with the library

    After compiling both the LibNcFTP library and its support library, LibStrn, you are ready to build your applications.

    Add "-lncftp -lStrn" to your list of libraries in your Makefile. If you did not install the libraries in the same place as the system libraries (i.e. /usr/lib), you will need to use -L with your C compiler to tell it which directory you put them in.

    Similarly, if you did not install the library headers in /usr/include, you will need to use -I, like -I/usr/local/include. Here is a sample command line compile to illustrate:

    cc -I/usr/local/include minincftp.c -o minincftp -L/usr/local/lib -lncftp -lStrn

    For your applications, you will need to add #include <ncftp.h> to every relevant source file that references a LibNcFTP function.


    Tutorial

    The library uses no 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 fi;
    

    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, &fi, 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-b1.

    	fi.debugLog = myDebugFile;
    	fi.errLog = myErrFile;
    	strcpy(fi.user, "mgleason");
    	strcpy(fi.pass, "au29x-b1");
    	strcpy(fi.host, "ftp.cs.unl.edu");
    

    I recommend that you use the optional debugLog and errLog parameters while you are testing. These are FILE * pointers, and you are responsible for opening and closing them. You may want to use just stdout and stderr.

    Then open the host:

    	if ((result = FTPOpenHost(&fi)) < 0) {
    		fprintf(stderr, "Cannot open host.  Err %d.\n", 
    result);
    		exit(1);
    	}
    

    Now try some downloads:

    	FTPChdir(&fi, "/pub/foobar");
    	globPattern = "log.????96";
    	dstDir = "/usr/log/junk";		/* local directory */
    	result = FTPGetFiles(&fi, globPattern, dstDir, kRecursiveNo, 
    kGlobYes);
    

    Make a few directories, setting the recursive parameter to kRecursiveYes to simulate "mkdir -p":

    	newDir = "/pub/foobar/uploads/May96";
    	FTPMkdir(&fi, newDir, kRecursiveYes);
    

    Upload some files:

    	result = FTPPutFiles(&fi, "/usr/logs/*.05??96", newDir, kRecursiveNo, kGlobNo);
    

    Finally, close the connection. If your FTPOpenHost succeeded, you should make a good effort to make sure you close it:

    	FTPCloseHost(&fi);
    


    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[12];		       /* Don't modify this field. */
    	int init;		       /* Don't modify this field. */
    	int socksInit;		       /* Don't modify this field. */
    	unsigned int defaultPort;      /* Don't modify this field. */
    	char defaultAnonPassword[80];  /* You may set this after init. */
    } 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.

    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.

    typedef struct FTPConnectionInfo {
    	char magic[12];		       /* Don't modify this field. */
    	char host[64];		       /* REQUIRED input parameter. */
    	char user[32];		       /* OPTIONAL input parameter. */
    	char pass[64];		       /* OPTIONAL input parameter. */
    	char acct[32];		       /* OPTIONAL input parameter. */
    	unsigned int port;	       /* OPTIONAL input parameter. */
    	int maxDials;		       /* OPTIONAL input parameter. */
    	int redialDelay;	       /* OPTIONAL input parameter. */
    	int netTimeout;		       /* OPTIONAL input parameter. */
    	int dataPortMode;	       /* OPTIONAL input parameter. */
    	FILE *debugLog;		       /* OPTIONAL input parameter. */
    	FILE *errLog;		       /* OPTIONAL input parameter. */
    	FTPLIPtr lip;		       /* Do not modify this field. */
    	char actualHost[64];	       /* Do not modify this field. */
    	char ip[32];		       /* Do not modify this field. */
    	int connected;		       /* Do not modify this field. */
    	int loggedIn;		       /* Do not modify this field. */
    	int curTransferType;	       /* Do not modify this field. */
    	unsigned long startPoint;      /* Do not modify this field. */
    	int hasPASV;		       /* Do not modify this field. */
    	int hasSIZE;		       /* Do not modify this field. */
    	int hasMDTM;		       /* Do not modify this field. */
    	int hasNLST_d;		       /* Do not modify this field. */
    	struct sockaddr_in servCtlAddr;		/* Do not modify this field. */
    	struct sockaddr_in servDataAddr;	/* Do not modify this field. */
    	struct sockaddr_in ourCtlAddr; /* Do not modify this field. */
    	struct sockaddr_in ourDataAddr;		/* Do not modify this field. */
    	char *buf;		       /* Do not modify this field. */
    	size_t bufSize;		       /* Do not modify this field. */
    	FILE *cin;		       /* Do not use or modify. */
    	FILE *cout;		       /* Do not use or modify. */
    	int netMode;		       /* Do not use or modify. */
    	int dataSocket;		       /* You may use but not modify/close. */
    	int errno;		       /* You may modify this if you want. */
    	char lastFTPCmdResultStr[128]; /* You may modify this if you want. */
    	int lastFTPCmdResultNum;       /* You may modify this if you want. */
    	struct timeval t0;	       /* Do not modify this field. */
    	long bytesTransferred;	       /* Do not modify this field. */
    	double sec;		       /* Do not modify this field. */
    	double kBytesPerSec;	       /* Do not modify this field. */
    } FTPConnectionInfo, *FTPCIPtr;
    

    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 reply from the FTP server. Similarly, the lastFTPCmdResultNum contains the numeric code that came along with it.

    For debugging purposes you may want to use the debugLog and errLog fields, 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. The errLog file gets written to whenever an error occurs, so this should be a lot smaller than the debugLog.

    The netTimeout field can be set if you wish blocking FTP operations to abort after a period of time. If you set that, you also need to have a signal handler for SIGALRM, because alarm is used for this.

    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.

    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.

    Working with LineLists

    Some of the library routines work with a LineList structure. This is simply a linked-list of dynamically allocated C-strings.

    typedef struct Line *LinePtr;
    typedef struct Line {
    	LinePtr prev, next;
    	char *line;
    } Line;
    
    typedef struct LineList {
    	LinePtr first, last;
    	int nLines;
    } LineList, *LineListPtr;
    

    Here is an example use that shows how to print the contents of a remote wildcard match:

    	LineList fileList;
    	LinePtr 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);
    


    Function reference


    FTP functions


    FTPChdir

    int FTPChdir(const FTPCIPtr cip, const char *newCwd);

    Changes the current remote working directory to the path specified by newCwd The path is system-dependent, so it need not be a unix-style path if the system you are connected to is not a unix system.

    If the change succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    When you first connect, the default working directory is assumed to be the root directory.

    FTPChmod

    int FTPChmod(const FTPCIPtr cip, const char *pattern, const char *mode, int doGlob);

    This is equivalent of the UNIX /bin/chmod program, only for remote files. This is not in the FTP standard, but many UNIX hosts implement this as a site-specific command.

    If the change succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPCloseHost

    int FTPCloseHost(const FTPCIPtr cip);

    Closes the connection to the current host, and disposes the library's data structures associated with it. This function may block because the remote server is notified that we want to close the connection via the FTP protocol command "QUIT" and a reply to that command is read back before cleanup is complete.

    Upon a normal close, 0 is returned, otherwise if something bizarre happened a number less than zero is returned.

    FTPCmd

    int FTPCmd(const FTPCIPtr cip, const char *cmdspec, ...);

    This allows you to issue an FTP command directly on the control connection. You do not get back the textual repsonse string, but you are returned the first digit of the numeric response, which will be from 1 to 5.

    The function behaves like printf, so you can pass a variable number of parameters.

    Example:

    	idleAmt = 300;
    	err = FTPCmd(cip, "SITE IDLE %d", idleAmt);
    	if (err == 2) {
    		/* success */
    	}
    

    FTPDelete

    int FTPDelete(const FTPCIPtr cip, const char *pattern, int recurse, int doGlob);

    Removes files on the remote system, like /bin/rm does locally. The doGlob parameter must be set to either kGlobYes or kGlobNo. The recurse parameter must be set to either kRecursiveYes or kRecursiveNo.

    If you set the doGlob parameter to kGlobYes, the pattern is considered a shell-wildcard-style regular expression, and FTPRemoteGlob is used to build a list of files to delete.

    Example 1: Delete all files in the current directory whose names end in '.zip'.

    	err = FTPDelete(cip, "*.zip", kRecursiveNo, kGlobYes);
    

    Example 2: Delete one file whose name is *README*, but not files named README nor *README-NOW*.

    	err = FTPDelete(cip, "*README*", kRecursiveNo, kGlobNo);
    

    Note: The recurse parameter will be implemented in a future release. When it is, it will be equivalent to "/bin/rm -rf" on UNIX.

    If all deletions succeeded, 0 is returned, otherwise a number less than zero is returned if one or more deletions failed. All files matched are attempted to be deleted, so if one deletion fails, that does not cause the remaining list to be aborted.

    FTPFileModificationTime

    int FTPFileModificationTime(const FTPCIPtr cip, const char *file, time_t *mdtm);

    This tries to determine the last modification timestamp of the remote file specified by file.

    This may or may not work, because this relies upon the implementation of the "MDTM" low-level FTP command. There are still a lot of traditional servers out there that do not support it nor the "SIZE" command.

    There may also be a question of whether the time returned is in local time or GMT. Unfortunately this also varies among servers, most likely because there are no formal specifications of MDTM in RFC-959.

    If the query succeeded, 0 is returned and the mdtm parameter is set to the timestamp of last modification, otherwise a number less than zero is returned upon failure.

    FTPFileSize

    int FTPFileSize(const FTPCIPtr cip, const char *file, long *size, int type);

    This tries to determine how many bytes would be transferred if you downloaded file. The size in bytes is returned in the size parameter. The type parameter exists because this number varies depending on the transfer type. Set it to kTypeBinary, kTypeAscii, or kTypeEbcdic.

    If the query succeeded, 0 is returned and the size parameter is set, otherwise a number less than zero is returned upon failure.

    FTPGetCWD

    int FTPGetCWD(const FTPCIPtr cip, char *newCwd, size_t newCwdSize);

    This writes up to newCwdSize bytes of the pathname of the current remote working directory in newCwd.

    If the request succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPGetFiles

    int FTPGetFiles(const FTPCIPtr cip, const char *pattern, const char *dstdir, int recurse, int doGlob);

    Fetches files from the remote system. The doGlob parameter must be set to either kGlobYes or kGlobNo. The recurse parameter must be set to either kRecursiveYes or kRecursiveNo. The dstdir parameter is local directory where the files are to be written. If you want them placed in the current local directory, just use "." as the dstdir. The files retrieved are named to be the same as they were on the remote system.

    If you set the doGlob parameter to kGlobYes, the pattern is considered a shell-wildcard-style regular expression, and FTPRemoteGlob is used to build a list of files to retrieve.

    Example 1: Retrieve all files in the current directory whose names end in '.zip' and write them to the "/tmp" local directory.

    	err = FTPGetFiles(cip, "*.zip", "/tmp", kRecursiveNo, kGlobYes);
    

    Example 2: Fetch one file whose name is *README*, but not files named README nor *README-NOW*, and write it to the current local directory.

    	err = FTPGetFiles(cip, "*README*", ".", kRecursiveNo, kGlobNo);
    

    Note 1: The recurse parameter will be implemented in a future release. When it is, it will be equivalent to "/bin/cp -pr" on UNIX.

    Note 2: All files are retrieved in binary -- if you retrieve a .TXT file written by an MS-DOS system to a UNIX host, the file will not have been converted to a UNIX text file.

    If all transfers succeeded, 0 is returned, otherwise a number less than zero is returned if one or more transfers failed. All files matched are attempted to be transferred, so if one fails, that does not cause the remaining list to be aborted.

    FTPGetFilesAscii

    int FTPGetFilesAscii(const FTPCIPtr cip, const char *pattern, const char *dstdir, int recurse, int doGlob);

    Behaves just like FTPGetFiles, except the transfer type used is ASCII.

    Note: The files are retrieved in ascii -- if you retrieve a .TXT file written by an MS-DOS system to a UNIX host, the file will be converted to a UNIX text file. Do not use this to unless you are certain the files you want are really text files. Otherwise text conversion is performed which would corrupt a binary file.

    If all transfers succeeded, 0 is returned, otherwise a number less than zero is returned if one or more transfers failed. All files matched are attempted to be transferred, so if one fails, that does not cause the remaining list to be aborted.

    FTPGetOneFile

    int FTPGetOneFile(const FTPCIPtr cip, const char *file, const char *dstfile);

    This is provides a way to get a single remote file and write it under a different name locally.

    Example: Retrieve a file named "/pub/README.TXT" and write it as "/tmp/xx".

    	err = FTPGetOneFile(cip, "/pub/README.TXT", "/tmp/xx");
    

    Note: The file is retrieved in binary -- if you retrieve a .TXT file written by an MS-DOS system to a UNIX host, the file will not have been converted to a UNIX text file.

    If the fetch succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPGetOneFileAscii

    int FTPGetOneFileAscii(const FTPCIPtr cip, const char *file, const char *dstfile);

    This is provides a way to get a single remote file and write it under a different name locally.

    Example: Retrieve a file named "/pub/README.TXT" and write it as "/tmp/xx".

    	err = FTPGetOneFileAscii(cip, "/pub/README.TXT", "/tmp/xx");
    

    Note: The file is retrieved in ascii -- if you retrieve a .TXT file written by an MS-DOS system to a UNIX host, the file will be converted to a UNIX text file. Do not use this to unless you are certain the files you want are really text files. Otherwise text conversion is performed which would corrupt a binary file.

    If the fetch succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPInitConnectionInfo

    int FTPInitConnectionInfo(const FTPLIPtr lip, const FTPCIPtr cip, size_t bufSize);

    Before you can attempt to connect to a host using the FTP protocol, you must have first initialized the library using FTPInitLibrary .

    Then, when you want to open a host, you use this function to initialize a session structure to the default values. After you have done that, you may change whatever non-default values you need.

    Typically you use a global variable to hold the library's session information, and then pass a pointer to it for all FTP functions.

    The bufsize parameter specifies the size of the data transfer I/O buffer to use, which is reserved using malloc(). You should use kDefaultFTPBufSize as the value for bufsize in most cases.

    Here are some default values that may be of interest:

    	cip->port = kDefaultFTPPort;
    	cip->maxDials = 3;
    	cip->redialDelay = 45;
    	cip->netTimeout = 0;
    	cip->dataPortMode = kSendPortMode;
    

    If the initialization succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    Example:

    	FTPLibraryInfo li;
    	FTPConnectionInfo fi;
    
    	if (FTPInitLibrary(&li) != kNoErr) {
    		/* ... init library failed ... */
    	}
    	if (FTPInitConnectionInfo(&li, &fi, kDefaultFTPBufSize) != kNoErr) {
    		/* ... init session failed ... */
    	}
    

    	strcpy(fi.host, "hostname.here.com");
    	strcpy(fi.user, "username");
    	strcpy(fi.pass, "password");
    	/* ... */
    

    FTPInitLibrary

    int FTPInitLibrary(const FTPLIPtr lip);

    Before you can attempt to connect to a host using the FTP protocol, you must have first initialized the library using this function. Typically, you use a global variable to hold the library's internal data structures.

    Example:

    	FTPLibraryInfo li;
    
    	if (FTPInitLibrary(&li) != kNoErr) {
    		/* ... init library failed ... */
    	}
    

    FTPList

    int FTPList(const FTPCIPtr cip, int outfd, int longMode, const char *lsflag);

    This is a simple way to get a remote directory listing dumped to the screen. The outdfd parameter specifies which file descriptor to write to, so you do not necessarily have to use stdout (file descriptor 1) here.

    The longMode parameter determines which method of listing you want. The FTP Protocol currently has two methods, one which is a simple one file per line style (longMode == 0), and another host-specific output method (longMode == 1). Typically for UNIX systems, these methods equate to "/bin/ls -1" and "/bin/ls -la".

    The lsflag parameter can be used to give a specific directory (or file) to list, and also as a way to specify alternate flags. For example, many systems accept UNIX's /bin/ls flags, like "-CF".

    Example 1: Dump a simple listing of the current directory to the screen.

    	err = FTPList(cip, 1, 0, NULL);
    
    Example 2: Dump a long listing of the directory "/pub" to the screen.
    	err = FTPList(cip, 1, 1, "/pub");
    

    Example 3: Simulate /bin/ls -CF behavior on the current remote working directory.

    	err = FTPList(cip, 1, 0, "-CF");
    

    Note: This really isn't too useful for doing programmatical analysis with. If you want to do that, FTPListToMemory is a much better choice.

    If the listing succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPListToMemory

    int FTPListToMemory(const FTPCIPtr cip, const char *pattern, LineListPtr lines, const char *lsflags);

    This allows you to get a directory listing of a remote directory, and have it loaded into a dynamic data structure.

    The pattern parameter specifies a directory to list, or a wildcard expression of files to list. The output is loaded into the LineList specified by the lines parameter. The lsflags parameter lets you specify additional /bin/ls style flags. If you use it, you must have a trailing space, like "-CF " and if you don't want any flags, you must use an empty string, like "".

    Example 1: Get a listing of files in the /pub directory.

    	LineList fileList;
    
    	err = FTPListToMemory(cip, "/pub", &fileList, "");
    
    Example 2: Get a long listing of files in the current directory, sorted by older files first.
    	LineList fileList;
    
    	err = FTPListToMemory(cip, ".", &fileList, "-lrt ");
    

    Note: If all you want is a list of files, it may be easier to just use FTPRemoteGlob . That function calls FTPListToMemory for you.

    If the listing succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPLocalGlob

    int FTPLocalGlob(FTPCIPtr cip, LineListPtr fileList, const char *pattern, int doGlob);

    This gives you a way to do a shell-expansion of a wildcard pattern on the local host. You can use this to gather a list of files, and then do something with the list.

    The pattern parameter specifies a wildcard expression. The pattern may also contain the tilde-notation popularized by /bin/csh. These are expanded by the library, and then /bin/sh is used in conjunction with /bin/ls to produce the list of files for you.

    The doGlob parameter may seem redundant, but if you set it to kGlobNo you can have the function only do the tilde expansion.

    Example: Get a list of all C source files in the current directory.

    	LineList fileList;
    
    	err = FTPLocalGlob(cip, &fileList, "*.c", &fileList, kGlobYes);
    

    If the globbing succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPMkdir

    int FTPMkdir(const FTPCIPtr cip, const char *newDir, int recurse);

    This creates a new directory on the remote host. The recurse parameter specifies whether it should attempt to create all directories in the path and not just the last node (this emulates "/bin/mkdir -p"). The recurse parameter must be set to either kRecursiveYes or kRecursiveNo.

    If the directory creation succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPOpenHost

    int FTPOpenHost(const FTPCIPtr cip);

    This is routine is used to establish the connection to the remote host. Before you can use it, you must set some of the fields in your FTPConnectionInfo structure.

    The host field must be set to the name of the remote host. You may also use an IP Address in place of a hostname.

    The user and pass fields must be set if you are not logging in anonymously. If you leave them unset, an anonymous login is attempted, with the password being a guess at a the email address for the user running your program. There is also an acct field which you may set if for some reason the remote server requires an account designation in addition to a user and password.

    The port parameter may be set to a non-standard port if you wish to connect to an FTP server running on a port other than the default port number, 21.

    The library has a built-in facility to "redial" a host if it could not login in the first time. You may set the maxDials field to a number greater than one to turn that on. If you do that, you may want to tune the time delay between dials by setting the redialDelay field.

    Example 1: Establish an anonymous connection to ftp.cdrom.com.

    	FTPLibraryInfo li;
    	FTPConnectionInfo fi;
    
    	if (FTPInitLibrary(&li) != kNoErr) {
    		/* ... init library failed ... */
    	}
    	if (FTPInitConnectionInfo(&li, &fi, kDefaultFTPBufSize) != kNoErr) {
    		/* ... init session failed ... */
    	}
    
    	strcpy(fi.host, "ftp.cdrom.com");
    	if (FTPOpenHost(&fi) != kNoErr) {
    		/* ... could not open a connection there ... */
    	}
    

    Example 2: Establish an non-anonymous connection to ftp.cs.unl.edu.

    	FTPLibraryInfo li;
    	FTPConnectionInfo fi;
    
    	if (FTPInitLibrary(&li) != kNoErr) {
    		/* ... init library failed ... */
    	}
    	if (FTPInitConnectionInfo(&li, &fi, kDefaultFTPBufSize) != kNoErr) {
    		/* ... init session failed ... */
    	}
    
    	strcpy(fi.host, "ftp.cs.unl.edu");
    	strcpy(fi.user, "gleason");
    	strcpy(fi.pass, "mypassword");
    	fi.maxDials = 2;
    	fi.redialDelay = 120;		/* Wait two minutes between. */
    
    	if (FTPOpenHost(&fi) != kNoErr) {
    		/* ... could not open a connection there ... */
    	}
    

    If the connection was established, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPPutFiles

    int FTPPutFiles(const FTPCIPtr cip, const char *pattern, const char *dstdir, int recurse, int doGlob);

    Writes files to the remote system. The doGlob parameter must be set to either kGlobYes or kGlobNo. The recurse parameter must be set to either kRecursiveYes or kRecursiveNo. The dstdir parameter is remote directory where the files are to be written. If you want them placed in the current remote directory, just use "." as the dstdir. The files written are named to be the same as they were on the local system.

    If you set the doGlob parameter to kGlobYes, the pattern is considered a shell-wildcard-style regular expression, and FTPLocalGlob is used to build a list of files to send.

    Example 1: Send all files in the current directory whose names end in '.zip' and write them to the "/tmp" remote directory.

    	err = FTPPutFiles(cip, "*.zip", "/tmp", kRecursiveNo, kGlobYes);
    

    Example 2: Send one file whose name is *README*, but not files named README nor *README-NOW*, and write it to the current remote directory.

    	err = FTPPutFiles(cip, "*README*", ".", kRecursiveNo, kGlobNo);
    

    Note 1: The recurse parameter will be implemented in a future release. When it is, it will be equivalent to "/bin/cp -r" on UNIX.

    Note 2: All files are sent in binary -- if you send a text file written by a UNIX host to a MS-DOS server, the file will not have been converted to a MS-DOS text file.

    If all transfers succeeded, 0 is returned, otherwise a number less than zero is returned if one or more transfers failed. All files matched are attempted to be transferred, so if one fails, that does not cause the remaining list to be aborted.

    FTPPutFilesAscii

    int FTPPutFilesAscii(const FTPCIPtr cip, const char *pattern, const char *dstdir, int recurse, int doGlob);

    Behaves just like FTPPutFiles, except the transfer type used is ASCII.

    Note: The files are sent as ASCII text. The file will be converted to the remote host's local text file representation by the remote server. Do not use this to transfer any other files except text files. If you send a binary file, the remote server will mangle it.

    If all transfers succeeded, 0 is returned, otherwise a number less than zero is returned if one or more transfers failed. All files matched are attempted to be transferred, so if one fails, that does not cause the remaining list to be aborted.

    FTPPutOneFile

    int FTPPutOneFile(const FTPCIPtr cip, const char *file, const char *dstfile);

    This is provides a way to get a send a local file and write it under a different name on the remote host.

    Example: Send a file named "/tmp/xx" and write it as "/pub/README".

    	err = FTPPutOneFile(cip, "/tmp/xx", "/pub/README");
    

    Note: The file is sent in binary -- if you send a text file written by a UNIX host to a MS-DOS server, the file will not have been converted to a MS-DOS text file.

    If the send succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPPutOneFileAscii

    int FTPPutOneFileAscii(const FTPCIPtr cip, const char *file, const char *dstfile);

    This is provides a way to get a send a local file and write it under a different name on the remote host, using ASCII translation.

    Example: Send a file named "/tmp/xx" and write it as "/pub/README".

    	err = FTPPutOneFileAscii(cip, "/tmp/xx", "/pub/README");
    

    Note: The file is sent in ASCII. The file will be converted to the remote host's local text file representation by the remote server. Do not use this to transfer any other files except text files. If you send a binary file, the remote server will mangle it.

    If the send succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPRemoteGlob

    int FTPRemoteGlob(FTPCIPtr cip, LineListPtr fileList, const char *pattern, int doGlob);

    This gives you a way to do a shell-expansion of a wildcard pattern on the remote host. You can use this to gather a list of files, and then do something with the list.

    The pattern parameter specifies a wildcard expression. The pattern is interpreted in a host-specific manner, but most hosts obey /bin/csh or /bin/sh notation.

    The doGlob parameter is not used, so just set it to kGlobYes.

    Example: Get a list of all C source files in the current remote directory.

    	LineList fileList;
    
    	err = FTPRemoteGlob(cip, &fileList, "*.c", &fileList, kGlobYes);
    

    If the globbing succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPRename

    int FTPRename(const FTPCIPtr cip, const char *oldname, const char *newname);

    Lets you rename a remote file or directory.

    If the renaming succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.

    FTPRmdir

    int FTPRmdir(const FTPCIPtr cip, const char *pattern, int recurse, int doGlob);

    Removes remote directories on the remote system, like /bin/rmdir does locally. Trying to remove a non-empty directory may or may not work, depending on the remote server. It won't on most UNIX servers.

    The doGlob parameter must be set to either kGlobYes or kGlobNo. The recurse parameter must be set to either kRecursiveYes or kRecursiveNo.

    If you set the doGlob parameter to kGlobYes, the pattern is considered a shell-wildcard-style regular expression.

    Example 1: Delete all subdirectories in the current remote directory whose names contain 'tmp'.

    	err = FTPRmdir(cip, "*tmp*", kRecursiveNo, kGlobYes);
    

    Example 2: Delete one remote directory whose name is /tmp.

    	err = FTPDelete(cip, "/tmp", kRecursiveNo, kGlobNo);
    

    Note: The recurse parameter will be implemented in a future release. When it is, it will be equivalent to "/bin/rm -rf" on UNIX.

    If all deletions succeeded, 0 is returned, otherwise a number less than zero is returned if one or more deletions failed. All files matched are attempted to be deleted, so if one deletion fails, that does not cause the remaining list to be aborted.

    FTPShutdownHost

    void FTPShutdownHost(const FTPCIPtr cip);

    Forcibly closes the connection to the current host, and disposes the library's data structures associated with it.

    Unlike FTPCloseHost, This function will not block, but you should use FTPCloseHost whenever possible because it does a close that is more polite to the remote host.

    FTPUmask

    int FTPUmask(const FTPCIPtr cip, const char *umask);

    This is attempts to emulate the umask command that UNIX shells and programs use. This is not in the FTP standard, but many UNIX hosts implement this as a site-specific command.

    Example: Set the umask for future uploads to 022.

    	err = FTPUmask(cip, "022");
    

    If the umask was set, 0 is returned, otherwise a number less than zero is returned upon failure.


    LineList functions


    DisposeLineListContents

    void DisposeLineListContents(LineListPtr list);

    This frees all dynamic memory allocations associated with list.

    Example:

    	LineList list;
    	int e;
    
    	InitLineList(&list);
    	for (e=1; (strerror(e) != NULL) && (e <= 200); e++)
    		if (AddLine(&list, strerror(e)) == NULL)
    			break; 
    
    	/* ... do something with the list you made ... */
    
    	DisposeLineListContents(&list);
    

    InitLineList

    void InitLineList(LineListPtr list);

    Prepares a LineList structure for use. The best way to use these is to simply declare a LineList local variable and then pass a pointer to it. (i.e., you don't need to declare a LineListPtr and then malloc space for it.)

    RemoveLine

    LinePtr RemoveLine(LineListPtr list, LinePtr killMe);

    This unlinks the Line structure and then disposes its contents, and of course re-links the list together.

    Example: Remove the second line of a list.

    	LineList list;
    	LinePtr lp;
    
    	/* ... create the list ... */
    	lp = list.first;
    	lp = lp->next;
    	if (lp != NULL)
    		RemoveLine(lp, &list);
    

    AddLine

    LinePtr AddLine(LineListPtr list, const char *buf);

    This makes a dynamically-allocated copy of buf using malloc and attaches it to the last node of the list.

    This returns a pointer to the allocated Line, or NULL if it could not be allocated.


    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 nonblocking FTP library calls?

    Technically, you can't. The library isn't coded for nonblocking 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 nonresponsive server.

    To do that, the netTimeout field of your FTPConnectionInfo structure can be set to a positive integer. You will also need to have a signal handler for SIGALRM, because alarm is used for this, and when the timer expires you should jump to your signal handler instead of exiting your program.

    If you jump out of a library function, the FTP connection is in an undefined state and cannot be used any further. You should then call FTPShutdownHost to free up system resources before continuing.

    3. How do I debug an application using the library?

    The easiest is to take advantage of the debugLog field in your FTPConnectionInfo structure, 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. How can I contact the library's author?

    Send e-mail to Mike Gleason at

    mgleason@probe.net

    5. Is SOCKS supported?

    Possibly. I built the library from my NcFTP code, which supposedly supports the SOCKS library. I am not sure whether it is implemented correctly because I don't have a SOCKS server to test with.

    When the library is built, the configure script must be told to look for SOCKS, as in ./configure --enable-socks5.