xref: /freebsd/lib/libfetch/ftp.c (revision c7d40ef296519756ed391c3496f659c2cf2c0295)
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>
64c7d40ef2SDag-Erling Smørgrav #include <fcntl.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 
80ea014d85SDag-Erling Smørgrav #define FTP_CONNECTION_ALREADY_OPEN	125
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
945cd33c40SDag-Erling Smørgrav #define FTP_PROTOCOL_ERROR		999
954ca1ab94SDag-Erling Smørgrav 
96d8acd8dcSDag-Erling Smørgrav static struct url cached_host;
97fc6e9e65SDag-Erling Smørgrav static int cached_socket;
984ca1ab94SDag-Erling Smørgrav 
99fc6e9e65SDag-Erling Smørgrav static char *last_reply;
100fc6e9e65SDag-Erling Smørgrav static size_t lr_size, lr_length;
101fc6e9e65SDag-Erling Smørgrav static int last_code;
102fc6e9e65SDag-Erling Smørgrav 
103fc6e9e65SDag-Erling Smørgrav #define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
1046efb30c8SDag-Erling Smørgrav 			 && isdigit(foo[2]) \
1056efb30c8SDag-Erling Smørgrav                          && (foo[3] == ' ' || foo[3] == '\0'))
106fc6e9e65SDag-Erling Smørgrav #define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
107fc6e9e65SDag-Erling Smørgrav 			&& isdigit(foo[2]) && foo[3] == '-')
1084ca1ab94SDag-Erling Smørgrav 
10928c645cfSHajimu UMEMOTO /* translate IPv4 mapped IPv6 address to IPv4 address */
11028c645cfSHajimu UMEMOTO static void
11128c645cfSHajimu UMEMOTO unmappedaddr(struct sockaddr_in6 *sin6)
11228c645cfSHajimu UMEMOTO {
11328c645cfSHajimu UMEMOTO     struct sockaddr_in *sin4;
11428c645cfSHajimu UMEMOTO     u_int32_t addr;
11528c645cfSHajimu UMEMOTO     int port;
11628c645cfSHajimu UMEMOTO 
11728c645cfSHajimu UMEMOTO     if (sin6->sin6_family != AF_INET6 ||
11828c645cfSHajimu UMEMOTO 	!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
11928c645cfSHajimu UMEMOTO 	return;
12028c645cfSHajimu UMEMOTO     sin4 = (struct sockaddr_in *)sin6;
12128c645cfSHajimu UMEMOTO     addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
12228c645cfSHajimu UMEMOTO     port = sin6->sin6_port;
12328c645cfSHajimu UMEMOTO     memset(sin4, 0, sizeof(struct sockaddr_in));
12428c645cfSHajimu UMEMOTO     sin4->sin_addr.s_addr = addr;
12528c645cfSHajimu UMEMOTO     sin4->sin_port = port;
12628c645cfSHajimu UMEMOTO     sin4->sin_family = AF_INET;
12728c645cfSHajimu UMEMOTO     sin4->sin_len = sizeof(struct sockaddr_in);
12828c645cfSHajimu UMEMOTO }
12928c645cfSHajimu UMEMOTO 
1304ca1ab94SDag-Erling Smørgrav /*
131fc6e9e65SDag-Erling Smørgrav  * Get server response
1328e3986eaSDag-Erling Smørgrav  */
1338e3986eaSDag-Erling Smørgrav static int
134fc6e9e65SDag-Erling Smørgrav _ftp_chkerr(int cd)
1358e3986eaSDag-Erling Smørgrav {
136fc6e9e65SDag-Erling Smørgrav     if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) {
137842a95ccSDag-Erling Smørgrav 	_fetch_syserr();
1388e3986eaSDag-Erling Smørgrav 	return -1;
1398e3986eaSDag-Erling Smørgrav     }
140eac7a1e0SDag-Erling Smørgrav     if (isftpinfo(last_reply)) {
141eac7a1e0SDag-Erling Smørgrav 	while (!isftpreply(last_reply)) {
142eac7a1e0SDag-Erling Smørgrav 	    if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) {
143eac7a1e0SDag-Erling Smørgrav 		_fetch_syserr();
144eac7a1e0SDag-Erling Smørgrav 		return -1;
145eac7a1e0SDag-Erling Smørgrav 	    }
146eac7a1e0SDag-Erling Smørgrav 	}
147eac7a1e0SDag-Erling Smørgrav     }
148346298f0SDag-Erling Smørgrav 
149fc6e9e65SDag-Erling Smørgrav     while (lr_length && isspace(last_reply[lr_length-1]))
150fc6e9e65SDag-Erling Smørgrav 	lr_length--;
151fc6e9e65SDag-Erling Smørgrav     last_reply[lr_length] = 0;
152fc6e9e65SDag-Erling Smørgrav 
153fc6e9e65SDag-Erling Smørgrav     if (!isftpreply(last_reply)) {
1545cd33c40SDag-Erling Smørgrav 	_ftp_seterr(FTP_PROTOCOL_ERROR);
1558e3986eaSDag-Erling Smørgrav 	return -1;
1568e3986eaSDag-Erling Smørgrav     }
1578e3986eaSDag-Erling Smørgrav 
158fc6e9e65SDag-Erling Smørgrav     last_code = (last_reply[0] - '0') * 100
159fc6e9e65SDag-Erling Smørgrav 	+ (last_reply[1] - '0') * 10
160fc6e9e65SDag-Erling Smørgrav 	+ (last_reply[2] - '0');
161fc6e9e65SDag-Erling Smørgrav 
162fc6e9e65SDag-Erling Smørgrav     return last_code;
1638e3986eaSDag-Erling Smørgrav }
1648e3986eaSDag-Erling Smørgrav 
1658e3986eaSDag-Erling Smørgrav /*
166346298f0SDag-Erling Smørgrav  * Send a command and check reply
1674ca1ab94SDag-Erling Smørgrav  */
1684ca1ab94SDag-Erling Smørgrav static int
169fc6e9e65SDag-Erling Smørgrav _ftp_cmd(int cd, char *fmt, ...)
1704ca1ab94SDag-Erling Smørgrav {
171346298f0SDag-Erling Smørgrav     va_list ap;
172e137bcebSDag-Erling Smørgrav     size_t len;
173fc6e9e65SDag-Erling Smørgrav     char *msg;
174fc6e9e65SDag-Erling Smørgrav     int r;
1758e3986eaSDag-Erling Smørgrav 
176346298f0SDag-Erling Smørgrav     va_start(ap, fmt);
177e137bcebSDag-Erling Smørgrav     len = vasprintf(&msg, fmt, ap);
178346298f0SDag-Erling Smørgrav     va_end(ap);
179346298f0SDag-Erling Smørgrav 
180fc6e9e65SDag-Erling Smørgrav     if (msg == NULL) {
181fc6e9e65SDag-Erling Smørgrav 	errno = ENOMEM;
182fc6e9e65SDag-Erling Smørgrav 	_fetch_syserr();
183fc6e9e65SDag-Erling Smørgrav 	return -1;
184fc6e9e65SDag-Erling Smørgrav     }
185e137bcebSDag-Erling Smørgrav 
186e137bcebSDag-Erling Smørgrav     r = _fetch_putln(cd, msg, len);
187fc6e9e65SDag-Erling Smørgrav     free(msg);
188e137bcebSDag-Erling Smørgrav 
189fc6e9e65SDag-Erling Smørgrav     if (r == -1) {
190fc6e9e65SDag-Erling Smørgrav 	_fetch_syserr();
191fc6e9e65SDag-Erling Smørgrav 	return -1;
192fc6e9e65SDag-Erling Smørgrav     }
193fc6e9e65SDag-Erling Smørgrav 
194fc6e9e65SDag-Erling Smørgrav     return _ftp_chkerr(cd);
1954ca1ab94SDag-Erling Smørgrav }
1964ca1ab94SDag-Erling Smørgrav 
1974ca1ab94SDag-Erling Smørgrav /*
1981a5faa10SDag-Erling Smørgrav  * Return a pointer to the filename part of a path
1991a5faa10SDag-Erling Smørgrav  */
2001a5faa10SDag-Erling Smørgrav static char *
2011a5faa10SDag-Erling Smørgrav _ftp_filename(char *file)
2021a5faa10SDag-Erling Smørgrav {
2031a5faa10SDag-Erling Smørgrav     char *s;
2041a5faa10SDag-Erling Smørgrav 
2051a5faa10SDag-Erling Smørgrav     if ((s = strrchr(file, '/')) == NULL)
2061a5faa10SDag-Erling Smørgrav 	return file;
2071a5faa10SDag-Erling Smørgrav     else
2081a5faa10SDag-Erling Smørgrav 	return s + 1;
2091a5faa10SDag-Erling Smørgrav }
2101a5faa10SDag-Erling Smørgrav 
2111a5faa10SDag-Erling Smørgrav /*
2121a5faa10SDag-Erling Smørgrav  * Change working directory to the directory that contains the
2131a5faa10SDag-Erling Smørgrav  * specified file.
2141a5faa10SDag-Erling Smørgrav  */
2151a5faa10SDag-Erling Smørgrav static int
2161a5faa10SDag-Erling Smørgrav _ftp_cwd(int cd, char *file)
2171a5faa10SDag-Erling Smørgrav {
2181a5faa10SDag-Erling Smørgrav     char *s;
2191a5faa10SDag-Erling Smørgrav     int e;
2201a5faa10SDag-Erling Smørgrav 
2215e3f46b5SDag-Erling Smørgrav     if ((s = strrchr(file, '/')) == NULL || s == file) {
2221a5faa10SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "CWD /");
2231a5faa10SDag-Erling Smørgrav     } else {
2241a5faa10SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "CWD %.*s", s - file, file);
2251a5faa10SDag-Erling Smørgrav     }
2261a5faa10SDag-Erling Smørgrav     if (e != FTP_FILE_ACTION_OK) {
2271a5faa10SDag-Erling Smørgrav 	_ftp_seterr(e);
2281a5faa10SDag-Erling Smørgrav 	return -1;
2291a5faa10SDag-Erling Smørgrav     }
2301a5faa10SDag-Erling Smørgrav     return 0;
2311a5faa10SDag-Erling Smørgrav }
2321a5faa10SDag-Erling Smørgrav 
2331a5faa10SDag-Erling Smørgrav /*
2341a5faa10SDag-Erling Smørgrav  * Request and parse file stats
2351a5faa10SDag-Erling Smørgrav  */
2361a5faa10SDag-Erling Smørgrav static int
2371a5faa10SDag-Erling Smørgrav _ftp_stat(int cd, char *file, struct url_stat *us)
2381a5faa10SDag-Erling Smørgrav {
2391a5faa10SDag-Erling Smørgrav     char *ln, *s;
2401a5faa10SDag-Erling Smørgrav     struct tm tm;
2411a5faa10SDag-Erling Smørgrav     time_t t;
2421a5faa10SDag-Erling Smørgrav     int e;
2431a5faa10SDag-Erling Smørgrav 
244269532d9SDag-Erling Smørgrav     us->size = -1;
245269532d9SDag-Erling Smørgrav     us->atime = us->mtime = 0;
246269532d9SDag-Erling Smørgrav 
2471a5faa10SDag-Erling Smørgrav     if ((s = strrchr(file, '/')) == NULL)
2481a5faa10SDag-Erling Smørgrav 	s = file;
2491a5faa10SDag-Erling Smørgrav     else
2501a5faa10SDag-Erling Smørgrav 	++s;
2511a5faa10SDag-Erling Smørgrav 
2521a5faa10SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "SIZE %s", s)) != FTP_FILE_STATUS) {
2531a5faa10SDag-Erling Smørgrav 	_ftp_seterr(e);
2541a5faa10SDag-Erling Smørgrav 	return -1;
2551a5faa10SDag-Erling Smørgrav     }
2561a5faa10SDag-Erling Smørgrav     for (ln = last_reply + 4; *ln && isspace(*ln); ln++)
2571a5faa10SDag-Erling Smørgrav 	/* nothing */ ;
2581a5faa10SDag-Erling Smørgrav     for (us->size = 0; *ln && isdigit(*ln); ln++)
2591a5faa10SDag-Erling Smørgrav 	us->size = us->size * 10 + *ln - '0';
2601a5faa10SDag-Erling Smørgrav     if (*ln && !isspace(*ln)) {
2611a5faa10SDag-Erling Smørgrav 	_ftp_seterr(FTP_PROTOCOL_ERROR);
2621a5faa10SDag-Erling Smørgrav 	return -1;
2631a5faa10SDag-Erling Smørgrav     }
26463428824SDag-Erling Smørgrav     if (us->size == 0)
26563428824SDag-Erling Smørgrav 	us->size = -1;
2661a5faa10SDag-Erling Smørgrav     DEBUG(fprintf(stderr, "size: [\033[1m%lld\033[m]\n", us->size));
2671a5faa10SDag-Erling Smørgrav 
2681a5faa10SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "MDTM %s", s)) != FTP_FILE_STATUS) {
2691a5faa10SDag-Erling Smørgrav 	_ftp_seterr(e);
2701a5faa10SDag-Erling Smørgrav 	return -1;
2711a5faa10SDag-Erling Smørgrav     }
2721a5faa10SDag-Erling Smørgrav     for (ln = last_reply + 4; *ln && isspace(*ln); ln++)
2731a5faa10SDag-Erling Smørgrav 	/* nothing */ ;
2741a5faa10SDag-Erling Smørgrav     switch (strspn(ln, "0123456789")) {
2751a5faa10SDag-Erling Smørgrav     case 14:
2761a5faa10SDag-Erling Smørgrav 	break;
2771a5faa10SDag-Erling Smørgrav     case 15:
2781a5faa10SDag-Erling Smørgrav 	ln++;
2791a5faa10SDag-Erling Smørgrav 	ln[0] = '2';
2801a5faa10SDag-Erling Smørgrav 	ln[1] = '0';
2811a5faa10SDag-Erling Smørgrav 	break;
2821a5faa10SDag-Erling Smørgrav     default:
2831a5faa10SDag-Erling Smørgrav 	_ftp_seterr(FTP_PROTOCOL_ERROR);
2841a5faa10SDag-Erling Smørgrav 	return -1;
2851a5faa10SDag-Erling Smørgrav     }
2861a5faa10SDag-Erling Smørgrav     if (sscanf(ln, "%04d%02d%02d%02d%02d%02d",
2871a5faa10SDag-Erling Smørgrav 	       &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2881a5faa10SDag-Erling Smørgrav 	       &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2891a5faa10SDag-Erling Smørgrav 	_ftp_seterr(FTP_PROTOCOL_ERROR);
2901a5faa10SDag-Erling Smørgrav 	return -1;
2911a5faa10SDag-Erling Smørgrav     }
2921a5faa10SDag-Erling Smørgrav     tm.tm_mon--;
2931a5faa10SDag-Erling Smørgrav     tm.tm_year -= 1900;
2941a5faa10SDag-Erling Smørgrav     tm.tm_isdst = -1;
2951a5faa10SDag-Erling Smørgrav     t = timegm(&tm);
2961a5faa10SDag-Erling Smørgrav     if (t == (time_t)-1)
2971a5faa10SDag-Erling Smørgrav 	t = time(NULL);
2981a5faa10SDag-Erling Smørgrav     us->mtime = t;
2991a5faa10SDag-Erling Smørgrav     us->atime = t;
3001a5faa10SDag-Erling Smørgrav     DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d "
3011a5faa10SDag-Erling Smørgrav 		  "%02d:%02d:%02d\033[m]\n",
3021a5faa10SDag-Erling Smørgrav 		  tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
3031a5faa10SDag-Erling Smørgrav 		  tm.tm_hour, tm.tm_min, tm.tm_sec));
3041a5faa10SDag-Erling Smørgrav     return 0;
3051a5faa10SDag-Erling Smørgrav }
3061a5faa10SDag-Erling Smørgrav 
3071a5faa10SDag-Erling Smørgrav /*
308c7d40ef2SDag-Erling Smørgrav  * I/O functions for FTP
309c7d40ef2SDag-Erling Smørgrav  */
310c7d40ef2SDag-Erling Smørgrav struct ftpio {
311c7d40ef2SDag-Erling Smørgrav     int		 csd;		/* Control socket descriptor */
312c7d40ef2SDag-Erling Smørgrav     int		 dsd;		/* Data socket descriptor */
313c7d40ef2SDag-Erling Smørgrav     int		 dir;		/* Direction */
314c7d40ef2SDag-Erling Smørgrav     int		 eof;		/* EOF reached */
315c7d40ef2SDag-Erling Smørgrav     int		 err;		/* Error code */
316c7d40ef2SDag-Erling Smørgrav };
317c7d40ef2SDag-Erling Smørgrav 
318c7d40ef2SDag-Erling Smørgrav static int	_ftp_readfn(void *, char *, int);
319c7d40ef2SDag-Erling Smørgrav static int	_ftp_writefn(void *, const char *, int);
320c7d40ef2SDag-Erling Smørgrav static fpos_t	_ftp_seekfn(void *, fpos_t, int);
321c7d40ef2SDag-Erling Smørgrav static int	_ftp_closefn(void *);
322c7d40ef2SDag-Erling Smørgrav 
323c7d40ef2SDag-Erling Smørgrav static int
324c7d40ef2SDag-Erling Smørgrav _ftp_readfn(void *v, char *buf, int len)
325c7d40ef2SDag-Erling Smørgrav {
326c7d40ef2SDag-Erling Smørgrav     struct ftpio *io;
327c7d40ef2SDag-Erling Smørgrav     int r;
328c7d40ef2SDag-Erling Smørgrav 
329c7d40ef2SDag-Erling Smørgrav     io = (struct ftpio *)v;
330c7d40ef2SDag-Erling Smørgrav     if (io->csd == -1 || io->dsd == -1 || io->dir == O_WRONLY) {
331c7d40ef2SDag-Erling Smørgrav 	errno = EBADF;
332c7d40ef2SDag-Erling Smørgrav 	return -1;
333c7d40ef2SDag-Erling Smørgrav     }
334c7d40ef2SDag-Erling Smørgrav     if (io->err) {
335c7d40ef2SDag-Erling Smørgrav 	errno = io->err;
336c7d40ef2SDag-Erling Smørgrav 	return -1;
337c7d40ef2SDag-Erling Smørgrav     }
338c7d40ef2SDag-Erling Smørgrav     if (io->eof)
339c7d40ef2SDag-Erling Smørgrav 	return 0;
340c7d40ef2SDag-Erling Smørgrav     r = read(io->dsd, buf, len);
341c7d40ef2SDag-Erling Smørgrav     if (r > 0)
342c7d40ef2SDag-Erling Smørgrav 	return r;
343c7d40ef2SDag-Erling Smørgrav     if (r == 0) {
344c7d40ef2SDag-Erling Smørgrav 	io->eof = 1;
345c7d40ef2SDag-Erling Smørgrav 	return _ftp_closefn(v);
346c7d40ef2SDag-Erling Smørgrav     }
347c7d40ef2SDag-Erling Smørgrav     io->err = errno;
348c7d40ef2SDag-Erling Smørgrav     return -1;
349c7d40ef2SDag-Erling Smørgrav }
350c7d40ef2SDag-Erling Smørgrav 
351c7d40ef2SDag-Erling Smørgrav static int
352c7d40ef2SDag-Erling Smørgrav _ftp_writefn(void *v, const char *buf, int len)
353c7d40ef2SDag-Erling Smørgrav {
354c7d40ef2SDag-Erling Smørgrav     struct ftpio *io;
355c7d40ef2SDag-Erling Smørgrav     int w;
356c7d40ef2SDag-Erling Smørgrav 
357c7d40ef2SDag-Erling Smørgrav     io = (struct ftpio *)v;
358c7d40ef2SDag-Erling Smørgrav     if (io->csd == -1 || io->dsd == -1 || io->dir == O_RDONLY) {
359c7d40ef2SDag-Erling Smørgrav 	errno = EBADF;
360c7d40ef2SDag-Erling Smørgrav 	return -1;
361c7d40ef2SDag-Erling Smørgrav     }
362c7d40ef2SDag-Erling Smørgrav     if (io->err) {
363c7d40ef2SDag-Erling Smørgrav 	errno = io->err;
364c7d40ef2SDag-Erling Smørgrav 	return -1;
365c7d40ef2SDag-Erling Smørgrav     }
366c7d40ef2SDag-Erling Smørgrav     w = write(io->dsd, buf, len);
367c7d40ef2SDag-Erling Smørgrav     if (w >= 0)
368c7d40ef2SDag-Erling Smørgrav 	return w;
369c7d40ef2SDag-Erling Smørgrav     io->err = errno;
370c7d40ef2SDag-Erling Smørgrav     return -1;
371c7d40ef2SDag-Erling Smørgrav }
372c7d40ef2SDag-Erling Smørgrav 
373c7d40ef2SDag-Erling Smørgrav static fpos_t
374c7d40ef2SDag-Erling Smørgrav _ftp_seekfn(void *v, fpos_t pos, int whence)
375c7d40ef2SDag-Erling Smørgrav {
376c7d40ef2SDag-Erling Smørgrav     errno = ESPIPE;
377c7d40ef2SDag-Erling Smørgrav     return -1;
378c7d40ef2SDag-Erling Smørgrav }
379c7d40ef2SDag-Erling Smørgrav 
380c7d40ef2SDag-Erling Smørgrav static int
381c7d40ef2SDag-Erling Smørgrav _ftp_closefn(void *v)
382c7d40ef2SDag-Erling Smørgrav {
383c7d40ef2SDag-Erling Smørgrav     struct ftpio *io;
384c7d40ef2SDag-Erling Smørgrav 
385c7d40ef2SDag-Erling Smørgrav     io = (struct ftpio *)v;
386c7d40ef2SDag-Erling Smørgrav     if (io->dir == -1)
387c7d40ef2SDag-Erling Smørgrav 	return 0;
388c7d40ef2SDag-Erling Smørgrav     if (io->csd == -1 || io->dsd == -1) {
389c7d40ef2SDag-Erling Smørgrav 	errno = EBADF;
390c7d40ef2SDag-Erling Smørgrav 	return -1;
391c7d40ef2SDag-Erling Smørgrav     }
392c7d40ef2SDag-Erling Smørgrav     io->err = _ftp_chkerr(io->csd);
393c7d40ef2SDag-Erling Smørgrav     io->dir = -1;
394c7d40ef2SDag-Erling Smørgrav     if (close(io->dsd) == -1)
395c7d40ef2SDag-Erling Smørgrav 	return -1;
396c7d40ef2SDag-Erling Smørgrav     io->dsd = -1;
397c7d40ef2SDag-Erling Smørgrav     close(io->csd);
398c7d40ef2SDag-Erling Smørgrav     io->csd = -1;
399c7d40ef2SDag-Erling Smørgrav     return io->err ? -1 : 0;
400c7d40ef2SDag-Erling Smørgrav }
401c7d40ef2SDag-Erling Smørgrav 
402c7d40ef2SDag-Erling Smørgrav static FILE *
403c7d40ef2SDag-Erling Smørgrav _ftp_setup(int csd, int dsd, int mode)
404c7d40ef2SDag-Erling Smørgrav {
405c7d40ef2SDag-Erling Smørgrav     struct ftpio *io;
406c7d40ef2SDag-Erling Smørgrav     FILE *f;
407c7d40ef2SDag-Erling Smørgrav 
408c7d40ef2SDag-Erling Smørgrav     if ((io = malloc(sizeof *io)) == NULL)
409c7d40ef2SDag-Erling Smørgrav 	return NULL;
410c7d40ef2SDag-Erling Smørgrav     io->csd = dup(csd);
411c7d40ef2SDag-Erling Smørgrav     io->dsd = dsd;
412c7d40ef2SDag-Erling Smørgrav     io->dir = mode;
413c7d40ef2SDag-Erling Smørgrav     io->eof = io->err = 0;
414c7d40ef2SDag-Erling Smørgrav     f = funopen(io, _ftp_readfn, _ftp_writefn, _ftp_seekfn, _ftp_closefn);
415c7d40ef2SDag-Erling Smørgrav     if (f == NULL)
416c7d40ef2SDag-Erling Smørgrav 	free(io);
417c7d40ef2SDag-Erling Smørgrav     return f;
418c7d40ef2SDag-Erling Smørgrav }
419c7d40ef2SDag-Erling Smørgrav 
420c7d40ef2SDag-Erling Smørgrav /*
421f62e5228SDag-Erling Smørgrav  * Transfer file
4224ca1ab94SDag-Erling Smørgrav  */
4234ca1ab94SDag-Erling Smørgrav static FILE *
42432425dafSDag-Erling Smørgrav _ftp_transfer(int cd, char *oper, char *file,
425c7d40ef2SDag-Erling Smørgrav 	      int mode, off_t offset, char *flags)
4264ca1ab94SDag-Erling Smørgrav {
42728c645cfSHajimu UMEMOTO     struct sockaddr_storage sin;
42828c645cfSHajimu UMEMOTO     struct sockaddr_in6 *sin6;
42928c645cfSHajimu UMEMOTO     struct sockaddr_in *sin4;
430f5f109a0SDag-Erling Smørgrav     int pasv, high, verbose;
431f5f109a0SDag-Erling Smørgrav     int e, sd = -1;
432f5f109a0SDag-Erling Smørgrav     socklen_t l;
433346298f0SDag-Erling Smørgrav     char *s;
434346298f0SDag-Erling Smørgrav     FILE *df;
4358e3986eaSDag-Erling Smørgrav 
436f5f109a0SDag-Erling Smørgrav     /* check flags */
437f5f109a0SDag-Erling Smørgrav     pasv = (flags && strchr(flags, 'p'));
438f5f109a0SDag-Erling Smørgrav     high = (flags && strchr(flags, 'h'));
439f5f109a0SDag-Erling Smørgrav     verbose = (flags && strchr(flags, 'v'));
440f5f109a0SDag-Erling Smørgrav 
441d02e84a6SDag-Erling Smørgrav     /* passive mode */
4428b9ba466SDag-Erling Smørgrav     if (!pasv)
4438b9ba466SDag-Erling Smørgrav 	pasv = ((s = getenv("FTP_PASSIVE_MODE")) == NULL ||
4448b9ba466SDag-Erling Smørgrav 		strncasecmp(s, "no", 2) != 0);
445d02e84a6SDag-Erling Smørgrav 
44628c645cfSHajimu UMEMOTO     /* find our own address, bind, and listen */
44728c645cfSHajimu UMEMOTO     l = sizeof sin;
44828c645cfSHajimu UMEMOTO     if (getsockname(cd, (struct sockaddr *)&sin, &l) == -1)
44928c645cfSHajimu UMEMOTO 	goto sysouch;
45028c645cfSHajimu UMEMOTO     if (sin.ss_family == AF_INET6)
45128c645cfSHajimu UMEMOTO 	unmappedaddr((struct sockaddr_in6 *)&sin);
45228c645cfSHajimu UMEMOTO 
453346298f0SDag-Erling Smørgrav     /* open data socket */
45428c645cfSHajimu UMEMOTO     if ((sd = socket(sin.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
455842a95ccSDag-Erling Smørgrav 	_fetch_syserr();
456346298f0SDag-Erling Smørgrav 	return NULL;
457346298f0SDag-Erling Smørgrav     }
458346298f0SDag-Erling Smørgrav 
459346298f0SDag-Erling Smørgrav     if (pasv) {
46028c645cfSHajimu UMEMOTO 	u_char addr[64];
461346298f0SDag-Erling Smørgrav 	char *ln, *p;
462346298f0SDag-Erling Smørgrav 	int i;
46328c645cfSHajimu UMEMOTO 	int port;
464346298f0SDag-Erling Smørgrav 
465346298f0SDag-Erling Smørgrav 	/* send PASV command */
466f5f109a0SDag-Erling Smørgrav 	if (verbose)
467f5f109a0SDag-Erling Smørgrav 	    _fetch_info("setting passive mode");
46828c645cfSHajimu UMEMOTO 	switch (sin.ss_family) {
46928c645cfSHajimu UMEMOTO 	case AF_INET:
470fc6e9e65SDag-Erling Smørgrav 	    if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE)
471346298f0SDag-Erling Smørgrav 		goto ouch;
47228c645cfSHajimu UMEMOTO 	    break;
47328c645cfSHajimu UMEMOTO 	case AF_INET6:
47428c645cfSHajimu UMEMOTO 	    if ((e = _ftp_cmd(cd, "EPSV")) != FTP_EPASSIVE_MODE) {
47528c645cfSHajimu UMEMOTO 		if (e == -1)
47628c645cfSHajimu UMEMOTO 		    goto ouch;
47728c645cfSHajimu UMEMOTO 		if ((e = _ftp_cmd(cd, "LPSV")) != FTP_LPASSIVE_MODE)
47828c645cfSHajimu UMEMOTO 		    goto ouch;
47928c645cfSHajimu UMEMOTO 	    }
48028c645cfSHajimu UMEMOTO 	    break;
48128c645cfSHajimu UMEMOTO 	default:
4825cd33c40SDag-Erling Smørgrav 	    e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
48328c645cfSHajimu UMEMOTO 	    goto ouch;
48428c645cfSHajimu UMEMOTO 	}
485346298f0SDag-Erling Smørgrav 
486f5f109a0SDag-Erling Smørgrav 	/*
487f5f109a0SDag-Erling Smørgrav 	 * Find address and port number. The reply to the PASV command
488f5f109a0SDag-Erling Smørgrav          * is IMHO the one and only weak point in the FTP protocol.
489f5f109a0SDag-Erling Smørgrav 	 */
490fc6e9e65SDag-Erling Smørgrav 	ln = last_reply;
491fa5dce6cSHajimu UMEMOTO       	switch (e) {
492fa5dce6cSHajimu UMEMOTO 	case FTP_PASSIVE_MODE:
493fa5dce6cSHajimu UMEMOTO 	case FTP_LPASSIVE_MODE:
49451e3d46eSDag-Erling Smørgrav 	    for (p = ln + 3; *p && !isdigit(*p); p++)
495346298f0SDag-Erling Smørgrav 		/* nothing */ ;
49628c645cfSHajimu UMEMOTO 	    if (!*p) {
4975cd33c40SDag-Erling Smørgrav 		e = FTP_PROTOCOL_ERROR;
4986efb30c8SDag-Erling Smørgrav 		goto ouch;
499346298f0SDag-Erling Smørgrav 	    }
50028c645cfSHajimu UMEMOTO 	    l = (e == FTP_PASSIVE_MODE ? 6 : 21);
50128c645cfSHajimu UMEMOTO 	    for (i = 0; *p && i < l; i++, p++)
50228c645cfSHajimu UMEMOTO 		addr[i] = strtol(p, &p, 10);
50328c645cfSHajimu UMEMOTO 	    if (i < l) {
5045cd33c40SDag-Erling Smørgrav 		e = FTP_PROTOCOL_ERROR;
50528c645cfSHajimu UMEMOTO 		goto ouch;
50628c645cfSHajimu UMEMOTO 	    }
50728c645cfSHajimu UMEMOTO 	    break;
50828c645cfSHajimu UMEMOTO 	case FTP_EPASSIVE_MODE:
509fa5dce6cSHajimu UMEMOTO 	    for (p = ln + 3; *p && *p != '('; p++)
510fa5dce6cSHajimu UMEMOTO 		/* nothing */ ;
511fa5dce6cSHajimu UMEMOTO 	    if (!*p) {
5125cd33c40SDag-Erling Smørgrav 		e = FTP_PROTOCOL_ERROR;
513fa5dce6cSHajimu UMEMOTO 		goto ouch;
514fa5dce6cSHajimu UMEMOTO 	    }
515fa5dce6cSHajimu UMEMOTO 	    ++p;
51628c645cfSHajimu UMEMOTO 	    if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2],
51728c645cfSHajimu UMEMOTO 		       &port, &addr[3]) != 5 ||
51828c645cfSHajimu UMEMOTO 		addr[0] != addr[1] ||
51928c645cfSHajimu UMEMOTO 		addr[0] != addr[2] || addr[0] != addr[3]) {
5205cd33c40SDag-Erling Smørgrav 		e = FTP_PROTOCOL_ERROR;
52128c645cfSHajimu UMEMOTO 		goto ouch;
52228c645cfSHajimu UMEMOTO 	    }
52328c645cfSHajimu UMEMOTO 	    break;
52428c645cfSHajimu UMEMOTO 	}
525346298f0SDag-Erling Smørgrav 
52632425dafSDag-Erling Smørgrav 	/* seek to required offset */
52732425dafSDag-Erling Smørgrav 	if (offset)
52832425dafSDag-Erling Smørgrav 	    if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK)
52932425dafSDag-Erling Smørgrav 		goto sysouch;
53032425dafSDag-Erling Smørgrav 
531346298f0SDag-Erling Smørgrav 	/* construct sockaddr for data socket */
53232425dafSDag-Erling Smørgrav 	l = sizeof sin;
533fc6e9e65SDag-Erling Smørgrav 	if (getpeername(cd, (struct sockaddr *)&sin, &l) == -1)
534346298f0SDag-Erling Smørgrav 	    goto sysouch;
53528c645cfSHajimu UMEMOTO 	if (sin.ss_family == AF_INET6)
53628c645cfSHajimu UMEMOTO 	    unmappedaddr((struct sockaddr_in6 *)&sin);
53728c645cfSHajimu UMEMOTO 	switch (sin.ss_family) {
53828c645cfSHajimu UMEMOTO 	case AF_INET6:
53928c645cfSHajimu UMEMOTO 	    sin6 = (struct sockaddr_in6 *)&sin;
54028c645cfSHajimu UMEMOTO 	    if (e == FTP_EPASSIVE_MODE)
54128c645cfSHajimu UMEMOTO 		sin6->sin6_port = htons(port);
54228c645cfSHajimu UMEMOTO 	    else {
54328c645cfSHajimu UMEMOTO 		bcopy(addr + 2, (char *)&sin6->sin6_addr, 16);
54428c645cfSHajimu UMEMOTO 		bcopy(addr + 19, (char *)&sin6->sin6_port, 2);
54528c645cfSHajimu UMEMOTO 	    }
54628c645cfSHajimu UMEMOTO 	    break;
54728c645cfSHajimu UMEMOTO 	case AF_INET:
54828c645cfSHajimu UMEMOTO 	    sin4 = (struct sockaddr_in *)&sin;
54928c645cfSHajimu UMEMOTO 	    if (e == FTP_EPASSIVE_MODE)
55028c645cfSHajimu UMEMOTO 		sin4->sin_port = htons(port);
55128c645cfSHajimu UMEMOTO 	    else {
55228c645cfSHajimu UMEMOTO 		bcopy(addr, (char *)&sin4->sin_addr, 4);
55328c645cfSHajimu UMEMOTO 		bcopy(addr + 4, (char *)&sin4->sin_port, 2);
55428c645cfSHajimu UMEMOTO 	    }
55528c645cfSHajimu UMEMOTO 	    break;
55628c645cfSHajimu UMEMOTO 	default:
5575cd33c40SDag-Erling Smørgrav 	    e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
55828c645cfSHajimu UMEMOTO 	    break;
55928c645cfSHajimu UMEMOTO 	}
560346298f0SDag-Erling Smørgrav 
561346298f0SDag-Erling Smørgrav 	/* connect to data port */
562f5f109a0SDag-Erling Smørgrav 	if (verbose)
563f5f109a0SDag-Erling Smørgrav 	    _fetch_info("opening data connection");
56428c645cfSHajimu UMEMOTO 	if (connect(sd, (struct sockaddr *)&sin, sin.ss_len) == -1)
565346298f0SDag-Erling Smørgrav 	    goto sysouch;
566346298f0SDag-Erling Smørgrav 
567346298f0SDag-Erling Smørgrav 	/* make the server initiate the transfer */
568f5f109a0SDag-Erling Smørgrav 	if (verbose)
569def5f54cSDag-Erling Smørgrav 	    _fetch_info("initiating transfer");
5701a5faa10SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "%s %s", oper, _ftp_filename(file));
571ea014d85SDag-Erling Smørgrav 	if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
572346298f0SDag-Erling Smørgrav 	    goto ouch;
573346298f0SDag-Erling Smørgrav 
574346298f0SDag-Erling Smørgrav     } else {
575346298f0SDag-Erling Smørgrav 	u_int32_t a;
576346298f0SDag-Erling Smørgrav 	u_short p;
577f5f109a0SDag-Erling Smørgrav 	int arg, d;
57828c645cfSHajimu UMEMOTO 	char *ap;
57928c645cfSHajimu UMEMOTO 	char hname[INET6_ADDRSTRLEN];
580346298f0SDag-Erling Smørgrav 
58128c645cfSHajimu UMEMOTO 	switch (sin.ss_family) {
58228c645cfSHajimu UMEMOTO 	case AF_INET6:
58328c645cfSHajimu UMEMOTO 	    ((struct sockaddr_in6 *)&sin)->sin6_port = 0;
58428c645cfSHajimu UMEMOTO #ifdef IPV6_PORTRANGE
58528c645cfSHajimu UMEMOTO 	    arg = high ? IPV6_PORTRANGE_HIGH : IPV6_PORTRANGE_DEFAULT;
58628c645cfSHajimu UMEMOTO 	    if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE,
58728c645cfSHajimu UMEMOTO 			   (char *)&arg, sizeof(arg)) == -1)
588346298f0SDag-Erling Smørgrav 		goto sysouch;
58928c645cfSHajimu UMEMOTO #endif
59028c645cfSHajimu UMEMOTO 	    break;
59128c645cfSHajimu UMEMOTO 	case AF_INET:
59228c645cfSHajimu UMEMOTO 	    ((struct sockaddr_in *)&sin)->sin_port = 0;
593f5f109a0SDag-Erling Smørgrav 	    arg = high ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT;
594f5f109a0SDag-Erling Smørgrav 	    if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
59532425dafSDag-Erling Smørgrav 			   (char *)&arg, sizeof arg) == -1)
596f5f109a0SDag-Erling Smørgrav 		goto sysouch;
59728c645cfSHajimu UMEMOTO 	    break;
59828c645cfSHajimu UMEMOTO 	}
599f5f109a0SDag-Erling Smørgrav 	if (verbose)
600f5f109a0SDag-Erling Smørgrav 	    _fetch_info("binding data socket");
60128c645cfSHajimu UMEMOTO 	if (bind(sd, (struct sockaddr *)&sin, sin.ss_len) == -1)
602346298f0SDag-Erling Smørgrav 	    goto sysouch;
603ecc91352SDag-Erling Smørgrav 	if (listen(sd, 1) == -1)
604346298f0SDag-Erling Smørgrav 	    goto sysouch;
605346298f0SDag-Erling Smørgrav 
606346298f0SDag-Erling Smørgrav 	/* find what port we're on and tell the server */
607ecc91352SDag-Erling Smørgrav 	if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1)
608346298f0SDag-Erling Smørgrav 	    goto sysouch;
60928c645cfSHajimu UMEMOTO 	switch (sin.ss_family) {
61028c645cfSHajimu UMEMOTO 	case AF_INET:
61128c645cfSHajimu UMEMOTO 	    sin4 = (struct sockaddr_in *)&sin;
61228c645cfSHajimu UMEMOTO 	    a = ntohl(sin4->sin_addr.s_addr);
61328c645cfSHajimu UMEMOTO 	    p = ntohs(sin4->sin_port);
614fc6e9e65SDag-Erling Smørgrav 	    e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d",
6155aea254fSDag-Erling Smørgrav 			 (a >> 24) & 0xff, (a >> 16) & 0xff,
6165aea254fSDag-Erling Smørgrav 			 (a >> 8) & 0xff, a & 0xff,
6175aea254fSDag-Erling Smørgrav 			 (p >> 8) & 0xff, p & 0xff);
61828c645cfSHajimu UMEMOTO 	    break;
61928c645cfSHajimu UMEMOTO 	case AF_INET6:
62028c645cfSHajimu UMEMOTO #define UC(b)	(((int)b)&0xff)
62128c645cfSHajimu UMEMOTO 	    e = -1;
62228c645cfSHajimu UMEMOTO 	    sin6 = (struct sockaddr_in6 *)&sin;
62328c645cfSHajimu UMEMOTO 	    if (getnameinfo((struct sockaddr *)&sin, sin.ss_len,
62428c645cfSHajimu UMEMOTO 			    hname, sizeof(hname),
62528c645cfSHajimu UMEMOTO 			    NULL, 0, NI_NUMERICHOST) == 0) {
62628c645cfSHajimu UMEMOTO 		e = _ftp_cmd(cd, "EPRT |%d|%s|%d|", 2, hname,
62728c645cfSHajimu UMEMOTO 			     htons(sin6->sin6_port));
62828c645cfSHajimu UMEMOTO 		if (e == -1)
62928c645cfSHajimu UMEMOTO 		    goto ouch;
63028c645cfSHajimu UMEMOTO 	    }
63128c645cfSHajimu UMEMOTO 	    if (e != FTP_OK) {
63228c645cfSHajimu UMEMOTO 		ap = (char *)&sin6->sin6_addr;
63328c645cfSHajimu UMEMOTO 		e = _ftp_cmd(cd,
63428c645cfSHajimu UMEMOTO      "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
63528c645cfSHajimu UMEMOTO 			     6, 16,
63628c645cfSHajimu UMEMOTO 			     UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]),
63728c645cfSHajimu UMEMOTO 			     UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]),
63828c645cfSHajimu UMEMOTO 			     UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]),
63928c645cfSHajimu UMEMOTO 			     UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]),
64028c645cfSHajimu UMEMOTO 			     2,
64128c645cfSHajimu UMEMOTO 			     (ntohs(sin6->sin6_port) >> 8) & 0xff,
64228c645cfSHajimu UMEMOTO 			     ntohs(sin6->sin6_port)        & 0xff);
64328c645cfSHajimu UMEMOTO 	    }
64428c645cfSHajimu UMEMOTO 	    break;
64528c645cfSHajimu UMEMOTO 	default:
6465cd33c40SDag-Erling Smørgrav 	    e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
64728c645cfSHajimu UMEMOTO 	    goto ouch;
64828c645cfSHajimu UMEMOTO 	}
6495aea254fSDag-Erling Smørgrav 	if (e != FTP_OK)
650346298f0SDag-Erling Smørgrav 	    goto ouch;
651346298f0SDag-Erling Smørgrav 
652893980adSDag-Erling Smørgrav 	/* seek to required offset */
653893980adSDag-Erling Smørgrav 	if (offset)
654893980adSDag-Erling Smørgrav 	    if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK)
655893980adSDag-Erling Smørgrav 		goto sysouch;
656893980adSDag-Erling Smørgrav 
657346298f0SDag-Erling Smørgrav 	/* make the server initiate the transfer */
658f5f109a0SDag-Erling Smørgrav 	if (verbose)
659f5f109a0SDag-Erling Smørgrav 	    _fetch_info("initiating transfer");
6601a5faa10SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "%s %s", oper, _ftp_filename(file));
6615aea254fSDag-Erling Smørgrav 	if (e != FTP_OPEN_DATA_CONNECTION)
662346298f0SDag-Erling Smørgrav 	    goto ouch;
663346298f0SDag-Erling Smørgrav 
664346298f0SDag-Erling Smørgrav 	/* accept the incoming connection and go to town */
665ecc91352SDag-Erling Smørgrav 	if ((d = accept(sd, NULL, NULL)) == -1)
666346298f0SDag-Erling Smørgrav 	    goto sysouch;
667346298f0SDag-Erling Smørgrav 	close(sd);
668346298f0SDag-Erling Smørgrav 	sd = d;
669346298f0SDag-Erling Smørgrav     }
670346298f0SDag-Erling Smørgrav 
671c7d40ef2SDag-Erling Smørgrav     if ((df = _ftp_setup(cd, sd, mode)) == NULL)
672346298f0SDag-Erling Smørgrav 	goto sysouch;
673346298f0SDag-Erling Smørgrav     return df;
674346298f0SDag-Erling Smørgrav 
675346298f0SDag-Erling Smørgrav sysouch:
676842a95ccSDag-Erling Smørgrav     _fetch_syserr();
67728c645cfSHajimu UMEMOTO     if (sd >= 0)
6785aea254fSDag-Erling Smørgrav 	close(sd);
6795aea254fSDag-Erling Smørgrav     return NULL;
6805aea254fSDag-Erling Smørgrav 
681346298f0SDag-Erling Smørgrav ouch:
682fc6e9e65SDag-Erling Smørgrav     if (e != -1)
6835aea254fSDag-Erling Smørgrav 	_ftp_seterr(e);
68428c645cfSHajimu UMEMOTO     if (sd >= 0)
685346298f0SDag-Erling Smørgrav 	close(sd);
6864ca1ab94SDag-Erling Smørgrav     return NULL;
6874ca1ab94SDag-Erling Smørgrav }
6884ca1ab94SDag-Erling Smørgrav 
6898e3986eaSDag-Erling Smørgrav /*
69010851dc4SDag-Erling Smørgrav  * Return default port
69110851dc4SDag-Erling Smørgrav  */
69210851dc4SDag-Erling Smørgrav static int
69310851dc4SDag-Erling Smørgrav _ftp_default_port(void)
69410851dc4SDag-Erling Smørgrav {
69510851dc4SDag-Erling Smørgrav     struct servent *se;
69610851dc4SDag-Erling Smørgrav 
6971a16ed4cSDag-Erling Smørgrav     if ((se = getservbyname(SCHEME_FTP, "tcp")) != NULL)
69810851dc4SDag-Erling Smørgrav 	return ntohs(se->s_port);
69910851dc4SDag-Erling Smørgrav     return FTP_DEFAULT_PORT;
70010851dc4SDag-Erling Smørgrav }
70110851dc4SDag-Erling Smørgrav 
70210851dc4SDag-Erling Smørgrav /*
7038e3986eaSDag-Erling Smørgrav  * Log on to FTP server
7044ca1ab94SDag-Erling Smørgrav  */
705fc6e9e65SDag-Erling Smørgrav static int
7061a16ed4cSDag-Erling Smørgrav _ftp_connect(struct url *url, struct url *purl, char *flags)
7078e3986eaSDag-Erling Smørgrav {
7081a16ed4cSDag-Erling Smørgrav     int cd, e, direct, verbose;
70928c645cfSHajimu UMEMOTO #ifdef INET6
71028c645cfSHajimu UMEMOTO     int af = AF_UNSPEC;
71128c645cfSHajimu UMEMOTO #else
71228c645cfSHajimu UMEMOTO     int af = AF_INET;
71328c645cfSHajimu UMEMOTO #endif
7144ab587f4SHajimu UMEMOTO     const char *logname;
7151a16ed4cSDag-Erling Smørgrav     char *user, *pwd;
7164ab587f4SHajimu UMEMOTO     char localhost[MAXHOSTNAMELEN];
7174ab587f4SHajimu UMEMOTO     char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1];
7188e3986eaSDag-Erling Smørgrav 
719f5f109a0SDag-Erling Smørgrav     direct = (flags && strchr(flags, 'd'));
720f5f109a0SDag-Erling Smørgrav     verbose = (flags && strchr(flags, 'v'));
72128c645cfSHajimu UMEMOTO     if ((flags && strchr(flags, '4')))
72228c645cfSHajimu UMEMOTO 	af = AF_INET;
72328c645cfSHajimu UMEMOTO     else if ((flags && strchr(flags, '6')))
72428c645cfSHajimu UMEMOTO 	af = AF_INET6;
725f5f109a0SDag-Erling Smørgrav 
7261a16ed4cSDag-Erling Smørgrav     if (direct)
7271a16ed4cSDag-Erling Smørgrav 	purl = NULL;
72828c645cfSHajimu UMEMOTO 
7291a16ed4cSDag-Erling Smørgrav     /* check for proxy */
7301a16ed4cSDag-Erling Smørgrav     if (purl) {
7311a16ed4cSDag-Erling Smørgrav 	/* XXX proxy authentication! */
7321a16ed4cSDag-Erling Smørgrav 	cd = _fetch_connect(purl->host, purl->port, af, verbose);
733f62e5228SDag-Erling Smørgrav     } else {
734f62e5228SDag-Erling Smørgrav 	/* no proxy, go straight to target */
7351a16ed4cSDag-Erling Smørgrav 	cd = _fetch_connect(url->host, url->port, af, verbose);
7361a16ed4cSDag-Erling Smørgrav 	purl = NULL;
737f62e5228SDag-Erling Smørgrav     }
738f62e5228SDag-Erling Smørgrav 
739f62e5228SDag-Erling Smørgrav     /* check connection */
740fc6e9e65SDag-Erling Smørgrav     if (cd == -1) {
741842a95ccSDag-Erling Smørgrav 	_fetch_syserr();
7428e3986eaSDag-Erling Smørgrav 	return NULL;
7438e3986eaSDag-Erling Smørgrav     }
744f62e5228SDag-Erling Smørgrav 
7458e3986eaSDag-Erling Smørgrav     /* expect welcome message */
746fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY)
7478e3986eaSDag-Erling Smørgrav 	goto fouch;
7488e3986eaSDag-Erling Smørgrav 
7491a16ed4cSDag-Erling Smørgrav     /* XXX FTP_AUTH, and maybe .netrc */
7501a16ed4cSDag-Erling Smørgrav 
7518e3986eaSDag-Erling Smørgrav     /* send user name and password */
7521a16ed4cSDag-Erling Smørgrav     user = url->user;
753f62e5228SDag-Erling Smørgrav     if (!user || !*user)
754f62e5228SDag-Erling Smørgrav 	user = FTP_ANONYMOUS_USER;
7551a16ed4cSDag-Erling Smørgrav     if (purl && url->port == FTP_DEFAULT_PORT)
756cb5b7353SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "USER %s@%s", user, url->host);
7571a16ed4cSDag-Erling Smørgrav     else if (purl)
758cb5b7353SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "USER %s@%s@%d", user, url->host, url->port);
7592e88106bSDag-Erling Smørgrav     else
760cb5b7353SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "USER %s", user);
761f62e5228SDag-Erling Smørgrav 
762f62e5228SDag-Erling Smørgrav     /* did the server request a password? */
763f62e5228SDag-Erling Smørgrav     if (e == FTP_NEED_PASSWORD) {
7641a16ed4cSDag-Erling Smørgrav 	pwd = url->pwd;
765f62e5228SDag-Erling Smørgrav 	if (!pwd || !*pwd)
7664ab587f4SHajimu UMEMOTO 	    pwd = getenv("FTP_PASSWORD");
7674ab587f4SHajimu UMEMOTO 	if (!pwd || !*pwd) {
7684ab587f4SHajimu UMEMOTO 	    if ((logname = getlogin()) == 0)
7694ab587f4SHajimu UMEMOTO 		logname = FTP_ANONYMOUS_PASSWORD;
7704ab587f4SHajimu UMEMOTO 	    gethostname(localhost, sizeof localhost);
7714ab587f4SHajimu UMEMOTO 	    snprintf(pbuf, sizeof pbuf, "%s@%s", logname, localhost);
7724ab587f4SHajimu UMEMOTO 	    pwd = pbuf;
7734ab587f4SHajimu UMEMOTO 	}
774fc6e9e65SDag-Erling Smørgrav 	e = _ftp_cmd(cd, "PASS %s", pwd);
775f62e5228SDag-Erling Smørgrav     }
776f62e5228SDag-Erling Smørgrav 
777f62e5228SDag-Erling Smørgrav     /* did the server request an account? */
7785aea254fSDag-Erling Smørgrav     if (e == FTP_NEED_ACCOUNT)
7793b7a6740SDag-Erling Smørgrav 	goto fouch;
780f62e5228SDag-Erling Smørgrav 
781f62e5228SDag-Erling Smørgrav     /* we should be done by now */
7825aea254fSDag-Erling Smørgrav     if (e != FTP_LOGGED_IN)
7838e3986eaSDag-Erling Smørgrav 	goto fouch;
7848e3986eaSDag-Erling Smørgrav 
7858e3986eaSDag-Erling Smørgrav     /* might as well select mode and type at once */
7868e3986eaSDag-Erling Smørgrav #ifdef FTP_FORCE_STREAM_MODE
787fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */
7885aea254fSDag-Erling Smørgrav 	goto fouch;
7898e3986eaSDag-Erling Smørgrav #endif
790fc6e9e65SDag-Erling Smørgrav     if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */
7915aea254fSDag-Erling Smørgrav 	goto fouch;
7928e3986eaSDag-Erling Smørgrav 
7938e3986eaSDag-Erling Smørgrav     /* done */
794fc6e9e65SDag-Erling Smørgrav     return cd;
7958e3986eaSDag-Erling Smørgrav 
7968e3986eaSDag-Erling Smørgrav fouch:
797fc6e9e65SDag-Erling Smørgrav     if (e != -1)
7985aea254fSDag-Erling Smørgrav 	_ftp_seterr(e);
799fc6e9e65SDag-Erling Smørgrav     close(cd);
8008e3986eaSDag-Erling Smørgrav     return NULL;
8018e3986eaSDag-Erling Smørgrav }
8028e3986eaSDag-Erling Smørgrav 
8038e3986eaSDag-Erling Smørgrav /*
8048e3986eaSDag-Erling Smørgrav  * Disconnect from server
8058e3986eaSDag-Erling Smørgrav  */
8068e3986eaSDag-Erling Smørgrav static void
807fc6e9e65SDag-Erling Smørgrav _ftp_disconnect(int cd)
8088e3986eaSDag-Erling Smørgrav {
809fc6e9e65SDag-Erling Smørgrav     (void)_ftp_cmd(cd, "QUIT");
810fc6e9e65SDag-Erling Smørgrav     close(cd);
8118e3986eaSDag-Erling Smørgrav }
8128e3986eaSDag-Erling Smørgrav 
8138e3986eaSDag-Erling Smørgrav /*
8148e3986eaSDag-Erling Smørgrav  * Check if we're already connected
8158e3986eaSDag-Erling Smørgrav  */
8168e3986eaSDag-Erling Smørgrav static int
817d8acd8dcSDag-Erling Smørgrav _ftp_isconnected(struct url *url)
8188e3986eaSDag-Erling Smørgrav {
8198e3986eaSDag-Erling Smørgrav     return (cached_socket
8208e3986eaSDag-Erling Smørgrav 	    && (strcmp(url->host, cached_host.host) == 0)
8218e3986eaSDag-Erling Smørgrav 	    && (strcmp(url->user, cached_host.user) == 0)
8228e3986eaSDag-Erling Smørgrav 	    && (strcmp(url->pwd, cached_host.pwd) == 0)
8238e3986eaSDag-Erling Smørgrav 	    && (url->port == cached_host.port));
8248e3986eaSDag-Erling Smørgrav }
8258e3986eaSDag-Erling Smørgrav 
826f62e5228SDag-Erling Smørgrav /*
8275aea254fSDag-Erling Smørgrav  * Check the cache, reconnect if no luck
828f62e5228SDag-Erling Smørgrav  */
829fc6e9e65SDag-Erling Smørgrav static int
8301a16ed4cSDag-Erling Smørgrav _ftp_cached_connect(struct url *url, struct url *purl, char *flags)
8314ca1ab94SDag-Erling Smørgrav {
832fc6e9e65SDag-Erling Smørgrav     int e, cd;
8335aea254fSDag-Erling Smørgrav 
834fc6e9e65SDag-Erling Smørgrav     cd = -1;
8358e3986eaSDag-Erling Smørgrav 
8368e3986eaSDag-Erling Smørgrav     /* set default port */
83710851dc4SDag-Erling Smørgrav     if (!url->port)
83810851dc4SDag-Erling Smørgrav 	url->port = _ftp_default_port();
8398e3986eaSDag-Erling Smørgrav 
8403b7a6740SDag-Erling Smørgrav     /* try to use previously cached connection */
841fc6e9e65SDag-Erling Smørgrav     if (_ftp_isconnected(url)) {
842fc6e9e65SDag-Erling Smørgrav 	e = _ftp_cmd(cached_socket, "NOOP");
843fc6e9e65SDag-Erling Smørgrav 	if (e == FTP_OK || e == FTP_SYNTAX_ERROR)
8441a16ed4cSDag-Erling Smørgrav 	    return cached_socket;
845fc6e9e65SDag-Erling Smørgrav     }
8464ca1ab94SDag-Erling Smørgrav 
8478e3986eaSDag-Erling Smørgrav     /* connect to server */
8481a16ed4cSDag-Erling Smørgrav     if ((cd = _ftp_connect(url, purl, flags)) == -1)
849fc6e9e65SDag-Erling Smørgrav 	return -1;
8508e3986eaSDag-Erling Smørgrav     if (cached_socket)
8518e3986eaSDag-Erling Smørgrav 	_ftp_disconnect(cached_socket);
852fc6e9e65SDag-Erling Smørgrav     cached_socket = cd;
85332425dafSDag-Erling Smørgrav     memcpy(&cached_host, url, sizeof *url);
854fc6e9e65SDag-Erling Smørgrav     return cd;
8558e3986eaSDag-Erling Smørgrav }
8568e3986eaSDag-Erling Smørgrav 
8578e3986eaSDag-Erling Smørgrav /*
8581a16ed4cSDag-Erling Smørgrav  * Check the proxy settings
859dfe7c55fSDag-Erling Smørgrav  */
8601a16ed4cSDag-Erling Smørgrav static struct url *
8611a16ed4cSDag-Erling Smørgrav _ftp_get_proxy(void)
862dfe7c55fSDag-Erling Smørgrav {
8631a16ed4cSDag-Erling Smørgrav     struct url *purl;
864dfe7c55fSDag-Erling Smørgrav     char *p;
865dfe7c55fSDag-Erling Smørgrav 
8661a16ed4cSDag-Erling Smørgrav     if (((p = getenv("FTP_PROXY")) || (p = getenv("HTTP_PROXY"))) &&
8671a16ed4cSDag-Erling Smørgrav 	*p && (purl = fetchParseURL(p)) != NULL) {
8681a16ed4cSDag-Erling Smørgrav 	if (!*purl->scheme)
8691a16ed4cSDag-Erling Smørgrav 	    strcpy(purl->scheme, SCHEME_FTP);
8701a16ed4cSDag-Erling Smørgrav 	if (!purl->port)
8711a16ed4cSDag-Erling Smørgrav 	    purl->port = _ftp_default_port();
8721a16ed4cSDag-Erling Smørgrav 	if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 ||
8731a16ed4cSDag-Erling Smørgrav 	    strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
8741a16ed4cSDag-Erling Smørgrav 	    return purl;
8751a16ed4cSDag-Erling Smørgrav 	fetchFreeURL(purl);
8761a16ed4cSDag-Erling Smørgrav     }
8771a16ed4cSDag-Erling Smørgrav     return NULL;
878dfe7c55fSDag-Erling Smørgrav }
879dfe7c55fSDag-Erling Smørgrav 
880dfe7c55fSDag-Erling Smørgrav /*
8811a5faa10SDag-Erling Smørgrav  * Get and stat file
8828e3986eaSDag-Erling Smørgrav  */
8834ca1ab94SDag-Erling Smørgrav FILE *
8841a5faa10SDag-Erling Smørgrav fetchXGetFTP(struct url *url, struct url_stat *us, char *flags)
885f62e5228SDag-Erling Smørgrav {
8861a16ed4cSDag-Erling Smørgrav     struct url *purl;
887fc6e9e65SDag-Erling Smørgrav     int cd;
8885aea254fSDag-Erling Smørgrav 
8891a16ed4cSDag-Erling Smørgrav     /* get the proxy URL, and check if we should use HTTP instead */
8901a16ed4cSDag-Erling Smørgrav     if (!strchr(flags, 'd') && (purl = _ftp_get_proxy()) != NULL) {
8911a16ed4cSDag-Erling Smørgrav 	if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
8921a16ed4cSDag-Erling Smørgrav 	    return _http_request(url, "GET", us, purl, flags);
8931a16ed4cSDag-Erling Smørgrav     } else {
8941a16ed4cSDag-Erling Smørgrav 	purl = NULL;
8951a16ed4cSDag-Erling Smørgrav     }
896dfe7c55fSDag-Erling Smørgrav 
8975aea254fSDag-Erling Smørgrav     /* connect to server */
8981a16ed4cSDag-Erling Smørgrav     cd = _ftp_cached_connect(url, purl, flags);
8991a16ed4cSDag-Erling Smørgrav     if (purl)
9001a16ed4cSDag-Erling Smørgrav 	fetchFreeURL(purl);
9011a16ed4cSDag-Erling Smørgrav     if (cd == NULL)
9025aea254fSDag-Erling Smørgrav 	return NULL;
9035aea254fSDag-Erling Smørgrav 
9041a5faa10SDag-Erling Smørgrav     /* change directory */
9051a5faa10SDag-Erling Smørgrav     if (_ftp_cwd(cd, url->doc) == -1)
9061a5faa10SDag-Erling Smørgrav 	return NULL;
9071a5faa10SDag-Erling Smørgrav 
9081a5faa10SDag-Erling Smørgrav     /* stat file */
909269532d9SDag-Erling Smørgrav     if (us && _ftp_stat(cd, url->doc, us) == -1
9100f27c783SDag-Erling Smørgrav 	&& fetchLastErrCode != FETCH_PROTO
911269532d9SDag-Erling Smørgrav 	&& fetchLastErrCode != FETCH_UNAVAIL)
9121a5faa10SDag-Erling Smørgrav 	return NULL;
9131a5faa10SDag-Erling Smørgrav 
9145aea254fSDag-Erling Smørgrav     /* initiate the transfer */
915c7d40ef2SDag-Erling Smørgrav     return _ftp_transfer(cd, "RETR", url->doc, O_RDONLY, url->offset, flags);
916f62e5228SDag-Erling Smørgrav }
917f62e5228SDag-Erling Smørgrav 
9185aea254fSDag-Erling Smørgrav /*
9191a5faa10SDag-Erling Smørgrav  * Get file
9201a5faa10SDag-Erling Smørgrav  */
9211a5faa10SDag-Erling Smørgrav FILE *
9221a5faa10SDag-Erling Smørgrav fetchGetFTP(struct url *url, char *flags)
9231a5faa10SDag-Erling Smørgrav {
9241a5faa10SDag-Erling Smørgrav     return fetchXGetFTP(url, NULL, flags);
9251a5faa10SDag-Erling Smørgrav }
9261a5faa10SDag-Erling Smørgrav 
9271a5faa10SDag-Erling Smørgrav /*
9285aea254fSDag-Erling Smørgrav  * Put file
9295aea254fSDag-Erling Smørgrav  */
930f62e5228SDag-Erling Smørgrav FILE *
931d8acd8dcSDag-Erling Smørgrav fetchPutFTP(struct url *url, char *flags)
9324ca1ab94SDag-Erling Smørgrav {
9331a16ed4cSDag-Erling Smørgrav     struct url *purl;
934fc6e9e65SDag-Erling Smørgrav     int cd;
9355aea254fSDag-Erling Smørgrav 
9361a16ed4cSDag-Erling Smørgrav     /* get the proxy URL, and check if we should use HTTP instead */
9371a16ed4cSDag-Erling Smørgrav     if (!strchr(flags, 'd') && (purl = _ftp_get_proxy()) != NULL) {
9381a16ed4cSDag-Erling Smørgrav 	if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
9391a16ed4cSDag-Erling Smørgrav 	    /* XXX HTTP PUT is not implemented, so try without the proxy */
9401a16ed4cSDag-Erling Smørgrav 	    purl = NULL;
9411a16ed4cSDag-Erling Smørgrav     } else {
9421a16ed4cSDag-Erling Smørgrav 	purl = NULL;
9431a16ed4cSDag-Erling Smørgrav     }
944dfe7c55fSDag-Erling Smørgrav 
9455aea254fSDag-Erling Smørgrav     /* connect to server */
9461a16ed4cSDag-Erling Smørgrav     cd = _ftp_cached_connect(url, purl, flags);
9471a16ed4cSDag-Erling Smørgrav     if (purl)
9481a16ed4cSDag-Erling Smørgrav 	fetchFreeURL(purl);
9491a16ed4cSDag-Erling Smørgrav     if (cd == NULL)
9505aea254fSDag-Erling Smørgrav 	return NULL;
9515aea254fSDag-Erling Smørgrav 
9521a5faa10SDag-Erling Smørgrav     /* change directory */
9531a5faa10SDag-Erling Smørgrav     if (_ftp_cwd(cd, url->doc) == -1)
9541a5faa10SDag-Erling Smørgrav 	return NULL;
9551a5faa10SDag-Erling Smørgrav 
9565aea254fSDag-Erling Smørgrav     /* initiate the transfer */
957fc6e9e65SDag-Erling Smørgrav     return _ftp_transfer(cd, (flags && strchr(flags, 'a')) ? "APPE" : "STOR",
958c7d40ef2SDag-Erling Smørgrav 			 url->doc, O_WRONLY, url->offset, flags);
9598e3986eaSDag-Erling Smørgrav }
960d8acd8dcSDag-Erling Smørgrav 
9615aea254fSDag-Erling Smørgrav /*
9625aea254fSDag-Erling Smørgrav  * Get file stats
9635aea254fSDag-Erling Smørgrav  */
964d8acd8dcSDag-Erling Smørgrav int
965d8acd8dcSDag-Erling Smørgrav fetchStatFTP(struct url *url, struct url_stat *us, char *flags)
966d8acd8dcSDag-Erling Smørgrav {
9671a16ed4cSDag-Erling Smørgrav     struct url *purl;
9681a5faa10SDag-Erling Smørgrav     int cd;
9695aea254fSDag-Erling Smørgrav 
9701a16ed4cSDag-Erling Smørgrav     /* get the proxy URL, and check if we should use HTTP instead */
9711a16ed4cSDag-Erling Smørgrav     if (!strchr(flags, 'd') && (purl = _ftp_get_proxy()) != NULL) {
9721a16ed4cSDag-Erling Smørgrav 	if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0) {
9731a16ed4cSDag-Erling Smørgrav 	    FILE *f;
9741a16ed4cSDag-Erling Smørgrav 
9751a16ed4cSDag-Erling Smørgrav 	    if ((f = _http_request(url, "HEAD", us, purl, flags)) == NULL)
9761a16ed4cSDag-Erling Smørgrav 		return -1;
9771a16ed4cSDag-Erling Smørgrav 	    fclose(f);
9781a16ed4cSDag-Erling Smørgrav 	    return 0;
9791a16ed4cSDag-Erling Smørgrav 	}
9801a16ed4cSDag-Erling Smørgrav     } else {
9811a16ed4cSDag-Erling Smørgrav 	purl = NULL;
9821a16ed4cSDag-Erling Smørgrav     }
983dfe7c55fSDag-Erling Smørgrav 
9845aea254fSDag-Erling Smørgrav     /* connect to server */
9851a16ed4cSDag-Erling Smørgrav     cd = _ftp_cached_connect(url, purl, flags);
9861a16ed4cSDag-Erling Smørgrav     if (purl)
9871a16ed4cSDag-Erling Smørgrav 	fetchFreeURL(purl);
9881a16ed4cSDag-Erling Smørgrav     if (cd == NULL)
9891a16ed4cSDag-Erling Smørgrav 	return NULL;
9905aea254fSDag-Erling Smørgrav 
9915aea254fSDag-Erling Smørgrav     /* change directory */
9921a5faa10SDag-Erling Smørgrav     if (_ftp_cwd(cd, url->doc) == -1)
9935aea254fSDag-Erling Smørgrav 	return -1;
9945aea254fSDag-Erling Smørgrav 
9951a5faa10SDag-Erling Smørgrav     /* stat file */
9961a5faa10SDag-Erling Smørgrav     return _ftp_stat(cd, url->doc, us);
997d8acd8dcSDag-Erling Smørgrav }
998ce71b736SDag-Erling Smørgrav 
999ce71b736SDag-Erling Smørgrav /*
1000ce71b736SDag-Erling Smørgrav  * List a directory
1001ce71b736SDag-Erling Smørgrav  */
1002ce71b736SDag-Erling Smørgrav extern void warnx(char *, ...);
1003ce71b736SDag-Erling Smørgrav struct url_ent *
1004ce71b736SDag-Erling Smørgrav fetchListFTP(struct url *url, char *flags)
1005ce71b736SDag-Erling Smørgrav {
1006def5f54cSDag-Erling Smørgrav     warnx("fetchListFTP(): not implemented");
1007ce71b736SDag-Erling Smørgrav     return NULL;
1008ce71b736SDag-Erling Smørgrav }
1009