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 "sh ./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.
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 -lStrnFor your applications, you will need to add #include <ncftp.h> to every relevant source file that references a LibNcFTP function.
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:
FTPChdir(&ci, "/pub/foobar"); globPattern = "log.????96"; dstDir = "/usr/log/junk"; /* local directory */ result = FTPGetFiles(&ci, globPattern, dstDir, kRecursiveNo, kGlobYes);Make a few directories, setting the recursive parameter to kRecursiveYes to simulate "mkdir -p":
newDir = "/pub/foobar/uploads/May96"; FTPMkdir(&ci, newDir, kRecursiveYes);Upload some files:
result = FTPPutFiles3(&ci, "/usr/logs/*.05??96", newDir, kRecursiveNo, kGlobYes, kTypeBinary, kAppendNo, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 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.
typedef struct FTPLibraryInfo { char magic[16]; int init; int socksInit; unsigned int defaultPort; char ourHostName[64]; int hresult; int htried; 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.
The other fields are used internally, but may be useful information to you while debugging (such as ourHostName).
This structure is a mixture of mostly internal-use variables and configurable options.
typedef struct FTPConnectionInfo { char magic[16]; char host[64]; char user[64]; char pass[64]; char acct[64]; unsigned int port; unsigned int xferTimeout; unsigned int connTimeout; unsigned int ctrlTimeout; unsigned int abortTimeout; FILE *debugLog; FILE *errLog; FTPLogProc debugLogProc; FTPLogProc errLogProc; FTPLIPtr lip; int maxDials; int redialDelay; int dataPortMode; char actualHost[64]; char ip[32]; int connected; int loggedIn; int curTransferType; char *startingWorkingDirectory; longest_int startPoint; int hasPASV; int hasSIZE; int hasMDTM; int hasREST; int hasNLST_d; int hasUTIME; int hasFEAT; int hasMLSD; int hasMLST; int usedMLS; int hasCLNT; int mlsFeatures; int STATfileParamWorks; int NLSTfileParamWorks; struct sockaddr_in servCtlAddr; struct sockaddr_in servDataAddr; struct sockaddr_in ourCtlAddr; struct sockaddr_in ourDataAddr; int netMode; char *buf; size_t bufSize; FILE *cin; FILE *cout; int ctrlSocketR; int ctrlSocketW; int dataSocket; int errNo; unsigned short ephemLo; unsigned short ephemHi; int cancelXfer; longest_int bytesTransferred; FTPProgressMeterProc progress; int useProgressMeter; int leavePass; double sec; double secLeft; double kBytesPerSec; double percentCompleted; longest_int expectedSize; time_t mdtm; time_t nextProgressUpdate; const char *rname; const char *lname; struct timeval t0; int stalled; int eofOkay; char lastFTPCmdResultStr[128]; LineList lastFTPCmdResultLL; int lastFTPCmdResultNum; char firewallHost[64]; char firewallUser[64]; char firewallPass[64]; unsigned int firewallPort; int firewallType; int require20; int usingTAR; FTPConnectMessageProc onConnectMsgProc; FTPRedialStatusProc redialStatusProc; FTPPrintResponseProc printResponseProc; FTPLoginMessageProc onLoginMsgProc; size_t ctrlSocketRBufSize; size_t ctrlSocketSBufSize; size_t dataSocketRBufSize; size_t dataSocketSBufSize; int serverType; int ietfCompatLevel; int reserved[32]; } FTPConnectionInfo;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 you are logging in non-anonymously, you will also need to set the user and pass fields (and very rarely, the acct field). If the server is running on a non-standard port number (not 21), you may set the port field.
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.
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. Similarly, the lastFTPCmdResultNum contains the numeric code that came along with it, and the lastFTPCmdResultLL is the LineList containing the complete reply.
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.
Instead of having the library write to files directly, you may wish to use your own logging function. You can use the errLogProc and debugLogProc fields to point to your custom logging functions. 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.
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, you also need to have a signal handler for SIGALRM, because alarm is used for this, so when a timeout expires, a SIGALRM signal is generated.
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.)
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.
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.
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 can pass a pointer to a function of type:
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 kPrUpdateMsgfor each block transferred. The sample source code included with the package shows how to use progress meters.
Here is an example use that shows how to print the contents of a remote wildcard match:typedef struct Line *LinePtr; typedef struct Line { LinePtr prev, next; char *line; } Line; typedef struct LineList { LinePtr first, last; int nLines; } LineList, *LineListPtr;
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);
If a data transfer is in progress, you can attempt to abort the remaining data. This routine can be useful if you want to setup a signal handler so that a user could interactively cancel a transfer. Many FTP servers will also log you out when you do this, so don't be surprised if the next command you try after aborting a transfer fails.
Changes the current remote working directory to the path specified by cdCwd. 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 for anonymous FTP access. For a user login, the default is often the home directory of the user.
This is a combination of FTPChdir and FTPGetCWD. The reason this can be useful is that some servers return the new path after a change of directory, thus saving a PWD.
If the change succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.
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 mode change succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.
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.
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, or a negative error code.
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 */ }
The purpose of this function is to parse a RFC 1738 FTP URL, such as ftp://ftp.ncftp.com/pub/ncftp/README. The URL is of the form ftp://<user>:<password>@<host>:<port>/<url-path><;type>.
Upon return, it will return a LineList, with each line representing one directory level. This is required by RFC 1738, so that instead of doing a chdir /pub/ncftp, you need to do a chdir pub followed by a chdir ncftp.
It returns an integer indicating if the URL was successfully parsed. Zero means success, while the error code kMalformedURL means that it appeared to be a URL, but it had syntax errors. The error code kNotURL means that it didn't even resemble a URL at all.
The fn parameter points to a string to hold the filename for the requested file. For the example above, it would be set to "README". For a directory URL, this will be set to an empty string. The fnsize parameter should be the maximum size of the fn buffer.
Besides giving you back a chdir list, it may also set the user, pass, and port field of the connection structure, if the URL contains those fields, and optionally the transfer type or whether the URL should be a directory listing.
If the xtype parameter is not NULL, it will write whether the transfer type was specified, as either kTypeAscii or kTypeBinary. If the wantnlst parameter is not NULL, it will write whether the user requested a listing instead of a download of that directory.
The url is modified by the function, so make a copy of it if you need to preserve the contents.
Example:
rc = FTPDecodeURL(&ci, url, &cdlist, urlfile, sizeof(urlfile), &xtype, NULL); if (rc == kMalformedURL) { fprintf(stderr, "Malformed URL: %s\n", url); } else if (rc == kNotURL) { fprintf(stderr, "Not a URL: %s\n", url); } else { /* URL okay */ printf("open %s %u\n", ci.host, (unsigned int) ci.port); printf("user %s\n", ci.user); if (ci.pass[0] != '\0') printf("pass %s\n", ci.pass); for (lp = cdlist.first; lp != NULL; lp = lp->next) printf("cd %s\n", lp->line); printf("type %c\n", xtype); if (urlfile[0] != '\0') printf("get %s\n", urlfile); }
Removes remote files on the remote system, like /bin/rmdir does locally. This can also delete entire directories, if recursion is specified.
The doGlob parameter must be set to either kGlobYes or kGlobNo. When set, the pattern is considered a shell-wildcard-style regular expression.
The recurse parameter must be set to either kRecursiveYes or kRecursiveNo. When set, the library attempts to remove all files and subdirectories also (i.e. like /bin/rm -rf on UNIX).
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);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.
This tries to determine if a file or directory specified in the file parameter exists on the remote server.
Unfortunately this can be a very expensive operation on older servers because there was no standard functionality available in the protocol specificiation. On these servers, the library tries a variety of methods until it succeeds or exhausts the list. The good news is that once the library has found a reliable way that works on the remote server, it remembers this for subsequent iterations so it does not need to repeat the learning process.
If the item exists, 0 (kNoErr) is returned. If the server has a reliable way to determine file existence and the file did not exist, kErrNoSuchFileOrDirectory is returned. Otherwise, the result is inconlcusive and a negative error code is returned.
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.
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.
This function is a hybrid of FTPFileSize and FTPFileModificationTime. Some newer FTP servers have the capability to return both of these values at the same time, so it is more efficient than doing them individuallly. 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.
The mdtm parameter is set to the time of last modification for the file or directory specified.
If the query succeeded, 0 is returned and the size and mdtm parameters are set, otherwise a number less than zero is returned if both could not be determined.
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); }
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.
int FTPGetFiles2(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob, const int xtype, const int resumeflag, const int appendflag);
These functions have been superceded by FTPGetFiles3 and are only provided for backward-compatibility with code based off of older versions of the library.
Downloads (reads) files from the remote system.
The pattern parameter is the remote pathname to download. When coupled with globbing, the pattern can denote a regular expression so that multiple files can be downloaded with a single wildcard expression.
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.
The recurse parameter must be set to either kRecursiveYes or kRecursiveNo. When set the library attempts to download the entire directory structure if pattern is a directory. Recursion is not very portable; For it to work properly, the remote server must produce UNIX-like listings for the LIST primitive, and it must also support the "-R" flag (LIST -R). The good news is that many non-UNIX FTP servers do try to emulate that behavior.
The doGlob parameter must be set to either kGlobYes or kGlobNo. When set, the pattern is considered a shell wildcard-style regular expression, and FTPRemoteGlob is used if needed to build a list of files to retrieve.
The xtype parameter must be set to either kTypeAscii or kTypeBinary. Unless the file is known to be stored in the host's native text format, where ASCII text translation to your host's text format would be useful, you should use binary transfer type.
The resumeflag parameter must be set to either kResumeYes or kResumeNo. When set, the library will attempt to resume the download. This is done if the local file already exists and is smaller than the remote file. In addition, the library tries to use modification times when possible to determine if this should be done.
The appendflag parameter must be set to either kAppendYes or kAppendNo. When set, the entire remote file is downloaded, and the local file is appended to, if present. Generally that is not very useful.
The deleteflag parameter must be set to either kDeleteYes or kDeleteNo. When set, after the file is downloaded successfully the remote file is deleted. This requires the applicable permissions on the remote file.
The tarflag parameter must be set to either kTarYes or kTarNo. When set and the recurse parameter is also set, the library attempts to see if the server supports "on-the-fly TAR" and uses that to transfer the entire directory. The advantages to this are two-fold; first, it will be faster since a separate data connection is not required for each file in the directory, and second, since it is a TAR file it also preserves the exact file permissions and timestamps. The downside to using this mode is that you always get the entire directory structure. There is no resumption of broken downloads.
The resumeProc parameter can be set to a callback function
to give you fine-grained control on what to do when the local file already
exists. Your function can determine whether to resume the download,
overwrite and download the entire file, append to the local file, or skip
the file transfer. This is useful for interactive programs where
you want the user to choose which action to take. This parameter
must be set to NoConfirmResumeDownloadProc to indicate you do
not want a callback function, which is most of the time. Otherwise
your callback function should be a valid ConfirmResumeDownloadProc,
which is declared as follows:
typedef int (*ConfirmResumeDownloadProc)( const char *volatile *localpath, volatile longest_int localsize, volatile time_t localmtime, const char *volatile remotepath, volatile longest_int remotesize, volatile time_t remotetime, volatile longest_int *volatile startPoint );Your callback function should examine the parameters passed into it, and return one of the following result codes:
Your callback function should also set the startPoint to
the offset into the file where to resume the download at. You also
have the option of changing the localpath parameter, which could
be useful if your function decides it can save to a new name.
The reserved parameter must be set to zero. This is reserved for future use.
If all transfers succeeded, FTPGetFiles3 returns 0 (kNoErr), 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.
Example 1: Retrieve all files in the current directory whose names end in ".zip" and write them to the "/tmp" local directory.
err = FTPGetFiles3(cip, "*.zip", "/tmp", kRecursiveNo, kGlobYes, kTypeBinary, kResumeNo, kAppendNo, kDeleteNo, kTarNo, NoConfirmResumeDownloadProc, 0);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 = FTPGetFiles3(cip, "*README*", ".", kRecursiveNo, kGlobNo, kTypeBinary, kResumeNo, kAppendNo, kDeleteNo, kTarNo, NoConfirmResumeDownloadProc, 0);Example 3: Fetch the entire contents of the directory "/pub/zzz" into a local directory named "/tmp/aaa/bbb/zzz:"
err = FTPGetFiles3(cip, "/pub/zzz", "/tmp/aaa/bbb", kRecursiveYes, kGlobNo, kTypeBinary, kResumeNo, kAppendNo, kDeleteNo, kTarNo, NoConfirmResumeDownloadProc, 0);
int FTPGetOneFile2(const FTPCIPtr cip, const char *const file, const char *const dstfile, const int xtype, const int fdtouse, const int resumeflag, const int appendflag);
These functions have been superceded by FTPGetOneFile3 and are only provided for backward-compatibility with code based off of older versions of the library.
This is provides a way to download a single remote file and write it under a different name (if required) locally.
The file parameter specifies the remote file name to download, and the dstfile parameter specifies the local file name to save it as.
The xtype parameter must be set to either kTypeAscii or kTypeBinary. Unless the file is known to be stored in the host's native text format, where ASCII text translation to your host's text format would be useful, you should use binary transfer type.
The fdtouse parameter must be either an opened file descriptor for writing, or less than zero if the function should open the file as needed. Most of the time you will use (-1) for this parameter and let the library open the local file for you.
The resumeflag parameter must be set to either kResumeYes or kResumeNo. When set, the library will attempt to resume the download. This is done if the local file already exists and is smaller than the remote file. In addition, the library tries to use modification times when possible to determine if this should be done.
The appendflag parameter must be set to either kAppendYes or kAppendNo. When set, the entire remote file is downloaded, and the local file is appended to, if present. Generally that is not very useful.
The deleteflag parameter must be set to either kDeleteYes or kDeleteNo. When set, after the file is downloaded successfully the remote file is deleted. This requires the applicable permissions on the remote file.
The resumeProc parameter can be set to a callback function to give you fine-grained control on what to do when the local file already exists. See the description of FTPGetFiles3 for more information on how to use this.
The reserved parameter must be set to zero. This is reserved for future use.
Example: Retrieve a file named "/pub/README.TXT" and write it as "/tmp/xx".
err = FTPGetOneFile2(cip, "/pub/README.TXT", "/tmp/xx", kTypeBinary, -1, kResumeYes, kAppendNo);If the fetch succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.
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 = 20; cip->xferTimeout = 0; /* Do not timeout data blocks. */ cip->connTimeout = 0; cip->ctrlTimeout = 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"); /* ... */
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 ... */ }
This function returns 1 if the pathname is a valid directory, zero if it is a plain file, or a negative error code if an error occurred. This function may also return 0 if the item is known to exist but the file type could not be determined (so, if the item exists it is assumed to be a regular file; however due to the way the test is performed, the item in question could not be changed to so it is most likely a regular file, and less likely an inaccessible directory).
This function calls FTPFileExists, which may be an expensive call.
Example:
FTPConnectionInfo ci; if (FTPIsDir(&ci, "/pub/linux") > 0) { /* directory */ }
This function returns 1 if the pathname is a file, zero if it is a valid directory, or a negative error code if an error occurred. This function may also return 0 if the item is known to exist but the file type could not be determined (so, if the item exists it is assumed to be a regular file; however due to the way the test is performed, the item in question could not be changed to so it is most likely a regular file, and less likely an inaccessible directory).
This function calls FTPFileExists, which may be an expensive call.
Example:
FTPConnectionInfo ci; if (FTPIsRegularFile(&ci, "/pub/linux/README") > 0) { /* file */ }
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.
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.
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.
This uses the values for the user, pass, and acct fields from the FTPConnectionInfo structure to sign on to the remote system, and reads the connect message from the server.
You should not need to use this function, since FTPOpenHost does this for you automatically.
If the login was successful, 0 is returned, otherwise a number less than zero is returned upon failure.
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.
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 and the login succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.
This is identical to FTPOpenHost, except that FTPLoginHost is not performed.
If the connection was established, 0 is returned, otherwise a number less than zero is returned upon failure.
This is roughly the library's equivalent to the perror() C library function. It returns a textual error message from a library error number (which will be negative numbers).
It is often useful to print an error message when an error occurs, like:
if (FTPChdir(cip, "/pub") < 0) { /* print an error */ }However, many of the errors aren't very helpful, because the error code is meant be propagated up through a calling chain. So for this particular example, you'd often see get the error code kErrCWDFailed (remote chdir failed) as the reason. For this reason, when a non-library related error occurs, it is often best to inspect the textual response from the server, so the error from the server is printed instead of the generic library error.
To handle this, you can call the FTPPerror function with an expected error code, so when that particular error occurs the server's error string is printed.
The s1 and s2 parameters are words to print along with the error. One or both may be NULL if you only need to print one or no extra strings.
if (FTPChdir(cip, dirname) < 0) FTPPerror(cip, cip->errno, kErrCWDFailed, "cd", dirname);
int FTPPutFiles2(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob, const int xtype, const int appendflag, const char *const tmppfx, const char *const tmpsfx);
These functions have been superceded by FTPPutFiles3 and are only provided for backward-compatibility with code based off of older versions of the library.
Uploads (writes) files to the remote system.
The pattern parameter is the remote pathname to upload. When coupled with globbing, the pattern can denote a regular expression so that multiple files can be uploaded with a single wildcard expression.
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 sent are named to be the same as they were on the local system.
The recurse parameter must be set to either kRecursiveYes or kRecursiveNo. When set the library attempts to upload the entire directory structure if pattern is a directory. Recursion for this function should be portable. It does not require any special treatment from the remote server (unlike the FTPGetFiles3 function).
The doGlob parameter must be set to either kGlobYes or kGlobNo. When set, the pattern is considered a shell wildcard-style regular expression, and FTPLocalGlob is used if needed to build a list of files to retrieve.
The xtype parameter must be set to either kTypeAscii or kTypeBinary. Unless the file is known to be stored in the local host's native text format, where ASCII text translation to the remote host's text format would be useful, you should use binary transfer type.
The appendflag parameter must be set to either kAppendYes or kAppendNo. When set, the entire local file is uploaded, and the remote file is appended to, if present. Generally that is not very useful.
The tmppfx parameter must be set to the prefix of the temporary file to use, or NULL if no prefix should be used. The tmpsfx parameter must be set to the suffix of the temporary file to use, or NULL if no suffix should be used. These two parameters are used to create a temporary filename based on the real file name you want. If either of these is set, then the library uploads to a temporary file, and when the upload finishes, renames the temporary file to the real name. For example, if the file to upload is to be named "/tmp/aaa/bbb.txt" and the tmpsfx is ".TMP", then "/tmp/aaa/bbb.txt.TMP" is uploaded, and it it worked, renamed to "/tmp/aaa/bbb.txt".
The resumeflag parameter must be set to either kResumeYes or kResumeNo. When set, the library will attempt to resume the upload. This is done if the remote file already exists and is smaller than the local file. In addition, the library tries to use modification times when possible to determine if this should be done.
The deleteflag parameter must be set to either kDeleteYes or kDeleteNo. When set, after the file is uploaded successfully the local file is deleted.
The resumeProc parameter can be set to a callback function
to give you fine-grained control on what to do when the remote file already
exists. Your function can determine whether to resume the upload,
overwrite and upload the entire file, append to the remote file, or skip
the file transfer. This is useful for interactive programs where
you want the user to choose which action to take. This parameter
must be set to NoConfirmResumeUploadProc to indicate you do not
want a callback function, which is most of the time. Otherwise your
callback function should be a valid ConfirmResumeUploadProc, which
is declared as follows:
typedef int (*ConfirmResumeUploadProc)( const char *volatile localpath, volatile longest_int localsize, volatile time_t localmtime, const char *volatile *remotepath, volatile longest_int remotesize, volatile time_t remotetime, volatile longest_int *volatile startPoint );Your callback function should examine the parameters passed into it, and return one of the following result codes:
Your callback function should also set the startPoint to
the offset into the local file where to resume the upload at. You
also have the option of changing the remotepath parameter, which
could be useful if your function decides it can save to a new name.
The reserved parameter must be set to zero. This is reserved for future use.
If all transfers succeeded, FTPPutFiles3 returns 0 (kNoErr), 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.
Example 1: Send all files in the current directory whose names end in ".zip" and write them to the "/tmp" remote directory.
err = FTPPutFiles3(cip, "*.zip", "/tmp", kRecursiveNo, kGlobYes, kTypeBinary, kAppendNo, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0);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 = FTPPutFiles3(cip, "*README*", ".", kRecursiveNo, kGlobNo, kTypeBinary, kAppendNo, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0);Example 3: Send the entire contents of the local directory "/usr/local/bin", creating a "/pub/bin" directory tree on the remote server:
err = FTPPutFiles3(cip, "/usr/local/bin", "/pub", kRecursiveYes, kGlobNo, kTypeBinary, kAppendNo, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0);Example 4: Send all files in the current directory whose names end in ".zip" and write them to the "/tmp" remote directory, using "AA" as the prefix to temporary files:
err = FTPPutFiles3(cip, "*.zip", "/tmp", kRecursiveNo, kGlobYes, kTypeBinary, kAppendNo, "AA", NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0);
int FTPPutOneFile2(const FTPCIPtr cip, const char *const file, const char *const dstfile, const int xtype, const int fdtouse, const int appendflag, const char *const tmppfx, const char *const tmpsfx);
These functions have been superceded by FTPPutOneFile3 and are only provided for backward-compatibility with code based off of older versions of the library.
This is provides a way to upload a single local file and write it under a different name (if needed) on the remote server.
The file parameter specifies the remote file name to upload, and the dstfile parameter specifies the remote file name to save it as.
The xtype parameter must be set to either kTypeAscii or kTypeBinary. Unless the file is known to be stored in the local host's native text format, where ASCII text translation to the remote host's text format would be useful, you should use binary transfer type.
The fdtouse parameter must be either an opened file descriptor for reading, or less than zero if the function should open the file as needed. Most of the time you will use (-1) for this parameter and let the library open the local file for you.
The appendflag parameter must be set to either kAppendYes or kAppendNo. When set, the entire local file is uploaded, and the remote file is appended to, if present. Generally that is not very useful.
The tmppfx parameter must be set to the prefix of the temporary file to use, or NULL if no prefix should be used. The tmpsfx parameter must be set to the suffix of the temporary file to use, or NULL if no suffix should be used. These two parameters are used to create a temporary filename based on the real file name you want. If either of these is set, then the library uploads to a temporary file, and when the upload finishes, renames the temporary file to the real name. For example, if the file to upload is to be named "/tmp/aaa/bbb.txt" and the tmpsfx is ".TMP", then "/tmp/aaa/bbb.txt.TMP" is uploaded, and it it worked, renamed to "/tmp/aaa/bbb.txt".
The resumeflag parameter must be set to either kResumeYes or kResumeNo. When set, the library will attempt to resume the upload. This is done if the remote file already exists and is smaller than the local file. In addition, the library tries to use modification times when possible to determine if this should be done.
The deleteflag parameter must be set to either kDeleteYes or kDeleteNo. When set, after the file is uploaded successfully the local file is deleted.
The resumeProc parameter can be set to a callback function to give you fine-grained control on what to do when the local file already exists. See the description of FTPPutFiles3 for more information on how to use this.
The reserved parameter must be set to zero. This is reserved for future use.
Example 1: Send a file named "/tmp/xx" and write it as "/pub/README".
err = FTPPutOneFile2(cip, "/tmp/xx", "/pub/README", kTypeAscii, -1, kAppendNo, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0);Example 2: Send a file named "/tmp/xx" and write it temporarily as "/pub/README~", and when finished, name it "/pub/README".
err = FTPPutOneFile2(cip, "/tmp/xx", "/pub/README", kTypeAscii, -1, kAppendNo, NULL, "~", kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0);If the send succeeded, 0 is returned, otherwise a number less than zero is returned upon failure.
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.
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.
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. When set, the pattern is considered a shell-wildcard-style regular expression.
The recurse parameter must be set to either kRecursiveYes or kRecursiveNo. When set, the library attempts to remove all files and subdirectories also (i.e. like /bin/rm -rf on UNIX).
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);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.
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.
This is the library's equivalent to the strerror() C library function. It returns a textual error message from a library error number (which will be negative numbers).
This function is often useful to dump an error message when an error occurs, like:
if (FTPChdir(cip, "/pub") < 0) { fprintf(stderr, "Could not cd to /pub: %s.\n", FTPStrError(cip->errno)); }However, many of the errors aren't very helpful, because the error code is meant be propagated up through a calling chain. So for this particular example, you'd often see remote chdir failed as the reason. For this reason, it is often best to inspect the response from the server, like this:
if (FTPChdir(cip, "/pub") < 0) { if (cip->errno == kErrCWDFailed) { fprintf(stderr, "Could not cd to /pub: %s.\n", cip->lastFTPCmdResultStr); } else { fprintf(stderr, "Could not cd to /pub: %s.\n", FTPStrError(cip->errno)); } }
A few FTP server types support a special extension which allows creation of symbolic links. This function allows you to attempt to take advantage of that functionality by creating a symbolic link on the remote host.
If the link succeeded, 0 is returned, otherwise a negative error code is returned.
This 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.
This attempts to set a remote file's timestamps, similar to to the way you would use the utime() system call on a local file. This is not in the FTP standard, but some UNIX hosts implement this as a site-specific command.
Example: Set the times for the remote file "/pub/README":
if (stat(localfile, &st) == 0) err = FTPUtime(cip, "/pub/README", st.st_atime, st.st_mtime, st.st_ctime);If the times were set, 0 is returned, otherwise a number less than zero is returned upon failure.
This makes a duplicate of a LineList structure. The dynamically allocated sub-structures are duplicated dynamically, so that disposing the original LineList does not invalidate the copies in the new LineList.
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);
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.)
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);
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.
To do that, the xferTimeout 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.
When the library is built, the configure script must be told to look for SOCKS, as in ./configure --enable-socks5.