xref: /freebsd/lib/libfetch/ftp.c (revision 102a87c1e16893ad87af7ee0a6643236814094f6)
14ca1ab94SDag-Erling Smørgrav /*-
24ca1ab94SDag-Erling Smørgrav  * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav
34ca1ab94SDag-Erling Smørgrav  * All rights reserved.
44ca1ab94SDag-Erling Smørgrav  *
54ca1ab94SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
64ca1ab94SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
74ca1ab94SDag-Erling Smørgrav  * are met:
84ca1ab94SDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
94ca1ab94SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer
104ca1ab94SDag-Erling Smørgrav  *    in this position and unchanged.
114ca1ab94SDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
124ca1ab94SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
134ca1ab94SDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
144ca1ab94SDag-Erling Smørgrav  * 3. The name of the author may not be used to endorse or promote products
154ca1ab94SDag-Erling Smørgrav  *    derived from this software without specific prior written permission
164ca1ab94SDag-Erling Smørgrav  *
174ca1ab94SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
184ca1ab94SDag-Erling Smørgrav  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
194ca1ab94SDag-Erling Smørgrav  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
204ca1ab94SDag-Erling Smørgrav  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
214ca1ab94SDag-Erling Smørgrav  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
224ca1ab94SDag-Erling Smørgrav  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
234ca1ab94SDag-Erling Smørgrav  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
244ca1ab94SDag-Erling Smørgrav  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
254ca1ab94SDag-Erling Smørgrav  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
264ca1ab94SDag-Erling Smørgrav  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
274ca1ab94SDag-Erling Smørgrav  */
284ca1ab94SDag-Erling Smørgrav 
29cecb889fSMatthew Dillon #include <sys/cdefs.h>
30cecb889fSMatthew Dillon __FBSDID("$FreeBSD$");
31cecb889fSMatthew Dillon 
324ca1ab94SDag-Erling Smørgrav /*
338e3986eaSDag-Erling Smørgrav  * Portions of this code were taken from or based on ftpio.c:
344ca1ab94SDag-Erling Smørgrav  *
354ca1ab94SDag-Erling Smørgrav  * ----------------------------------------------------------------------------
364ca1ab94SDag-Erling Smørgrav  * "THE BEER-WARE LICENSE" (Revision 42):
374ca1ab94SDag-Erling Smørgrav  * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
384ca1ab94SDag-Erling Smørgrav  * can do whatever you want with this stuff. If we meet some day, and you think
394ca1ab94SDag-Erling Smørgrav  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
404ca1ab94SDag-Erling Smørgrav  * ----------------------------------------------------------------------------
414ca1ab94SDag-Erling Smørgrav  *
424ca1ab94SDag-Erling Smørgrav  * Major Changelog:
434ca1ab94SDag-Erling Smørgrav  *
444ca1ab94SDag-Erling Smørgrav  * Dag-Erling Co�dan Sm�rgrav
454ca1ab94SDag-Erling Smørgrav  * 9 Jun 1998
464ca1ab94SDag-Erling Smørgrav  *
474ca1ab94SDag-Erling Smørgrav  * Incorporated into libfetch
484ca1ab94SDag-Erling Smørgrav  *
494ca1ab94SDag-Erling Smørgrav  * Jordan K. Hubbard
504ca1ab94SDag-Erling Smørgrav  * 17 Jan 1996
514ca1ab94SDag-Erling Smørgrav  *
524ca1ab94SDag-Erling Smørgrav  * Turned inside out. Now returns xfers as new file ids, not as a special
534ca1ab94SDag-Erling Smørgrav  * `state' of FTP_t
544ca1ab94SDag-Erling Smørgrav  *
554ca1ab94SDag-Erling Smørgrav  * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $
564ca1ab94SDag-Erling Smørgrav  *
574ca1ab94SDag-Erling Smørgrav  */
584ca1ab94SDag-Erling Smørgrav 
590fba3a00SDag-Erling Smørgrav #include <sys/param.h>
604ca1ab94SDag-Erling Smørgrav #include <sys/socket.h>
614ca1ab94SDag-Erling Smørgrav #include <netinet/in.h>
624ca1ab94SDag-Erling Smørgrav 
634ca1ab94SDag-Erling Smørgrav #include <ctype.h>
6438c7e4a6SArchie Cobbs #include <err.h>
65fc6e9e65SDag-Erling Smørgrav #include <errno.h>
66c7d40ef2SDag-Erling Smørgrav #include <fcntl.h>
6732425dafSDag-Erling Smørgrav #include <netdb.h>
68346298f0SDag-Erling Smørgrav #include <stdarg.h>
694ca1ab94SDag-Erling Smørgrav #include <stdio.h>
708e3986eaSDag-Erling Smørgrav #include <stdlib.h>
714ca1ab94SDag-Erling Smørgrav #include <string.h>
725aea254fSDag-Erling Smørgrav #include <time.h>
738e3986eaSDag-Erling Smørgrav #include <unistd.h>
744ca1ab94SDag-Erling Smørgrav 
754ca1ab94SDag-Erling Smørgrav #include "fetch.h"
76842a95ccSDag-Erling Smørgrav #include "common.h"
770fba3a00SDag-Erling Smørgrav #include "ftperr.h"
784ca1ab94SDag-Erling Smørgrav 
7980ed165eSDag-Erling Smørgrav #define FTP_ANONYMOUS_USER	"anonymous"
80346298f0SDag-Erling Smørgrav 
81ea014d85SDag-Erling Smørgrav #define FTP_CONNECTION_ALREADY_OPEN	125
82346298f0SDag-Erling Smørgrav #define FTP_OPEN_DATA_CONNECTION	150
83346298f0SDag-Erling Smørgrav #define FTP_OK				200
845aea254fSDag-Erling Smørgrav #define FTP_FILE_STATUS			213
853b7a6740SDag-Erling Smørgrav #define FTP_SERVICE_READY		220
8623109751SDag-Erling Smørgrav #define FTP_TRANSFER_COMPLETE		226
87346298f0SDag-Erling Smørgrav #define FTP_PASSIVE_MODE		227
8828c645cfSHajimu UMEMOTO #define FTP_LPASSIVE_MODE		228
8928c645cfSHajimu UMEMOTO #define FTP_EPASSIVE_MODE		229
90346298f0SDag-Erling Smørgrav #define FTP_LOGGED_IN			230
91346298f0SDag-Erling Smørgrav #define FTP_FILE_ACTION_OK		250
92346298f0SDag-Erling Smørgrav #define FTP_NEED_PASSWORD		331
93346298f0SDag-Erling Smørgrav #define FTP_NEED_ACCOUNT		332
9432425dafSDag-Erling Smørgrav #define FTP_FILE_OK			350
95fc6e9e65SDag-Erling Smørgrav #define FTP_SYNTAX_ERROR		500
965cd33c40SDag-Erling Smørgrav #define FTP_PROTOCOL_ERROR		999
974ca1ab94SDag-Erling Smørgrav 
98d8acd8dcSDag-Erling Smørgrav static struct url cached_host;
99fc6e9e65SDag-Erling Smørgrav static int cached_socket;
1004ca1ab94SDag-Erling Smørgrav 
101fc6e9e65SDag-Erling Smørgrav static char *last_reply;
102fc6e9e65SDag-Erling Smørgrav static size_t lr_size, lr_length;
103fc6e9e65SDag-Erling Smørgrav static int last_code;
104fc6e9e65SDag-Erling Smørgrav 
105fc6e9e65SDag-Erling Smørgrav #define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
1066efb30c8SDag-Erling Smørgrav 			 && isdigit(foo[2]) \
1076efb30c8SDag-Erling Smørgrav                          && (foo[3] == ' ' || foo[3] == '\0'))
108fc6e9e65SDag-Erling Smørgrav #define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
109fc6e9e65SDag-Erling Smørgrav 			&& isdigit(foo[2]) && foo[3] == '-')
1104ca1ab94SDag-Erling Smørgrav 
11128c645cfSHajimu UMEMOTO /* translate IPv4 mapped IPv6 address to IPv4 address */
11228c645cfSHajimu UMEMOTO static void
11328c645cfSHajimu UMEMOTO unmappedaddr(struct sockaddr_in6 *sin6)
11428c645cfSHajimu UMEMOTO {
11528c645cfSHajimu UMEMOTO     struct sockaddr_in *sin4;
11628c645cfSHajimu UMEMOTO     u_int32_t addr;
11728c645cfSHajimu UMEMOTO     int port;
11828c645cfSHajimu UMEMOTO 
11928c645cfSHajimu UMEMOTO     if (sin6->sin6_family != AF_INET6 ||
12028c645cfSHajimu UMEMOTO 	!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
12128c645cfSHajimu UMEMOTO 	return;
12228c645cfSHajimu UMEMOTO     sin4 = (struct sockaddr_in *)sin6;
12328c645cfSHajimu UMEMOTO     addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
12428c645cfSHajimu UMEMOTO     port = sin6->sin6_port;
12528c645cfSHajimu UMEMOTO     memset(sin4, 0, sizeof(struct sockaddr_in));
12628c645cfSHajimu UMEMOTO     sin4->sin_addr.s_addr = addr;
12728c645cfSHajimu UMEMOTO     sin4->sin_port = port;
12828c645cfSHajimu UMEMOTO     sin4->sin_family = AF_INET;
12928c645cfSHajimu UMEMOTO     sin4->sin_len = sizeof(struct sockaddr_in);
13028c645cfSHajimu UMEMOTO }
13128c645cfSHajimu UMEMOTO 
1324ca1ab94SDag-Erling Smørgrav /*
133fc6e9e65SDag-Erling Smørgrav  * Get server response
1348e3986eaSDag-Erling Smørgrav  */
1358e3986eaSDag-Erling Smørgrav static int
136fc6e9e65SDag-Erling Smørgrav _ftp_chkerr(int cd)
1378e3986eaSDag-Erling Smørgrav {
138fc6e9e65SDag-Erling Smørgrav     if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) {
139842a95ccSDag-Erling Smørgrav 	_fetch_syserr();
1408e3986eaSDag-Erling Smørgrav 	return -1;
1418e3986eaSDag-Erling Smørgrav     }
142eac7a1e0SDag-Erling Smørgrav     if (isftpinfo(last_reply)) {
143d41c0df8SDag-Erling Smørgrav 	while (lr_length && !isftpreply(last_reply)) {
14404a80993SDag-Erling Smørgrav 	    if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) {
145eac7a1e0SDag-Erling Smørgrav 		_fetch_syserr();
146eac7a1e0SDag-Erling Smørgrav 		return -1;
147eac7a1e0SDag-Erling Smørgrav 	    }
148eac7a1e0SDag-Erling Smørgrav 	}
149eac7a1e0SDag-Erling Smørgrav     }
150346298f0SDag-Erling Smørgrav 
151fc6e9e65SDag-Erling Smørgrav     while (lr_length && isspace(last_reply[lr_length-1]))
152fc6e9e65SDag-Erling Smørgrav 	lr_length--;
153fc6e9e65SDag-Erling Smørgrav     last_reply[lr_length] = 0;
154fc6e9e65SDag-Erling Smørgrav 
155fc6e9e65SDag-Erling Smørgrav     if (!isftpreply(last_reply)) {
1565cd33c40SDag-Erling Smørgrav 	_ftp_seterr(FTP_PROTOCOL_ERROR);
1578e3986eaSDag-Erling Smørgrav 	return -1;
1588e3986eaSDag-Erling Smørgrav     }
1598e3986eaSDag-Erling Smørgrav 
160fc6e9e65SDag-Erling Smørgrav     last_code = (last_reply[0] - '0') * 100
161fc6e9e65SDag-Erling Smørgrav 	+ (last_reply[1] - '0') * 10
162fc6e9e65SDag-Erling Smørgrav 	+ (last_reply[2] - '0');
163fc6e9e65SDag-Erling Smørgrav 
164fc6e9e65SDag-Erling Smørgrav     return last_code;
1658e3986eaSDag-Erling Smørgrav }
1668e3986eaSDag-Erling Smørgrav 
1678e3986eaSDag-Erling Smørgrav /*
168346298f0SDag-Erling Smørgrav  * Send a command and check reply
1694ca1ab94SDag-Erling Smørgrav  */
1704ca1ab94SDag-Erling Smørgrav static int
17138c7e4a6SArchie Cobbs _ftp_cmd(int cd, const char *fmt, ...)
1724ca1ab94SDag-Erling Smørgrav {
173346298f0SDag-Erling Smørgrav     va_list ap;
174e137bcebSDag-Erling Smørgrav     size_t len;
175fc6e9e65SDag-Erling Smørgrav     char *msg;
176fc6e9e65SDag-Erling Smørgrav     int r;
1778e3986eaSDag-Erling Smørgrav 
178346298f0SDag-Erling Smørgrav     va_start(ap, fmt);
179e137bcebSDag-Erling Smørgrav     len = vasprintf(&msg, fmt, ap);
180346298f0SDag-Erling Smørgrav     va_end(ap);
181346298f0SDag-Erling Smørgrav 
182fc6e9e65SDag-Erling Smørgrav     if (msg == NULL) {
183fc6e9e65SDag-Erling Smørgrav 	errno = ENOMEM;
184fc6e9e65SDag-Erling Smørgrav 	_fetch_syserr();
185fc6e9e65SDag-Erling Smørgrav 	return -1;
186fc6e9e65SDag-Erling Smørgrav     }
187e137bcebSDag-Erling Smørgrav 
188e137bcebSDag-Erling Smørgrav     r = _fetch_putln(cd, msg, len);
189fc6e9e65SDag-Erling Smørgrav     free(msg);
190e137bcebSDag-Erling Smørgrav 
191fc6e9e65SDag-Erling Smørgrav     if (r == -1) {
192fc6e9e65SDag-Erling Smørgrav 	_fetch_syserr();
193fc6e9e65SDag-Erling Smørgrav 	return -1;
194fc6e9e65SDag-Erling Smørgrav     }
195fc6e9e65SDag-Erling Smørgrav 
196fc6e9e65SDag-Erling Smørgrav     return _ftp_chkerr(cd);
1974ca1ab94SDag-Erling Smørgrav }
1984ca1ab94SDag-Erling Smørgrav 
1994ca1ab94SDag-Erling Smørgrav /*
2001a5faa10SDag-Erling Smørgrav  * Return a pointer to the filename part of a path
2011a5faa10SDag-Erling Smørgrav  */
20238c7e4a6SArchie Cobbs static const char *
20338c7e4a6SArchie Cobbs _ftp_filename(const char *file)
2041a5faa10SDag-Erling Smørgrav {
2051a5faa10SDag-Erling Smørgrav     char *s;
2061a5faa10SDag-Erling Smørgrav 
2071a5faa10SDag-Erling Smørgrav     if ((s = strrchr(file, '/')) == NULL)
2081a5faa10SDag-Erling Smørgrav 	return file;
2091a5faa10SDag-Erling Smørgrav     else
2101a5faa10SDag-Erling Smørgrav 	return s + 1;
2111a5faa10SDag-Erling Smørgrav }
2121a5faa10SDag-Erling Smørgrav 
2131a5faa10SDag-Erling Smørgrav /*
2141a5faa10SDag-Erling Smørgrav  * Change working directory to the directory that contains the
2151a5faa10SDag-Erling Smørgrav  * specified file.
2161a5faa10SDag-Erling Smørgrav  */
2171a5faa10SDag-Erling Smørgrav static int
21838c7e4a6SArchie Cobbs _ftp_cwd(int cd, const char *file)
2191a5faa10SDag-Erling Smørgrav {
2201a5faa10SDag-Erling Smørgrav     char *s;
2211a5faa10SDag-Erling Smørgrav     int e;
2221a5faa10SDag-Erling Smørgrav 
2235e3f46b5SDag-Erling Smørgrav     if ((s = strrchr(file, '/')) == NULL || s == file) {
2241a5faa10SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "CWD /");
2251a5faa10SDag-Erling Smørgrav     } else {
2261a5faa10SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "CWD %.*s", s - file, file);
2271a5faa10SDag-Erling Smørgrav     }
2281a5faa10SDag-Erling Smørgrav     if (e != FTP_FILE_ACTION_OK) {
2291a5faa10SDag-Erling Smørgrav 	_ftp_seterr(e);
2301a5faa10SDag-Erling Smørgrav 	return -1;
2311a5faa10SDag-Erling Smørgrav     }
2321a5faa10SDag-Erling Smørgrav     return 0;
2331a5faa10SDag-Erling Smørgrav }
2341a5faa10SDag-Erling Smørgrav 
2351a5faa10SDag-Erling Smørgrav /*
2361a5faa10SDag-Erling Smørgrav  * Request and parse file stats
2371a5faa10SDag-Erling Smørgrav  */
2381a5faa10SDag-Erling Smørgrav static int
23938c7e4a6SArchie Cobbs _ftp_stat(int cd, const char *file, struct url_stat *us)
2401a5faa10SDag-Erling Smørgrav {
24138c7e4a6SArchie Cobbs     char *ln;
24238c7e4a6SArchie Cobbs     const char *s;
2431a5faa10SDag-Erling Smørgrav     struct tm tm;
2441a5faa10SDag-Erling Smørgrav     time_t t;
2451a5faa10SDag-Erling Smørgrav     int e;
2461a5faa10SDag-Erling Smørgrav 
247269532d9SDag-Erling Smørgrav     us->size = -1;
248269532d9SDag-Erling Smørgrav     us->atime = us->mtime = 0;
249269532d9SDag-Erling Smørgrav 
2501a5faa10SDag-Erling Smørgrav     if ((s = strrchr(file, '/')) == NULL)
2511a5faa10SDag-Erling Smørgrav 	s = file;
2521a5faa10SDag-Erling Smørgrav     else
2531a5faa10SDag-Erling Smørgrav 	++s;
2541a5faa10SDag-Erling Smørgrav 
2551a5faa10SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "SIZE %s", s)) != FTP_FILE_STATUS) {
2561a5faa10SDag-Erling Smørgrav 	_ftp_seterr(e);
2571a5faa10SDag-Erling Smørgrav 	return -1;
2581a5faa10SDag-Erling Smørgrav     }
2591a5faa10SDag-Erling Smørgrav     for (ln = last_reply + 4; *ln && isspace(*ln); ln++)
2601a5faa10SDag-Erling Smørgrav 	/* nothing */ ;
2611a5faa10SDag-Erling Smørgrav     for (us->size = 0; *ln && isdigit(*ln); ln++)
2621a5faa10SDag-Erling Smørgrav 	us->size = us->size * 10 + *ln - '0';
2631a5faa10SDag-Erling Smørgrav     if (*ln && !isspace(*ln)) {
2641a5faa10SDag-Erling Smørgrav 	_ftp_seterr(FTP_PROTOCOL_ERROR);
265525be862SDag-Erling Smørgrav 	us->size = -1;
2661a5faa10SDag-Erling Smørgrav 	return -1;
2671a5faa10SDag-Erling Smørgrav     }
26863428824SDag-Erling Smørgrav     if (us->size == 0)
26963428824SDag-Erling Smørgrav 	us->size = -1;
270f573a5fcSDag-Erling Smørgrav     DEBUG(fprintf(stderr, "size: [\033[1m%lld\033[m]\n", (long long)us->size));
2711a5faa10SDag-Erling Smørgrav 
2721a5faa10SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "MDTM %s", s)) != FTP_FILE_STATUS) {
2731a5faa10SDag-Erling Smørgrav 	_ftp_seterr(e);
2741a5faa10SDag-Erling Smørgrav 	return -1;
2751a5faa10SDag-Erling Smørgrav     }
2761a5faa10SDag-Erling Smørgrav     for (ln = last_reply + 4; *ln && isspace(*ln); ln++)
2771a5faa10SDag-Erling Smørgrav 	/* nothing */ ;
2781a5faa10SDag-Erling Smørgrav     switch (strspn(ln, "0123456789")) {
2791a5faa10SDag-Erling Smørgrav     case 14:
2801a5faa10SDag-Erling Smørgrav 	break;
2811a5faa10SDag-Erling Smørgrav     case 15:
2821a5faa10SDag-Erling Smørgrav 	ln++;
2831a5faa10SDag-Erling Smørgrav 	ln[0] = '2';
2841a5faa10SDag-Erling Smørgrav 	ln[1] = '0';
2851a5faa10SDag-Erling Smørgrav 	break;
2861a5faa10SDag-Erling Smørgrav     default:
2871a5faa10SDag-Erling Smørgrav 	_ftp_seterr(FTP_PROTOCOL_ERROR);
2881a5faa10SDag-Erling Smørgrav 	return -1;
2891a5faa10SDag-Erling Smørgrav     }
2901a5faa10SDag-Erling Smørgrav     if (sscanf(ln, "%04d%02d%02d%02d%02d%02d",
2911a5faa10SDag-Erling Smørgrav 	       &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2921a5faa10SDag-Erling Smørgrav 	       &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2931a5faa10SDag-Erling Smørgrav 	_ftp_seterr(FTP_PROTOCOL_ERROR);
2941a5faa10SDag-Erling Smørgrav 	return -1;
2951a5faa10SDag-Erling Smørgrav     }
2961a5faa10SDag-Erling Smørgrav     tm.tm_mon--;
2971a5faa10SDag-Erling Smørgrav     tm.tm_year -= 1900;
2981a5faa10SDag-Erling Smørgrav     tm.tm_isdst = -1;
2991a5faa10SDag-Erling Smørgrav     t = timegm(&tm);
3001a5faa10SDag-Erling Smørgrav     if (t == (time_t)-1)
3011a5faa10SDag-Erling Smørgrav 	t = time(NULL);
3021a5faa10SDag-Erling Smørgrav     us->mtime = t;
3031a5faa10SDag-Erling Smørgrav     us->atime = t;
3041a5faa10SDag-Erling Smørgrav     DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d "
3051a5faa10SDag-Erling Smørgrav 		  "%02d:%02d:%02d\033[m]\n",
3061a5faa10SDag-Erling Smørgrav 		  tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
3071a5faa10SDag-Erling Smørgrav 		  tm.tm_hour, tm.tm_min, tm.tm_sec));
3081a5faa10SDag-Erling Smørgrav     return 0;
3091a5faa10SDag-Erling Smørgrav }
3101a5faa10SDag-Erling Smørgrav 
3111a5faa10SDag-Erling Smørgrav /*
312c7d40ef2SDag-Erling Smørgrav  * I/O functions for FTP
313c7d40ef2SDag-Erling Smørgrav  */
314c7d40ef2SDag-Erling Smørgrav struct ftpio {
315c7d40ef2SDag-Erling Smørgrav     int		 csd;		/* Control socket descriptor */
316c7d40ef2SDag-Erling Smørgrav     int		 dsd;		/* Data socket descriptor */
317c7d40ef2SDag-Erling Smørgrav     int		 dir;		/* Direction */
318c7d40ef2SDag-Erling Smørgrav     int		 eof;		/* EOF reached */
319c7d40ef2SDag-Erling Smørgrav     int		 err;		/* Error code */
320c7d40ef2SDag-Erling Smørgrav };
321c7d40ef2SDag-Erling Smørgrav 
322c7d40ef2SDag-Erling Smørgrav static int	_ftp_readfn(void *, char *, int);
323c7d40ef2SDag-Erling Smørgrav static int	_ftp_writefn(void *, const char *, int);
324c7d40ef2SDag-Erling Smørgrav static fpos_t	_ftp_seekfn(void *, fpos_t, int);
325c7d40ef2SDag-Erling Smørgrav static int	_ftp_closefn(void *);
326c7d40ef2SDag-Erling Smørgrav 
327c7d40ef2SDag-Erling Smørgrav static int
328c7d40ef2SDag-Erling Smørgrav _ftp_readfn(void *v, char *buf, int len)
329c7d40ef2SDag-Erling Smørgrav {
330c7d40ef2SDag-Erling Smørgrav     struct ftpio *io;
331c7d40ef2SDag-Erling Smørgrav     int r;
332c7d40ef2SDag-Erling Smørgrav 
333c7d40ef2SDag-Erling Smørgrav     io = (struct ftpio *)v;
33423109751SDag-Erling Smørgrav     if (io == NULL) {
33523109751SDag-Erling Smørgrav 	errno = EBADF;
33623109751SDag-Erling Smørgrav 	return -1;
33723109751SDag-Erling Smørgrav     }
338c7d40ef2SDag-Erling Smørgrav     if (io->csd == -1 || io->dsd == -1 || io->dir == O_WRONLY) {
339c7d40ef2SDag-Erling Smørgrav 	errno = EBADF;
340c7d40ef2SDag-Erling Smørgrav 	return -1;
341c7d40ef2SDag-Erling Smørgrav     }
342c7d40ef2SDag-Erling Smørgrav     if (io->err) {
343c7d40ef2SDag-Erling Smørgrav 	errno = io->err;
344c7d40ef2SDag-Erling Smørgrav 	return -1;
345c7d40ef2SDag-Erling Smørgrav     }
346c7d40ef2SDag-Erling Smørgrav     if (io->eof)
347c7d40ef2SDag-Erling Smørgrav 	return 0;
348c7d40ef2SDag-Erling Smørgrav     r = read(io->dsd, buf, len);
349c7d40ef2SDag-Erling Smørgrav     if (r > 0)
350c7d40ef2SDag-Erling Smørgrav 	return r;
351c7d40ef2SDag-Erling Smørgrav     if (r == 0) {
352c7d40ef2SDag-Erling Smørgrav 	io->eof = 1;
3539e2a792cSDag-Erling Smørgrav 	return 0;
354c7d40ef2SDag-Erling Smørgrav     }
355e238d2a8SDag-Erling Smørgrav     if (errno != EINTR)
356c7d40ef2SDag-Erling Smørgrav 	io->err = errno;
357c7d40ef2SDag-Erling Smørgrav     return -1;
358c7d40ef2SDag-Erling Smørgrav }
359c7d40ef2SDag-Erling Smørgrav 
360c7d40ef2SDag-Erling Smørgrav static int
361c7d40ef2SDag-Erling Smørgrav _ftp_writefn(void *v, const char *buf, int len)
362c7d40ef2SDag-Erling Smørgrav {
363c7d40ef2SDag-Erling Smørgrav     struct ftpio *io;
364c7d40ef2SDag-Erling Smørgrav     int w;
365c7d40ef2SDag-Erling Smørgrav 
366c7d40ef2SDag-Erling Smørgrav     io = (struct ftpio *)v;
36723109751SDag-Erling Smørgrav     if (io == NULL) {
36823109751SDag-Erling Smørgrav 	errno = EBADF;
36923109751SDag-Erling Smørgrav 	return -1;
37023109751SDag-Erling Smørgrav     }
371c7d40ef2SDag-Erling Smørgrav     if (io->csd == -1 || io->dsd == -1 || io->dir == O_RDONLY) {
372c7d40ef2SDag-Erling Smørgrav 	errno = EBADF;
373c7d40ef2SDag-Erling Smørgrav 	return -1;
374c7d40ef2SDag-Erling Smørgrav     }
375c7d40ef2SDag-Erling Smørgrav     if (io->err) {
376c7d40ef2SDag-Erling Smørgrav 	errno = io->err;
377c7d40ef2SDag-Erling Smørgrav 	return -1;
378c7d40ef2SDag-Erling Smørgrav     }
379c7d40ef2SDag-Erling Smørgrav     w = write(io->dsd, buf, len);
380c7d40ef2SDag-Erling Smørgrav     if (w >= 0)
381c7d40ef2SDag-Erling Smørgrav 	return w;
382e238d2a8SDag-Erling Smørgrav     if (errno != EINTR)
383c7d40ef2SDag-Erling Smørgrav 	io->err = errno;
384c7d40ef2SDag-Erling Smørgrav     return -1;
385c7d40ef2SDag-Erling Smørgrav }
386c7d40ef2SDag-Erling Smørgrav 
387c7d40ef2SDag-Erling Smørgrav static fpos_t
388f573a5fcSDag-Erling Smørgrav _ftp_seekfn(void *v, fpos_t pos __unused, int whence __unused)
389c7d40ef2SDag-Erling Smørgrav {
39023109751SDag-Erling Smørgrav     struct ftpio *io;
39123109751SDag-Erling Smørgrav 
39223109751SDag-Erling Smørgrav     io = (struct ftpio *)v;
39323109751SDag-Erling Smørgrav     if (io == NULL) {
39423109751SDag-Erling Smørgrav 	errno = EBADF;
39523109751SDag-Erling Smørgrav 	return -1;
39623109751SDag-Erling Smørgrav     }
397c7d40ef2SDag-Erling Smørgrav     errno = ESPIPE;
398c7d40ef2SDag-Erling Smørgrav     return -1;
399c7d40ef2SDag-Erling Smørgrav }
400c7d40ef2SDag-Erling Smørgrav 
401c7d40ef2SDag-Erling Smørgrav static int
402c7d40ef2SDag-Erling Smørgrav _ftp_closefn(void *v)
403c7d40ef2SDag-Erling Smørgrav {
404c7d40ef2SDag-Erling Smørgrav     struct ftpio *io;
40523109751SDag-Erling Smørgrav     int r;
406c7d40ef2SDag-Erling Smørgrav 
407c7d40ef2SDag-Erling Smørgrav     io = (struct ftpio *)v;
40823109751SDag-Erling Smørgrav     if (io == NULL) {
40923109751SDag-Erling Smørgrav 	errno = EBADF;
41023109751SDag-Erling Smørgrav 	return -1;
41123109751SDag-Erling Smørgrav     }
412c7d40ef2SDag-Erling Smørgrav     if (io->dir == -1)
413c7d40ef2SDag-Erling Smørgrav 	return 0;
414c7d40ef2SDag-Erling Smørgrav     if (io->csd == -1 || io->dsd == -1) {
415c7d40ef2SDag-Erling Smørgrav 	errno = EBADF;
416c7d40ef2SDag-Erling Smørgrav 	return -1;
417c7d40ef2SDag-Erling Smørgrav     }
418303fd73aSDag-Erling Smørgrav     close(io->dsd);
419c7d40ef2SDag-Erling Smørgrav     io->dir = -1;
420c7d40ef2SDag-Erling Smørgrav     io->dsd = -1;
421303fd73aSDag-Erling Smørgrav     DEBUG(fprintf(stderr, "Waiting for final status\n"));
422b554dea7SDag-Erling Smørgrav     r = _ftp_chkerr(io->csd);
423c7d40ef2SDag-Erling Smørgrav     close(io->csd);
424b554dea7SDag-Erling Smørgrav     free(io);
425b554dea7SDag-Erling Smørgrav     return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1;
426c7d40ef2SDag-Erling Smørgrav }
427c7d40ef2SDag-Erling Smørgrav 
428c7d40ef2SDag-Erling Smørgrav static FILE *
429c7d40ef2SDag-Erling Smørgrav _ftp_setup(int csd, int dsd, int mode)
430c7d40ef2SDag-Erling Smørgrav {
431c7d40ef2SDag-Erling Smørgrav     struct ftpio *io;
432c7d40ef2SDag-Erling Smørgrav     FILE *f;
433c7d40ef2SDag-Erling Smørgrav 
434c7d40ef2SDag-Erling Smørgrav     if ((io = malloc(sizeof *io)) == NULL)
435c7d40ef2SDag-Erling Smørgrav 	return NULL;
436c7d40ef2SDag-Erling Smørgrav     io->csd = dup(csd);
437c7d40ef2SDag-Erling Smørgrav     io->dsd = dsd;
438c7d40ef2SDag-Erling Smørgrav     io->dir = mode;
439c7d40ef2SDag-Erling Smørgrav     io->eof = io->err = 0;
440c7d40ef2SDag-Erling Smørgrav     f = funopen(io, _ftp_readfn, _ftp_writefn, _ftp_seekfn, _ftp_closefn);
441c7d40ef2SDag-Erling Smørgrav     if (f == NULL)
442c7d40ef2SDag-Erling Smørgrav 	free(io);
443c7d40ef2SDag-Erling Smørgrav     return f;
444c7d40ef2SDag-Erling Smørgrav }
445c7d40ef2SDag-Erling Smørgrav 
446c7d40ef2SDag-Erling Smørgrav /*
447f62e5228SDag-Erling Smørgrav  * Transfer file
4484ca1ab94SDag-Erling Smørgrav  */
4494ca1ab94SDag-Erling Smørgrav static FILE *
45038c7e4a6SArchie Cobbs _ftp_transfer(int cd, const char *oper, const char *file,
45138c7e4a6SArchie Cobbs 	      int mode, off_t offset, const char *flags)
4524ca1ab94SDag-Erling Smørgrav {
453f573a5fcSDag-Erling Smørgrav     struct sockaddr_storage sa;
45428c645cfSHajimu UMEMOTO     struct sockaddr_in6 *sin6;
45528c645cfSHajimu UMEMOTO     struct sockaddr_in *sin4;
456d5f175ceSDag-Erling Smørgrav     int low, pasv, verbose;
457f5f109a0SDag-Erling Smørgrav     int e, sd = -1;
458f5f109a0SDag-Erling Smørgrav     socklen_t l;
459346298f0SDag-Erling Smørgrav     char *s;
460346298f0SDag-Erling Smørgrav     FILE *df;
4618e3986eaSDag-Erling Smørgrav 
462f5f109a0SDag-Erling Smørgrav     /* check flags */
463d5f175ceSDag-Erling Smørgrav     low = CHECK_FLAG('l');
464d74a913bSDag-Erling Smørgrav     pasv = CHECK_FLAG('p');
465d74a913bSDag-Erling Smørgrav     verbose = CHECK_FLAG('v');
466f5f109a0SDag-Erling Smørgrav 
467d02e84a6SDag-Erling Smørgrav     /* passive mode */
4688b9ba466SDag-Erling Smørgrav     if (!pasv)
4696c81eb52SDag-Erling Smørgrav 	pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL &&
4708b9ba466SDag-Erling Smørgrav 		strncasecmp(s, "no", 2) != 0);
471d02e84a6SDag-Erling Smørgrav 
47228c645cfSHajimu UMEMOTO     /* find our own address, bind, and listen */
473f573a5fcSDag-Erling Smørgrav     l = sizeof sa;
474f573a5fcSDag-Erling Smørgrav     if (getsockname(cd, (struct sockaddr *)&sa, &l) == -1)
47528c645cfSHajimu UMEMOTO 	goto sysouch;
476f573a5fcSDag-Erling Smørgrav     if (sa.ss_family == AF_INET6)
477f573a5fcSDag-Erling Smørgrav 	unmappedaddr((struct sockaddr_in6 *)&sa);
47828c645cfSHajimu UMEMOTO 
479346298f0SDag-Erling Smørgrav     /* open data socket */
480f573a5fcSDag-Erling Smørgrav     if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
481842a95ccSDag-Erling Smørgrav 	_fetch_syserr();
482346298f0SDag-Erling Smørgrav 	return NULL;
483346298f0SDag-Erling Smørgrav     }
484346298f0SDag-Erling Smørgrav 
485346298f0SDag-Erling Smørgrav     if (pasv) {
48628c645cfSHajimu UMEMOTO 	u_char addr[64];
487346298f0SDag-Erling Smørgrav 	char *ln, *p;
488f573a5fcSDag-Erling Smørgrav 	unsigned int i;
48928c645cfSHajimu UMEMOTO 	int port;
490346298f0SDag-Erling Smørgrav 
491346298f0SDag-Erling Smørgrav 	/* send PASV command */
492f5f109a0SDag-Erling Smørgrav 	if (verbose)
493f5f109a0SDag-Erling Smørgrav 	    _fetch_info("setting passive mode");
494f573a5fcSDag-Erling Smørgrav 	switch (sa.ss_family) {
49528c645cfSHajimu UMEMOTO 	case AF_INET:
496fc6e9e65SDag-Erling Smørgrav 	    if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE)
497346298f0SDag-Erling Smørgrav 		goto ouch;
49828c645cfSHajimu UMEMOTO 	    break;
49928c645cfSHajimu UMEMOTO 	case AF_INET6:
50028c645cfSHajimu UMEMOTO 	    if ((e = _ftp_cmd(cd, "EPSV")) != FTP_EPASSIVE_MODE) {
50128c645cfSHajimu UMEMOTO 		if (e == -1)
50228c645cfSHajimu UMEMOTO 		    goto ouch;
50328c645cfSHajimu UMEMOTO 		if ((e = _ftp_cmd(cd, "LPSV")) != FTP_LPASSIVE_MODE)
50428c645cfSHajimu UMEMOTO 		    goto ouch;
50528c645cfSHajimu UMEMOTO 	    }
50628c645cfSHajimu UMEMOTO 	    break;
50728c645cfSHajimu UMEMOTO 	default:
5085cd33c40SDag-Erling Smørgrav 	    e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
50928c645cfSHajimu UMEMOTO 	    goto ouch;
51028c645cfSHajimu UMEMOTO 	}
511346298f0SDag-Erling Smørgrav 
512f5f109a0SDag-Erling Smørgrav 	/*
513f5f109a0SDag-Erling Smørgrav 	 * Find address and port number. The reply to the PASV command
514f5f109a0SDag-Erling Smørgrav          * is IMHO the one and only weak point in the FTP protocol.
515f5f109a0SDag-Erling Smørgrav 	 */
516fc6e9e65SDag-Erling Smørgrav 	ln = last_reply;
517fa5dce6cSHajimu UMEMOTO       	switch (e) {
518fa5dce6cSHajimu UMEMOTO 	case FTP_PASSIVE_MODE:
519fa5dce6cSHajimu UMEMOTO 	case FTP_LPASSIVE_MODE:
52051e3d46eSDag-Erling Smørgrav 	    for (p = ln + 3; *p && !isdigit(*p); p++)
521346298f0SDag-Erling Smørgrav 		/* nothing */ ;
52228c645cfSHajimu UMEMOTO 	    if (!*p) {
5235cd33c40SDag-Erling Smørgrav 		e = FTP_PROTOCOL_ERROR;
5246efb30c8SDag-Erling Smørgrav 		goto ouch;
525346298f0SDag-Erling Smørgrav 	    }
52628c645cfSHajimu UMEMOTO 	    l = (e == FTP_PASSIVE_MODE ? 6 : 21);
52728c645cfSHajimu UMEMOTO 	    for (i = 0; *p && i < l; i++, p++)
52828c645cfSHajimu UMEMOTO 		addr[i] = strtol(p, &p, 10);
52928c645cfSHajimu UMEMOTO 	    if (i < l) {
5305cd33c40SDag-Erling Smørgrav 		e = FTP_PROTOCOL_ERROR;
53128c645cfSHajimu UMEMOTO 		goto ouch;
53228c645cfSHajimu UMEMOTO 	    }
53328c645cfSHajimu UMEMOTO 	    break;
53428c645cfSHajimu UMEMOTO 	case FTP_EPASSIVE_MODE:
535fa5dce6cSHajimu UMEMOTO 	    for (p = ln + 3; *p && *p != '('; p++)
536fa5dce6cSHajimu UMEMOTO 		/* nothing */ ;
537fa5dce6cSHajimu UMEMOTO 	    if (!*p) {
5385cd33c40SDag-Erling Smørgrav 		e = FTP_PROTOCOL_ERROR;
539fa5dce6cSHajimu UMEMOTO 		goto ouch;
540fa5dce6cSHajimu UMEMOTO 	    }
541fa5dce6cSHajimu UMEMOTO 	    ++p;
54228c645cfSHajimu UMEMOTO 	    if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2],
54328c645cfSHajimu UMEMOTO 		       &port, &addr[3]) != 5 ||
54428c645cfSHajimu UMEMOTO 		addr[0] != addr[1] ||
54528c645cfSHajimu UMEMOTO 		addr[0] != addr[2] || addr[0] != addr[3]) {
5465cd33c40SDag-Erling Smørgrav 		e = FTP_PROTOCOL_ERROR;
54728c645cfSHajimu UMEMOTO 		goto ouch;
54828c645cfSHajimu UMEMOTO 	    }
54928c645cfSHajimu UMEMOTO 	    break;
55028c645cfSHajimu UMEMOTO 	}
551346298f0SDag-Erling Smørgrav 
55232425dafSDag-Erling Smørgrav 	/* seek to required offset */
55332425dafSDag-Erling Smørgrav 	if (offset)
55432425dafSDag-Erling Smørgrav 	    if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK)
55532425dafSDag-Erling Smørgrav 		goto sysouch;
55632425dafSDag-Erling Smørgrav 
557346298f0SDag-Erling Smørgrav 	/* construct sockaddr for data socket */
558f573a5fcSDag-Erling Smørgrav 	l = sizeof sa;
559f573a5fcSDag-Erling Smørgrav 	if (getpeername(cd, (struct sockaddr *)&sa, &l) == -1)
560346298f0SDag-Erling Smørgrav 	    goto sysouch;
561f573a5fcSDag-Erling Smørgrav 	if (sa.ss_family == AF_INET6)
562f573a5fcSDag-Erling Smørgrav 	    unmappedaddr((struct sockaddr_in6 *)&sa);
563f573a5fcSDag-Erling Smørgrav 	switch (sa.ss_family) {
56428c645cfSHajimu UMEMOTO 	case AF_INET6:
565f573a5fcSDag-Erling Smørgrav 	    sin6 = (struct sockaddr_in6 *)&sa;
56628c645cfSHajimu UMEMOTO 	    if (e == FTP_EPASSIVE_MODE)
56728c645cfSHajimu UMEMOTO 		sin6->sin6_port = htons(port);
56828c645cfSHajimu UMEMOTO 	    else {
56928c645cfSHajimu UMEMOTO 		bcopy(addr + 2, (char *)&sin6->sin6_addr, 16);
57028c645cfSHajimu UMEMOTO 		bcopy(addr + 19, (char *)&sin6->sin6_port, 2);
57128c645cfSHajimu UMEMOTO 	    }
57228c645cfSHajimu UMEMOTO 	    break;
57328c645cfSHajimu UMEMOTO 	case AF_INET:
574f573a5fcSDag-Erling Smørgrav 	    sin4 = (struct sockaddr_in *)&sa;
57528c645cfSHajimu UMEMOTO 	    if (e == FTP_EPASSIVE_MODE)
57628c645cfSHajimu UMEMOTO 		sin4->sin_port = htons(port);
57728c645cfSHajimu UMEMOTO 	    else {
57828c645cfSHajimu UMEMOTO 		bcopy(addr, (char *)&sin4->sin_addr, 4);
57928c645cfSHajimu UMEMOTO 		bcopy(addr + 4, (char *)&sin4->sin_port, 2);
58028c645cfSHajimu UMEMOTO 	    }
58128c645cfSHajimu UMEMOTO 	    break;
58228c645cfSHajimu UMEMOTO 	default:
5835cd33c40SDag-Erling Smørgrav 	    e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
58428c645cfSHajimu UMEMOTO 	    break;
58528c645cfSHajimu UMEMOTO 	}
586346298f0SDag-Erling Smørgrav 
587346298f0SDag-Erling Smørgrav 	/* connect to data port */
588f5f109a0SDag-Erling Smørgrav 	if (verbose)
589f5f109a0SDag-Erling Smørgrav 	    _fetch_info("opening data connection");
590f573a5fcSDag-Erling Smørgrav 	if (connect(sd, (struct sockaddr *)&sa, sa.ss_len) == -1)
591346298f0SDag-Erling Smørgrav 	    goto sysouch;
592346298f0SDag-Erling Smørgrav 
593346298f0SDag-Erling Smørgrav 	/* make the server initiate the transfer */
594f5f109a0SDag-Erling Smørgrav 	if (verbose)
595def5f54cSDag-Erling Smørgrav 	    _fetch_info("initiating transfer");
5961a5faa10SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "%s %s", oper, _ftp_filename(file));
597ea014d85SDag-Erling Smørgrav 	if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
598346298f0SDag-Erling Smørgrav 	    goto ouch;
599346298f0SDag-Erling Smørgrav 
600346298f0SDag-Erling Smørgrav     } else {
601346298f0SDag-Erling Smørgrav 	u_int32_t a;
602346298f0SDag-Erling Smørgrav 	u_short p;
603f5f109a0SDag-Erling Smørgrav 	int arg, d;
60428c645cfSHajimu UMEMOTO 	char *ap;
60528c645cfSHajimu UMEMOTO 	char hname[INET6_ADDRSTRLEN];
606346298f0SDag-Erling Smørgrav 
607f573a5fcSDag-Erling Smørgrav 	switch (sa.ss_family) {
60828c645cfSHajimu UMEMOTO 	case AF_INET6:
609f573a5fcSDag-Erling Smørgrav 	    ((struct sockaddr_in6 *)&sa)->sin6_port = 0;
61028c645cfSHajimu UMEMOTO #ifdef IPV6_PORTRANGE
611d5f175ceSDag-Erling Smørgrav 	    arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH;
61228c645cfSHajimu UMEMOTO 	    if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE,
61328c645cfSHajimu UMEMOTO 			   (char *)&arg, sizeof(arg)) == -1)
614346298f0SDag-Erling Smørgrav 		goto sysouch;
61528c645cfSHajimu UMEMOTO #endif
61628c645cfSHajimu UMEMOTO 	    break;
61728c645cfSHajimu UMEMOTO 	case AF_INET:
618f573a5fcSDag-Erling Smørgrav 	    ((struct sockaddr_in *)&sa)->sin_port = 0;
619d5f175ceSDag-Erling Smørgrav 	    arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH;
620f5f109a0SDag-Erling Smørgrav 	    if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
62132425dafSDag-Erling Smørgrav 			   (char *)&arg, sizeof arg) == -1)
622f5f109a0SDag-Erling Smørgrav 		goto sysouch;
62328c645cfSHajimu UMEMOTO 	    break;
62428c645cfSHajimu UMEMOTO 	}
625f5f109a0SDag-Erling Smørgrav 	if (verbose)
626f5f109a0SDag-Erling Smørgrav 	    _fetch_info("binding data socket");
627f573a5fcSDag-Erling Smørgrav 	if (bind(sd, (struct sockaddr *)&sa, sa.ss_len) == -1)
628346298f0SDag-Erling Smørgrav 	    goto sysouch;
629ecc91352SDag-Erling Smørgrav 	if (listen(sd, 1) == -1)
630346298f0SDag-Erling Smørgrav 	    goto sysouch;
631346298f0SDag-Erling Smørgrav 
632346298f0SDag-Erling Smørgrav 	/* find what port we're on and tell the server */
633f573a5fcSDag-Erling Smørgrav 	if (getsockname(sd, (struct sockaddr *)&sa, &l) == -1)
634346298f0SDag-Erling Smørgrav 	    goto sysouch;
635f573a5fcSDag-Erling Smørgrav 	switch (sa.ss_family) {
63628c645cfSHajimu UMEMOTO 	case AF_INET:
637f573a5fcSDag-Erling Smørgrav 	    sin4 = (struct sockaddr_in *)&sa;
63828c645cfSHajimu UMEMOTO 	    a = ntohl(sin4->sin_addr.s_addr);
63928c645cfSHajimu UMEMOTO 	    p = ntohs(sin4->sin_port);
640fc6e9e65SDag-Erling Smørgrav 	    e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d",
6415aea254fSDag-Erling Smørgrav 			 (a >> 24) & 0xff, (a >> 16) & 0xff,
6425aea254fSDag-Erling Smørgrav 			 (a >> 8) & 0xff, a & 0xff,
6435aea254fSDag-Erling Smørgrav 			 (p >> 8) & 0xff, p & 0xff);
64428c645cfSHajimu UMEMOTO 	    break;
64528c645cfSHajimu UMEMOTO 	case AF_INET6:
64628c645cfSHajimu UMEMOTO #define UC(b)	(((int)b)&0xff)
64728c645cfSHajimu UMEMOTO 	    e = -1;
648f573a5fcSDag-Erling Smørgrav 	    sin6 = (struct sockaddr_in6 *)&sa;
649f573a5fcSDag-Erling Smørgrav 	    if (getnameinfo((struct sockaddr *)&sa, sa.ss_len,
65028c645cfSHajimu UMEMOTO 			    hname, sizeof(hname),
65128c645cfSHajimu UMEMOTO 			    NULL, 0, NI_NUMERICHOST) == 0) {
65228c645cfSHajimu UMEMOTO 		e = _ftp_cmd(cd, "EPRT |%d|%s|%d|", 2, hname,
65328c645cfSHajimu UMEMOTO 			     htons(sin6->sin6_port));
65428c645cfSHajimu UMEMOTO 		if (e == -1)
65528c645cfSHajimu UMEMOTO 		    goto ouch;
65628c645cfSHajimu UMEMOTO 	    }
65728c645cfSHajimu UMEMOTO 	    if (e != FTP_OK) {
65828c645cfSHajimu UMEMOTO 		ap = (char *)&sin6->sin6_addr;
65928c645cfSHajimu UMEMOTO 		e = _ftp_cmd(cd,
66028c645cfSHajimu UMEMOTO      "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
66128c645cfSHajimu UMEMOTO 			     6, 16,
66228c645cfSHajimu UMEMOTO 			     UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]),
66328c645cfSHajimu UMEMOTO 			     UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]),
66428c645cfSHajimu UMEMOTO 			     UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]),
66528c645cfSHajimu UMEMOTO 			     UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]),
66628c645cfSHajimu UMEMOTO 			     2,
66728c645cfSHajimu UMEMOTO 			     (ntohs(sin6->sin6_port) >> 8) & 0xff,
66828c645cfSHajimu UMEMOTO 			     ntohs(sin6->sin6_port)        & 0xff);
66928c645cfSHajimu UMEMOTO 	    }
67028c645cfSHajimu UMEMOTO 	    break;
67128c645cfSHajimu UMEMOTO 	default:
6725cd33c40SDag-Erling Smørgrav 	    e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
67328c645cfSHajimu UMEMOTO 	    goto ouch;
67428c645cfSHajimu UMEMOTO 	}
6755aea254fSDag-Erling Smørgrav 	if (e != FTP_OK)
676346298f0SDag-Erling Smørgrav 	    goto ouch;
677346298f0SDag-Erling Smørgrav 
678893980adSDag-Erling Smørgrav 	/* seek to required offset */
679893980adSDag-Erling Smørgrav 	if (offset)
680893980adSDag-Erling Smørgrav 	    if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK)
681893980adSDag-Erling Smørgrav 		goto sysouch;
682893980adSDag-Erling Smørgrav 
683346298f0SDag-Erling Smørgrav 	/* make the server initiate the transfer */
684f5f109a0SDag-Erling Smørgrav 	if (verbose)
685f5f109a0SDag-Erling Smørgrav 	    _fetch_info("initiating transfer");
6861a5faa10SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "%s %s", oper, _ftp_filename(file));
6875aea254fSDag-Erling Smørgrav 	if (e != FTP_OPEN_DATA_CONNECTION)
688346298f0SDag-Erling Smørgrav 	    goto ouch;
689346298f0SDag-Erling Smørgrav 
690346298f0SDag-Erling Smørgrav 	/* accept the incoming connection and go to town */
691ecc91352SDag-Erling Smørgrav 	if ((d = accept(sd, NULL, NULL)) == -1)
692346298f0SDag-Erling Smørgrav 	    goto sysouch;
693346298f0SDag-Erling Smørgrav 	close(sd);
694346298f0SDag-Erling Smørgrav 	sd = d;
695346298f0SDag-Erling Smørgrav     }
696346298f0SDag-Erling Smørgrav 
697c7d40ef2SDag-Erling Smørgrav     if ((df = _ftp_setup(cd, sd, mode)) == NULL)
698346298f0SDag-Erling Smørgrav 	goto sysouch;
699346298f0SDag-Erling Smørgrav     return df;
700346298f0SDag-Erling Smørgrav 
701346298f0SDag-Erling Smørgrav sysouch:
702842a95ccSDag-Erling Smørgrav     _fetch_syserr();
70328c645cfSHajimu UMEMOTO     if (sd >= 0)
7045aea254fSDag-Erling Smørgrav 	close(sd);
7055aea254fSDag-Erling Smørgrav     return NULL;
7065aea254fSDag-Erling Smørgrav 
707346298f0SDag-Erling Smørgrav ouch:
708fc6e9e65SDag-Erling Smørgrav     if (e != -1)
7095aea254fSDag-Erling Smørgrav 	_ftp_seterr(e);
71028c645cfSHajimu UMEMOTO     if (sd >= 0)
711346298f0SDag-Erling Smørgrav 	close(sd);
7124ca1ab94SDag-Erling Smørgrav     return NULL;
7134ca1ab94SDag-Erling Smørgrav }
7144ca1ab94SDag-Erling Smørgrav 
7158e3986eaSDag-Erling Smørgrav /*
7166490b215SDag-Erling Smørgrav  * Authenticate
7176490b215SDag-Erling Smørgrav  */
7186490b215SDag-Erling Smørgrav static int
7196490b215SDag-Erling Smørgrav _ftp_authenticate(int cd, struct url *url, struct url *purl)
7206490b215SDag-Erling Smørgrav {
721f573a5fcSDag-Erling Smørgrav     const char *user, *pwd, *logname;
7226490b215SDag-Erling Smørgrav     char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1];
7236490b215SDag-Erling Smørgrav     int e, len;
7246490b215SDag-Erling Smørgrav 
7256490b215SDag-Erling Smørgrav     /* XXX FTP_AUTH, and maybe .netrc */
7266490b215SDag-Erling Smørgrav 
7276490b215SDag-Erling Smørgrav     /* send user name and password */
7286490b215SDag-Erling Smørgrav     user = url->user;
7296490b215SDag-Erling Smørgrav     if (!user || !*user)
7306490b215SDag-Erling Smørgrav 	user = getenv("FTP_LOGIN");
7316490b215SDag-Erling Smørgrav     if (!user || !*user)
7326490b215SDag-Erling Smørgrav 	user = FTP_ANONYMOUS_USER;
7336490b215SDag-Erling Smørgrav     if (purl && url->port == _fetch_default_port(url->scheme))
7346490b215SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "USER %s@%s", user, url->host);
7356490b215SDag-Erling Smørgrav     else if (purl)
7366490b215SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "USER %s@%s@%d", user, url->host, url->port);
7376490b215SDag-Erling Smørgrav     else
7386490b215SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "USER %s", user);
7396490b215SDag-Erling Smørgrav 
7406490b215SDag-Erling Smørgrav     /* did the server request a password? */
7416490b215SDag-Erling Smørgrav     if (e == FTP_NEED_PASSWORD) {
7426490b215SDag-Erling Smørgrav 	pwd = url->pwd;
7436490b215SDag-Erling Smørgrav 	if (!pwd || !*pwd)
7446490b215SDag-Erling Smørgrav 	    pwd = getenv("FTP_PASSWORD");
7456490b215SDag-Erling Smørgrav 	if (!pwd || !*pwd) {
7466490b215SDag-Erling Smørgrav 	    if ((logname = getlogin()) == 0)
7476490b215SDag-Erling Smørgrav 		logname = FTP_ANONYMOUS_USER;
748778de359SBrian Somers 	    if ((len = snprintf(pbuf, MAXLOGNAME + 1, "%s@", logname)) < 0)
7492449bf28SBrian Somers 		len = 0;
7505f328905SBrian Somers 	    else if (len > MAXLOGNAME)
7515f328905SBrian Somers 	        len = MAXLOGNAME;
7526490b215SDag-Erling Smørgrav 	    gethostname(pbuf + len, sizeof pbuf - len);
7536490b215SDag-Erling Smørgrav 	    pwd = pbuf;
7546490b215SDag-Erling Smørgrav 	}
7556490b215SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "PASS %s", pwd);
7566490b215SDag-Erling Smørgrav     }
7576490b215SDag-Erling Smørgrav 
7586490b215SDag-Erling Smørgrav     return e;
7596490b215SDag-Erling Smørgrav }
7606490b215SDag-Erling Smørgrav 
7616490b215SDag-Erling Smørgrav /*
7628e3986eaSDag-Erling Smørgrav  * Log on to FTP server
7634ca1ab94SDag-Erling Smørgrav  */
764fc6e9e65SDag-Erling Smørgrav static int
76538c7e4a6SArchie Cobbs _ftp_connect(struct url *url, struct url *purl, const char *flags)
7668e3986eaSDag-Erling Smørgrav {
7671a16ed4cSDag-Erling Smørgrav     int cd, e, direct, verbose;
76828c645cfSHajimu UMEMOTO #ifdef INET6
76928c645cfSHajimu UMEMOTO     int af = AF_UNSPEC;
77028c645cfSHajimu UMEMOTO #else
77128c645cfSHajimu UMEMOTO     int af = AF_INET;
77228c645cfSHajimu UMEMOTO #endif
7738e3986eaSDag-Erling Smørgrav 
774d74a913bSDag-Erling Smørgrav     direct = CHECK_FLAG('d');
775d74a913bSDag-Erling Smørgrav     verbose = CHECK_FLAG('v');
776d74a913bSDag-Erling Smørgrav     if (CHECK_FLAG('4'))
77728c645cfSHajimu UMEMOTO 	af = AF_INET;
778d74a913bSDag-Erling Smørgrav     else if (CHECK_FLAG('6'))
77928c645cfSHajimu UMEMOTO 	af = AF_INET6;
780f5f109a0SDag-Erling Smørgrav 
7811a16ed4cSDag-Erling Smørgrav     if (direct)
7821a16ed4cSDag-Erling Smørgrav 	purl = NULL;
78328c645cfSHajimu UMEMOTO 
7841a16ed4cSDag-Erling Smørgrav     /* check for proxy */
7851a16ed4cSDag-Erling Smørgrav     if (purl) {
7861a16ed4cSDag-Erling Smørgrav 	/* XXX proxy authentication! */
7871a16ed4cSDag-Erling Smørgrav 	cd = _fetch_connect(purl->host, purl->port, af, verbose);
788f62e5228SDag-Erling Smørgrav     } else {
789f62e5228SDag-Erling Smørgrav 	/* no proxy, go straight to target */
7901a16ed4cSDag-Erling Smørgrav 	cd = _fetch_connect(url->host, url->port, af, verbose);
7911a16ed4cSDag-Erling Smørgrav 	purl = NULL;
792f62e5228SDag-Erling Smørgrav     }
793f62e5228SDag-Erling Smørgrav 
794f62e5228SDag-Erling Smørgrav     /* check connection */
795fc6e9e65SDag-Erling Smørgrav     if (cd == -1) {
796842a95ccSDag-Erling Smørgrav 	_fetch_syserr();
7978e3986eaSDag-Erling Smørgrav 	return NULL;
7988e3986eaSDag-Erling Smørgrav     }
799f62e5228SDag-Erling Smørgrav 
8008e3986eaSDag-Erling Smørgrav     /* expect welcome message */
801fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY)
8028e3986eaSDag-Erling Smørgrav 	goto fouch;
8038e3986eaSDag-Erling Smørgrav 
8046490b215SDag-Erling Smørgrav     /* authenticate */
8056490b215SDag-Erling Smørgrav     if ((e = _ftp_authenticate(cd, url, purl)) != FTP_LOGGED_IN)
8068e3986eaSDag-Erling Smørgrav 	goto fouch;
8078e3986eaSDag-Erling Smørgrav 
8088e3986eaSDag-Erling Smørgrav     /* might as well select mode and type at once */
8098e3986eaSDag-Erling Smørgrav #ifdef FTP_FORCE_STREAM_MODE
810fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */
8115aea254fSDag-Erling Smørgrav 	goto fouch;
8128e3986eaSDag-Erling Smørgrav #endif
813fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */
8145aea254fSDag-Erling Smørgrav 	goto fouch;
8158e3986eaSDag-Erling Smørgrav 
8168e3986eaSDag-Erling Smørgrav     /* done */
817fc6e9e65SDag-Erling Smørgrav     return cd;
8188e3986eaSDag-Erling Smørgrav 
8198e3986eaSDag-Erling Smørgrav fouch:
820fc6e9e65SDag-Erling Smørgrav     if (e != -1)
8215aea254fSDag-Erling Smørgrav 	_ftp_seterr(e);
822fc6e9e65SDag-Erling Smørgrav     close(cd);
8238e3986eaSDag-Erling Smørgrav     return NULL;
8248e3986eaSDag-Erling Smørgrav }
8258e3986eaSDag-Erling Smørgrav 
8268e3986eaSDag-Erling Smørgrav /*
8278e3986eaSDag-Erling Smørgrav  * Disconnect from server
8288e3986eaSDag-Erling Smørgrav  */
8298e3986eaSDag-Erling Smørgrav static void
830fc6e9e65SDag-Erling Smørgrav _ftp_disconnect(int cd)
8318e3986eaSDag-Erling Smørgrav {
832fc6e9e65SDag-Erling Smørgrav     (void)_ftp_cmd(cd, "QUIT");
833fc6e9e65SDag-Erling Smørgrav     close(cd);
8348e3986eaSDag-Erling Smørgrav }
8358e3986eaSDag-Erling Smørgrav 
8368e3986eaSDag-Erling Smørgrav /*
8378e3986eaSDag-Erling Smørgrav  * Check if we're already connected
8388e3986eaSDag-Erling Smørgrav  */
8398e3986eaSDag-Erling Smørgrav static int
840d8acd8dcSDag-Erling Smørgrav _ftp_isconnected(struct url *url)
8418e3986eaSDag-Erling Smørgrav {
8428e3986eaSDag-Erling Smørgrav     return (cached_socket
8438e3986eaSDag-Erling Smørgrav 	    && (strcmp(url->host, cached_host.host) == 0)
8448e3986eaSDag-Erling Smørgrav 	    && (strcmp(url->user, cached_host.user) == 0)
8458e3986eaSDag-Erling Smørgrav 	    && (strcmp(url->pwd, cached_host.pwd) == 0)
8468e3986eaSDag-Erling Smørgrav 	    && (url->port == cached_host.port));
8478e3986eaSDag-Erling Smørgrav }
8488e3986eaSDag-Erling Smørgrav 
849f62e5228SDag-Erling Smørgrav /*
8505aea254fSDag-Erling Smørgrav  * Check the cache, reconnect if no luck
851f62e5228SDag-Erling Smørgrav  */
852fc6e9e65SDag-Erling Smørgrav static int
85338c7e4a6SArchie Cobbs _ftp_cached_connect(struct url *url, struct url *purl, const char *flags)
8544ca1ab94SDag-Erling Smørgrav {
855fc6e9e65SDag-Erling Smørgrav     int e, cd;
8565aea254fSDag-Erling Smørgrav 
857fc6e9e65SDag-Erling Smørgrav     cd = -1;
8588e3986eaSDag-Erling Smørgrav 
8598e3986eaSDag-Erling Smørgrav     /* set default port */
86010851dc4SDag-Erling Smørgrav     if (!url->port)
861e828ada7SDag-Erling Smørgrav 	url->port = _fetch_default_port(url->scheme);
8628e3986eaSDag-Erling Smørgrav 
8633b7a6740SDag-Erling Smørgrav     /* try to use previously cached connection */
864fc6e9e65SDag-Erling Smørgrav     if (_ftp_isconnected(url)) {
865fc6e9e65SDag-Erling Smørgrav 	e = _ftp_cmd(cached_socket, "NOOP");
866fc6e9e65SDag-Erling Smørgrav 	if (e == FTP_OK || e == FTP_SYNTAX_ERROR)
8671a16ed4cSDag-Erling Smørgrav 	    return cached_socket;
868fc6e9e65SDag-Erling Smørgrav     }
8694ca1ab94SDag-Erling Smørgrav 
8708e3986eaSDag-Erling Smørgrav     /* connect to server */
8711a16ed4cSDag-Erling Smørgrav     if ((cd = _ftp_connect(url, purl, flags)) == -1)
872fc6e9e65SDag-Erling Smørgrav 	return -1;
8738e3986eaSDag-Erling Smørgrav     if (cached_socket)
8748e3986eaSDag-Erling Smørgrav 	_ftp_disconnect(cached_socket);
875fc6e9e65SDag-Erling Smørgrav     cached_socket = cd;
87632425dafSDag-Erling Smørgrav     memcpy(&cached_host, url, sizeof *url);
877fc6e9e65SDag-Erling Smørgrav     return cd;
8788e3986eaSDag-Erling Smørgrav }
8798e3986eaSDag-Erling Smørgrav 
8808e3986eaSDag-Erling Smørgrav /*
8811a16ed4cSDag-Erling Smørgrav  * Check the proxy settings
882dfe7c55fSDag-Erling Smørgrav  */
8831a16ed4cSDag-Erling Smørgrav static struct url *
8841a16ed4cSDag-Erling Smørgrav _ftp_get_proxy(void)
885dfe7c55fSDag-Erling Smørgrav {
8861a16ed4cSDag-Erling Smørgrav     struct url *purl;
887dfe7c55fSDag-Erling Smørgrav     char *p;
888dfe7c55fSDag-Erling Smørgrav 
8894cee73c8SDag-Erling Smørgrav     if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) ||
8904cee73c8SDag-Erling Smørgrav 	 (p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&
8911a16ed4cSDag-Erling Smørgrav 	*p && (purl = fetchParseURL(p)) != NULL) {
892882974d4SDag-Erling Smørgrav 	if (!*purl->scheme) {
8934cee73c8SDag-Erling Smørgrav 	    if (getenv("FTP_PROXY") || getenv("ftp_proxy"))
894882974d4SDag-Erling Smørgrav 		strcpy(purl->scheme, SCHEME_FTP);
895882974d4SDag-Erling Smørgrav 	    else
896e828ada7SDag-Erling Smørgrav 		strcpy(purl->scheme, SCHEME_HTTP);
897882974d4SDag-Erling Smørgrav 	}
8981a16ed4cSDag-Erling Smørgrav 	if (!purl->port)
899e828ada7SDag-Erling Smørgrav 	    purl->port = _fetch_default_proxy_port(purl->scheme);
9001a16ed4cSDag-Erling Smørgrav 	if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 ||
9011a16ed4cSDag-Erling Smørgrav 	    strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
9021a16ed4cSDag-Erling Smørgrav 	    return purl;
9031a16ed4cSDag-Erling Smørgrav 	fetchFreeURL(purl);
9041a16ed4cSDag-Erling Smørgrav     }
9051a16ed4cSDag-Erling Smørgrav     return NULL;
906dfe7c55fSDag-Erling Smørgrav }
907dfe7c55fSDag-Erling Smørgrav 
908dfe7c55fSDag-Erling Smørgrav /*
909102a87c1SDag-Erling Smørgrav  * Process an FTP request
9108e3986eaSDag-Erling Smørgrav  */
9114ca1ab94SDag-Erling Smørgrav FILE *
912102a87c1SDag-Erling Smørgrav _ftp_request(struct url *url, const char *op, struct url_stat *us,
913102a87c1SDag-Erling Smørgrav 	     struct url *purl, const char *flags)
914f62e5228SDag-Erling Smørgrav {
915fc6e9e65SDag-Erling Smørgrav     int cd;
9165aea254fSDag-Erling Smørgrav 
917102a87c1SDag-Erling Smørgrav     /* check if we should use HTTP instead */
918102a87c1SDag-Erling Smørgrav     if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) {
919102a87c1SDag-Erling Smørgrav 	if (strcmp(op, "STAT") == 0)
920102a87c1SDag-Erling Smørgrav 	    return _http_request(url, "HEAD", us, purl, flags);
921102a87c1SDag-Erling Smørgrav 	else if (strcmp(op, "RETR") == 0)
9221a16ed4cSDag-Erling Smørgrav 	    return _http_request(url, "GET", us, purl, flags);
923102a87c1SDag-Erling Smørgrav 	/*
924102a87c1SDag-Erling Smørgrav 	 * Our HTTP code doesn't support PUT requests yet, so try a
925102a87c1SDag-Erling Smørgrav 	 * direct connection.
926102a87c1SDag-Erling Smørgrav 	 */
9271a16ed4cSDag-Erling Smørgrav     }
928dfe7c55fSDag-Erling Smørgrav 
9295aea254fSDag-Erling Smørgrav     /* connect to server */
9301a16ed4cSDag-Erling Smørgrav     cd = _ftp_cached_connect(url, purl, flags);
9311a16ed4cSDag-Erling Smørgrav     if (purl)
9321a16ed4cSDag-Erling Smørgrav 	fetchFreeURL(purl);
9331a16ed4cSDag-Erling Smørgrav     if (cd == NULL)
9345aea254fSDag-Erling Smørgrav 	return NULL;
9355aea254fSDag-Erling Smørgrav 
9361a5faa10SDag-Erling Smørgrav     /* change directory */
9371a5faa10SDag-Erling Smørgrav     if (_ftp_cwd(cd, url->doc) == -1)
9381a5faa10SDag-Erling Smørgrav 	return NULL;
9391a5faa10SDag-Erling Smørgrav 
9401a5faa10SDag-Erling Smørgrav     /* stat file */
941269532d9SDag-Erling Smørgrav     if (us && _ftp_stat(cd, url->doc, us) == -1
9420f27c783SDag-Erling Smørgrav 	&& fetchLastErrCode != FETCH_PROTO
943269532d9SDag-Erling Smørgrav 	&& fetchLastErrCode != FETCH_UNAVAIL)
9441a5faa10SDag-Erling Smørgrav 	return NULL;
9451a5faa10SDag-Erling Smørgrav 
946102a87c1SDag-Erling Smørgrav     /* just a stat */
947102a87c1SDag-Erling Smørgrav     if (strcmp(op, "STAT") == 0)
948102a87c1SDag-Erling Smørgrav 	return (FILE *)1; /* bogus return value */
949102a87c1SDag-Erling Smørgrav 
9505aea254fSDag-Erling Smørgrav     /* initiate the transfer */
951102a87c1SDag-Erling Smørgrav     return _ftp_transfer(cd, op, url->doc, O_RDONLY, url->offset, flags);
952102a87c1SDag-Erling Smørgrav }
953102a87c1SDag-Erling Smørgrav 
954102a87c1SDag-Erling Smørgrav /*
955102a87c1SDag-Erling Smørgrav  * Get and stat file
956102a87c1SDag-Erling Smørgrav  */
957102a87c1SDag-Erling Smørgrav FILE *
958102a87c1SDag-Erling Smørgrav fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags)
959102a87c1SDag-Erling Smørgrav {
960102a87c1SDag-Erling Smørgrav     return _ftp_request(url, "RETR", us, _ftp_get_proxy(), flags);
961f62e5228SDag-Erling Smørgrav }
962f62e5228SDag-Erling Smørgrav 
9635aea254fSDag-Erling Smørgrav /*
9641a5faa10SDag-Erling Smørgrav  * Get file
9651a5faa10SDag-Erling Smørgrav  */
9661a5faa10SDag-Erling Smørgrav FILE *
96738c7e4a6SArchie Cobbs fetchGetFTP(struct url *url, const char *flags)
9681a5faa10SDag-Erling Smørgrav {
9691a5faa10SDag-Erling Smørgrav     return fetchXGetFTP(url, NULL, flags);
9701a5faa10SDag-Erling Smørgrav }
9711a5faa10SDag-Erling Smørgrav 
9721a5faa10SDag-Erling Smørgrav /*
9735aea254fSDag-Erling Smørgrav  * Put file
9745aea254fSDag-Erling Smørgrav  */
975f62e5228SDag-Erling Smørgrav FILE *
97638c7e4a6SArchie Cobbs fetchPutFTP(struct url *url, const char *flags)
9774ca1ab94SDag-Erling Smørgrav {
9785aea254fSDag-Erling Smørgrav 
979102a87c1SDag-Erling Smørgrav     return _ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL,
980102a87c1SDag-Erling Smørgrav 			_ftp_get_proxy(), flags);
9818e3986eaSDag-Erling Smørgrav }
982d8acd8dcSDag-Erling Smørgrav 
9835aea254fSDag-Erling Smørgrav /*
9845aea254fSDag-Erling Smørgrav  * Get file stats
9855aea254fSDag-Erling Smørgrav  */
986d8acd8dcSDag-Erling Smørgrav int
98738c7e4a6SArchie Cobbs fetchStatFTP(struct url *url, struct url_stat *us, const char *flags)
988d8acd8dcSDag-Erling Smørgrav {
9895aea254fSDag-Erling Smørgrav 
990102a87c1SDag-Erling Smørgrav     if (_ftp_request(url, "STAT", us, _ftp_get_proxy(), flags) == NULL)
9911a16ed4cSDag-Erling Smørgrav 	return -1;
9921a16ed4cSDag-Erling Smørgrav     return 0;
9931a16ed4cSDag-Erling Smørgrav }
994ce71b736SDag-Erling Smørgrav 
995ce71b736SDag-Erling Smørgrav /*
996ce71b736SDag-Erling Smørgrav  * List a directory
997ce71b736SDag-Erling Smørgrav  */
998ce71b736SDag-Erling Smørgrav struct url_ent *
999f573a5fcSDag-Erling Smørgrav fetchListFTP(struct url *url __unused, const char *flags __unused)
1000ce71b736SDag-Erling Smørgrav {
1001def5f54cSDag-Erling Smørgrav     warnx("fetchListFTP(): not implemented");
1002ce71b736SDag-Erling Smørgrav     return NULL;
1003ce71b736SDag-Erling Smørgrav }
1004