diff options
Diffstat (limited to 'lib/libfetch/ftp.c')
-rw-r--r-- | lib/libfetch/ftp.c | 330 |
1 files changed, 235 insertions, 95 deletions
diff --git a/lib/libfetch/ftp.c b/lib/libfetch/ftp.c index 14d43b9..5df13ec 100644 --- a/lib/libfetch/ftp.c +++ b/lib/libfetch/ftp.c @@ -25,11 +25,11 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id$ + * $Id: ftp.c,v 1.1.1.1 1998/07/09 16:52:42 des Exp $ */ /* - * Portions of this code were taken from ftpio.c: + * Portions of this code were taken from or based on ftpio.c: * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): @@ -58,80 +58,117 @@ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> +#include <sys/errno.h> #include <ctype.h> +#include <errno.h> #include <netdb.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <unistd.h> #include "fetch.h" #include "ftperr.c" +#define FTP_DEFAULT_TO_ANONYMOUS #define FTP_ANONYMOUS_USER "ftp" #define FTP_ANONYMOUS_PASSWORD "ftp" +#define ENDL "\r\n" + static url_t cached_host; static FILE *cached_socket; -static int _ftp_errcode; -static int -_ftp_isconnected(url_t *url) +#ifndef NDEBUG +#define TRACE fprintf(stderr, "TRACE on line %d in " __FILE__ "\n", __LINE__); +#else +#define TRACE +#endif + +/* + * Map error code to string + */ +static const char * +_ftp_errstring(int e) { - return (cached_socket - && (strcmp(url->host, cached_host.host) == 0) - && (strcmp(url->user, cached_host.user) == 0) - && (strcmp(url->pwd, cached_host.pwd) == 0) - && (url->port == cached_host.port)); + struct ftperr *p = _ftp_errlist; + + while ((p->num != -1) && (p->num != e)) + p++; + + return p->string; +} + +/* + * Set error code + */ +static void +_ftp_seterr(int e) +{ + fetchLastErrCode = e; + fetchLastErrText = _ftp_errstring(e); +} + +/* + * Set error code according to errno + */ +static void +_ftp_syserr(void) +{ + fetchLastErrCode = errno; + fetchLastErrText = strerror(errno); } /* * Get server response, check that first digit is a '2' */ static int -_ftp_chkerr(FILE *s, char *e) +_ftp_chkerr(FILE *s, int *e) { char *line; size_t len; + TRACE; + + if (e) + *e = 0; + do { if (((line = fgetln(s, &len)) == NULL) || (len < 4)) + { + _ftp_syserr(); return -1; + } } while (line[3] == '-'); - if (!isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2]) || (line[3] != ' ')) + if (!isdigit(line[1]) || !isdigit(line[1]) + || !isdigit(line[2]) || (line[3] != ' ')) { + _ftp_seterr(-1); return -1; + } - _ftp_errcode = (line[0] - '0') * 100 + (line[1] - '0') * 10 + (line[2] - '0'); + _ftp_seterr((line[0] - '0') * 100 + (line[1] - '0') * 10 + (line[2] - '0')); if (e) - *e = _ftp_errcode; + *e = fetchLastErrCode; return (line[0] == '2') - 1; } /* - * Map error code to string - */ -static const char * -_ftp_errstring(int e) -{ - struct ftperr *p = _ftp_errlist; - - while ((p->num) && (p->num != e)) - p++; - - return p->string; -} - -/* * Change remote working directory */ static int _ftp_cwd(FILE *s, char *dir) { + TRACE; + fprintf(s, "CWD %s\n", dir); - if (ferror(s)) + if (ferror(s)) { + _ftp_syserr(); return -1; + } return _ftp_chkerr(s, NULL); /* expecting 250 */ } @@ -139,112 +176,215 @@ _ftp_cwd(FILE *s, char *dir) * Retrieve file */ static FILE * -_ftp_retr(FILE *s, char *file, int pasv) +_ftp_retrieve(FILE *cf, char *file, int pasv) { char *p; + TRACE; + /* change directory */ if (((p = strrchr(file, '/')) != NULL) && (p != file)) { *p = 0; - if (_ftp_cwd(s, file) < 0) { + if (_ftp_cwd(cf, file) < 0) { *p = '/'; return NULL; } *p++ = '/'; } else { - if (_ftp_cwd(s, "/") < 0) + if (_ftp_cwd(cf, "/") < 0) return NULL; } /* retrieve file; p now points to file name */ + fprintf(stderr, "Arrrgh! No! No! I can't do it! Leave me alone!\n"); + return NULL; +} + +/* + * Store file + */ +static FILE * +_ftp_store(FILE *cf, char *file, int pasv) +{ + TRACE; + + cf = cf; + file = file; + pasv = pasv; + return NULL; +} + +/* + * Log on to FTP server + */ +static FILE * +_ftp_connect(char *host, int port, char *user, char *pwd) +{ + int sd, e; + FILE *f; + + TRACE; + + /* establish control connection */ + if ((sd = fetchConnect(host, port)) < 0) { + _ftp_syserr(); + return NULL; + } + if ((f = fdopen(sd, "r+")) == NULL) { + _ftp_syserr(); + goto ouch; + } + + /* expect welcome message */ + if (_ftp_chkerr(f, NULL) < 0) + goto fouch; + + /* send user name and password */ + fprintf(f, "USER %s" ENDL, user); + _ftp_chkerr(f, &e); + if (e == 331) { + /* server requested a password */ + fprintf(f, "PASS %s" ENDL, pwd); + _ftp_chkerr(f, &e); + } + if (e == 332) { + /* server requested an account */ + } + if (e != 230) /* won't let us near the WaReZ */ + goto fouch; + + /* might as well select mode and type at once */ +#ifdef FTP_FORCE_STREAM_MODE + fprintf(f, "MODE S" ENDL); + if (_ftp_chkerr(f, NULL) < 0) + goto ouch; +#endif + fprintf(f, "TYPE I" ENDL); + if (_ftp_chkerr(f, NULL) < 0) + goto ouch; + + /* done */ + return f; + +ouch: + close(sd); + return NULL; +fouch: + fclose(f); return NULL; } +/* + * Disconnect from server + */ +static void +_ftp_disconnect(FILE *f) +{ + TRACE; + + fprintf(f, "QUIT" ENDL); + _ftp_chkerr(f, NULL); + fclose(f); +} /* - * XXX rewrite these + * Check if we're already connected */ -#if 0 +static int +_ftp_isconnected(url_t *url) +{ + TRACE; + + return (cached_socket + && (strcmp(url->host, cached_host.host) == 0) + && (strcmp(url->user, cached_host.user) == 0) + && (strcmp(url->pwd, cached_host.pwd) == 0) + && (url->port == cached_host.port)); +} + FILE * fetchGetFTP(url_t *url, char *flags) { - int retcode = 0; - static FILE *fp = NULL; - static char *prev_host = NULL; - FILE *fp2; + FILE *cf = NULL; + int e; + TRACE; + #ifdef DEFAULT_TO_ANONYMOUS if (!url->user[0]) { strcpy(url->user, FTP_ANONYMOUS_USER); strcpy(url->pwd, FTP_ANONYMOUS_PASSWORD); } #endif + + /* set default port */ + if (!url->port) + url->port = 21; - if (fp && prev_host) { - if (!strcmp(prev_host, url->host)) { - /* Try to use cached connection */ - fp2 = ftpGet(fp, url->doc, NULL); - if (!fp2) { - /* Connection timed out or was no longer valid */ - fclose(fp); - free(prev_host); - prev_host = NULL; - } - else - return fp2; - } - else { - /* It's a different host now, flush old */ - fclose(fp); - free(prev_host); - prev_host = NULL; - } + /* try to use previously cached connection */ + if (_ftp_isconnected(url)) { + fprintf(cached_socket, "PWD" ENDL); + _ftp_chkerr(cached_socket, &e); + if (e > 0) + cf = cached_socket; } - fp = ftpLogin(url->host, url->user, url->pwd, url->port, 0, &retcode); - if (fp) { - if (strchr(flags, 'p')) { - if (ftpPassive(fp, 1) != SUCCESS) - /* XXX what should we do? */ ; - } - fp2 = ftpGet(fp, url->doc, NULL); - if (!fp2) { - /* Connection timed out or was no longer valid */ - retcode = ftpErrno(fp); - fclose(fp); - fp = NULL; - } - else - prev_host = strdup(url->host); - return fp2; + + /* connect to server */ + if (!cf) { + cf = _ftp_connect(url->host, url->port, url->user, url->pwd); + if (!cf) + return NULL; + if (cached_socket) + _ftp_disconnect(cached_socket); + cached_socket = cf; + memcpy(&cached_host, url, sizeof(url_t)); } - return NULL; + + /* initiate the transfer */ + return _ftp_retrieve(cf, url->doc, (flags && strchr(flags, 'p'))); } +/* + * Upload a file. + * Hmmm, that's almost an exact duplicate of the above... + */ FILE * fetchPutFTP(url_t *url, char *flags) { - static FILE *fp = NULL; - FILE *fp2; - int retcode = 0; + FILE *cf = NULL; + int e; + +#ifdef DEFAULT_TO_ANONYMOUS + if (!url->user[0]) { + strcpy(url->user, FTP_ANONYMOUS_USER); + strcpy(url->pwd, FTP_ANONYMOUS_PASSWORD); + } +#endif - if (fp) { /* Close previous managed connection */ - fclose(fp); - fp = NULL; + /* set default port */ + if (!url->port) + url->port = 21; + + /* try to use previously cached connection */ + if (_ftp_isconnected(url)) { + fprintf(cached_socket, "PWD" ENDL); + _ftp_chkerr(cached_socket, &e); + if (e > 0) + cf = cached_socket; } - fp = ftpLogin(url->host, url->user, url->pwd, url->port, 0, &retcode); - if (fp) { - if (strchr(flags, 'p')) { - if (ftpPassive(fp, 1) != SUCCESS) - /* XXX what should we do? */ ; - } - fp2 = ftpPut(fp, url->doc); - if (!fp2) { - retcode = ftpErrno(fp); - fclose(fp); - fp = NULL; - } - return fp2; + + /* connect to server */ + if (!cf) { + cf = _ftp_connect(url->host, url->port, url->user, url->pwd); + if (!cf) + return NULL; + if (cached_socket) + _ftp_disconnect(cached_socket); + cached_socket = cf; + memcpy(&cached_host, url, sizeof(url_t)); } - return NULL; + + + /* initiate the transfer */ + return _ftp_store(cf, url->doc, (flags && strchr(flags, 'p'))); } -#endif |