xref: /freebsd/lib/libfetch/ftp.c (revision 10851dc4ad91601a58cf679252ebc43d2f17e1c9)
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>
604ca1ab94SDag-Erling Smørgrav #include <netinet/in.h>
614ca1ab94SDag-Erling Smørgrav 
624ca1ab94SDag-Erling Smørgrav #include <ctype.h>
63fc6e9e65SDag-Erling Smørgrav #include <errno.h>
6432425dafSDag-Erling Smørgrav #include <netdb.h>
65346298f0SDag-Erling Smørgrav #include <stdarg.h>
664ca1ab94SDag-Erling Smørgrav #include <stdio.h>
678e3986eaSDag-Erling Smørgrav #include <stdlib.h>
684ca1ab94SDag-Erling Smørgrav #include <string.h>
695aea254fSDag-Erling Smørgrav #include <time.h>
708e3986eaSDag-Erling Smørgrav #include <unistd.h>
714ca1ab94SDag-Erling Smørgrav 
724ca1ab94SDag-Erling Smørgrav #include "fetch.h"
73842a95ccSDag-Erling Smørgrav #include "common.h"
740fba3a00SDag-Erling Smørgrav #include "ftperr.h"
754ca1ab94SDag-Erling Smørgrav 
764ca1ab94SDag-Erling Smørgrav #define FTP_ANONYMOUS_USER	"ftp"
774ca1ab94SDag-Erling Smørgrav #define FTP_ANONYMOUS_PASSWORD	"ftp"
78346298f0SDag-Erling Smørgrav 
79346298f0SDag-Erling Smørgrav #define FTP_OPEN_DATA_CONNECTION	150
80346298f0SDag-Erling Smørgrav #define FTP_OK				200
815aea254fSDag-Erling Smørgrav #define FTP_FILE_STATUS			213
823b7a6740SDag-Erling Smørgrav #define FTP_SERVICE_READY		220
83346298f0SDag-Erling Smørgrav #define FTP_PASSIVE_MODE		227
8428c645cfSHajimu UMEMOTO #define FTP_LPASSIVE_MODE		228
8528c645cfSHajimu UMEMOTO #define FTP_EPASSIVE_MODE		229
86346298f0SDag-Erling Smørgrav #define FTP_LOGGED_IN			230
87346298f0SDag-Erling Smørgrav #define FTP_FILE_ACTION_OK		250
88346298f0SDag-Erling Smørgrav #define FTP_NEED_PASSWORD		331
89346298f0SDag-Erling Smørgrav #define FTP_NEED_ACCOUNT		332
9032425dafSDag-Erling Smørgrav #define FTP_FILE_OK			350
91fc6e9e65SDag-Erling Smørgrav #define FTP_SYNTAX_ERROR		500
925cd33c40SDag-Erling Smørgrav #define FTP_PROTOCOL_ERROR		999
934ca1ab94SDag-Erling Smørgrav 
94d8acd8dcSDag-Erling Smørgrav static struct url cached_host;
95fc6e9e65SDag-Erling Smørgrav static int cached_socket;
964ca1ab94SDag-Erling Smørgrav 
97fc6e9e65SDag-Erling Smørgrav static char *last_reply;
98fc6e9e65SDag-Erling Smørgrav static size_t lr_size, lr_length;
99fc6e9e65SDag-Erling Smørgrav static int last_code;
100fc6e9e65SDag-Erling Smørgrav 
101fc6e9e65SDag-Erling Smørgrav #define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
1026efb30c8SDag-Erling Smørgrav 			 && isdigit(foo[2]) \
1036efb30c8SDag-Erling Smørgrav                          && (foo[3] == ' ' || foo[3] == '\0'))
104fc6e9e65SDag-Erling Smørgrav #define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
105fc6e9e65SDag-Erling Smørgrav 			&& isdigit(foo[2]) && foo[3] == '-')
1064ca1ab94SDag-Erling Smørgrav 
10728c645cfSHajimu UMEMOTO /* translate IPv4 mapped IPv6 address to IPv4 address */
10828c645cfSHajimu UMEMOTO static void
10928c645cfSHajimu UMEMOTO unmappedaddr(struct sockaddr_in6 *sin6)
11028c645cfSHajimu UMEMOTO {
11128c645cfSHajimu UMEMOTO     struct sockaddr_in *sin4;
11228c645cfSHajimu UMEMOTO     u_int32_t addr;
11328c645cfSHajimu UMEMOTO     int port;
11428c645cfSHajimu UMEMOTO 
11528c645cfSHajimu UMEMOTO     if (sin6->sin6_family != AF_INET6 ||
11628c645cfSHajimu UMEMOTO 	!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
11728c645cfSHajimu UMEMOTO 	return;
11828c645cfSHajimu UMEMOTO     sin4 = (struct sockaddr_in *)sin6;
11928c645cfSHajimu UMEMOTO     addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
12028c645cfSHajimu UMEMOTO     port = sin6->sin6_port;
12128c645cfSHajimu UMEMOTO     memset(sin4, 0, sizeof(struct sockaddr_in));
12228c645cfSHajimu UMEMOTO     sin4->sin_addr.s_addr = addr;
12328c645cfSHajimu UMEMOTO     sin4->sin_port = port;
12428c645cfSHajimu UMEMOTO     sin4->sin_family = AF_INET;
12528c645cfSHajimu UMEMOTO     sin4->sin_len = sizeof(struct sockaddr_in);
12628c645cfSHajimu UMEMOTO }
12728c645cfSHajimu UMEMOTO 
1284ca1ab94SDag-Erling Smørgrav /*
129fc6e9e65SDag-Erling Smørgrav  * Get server response
1308e3986eaSDag-Erling Smørgrav  */
1318e3986eaSDag-Erling Smørgrav static int
132fc6e9e65SDag-Erling Smørgrav _ftp_chkerr(int cd)
1338e3986eaSDag-Erling Smørgrav {
134fc6e9e65SDag-Erling Smørgrav     if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) {
135842a95ccSDag-Erling Smørgrav 	_fetch_syserr();
1368e3986eaSDag-Erling Smørgrav 	return -1;
1378e3986eaSDag-Erling Smørgrav     }
138eac7a1e0SDag-Erling Smørgrav     if (isftpinfo(last_reply)) {
139eac7a1e0SDag-Erling Smørgrav 	while (!isftpreply(last_reply)) {
140eac7a1e0SDag-Erling Smørgrav 	    if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) {
141eac7a1e0SDag-Erling Smørgrav 		_fetch_syserr();
142eac7a1e0SDag-Erling Smørgrav 		return -1;
143eac7a1e0SDag-Erling Smørgrav 	    }
144eac7a1e0SDag-Erling Smørgrav 	}
145eac7a1e0SDag-Erling Smørgrav     }
146346298f0SDag-Erling Smørgrav 
147fc6e9e65SDag-Erling Smørgrav     while (lr_length && isspace(last_reply[lr_length-1]))
148fc6e9e65SDag-Erling Smørgrav 	lr_length--;
149fc6e9e65SDag-Erling Smørgrav     last_reply[lr_length] = 0;
150fc6e9e65SDag-Erling Smørgrav 
151fc6e9e65SDag-Erling Smørgrav     if (!isftpreply(last_reply)) {
1525cd33c40SDag-Erling Smørgrav 	_ftp_seterr(FTP_PROTOCOL_ERROR);
1538e3986eaSDag-Erling Smørgrav 	return -1;
1548e3986eaSDag-Erling Smørgrav     }
1558e3986eaSDag-Erling Smørgrav 
156fc6e9e65SDag-Erling Smørgrav     last_code = (last_reply[0] - '0') * 100
157fc6e9e65SDag-Erling Smørgrav 	+ (last_reply[1] - '0') * 10
158fc6e9e65SDag-Erling Smørgrav 	+ (last_reply[2] - '0');
159fc6e9e65SDag-Erling Smørgrav 
160fc6e9e65SDag-Erling Smørgrav     return last_code;
1618e3986eaSDag-Erling Smørgrav }
1628e3986eaSDag-Erling Smørgrav 
1638e3986eaSDag-Erling Smørgrav /*
164346298f0SDag-Erling Smørgrav  * Send a command and check reply
1654ca1ab94SDag-Erling Smørgrav  */
1664ca1ab94SDag-Erling Smørgrav static int
167fc6e9e65SDag-Erling Smørgrav _ftp_cmd(int cd, char *fmt, ...)
1684ca1ab94SDag-Erling Smørgrav {
169346298f0SDag-Erling Smørgrav     va_list ap;
170e137bcebSDag-Erling Smørgrav     size_t len;
171fc6e9e65SDag-Erling Smørgrav     char *msg;
172fc6e9e65SDag-Erling Smørgrav     int r;
1738e3986eaSDag-Erling Smørgrav 
174346298f0SDag-Erling Smørgrav     va_start(ap, fmt);
175e137bcebSDag-Erling Smørgrav     len = vasprintf(&msg, fmt, ap);
176346298f0SDag-Erling Smørgrav     va_end(ap);
177346298f0SDag-Erling Smørgrav 
178fc6e9e65SDag-Erling Smørgrav     if (msg == NULL) {
179fc6e9e65SDag-Erling Smørgrav 	errno = ENOMEM;
180fc6e9e65SDag-Erling Smørgrav 	_fetch_syserr();
181fc6e9e65SDag-Erling Smørgrav 	return -1;
182fc6e9e65SDag-Erling Smørgrav     }
183e137bcebSDag-Erling Smørgrav 
184e137bcebSDag-Erling Smørgrav     r = _fetch_putln(cd, msg, len);
185fc6e9e65SDag-Erling Smørgrav     free(msg);
186e137bcebSDag-Erling Smørgrav 
187fc6e9e65SDag-Erling Smørgrav     if (r == -1) {
188fc6e9e65SDag-Erling Smørgrav 	_fetch_syserr();
189fc6e9e65SDag-Erling Smørgrav 	return -1;
190fc6e9e65SDag-Erling Smørgrav     }
191fc6e9e65SDag-Erling Smørgrav 
192fc6e9e65SDag-Erling Smørgrav     return _ftp_chkerr(cd);
1934ca1ab94SDag-Erling Smørgrav }
1944ca1ab94SDag-Erling Smørgrav 
1954ca1ab94SDag-Erling Smørgrav /*
1961a5faa10SDag-Erling Smørgrav  * Return a pointer to the filename part of a path
1971a5faa10SDag-Erling Smørgrav  */
1981a5faa10SDag-Erling Smørgrav static char *
1991a5faa10SDag-Erling Smørgrav _ftp_filename(char *file)
2001a5faa10SDag-Erling Smørgrav {
2011a5faa10SDag-Erling Smørgrav     char *s;
2021a5faa10SDag-Erling Smørgrav 
2031a5faa10SDag-Erling Smørgrav     if ((s = strrchr(file, '/')) == NULL)
2041a5faa10SDag-Erling Smørgrav 	return file;
2051a5faa10SDag-Erling Smørgrav     else
2061a5faa10SDag-Erling Smørgrav 	return s + 1;
2071a5faa10SDag-Erling Smørgrav }
2081a5faa10SDag-Erling Smørgrav 
2091a5faa10SDag-Erling Smørgrav /*
2101a5faa10SDag-Erling Smørgrav  * Change working directory to the directory that contains the
2111a5faa10SDag-Erling Smørgrav  * specified file.
2121a5faa10SDag-Erling Smørgrav  */
2131a5faa10SDag-Erling Smørgrav static int
2141a5faa10SDag-Erling Smørgrav _ftp_cwd(int cd, char *file)
2151a5faa10SDag-Erling Smørgrav {
2161a5faa10SDag-Erling Smørgrav     char *s;
2171a5faa10SDag-Erling Smørgrav     int e;
2181a5faa10SDag-Erling Smørgrav 
2195e3f46b5SDag-Erling Smørgrav     if ((s = strrchr(file, '/')) == NULL || s == file) {
2201a5faa10SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "CWD /");
2211a5faa10SDag-Erling Smørgrav     } else {
2221a5faa10SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "CWD %.*s", s - file, file);
2231a5faa10SDag-Erling Smørgrav     }
2241a5faa10SDag-Erling Smørgrav     if (e != FTP_FILE_ACTION_OK) {
2251a5faa10SDag-Erling Smørgrav 	_ftp_seterr(e);
2261a5faa10SDag-Erling Smørgrav 	return -1;
2271a5faa10SDag-Erling Smørgrav     }
2281a5faa10SDag-Erling Smørgrav     return 0;
2291a5faa10SDag-Erling Smørgrav }
2301a5faa10SDag-Erling Smørgrav 
2311a5faa10SDag-Erling Smørgrav /*
2321a5faa10SDag-Erling Smørgrav  * Request and parse file stats
2331a5faa10SDag-Erling Smørgrav  */
2341a5faa10SDag-Erling Smørgrav static int
2351a5faa10SDag-Erling Smørgrav _ftp_stat(int cd, char *file, struct url_stat *us)
2361a5faa10SDag-Erling Smørgrav {
2371a5faa10SDag-Erling Smørgrav     char *ln, *s;
2381a5faa10SDag-Erling Smørgrav     struct tm tm;
2391a5faa10SDag-Erling Smørgrav     time_t t;
2401a5faa10SDag-Erling Smørgrav     int e;
2411a5faa10SDag-Erling Smørgrav 
242269532d9SDag-Erling Smørgrav     us->size = -1;
243269532d9SDag-Erling Smørgrav     us->atime = us->mtime = 0;
244269532d9SDag-Erling Smørgrav 
2451a5faa10SDag-Erling Smørgrav     if ((s = strrchr(file, '/')) == NULL)
2461a5faa10SDag-Erling Smørgrav 	s = file;
2471a5faa10SDag-Erling Smørgrav     else
2481a5faa10SDag-Erling Smørgrav 	++s;
2491a5faa10SDag-Erling Smørgrav 
2501a5faa10SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "SIZE %s", s)) != FTP_FILE_STATUS) {
2511a5faa10SDag-Erling Smørgrav 	_ftp_seterr(e);
2521a5faa10SDag-Erling Smørgrav 	return -1;
2531a5faa10SDag-Erling Smørgrav     }
2541a5faa10SDag-Erling Smørgrav     for (ln = last_reply + 4; *ln && isspace(*ln); ln++)
2551a5faa10SDag-Erling Smørgrav 	/* nothing */ ;
2561a5faa10SDag-Erling Smørgrav     for (us->size = 0; *ln && isdigit(*ln); ln++)
2571a5faa10SDag-Erling Smørgrav 	us->size = us->size * 10 + *ln - '0';
2581a5faa10SDag-Erling Smørgrav     if (*ln && !isspace(*ln)) {
2591a5faa10SDag-Erling Smørgrav 	_ftp_seterr(FTP_PROTOCOL_ERROR);
2601a5faa10SDag-Erling Smørgrav 	return -1;
2611a5faa10SDag-Erling Smørgrav     }
2621a5faa10SDag-Erling Smørgrav     DEBUG(fprintf(stderr, "size: [\033[1m%lld\033[m]\n", us->size));
2631a5faa10SDag-Erling Smørgrav 
2641a5faa10SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "MDTM %s", s)) != FTP_FILE_STATUS) {
2651a5faa10SDag-Erling Smørgrav 	_ftp_seterr(e);
2661a5faa10SDag-Erling Smørgrav 	return -1;
2671a5faa10SDag-Erling Smørgrav     }
2681a5faa10SDag-Erling Smørgrav     for (ln = last_reply + 4; *ln && isspace(*ln); ln++)
2691a5faa10SDag-Erling Smørgrav 	/* nothing */ ;
2701a5faa10SDag-Erling Smørgrav     switch (strspn(ln, "0123456789")) {
2711a5faa10SDag-Erling Smørgrav     case 14:
2721a5faa10SDag-Erling Smørgrav 	break;
2731a5faa10SDag-Erling Smørgrav     case 15:
2741a5faa10SDag-Erling Smørgrav 	ln++;
2751a5faa10SDag-Erling Smørgrav 	ln[0] = '2';
2761a5faa10SDag-Erling Smørgrav 	ln[1] = '0';
2771a5faa10SDag-Erling Smørgrav 	break;
2781a5faa10SDag-Erling Smørgrav     default:
2791a5faa10SDag-Erling Smørgrav 	_ftp_seterr(FTP_PROTOCOL_ERROR);
2801a5faa10SDag-Erling Smørgrav 	return -1;
2811a5faa10SDag-Erling Smørgrav     }
2821a5faa10SDag-Erling Smørgrav     if (sscanf(ln, "%04d%02d%02d%02d%02d%02d",
2831a5faa10SDag-Erling Smørgrav 	       &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2841a5faa10SDag-Erling Smørgrav 	       &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2851a5faa10SDag-Erling Smørgrav 	_ftp_seterr(FTP_PROTOCOL_ERROR);
2861a5faa10SDag-Erling Smørgrav 	return -1;
2871a5faa10SDag-Erling Smørgrav     }
2881a5faa10SDag-Erling Smørgrav     tm.tm_mon--;
2891a5faa10SDag-Erling Smørgrav     tm.tm_year -= 1900;
2901a5faa10SDag-Erling Smørgrav     tm.tm_isdst = -1;
2911a5faa10SDag-Erling Smørgrav     t = timegm(&tm);
2921a5faa10SDag-Erling Smørgrav     if (t == (time_t)-1)
2931a5faa10SDag-Erling Smørgrav 	t = time(NULL);
2941a5faa10SDag-Erling Smørgrav     us->mtime = t;
2951a5faa10SDag-Erling Smørgrav     us->atime = t;
2961a5faa10SDag-Erling Smørgrav     DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d "
2971a5faa10SDag-Erling Smørgrav 		  "%02d:%02d:%02d\033[m]\n",
2981a5faa10SDag-Erling Smørgrav 		  tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
2991a5faa10SDag-Erling Smørgrav 		  tm.tm_hour, tm.tm_min, tm.tm_sec));
3001a5faa10SDag-Erling Smørgrav     return 0;
3011a5faa10SDag-Erling Smørgrav }
3021a5faa10SDag-Erling Smørgrav 
3031a5faa10SDag-Erling Smørgrav /*
304f62e5228SDag-Erling Smørgrav  * Transfer file
3054ca1ab94SDag-Erling Smørgrav  */
3064ca1ab94SDag-Erling Smørgrav static FILE *
30732425dafSDag-Erling Smørgrav _ftp_transfer(int cd, char *oper, char *file,
30832425dafSDag-Erling Smørgrav 	      char *mode, off_t offset, char *flags)
3094ca1ab94SDag-Erling Smørgrav {
31028c645cfSHajimu UMEMOTO     struct sockaddr_storage sin;
31128c645cfSHajimu UMEMOTO     struct sockaddr_in6 *sin6;
31228c645cfSHajimu UMEMOTO     struct sockaddr_in *sin4;
313f5f109a0SDag-Erling Smørgrav     int pasv, high, verbose;
314f5f109a0SDag-Erling Smørgrav     int e, sd = -1;
315f5f109a0SDag-Erling Smørgrav     socklen_t l;
316346298f0SDag-Erling Smørgrav     char *s;
317346298f0SDag-Erling Smørgrav     FILE *df;
3188e3986eaSDag-Erling Smørgrav 
319f5f109a0SDag-Erling Smørgrav     /* check flags */
320f5f109a0SDag-Erling Smørgrav     pasv = (flags && strchr(flags, 'p'));
321f5f109a0SDag-Erling Smørgrav     high = (flags && strchr(flags, 'h'));
322f5f109a0SDag-Erling Smørgrav     verbose = (flags && strchr(flags, 'v'));
323f5f109a0SDag-Erling Smørgrav 
324d02e84a6SDag-Erling Smørgrav     /* passive mode */
325d02e84a6SDag-Erling Smørgrav     if (!pasv && (s = getenv("FTP_PASSIVE_MODE")) != NULL)
326d02e84a6SDag-Erling Smørgrav 	pasv = (strncasecmp(s, "no", 2) != 0);
327d02e84a6SDag-Erling Smørgrav 
32828c645cfSHajimu UMEMOTO     /* find our own address, bind, and listen */
32928c645cfSHajimu UMEMOTO     l = sizeof sin;
33028c645cfSHajimu UMEMOTO     if (getsockname(cd, (struct sockaddr *)&sin, &l) == -1)
33128c645cfSHajimu UMEMOTO 	goto sysouch;
33228c645cfSHajimu UMEMOTO     if (sin.ss_family == AF_INET6)
33328c645cfSHajimu UMEMOTO 	unmappedaddr((struct sockaddr_in6 *)&sin);
33428c645cfSHajimu UMEMOTO 
335346298f0SDag-Erling Smørgrav     /* open data socket */
33628c645cfSHajimu UMEMOTO     if ((sd = socket(sin.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
337842a95ccSDag-Erling Smørgrav 	_fetch_syserr();
338346298f0SDag-Erling Smørgrav 	return NULL;
339346298f0SDag-Erling Smørgrav     }
340346298f0SDag-Erling Smørgrav 
341346298f0SDag-Erling Smørgrav     if (pasv) {
34228c645cfSHajimu UMEMOTO 	u_char addr[64];
343346298f0SDag-Erling Smørgrav 	char *ln, *p;
344346298f0SDag-Erling Smørgrav 	int i;
34528c645cfSHajimu UMEMOTO 	int port;
346346298f0SDag-Erling Smørgrav 
347346298f0SDag-Erling Smørgrav 	/* send PASV command */
348f5f109a0SDag-Erling Smørgrav 	if (verbose)
349f5f109a0SDag-Erling Smørgrav 	    _fetch_info("setting passive mode");
35028c645cfSHajimu UMEMOTO 	switch (sin.ss_family) {
35128c645cfSHajimu UMEMOTO 	case AF_INET:
352fc6e9e65SDag-Erling Smørgrav 	    if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE)
353346298f0SDag-Erling Smørgrav 		goto ouch;
35428c645cfSHajimu UMEMOTO 	    break;
35528c645cfSHajimu UMEMOTO 	case AF_INET6:
35628c645cfSHajimu UMEMOTO 	    if ((e = _ftp_cmd(cd, "EPSV")) != FTP_EPASSIVE_MODE) {
35728c645cfSHajimu UMEMOTO 		if (e == -1)
35828c645cfSHajimu UMEMOTO 		    goto ouch;
35928c645cfSHajimu UMEMOTO 		if ((e = _ftp_cmd(cd, "LPSV")) != FTP_LPASSIVE_MODE)
36028c645cfSHajimu UMEMOTO 		    goto ouch;
36128c645cfSHajimu UMEMOTO 	    }
36228c645cfSHajimu UMEMOTO 	    break;
36328c645cfSHajimu UMEMOTO 	default:
3645cd33c40SDag-Erling Smørgrav 	    e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
36528c645cfSHajimu UMEMOTO 	    goto ouch;
36628c645cfSHajimu UMEMOTO 	}
367346298f0SDag-Erling Smørgrav 
368f5f109a0SDag-Erling Smørgrav 	/*
369f5f109a0SDag-Erling Smørgrav 	 * Find address and port number. The reply to the PASV command
370f5f109a0SDag-Erling Smørgrav          * is IMHO the one and only weak point in the FTP protocol.
371f5f109a0SDag-Erling Smørgrav 	 */
372fc6e9e65SDag-Erling Smørgrav 	ln = last_reply;
373fa5dce6cSHajimu UMEMOTO       	switch (e) {
374fa5dce6cSHajimu UMEMOTO 	case FTP_PASSIVE_MODE:
375fa5dce6cSHajimu UMEMOTO 	case FTP_LPASSIVE_MODE:
37651e3d46eSDag-Erling Smørgrav 	    for (p = ln + 3; *p && !isdigit(*p); p++)
377346298f0SDag-Erling Smørgrav 		/* nothing */ ;
37828c645cfSHajimu UMEMOTO 	    if (!*p) {
3795cd33c40SDag-Erling Smørgrav 		e = FTP_PROTOCOL_ERROR;
3806efb30c8SDag-Erling Smørgrav 		goto ouch;
381346298f0SDag-Erling Smørgrav 	    }
38228c645cfSHajimu UMEMOTO 	    l = (e == FTP_PASSIVE_MODE ? 6 : 21);
38328c645cfSHajimu UMEMOTO 	    for (i = 0; *p && i < l; i++, p++)
38428c645cfSHajimu UMEMOTO 		addr[i] = strtol(p, &p, 10);
38528c645cfSHajimu UMEMOTO 	    if (i < l) {
3865cd33c40SDag-Erling Smørgrav 		e = FTP_PROTOCOL_ERROR;
38728c645cfSHajimu UMEMOTO 		goto ouch;
38828c645cfSHajimu UMEMOTO 	    }
38928c645cfSHajimu UMEMOTO 	    break;
39028c645cfSHajimu UMEMOTO 	case FTP_EPASSIVE_MODE:
391fa5dce6cSHajimu UMEMOTO 	    for (p = ln + 3; *p && *p != '('; p++)
392fa5dce6cSHajimu UMEMOTO 		/* nothing */ ;
393fa5dce6cSHajimu UMEMOTO 	    if (!*p) {
3945cd33c40SDag-Erling Smørgrav 		e = FTP_PROTOCOL_ERROR;
395fa5dce6cSHajimu UMEMOTO 		goto ouch;
396fa5dce6cSHajimu UMEMOTO 	    }
397fa5dce6cSHajimu UMEMOTO 	    ++p;
39828c645cfSHajimu UMEMOTO 	    if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2],
39928c645cfSHajimu UMEMOTO 		       &port, &addr[3]) != 5 ||
40028c645cfSHajimu UMEMOTO 		addr[0] != addr[1] ||
40128c645cfSHajimu UMEMOTO 		addr[0] != addr[2] || addr[0] != addr[3]) {
4025cd33c40SDag-Erling Smørgrav 		e = FTP_PROTOCOL_ERROR;
40328c645cfSHajimu UMEMOTO 		goto ouch;
40428c645cfSHajimu UMEMOTO 	    }
40528c645cfSHajimu UMEMOTO 	    break;
40628c645cfSHajimu UMEMOTO 	}
407346298f0SDag-Erling Smørgrav 
40832425dafSDag-Erling Smørgrav 	/* seek to required offset */
40932425dafSDag-Erling Smørgrav 	if (offset)
41032425dafSDag-Erling Smørgrav 	    if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK)
41132425dafSDag-Erling Smørgrav 		goto sysouch;
41232425dafSDag-Erling Smørgrav 
413346298f0SDag-Erling Smørgrav 	/* construct sockaddr for data socket */
41432425dafSDag-Erling Smørgrav 	l = sizeof sin;
415fc6e9e65SDag-Erling Smørgrav 	if (getpeername(cd, (struct sockaddr *)&sin, &l) == -1)
416346298f0SDag-Erling Smørgrav 	    goto sysouch;
41728c645cfSHajimu UMEMOTO 	if (sin.ss_family == AF_INET6)
41828c645cfSHajimu UMEMOTO 	    unmappedaddr((struct sockaddr_in6 *)&sin);
41928c645cfSHajimu UMEMOTO 	switch (sin.ss_family) {
42028c645cfSHajimu UMEMOTO 	case AF_INET6:
42128c645cfSHajimu UMEMOTO 	    sin6 = (struct sockaddr_in6 *)&sin;
42228c645cfSHajimu UMEMOTO 	    if (e == FTP_EPASSIVE_MODE)
42328c645cfSHajimu UMEMOTO 		sin6->sin6_port = htons(port);
42428c645cfSHajimu UMEMOTO 	    else {
42528c645cfSHajimu UMEMOTO 		bcopy(addr + 2, (char *)&sin6->sin6_addr, 16);
42628c645cfSHajimu UMEMOTO 		bcopy(addr + 19, (char *)&sin6->sin6_port, 2);
42728c645cfSHajimu UMEMOTO 	    }
42828c645cfSHajimu UMEMOTO 	    break;
42928c645cfSHajimu UMEMOTO 	case AF_INET:
43028c645cfSHajimu UMEMOTO 	    sin4 = (struct sockaddr_in *)&sin;
43128c645cfSHajimu UMEMOTO 	    if (e == FTP_EPASSIVE_MODE)
43228c645cfSHajimu UMEMOTO 		sin4->sin_port = htons(port);
43328c645cfSHajimu UMEMOTO 	    else {
43428c645cfSHajimu UMEMOTO 		bcopy(addr, (char *)&sin4->sin_addr, 4);
43528c645cfSHajimu UMEMOTO 		bcopy(addr + 4, (char *)&sin4->sin_port, 2);
43628c645cfSHajimu UMEMOTO 	    }
43728c645cfSHajimu UMEMOTO 	    break;
43828c645cfSHajimu UMEMOTO 	default:
4395cd33c40SDag-Erling Smørgrav 	    e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
44028c645cfSHajimu UMEMOTO 	    break;
44128c645cfSHajimu UMEMOTO 	}
442346298f0SDag-Erling Smørgrav 
443346298f0SDag-Erling Smørgrav 	/* connect to data port */
444f5f109a0SDag-Erling Smørgrav 	if (verbose)
445f5f109a0SDag-Erling Smørgrav 	    _fetch_info("opening data connection");
44628c645cfSHajimu UMEMOTO 	if (connect(sd, (struct sockaddr *)&sin, sin.ss_len) == -1)
447346298f0SDag-Erling Smørgrav 	    goto sysouch;
448346298f0SDag-Erling Smørgrav 
449346298f0SDag-Erling Smørgrav 	/* make the server initiate the transfer */
450f5f109a0SDag-Erling Smørgrav 	if (verbose)
451def5f54cSDag-Erling Smørgrav 	    _fetch_info("initiating transfer");
4521a5faa10SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "%s %s", oper, _ftp_filename(file));
4535aea254fSDag-Erling Smørgrav 	if (e != FTP_OPEN_DATA_CONNECTION)
454346298f0SDag-Erling Smørgrav 	    goto ouch;
455346298f0SDag-Erling Smørgrav 
456346298f0SDag-Erling Smørgrav     } else {
457346298f0SDag-Erling Smørgrav 	u_int32_t a;
458346298f0SDag-Erling Smørgrav 	u_short p;
459f5f109a0SDag-Erling Smørgrav 	int arg, d;
46028c645cfSHajimu UMEMOTO 	char *ap;
46128c645cfSHajimu UMEMOTO 	char hname[INET6_ADDRSTRLEN];
462346298f0SDag-Erling Smørgrav 
46328c645cfSHajimu UMEMOTO 	switch (sin.ss_family) {
46428c645cfSHajimu UMEMOTO 	case AF_INET6:
46528c645cfSHajimu UMEMOTO 	    ((struct sockaddr_in6 *)&sin)->sin6_port = 0;
46628c645cfSHajimu UMEMOTO #ifdef IPV6_PORTRANGE
46728c645cfSHajimu UMEMOTO 	    arg = high ? IPV6_PORTRANGE_HIGH : IPV6_PORTRANGE_DEFAULT;
46828c645cfSHajimu UMEMOTO 	    if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE,
46928c645cfSHajimu UMEMOTO 			   (char *)&arg, sizeof(arg)) == -1)
470346298f0SDag-Erling Smørgrav 		goto sysouch;
47128c645cfSHajimu UMEMOTO #endif
47228c645cfSHajimu UMEMOTO 	    break;
47328c645cfSHajimu UMEMOTO 	case AF_INET:
47428c645cfSHajimu UMEMOTO 	    ((struct sockaddr_in *)&sin)->sin_port = 0;
475f5f109a0SDag-Erling Smørgrav 	    arg = high ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT;
476f5f109a0SDag-Erling Smørgrav 	    if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
47732425dafSDag-Erling Smørgrav 			   (char *)&arg, sizeof arg) == -1)
478f5f109a0SDag-Erling Smørgrav 		goto sysouch;
47928c645cfSHajimu UMEMOTO 	    break;
48028c645cfSHajimu UMEMOTO 	}
481f5f109a0SDag-Erling Smørgrav 	if (verbose)
482f5f109a0SDag-Erling Smørgrav 	    _fetch_info("binding data socket");
48328c645cfSHajimu UMEMOTO 	if (bind(sd, (struct sockaddr *)&sin, sin.ss_len) == -1)
484346298f0SDag-Erling Smørgrav 	    goto sysouch;
485ecc91352SDag-Erling Smørgrav 	if (listen(sd, 1) == -1)
486346298f0SDag-Erling Smørgrav 	    goto sysouch;
487346298f0SDag-Erling Smørgrav 
488346298f0SDag-Erling Smørgrav 	/* find what port we're on and tell the server */
489ecc91352SDag-Erling Smørgrav 	if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1)
490346298f0SDag-Erling Smørgrav 	    goto sysouch;
49128c645cfSHajimu UMEMOTO 	switch (sin.ss_family) {
49228c645cfSHajimu UMEMOTO 	case AF_INET:
49328c645cfSHajimu UMEMOTO 	    sin4 = (struct sockaddr_in *)&sin;
49428c645cfSHajimu UMEMOTO 	    a = ntohl(sin4->sin_addr.s_addr);
49528c645cfSHajimu UMEMOTO 	    p = ntohs(sin4->sin_port);
496fc6e9e65SDag-Erling Smørgrav 	    e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d",
4975aea254fSDag-Erling Smørgrav 			 (a >> 24) & 0xff, (a >> 16) & 0xff,
4985aea254fSDag-Erling Smørgrav 			 (a >> 8) & 0xff, a & 0xff,
4995aea254fSDag-Erling Smørgrav 			 (p >> 8) & 0xff, p & 0xff);
50028c645cfSHajimu UMEMOTO 	    break;
50128c645cfSHajimu UMEMOTO 	case AF_INET6:
50228c645cfSHajimu UMEMOTO #define UC(b)	(((int)b)&0xff)
50328c645cfSHajimu UMEMOTO 	    e = -1;
50428c645cfSHajimu UMEMOTO 	    sin6 = (struct sockaddr_in6 *)&sin;
50528c645cfSHajimu UMEMOTO 	    if (getnameinfo((struct sockaddr *)&sin, sin.ss_len,
50628c645cfSHajimu UMEMOTO 			    hname, sizeof(hname),
50728c645cfSHajimu UMEMOTO 			    NULL, 0, NI_NUMERICHOST) == 0) {
50828c645cfSHajimu UMEMOTO 		e = _ftp_cmd(cd, "EPRT |%d|%s|%d|", 2, hname,
50928c645cfSHajimu UMEMOTO 			     htons(sin6->sin6_port));
51028c645cfSHajimu UMEMOTO 		if (e == -1)
51128c645cfSHajimu UMEMOTO 		    goto ouch;
51228c645cfSHajimu UMEMOTO 	    }
51328c645cfSHajimu UMEMOTO 	    if (e != FTP_OK) {
51428c645cfSHajimu UMEMOTO 		ap = (char *)&sin6->sin6_addr;
51528c645cfSHajimu UMEMOTO 		e = _ftp_cmd(cd,
51628c645cfSHajimu UMEMOTO      "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
51728c645cfSHajimu UMEMOTO 			     6, 16,
51828c645cfSHajimu UMEMOTO 			     UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]),
51928c645cfSHajimu UMEMOTO 			     UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]),
52028c645cfSHajimu UMEMOTO 			     UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]),
52128c645cfSHajimu UMEMOTO 			     UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]),
52228c645cfSHajimu UMEMOTO 			     2,
52328c645cfSHajimu UMEMOTO 			     (ntohs(sin6->sin6_port) >> 8) & 0xff,
52428c645cfSHajimu UMEMOTO 			     ntohs(sin6->sin6_port)        & 0xff);
52528c645cfSHajimu UMEMOTO 	    }
52628c645cfSHajimu UMEMOTO 	    break;
52728c645cfSHajimu UMEMOTO 	default:
5285cd33c40SDag-Erling Smørgrav 	    e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
52928c645cfSHajimu UMEMOTO 	    goto ouch;
53028c645cfSHajimu UMEMOTO 	}
5315aea254fSDag-Erling Smørgrav 	if (e != FTP_OK)
532346298f0SDag-Erling Smørgrav 	    goto ouch;
533346298f0SDag-Erling Smørgrav 
534893980adSDag-Erling Smørgrav 	/* seek to required offset */
535893980adSDag-Erling Smørgrav 	if (offset)
536893980adSDag-Erling Smørgrav 	    if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK)
537893980adSDag-Erling Smørgrav 		goto sysouch;
538893980adSDag-Erling Smørgrav 
539346298f0SDag-Erling Smørgrav 	/* make the server initiate the transfer */
540f5f109a0SDag-Erling Smørgrav 	if (verbose)
541f5f109a0SDag-Erling Smørgrav 	    _fetch_info("initiating transfer");
5421a5faa10SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "%s %s", oper, _ftp_filename(file));
5435aea254fSDag-Erling Smørgrav 	if (e != FTP_OPEN_DATA_CONNECTION)
544346298f0SDag-Erling Smørgrav 	    goto ouch;
545346298f0SDag-Erling Smørgrav 
546346298f0SDag-Erling Smørgrav 	/* accept the incoming connection and go to town */
547ecc91352SDag-Erling Smørgrav 	if ((d = accept(sd, NULL, NULL)) == -1)
548346298f0SDag-Erling Smørgrav 	    goto sysouch;
549346298f0SDag-Erling Smørgrav 	close(sd);
550346298f0SDag-Erling Smørgrav 	sd = d;
551346298f0SDag-Erling Smørgrav     }
552346298f0SDag-Erling Smørgrav 
553f62e5228SDag-Erling Smørgrav     if ((df = fdopen(sd, mode)) == NULL)
554346298f0SDag-Erling Smørgrav 	goto sysouch;
555346298f0SDag-Erling Smørgrav     return df;
556346298f0SDag-Erling Smørgrav 
557346298f0SDag-Erling Smørgrav sysouch:
558842a95ccSDag-Erling Smørgrav     _fetch_syserr();
55928c645cfSHajimu UMEMOTO     if (sd >= 0)
5605aea254fSDag-Erling Smørgrav 	close(sd);
5615aea254fSDag-Erling Smørgrav     return NULL;
5625aea254fSDag-Erling Smørgrav 
563346298f0SDag-Erling Smørgrav ouch:
564fc6e9e65SDag-Erling Smørgrav     if (e != -1)
5655aea254fSDag-Erling Smørgrav 	_ftp_seterr(e);
56628c645cfSHajimu UMEMOTO     if (sd >= 0)
567346298f0SDag-Erling Smørgrav 	close(sd);
5684ca1ab94SDag-Erling Smørgrav     return NULL;
5694ca1ab94SDag-Erling Smørgrav }
5704ca1ab94SDag-Erling Smørgrav 
5718e3986eaSDag-Erling Smørgrav /*
57210851dc4SDag-Erling Smørgrav  * Return default port
57310851dc4SDag-Erling Smørgrav  */
57410851dc4SDag-Erling Smørgrav static int
57510851dc4SDag-Erling Smørgrav _ftp_default_port(void)
57610851dc4SDag-Erling Smørgrav {
57710851dc4SDag-Erling Smørgrav     struct servent *se;
57810851dc4SDag-Erling Smørgrav 
57910851dc4SDag-Erling Smørgrav     if ((se = getservbyname("ftp", "tcp")) != NULL)
58010851dc4SDag-Erling Smørgrav 	return ntohs(se->s_port);
58110851dc4SDag-Erling Smørgrav     return FTP_DEFAULT_PORT;
58210851dc4SDag-Erling Smørgrav }
58310851dc4SDag-Erling Smørgrav 
58410851dc4SDag-Erling Smørgrav /*
5858e3986eaSDag-Erling Smørgrav  * Log on to FTP server
5864ca1ab94SDag-Erling Smørgrav  */
587fc6e9e65SDag-Erling Smørgrav static int
588f5f109a0SDag-Erling Smørgrav _ftp_connect(char *host, int port, char *user, char *pwd, char *flags)
5898e3986eaSDag-Erling Smørgrav {
59032425dafSDag-Erling Smørgrav     int cd, e, pp = 0, direct, verbose;
59128c645cfSHajimu UMEMOTO #ifdef INET6
59228c645cfSHajimu UMEMOTO     int af = AF_UNSPEC;
59328c645cfSHajimu UMEMOTO #else
59428c645cfSHajimu UMEMOTO     int af = AF_INET;
59528c645cfSHajimu UMEMOTO #endif
596f62e5228SDag-Erling Smørgrav     char *p, *q;
5974ab587f4SHajimu UMEMOTO     const char *logname;
5984ab587f4SHajimu UMEMOTO     char localhost[MAXHOSTNAMELEN];
5994ab587f4SHajimu UMEMOTO     char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1];
6008e3986eaSDag-Erling Smørgrav 
601f5f109a0SDag-Erling Smørgrav     direct = (flags && strchr(flags, 'd'));
602f5f109a0SDag-Erling Smørgrav     verbose = (flags && strchr(flags, 'v'));
60328c645cfSHajimu UMEMOTO     if ((flags && strchr(flags, '4')))
60428c645cfSHajimu UMEMOTO 	af = AF_INET;
60528c645cfSHajimu UMEMOTO     else if ((flags && strchr(flags, '6')))
60628c645cfSHajimu UMEMOTO 	af = AF_INET6;
607f5f109a0SDag-Erling Smørgrav 
608f62e5228SDag-Erling Smørgrav     /* check for proxy */
609dfe7c55fSDag-Erling Smørgrav     if (!direct && (p = getenv("FTP_PROXY")) != NULL && *p) {
61028c645cfSHajimu UMEMOTO 	char c = 0;
61128c645cfSHajimu UMEMOTO 
61228c645cfSHajimu UMEMOTO #ifdef INET6
61328c645cfSHajimu UMEMOTO 	if (*p != '[' || (q = strchr(p + 1, ']')) == NULL ||
61428c645cfSHajimu UMEMOTO 	    (*++q != '\0' && *q != ':'))
61528c645cfSHajimu UMEMOTO #endif
61628c645cfSHajimu UMEMOTO 	    q = strchr(p, ':');
61728c645cfSHajimu UMEMOTO 	if (q != NULL && *q == ':') {
61832425dafSDag-Erling Smørgrav 	    if (strspn(q+1, "0123456789") != strlen(q+1) || strlen(q+1) > 5) {
61932425dafSDag-Erling Smørgrav 		/* XXX we should emit some kind of warning */
62032425dafSDag-Erling Smørgrav 	    }
621f62e5228SDag-Erling Smørgrav 	    pp = atoi(q+1);
62232425dafSDag-Erling Smørgrav 	    if (pp < 1 || pp > 65535) {
62332425dafSDag-Erling Smørgrav 		/* XXX we should emit some kind of warning */
62432425dafSDag-Erling Smørgrav 	    }
62532425dafSDag-Erling Smørgrav 	}
62610851dc4SDag-Erling Smørgrav 	if (!pp)
62710851dc4SDag-Erling Smørgrav 	    pp = _ftp_default_port();
62828c645cfSHajimu UMEMOTO 	if (q) {
62928c645cfSHajimu UMEMOTO #ifdef INET6
63028c645cfSHajimu UMEMOTO 	    if (q > p && *p == '[' && *(q - 1) == ']') {
63128c645cfSHajimu UMEMOTO 		p++;
63228c645cfSHajimu UMEMOTO 		q--;
63328c645cfSHajimu UMEMOTO 	    }
63428c645cfSHajimu UMEMOTO #endif
63528c645cfSHajimu UMEMOTO 	    c = *q;
636f62e5228SDag-Erling Smørgrav 	    *q = 0;
63728c645cfSHajimu UMEMOTO 	}
63828c645cfSHajimu UMEMOTO 	cd = _fetch_connect(p, pp, af, verbose);
639f62e5228SDag-Erling Smørgrav 	if (q)
64028c645cfSHajimu UMEMOTO 	    *q = c;
641f62e5228SDag-Erling Smørgrav     } else {
642f62e5228SDag-Erling Smørgrav 	/* no proxy, go straight to target */
64328c645cfSHajimu UMEMOTO 	cd = _fetch_connect(host, port, af, verbose);
644f5f109a0SDag-Erling Smørgrav 	p = NULL;
645f62e5228SDag-Erling Smørgrav     }
646f62e5228SDag-Erling Smørgrav 
647f62e5228SDag-Erling Smørgrav     /* check connection */
648fc6e9e65SDag-Erling Smørgrav     if (cd == -1) {
649842a95ccSDag-Erling Smørgrav 	_fetch_syserr();
6508e3986eaSDag-Erling Smørgrav 	return NULL;
6518e3986eaSDag-Erling Smørgrav     }
652f62e5228SDag-Erling Smørgrav 
6538e3986eaSDag-Erling Smørgrav     /* expect welcome message */
654fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY)
6558e3986eaSDag-Erling Smørgrav 	goto fouch;
6568e3986eaSDag-Erling Smørgrav 
6578e3986eaSDag-Erling Smørgrav     /* send user name and password */
658f62e5228SDag-Erling Smørgrav     if (!user || !*user)
659f62e5228SDag-Erling Smørgrav 	user = FTP_ANONYMOUS_USER;
6602e88106bSDag-Erling Smørgrav     if (p && port == FTP_DEFAULT_PORT)
6612e88106bSDag-Erling Smørgrav 	e = _ftp_cmd(cd, "USER %s@%s", user, host);
6622e88106bSDag-Erling Smørgrav     else if (p)
6632e88106bSDag-Erling Smørgrav 	e = _ftp_cmd(cd, "USER %s@%s@%d", user, host, port);
6642e88106bSDag-Erling Smørgrav     else
6652e88106bSDag-Erling Smørgrav 	e = _ftp_cmd(cd, "USER %s", user);
666f62e5228SDag-Erling Smørgrav 
667f62e5228SDag-Erling Smørgrav     /* did the server request a password? */
668f62e5228SDag-Erling Smørgrav     if (e == FTP_NEED_PASSWORD) {
669f62e5228SDag-Erling Smørgrav 	if (!pwd || !*pwd)
6704ab587f4SHajimu UMEMOTO 	    pwd = getenv("FTP_PASSWORD");
6714ab587f4SHajimu UMEMOTO 	if (!pwd || !*pwd) {
6724ab587f4SHajimu UMEMOTO 	    if ((logname = getlogin()) == 0)
6734ab587f4SHajimu UMEMOTO 		logname = FTP_ANONYMOUS_PASSWORD;
6744ab587f4SHajimu UMEMOTO 	    gethostname(localhost, sizeof localhost);
6754ab587f4SHajimu UMEMOTO 	    snprintf(pbuf, sizeof pbuf, "%s@%s", logname, localhost);
6764ab587f4SHajimu UMEMOTO 	    pwd = pbuf;
6774ab587f4SHajimu UMEMOTO 	}
678fc6e9e65SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "PASS %s", pwd);
679f62e5228SDag-Erling Smørgrav     }
680f62e5228SDag-Erling Smørgrav 
681f62e5228SDag-Erling Smørgrav     /* did the server request an account? */
6825aea254fSDag-Erling Smørgrav     if (e == FTP_NEED_ACCOUNT)
6833b7a6740SDag-Erling Smørgrav 	goto fouch;
684f62e5228SDag-Erling Smørgrav 
685f62e5228SDag-Erling Smørgrav     /* we should be done by now */
6865aea254fSDag-Erling Smørgrav     if (e != FTP_LOGGED_IN)
6878e3986eaSDag-Erling Smørgrav 	goto fouch;
6888e3986eaSDag-Erling Smørgrav 
6898e3986eaSDag-Erling Smørgrav     /* might as well select mode and type at once */
6908e3986eaSDag-Erling Smørgrav #ifdef FTP_FORCE_STREAM_MODE
691fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */
6925aea254fSDag-Erling Smørgrav 	goto fouch;
6938e3986eaSDag-Erling Smørgrav #endif
694fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */
6955aea254fSDag-Erling Smørgrav 	goto fouch;
6968e3986eaSDag-Erling Smørgrav 
6978e3986eaSDag-Erling Smørgrav     /* done */
698fc6e9e65SDag-Erling Smørgrav     return cd;
6998e3986eaSDag-Erling Smørgrav 
7008e3986eaSDag-Erling Smørgrav fouch:
701fc6e9e65SDag-Erling Smørgrav     if (e != -1)
7025aea254fSDag-Erling Smørgrav 	_ftp_seterr(e);
703fc6e9e65SDag-Erling Smørgrav     close(cd);
7048e3986eaSDag-Erling Smørgrav     return NULL;
7058e3986eaSDag-Erling Smørgrav }
7068e3986eaSDag-Erling Smørgrav 
7078e3986eaSDag-Erling Smørgrav /*
7088e3986eaSDag-Erling Smørgrav  * Disconnect from server
7098e3986eaSDag-Erling Smørgrav  */
7108e3986eaSDag-Erling Smørgrav static void
711fc6e9e65SDag-Erling Smørgrav _ftp_disconnect(int cd)
7128e3986eaSDag-Erling Smørgrav {
713fc6e9e65SDag-Erling Smørgrav     (void)_ftp_cmd(cd, "QUIT");
714fc6e9e65SDag-Erling Smørgrav     close(cd);
7158e3986eaSDag-Erling Smørgrav }
7168e3986eaSDag-Erling Smørgrav 
7178e3986eaSDag-Erling Smørgrav /*
7188e3986eaSDag-Erling Smørgrav  * Check if we're already connected
7198e3986eaSDag-Erling Smørgrav  */
7208e3986eaSDag-Erling Smørgrav static int
721d8acd8dcSDag-Erling Smørgrav _ftp_isconnected(struct url *url)
7228e3986eaSDag-Erling Smørgrav {
7238e3986eaSDag-Erling Smørgrav     return (cached_socket
7248e3986eaSDag-Erling Smørgrav 	    && (strcmp(url->host, cached_host.host) == 0)
7258e3986eaSDag-Erling Smørgrav 	    && (strcmp(url->user, cached_host.user) == 0)
7268e3986eaSDag-Erling Smørgrav 	    && (strcmp(url->pwd, cached_host.pwd) == 0)
7278e3986eaSDag-Erling Smørgrav 	    && (url->port == cached_host.port));
7288e3986eaSDag-Erling Smørgrav }
7298e3986eaSDag-Erling Smørgrav 
730f62e5228SDag-Erling Smørgrav /*
7315aea254fSDag-Erling Smørgrav  * Check the cache, reconnect if no luck
732f62e5228SDag-Erling Smørgrav  */
733fc6e9e65SDag-Erling Smørgrav static int
7345aea254fSDag-Erling Smørgrav _ftp_cached_connect(struct url *url, char *flags)
7354ca1ab94SDag-Erling Smørgrav {
736fc6e9e65SDag-Erling Smørgrav     int e, cd;
7375aea254fSDag-Erling Smørgrav 
738fc6e9e65SDag-Erling Smørgrav     cd = -1;
7398e3986eaSDag-Erling Smørgrav 
7408e3986eaSDag-Erling Smørgrav     /* set default port */
74110851dc4SDag-Erling Smørgrav     if (!url->port)
74210851dc4SDag-Erling Smørgrav 	url->port = _ftp_default_port();
7438e3986eaSDag-Erling Smørgrav 
7443b7a6740SDag-Erling Smørgrav     /* try to use previously cached connection */
745fc6e9e65SDag-Erling Smørgrav     if (_ftp_isconnected(url)) {
746fc6e9e65SDag-Erling Smørgrav 	e = _ftp_cmd(cached_socket, "NOOP");
747fc6e9e65SDag-Erling Smørgrav 	if (e == FTP_OK || e == FTP_SYNTAX_ERROR)
748fc6e9e65SDag-Erling Smørgrav 	    cd = cached_socket;
749fc6e9e65SDag-Erling Smørgrav     }
7504ca1ab94SDag-Erling Smørgrav 
7518e3986eaSDag-Erling Smørgrav     /* connect to server */
752fc6e9e65SDag-Erling Smørgrav     if (cd == -1) {
753fc6e9e65SDag-Erling Smørgrav 	cd = _ftp_connect(url->host, url->port, url->user, url->pwd, flags);
754fc6e9e65SDag-Erling Smørgrav 	if (cd == -1)
755fc6e9e65SDag-Erling Smørgrav 	    return -1;
7568e3986eaSDag-Erling Smørgrav 	if (cached_socket)
7578e3986eaSDag-Erling Smørgrav 	    _ftp_disconnect(cached_socket);
758fc6e9e65SDag-Erling Smørgrav 	cached_socket = cd;
75932425dafSDag-Erling Smørgrav 	memcpy(&cached_host, url, sizeof *url);
7608e3986eaSDag-Erling Smørgrav     }
7618e3986eaSDag-Erling Smørgrav 
762fc6e9e65SDag-Erling Smørgrav     return cd;
7638e3986eaSDag-Erling Smørgrav }
7648e3986eaSDag-Erling Smørgrav 
7658e3986eaSDag-Erling Smørgrav /*
766dfe7c55fSDag-Erling Smørgrav  * Check to see if we should use an HTTP proxy instead
767dfe7c55fSDag-Erling Smørgrav  */
768dfe7c55fSDag-Erling Smørgrav static int
769dfe7c55fSDag-Erling Smørgrav _ftp_use_http_proxy(void)
770dfe7c55fSDag-Erling Smørgrav {
771dfe7c55fSDag-Erling Smørgrav     char *p;
772dfe7c55fSDag-Erling Smørgrav 
773dfe7c55fSDag-Erling Smørgrav     return ((p = getenv("HTTP_PROXY")) && *p && !getenv("FTP_PROXY"));
774dfe7c55fSDag-Erling Smørgrav }
775dfe7c55fSDag-Erling Smørgrav 
776dfe7c55fSDag-Erling Smørgrav /*
7771a5faa10SDag-Erling Smørgrav  * Get and stat file
7788e3986eaSDag-Erling Smørgrav  */
7794ca1ab94SDag-Erling Smørgrav FILE *
7801a5faa10SDag-Erling Smørgrav fetchXGetFTP(struct url *url, struct url_stat *us, char *flags)
781f62e5228SDag-Erling Smørgrav {
782fc6e9e65SDag-Erling Smørgrav     int cd;
7835aea254fSDag-Erling Smørgrav 
784dfe7c55fSDag-Erling Smørgrav     if (_ftp_use_http_proxy())
785dfe7c55fSDag-Erling Smørgrav 	return fetchXGetHTTP(url, us, flags);
786dfe7c55fSDag-Erling Smørgrav 
7875aea254fSDag-Erling Smørgrav     /* connect to server */
788fc6e9e65SDag-Erling Smørgrav     if ((cd = _ftp_cached_connect(url, flags)) == NULL)
7895aea254fSDag-Erling Smørgrav 	return NULL;
7905aea254fSDag-Erling Smørgrav 
7911a5faa10SDag-Erling Smørgrav     /* change directory */
7921a5faa10SDag-Erling Smørgrav     if (_ftp_cwd(cd, url->doc) == -1)
7931a5faa10SDag-Erling Smørgrav 	return NULL;
7941a5faa10SDag-Erling Smørgrav 
7951a5faa10SDag-Erling Smørgrav     /* stat file */
796269532d9SDag-Erling Smørgrav     if (us && _ftp_stat(cd, url->doc, us) == -1
797269532d9SDag-Erling Smørgrav 	&& fetchLastErrCode != FETCH_UNAVAIL)
7981a5faa10SDag-Erling Smørgrav 	return NULL;
7991a5faa10SDag-Erling Smørgrav 
8005aea254fSDag-Erling Smørgrav     /* initiate the transfer */
80132425dafSDag-Erling Smørgrav     return _ftp_transfer(cd, "RETR", url->doc, "r", url->offset, flags);
802f62e5228SDag-Erling Smørgrav }
803f62e5228SDag-Erling Smørgrav 
8045aea254fSDag-Erling Smørgrav /*
8051a5faa10SDag-Erling Smørgrav  * Get file
8061a5faa10SDag-Erling Smørgrav  */
8071a5faa10SDag-Erling Smørgrav FILE *
8081a5faa10SDag-Erling Smørgrav fetchGetFTP(struct url *url, char *flags)
8091a5faa10SDag-Erling Smørgrav {
8101a5faa10SDag-Erling Smørgrav     return fetchXGetFTP(url, NULL, flags);
8111a5faa10SDag-Erling Smørgrav }
8121a5faa10SDag-Erling Smørgrav 
8131a5faa10SDag-Erling Smørgrav /*
8145aea254fSDag-Erling Smørgrav  * Put file
8155aea254fSDag-Erling Smørgrav  */
816f62e5228SDag-Erling Smørgrav FILE *
817d8acd8dcSDag-Erling Smørgrav fetchPutFTP(struct url *url, char *flags)
8184ca1ab94SDag-Erling Smørgrav {
819fc6e9e65SDag-Erling Smørgrav     int cd;
8205aea254fSDag-Erling Smørgrav 
821dfe7c55fSDag-Erling Smørgrav     if (_ftp_use_http_proxy())
822dfe7c55fSDag-Erling Smørgrav 	return fetchPutHTTP(url, flags);
823dfe7c55fSDag-Erling Smørgrav 
8245aea254fSDag-Erling Smørgrav     /* connect to server */
825fc6e9e65SDag-Erling Smørgrav     if ((cd = _ftp_cached_connect(url, flags)) == NULL)
8265aea254fSDag-Erling Smørgrav 	return NULL;
8275aea254fSDag-Erling Smørgrav 
8281a5faa10SDag-Erling Smørgrav     /* change directory */
8291a5faa10SDag-Erling Smørgrav     if (_ftp_cwd(cd, url->doc) == -1)
8301a5faa10SDag-Erling Smørgrav 	return NULL;
8311a5faa10SDag-Erling Smørgrav 
8325aea254fSDag-Erling Smørgrav     /* initiate the transfer */
833fc6e9e65SDag-Erling Smørgrav     return _ftp_transfer(cd, (flags && strchr(flags, 'a')) ? "APPE" : "STOR",
83432425dafSDag-Erling Smørgrav 			 url->doc, "w", url->offset, flags);
8358e3986eaSDag-Erling Smørgrav }
836d8acd8dcSDag-Erling Smørgrav 
8375aea254fSDag-Erling Smørgrav /*
8385aea254fSDag-Erling Smørgrav  * Get file stats
8395aea254fSDag-Erling Smørgrav  */
840d8acd8dcSDag-Erling Smørgrav int
841d8acd8dcSDag-Erling Smørgrav fetchStatFTP(struct url *url, struct url_stat *us, char *flags)
842d8acd8dcSDag-Erling Smørgrav {
8431a5faa10SDag-Erling Smørgrav     int cd;
8445aea254fSDag-Erling Smørgrav 
845dfe7c55fSDag-Erling Smørgrav     if (_ftp_use_http_proxy())
846dfe7c55fSDag-Erling Smørgrav 	return fetchStatHTTP(url, us, flags);
847dfe7c55fSDag-Erling Smørgrav 
8480669702cSDag-Erling Smørgrav     us->size = -1;
8490669702cSDag-Erling Smørgrav     us->atime = us->mtime = 0;
8500669702cSDag-Erling Smørgrav 
8515aea254fSDag-Erling Smørgrav     /* connect to server */
852fc6e9e65SDag-Erling Smørgrav     if ((cd = _ftp_cached_connect(url, flags)) == NULL)
8535aea254fSDag-Erling Smørgrav 	return -1;
8545aea254fSDag-Erling Smørgrav 
8555aea254fSDag-Erling Smørgrav     /* change directory */
8561a5faa10SDag-Erling Smørgrav     if (_ftp_cwd(cd, url->doc) == -1)
8575aea254fSDag-Erling Smørgrav 	return -1;
8585aea254fSDag-Erling Smørgrav 
8591a5faa10SDag-Erling Smørgrav     /* stat file */
8601a5faa10SDag-Erling Smørgrav     return _ftp_stat(cd, url->doc, us);
861d8acd8dcSDag-Erling Smørgrav }
862ce71b736SDag-Erling Smørgrav 
863ce71b736SDag-Erling Smørgrav /*
864ce71b736SDag-Erling Smørgrav  * List a directory
865ce71b736SDag-Erling Smørgrav  */
866ce71b736SDag-Erling Smørgrav extern void warnx(char *, ...);
867ce71b736SDag-Erling Smørgrav struct url_ent *
868ce71b736SDag-Erling Smørgrav fetchListFTP(struct url *url, char *flags)
869ce71b736SDag-Erling Smørgrav {
870dfe7c55fSDag-Erling Smørgrav     if (_ftp_use_http_proxy())
871dfe7c55fSDag-Erling Smørgrav 	return fetchListHTTP(url, flags);
872dfe7c55fSDag-Erling Smørgrav 
873def5f54cSDag-Erling Smørgrav     warnx("fetchListFTP(): not implemented");
874ce71b736SDag-Erling Smørgrav     return NULL;
875ce71b736SDag-Erling Smørgrav }
876