xref: /freebsd/lib/libfetch/ftp.c (revision eac7a1e07f603ccf07d224b5d24b4031cb96dd76)
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  *
287f3dea24SPeter Wemm  * $FreeBSD$
294ca1ab94SDag-Erling Smørgrav  */
304ca1ab94SDag-Erling Smørgrav 
314ca1ab94SDag-Erling Smørgrav /*
328e3986eaSDag-Erling Smørgrav  * Portions of this code were taken from or based on ftpio.c:
334ca1ab94SDag-Erling Smørgrav  *
344ca1ab94SDag-Erling Smørgrav  * ----------------------------------------------------------------------------
354ca1ab94SDag-Erling Smørgrav  * "THE BEER-WARE LICENSE" (Revision 42):
364ca1ab94SDag-Erling Smørgrav  * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
374ca1ab94SDag-Erling Smørgrav  * can do whatever you want with this stuff. If we meet some day, and you think
384ca1ab94SDag-Erling Smørgrav  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
394ca1ab94SDag-Erling Smørgrav  * ----------------------------------------------------------------------------
404ca1ab94SDag-Erling Smørgrav  *
414ca1ab94SDag-Erling Smørgrav  * Major Changelog:
424ca1ab94SDag-Erling Smørgrav  *
434ca1ab94SDag-Erling Smørgrav  * Dag-Erling Co�dan Sm�rgrav
444ca1ab94SDag-Erling Smørgrav  * 9 Jun 1998
454ca1ab94SDag-Erling Smørgrav  *
464ca1ab94SDag-Erling Smørgrav  * Incorporated into libfetch
474ca1ab94SDag-Erling Smørgrav  *
484ca1ab94SDag-Erling Smørgrav  * Jordan K. Hubbard
494ca1ab94SDag-Erling Smørgrav  * 17 Jan 1996
504ca1ab94SDag-Erling Smørgrav  *
514ca1ab94SDag-Erling Smørgrav  * Turned inside out. Now returns xfers as new file ids, not as a special
524ca1ab94SDag-Erling Smørgrav  * `state' of FTP_t
534ca1ab94SDag-Erling Smørgrav  *
544ca1ab94SDag-Erling Smørgrav  * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $
554ca1ab94SDag-Erling Smørgrav  *
564ca1ab94SDag-Erling Smørgrav  */
574ca1ab94SDag-Erling Smørgrav 
580fba3a00SDag-Erling Smørgrav #include <sys/param.h>
594ca1ab94SDag-Erling Smørgrav #include <sys/socket.h>
60fc6e9e65SDag-Erling Smørgrav #include <sys/uio.h>
614ca1ab94SDag-Erling Smørgrav #include <netinet/in.h>
624ca1ab94SDag-Erling Smørgrav 
634ca1ab94SDag-Erling Smørgrav #include <ctype.h>
64fc6e9e65SDag-Erling Smørgrav #include <errno.h>
6532425dafSDag-Erling Smørgrav #include <netdb.h>
66346298f0SDag-Erling Smørgrav #include <stdarg.h>
674ca1ab94SDag-Erling Smørgrav #include <stdio.h>
688e3986eaSDag-Erling Smørgrav #include <stdlib.h>
694ca1ab94SDag-Erling Smørgrav #include <string.h>
705aea254fSDag-Erling Smørgrav #include <time.h>
718e3986eaSDag-Erling Smørgrav #include <unistd.h>
724ca1ab94SDag-Erling Smørgrav 
734ca1ab94SDag-Erling Smørgrav #include "fetch.h"
74842a95ccSDag-Erling Smørgrav #include "common.h"
750fba3a00SDag-Erling Smørgrav #include "ftperr.h"
764ca1ab94SDag-Erling Smørgrav 
774ca1ab94SDag-Erling Smørgrav #define FTP_ANONYMOUS_USER	"ftp"
784ca1ab94SDag-Erling Smørgrav #define FTP_ANONYMOUS_PASSWORD	"ftp"
79346298f0SDag-Erling Smørgrav #define FTP_DEFAULT_PORT 21
80346298f0SDag-Erling Smørgrav 
81346298f0SDag-Erling Smørgrav #define FTP_OPEN_DATA_CONNECTION	150
82346298f0SDag-Erling Smørgrav #define FTP_OK				200
835aea254fSDag-Erling Smørgrav #define FTP_FILE_STATUS			213
843b7a6740SDag-Erling Smørgrav #define FTP_SERVICE_READY		220
85346298f0SDag-Erling Smørgrav #define FTP_PASSIVE_MODE		227
8628c645cfSHajimu UMEMOTO #define FTP_LPASSIVE_MODE		228
8728c645cfSHajimu UMEMOTO #define FTP_EPASSIVE_MODE		229
88346298f0SDag-Erling Smørgrav #define FTP_LOGGED_IN			230
89346298f0SDag-Erling Smørgrav #define FTP_FILE_ACTION_OK		250
90346298f0SDag-Erling Smørgrav #define FTP_NEED_PASSWORD		331
91346298f0SDag-Erling Smørgrav #define FTP_NEED_ACCOUNT		332
9232425dafSDag-Erling Smørgrav #define FTP_FILE_OK			350
93fc6e9e65SDag-Erling Smørgrav #define FTP_SYNTAX_ERROR		500
944ca1ab94SDag-Erling Smørgrav 
95fc6e9e65SDag-Erling Smørgrav static char ENDL[2] = "\r\n";
968e3986eaSDag-Erling Smørgrav 
97d8acd8dcSDag-Erling Smørgrav static struct url cached_host;
98fc6e9e65SDag-Erling Smørgrav static int cached_socket;
994ca1ab94SDag-Erling Smørgrav 
100fc6e9e65SDag-Erling Smørgrav static char *last_reply;
101fc6e9e65SDag-Erling Smørgrav static size_t lr_size, lr_length;
102fc6e9e65SDag-Erling Smørgrav static int last_code;
103fc6e9e65SDag-Erling Smørgrav 
104fc6e9e65SDag-Erling Smørgrav #define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
1056efb30c8SDag-Erling Smørgrav 			 && isdigit(foo[2]) \
1066efb30c8SDag-Erling Smørgrav                          && (foo[3] == ' ' || foo[3] == '\0'))
107fc6e9e65SDag-Erling Smørgrav #define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
108fc6e9e65SDag-Erling Smørgrav 			&& isdigit(foo[2]) && foo[3] == '-')
1094ca1ab94SDag-Erling Smørgrav 
11028c645cfSHajimu UMEMOTO /* translate IPv4 mapped IPv6 address to IPv4 address */
11128c645cfSHajimu UMEMOTO static void
11228c645cfSHajimu UMEMOTO unmappedaddr(struct sockaddr_in6 *sin6)
11328c645cfSHajimu UMEMOTO {
11428c645cfSHajimu UMEMOTO     struct sockaddr_in *sin4;
11528c645cfSHajimu UMEMOTO     u_int32_t addr;
11628c645cfSHajimu UMEMOTO     int port;
11728c645cfSHajimu UMEMOTO 
11828c645cfSHajimu UMEMOTO     if (sin6->sin6_family != AF_INET6 ||
11928c645cfSHajimu UMEMOTO 	!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
12028c645cfSHajimu UMEMOTO 	return;
12128c645cfSHajimu UMEMOTO     sin4 = (struct sockaddr_in *)sin6;
12228c645cfSHajimu UMEMOTO     addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
12328c645cfSHajimu UMEMOTO     port = sin6->sin6_port;
12428c645cfSHajimu UMEMOTO     memset(sin4, 0, sizeof(struct sockaddr_in));
12528c645cfSHajimu UMEMOTO     sin4->sin_addr.s_addr = addr;
12628c645cfSHajimu UMEMOTO     sin4->sin_port = port;
12728c645cfSHajimu UMEMOTO     sin4->sin_family = AF_INET;
12828c645cfSHajimu UMEMOTO     sin4->sin_len = sizeof(struct sockaddr_in);
12928c645cfSHajimu UMEMOTO }
13028c645cfSHajimu UMEMOTO 
1314ca1ab94SDag-Erling Smørgrav /*
132fc6e9e65SDag-Erling Smørgrav  * Get server response
1338e3986eaSDag-Erling Smørgrav  */
1348e3986eaSDag-Erling Smørgrav static int
135fc6e9e65SDag-Erling Smørgrav _ftp_chkerr(int cd)
1368e3986eaSDag-Erling Smørgrav {
137fc6e9e65SDag-Erling Smørgrav     if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) {
138842a95ccSDag-Erling Smørgrav 	_fetch_syserr();
1398e3986eaSDag-Erling Smørgrav 	return -1;
1408e3986eaSDag-Erling Smørgrav     }
141346298f0SDag-Erling Smørgrav #ifndef NDEBUG
142fc6e9e65SDag-Erling Smørgrav     _fetch_info("got reply '%.*s'", lr_length - 2, last_reply);
143346298f0SDag-Erling Smørgrav #endif
144eac7a1e0SDag-Erling Smørgrav     if (isftpinfo(last_reply)) {
145eac7a1e0SDag-Erling Smørgrav 	while (!isftpreply(last_reply)) {
146eac7a1e0SDag-Erling Smørgrav 	    if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) {
147eac7a1e0SDag-Erling Smørgrav 		_fetch_syserr();
148eac7a1e0SDag-Erling Smørgrav 		return -1;
149eac7a1e0SDag-Erling Smørgrav 	    }
150eac7a1e0SDag-Erling Smørgrav #ifndef NDEBUG
151eac7a1e0SDag-Erling Smørgrav 	    _fetch_info("got reply '%.*s'", lr_length - 2, last_reply);
152eac7a1e0SDag-Erling Smørgrav #endif
153eac7a1e0SDag-Erling Smørgrav 	}
154eac7a1e0SDag-Erling Smørgrav     }
155346298f0SDag-Erling Smørgrav 
156fc6e9e65SDag-Erling Smørgrav     while (lr_length && isspace(last_reply[lr_length-1]))
157fc6e9e65SDag-Erling Smørgrav 	lr_length--;
158fc6e9e65SDag-Erling Smørgrav     last_reply[lr_length] = 0;
159fc6e9e65SDag-Erling Smørgrav 
160fc6e9e65SDag-Erling Smørgrav     if (!isftpreply(last_reply)) {
161fc6e9e65SDag-Erling Smørgrav 	_ftp_seterr(999);
1628e3986eaSDag-Erling Smørgrav 	return -1;
1638e3986eaSDag-Erling Smørgrav     }
1648e3986eaSDag-Erling Smørgrav 
165fc6e9e65SDag-Erling Smørgrav     last_code = (last_reply[0] - '0') * 100
166fc6e9e65SDag-Erling Smørgrav 	+ (last_reply[1] - '0') * 10
167fc6e9e65SDag-Erling Smørgrav 	+ (last_reply[2] - '0');
168fc6e9e65SDag-Erling Smørgrav 
169fc6e9e65SDag-Erling Smørgrav     return last_code;
1708e3986eaSDag-Erling Smørgrav }
1718e3986eaSDag-Erling Smørgrav 
1728e3986eaSDag-Erling Smørgrav /*
173346298f0SDag-Erling Smørgrav  * Send a command and check reply
1744ca1ab94SDag-Erling Smørgrav  */
1754ca1ab94SDag-Erling Smørgrav static int
176fc6e9e65SDag-Erling Smørgrav _ftp_cmd(int cd, char *fmt, ...)
1774ca1ab94SDag-Erling Smørgrav {
178346298f0SDag-Erling Smørgrav     va_list ap;
179fc6e9e65SDag-Erling Smørgrav     struct iovec iov[2];
180fc6e9e65SDag-Erling Smørgrav     char *msg;
181fc6e9e65SDag-Erling Smørgrav     int r;
1828e3986eaSDag-Erling Smørgrav 
183346298f0SDag-Erling Smørgrav     va_start(ap, fmt);
184fc6e9e65SDag-Erling Smørgrav     vasprintf(&msg, fmt, ap);
185346298f0SDag-Erling Smørgrav     va_end(ap);
186346298f0SDag-Erling Smørgrav 
187fc6e9e65SDag-Erling Smørgrav     if (msg == NULL) {
188fc6e9e65SDag-Erling Smørgrav 	errno = ENOMEM;
189fc6e9e65SDag-Erling Smørgrav 	_fetch_syserr();
190fc6e9e65SDag-Erling Smørgrav 	return -1;
191fc6e9e65SDag-Erling Smørgrav     }
192fc6e9e65SDag-Erling Smørgrav #ifndef NDEBUG
193fc6e9e65SDag-Erling Smørgrav     _fetch_info("sending '%s'", msg);
194fc6e9e65SDag-Erling Smørgrav #endif
195fc6e9e65SDag-Erling Smørgrav     iov[0].iov_base = msg;
196fc6e9e65SDag-Erling Smørgrav     iov[0].iov_len = strlen(msg);
197fc6e9e65SDag-Erling Smørgrav     iov[1].iov_base = ENDL;
19832425dafSDag-Erling Smørgrav     iov[1].iov_len = sizeof ENDL;
199fc6e9e65SDag-Erling Smørgrav     r = writev(cd, iov, 2);
200fc6e9e65SDag-Erling Smørgrav     free(msg);
201fc6e9e65SDag-Erling Smørgrav     if (r == -1) {
202fc6e9e65SDag-Erling Smørgrav 	_fetch_syserr();
203fc6e9e65SDag-Erling Smørgrav 	return -1;
204fc6e9e65SDag-Erling Smørgrav     }
205fc6e9e65SDag-Erling Smørgrav 
206fc6e9e65SDag-Erling Smørgrav     return _ftp_chkerr(cd);
2074ca1ab94SDag-Erling Smørgrav }
2084ca1ab94SDag-Erling Smørgrav 
2094ca1ab94SDag-Erling Smørgrav /*
210f62e5228SDag-Erling Smørgrav  * Transfer file
2114ca1ab94SDag-Erling Smørgrav  */
2124ca1ab94SDag-Erling Smørgrav static FILE *
21332425dafSDag-Erling Smørgrav _ftp_transfer(int cd, char *oper, char *file,
21432425dafSDag-Erling Smørgrav 	      char *mode, off_t offset, char *flags)
2154ca1ab94SDag-Erling Smørgrav {
21628c645cfSHajimu UMEMOTO     struct sockaddr_storage sin;
21728c645cfSHajimu UMEMOTO     struct sockaddr_in6 *sin6;
21828c645cfSHajimu UMEMOTO     struct sockaddr_in *sin4;
219f5f109a0SDag-Erling Smørgrav     int pasv, high, verbose;
220f5f109a0SDag-Erling Smørgrav     int e, sd = -1;
221f5f109a0SDag-Erling Smørgrav     socklen_t l;
222346298f0SDag-Erling Smørgrav     char *s;
223346298f0SDag-Erling Smørgrav     FILE *df;
2248e3986eaSDag-Erling Smørgrav 
225f5f109a0SDag-Erling Smørgrav     /* check flags */
226f5f109a0SDag-Erling Smørgrav     pasv = (flags && strchr(flags, 'p'));
227f5f109a0SDag-Erling Smørgrav     high = (flags && strchr(flags, 'h'));
228f5f109a0SDag-Erling Smørgrav     verbose = (flags && strchr(flags, 'v'));
229f5f109a0SDag-Erling Smørgrav 
230d02e84a6SDag-Erling Smørgrav     /* passive mode */
231d02e84a6SDag-Erling Smørgrav     if (!pasv && (s = getenv("FTP_PASSIVE_MODE")) != NULL)
232d02e84a6SDag-Erling Smørgrav 	pasv = (strncasecmp(s, "no", 2) != 0);
233d02e84a6SDag-Erling Smørgrav 
2344ca1ab94SDag-Erling Smørgrav     /* change directory */
235346298f0SDag-Erling Smørgrav     if (((s = strrchr(file, '/')) != NULL) && (s != file)) {
236346298f0SDag-Erling Smørgrav 	*s = 0;
237f5f109a0SDag-Erling Smørgrav 	if (verbose)
238f5f109a0SDag-Erling Smørgrav 	    _fetch_info("changing directory to %s", file);
239fc6e9e65SDag-Erling Smørgrav 	if ((e = _ftp_cmd(cd, "CWD %s", file)) != FTP_FILE_ACTION_OK) {
240346298f0SDag-Erling Smørgrav 	    *s = '/';
241fc6e9e65SDag-Erling Smørgrav 	    if (e != -1)
2425aea254fSDag-Erling Smørgrav 		_ftp_seterr(e);
2434ca1ab94SDag-Erling Smørgrav 	    return NULL;
2444ca1ab94SDag-Erling Smørgrav 	}
245346298f0SDag-Erling Smørgrav 	*s++ = '/';
2464ca1ab94SDag-Erling Smørgrav     } else {
247f5f109a0SDag-Erling Smørgrav 	if (verbose)
248f5f109a0SDag-Erling Smørgrav 	    _fetch_info("changing directory to /");
249fc6e9e65SDag-Erling Smørgrav 	if ((e = _ftp_cmd(cd, "CWD /")) != FTP_FILE_ACTION_OK) {
250fc6e9e65SDag-Erling Smørgrav 	    if (e != -1)
2515aea254fSDag-Erling Smørgrav 		_ftp_seterr(e);
2524ca1ab94SDag-Erling Smørgrav 	    return NULL;
2534ca1ab94SDag-Erling Smørgrav 	}
2545aea254fSDag-Erling Smørgrav     }
2554ca1ab94SDag-Erling Smørgrav 
256346298f0SDag-Erling Smørgrav     /* s now points to file name */
257346298f0SDag-Erling Smørgrav 
25828c645cfSHajimu UMEMOTO     /* find our own address, bind, and listen */
25928c645cfSHajimu UMEMOTO     l = sizeof sin;
26028c645cfSHajimu UMEMOTO     if (getsockname(cd, (struct sockaddr *)&sin, &l) == -1)
26128c645cfSHajimu UMEMOTO 	goto sysouch;
26228c645cfSHajimu UMEMOTO     if (sin.ss_family == AF_INET6)
26328c645cfSHajimu UMEMOTO 	unmappedaddr((struct sockaddr_in6 *)&sin);
26428c645cfSHajimu UMEMOTO 
265346298f0SDag-Erling Smørgrav     /* open data socket */
26628c645cfSHajimu UMEMOTO     if ((sd = socket(sin.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
267842a95ccSDag-Erling Smørgrav 	_fetch_syserr();
268346298f0SDag-Erling Smørgrav 	return NULL;
269346298f0SDag-Erling Smørgrav     }
270346298f0SDag-Erling Smørgrav 
271346298f0SDag-Erling Smørgrav     if (pasv) {
27228c645cfSHajimu UMEMOTO 	u_char addr[64];
273346298f0SDag-Erling Smørgrav 	char *ln, *p;
274346298f0SDag-Erling Smørgrav 	int i;
27528c645cfSHajimu UMEMOTO 	int port;
276346298f0SDag-Erling Smørgrav 
277346298f0SDag-Erling Smørgrav 	/* send PASV command */
278f5f109a0SDag-Erling Smørgrav 	if (verbose)
279f5f109a0SDag-Erling Smørgrav 	    _fetch_info("setting passive mode");
28028c645cfSHajimu UMEMOTO 	switch (sin.ss_family) {
28128c645cfSHajimu UMEMOTO 	case AF_INET:
282fc6e9e65SDag-Erling Smørgrav 	    if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE)
283346298f0SDag-Erling Smørgrav 		goto ouch;
28428c645cfSHajimu UMEMOTO 	    break;
28528c645cfSHajimu UMEMOTO 	case AF_INET6:
28628c645cfSHajimu UMEMOTO 	    if ((e = _ftp_cmd(cd, "EPSV")) != FTP_EPASSIVE_MODE) {
28728c645cfSHajimu UMEMOTO 		if (e == -1)
28828c645cfSHajimu UMEMOTO 		    goto ouch;
28928c645cfSHajimu UMEMOTO 		if ((e = _ftp_cmd(cd, "LPSV")) != FTP_LPASSIVE_MODE)
29028c645cfSHajimu UMEMOTO 		    goto ouch;
29128c645cfSHajimu UMEMOTO 	    }
29228c645cfSHajimu UMEMOTO 	    break;
29328c645cfSHajimu UMEMOTO 	default:
29428c645cfSHajimu UMEMOTO 	    e = 999;		/* XXX: error code should be prepared */
29528c645cfSHajimu UMEMOTO 	    goto ouch;
29628c645cfSHajimu UMEMOTO 	}
297346298f0SDag-Erling Smørgrav 
298f5f109a0SDag-Erling Smørgrav 	/*
299f5f109a0SDag-Erling Smørgrav 	 * Find address and port number. The reply to the PASV command
300f5f109a0SDag-Erling Smørgrav          * is IMHO the one and only weak point in the FTP protocol.
301f5f109a0SDag-Erling Smørgrav 	 */
302fc6e9e65SDag-Erling Smørgrav 	ln = last_reply;
30328c645cfSHajimu UMEMOTO 	for (p = ln + 3; *p && *p != '('; p++)
304346298f0SDag-Erling Smørgrav 	    /* nothing */ ;
30528c645cfSHajimu UMEMOTO 	if (!*p) {
3066efb30c8SDag-Erling Smørgrav 	    e = 999;
3076efb30c8SDag-Erling Smørgrav 	    goto ouch;
308346298f0SDag-Erling Smørgrav 	}
30928c645cfSHajimu UMEMOTO 	p++;
31028c645cfSHajimu UMEMOTO 	switch (e) {
31128c645cfSHajimu UMEMOTO 	case FTP_PASSIVE_MODE:
31228c645cfSHajimu UMEMOTO 	case FTP_LPASSIVE_MODE:
31328c645cfSHajimu UMEMOTO 	    l = (e == FTP_PASSIVE_MODE ? 6 : 21);
31428c645cfSHajimu UMEMOTO 	    for (i = 0; *p && i < l; i++, p++)
31528c645cfSHajimu UMEMOTO 		addr[i] = strtol(p, &p, 10);
31628c645cfSHajimu UMEMOTO 	    if (i < l) {
31728c645cfSHajimu UMEMOTO 		e = 999;
31828c645cfSHajimu UMEMOTO 		goto ouch;
31928c645cfSHajimu UMEMOTO 	    }
32028c645cfSHajimu UMEMOTO 	    break;
32128c645cfSHajimu UMEMOTO 	case FTP_EPASSIVE_MODE:
32228c645cfSHajimu UMEMOTO 	    if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2],
32328c645cfSHajimu UMEMOTO 		       &port, &addr[3]) != 5 ||
32428c645cfSHajimu UMEMOTO 		addr[0] != addr[1] ||
32528c645cfSHajimu UMEMOTO 		addr[0] != addr[2] || addr[0] != addr[3]) {
32628c645cfSHajimu UMEMOTO 		e = 999;
32728c645cfSHajimu UMEMOTO 		goto ouch;
32828c645cfSHajimu UMEMOTO 	    }
32928c645cfSHajimu UMEMOTO 	    break;
33028c645cfSHajimu UMEMOTO 	}
331346298f0SDag-Erling Smørgrav 
33232425dafSDag-Erling Smørgrav 	/* seek to required offset */
33332425dafSDag-Erling Smørgrav 	if (offset)
33432425dafSDag-Erling Smørgrav 	    if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK)
33532425dafSDag-Erling Smørgrav 		goto sysouch;
33632425dafSDag-Erling Smørgrav 
337346298f0SDag-Erling Smørgrav 	/* construct sockaddr for data socket */
33832425dafSDag-Erling Smørgrav 	l = sizeof sin;
339fc6e9e65SDag-Erling Smørgrav 	if (getpeername(cd, (struct sockaddr *)&sin, &l) == -1)
340346298f0SDag-Erling Smørgrav 	    goto sysouch;
34128c645cfSHajimu UMEMOTO 	if (sin.ss_family == AF_INET6)
34228c645cfSHajimu UMEMOTO 	    unmappedaddr((struct sockaddr_in6 *)&sin);
34328c645cfSHajimu UMEMOTO 	switch (sin.ss_family) {
34428c645cfSHajimu UMEMOTO 	case AF_INET6:
34528c645cfSHajimu UMEMOTO 	    sin6 = (struct sockaddr_in6 *)&sin;
34628c645cfSHajimu UMEMOTO 	    if (e == FTP_EPASSIVE_MODE)
34728c645cfSHajimu UMEMOTO 		sin6->sin6_port = htons(port);
34828c645cfSHajimu UMEMOTO 	    else {
34928c645cfSHajimu UMEMOTO 		bcopy(addr + 2, (char *)&sin6->sin6_addr, 16);
35028c645cfSHajimu UMEMOTO 		bcopy(addr + 19, (char *)&sin6->sin6_port, 2);
35128c645cfSHajimu UMEMOTO 	    }
35228c645cfSHajimu UMEMOTO 	    break;
35328c645cfSHajimu UMEMOTO 	case AF_INET:
35428c645cfSHajimu UMEMOTO 	    sin4 = (struct sockaddr_in *)&sin;
35528c645cfSHajimu UMEMOTO 	    if (e == FTP_EPASSIVE_MODE)
35628c645cfSHajimu UMEMOTO 		sin4->sin_port = htons(port);
35728c645cfSHajimu UMEMOTO 	    else {
35828c645cfSHajimu UMEMOTO 		bcopy(addr, (char *)&sin4->sin_addr, 4);
35928c645cfSHajimu UMEMOTO 		bcopy(addr + 4, (char *)&sin4->sin_port, 2);
36028c645cfSHajimu UMEMOTO 	    }
36128c645cfSHajimu UMEMOTO 	    break;
36228c645cfSHajimu UMEMOTO 	default:
36328c645cfSHajimu UMEMOTO 	    e = 999;		/* XXX: error code should be prepared */
36428c645cfSHajimu UMEMOTO 	    break;
36528c645cfSHajimu UMEMOTO 	}
366346298f0SDag-Erling Smørgrav 
367346298f0SDag-Erling Smørgrav 	/* connect to data port */
368f5f109a0SDag-Erling Smørgrav 	if (verbose)
369f5f109a0SDag-Erling Smørgrav 	    _fetch_info("opening data connection");
37028c645cfSHajimu UMEMOTO 	if (connect(sd, (struct sockaddr *)&sin, sin.ss_len) == -1)
371346298f0SDag-Erling Smørgrav 	    goto sysouch;
372346298f0SDag-Erling Smørgrav 
373346298f0SDag-Erling Smørgrav 	/* make the server initiate the transfer */
374f5f109a0SDag-Erling Smørgrav 	if (verbose)
375def5f54cSDag-Erling Smørgrav 	    _fetch_info("initiating transfer");
376fc6e9e65SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "%s %s", oper, s);
3775aea254fSDag-Erling Smørgrav 	if (e != FTP_OPEN_DATA_CONNECTION)
378346298f0SDag-Erling Smørgrav 	    goto ouch;
379346298f0SDag-Erling Smørgrav 
380346298f0SDag-Erling Smørgrav     } else {
381346298f0SDag-Erling Smørgrav 	u_int32_t a;
382346298f0SDag-Erling Smørgrav 	u_short p;
383f5f109a0SDag-Erling Smørgrav 	int arg, d;
38428c645cfSHajimu UMEMOTO 	char *ap;
38528c645cfSHajimu UMEMOTO 	char hname[INET6_ADDRSTRLEN];
386346298f0SDag-Erling Smørgrav 
38728c645cfSHajimu UMEMOTO 	switch (sin.ss_family) {
38828c645cfSHajimu UMEMOTO 	case AF_INET6:
38928c645cfSHajimu UMEMOTO 	    ((struct sockaddr_in6 *)&sin)->sin6_port = 0;
39028c645cfSHajimu UMEMOTO #ifdef IPV6_PORTRANGE
39128c645cfSHajimu UMEMOTO 	    arg = high ? IPV6_PORTRANGE_HIGH : IPV6_PORTRANGE_DEFAULT;
39228c645cfSHajimu UMEMOTO 	    if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE,
39328c645cfSHajimu UMEMOTO 			   (char *)&arg, sizeof(arg)) == -1)
394346298f0SDag-Erling Smørgrav 		goto sysouch;
39528c645cfSHajimu UMEMOTO #endif
39628c645cfSHajimu UMEMOTO 	    break;
39728c645cfSHajimu UMEMOTO 	case AF_INET:
39828c645cfSHajimu UMEMOTO 	    ((struct sockaddr_in *)&sin)->sin_port = 0;
399f5f109a0SDag-Erling Smørgrav 	    arg = high ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT;
400f5f109a0SDag-Erling Smørgrav 	    if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
40132425dafSDag-Erling Smørgrav 			   (char *)&arg, sizeof arg) == -1)
402f5f109a0SDag-Erling Smørgrav 		goto sysouch;
40328c645cfSHajimu UMEMOTO 	    break;
40428c645cfSHajimu UMEMOTO 	}
405f5f109a0SDag-Erling Smørgrav 	if (verbose)
406f5f109a0SDag-Erling Smørgrav 	    _fetch_info("binding data socket");
40728c645cfSHajimu UMEMOTO 	if (bind(sd, (struct sockaddr *)&sin, sin.ss_len) == -1)
408346298f0SDag-Erling Smørgrav 	    goto sysouch;
409ecc91352SDag-Erling Smørgrav 	if (listen(sd, 1) == -1)
410346298f0SDag-Erling Smørgrav 	    goto sysouch;
411346298f0SDag-Erling Smørgrav 
412346298f0SDag-Erling Smørgrav 	/* find what port we're on and tell the server */
413ecc91352SDag-Erling Smørgrav 	if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1)
414346298f0SDag-Erling Smørgrav 	    goto sysouch;
41528c645cfSHajimu UMEMOTO 	switch (sin.ss_family) {
41628c645cfSHajimu UMEMOTO 	case AF_INET:
41728c645cfSHajimu UMEMOTO 	    sin4 = (struct sockaddr_in *)&sin;
41828c645cfSHajimu UMEMOTO 	    a = ntohl(sin4->sin_addr.s_addr);
41928c645cfSHajimu UMEMOTO 	    p = ntohs(sin4->sin_port);
420fc6e9e65SDag-Erling Smørgrav 	    e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d",
4215aea254fSDag-Erling Smørgrav 			 (a >> 24) & 0xff, (a >> 16) & 0xff,
4225aea254fSDag-Erling Smørgrav 			 (a >> 8) & 0xff, a & 0xff,
4235aea254fSDag-Erling Smørgrav 			 (p >> 8) & 0xff, p & 0xff);
42428c645cfSHajimu UMEMOTO 	    break;
42528c645cfSHajimu UMEMOTO 	case AF_INET6:
42628c645cfSHajimu UMEMOTO #define UC(b)	(((int)b)&0xff)
42728c645cfSHajimu UMEMOTO 	    e = -1;
42828c645cfSHajimu UMEMOTO 	    sin6 = (struct sockaddr_in6 *)&sin;
42928c645cfSHajimu UMEMOTO 	    if (getnameinfo((struct sockaddr *)&sin, sin.ss_len,
43028c645cfSHajimu UMEMOTO 			    hname, sizeof(hname),
43128c645cfSHajimu UMEMOTO 			    NULL, 0, NI_NUMERICHOST) == 0) {
43228c645cfSHajimu UMEMOTO 		e = _ftp_cmd(cd, "EPRT |%d|%s|%d|", 2, hname,
43328c645cfSHajimu UMEMOTO 			     htons(sin6->sin6_port));
43428c645cfSHajimu UMEMOTO 		if (e == -1)
43528c645cfSHajimu UMEMOTO 		    goto ouch;
43628c645cfSHajimu UMEMOTO 	    }
43728c645cfSHajimu UMEMOTO 	    if (e != FTP_OK) {
43828c645cfSHajimu UMEMOTO 		ap = (char *)&sin6->sin6_addr;
43928c645cfSHajimu UMEMOTO 		e = _ftp_cmd(cd,
44028c645cfSHajimu UMEMOTO      "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
44128c645cfSHajimu UMEMOTO 			     6, 16,
44228c645cfSHajimu UMEMOTO 			     UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]),
44328c645cfSHajimu UMEMOTO 			     UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]),
44428c645cfSHajimu UMEMOTO 			     UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]),
44528c645cfSHajimu UMEMOTO 			     UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]),
44628c645cfSHajimu UMEMOTO 			     2,
44728c645cfSHajimu UMEMOTO 			     (ntohs(sin6->sin6_port) >> 8) & 0xff,
44828c645cfSHajimu UMEMOTO 			     ntohs(sin6->sin6_port)        & 0xff);
44928c645cfSHajimu UMEMOTO 	    }
45028c645cfSHajimu UMEMOTO 	    break;
45128c645cfSHajimu UMEMOTO 	default:
45228c645cfSHajimu UMEMOTO 	    e = 999;		/* XXX: error code should be prepared */
45328c645cfSHajimu UMEMOTO 	    goto ouch;
45428c645cfSHajimu UMEMOTO 	}
4555aea254fSDag-Erling Smørgrav 	if (e != FTP_OK)
456346298f0SDag-Erling Smørgrav 	    goto ouch;
457346298f0SDag-Erling Smørgrav 
458346298f0SDag-Erling Smørgrav 	/* make the server initiate the transfer */
459f5f109a0SDag-Erling Smørgrav 	if (verbose)
460f5f109a0SDag-Erling Smørgrav 	    _fetch_info("initiating transfer");
461fc6e9e65SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "%s %s", oper, s);
4625aea254fSDag-Erling Smørgrav 	if (e != FTP_OPEN_DATA_CONNECTION)
463346298f0SDag-Erling Smørgrav 	    goto ouch;
464346298f0SDag-Erling Smørgrav 
465346298f0SDag-Erling Smørgrav 	/* accept the incoming connection and go to town */
466ecc91352SDag-Erling Smørgrav 	if ((d = accept(sd, NULL, NULL)) == -1)
467346298f0SDag-Erling Smørgrav 	    goto sysouch;
468346298f0SDag-Erling Smørgrav 	close(sd);
469346298f0SDag-Erling Smørgrav 	sd = d;
470346298f0SDag-Erling Smørgrav     }
471346298f0SDag-Erling Smørgrav 
472f62e5228SDag-Erling Smørgrav     if ((df = fdopen(sd, mode)) == NULL)
473346298f0SDag-Erling Smørgrav 	goto sysouch;
474346298f0SDag-Erling Smørgrav     return df;
475346298f0SDag-Erling Smørgrav 
476346298f0SDag-Erling Smørgrav sysouch:
477842a95ccSDag-Erling Smørgrav     _fetch_syserr();
47828c645cfSHajimu UMEMOTO     if (sd >= 0)
4795aea254fSDag-Erling Smørgrav 	close(sd);
4805aea254fSDag-Erling Smørgrav     return NULL;
4815aea254fSDag-Erling Smørgrav 
482346298f0SDag-Erling Smørgrav ouch:
483fc6e9e65SDag-Erling Smørgrav     if (e != -1)
4845aea254fSDag-Erling Smørgrav 	_ftp_seterr(e);
48528c645cfSHajimu UMEMOTO     if (sd >= 0)
486346298f0SDag-Erling Smørgrav 	close(sd);
4874ca1ab94SDag-Erling Smørgrav     return NULL;
4884ca1ab94SDag-Erling Smørgrav }
4894ca1ab94SDag-Erling Smørgrav 
4908e3986eaSDag-Erling Smørgrav /*
4918e3986eaSDag-Erling Smørgrav  * Log on to FTP server
4924ca1ab94SDag-Erling Smørgrav  */
493fc6e9e65SDag-Erling Smørgrav static int
494f5f109a0SDag-Erling Smørgrav _ftp_connect(char *host, int port, char *user, char *pwd, char *flags)
4958e3986eaSDag-Erling Smørgrav {
49632425dafSDag-Erling Smørgrav     int cd, e, pp = 0, direct, verbose;
49728c645cfSHajimu UMEMOTO #ifdef INET6
49828c645cfSHajimu UMEMOTO     int af = AF_UNSPEC;
49928c645cfSHajimu UMEMOTO #else
50028c645cfSHajimu UMEMOTO     int af = AF_INET;
50128c645cfSHajimu UMEMOTO #endif
502f62e5228SDag-Erling Smørgrav     char *p, *q;
5034ab587f4SHajimu UMEMOTO     const char *logname;
5044ab587f4SHajimu UMEMOTO     char localhost[MAXHOSTNAMELEN];
5054ab587f4SHajimu UMEMOTO     char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1];
5068e3986eaSDag-Erling Smørgrav 
507f5f109a0SDag-Erling Smørgrav     direct = (flags && strchr(flags, 'd'));
508f5f109a0SDag-Erling Smørgrav     verbose = (flags && strchr(flags, 'v'));
50928c645cfSHajimu UMEMOTO     if ((flags && strchr(flags, '4')))
51028c645cfSHajimu UMEMOTO 	af = AF_INET;
51128c645cfSHajimu UMEMOTO     else if ((flags && strchr(flags, '6')))
51228c645cfSHajimu UMEMOTO 	af = AF_INET6;
513f5f109a0SDag-Erling Smørgrav 
514f62e5228SDag-Erling Smørgrav     /* check for proxy */
515f5f109a0SDag-Erling Smørgrav     if (!direct && (p = getenv("FTP_PROXY")) != NULL) {
51628c645cfSHajimu UMEMOTO 	char c = 0;
51728c645cfSHajimu UMEMOTO 
51828c645cfSHajimu UMEMOTO #ifdef INET6
51928c645cfSHajimu UMEMOTO 	if (*p != '[' || (q = strchr(p + 1, ']')) == NULL ||
52028c645cfSHajimu UMEMOTO 	    (*++q != '\0' && *q != ':'))
52128c645cfSHajimu UMEMOTO #endif
52228c645cfSHajimu UMEMOTO 	    q = strchr(p, ':');
52328c645cfSHajimu UMEMOTO 	if (q != NULL && *q == ':') {
52432425dafSDag-Erling Smørgrav 	    if (strspn(q+1, "0123456789") != strlen(q+1) || strlen(q+1) > 5) {
52532425dafSDag-Erling Smørgrav 		/* XXX we should emit some kind of warning */
52632425dafSDag-Erling Smørgrav 	    }
527f62e5228SDag-Erling Smørgrav 	    pp = atoi(q+1);
52832425dafSDag-Erling Smørgrav 	    if (pp < 1 || pp > 65535) {
52932425dafSDag-Erling Smørgrav 		/* XXX we should emit some kind of warning */
53032425dafSDag-Erling Smørgrav 	    }
53132425dafSDag-Erling Smørgrav 	}
53232425dafSDag-Erling Smørgrav 	if (!pp) {
53332425dafSDag-Erling Smørgrav 	    struct servent *se;
53432425dafSDag-Erling Smørgrav 
53532425dafSDag-Erling Smørgrav 	    if ((se = getservbyname("ftp", "tcp")) != NULL)
53632425dafSDag-Erling Smørgrav 		pp = ntohs(se->s_port);
53732425dafSDag-Erling Smørgrav 	    else
53832425dafSDag-Erling Smørgrav 		pp = FTP_DEFAULT_PORT;
539f62e5228SDag-Erling Smørgrav 	}
54028c645cfSHajimu UMEMOTO 	if (q) {
54128c645cfSHajimu UMEMOTO #ifdef INET6
54228c645cfSHajimu UMEMOTO 	    if (q > p && *p == '[' && *(q - 1) == ']') {
54328c645cfSHajimu UMEMOTO 		p++;
54428c645cfSHajimu UMEMOTO 		q--;
54528c645cfSHajimu UMEMOTO 	    }
54628c645cfSHajimu UMEMOTO #endif
54728c645cfSHajimu UMEMOTO 	    c = *q;
548f62e5228SDag-Erling Smørgrav 	    *q = 0;
54928c645cfSHajimu UMEMOTO 	}
55028c645cfSHajimu UMEMOTO 	cd = _fetch_connect(p, pp, af, verbose);
551f62e5228SDag-Erling Smørgrav 	if (q)
55228c645cfSHajimu UMEMOTO 	    *q = c;
553f62e5228SDag-Erling Smørgrav     } else {
554f62e5228SDag-Erling Smørgrav 	/* no proxy, go straight to target */
55528c645cfSHajimu UMEMOTO 	cd = _fetch_connect(host, port, af, verbose);
556f5f109a0SDag-Erling Smørgrav 	p = NULL;
557f62e5228SDag-Erling Smørgrav     }
558f62e5228SDag-Erling Smørgrav 
559f62e5228SDag-Erling Smørgrav     /* check connection */
560fc6e9e65SDag-Erling Smørgrav     if (cd == -1) {
561842a95ccSDag-Erling Smørgrav 	_fetch_syserr();
5628e3986eaSDag-Erling Smørgrav 	return NULL;
5638e3986eaSDag-Erling Smørgrav     }
564f62e5228SDag-Erling Smørgrav 
5658e3986eaSDag-Erling Smørgrav     /* expect welcome message */
566fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY)
5678e3986eaSDag-Erling Smørgrav 	goto fouch;
5688e3986eaSDag-Erling Smørgrav 
5698e3986eaSDag-Erling Smørgrav     /* send user name and password */
570f62e5228SDag-Erling Smørgrav     if (!user || !*user)
571f62e5228SDag-Erling Smørgrav 	user = FTP_ANONYMOUS_USER;
572fc6e9e65SDag-Erling Smørgrav     e = p ? _ftp_cmd(cd, "USER %s@%s@%d", user, host, port)
573fc6e9e65SDag-Erling Smørgrav 	  : _ftp_cmd(cd, "USER %s", user);
574f62e5228SDag-Erling Smørgrav 
575f62e5228SDag-Erling Smørgrav     /* did the server request a password? */
576f62e5228SDag-Erling Smørgrav     if (e == FTP_NEED_PASSWORD) {
577f62e5228SDag-Erling Smørgrav 	if (!pwd || !*pwd)
5784ab587f4SHajimu UMEMOTO 	    pwd = getenv("FTP_PASSWORD");
5794ab587f4SHajimu UMEMOTO 	if (!pwd || !*pwd) {
5804ab587f4SHajimu UMEMOTO 	    if ((logname = getlogin()) == 0)
5814ab587f4SHajimu UMEMOTO 		logname = FTP_ANONYMOUS_PASSWORD;
5824ab587f4SHajimu UMEMOTO 	    gethostname(localhost, sizeof localhost);
5834ab587f4SHajimu UMEMOTO 	    snprintf(pbuf, sizeof pbuf, "%s@%s", logname, localhost);
5844ab587f4SHajimu UMEMOTO 	    pwd = pbuf;
5854ab587f4SHajimu UMEMOTO 	}
586fc6e9e65SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "PASS %s", pwd);
587f62e5228SDag-Erling Smørgrav     }
588f62e5228SDag-Erling Smørgrav 
589f62e5228SDag-Erling Smørgrav     /* did the server request an account? */
5905aea254fSDag-Erling Smørgrav     if (e == FTP_NEED_ACCOUNT)
5913b7a6740SDag-Erling Smørgrav 	goto fouch;
592f62e5228SDag-Erling Smørgrav 
593f62e5228SDag-Erling Smørgrav     /* we should be done by now */
5945aea254fSDag-Erling Smørgrav     if (e != FTP_LOGGED_IN)
5958e3986eaSDag-Erling Smørgrav 	goto fouch;
5968e3986eaSDag-Erling Smørgrav 
5978e3986eaSDag-Erling Smørgrav     /* might as well select mode and type at once */
5988e3986eaSDag-Erling Smørgrav #ifdef FTP_FORCE_STREAM_MODE
599fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */
6005aea254fSDag-Erling Smørgrav 	goto fouch;
6018e3986eaSDag-Erling Smørgrav #endif
602fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */
6035aea254fSDag-Erling Smørgrav 	goto fouch;
6048e3986eaSDag-Erling Smørgrav 
6058e3986eaSDag-Erling Smørgrav     /* done */
606fc6e9e65SDag-Erling Smørgrav     return cd;
6078e3986eaSDag-Erling Smørgrav 
6088e3986eaSDag-Erling Smørgrav fouch:
609fc6e9e65SDag-Erling Smørgrav     if (e != -1)
6105aea254fSDag-Erling Smørgrav 	_ftp_seterr(e);
611fc6e9e65SDag-Erling Smørgrav     close(cd);
6128e3986eaSDag-Erling Smørgrav     return NULL;
6138e3986eaSDag-Erling Smørgrav }
6148e3986eaSDag-Erling Smørgrav 
6158e3986eaSDag-Erling Smørgrav /*
6168e3986eaSDag-Erling Smørgrav  * Disconnect from server
6178e3986eaSDag-Erling Smørgrav  */
6188e3986eaSDag-Erling Smørgrav static void
619fc6e9e65SDag-Erling Smørgrav _ftp_disconnect(int cd)
6208e3986eaSDag-Erling Smørgrav {
621fc6e9e65SDag-Erling Smørgrav     (void)_ftp_cmd(cd, "QUIT");
622fc6e9e65SDag-Erling Smørgrav     close(cd);
6238e3986eaSDag-Erling Smørgrav }
6248e3986eaSDag-Erling Smørgrav 
6258e3986eaSDag-Erling Smørgrav /*
6268e3986eaSDag-Erling Smørgrav  * Check if we're already connected
6278e3986eaSDag-Erling Smørgrav  */
6288e3986eaSDag-Erling Smørgrav static int
629d8acd8dcSDag-Erling Smørgrav _ftp_isconnected(struct url *url)
6308e3986eaSDag-Erling Smørgrav {
6318e3986eaSDag-Erling Smørgrav     return (cached_socket
6328e3986eaSDag-Erling Smørgrav 	    && (strcmp(url->host, cached_host.host) == 0)
6338e3986eaSDag-Erling Smørgrav 	    && (strcmp(url->user, cached_host.user) == 0)
6348e3986eaSDag-Erling Smørgrav 	    && (strcmp(url->pwd, cached_host.pwd) == 0)
6358e3986eaSDag-Erling Smørgrav 	    && (url->port == cached_host.port));
6368e3986eaSDag-Erling Smørgrav }
6378e3986eaSDag-Erling Smørgrav 
638f62e5228SDag-Erling Smørgrav /*
6395aea254fSDag-Erling Smørgrav  * Check the cache, reconnect if no luck
640f62e5228SDag-Erling Smørgrav  */
641fc6e9e65SDag-Erling Smørgrav static int
6425aea254fSDag-Erling Smørgrav _ftp_cached_connect(struct url *url, char *flags)
6434ca1ab94SDag-Erling Smørgrav {
644fc6e9e65SDag-Erling Smørgrav     int e, cd;
6455aea254fSDag-Erling Smørgrav 
646fc6e9e65SDag-Erling Smørgrav     cd = -1;
6478e3986eaSDag-Erling Smørgrav 
6488e3986eaSDag-Erling Smørgrav     /* set default port */
64932425dafSDag-Erling Smørgrav     if (!url->port) {
65032425dafSDag-Erling Smørgrav 	struct servent *se;
65132425dafSDag-Erling Smørgrav 
65232425dafSDag-Erling Smørgrav 	if ((se = getservbyname("ftp", "tcp")) != NULL)
65332425dafSDag-Erling Smørgrav 	    url->port = ntohs(se->s_port);
65432425dafSDag-Erling Smørgrav 	else
655346298f0SDag-Erling Smørgrav 	    url->port = FTP_DEFAULT_PORT;
65632425dafSDag-Erling Smørgrav     }
6578e3986eaSDag-Erling Smørgrav 
6583b7a6740SDag-Erling Smørgrav     /* try to use previously cached connection */
659fc6e9e65SDag-Erling Smørgrav     if (_ftp_isconnected(url)) {
660fc6e9e65SDag-Erling Smørgrav 	e = _ftp_cmd(cached_socket, "NOOP");
661fc6e9e65SDag-Erling Smørgrav 	if (e == FTP_OK || e == FTP_SYNTAX_ERROR)
662fc6e9e65SDag-Erling Smørgrav 	    cd = cached_socket;
663fc6e9e65SDag-Erling Smørgrav     }
6644ca1ab94SDag-Erling Smørgrav 
6658e3986eaSDag-Erling Smørgrav     /* connect to server */
666fc6e9e65SDag-Erling Smørgrav     if (cd == -1) {
667fc6e9e65SDag-Erling Smørgrav 	cd = _ftp_connect(url->host, url->port, url->user, url->pwd, flags);
668fc6e9e65SDag-Erling Smørgrav 	if (cd == -1)
669fc6e9e65SDag-Erling Smørgrav 	    return -1;
6708e3986eaSDag-Erling Smørgrav 	if (cached_socket)
6718e3986eaSDag-Erling Smørgrav 	    _ftp_disconnect(cached_socket);
672fc6e9e65SDag-Erling Smørgrav 	cached_socket = cd;
67332425dafSDag-Erling Smørgrav 	memcpy(&cached_host, url, sizeof *url);
6748e3986eaSDag-Erling Smørgrav     }
6758e3986eaSDag-Erling Smørgrav 
676fc6e9e65SDag-Erling Smørgrav     return cd;
6778e3986eaSDag-Erling Smørgrav }
6788e3986eaSDag-Erling Smørgrav 
6798e3986eaSDag-Erling Smørgrav /*
6805aea254fSDag-Erling Smørgrav  * Get file
6818e3986eaSDag-Erling Smørgrav  */
6824ca1ab94SDag-Erling Smørgrav FILE *
683d8acd8dcSDag-Erling Smørgrav fetchGetFTP(struct url *url, char *flags)
684f62e5228SDag-Erling Smørgrav {
685fc6e9e65SDag-Erling Smørgrav     int cd;
6865aea254fSDag-Erling Smørgrav 
6875aea254fSDag-Erling Smørgrav     /* connect to server */
688fc6e9e65SDag-Erling Smørgrav     if ((cd = _ftp_cached_connect(url, flags)) == NULL)
6895aea254fSDag-Erling Smørgrav 	return NULL;
6905aea254fSDag-Erling Smørgrav 
6915aea254fSDag-Erling Smørgrav     /* initiate the transfer */
69232425dafSDag-Erling Smørgrav     return _ftp_transfer(cd, "RETR", url->doc, "r", url->offset, flags);
693f62e5228SDag-Erling Smørgrav }
694f62e5228SDag-Erling Smørgrav 
6955aea254fSDag-Erling Smørgrav /*
6965aea254fSDag-Erling Smørgrav  * Put file
6975aea254fSDag-Erling Smørgrav  */
698f62e5228SDag-Erling Smørgrav FILE *
699d8acd8dcSDag-Erling Smørgrav fetchPutFTP(struct url *url, char *flags)
7004ca1ab94SDag-Erling Smørgrav {
701fc6e9e65SDag-Erling Smørgrav     int cd;
7025aea254fSDag-Erling Smørgrav 
7035aea254fSDag-Erling Smørgrav     /* connect to server */
704fc6e9e65SDag-Erling Smørgrav     if ((cd = _ftp_cached_connect(url, flags)) == NULL)
7055aea254fSDag-Erling Smørgrav 	return NULL;
7065aea254fSDag-Erling Smørgrav 
7075aea254fSDag-Erling Smørgrav     /* initiate the transfer */
708fc6e9e65SDag-Erling Smørgrav     return _ftp_transfer(cd, (flags && strchr(flags, 'a')) ? "APPE" : "STOR",
70932425dafSDag-Erling Smørgrav 			 url->doc, "w", url->offset, flags);
7108e3986eaSDag-Erling Smørgrav }
711d8acd8dcSDag-Erling Smørgrav 
7125aea254fSDag-Erling Smørgrav /*
7135aea254fSDag-Erling Smørgrav  * Get file stats
7145aea254fSDag-Erling Smørgrav  */
715d8acd8dcSDag-Erling Smørgrav int
716d8acd8dcSDag-Erling Smørgrav fetchStatFTP(struct url *url, struct url_stat *us, char *flags)
717d8acd8dcSDag-Erling Smørgrav {
7185aea254fSDag-Erling Smørgrav     char *ln, *s;
7195aea254fSDag-Erling Smørgrav     struct tm tm;
7205aea254fSDag-Erling Smørgrav     time_t t;
721fc6e9e65SDag-Erling Smørgrav     int e, cd;
7225aea254fSDag-Erling Smørgrav 
7230669702cSDag-Erling Smørgrav     us->size = -1;
7240669702cSDag-Erling Smørgrav     us->atime = us->mtime = 0;
7250669702cSDag-Erling Smørgrav 
7265aea254fSDag-Erling Smørgrav     /* connect to server */
727fc6e9e65SDag-Erling Smørgrav     if ((cd = _ftp_cached_connect(url, flags)) == NULL)
7285aea254fSDag-Erling Smørgrav 	return -1;
7295aea254fSDag-Erling Smørgrav 
7305aea254fSDag-Erling Smørgrav     /* change directory */
7315aea254fSDag-Erling Smørgrav     if (((s = strrchr(url->doc, '/')) != NULL) && (s != url->doc)) {
7325aea254fSDag-Erling Smørgrav 	*s = 0;
733fc6e9e65SDag-Erling Smørgrav 	if ((e = _ftp_cmd(cd, "CWD %s", url->doc)) != FTP_FILE_ACTION_OK) {
7345aea254fSDag-Erling Smørgrav 	    *s = '/';
7355aea254fSDag-Erling Smørgrav 	    goto ouch;
7365aea254fSDag-Erling Smørgrav 	}
7375aea254fSDag-Erling Smørgrav 	*s++ = '/';
7385aea254fSDag-Erling Smørgrav     } else {
739fc6e9e65SDag-Erling Smørgrav 	if ((e = _ftp_cmd(cd, "CWD /")) != FTP_FILE_ACTION_OK)
7405aea254fSDag-Erling Smørgrav 	    goto ouch;
7415aea254fSDag-Erling Smørgrav     }
7425aea254fSDag-Erling Smørgrav 
7435aea254fSDag-Erling Smørgrav     /* s now points to file name */
7445aea254fSDag-Erling Smørgrav 
745fc6e9e65SDag-Erling Smørgrav     if (_ftp_cmd(cd, "SIZE %s", s) != FTP_FILE_STATUS)
7465aea254fSDag-Erling Smørgrav 	goto ouch;
747fc6e9e65SDag-Erling Smørgrav     for (ln = last_reply + 4; *ln && isspace(*ln); ln++)
7485aea254fSDag-Erling Smørgrav 	/* nothing */ ;
7495aea254fSDag-Erling Smørgrav     for (us->size = 0; *ln && isdigit(*ln); ln++)
7505aea254fSDag-Erling Smørgrav 	us->size = us->size * 10 + *ln - '0';
7515aea254fSDag-Erling Smørgrav     if (*ln && !isspace(*ln)) {
752fc6e9e65SDag-Erling Smørgrav 	_ftp_seterr(999);
7535aea254fSDag-Erling Smørgrav 	return -1;
7545aea254fSDag-Erling Smørgrav     }
75589474d12SDag-Erling Smørgrav     DEBUG(fprintf(stderr, "size: [\033[1m%lld\033[m]\n", us->size));
7565aea254fSDag-Erling Smørgrav 
757fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "MDTM %s", s)) != FTP_FILE_STATUS)
7585aea254fSDag-Erling Smørgrav 	goto ouch;
759fc6e9e65SDag-Erling Smørgrav     for (ln = last_reply + 4; *ln && isspace(*ln); ln++)
7605aea254fSDag-Erling Smørgrav 	/* nothing */ ;
76189474d12SDag-Erling Smørgrav     e = 999;
76289474d12SDag-Erling Smørgrav     switch (strspn(ln, "0123456789")) {
76389474d12SDag-Erling Smørgrav     case 14:
76489474d12SDag-Erling Smørgrav 	break;
76589474d12SDag-Erling Smørgrav     case 15:
76689474d12SDag-Erling Smørgrav 	ln++;
76789474d12SDag-Erling Smørgrav 	ln[0] = '2';
76889474d12SDag-Erling Smørgrav 	ln[1] = '0';
76989474d12SDag-Erling Smørgrav 	break;
77089474d12SDag-Erling Smørgrav     default:
77189474d12SDag-Erling Smørgrav 	goto ouch;
77289474d12SDag-Erling Smørgrav     }
77389474d12SDag-Erling Smørgrav     if (sscanf(ln, "%04d%02d%02d%02d%02d%02d",
7745aea254fSDag-Erling Smørgrav 	       &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
77589474d12SDag-Erling Smørgrav 	       &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6)
77689474d12SDag-Erling Smørgrav 	goto ouch;
7775aea254fSDag-Erling Smørgrav     tm.tm_mon--;
7785aea254fSDag-Erling Smørgrav     tm.tm_year -= 1900;
7795aea254fSDag-Erling Smørgrav     tm.tm_isdst = -1;
78089474d12SDag-Erling Smørgrav     t = timegm(&tm);
7815d32c97cSDag-Erling Smørgrav     if (t == (time_t)-1)
7825d32c97cSDag-Erling Smørgrav 	t = time(NULL);
7835d32c97cSDag-Erling Smørgrav     us->mtime = t;
7845d32c97cSDag-Erling Smørgrav     us->atime = t;
78589474d12SDag-Erling Smørgrav     DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d "
78689474d12SDag-Erling Smørgrav 		  "%02d:%02d:%02d\033[m]\n",
78789474d12SDag-Erling Smørgrav 		  tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
78889474d12SDag-Erling Smørgrav 		  tm.tm_hour, tm.tm_min, tm.tm_sec));
7895aea254fSDag-Erling Smørgrav     return 0;
7905aea254fSDag-Erling Smørgrav 
7915aea254fSDag-Erling Smørgrav ouch:
792fc6e9e65SDag-Erling Smørgrav     if (e != -1)
7935aea254fSDag-Erling Smørgrav 	_ftp_seterr(e);
794d8acd8dcSDag-Erling Smørgrav     return -1;
795d8acd8dcSDag-Erling Smørgrav }
796ce71b736SDag-Erling Smørgrav 
797ce71b736SDag-Erling Smørgrav /*
798ce71b736SDag-Erling Smørgrav  * List a directory
799ce71b736SDag-Erling Smørgrav  */
800ce71b736SDag-Erling Smørgrav extern void warnx(char *, ...);
801ce71b736SDag-Erling Smørgrav struct url_ent *
802ce71b736SDag-Erling Smørgrav fetchListFTP(struct url *url, char *flags)
803ce71b736SDag-Erling Smørgrav {
804def5f54cSDag-Erling Smørgrav     warnx("fetchListFTP(): not implemented");
805ce71b736SDag-Erling Smørgrav     return NULL;
806ce71b736SDag-Erling Smørgrav }
807