ftp.c revision 60383
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 60383 2000-05-11 16:01:03Z 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]) \ 10355557Sdes && isdigit(foo[2]) && foo[3] == ' ') 10455557Sdes#define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 10555557Sdes && isdigit(foo[2]) && foo[3] == '-') 10655557Sdes 10737571Sdes/* 10855557Sdes * Get server response 10937535Sdes */ 11037535Sdesstatic int 11155557Sdes_ftp_chkerr(int cd) 11237535Sdes{ 11337535Sdes do { 11455557Sdes if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) { 11540939Sdes _fetch_syserr(); 11637535Sdes return -1; 11737571Sdes } 11837573Sdes#ifndef NDEBUG 11955557Sdes _fetch_info("got reply '%.*s'", lr_length - 2, last_reply); 12037573Sdes#endif 12155557Sdes } while (isftpinfo(last_reply)); 12255557Sdes 12355557Sdes while (lr_length && isspace(last_reply[lr_length-1])) 12455557Sdes lr_length--; 12555557Sdes last_reply[lr_length] = 0; 12637573Sdes 12755557Sdes if (!isftpreply(last_reply)) { 12855557Sdes _ftp_seterr(999); 12937535Sdes return -1; 13037571Sdes } 13137535Sdes 13255557Sdes last_code = (last_reply[0] - '0') * 100 13355557Sdes + (last_reply[1] - '0') * 10 13455557Sdes + (last_reply[2] - '0'); 13555557Sdes 13655557Sdes return last_code; 13737535Sdes} 13837535Sdes 13937535Sdes/* 14037573Sdes * Send a command and check reply 14137535Sdes */ 14237535Sdesstatic int 14355557Sdes_ftp_cmd(int cd, char *fmt, ...) 14437535Sdes{ 14537573Sdes va_list ap; 14655557Sdes struct iovec iov[2]; 14755557Sdes char *msg; 14855557Sdes int r; 14937573Sdes 15037573Sdes va_start(ap, fmt); 15155557Sdes vasprintf(&msg, fmt, ap); 15255557Sdes va_end(ap); 15355557Sdes 15455557Sdes if (msg == NULL) { 15555557Sdes errno = ENOMEM; 15655557Sdes _fetch_syserr(); 15755557Sdes return -1; 15855557Sdes } 15937573Sdes#ifndef NDEBUG 16055557Sdes _fetch_info("sending '%s'", msg); 16137573Sdes#endif 16255557Sdes iov[0].iov_base = msg; 16355557Sdes iov[0].iov_len = strlen(msg); 16455557Sdes iov[1].iov_base = ENDL; 16560188Sdes iov[1].iov_len = sizeof ENDL; 16655557Sdes r = writev(cd, iov, 2); 16755557Sdes free(msg); 16855557Sdes if (r == -1) { 16955557Sdes _fetch_syserr(); 17055557Sdes return -1; 17155557Sdes } 17237571Sdes 17355557Sdes return _ftp_chkerr(cd); 17437535Sdes} 17537535Sdes 17637535Sdes/* 17737608Sdes * Transfer file 17837535Sdes */ 17937535Sdesstatic FILE * 18060188Sdes_ftp_transfer(int cd, char *oper, char *file, 18160188Sdes char *mode, off_t offset, char *flags) 18237535Sdes{ 18337573Sdes struct sockaddr_in sin; 18455544Sdes int pasv, high, verbose; 18555544Sdes int e, sd = -1; 18655544Sdes socklen_t l; 18737573Sdes char *s; 18837573Sdes FILE *df; 18955544Sdes 19055544Sdes /* check flags */ 19155544Sdes pasv = (flags && strchr(flags, 'p')); 19255544Sdes high = (flags && strchr(flags, 'h')); 19355544Sdes verbose = (flags && strchr(flags, 'v')); 19455544Sdes 19537535Sdes /* change directory */ 19637573Sdes if (((s = strrchr(file, '/')) != NULL) && (s != file)) { 19737573Sdes *s = 0; 19855544Sdes if (verbose) 19955544Sdes _fetch_info("changing directory to %s", file); 20055557Sdes if ((e = _ftp_cmd(cd, "CWD %s", file)) != FTP_FILE_ACTION_OK) { 20137573Sdes *s = '/'; 20255557Sdes if (e != -1) 20355557Sdes _ftp_seterr(e); 20437535Sdes return NULL; 20537535Sdes } 20637573Sdes *s++ = '/'; 20737535Sdes } else { 20855544Sdes if (verbose) 20955544Sdes _fetch_info("changing directory to /"); 21055557Sdes if ((e = _ftp_cmd(cd, "CWD /")) != FTP_FILE_ACTION_OK) { 21155557Sdes if (e != -1) 21255557Sdes _ftp_seterr(e); 21337535Sdes return NULL; 21441869Sdes } 21537535Sdes } 21637535Sdes 21737573Sdes /* s now points to file name */ 21837573Sdes 21937573Sdes /* open data socket */ 22038394Sdes if ((sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { 22140939Sdes _fetch_syserr(); 22237573Sdes return NULL; 22337573Sdes } 22437573Sdes 22537573Sdes if (pasv) { 22637573Sdes u_char addr[6]; 22737573Sdes char *ln, *p; 22837573Sdes int i; 22937573Sdes 23037573Sdes /* send PASV command */ 23155544Sdes if (verbose) 23255544Sdes _fetch_info("setting passive mode"); 23355557Sdes if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE) 23437573Sdes goto ouch; 23537573Sdes 23655544Sdes /* 23755544Sdes * Find address and port number. The reply to the PASV command 23855544Sdes * is IMHO the one and only weak point in the FTP protocol. 23955544Sdes */ 24055557Sdes ln = last_reply; 24137573Sdes for (p = ln + 3; !isdigit(*p); p++) 24237573Sdes /* nothing */ ; 24337573Sdes for (p--, i = 0; i < 6; i++) { 24437573Sdes p++; /* skip the comma */ 24537573Sdes addr[i] = strtol(p, &p, 10); 24637573Sdes } 24737573Sdes 24860188Sdes /* seek to required offset */ 24960188Sdes if (offset) 25060188Sdes if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK) 25160188Sdes goto sysouch; 25260188Sdes 25337573Sdes /* construct sockaddr for data socket */ 25460188Sdes l = sizeof sin; 25555557Sdes if (getpeername(cd, (struct sockaddr *)&sin, &l) == -1) 25637573Sdes goto sysouch; 25737573Sdes bcopy(addr, (char *)&sin.sin_addr, 4); 25837573Sdes bcopy(addr + 4, (char *)&sin.sin_port, 2); 25937573Sdes 26037573Sdes /* connect to data port */ 26155544Sdes if (verbose) 26255544Sdes _fetch_info("opening data connection"); 26360188Sdes if (connect(sd, (struct sockaddr *)&sin, sizeof sin) == -1) 26437573Sdes goto sysouch; 26560188Sdes 26637573Sdes /* make the server initiate the transfer */ 26755544Sdes if (verbose) 26855544Sdes _fetch_info("initiating transfer"); 26955557Sdes e = _ftp_cmd(cd, "%s %s", oper, s); 27041869Sdes if (e != FTP_OPEN_DATA_CONNECTION) 27137573Sdes goto ouch; 27237573Sdes 27337573Sdes } else { 27437573Sdes u_int32_t a; 27537573Sdes u_short p; 27655544Sdes int arg, d; 27737573Sdes 27837573Sdes /* find our own address, bind, and listen */ 27960188Sdes l = sizeof sin; 28055557Sdes if (getsockname(cd, (struct sockaddr *)&sin, &l) == -1) 28137573Sdes goto sysouch; 28237573Sdes sin.sin_port = 0; 28355544Sdes arg = high ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; 28455544Sdes if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, 28560188Sdes (char *)&arg, sizeof arg) == -1) 28655544Sdes goto sysouch; 28755544Sdes if (verbose) 28855544Sdes _fetch_info("binding data socket"); 28938394Sdes if (bind(sd, (struct sockaddr *)&sin, l) == -1) 29037573Sdes goto sysouch; 29138394Sdes if (listen(sd, 1) == -1) 29237573Sdes goto sysouch; 29337573Sdes 29437573Sdes /* find what port we're on and tell the server */ 29538394Sdes if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1) 29637573Sdes goto sysouch; 29737573Sdes a = ntohl(sin.sin_addr.s_addr); 29837573Sdes p = ntohs(sin.sin_port); 29955557Sdes e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d", 30041869Sdes (a >> 24) & 0xff, (a >> 16) & 0xff, 30141869Sdes (a >> 8) & 0xff, a & 0xff, 30241869Sdes (p >> 8) & 0xff, p & 0xff); 30341869Sdes if (e != FTP_OK) 30437573Sdes goto ouch; 30537573Sdes 30637573Sdes /* make the server initiate the transfer */ 30755544Sdes if (verbose) 30855544Sdes _fetch_info("initiating transfer"); 30955557Sdes e = _ftp_cmd(cd, "%s %s", oper, s); 31041869Sdes if (e != FTP_OPEN_DATA_CONNECTION) 31137573Sdes goto ouch; 31237573Sdes 31337573Sdes /* accept the incoming connection and go to town */ 31438394Sdes if ((d = accept(sd, NULL, NULL)) == -1) 31537573Sdes goto sysouch; 31637573Sdes close(sd); 31737573Sdes sd = d; 31837573Sdes } 31937573Sdes 32037608Sdes if ((df = fdopen(sd, mode)) == NULL) 32137573Sdes goto sysouch; 32237573Sdes return df; 32337573Sdes 32437573Sdessysouch: 32540939Sdes _fetch_syserr(); 32641869Sdes close(sd); 32741869Sdes return NULL; 32841869Sdes 32937573Sdesouch: 33055557Sdes if (e != -1) 33155557Sdes _ftp_seterr(e); 33237573Sdes close(sd); 33337535Sdes return NULL; 33437535Sdes} 33537535Sdes 33637571Sdes/* 33737571Sdes * Log on to FTP server 33837535Sdes */ 33955557Sdesstatic int 34055544Sdes_ftp_connect(char *host, int port, char *user, char *pwd, char *flags) 34137571Sdes{ 34260188Sdes int cd, e, pp = 0, direct, verbose; 34337608Sdes char *p, *q; 34437571Sdes 34555544Sdes direct = (flags && strchr(flags, 'd')); 34655544Sdes verbose = (flags && strchr(flags, 'v')); 34755544Sdes 34837608Sdes /* check for proxy */ 34955544Sdes if (!direct && (p = getenv("FTP_PROXY")) != NULL) { 35037608Sdes if ((q = strchr(p, ':')) != NULL) { 35160188Sdes if (strspn(q+1, "0123456789") != strlen(q+1) || strlen(q+1) > 5) { 35260188Sdes /* XXX we should emit some kind of warning */ 35360188Sdes } 35437608Sdes pp = atoi(q+1); 35560188Sdes if (pp < 1 || pp > 65535) { 35660188Sdes /* XXX we should emit some kind of warning */ 35760188Sdes } 35837608Sdes } 35960188Sdes if (!pp) { 36060188Sdes struct servent *se; 36160188Sdes 36260188Sdes if ((se = getservbyname("ftp", "tcp")) != NULL) 36360188Sdes pp = ntohs(se->s_port); 36460188Sdes else 36560188Sdes pp = FTP_DEFAULT_PORT; 36660188Sdes } 36737608Sdes if (q) 36837608Sdes *q = 0; 36955557Sdes cd = _fetch_connect(p, pp, verbose); 37037608Sdes if (q) 37137608Sdes *q = ':'; 37237608Sdes } else { 37337608Sdes /* no proxy, go straight to target */ 37455557Sdes cd = _fetch_connect(host, port, verbose); 37555544Sdes p = NULL; 37637608Sdes } 37737608Sdes 37837608Sdes /* check connection */ 37955557Sdes if (cd == -1) { 38040939Sdes _fetch_syserr(); 38137571Sdes return NULL; 38237571Sdes } 38337608Sdes 38437571Sdes /* expect welcome message */ 38555557Sdes if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY) 38637571Sdes goto fouch; 38737571Sdes 38837571Sdes /* send user name and password */ 38937608Sdes if (!user || !*user) 39037608Sdes user = FTP_ANONYMOUS_USER; 39155557Sdes e = p ? _ftp_cmd(cd, "USER %s@%s@%d", user, host, port) 39255557Sdes : _ftp_cmd(cd, "USER %s", user); 39337608Sdes 39437608Sdes /* did the server request a password? */ 39537608Sdes if (e == FTP_NEED_PASSWORD) { 39637608Sdes if (!pwd || !*pwd) 39737608Sdes pwd = FTP_ANONYMOUS_PASSWORD; 39855557Sdes e = _ftp_cmd(cd, "PASS %s", pwd); 39937608Sdes } 40037608Sdes 40137608Sdes /* did the server request an account? */ 40241869Sdes if (e == FTP_NEED_ACCOUNT) 40341863Sdes goto fouch; 40437608Sdes 40537608Sdes /* we should be done by now */ 40641869Sdes if (e != FTP_LOGGED_IN) 40737571Sdes goto fouch; 40837571Sdes 40937571Sdes /* might as well select mode and type at once */ 41037571Sdes#ifdef FTP_FORCE_STREAM_MODE 41155557Sdes if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */ 41241869Sdes goto fouch; 41337571Sdes#endif 41455557Sdes if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */ 41541869Sdes goto fouch; 41637571Sdes 41737571Sdes /* done */ 41855557Sdes return cd; 41937571Sdes 42037571Sdesfouch: 42155557Sdes if (e != -1) 42255557Sdes _ftp_seterr(e); 42355557Sdes close(cd); 42437571Sdes return NULL; 42537571Sdes} 42637571Sdes 42737571Sdes/* 42837571Sdes * Disconnect from server 42937571Sdes */ 43037571Sdesstatic void 43155557Sdes_ftp_disconnect(int cd) 43237571Sdes{ 43355557Sdes (void)_ftp_cmd(cd, "QUIT"); 43455557Sdes close(cd); 43537571Sdes} 43637571Sdes 43737571Sdes/* 43837571Sdes * Check if we're already connected 43937571Sdes */ 44037571Sdesstatic int 44140975Sdes_ftp_isconnected(struct url *url) 44237571Sdes{ 44337571Sdes return (cached_socket 44437571Sdes && (strcmp(url->host, cached_host.host) == 0) 44537571Sdes && (strcmp(url->user, cached_host.user) == 0) 44637571Sdes && (strcmp(url->pwd, cached_host.pwd) == 0) 44737571Sdes && (url->port == cached_host.port)); 44837571Sdes} 44937571Sdes 45037608Sdes/* 45141869Sdes * Check the cache, reconnect if no luck 45237608Sdes */ 45355557Sdesstatic int 45441869Sdes_ftp_cached_connect(struct url *url, char *flags) 45537535Sdes{ 45655557Sdes int e, cd; 45737535Sdes 45855557Sdes cd = -1; 45941869Sdes 46037571Sdes /* set default port */ 46160188Sdes if (!url->port) { 46260188Sdes struct servent *se; 46360188Sdes 46460188Sdes if ((se = getservbyname("ftp", "tcp")) != NULL) 46560188Sdes url->port = ntohs(se->s_port); 46660188Sdes else 46760188Sdes url->port = FTP_DEFAULT_PORT; 46860188Sdes } 46937535Sdes 47041863Sdes /* try to use previously cached connection */ 47155557Sdes if (_ftp_isconnected(url)) { 47255557Sdes e = _ftp_cmd(cached_socket, "NOOP"); 47355557Sdes if (e == FTP_OK || e == FTP_SYNTAX_ERROR) 47455557Sdes cd = cached_socket; 47555557Sdes } 47637571Sdes 47737571Sdes /* connect to server */ 47855557Sdes if (cd == -1) { 47955557Sdes cd = _ftp_connect(url->host, url->port, url->user, url->pwd, flags); 48055557Sdes if (cd == -1) 48155557Sdes return -1; 48237571Sdes if (cached_socket) 48337571Sdes _ftp_disconnect(cached_socket); 48455557Sdes cached_socket = cd; 48560188Sdes memcpy(&cached_host, url, sizeof *url); 48637535Sdes } 48737571Sdes 48855557Sdes return cd; 48937535Sdes} 49037535Sdes 49137571Sdes/* 49241869Sdes * Get file 49337571Sdes */ 49437535SdesFILE * 49540975SdesfetchGetFTP(struct url *url, char *flags) 49637608Sdes{ 49755557Sdes int cd; 49855557Sdes 49941869Sdes /* connect to server */ 50055557Sdes if ((cd = _ftp_cached_connect(url, flags)) == NULL) 50141869Sdes return NULL; 50241869Sdes 50341869Sdes /* initiate the transfer */ 50460188Sdes return _ftp_transfer(cd, "RETR", url->doc, "r", url->offset, flags); 50537608Sdes} 50637608Sdes 50741869Sdes/* 50841869Sdes * Put file 50941869Sdes */ 51037608SdesFILE * 51140975SdesfetchPutFTP(struct url *url, char *flags) 51237535Sdes{ 51355557Sdes int cd; 51441869Sdes 51541869Sdes /* connect to server */ 51655557Sdes if ((cd = _ftp_cached_connect(url, flags)) == NULL) 51741869Sdes return NULL; 51841869Sdes 51941869Sdes /* initiate the transfer */ 52055557Sdes return _ftp_transfer(cd, (flags && strchr(flags, 'a')) ? "APPE" : "STOR", 52160188Sdes url->doc, "w", url->offset, flags); 52237535Sdes} 52340975Sdes 52441869Sdes/* 52541869Sdes * Get file stats 52641869Sdes */ 52740975Sdesint 52840975SdesfetchStatFTP(struct url *url, struct url_stat *us, char *flags) 52940975Sdes{ 53041869Sdes char *ln, *s; 53141869Sdes struct tm tm; 53241869Sdes time_t t; 53355557Sdes int e, cd; 53441869Sdes 53541869Sdes /* connect to server */ 53655557Sdes if ((cd = _ftp_cached_connect(url, flags)) == NULL) 53741869Sdes return -1; 53841869Sdes 53941869Sdes /* change directory */ 54041869Sdes if (((s = strrchr(url->doc, '/')) != NULL) && (s != url->doc)) { 54141869Sdes *s = 0; 54255557Sdes if ((e = _ftp_cmd(cd, "CWD %s", url->doc)) != FTP_FILE_ACTION_OK) { 54341869Sdes *s = '/'; 54441869Sdes goto ouch; 54541869Sdes } 54641869Sdes *s++ = '/'; 54741869Sdes } else { 54855557Sdes if ((e = _ftp_cmd(cd, "CWD /")) != FTP_FILE_ACTION_OK) 54941869Sdes goto ouch; 55041869Sdes } 55141869Sdes 55241869Sdes /* s now points to file name */ 55341869Sdes 55455557Sdes if (_ftp_cmd(cd, "SIZE %s", s) != FTP_FILE_STATUS) 55541869Sdes goto ouch; 55655557Sdes for (ln = last_reply + 4; *ln && isspace(*ln); ln++) 55741869Sdes /* nothing */ ; 55841869Sdes for (us->size = 0; *ln && isdigit(*ln); ln++) 55941869Sdes us->size = us->size * 10 + *ln - '0'; 56041869Sdes if (*ln && !isspace(*ln)) { 56155557Sdes _ftp_seterr(999); 56241869Sdes return -1; 56341869Sdes } 56460383Sdes DEBUG(fprintf(stderr, "size: [\033[1m%lld\033[m]\n", us->size)); 56541869Sdes 56655557Sdes if ((e = _ftp_cmd(cd, "MDTM %s", s)) != FTP_FILE_STATUS) 56741869Sdes goto ouch; 56855557Sdes for (ln = last_reply + 4; *ln && isspace(*ln); ln++) 56941869Sdes /* nothing */ ; 57060383Sdes e = 999; 57160383Sdes switch (strspn(ln, "0123456789")) { 57260383Sdes case 14: 57360383Sdes break; 57460383Sdes case 15: 57560383Sdes ln++; 57660383Sdes ln[0] = '2'; 57760383Sdes ln[1] = '0'; 57860383Sdes break; 57960383Sdes default: 58060383Sdes goto ouch; 58160383Sdes } 58260383Sdes if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", 58360383Sdes &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 58460383Sdes &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) 58560383Sdes goto ouch; 58641869Sdes tm.tm_mon--; 58741869Sdes tm.tm_year -= 1900; 58841869Sdes tm.tm_isdst = -1; 58960383Sdes t = timegm(&tm); 59056635Sdes if (t == (time_t)-1) 59156635Sdes t = time(NULL); 59256635Sdes us->mtime = t; 59356635Sdes us->atime = t; 59460383Sdes DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d " 59560383Sdes "%02d:%02d:%02d\033[m]\n", 59660383Sdes tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 59760383Sdes tm.tm_hour, tm.tm_min, tm.tm_sec)); 59841869Sdes return 0; 59941869Sdes 60041869Sdesouch: 60155557Sdes if (e != -1) 60255557Sdes _ftp_seterr(e); 60340975Sdes return -1; 60440975Sdes} 60541989Sdes 60641989Sdes/* 60741989Sdes * List a directory 60841989Sdes */ 60941989Sdesextern void warnx(char *, ...); 61041989Sdesstruct url_ent * 61141989SdesfetchListFTP(struct url *url, char *flags) 61241989Sdes{ 61341989Sdes warnx("fetchListFTP(): not implemented"); 61441989Sdes return NULL; 61541989Sdes} 616