ftp.c revision 60707
137535Sdes/*- 237535Sdes * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav 337535Sdes * All rights reserved. 437535Sdes * 537535Sdes * Redistribution and use in source and binary forms, with or without 637535Sdes * modification, are permitted provided that the following conditions 737535Sdes * are met: 837535Sdes * 1. Redistributions of source code must retain the above copyright 937535Sdes * notice, this list of conditions and the following disclaimer 1037535Sdes * in this position and unchanged. 1137535Sdes * 2. Redistributions in binary form must reproduce the above copyright 1237535Sdes * notice, this list of conditions and the following disclaimer in the 1337535Sdes * documentation and/or other materials provided with the distribution. 1437535Sdes * 3. The name of the author may not be used to endorse or promote products 1537535Sdes * derived from this software without specific prior written permission 1637535Sdes * 1737535Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1837535Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1937535Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2037535Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2137535Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2237535Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2337535Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2437535Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2537535Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2637535Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2737535Sdes * 2850476Speter * $FreeBSD: head/lib/libfetch/ftp.c 60707 2000-05-19 09:45:42Z des $ 2937535Sdes */ 3037535Sdes 3137535Sdes/* 3237571Sdes * Portions of this code were taken from or based on ftpio.c: 3337535Sdes * 3437535Sdes * ---------------------------------------------------------------------------- 3537535Sdes * "THE BEER-WARE LICENSE" (Revision 42): 3637535Sdes * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you 3737535Sdes * can do whatever you want with this stuff. If we meet some day, and you think 3837535Sdes * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 3937535Sdes * ---------------------------------------------------------------------------- 4037535Sdes * 4137535Sdes * Major Changelog: 4237535Sdes * 4337535Sdes * Dag-Erling Co�dan Sm�rgrav 4437535Sdes * 9 Jun 1998 4537535Sdes * 4637535Sdes * Incorporated into libfetch 4737535Sdes * 4837535Sdes * Jordan K. Hubbard 4937535Sdes * 17 Jan 1996 5037535Sdes * 5137535Sdes * Turned inside out. Now returns xfers as new file ids, not as a special 5237535Sdes * `state' of FTP_t 5337535Sdes * 5437535Sdes * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ 5537535Sdes * 5637535Sdes */ 5737535Sdes 5841862Sdes#include <sys/param.h> 5937535Sdes#include <sys/socket.h> 6055557Sdes#include <sys/uio.h> 6137535Sdes#include <netinet/in.h> 6237535Sdes 6337535Sdes#include <ctype.h> 6455557Sdes#include <errno.h> 6560188Sdes#include <netdb.h> 6637573Sdes#include <stdarg.h> 6737535Sdes#include <stdio.h> 6837571Sdes#include <stdlib.h> 6937535Sdes#include <string.h> 7041869Sdes#include <time.h> 7137571Sdes#include <unistd.h> 7237535Sdes 7337535Sdes#include "fetch.h" 7440939Sdes#include "common.h" 7541862Sdes#include "ftperr.h" 7637535Sdes 7737535Sdes#define FTP_ANONYMOUS_USER "ftp" 7837535Sdes#define FTP_ANONYMOUS_PASSWORD "ftp" 7937573Sdes#define FTP_DEFAULT_PORT 21 8037535Sdes 8137573Sdes#define FTP_OPEN_DATA_CONNECTION 150 8237573Sdes#define FTP_OK 200 8341869Sdes#define FTP_FILE_STATUS 213 8441863Sdes#define FTP_SERVICE_READY 220 8537573Sdes#define FTP_PASSIVE_MODE 227 8637573Sdes#define FTP_LOGGED_IN 230 8737573Sdes#define FTP_FILE_ACTION_OK 250 8837573Sdes#define FTP_NEED_PASSWORD 331 8937573Sdes#define FTP_NEED_ACCOUNT 332 9060188Sdes#define FTP_FILE_OK 350 9155557Sdes#define FTP_SYNTAX_ERROR 500 9237573Sdes 9355557Sdesstatic char ENDL[2] = "\r\n"; 9437571Sdes 9540975Sdesstatic struct url cached_host; 9655557Sdesstatic int cached_socket; 9737535Sdes 9855557Sdesstatic char *last_reply; 9955557Sdesstatic size_t lr_size, lr_length; 10055557Sdesstatic int last_code; 10137571Sdes 10255557Sdes#define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 10360707Sdes && isdigit(foo[2]) \ 10460707Sdes && (foo[3] == ' ' || foo[3] == '\0')) 10555557Sdes#define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 10655557Sdes && isdigit(foo[2]) && foo[3] == '-') 10755557Sdes 10837571Sdes/* 10955557Sdes * Get server response 11037535Sdes */ 11137535Sdesstatic int 11255557Sdes_ftp_chkerr(int cd) 11337535Sdes{ 11437535Sdes do { 11555557Sdes if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) { 11640939Sdes _fetch_syserr(); 11737535Sdes return -1; 11837571Sdes } 11937573Sdes#ifndef NDEBUG 12055557Sdes _fetch_info("got reply '%.*s'", lr_length - 2, last_reply); 12137573Sdes#endif 12255557Sdes } while (isftpinfo(last_reply)); 12355557Sdes 12455557Sdes while (lr_length && isspace(last_reply[lr_length-1])) 12555557Sdes lr_length--; 12655557Sdes last_reply[lr_length] = 0; 12737573Sdes 12855557Sdes if (!isftpreply(last_reply)) { 12955557Sdes _ftp_seterr(999); 13037535Sdes return -1; 13137571Sdes } 13237535Sdes 13355557Sdes last_code = (last_reply[0] - '0') * 100 13455557Sdes + (last_reply[1] - '0') * 10 13555557Sdes + (last_reply[2] - '0'); 13655557Sdes 13755557Sdes return last_code; 13837535Sdes} 13937535Sdes 14037535Sdes/* 14137573Sdes * Send a command and check reply 14237535Sdes */ 14337535Sdesstatic int 14455557Sdes_ftp_cmd(int cd, char *fmt, ...) 14537535Sdes{ 14637573Sdes va_list ap; 14755557Sdes struct iovec iov[2]; 14855557Sdes char *msg; 14955557Sdes int r; 15037573Sdes 15137573Sdes va_start(ap, fmt); 15255557Sdes vasprintf(&msg, fmt, ap); 15355557Sdes va_end(ap); 15455557Sdes 15555557Sdes if (msg == NULL) { 15655557Sdes errno = ENOMEM; 15755557Sdes _fetch_syserr(); 15855557Sdes return -1; 15955557Sdes } 16037573Sdes#ifndef NDEBUG 16155557Sdes _fetch_info("sending '%s'", msg); 16237573Sdes#endif 16355557Sdes iov[0].iov_base = msg; 16455557Sdes iov[0].iov_len = strlen(msg); 16555557Sdes iov[1].iov_base = ENDL; 16660188Sdes iov[1].iov_len = sizeof ENDL; 16755557Sdes r = writev(cd, iov, 2); 16855557Sdes free(msg); 16955557Sdes if (r == -1) { 17055557Sdes _fetch_syserr(); 17155557Sdes return -1; 17255557Sdes } 17337571Sdes 17455557Sdes return _ftp_chkerr(cd); 17537535Sdes} 17637535Sdes 17737535Sdes/* 17837608Sdes * Transfer file 17937535Sdes */ 18037535Sdesstatic FILE * 18160188Sdes_ftp_transfer(int cd, char *oper, char *file, 18260188Sdes char *mode, off_t offset, char *flags) 18337535Sdes{ 18437573Sdes struct sockaddr_in sin; 18555544Sdes int pasv, high, verbose; 18655544Sdes int e, sd = -1; 18755544Sdes socklen_t l; 18837573Sdes char *s; 18937573Sdes FILE *df; 19055544Sdes 19155544Sdes /* check flags */ 19255544Sdes pasv = (flags && strchr(flags, 'p')); 19355544Sdes high = (flags && strchr(flags, 'h')); 19455544Sdes verbose = (flags && strchr(flags, 'v')); 19555544Sdes 19637535Sdes /* change directory */ 19737573Sdes if (((s = strrchr(file, '/')) != NULL) && (s != file)) { 19837573Sdes *s = 0; 19955544Sdes if (verbose) 20055544Sdes _fetch_info("changing directory to %s", file); 20155557Sdes if ((e = _ftp_cmd(cd, "CWD %s", file)) != FTP_FILE_ACTION_OK) { 20237573Sdes *s = '/'; 20355557Sdes if (e != -1) 20455557Sdes _ftp_seterr(e); 20537535Sdes return NULL; 20637535Sdes } 20737573Sdes *s++ = '/'; 20837535Sdes } else { 20955544Sdes if (verbose) 21055544Sdes _fetch_info("changing directory to /"); 21155557Sdes if ((e = _ftp_cmd(cd, "CWD /")) != FTP_FILE_ACTION_OK) { 21255557Sdes if (e != -1) 21355557Sdes _ftp_seterr(e); 21437535Sdes return NULL; 21541869Sdes } 21637535Sdes } 21737535Sdes 21837573Sdes /* s now points to file name */ 21937573Sdes 22037573Sdes /* open data socket */ 22138394Sdes if ((sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { 22240939Sdes _fetch_syserr(); 22337573Sdes return NULL; 22437573Sdes } 22537573Sdes 22637573Sdes if (pasv) { 22737573Sdes u_char addr[6]; 22837573Sdes char *ln, *p; 22937573Sdes int i; 23037573Sdes 23137573Sdes /* send PASV command */ 23255544Sdes if (verbose) 23355544Sdes _fetch_info("setting passive mode"); 23455557Sdes if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE) 23537573Sdes goto ouch; 23637573Sdes 23755544Sdes /* 23855544Sdes * Find address and port number. The reply to the PASV command 23955544Sdes * is IMHO the one and only weak point in the FTP protocol. 24055544Sdes */ 24155557Sdes ln = last_reply; 24260707Sdes for (p = ln + 3; *p && !isdigit(*p); p++) 24337573Sdes /* nothing */ ; 24460707Sdes for (i = 0; *p, i < 6; i++, p++) 24537573Sdes addr[i] = strtol(p, &p, 10); 24660707Sdes if (i < 6) { 24760707Sdes e = 999; 24860707Sdes goto ouch; 24937573Sdes } 25037573Sdes 25160188Sdes /* seek to required offset */ 25260188Sdes if (offset) 25360188Sdes if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK) 25460188Sdes goto sysouch; 25560188Sdes 25637573Sdes /* construct sockaddr for data socket */ 25760188Sdes l = sizeof sin; 25855557Sdes if (getpeername(cd, (struct sockaddr *)&sin, &l) == -1) 25937573Sdes goto sysouch; 26037573Sdes bcopy(addr, (char *)&sin.sin_addr, 4); 26137573Sdes bcopy(addr + 4, (char *)&sin.sin_port, 2); 26237573Sdes 26337573Sdes /* connect to data port */ 26455544Sdes if (verbose) 26555544Sdes _fetch_info("opening data connection"); 26660188Sdes if (connect(sd, (struct sockaddr *)&sin, sizeof sin) == -1) 26737573Sdes goto sysouch; 26860188Sdes 26937573Sdes /* make the server initiate the transfer */ 27055544Sdes if (verbose) 27155544Sdes _fetch_info("initiating transfer"); 27255557Sdes e = _ftp_cmd(cd, "%s %s", oper, s); 27341869Sdes if (e != FTP_OPEN_DATA_CONNECTION) 27437573Sdes goto ouch; 27537573Sdes 27637573Sdes } else { 27737573Sdes u_int32_t a; 27837573Sdes u_short p; 27955544Sdes int arg, d; 28037573Sdes 28137573Sdes /* find our own address, bind, and listen */ 28260188Sdes l = sizeof sin; 28355557Sdes if (getsockname(cd, (struct sockaddr *)&sin, &l) == -1) 28437573Sdes goto sysouch; 28537573Sdes sin.sin_port = 0; 28655544Sdes arg = high ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; 28755544Sdes if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, 28860188Sdes (char *)&arg, sizeof arg) == -1) 28955544Sdes goto sysouch; 29055544Sdes if (verbose) 29155544Sdes _fetch_info("binding data socket"); 29238394Sdes if (bind(sd, (struct sockaddr *)&sin, l) == -1) 29337573Sdes goto sysouch; 29438394Sdes if (listen(sd, 1) == -1) 29537573Sdes goto sysouch; 29637573Sdes 29737573Sdes /* find what port we're on and tell the server */ 29838394Sdes if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1) 29937573Sdes goto sysouch; 30037573Sdes a = ntohl(sin.sin_addr.s_addr); 30137573Sdes p = ntohs(sin.sin_port); 30255557Sdes e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d", 30341869Sdes (a >> 24) & 0xff, (a >> 16) & 0xff, 30441869Sdes (a >> 8) & 0xff, a & 0xff, 30541869Sdes (p >> 8) & 0xff, p & 0xff); 30641869Sdes if (e != FTP_OK) 30737573Sdes goto ouch; 30837573Sdes 30937573Sdes /* make the server initiate the transfer */ 31055544Sdes if (verbose) 31155544Sdes _fetch_info("initiating transfer"); 31255557Sdes e = _ftp_cmd(cd, "%s %s", oper, s); 31341869Sdes if (e != FTP_OPEN_DATA_CONNECTION) 31437573Sdes goto ouch; 31537573Sdes 31637573Sdes /* accept the incoming connection and go to town */ 31738394Sdes if ((d = accept(sd, NULL, NULL)) == -1) 31837573Sdes goto sysouch; 31937573Sdes close(sd); 32037573Sdes sd = d; 32137573Sdes } 32237573Sdes 32337608Sdes if ((df = fdopen(sd, mode)) == NULL) 32437573Sdes goto sysouch; 32537573Sdes return df; 32637573Sdes 32737573Sdessysouch: 32840939Sdes _fetch_syserr(); 32941869Sdes close(sd); 33041869Sdes return NULL; 33141869Sdes 33237573Sdesouch: 33355557Sdes if (e != -1) 33455557Sdes _ftp_seterr(e); 33537573Sdes close(sd); 33637535Sdes return NULL; 33737535Sdes} 33837535Sdes 33937571Sdes/* 34037571Sdes * Log on to FTP server 34137535Sdes */ 34255557Sdesstatic int 34355544Sdes_ftp_connect(char *host, int port, char *user, char *pwd, char *flags) 34437571Sdes{ 34560188Sdes int cd, e, pp = 0, direct, verbose; 34637608Sdes char *p, *q; 34737571Sdes 34855544Sdes direct = (flags && strchr(flags, 'd')); 34955544Sdes verbose = (flags && strchr(flags, 'v')); 35055544Sdes 35137608Sdes /* check for proxy */ 35255544Sdes if (!direct && (p = getenv("FTP_PROXY")) != NULL) { 35337608Sdes if ((q = strchr(p, ':')) != NULL) { 35460188Sdes if (strspn(q+1, "0123456789") != strlen(q+1) || strlen(q+1) > 5) { 35560188Sdes /* XXX we should emit some kind of warning */ 35660188Sdes } 35737608Sdes pp = atoi(q+1); 35860188Sdes if (pp < 1 || pp > 65535) { 35960188Sdes /* XXX we should emit some kind of warning */ 36060188Sdes } 36137608Sdes } 36260188Sdes if (!pp) { 36360188Sdes struct servent *se; 36460188Sdes 36560188Sdes if ((se = getservbyname("ftp", "tcp")) != NULL) 36660188Sdes pp = ntohs(se->s_port); 36760188Sdes else 36860188Sdes pp = FTP_DEFAULT_PORT; 36960188Sdes } 37037608Sdes if (q) 37137608Sdes *q = 0; 37255557Sdes cd = _fetch_connect(p, pp, verbose); 37337608Sdes if (q) 37437608Sdes *q = ':'; 37537608Sdes } else { 37637608Sdes /* no proxy, go straight to target */ 37755557Sdes cd = _fetch_connect(host, port, verbose); 37855544Sdes p = NULL; 37937608Sdes } 38037608Sdes 38137608Sdes /* check connection */ 38255557Sdes if (cd == -1) { 38340939Sdes _fetch_syserr(); 38437571Sdes return NULL; 38537571Sdes } 38637608Sdes 38737571Sdes /* expect welcome message */ 38855557Sdes if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY) 38937571Sdes goto fouch; 39037571Sdes 39137571Sdes /* send user name and password */ 39237608Sdes if (!user || !*user) 39337608Sdes user = FTP_ANONYMOUS_USER; 39455557Sdes e = p ? _ftp_cmd(cd, "USER %s@%s@%d", user, host, port) 39555557Sdes : _ftp_cmd(cd, "USER %s", user); 39637608Sdes 39737608Sdes /* did the server request a password? */ 39837608Sdes if (e == FTP_NEED_PASSWORD) { 39937608Sdes if (!pwd || !*pwd) 40037608Sdes pwd = FTP_ANONYMOUS_PASSWORD; 40155557Sdes e = _ftp_cmd(cd, "PASS %s", pwd); 40237608Sdes } 40337608Sdes 40437608Sdes /* did the server request an account? */ 40541869Sdes if (e == FTP_NEED_ACCOUNT) 40641863Sdes goto fouch; 40737608Sdes 40837608Sdes /* we should be done by now */ 40941869Sdes if (e != FTP_LOGGED_IN) 41037571Sdes goto fouch; 41137571Sdes 41237571Sdes /* might as well select mode and type at once */ 41337571Sdes#ifdef FTP_FORCE_STREAM_MODE 41455557Sdes if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */ 41541869Sdes goto fouch; 41637571Sdes#endif 41755557Sdes if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */ 41841869Sdes goto fouch; 41937571Sdes 42037571Sdes /* done */ 42155557Sdes return cd; 42237571Sdes 42337571Sdesfouch: 42455557Sdes if (e != -1) 42555557Sdes _ftp_seterr(e); 42655557Sdes close(cd); 42737571Sdes return NULL; 42837571Sdes} 42937571Sdes 43037571Sdes/* 43137571Sdes * Disconnect from server 43237571Sdes */ 43337571Sdesstatic void 43455557Sdes_ftp_disconnect(int cd) 43537571Sdes{ 43655557Sdes (void)_ftp_cmd(cd, "QUIT"); 43755557Sdes close(cd); 43837571Sdes} 43937571Sdes 44037571Sdes/* 44137571Sdes * Check if we're already connected 44237571Sdes */ 44337571Sdesstatic int 44440975Sdes_ftp_isconnected(struct url *url) 44537571Sdes{ 44637571Sdes return (cached_socket 44737571Sdes && (strcmp(url->host, cached_host.host) == 0) 44837571Sdes && (strcmp(url->user, cached_host.user) == 0) 44937571Sdes && (strcmp(url->pwd, cached_host.pwd) == 0) 45037571Sdes && (url->port == cached_host.port)); 45137571Sdes} 45237571Sdes 45337608Sdes/* 45441869Sdes * Check the cache, reconnect if no luck 45537608Sdes */ 45655557Sdesstatic int 45741869Sdes_ftp_cached_connect(struct url *url, char *flags) 45837535Sdes{ 45955557Sdes int e, cd; 46037535Sdes 46155557Sdes cd = -1; 46241869Sdes 46337571Sdes /* set default port */ 46460188Sdes if (!url->port) { 46560188Sdes struct servent *se; 46660188Sdes 46760188Sdes if ((se = getservbyname("ftp", "tcp")) != NULL) 46860188Sdes url->port = ntohs(se->s_port); 46960188Sdes else 47060188Sdes url->port = FTP_DEFAULT_PORT; 47160188Sdes } 47237535Sdes 47341863Sdes /* try to use previously cached connection */ 47455557Sdes if (_ftp_isconnected(url)) { 47555557Sdes e = _ftp_cmd(cached_socket, "NOOP"); 47655557Sdes if (e == FTP_OK || e == FTP_SYNTAX_ERROR) 47755557Sdes cd = cached_socket; 47855557Sdes } 47937571Sdes 48037571Sdes /* connect to server */ 48155557Sdes if (cd == -1) { 48255557Sdes cd = _ftp_connect(url->host, url->port, url->user, url->pwd, flags); 48355557Sdes if (cd == -1) 48455557Sdes return -1; 48537571Sdes if (cached_socket) 48637571Sdes _ftp_disconnect(cached_socket); 48755557Sdes cached_socket = cd; 48860188Sdes memcpy(&cached_host, url, sizeof *url); 48937535Sdes } 49037571Sdes 49155557Sdes return cd; 49237535Sdes} 49337535Sdes 49437571Sdes/* 49541869Sdes * Get file 49637571Sdes */ 49737535SdesFILE * 49840975SdesfetchGetFTP(struct url *url, char *flags) 49937608Sdes{ 50055557Sdes int cd; 50155557Sdes 50241869Sdes /* connect to server */ 50355557Sdes if ((cd = _ftp_cached_connect(url, flags)) == NULL) 50441869Sdes return NULL; 50541869Sdes 50641869Sdes /* initiate the transfer */ 50760188Sdes return _ftp_transfer(cd, "RETR", url->doc, "r", url->offset, flags); 50837608Sdes} 50937608Sdes 51041869Sdes/* 51141869Sdes * Put file 51241869Sdes */ 51337608SdesFILE * 51440975SdesfetchPutFTP(struct url *url, char *flags) 51537535Sdes{ 51655557Sdes int cd; 51741869Sdes 51841869Sdes /* connect to server */ 51955557Sdes if ((cd = _ftp_cached_connect(url, flags)) == NULL) 52041869Sdes return NULL; 52141869Sdes 52241869Sdes /* initiate the transfer */ 52355557Sdes return _ftp_transfer(cd, (flags && strchr(flags, 'a')) ? "APPE" : "STOR", 52460188Sdes url->doc, "w", url->offset, flags); 52537535Sdes} 52640975Sdes 52741869Sdes/* 52841869Sdes * Get file stats 52941869Sdes */ 53040975Sdesint 53140975SdesfetchStatFTP(struct url *url, struct url_stat *us, char *flags) 53240975Sdes{ 53341869Sdes char *ln, *s; 53441869Sdes struct tm tm; 53541869Sdes time_t t; 53655557Sdes int e, cd; 53741869Sdes 53860582Sdes us->size = -1; 53960582Sdes us->atime = us->mtime = 0; 54060582Sdes 54141869Sdes /* connect to server */ 54255557Sdes if ((cd = _ftp_cached_connect(url, flags)) == NULL) 54341869Sdes return -1; 54441869Sdes 54541869Sdes /* change directory */ 54641869Sdes if (((s = strrchr(url->doc, '/')) != NULL) && (s != url->doc)) { 54741869Sdes *s = 0; 54855557Sdes if ((e = _ftp_cmd(cd, "CWD %s", url->doc)) != FTP_FILE_ACTION_OK) { 54941869Sdes *s = '/'; 55041869Sdes goto ouch; 55141869Sdes } 55241869Sdes *s++ = '/'; 55341869Sdes } else { 55455557Sdes if ((e = _ftp_cmd(cd, "CWD /")) != FTP_FILE_ACTION_OK) 55541869Sdes goto ouch; 55641869Sdes } 55741869Sdes 55841869Sdes /* s now points to file name */ 55941869Sdes 56055557Sdes if (_ftp_cmd(cd, "SIZE %s", s) != FTP_FILE_STATUS) 56141869Sdes goto ouch; 56255557Sdes for (ln = last_reply + 4; *ln && isspace(*ln); ln++) 56341869Sdes /* nothing */ ; 56441869Sdes for (us->size = 0; *ln && isdigit(*ln); ln++) 56541869Sdes us->size = us->size * 10 + *ln - '0'; 56641869Sdes if (*ln && !isspace(*ln)) { 56755557Sdes _ftp_seterr(999); 56841869Sdes return -1; 56941869Sdes } 57060383Sdes DEBUG(fprintf(stderr, "size: [\033[1m%lld\033[m]\n", us->size)); 57141869Sdes 57255557Sdes if ((e = _ftp_cmd(cd, "MDTM %s", s)) != FTP_FILE_STATUS) 57341869Sdes goto ouch; 57455557Sdes for (ln = last_reply + 4; *ln && isspace(*ln); ln++) 57541869Sdes /* nothing */ ; 57660383Sdes e = 999; 57760383Sdes switch (strspn(ln, "0123456789")) { 57860383Sdes case 14: 57960383Sdes break; 58060383Sdes case 15: 58160383Sdes ln++; 58260383Sdes ln[0] = '2'; 58360383Sdes ln[1] = '0'; 58460383Sdes break; 58560383Sdes default: 58660383Sdes goto ouch; 58760383Sdes } 58860383Sdes if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", 58960383Sdes &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 59060383Sdes &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) 59160383Sdes goto ouch; 59241869Sdes tm.tm_mon--; 59341869Sdes tm.tm_year -= 1900; 59441869Sdes tm.tm_isdst = -1; 59560383Sdes t = timegm(&tm); 59656635Sdes if (t == (time_t)-1) 59756635Sdes t = time(NULL); 59856635Sdes us->mtime = t; 59956635Sdes us->atime = t; 60060383Sdes DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d " 60160383Sdes "%02d:%02d:%02d\033[m]\n", 60260383Sdes tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 60360383Sdes tm.tm_hour, tm.tm_min, tm.tm_sec)); 60441869Sdes return 0; 60541869Sdes 60641869Sdesouch: 60755557Sdes if (e != -1) 60855557Sdes _ftp_seterr(e); 60940975Sdes return -1; 61040975Sdes} 61141989Sdes 61241989Sdes/* 61341989Sdes * List a directory 61441989Sdes */ 61541989Sdesextern void warnx(char *, ...); 61641989Sdesstruct url_ent * 61741989SdesfetchListFTP(struct url *url, char *flags) 61841989Sdes{ 61941989Sdes warnx("fetchListFTP(): not implemented"); 62041989Sdes return NULL; 62141989Sdes} 622