14ca1ab94SDag-Erling Smørgrav /*- 24ca1ab94SDag-Erling Smørgrav * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav 34ca1ab94SDag-Erling Smørgrav * All rights reserved. 44ca1ab94SDag-Erling Smørgrav * 54ca1ab94SDag-Erling Smørgrav * Redistribution and use in source and binary forms, with or without 64ca1ab94SDag-Erling Smørgrav * modification, are permitted provided that the following conditions 74ca1ab94SDag-Erling Smørgrav * are met: 84ca1ab94SDag-Erling Smørgrav * 1. Redistributions of source code must retain the above copyright 94ca1ab94SDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer 104ca1ab94SDag-Erling Smørgrav * in this position and unchanged. 114ca1ab94SDag-Erling Smørgrav * 2. Redistributions in binary form must reproduce the above copyright 124ca1ab94SDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer in the 134ca1ab94SDag-Erling Smørgrav * documentation and/or other materials provided with the distribution. 144ca1ab94SDag-Erling Smørgrav * 3. The name of the author may not be used to endorse or promote products 154ca1ab94SDag-Erling Smørgrav * derived from this software without specific prior written permission 164ca1ab94SDag-Erling Smørgrav * 174ca1ab94SDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 184ca1ab94SDag-Erling Smørgrav * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 194ca1ab94SDag-Erling Smørgrav * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 204ca1ab94SDag-Erling Smørgrav * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 214ca1ab94SDag-Erling Smørgrav * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 224ca1ab94SDag-Erling Smørgrav * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 234ca1ab94SDag-Erling Smørgrav * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 244ca1ab94SDag-Erling Smørgrav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 254ca1ab94SDag-Erling Smørgrav * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 264ca1ab94SDag-Erling Smørgrav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 274ca1ab94SDag-Erling Smørgrav */ 284ca1ab94SDag-Erling Smørgrav 29cecb889fSMatthew Dillon #include <sys/cdefs.h> 30cecb889fSMatthew Dillon __FBSDID("$FreeBSD$"); 31cecb889fSMatthew Dillon 324ca1ab94SDag-Erling Smørgrav /* 338e3986eaSDag-Erling Smørgrav * Portions of this code were taken from or based on ftpio.c: 344ca1ab94SDag-Erling Smørgrav * 354ca1ab94SDag-Erling Smørgrav * ---------------------------------------------------------------------------- 364ca1ab94SDag-Erling Smørgrav * "THE BEER-WARE LICENSE" (Revision 42): 3706229ad2SPoul-Henning Kamp * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 384ca1ab94SDag-Erling Smørgrav * can do whatever you want with this stuff. If we meet some day, and you think 394ca1ab94SDag-Erling Smørgrav * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 404ca1ab94SDag-Erling Smørgrav * ---------------------------------------------------------------------------- 414ca1ab94SDag-Erling Smørgrav * 424ca1ab94SDag-Erling Smørgrav * Major Changelog: 434ca1ab94SDag-Erling Smørgrav * 444ca1ab94SDag-Erling Smørgrav * Dag-Erling Co�dan Sm�rgrav 454ca1ab94SDag-Erling Smørgrav * 9 Jun 1998 464ca1ab94SDag-Erling Smørgrav * 474ca1ab94SDag-Erling Smørgrav * Incorporated into libfetch 484ca1ab94SDag-Erling Smørgrav * 494ca1ab94SDag-Erling Smørgrav * Jordan K. Hubbard 504ca1ab94SDag-Erling Smørgrav * 17 Jan 1996 514ca1ab94SDag-Erling Smørgrav * 524ca1ab94SDag-Erling Smørgrav * Turned inside out. Now returns xfers as new file ids, not as a special 534ca1ab94SDag-Erling Smørgrav * `state' of FTP_t 544ca1ab94SDag-Erling Smørgrav * 554ca1ab94SDag-Erling Smørgrav * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ 564ca1ab94SDag-Erling Smørgrav * 574ca1ab94SDag-Erling Smørgrav */ 584ca1ab94SDag-Erling Smørgrav 590fba3a00SDag-Erling Smørgrav #include <sys/param.h> 604ca1ab94SDag-Erling Smørgrav #include <sys/socket.h> 614ca1ab94SDag-Erling Smørgrav #include <netinet/in.h> 624ca1ab94SDag-Erling Smørgrav 634ca1ab94SDag-Erling Smørgrav #include <ctype.h> 6438c7e4a6SArchie Cobbs #include <err.h> 65fc6e9e65SDag-Erling Smørgrav #include <errno.h> 66c7d40ef2SDag-Erling Smørgrav #include <fcntl.h> 6732425dafSDag-Erling Smørgrav #include <netdb.h> 68346298f0SDag-Erling Smørgrav #include <stdarg.h> 69dea29ca1SDag-Erling Smørgrav #include <stdint.h> 704ca1ab94SDag-Erling Smørgrav #include <stdio.h> 718e3986eaSDag-Erling Smørgrav #include <stdlib.h> 724ca1ab94SDag-Erling Smørgrav #include <string.h> 735aea254fSDag-Erling Smørgrav #include <time.h> 748e3986eaSDag-Erling Smørgrav #include <unistd.h> 754ca1ab94SDag-Erling Smørgrav 764ca1ab94SDag-Erling Smørgrav #include "fetch.h" 77842a95ccSDag-Erling Smørgrav #include "common.h" 780fba3a00SDag-Erling Smørgrav #include "ftperr.h" 794ca1ab94SDag-Erling Smørgrav 8080ed165eSDag-Erling Smørgrav #define FTP_ANONYMOUS_USER "anonymous" 81346298f0SDag-Erling Smørgrav 82ea014d85SDag-Erling Smørgrav #define FTP_CONNECTION_ALREADY_OPEN 125 83346298f0SDag-Erling Smørgrav #define FTP_OPEN_DATA_CONNECTION 150 84346298f0SDag-Erling Smørgrav #define FTP_OK 200 855aea254fSDag-Erling Smørgrav #define FTP_FILE_STATUS 213 863b7a6740SDag-Erling Smørgrav #define FTP_SERVICE_READY 220 8723109751SDag-Erling Smørgrav #define FTP_TRANSFER_COMPLETE 226 88346298f0SDag-Erling Smørgrav #define FTP_PASSIVE_MODE 227 8928c645cfSHajimu UMEMOTO #define FTP_LPASSIVE_MODE 228 9028c645cfSHajimu UMEMOTO #define FTP_EPASSIVE_MODE 229 91346298f0SDag-Erling Smørgrav #define FTP_LOGGED_IN 230 92346298f0SDag-Erling Smørgrav #define FTP_FILE_ACTION_OK 250 93346298f0SDag-Erling Smørgrav #define FTP_NEED_PASSWORD 331 94346298f0SDag-Erling Smørgrav #define FTP_NEED_ACCOUNT 332 9532425dafSDag-Erling Smørgrav #define FTP_FILE_OK 350 96fc6e9e65SDag-Erling Smørgrav #define FTP_SYNTAX_ERROR 500 975cd33c40SDag-Erling Smørgrav #define FTP_PROTOCOL_ERROR 999 984ca1ab94SDag-Erling Smørgrav 99d8acd8dcSDag-Erling Smørgrav static struct url cached_host; 100dea29ca1SDag-Erling Smørgrav static conn_t *cached_connection; 101fc6e9e65SDag-Erling Smørgrav 102fc6e9e65SDag-Erling Smørgrav #define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 1036efb30c8SDag-Erling Smørgrav && isdigit(foo[2]) \ 1046efb30c8SDag-Erling Smørgrav && (foo[3] == ' ' || foo[3] == '\0')) 105fc6e9e65SDag-Erling Smørgrav #define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 106fc6e9e65SDag-Erling Smørgrav && isdigit(foo[2]) && foo[3] == '-') 1074ca1ab94SDag-Erling Smørgrav 108e19e6098SDag-Erling Smørgrav /* 109e19e6098SDag-Erling Smørgrav * Translate IPv4 mapped IPv6 address to IPv4 address 110e19e6098SDag-Erling Smørgrav */ 11128c645cfSHajimu UMEMOTO static void 11228c645cfSHajimu UMEMOTO unmappedaddr(struct sockaddr_in6 *sin6) 11328c645cfSHajimu UMEMOTO { 11428c645cfSHajimu UMEMOTO struct sockaddr_in *sin4; 11528c645cfSHajimu UMEMOTO u_int32_t addr; 11628c645cfSHajimu UMEMOTO int port; 11728c645cfSHajimu UMEMOTO 11828c645cfSHajimu UMEMOTO if (sin6->sin6_family != AF_INET6 || 11928c645cfSHajimu UMEMOTO !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 12028c645cfSHajimu UMEMOTO return; 12128c645cfSHajimu UMEMOTO sin4 = (struct sockaddr_in *)sin6; 12228c645cfSHajimu UMEMOTO addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12]; 12328c645cfSHajimu UMEMOTO port = sin6->sin6_port; 12428c645cfSHajimu UMEMOTO memset(sin4, 0, sizeof(struct sockaddr_in)); 12528c645cfSHajimu UMEMOTO sin4->sin_addr.s_addr = addr; 12628c645cfSHajimu UMEMOTO sin4->sin_port = port; 12728c645cfSHajimu UMEMOTO sin4->sin_family = AF_INET; 12828c645cfSHajimu UMEMOTO sin4->sin_len = sizeof(struct sockaddr_in); 12928c645cfSHajimu UMEMOTO } 13028c645cfSHajimu UMEMOTO 1314ca1ab94SDag-Erling Smørgrav /* 132fc6e9e65SDag-Erling Smørgrav * Get server response 1338e3986eaSDag-Erling Smørgrav */ 1348e3986eaSDag-Erling Smørgrav static int 135dea29ca1SDag-Erling Smørgrav _ftp_chkerr(conn_t *conn) 1368e3986eaSDag-Erling Smørgrav { 137dea29ca1SDag-Erling Smørgrav if (_fetch_getln(conn) == -1) { 138842a95ccSDag-Erling Smørgrav _fetch_syserr(); 139e19e6098SDag-Erling Smørgrav return (-1); 1408e3986eaSDag-Erling Smørgrav } 141dea29ca1SDag-Erling Smørgrav if (isftpinfo(conn->buf)) { 142dea29ca1SDag-Erling Smørgrav while (conn->buflen && !isftpreply(conn->buf)) { 143dea29ca1SDag-Erling Smørgrav if (_fetch_getln(conn) == -1) { 144eac7a1e0SDag-Erling Smørgrav _fetch_syserr(); 145e19e6098SDag-Erling Smørgrav return (-1); 146eac7a1e0SDag-Erling Smørgrav } 147eac7a1e0SDag-Erling Smørgrav } 148eac7a1e0SDag-Erling Smørgrav } 149346298f0SDag-Erling Smørgrav 150dea29ca1SDag-Erling Smørgrav while (conn->buflen && isspace(conn->buf[conn->buflen - 1])) 151dea29ca1SDag-Erling Smørgrav conn->buflen--; 152dea29ca1SDag-Erling Smørgrav conn->buf[conn->buflen] = '\0'; 153fc6e9e65SDag-Erling Smørgrav 154dea29ca1SDag-Erling Smørgrav if (!isftpreply(conn->buf)) { 1555cd33c40SDag-Erling Smørgrav _ftp_seterr(FTP_PROTOCOL_ERROR); 156e19e6098SDag-Erling Smørgrav return (-1); 1578e3986eaSDag-Erling Smørgrav } 1588e3986eaSDag-Erling Smørgrav 159dea29ca1SDag-Erling Smørgrav conn->err = (conn->buf[0] - '0') * 100 160dea29ca1SDag-Erling Smørgrav + (conn->buf[1] - '0') * 10 161dea29ca1SDag-Erling Smørgrav + (conn->buf[2] - '0'); 162fc6e9e65SDag-Erling Smørgrav 163dea29ca1SDag-Erling Smørgrav return (conn->err); 1648e3986eaSDag-Erling Smørgrav } 1658e3986eaSDag-Erling Smørgrav 1668e3986eaSDag-Erling Smørgrav /* 167346298f0SDag-Erling Smørgrav * Send a command and check reply 1684ca1ab94SDag-Erling Smørgrav */ 1694ca1ab94SDag-Erling Smørgrav static int 170dea29ca1SDag-Erling Smørgrav _ftp_cmd(conn_t *conn, const char *fmt, ...) 1714ca1ab94SDag-Erling Smørgrav { 172346298f0SDag-Erling Smørgrav va_list ap; 173e137bcebSDag-Erling Smørgrav size_t len; 174fc6e9e65SDag-Erling Smørgrav char *msg; 175fc6e9e65SDag-Erling Smørgrav int r; 1768e3986eaSDag-Erling Smørgrav 177346298f0SDag-Erling Smørgrav va_start(ap, fmt); 178e137bcebSDag-Erling Smørgrav len = vasprintf(&msg, fmt, ap); 179346298f0SDag-Erling Smørgrav va_end(ap); 180346298f0SDag-Erling Smørgrav 181fc6e9e65SDag-Erling Smørgrav if (msg == NULL) { 182fc6e9e65SDag-Erling Smørgrav errno = ENOMEM; 183fc6e9e65SDag-Erling Smørgrav _fetch_syserr(); 184e19e6098SDag-Erling Smørgrav return (-1); 185fc6e9e65SDag-Erling Smørgrav } 186e137bcebSDag-Erling Smørgrav 187dea29ca1SDag-Erling Smørgrav r = _fetch_putln(conn, msg, len); 188fc6e9e65SDag-Erling Smørgrav free(msg); 189e137bcebSDag-Erling Smørgrav 190fc6e9e65SDag-Erling Smørgrav if (r == -1) { 191fc6e9e65SDag-Erling Smørgrav _fetch_syserr(); 192e19e6098SDag-Erling Smørgrav return (-1); 193fc6e9e65SDag-Erling Smørgrav } 194fc6e9e65SDag-Erling Smørgrav 195dea29ca1SDag-Erling Smørgrav return (_ftp_chkerr(conn)); 1964ca1ab94SDag-Erling Smørgrav } 1974ca1ab94SDag-Erling Smørgrav 1984ca1ab94SDag-Erling Smørgrav /* 1991a5faa10SDag-Erling Smørgrav * Return a pointer to the filename part of a path 2001a5faa10SDag-Erling Smørgrav */ 20138c7e4a6SArchie Cobbs static const char * 20238c7e4a6SArchie Cobbs _ftp_filename(const char *file) 2031a5faa10SDag-Erling Smørgrav { 2041a5faa10SDag-Erling Smørgrav char *s; 2051a5faa10SDag-Erling Smørgrav 2061a5faa10SDag-Erling Smørgrav if ((s = strrchr(file, '/')) == NULL) 207e19e6098SDag-Erling Smørgrav return (file); 2081a5faa10SDag-Erling Smørgrav else 209e19e6098SDag-Erling Smørgrav return (s + 1); 2101a5faa10SDag-Erling Smørgrav } 2111a5faa10SDag-Erling Smørgrav 2121a5faa10SDag-Erling Smørgrav /* 213e19e6098SDag-Erling Smørgrav * Change working directory to the directory that contains the specified 214e19e6098SDag-Erling Smørgrav * file. 2151a5faa10SDag-Erling Smørgrav */ 2161a5faa10SDag-Erling Smørgrav static int 217dea29ca1SDag-Erling Smørgrav _ftp_cwd(conn_t *conn, const char *file) 2181a5faa10SDag-Erling Smørgrav { 2191a5faa10SDag-Erling Smørgrav char *s; 2201a5faa10SDag-Erling Smørgrav int e; 2211a5faa10SDag-Erling Smørgrav 2225e3f46b5SDag-Erling Smørgrav if ((s = strrchr(file, '/')) == NULL || s == file) { 223dea29ca1SDag-Erling Smørgrav e = _ftp_cmd(conn, "CWD /"); 2241a5faa10SDag-Erling Smørgrav } else { 225dea29ca1SDag-Erling Smørgrav e = _ftp_cmd(conn, "CWD %.*s", s - file, file); 2261a5faa10SDag-Erling Smørgrav } 2271a5faa10SDag-Erling Smørgrav if (e != FTP_FILE_ACTION_OK) { 2281a5faa10SDag-Erling Smørgrav _ftp_seterr(e); 229e19e6098SDag-Erling Smørgrav return (-1); 2301a5faa10SDag-Erling Smørgrav } 231e19e6098SDag-Erling Smørgrav return (0); 2321a5faa10SDag-Erling Smørgrav } 2331a5faa10SDag-Erling Smørgrav 2341a5faa10SDag-Erling Smørgrav /* 2351a5faa10SDag-Erling Smørgrav * Request and parse file stats 2361a5faa10SDag-Erling Smørgrav */ 2371a5faa10SDag-Erling Smørgrav static int 238dea29ca1SDag-Erling Smørgrav _ftp_stat(conn_t *conn, const char *file, struct url_stat *us) 2391a5faa10SDag-Erling Smørgrav { 24038c7e4a6SArchie Cobbs char *ln; 24138c7e4a6SArchie Cobbs const char *s; 2421a5faa10SDag-Erling Smørgrav struct tm tm; 2431a5faa10SDag-Erling Smørgrav time_t t; 2441a5faa10SDag-Erling Smørgrav int e; 2451a5faa10SDag-Erling Smørgrav 246269532d9SDag-Erling Smørgrav us->size = -1; 247269532d9SDag-Erling Smørgrav us->atime = us->mtime = 0; 248269532d9SDag-Erling Smørgrav 2491a5faa10SDag-Erling Smørgrav if ((s = strrchr(file, '/')) == NULL) 2501a5faa10SDag-Erling Smørgrav s = file; 2511a5faa10SDag-Erling Smørgrav else 2521a5faa10SDag-Erling Smørgrav ++s; 2531a5faa10SDag-Erling Smørgrav 254dea29ca1SDag-Erling Smørgrav if ((e = _ftp_cmd(conn, "SIZE %s", s)) != FTP_FILE_STATUS) { 2551a5faa10SDag-Erling Smørgrav _ftp_seterr(e); 256e19e6098SDag-Erling Smørgrav return (-1); 2571a5faa10SDag-Erling Smørgrav } 258dea29ca1SDag-Erling Smørgrav for (ln = conn->buf + 4; *ln && isspace(*ln); ln++) 2591a5faa10SDag-Erling Smørgrav /* nothing */ ; 2601a5faa10SDag-Erling Smørgrav for (us->size = 0; *ln && isdigit(*ln); ln++) 2611a5faa10SDag-Erling Smørgrav us->size = us->size * 10 + *ln - '0'; 2621a5faa10SDag-Erling Smørgrav if (*ln && !isspace(*ln)) { 2631a5faa10SDag-Erling Smørgrav _ftp_seterr(FTP_PROTOCOL_ERROR); 264525be862SDag-Erling Smørgrav us->size = -1; 265e19e6098SDag-Erling Smørgrav return (-1); 2661a5faa10SDag-Erling Smørgrav } 26763428824SDag-Erling Smørgrav if (us->size == 0) 26863428824SDag-Erling Smørgrav us->size = -1; 269f67efa37SDag-Erling Smørgrav DEBUG(fprintf(stderr, "size: [%lld]\n", (long long)us->size)); 2701a5faa10SDag-Erling Smørgrav 271dea29ca1SDag-Erling Smørgrav if ((e = _ftp_cmd(conn, "MDTM %s", s)) != FTP_FILE_STATUS) { 2721a5faa10SDag-Erling Smørgrav _ftp_seterr(e); 273e19e6098SDag-Erling Smørgrav return (-1); 2741a5faa10SDag-Erling Smørgrav } 275dea29ca1SDag-Erling Smørgrav for (ln = conn->buf + 4; *ln && isspace(*ln); ln++) 2761a5faa10SDag-Erling Smørgrav /* nothing */ ; 2771a5faa10SDag-Erling Smørgrav switch (strspn(ln, "0123456789")) { 2781a5faa10SDag-Erling Smørgrav case 14: 2791a5faa10SDag-Erling Smørgrav break; 2801a5faa10SDag-Erling Smørgrav case 15: 2811a5faa10SDag-Erling Smørgrav ln++; 2821a5faa10SDag-Erling Smørgrav ln[0] = '2'; 2831a5faa10SDag-Erling Smørgrav ln[1] = '0'; 2841a5faa10SDag-Erling Smørgrav break; 2851a5faa10SDag-Erling Smørgrav default: 2861a5faa10SDag-Erling Smørgrav _ftp_seterr(FTP_PROTOCOL_ERROR); 287e19e6098SDag-Erling Smørgrav return (-1); 2881a5faa10SDag-Erling Smørgrav } 2891a5faa10SDag-Erling Smørgrav if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", 2901a5faa10SDag-Erling Smørgrav &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 2911a5faa10SDag-Erling Smørgrav &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { 2921a5faa10SDag-Erling Smørgrav _ftp_seterr(FTP_PROTOCOL_ERROR); 293e19e6098SDag-Erling Smørgrav return (-1); 2941a5faa10SDag-Erling Smørgrav } 2951a5faa10SDag-Erling Smørgrav tm.tm_mon--; 2961a5faa10SDag-Erling Smørgrav tm.tm_year -= 1900; 2971a5faa10SDag-Erling Smørgrav tm.tm_isdst = -1; 2981a5faa10SDag-Erling Smørgrav t = timegm(&tm); 2991a5faa10SDag-Erling Smørgrav if (t == (time_t)-1) 3001a5faa10SDag-Erling Smørgrav t = time(NULL); 3011a5faa10SDag-Erling Smørgrav us->mtime = t; 3021a5faa10SDag-Erling Smørgrav us->atime = t; 303e19e6098SDag-Erling Smørgrav DEBUG(fprintf(stderr, 304e19e6098SDag-Erling Smørgrav "last modified: [%04d-%02d-%02d %02d:%02d:%02d]\n", 3051a5faa10SDag-Erling Smørgrav tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 3061a5faa10SDag-Erling Smørgrav tm.tm_hour, tm.tm_min, tm.tm_sec)); 307e19e6098SDag-Erling Smørgrav return (0); 3081a5faa10SDag-Erling Smørgrav } 3091a5faa10SDag-Erling Smørgrav 3101a5faa10SDag-Erling Smørgrav /* 311c7d40ef2SDag-Erling Smørgrav * I/O functions for FTP 312c7d40ef2SDag-Erling Smørgrav */ 313c7d40ef2SDag-Erling Smørgrav struct ftpio { 3149601e333SDag-Erling Smørgrav conn_t *cconn; /* Control connection */ 3159601e333SDag-Erling Smørgrav conn_t *dconn; /* Data connection */ 316c7d40ef2SDag-Erling Smørgrav int dir; /* Direction */ 317c7d40ef2SDag-Erling Smørgrav int eof; /* EOF reached */ 318c7d40ef2SDag-Erling Smørgrav int err; /* Error code */ 319c7d40ef2SDag-Erling Smørgrav }; 320c7d40ef2SDag-Erling Smørgrav 321c7d40ef2SDag-Erling Smørgrav static int _ftp_readfn(void *, char *, int); 322c7d40ef2SDag-Erling Smørgrav static int _ftp_writefn(void *, const char *, int); 323c7d40ef2SDag-Erling Smørgrav static fpos_t _ftp_seekfn(void *, fpos_t, int); 324c7d40ef2SDag-Erling Smørgrav static int _ftp_closefn(void *); 325c7d40ef2SDag-Erling Smørgrav 326c7d40ef2SDag-Erling Smørgrav static int 327c7d40ef2SDag-Erling Smørgrav _ftp_readfn(void *v, char *buf, int len) 328c7d40ef2SDag-Erling Smørgrav { 329c7d40ef2SDag-Erling Smørgrav struct ftpio *io; 330c7d40ef2SDag-Erling Smørgrav int r; 331c7d40ef2SDag-Erling Smørgrav 332c7d40ef2SDag-Erling Smørgrav io = (struct ftpio *)v; 33323109751SDag-Erling Smørgrav if (io == NULL) { 33423109751SDag-Erling Smørgrav errno = EBADF; 335e19e6098SDag-Erling Smørgrav return (-1); 33623109751SDag-Erling Smørgrav } 3379601e333SDag-Erling Smørgrav if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) { 338c7d40ef2SDag-Erling Smørgrav errno = EBADF; 339e19e6098SDag-Erling Smørgrav return (-1); 340c7d40ef2SDag-Erling Smørgrav } 341c7d40ef2SDag-Erling Smørgrav if (io->err) { 342c7d40ef2SDag-Erling Smørgrav errno = io->err; 343e19e6098SDag-Erling Smørgrav return (-1); 344c7d40ef2SDag-Erling Smørgrav } 345c7d40ef2SDag-Erling Smørgrav if (io->eof) 346e19e6098SDag-Erling Smørgrav return (0); 3479601e333SDag-Erling Smørgrav r = _fetch_read(io->dconn, buf, len); 348c7d40ef2SDag-Erling Smørgrav if (r > 0) 349e19e6098SDag-Erling Smørgrav return (r); 350c7d40ef2SDag-Erling Smørgrav if (r == 0) { 351c7d40ef2SDag-Erling Smørgrav io->eof = 1; 352e19e6098SDag-Erling Smørgrav return (0); 353c7d40ef2SDag-Erling Smørgrav } 354e238d2a8SDag-Erling Smørgrav if (errno != EINTR) 355c7d40ef2SDag-Erling Smørgrav io->err = errno; 356e19e6098SDag-Erling Smørgrav return (-1); 357c7d40ef2SDag-Erling Smørgrav } 358c7d40ef2SDag-Erling Smørgrav 359c7d40ef2SDag-Erling Smørgrav static int 360c7d40ef2SDag-Erling Smørgrav _ftp_writefn(void *v, const char *buf, int len) 361c7d40ef2SDag-Erling Smørgrav { 362c7d40ef2SDag-Erling Smørgrav struct ftpio *io; 363c7d40ef2SDag-Erling Smørgrav int w; 364c7d40ef2SDag-Erling Smørgrav 365c7d40ef2SDag-Erling Smørgrav io = (struct ftpio *)v; 36623109751SDag-Erling Smørgrav if (io == NULL) { 36723109751SDag-Erling Smørgrav errno = EBADF; 368e19e6098SDag-Erling Smørgrav return (-1); 36923109751SDag-Erling Smørgrav } 3709601e333SDag-Erling Smørgrav if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) { 371c7d40ef2SDag-Erling Smørgrav errno = EBADF; 372e19e6098SDag-Erling Smørgrav return (-1); 373c7d40ef2SDag-Erling Smørgrav } 374c7d40ef2SDag-Erling Smørgrav if (io->err) { 375c7d40ef2SDag-Erling Smørgrav errno = io->err; 376e19e6098SDag-Erling Smørgrav return (-1); 377c7d40ef2SDag-Erling Smørgrav } 3789601e333SDag-Erling Smørgrav w = _fetch_write(io->dconn, buf, len); 379c7d40ef2SDag-Erling Smørgrav if (w >= 0) 380e19e6098SDag-Erling Smørgrav return (w); 381e238d2a8SDag-Erling Smørgrav if (errno != EINTR) 382c7d40ef2SDag-Erling Smørgrav io->err = errno; 383e19e6098SDag-Erling Smørgrav return (-1); 384c7d40ef2SDag-Erling Smørgrav } 385c7d40ef2SDag-Erling Smørgrav 386c7d40ef2SDag-Erling Smørgrav static fpos_t 387f573a5fcSDag-Erling Smørgrav _ftp_seekfn(void *v, fpos_t pos __unused, int whence __unused) 388c7d40ef2SDag-Erling Smørgrav { 38923109751SDag-Erling Smørgrav struct ftpio *io; 39023109751SDag-Erling Smørgrav 39123109751SDag-Erling Smørgrav io = (struct ftpio *)v; 39223109751SDag-Erling Smørgrav if (io == NULL) { 39323109751SDag-Erling Smørgrav errno = EBADF; 394e19e6098SDag-Erling Smørgrav return (-1); 39523109751SDag-Erling Smørgrav } 396c7d40ef2SDag-Erling Smørgrav errno = ESPIPE; 397e19e6098SDag-Erling Smørgrav return (-1); 398c7d40ef2SDag-Erling Smørgrav } 399c7d40ef2SDag-Erling Smørgrav 400c7d40ef2SDag-Erling Smørgrav static int 401c7d40ef2SDag-Erling Smørgrav _ftp_closefn(void *v) 402c7d40ef2SDag-Erling Smørgrav { 403c7d40ef2SDag-Erling Smørgrav struct ftpio *io; 40423109751SDag-Erling Smørgrav int r; 405c7d40ef2SDag-Erling Smørgrav 406c7d40ef2SDag-Erling Smørgrav io = (struct ftpio *)v; 40723109751SDag-Erling Smørgrav if (io == NULL) { 40823109751SDag-Erling Smørgrav errno = EBADF; 409e19e6098SDag-Erling Smørgrav return (-1); 41023109751SDag-Erling Smørgrav } 411c7d40ef2SDag-Erling Smørgrav if (io->dir == -1) 412e19e6098SDag-Erling Smørgrav return (0); 4139601e333SDag-Erling Smørgrav if (io->cconn == NULL || io->dconn == NULL) { 414c7d40ef2SDag-Erling Smørgrav errno = EBADF; 415e19e6098SDag-Erling Smørgrav return (-1); 416c7d40ef2SDag-Erling Smørgrav } 4179601e333SDag-Erling Smørgrav _fetch_close(io->dconn); 418c7d40ef2SDag-Erling Smørgrav io->dir = -1; 4199601e333SDag-Erling Smørgrav io->dconn = NULL; 420303fd73aSDag-Erling Smørgrav DEBUG(fprintf(stderr, "Waiting for final status\n")); 4219601e333SDag-Erling Smørgrav r = _ftp_chkerr(io->cconn); 42213cc1c83SNate Lawson if (io->cconn == cached_connection && io->cconn->ref == 1) 42313cc1c83SNate Lawson cached_connection = NULL; 4249601e333SDag-Erling Smørgrav _fetch_close(io->cconn); 425b554dea7SDag-Erling Smørgrav free(io); 426b554dea7SDag-Erling Smørgrav return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1; 427c7d40ef2SDag-Erling Smørgrav } 428c7d40ef2SDag-Erling Smørgrav 429c7d40ef2SDag-Erling Smørgrav static FILE * 4309601e333SDag-Erling Smørgrav _ftp_setup(conn_t *cconn, conn_t *dconn, int mode) 431c7d40ef2SDag-Erling Smørgrav { 432c7d40ef2SDag-Erling Smørgrav struct ftpio *io; 433c7d40ef2SDag-Erling Smørgrav FILE *f; 434c7d40ef2SDag-Erling Smørgrav 4359601e333SDag-Erling Smørgrav if (cconn == NULL || dconn == NULL) 4369601e333SDag-Erling Smørgrav return (NULL); 437c7d40ef2SDag-Erling Smørgrav if ((io = malloc(sizeof *io)) == NULL) 438e19e6098SDag-Erling Smørgrav return (NULL); 4399601e333SDag-Erling Smørgrav io->cconn = cconn; 4409601e333SDag-Erling Smørgrav io->dconn = dconn; 441c7d40ef2SDag-Erling Smørgrav io->dir = mode; 442c7d40ef2SDag-Erling Smørgrav io->eof = io->err = 0; 443c7d40ef2SDag-Erling Smørgrav f = funopen(io, _ftp_readfn, _ftp_writefn, _ftp_seekfn, _ftp_closefn); 444c7d40ef2SDag-Erling Smørgrav if (f == NULL) 445c7d40ef2SDag-Erling Smørgrav free(io); 446e19e6098SDag-Erling Smørgrav return (f); 447c7d40ef2SDag-Erling Smørgrav } 448c7d40ef2SDag-Erling Smørgrav 449c7d40ef2SDag-Erling Smørgrav /* 450f62e5228SDag-Erling Smørgrav * Transfer file 4514ca1ab94SDag-Erling Smørgrav */ 4524ca1ab94SDag-Erling Smørgrav static FILE * 453dea29ca1SDag-Erling Smørgrav _ftp_transfer(conn_t *conn, const char *oper, const char *file, 45438c7e4a6SArchie Cobbs int mode, off_t offset, const char *flags) 4554ca1ab94SDag-Erling Smørgrav { 456f573a5fcSDag-Erling Smørgrav struct sockaddr_storage sa; 45728c645cfSHajimu UMEMOTO struct sockaddr_in6 *sin6; 45828c645cfSHajimu UMEMOTO struct sockaddr_in *sin4; 459d5f175ceSDag-Erling Smørgrav int low, pasv, verbose; 460f5f109a0SDag-Erling Smørgrav int e, sd = -1; 461f5f109a0SDag-Erling Smørgrav socklen_t l; 462346298f0SDag-Erling Smørgrav char *s; 463346298f0SDag-Erling Smørgrav FILE *df; 4648e3986eaSDag-Erling Smørgrav 465f5f109a0SDag-Erling Smørgrav /* check flags */ 466d5f175ceSDag-Erling Smørgrav low = CHECK_FLAG('l'); 467d74a913bSDag-Erling Smørgrav pasv = CHECK_FLAG('p'); 468d74a913bSDag-Erling Smørgrav verbose = CHECK_FLAG('v'); 469f5f109a0SDag-Erling Smørgrav 470d02e84a6SDag-Erling Smørgrav /* passive mode */ 4718b9ba466SDag-Erling Smørgrav if (!pasv) 4726c81eb52SDag-Erling Smørgrav pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL && 4738b9ba466SDag-Erling Smørgrav strncasecmp(s, "no", 2) != 0); 474d02e84a6SDag-Erling Smørgrav 47528c645cfSHajimu UMEMOTO /* find our own address, bind, and listen */ 476f573a5fcSDag-Erling Smørgrav l = sizeof sa; 477dea29ca1SDag-Erling Smørgrav if (getsockname(conn->sd, (struct sockaddr *)&sa, &l) == -1) 47828c645cfSHajimu UMEMOTO goto sysouch; 479f573a5fcSDag-Erling Smørgrav if (sa.ss_family == AF_INET6) 480f573a5fcSDag-Erling Smørgrav unmappedaddr((struct sockaddr_in6 *)&sa); 48128c645cfSHajimu UMEMOTO 482346298f0SDag-Erling Smørgrav /* open data socket */ 483f573a5fcSDag-Erling Smørgrav if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { 484842a95ccSDag-Erling Smørgrav _fetch_syserr(); 485e19e6098SDag-Erling Smørgrav return (NULL); 486346298f0SDag-Erling Smørgrav } 487346298f0SDag-Erling Smørgrav 488346298f0SDag-Erling Smørgrav if (pasv) { 48928c645cfSHajimu UMEMOTO u_char addr[64]; 490346298f0SDag-Erling Smørgrav char *ln, *p; 491f573a5fcSDag-Erling Smørgrav unsigned int i; 49228c645cfSHajimu UMEMOTO int port; 493346298f0SDag-Erling Smørgrav 494346298f0SDag-Erling Smørgrav /* send PASV command */ 495f5f109a0SDag-Erling Smørgrav if (verbose) 496f5f109a0SDag-Erling Smørgrav _fetch_info("setting passive mode"); 497f573a5fcSDag-Erling Smørgrav switch (sa.ss_family) { 49828c645cfSHajimu UMEMOTO case AF_INET: 499dea29ca1SDag-Erling Smørgrav if ((e = _ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE) 500346298f0SDag-Erling Smørgrav goto ouch; 50128c645cfSHajimu UMEMOTO break; 50228c645cfSHajimu UMEMOTO case AF_INET6: 503dea29ca1SDag-Erling Smørgrav if ((e = _ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) { 50428c645cfSHajimu UMEMOTO if (e == -1) 50528c645cfSHajimu UMEMOTO goto ouch; 506dea29ca1SDag-Erling Smørgrav if ((e = _ftp_cmd(conn, "LPSV")) != 507dea29ca1SDag-Erling Smørgrav FTP_LPASSIVE_MODE) 50828c645cfSHajimu UMEMOTO goto ouch; 50928c645cfSHajimu UMEMOTO } 51028c645cfSHajimu UMEMOTO break; 51128c645cfSHajimu UMEMOTO default: 5125cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 51328c645cfSHajimu UMEMOTO goto ouch; 51428c645cfSHajimu UMEMOTO } 515346298f0SDag-Erling Smørgrav 516f5f109a0SDag-Erling Smørgrav /* 517f5f109a0SDag-Erling Smørgrav * Find address and port number. The reply to the PASV command 518f5f109a0SDag-Erling Smørgrav * is IMHO the one and only weak point in the FTP protocol. 519f5f109a0SDag-Erling Smørgrav */ 520dea29ca1SDag-Erling Smørgrav ln = conn->buf; 521fa5dce6cSHajimu UMEMOTO switch (e) { 522fa5dce6cSHajimu UMEMOTO case FTP_PASSIVE_MODE: 523fa5dce6cSHajimu UMEMOTO case FTP_LPASSIVE_MODE: 52451e3d46eSDag-Erling Smørgrav for (p = ln + 3; *p && !isdigit(*p); p++) 525346298f0SDag-Erling Smørgrav /* nothing */ ; 52628c645cfSHajimu UMEMOTO if (!*p) { 5275cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; 5286efb30c8SDag-Erling Smørgrav goto ouch; 529346298f0SDag-Erling Smørgrav } 53028c645cfSHajimu UMEMOTO l = (e == FTP_PASSIVE_MODE ? 6 : 21); 53128c645cfSHajimu UMEMOTO for (i = 0; *p && i < l; i++, p++) 53228c645cfSHajimu UMEMOTO addr[i] = strtol(p, &p, 10); 53328c645cfSHajimu UMEMOTO if (i < l) { 5345cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; 53528c645cfSHajimu UMEMOTO goto ouch; 53628c645cfSHajimu UMEMOTO } 53728c645cfSHajimu UMEMOTO break; 53828c645cfSHajimu UMEMOTO case FTP_EPASSIVE_MODE: 539fa5dce6cSHajimu UMEMOTO for (p = ln + 3; *p && *p != '('; p++) 540fa5dce6cSHajimu UMEMOTO /* nothing */ ; 541fa5dce6cSHajimu UMEMOTO if (!*p) { 5425cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; 543fa5dce6cSHajimu UMEMOTO goto ouch; 544fa5dce6cSHajimu UMEMOTO } 545fa5dce6cSHajimu UMEMOTO ++p; 54628c645cfSHajimu UMEMOTO if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], 54728c645cfSHajimu UMEMOTO &port, &addr[3]) != 5 || 54828c645cfSHajimu UMEMOTO addr[0] != addr[1] || 54928c645cfSHajimu UMEMOTO addr[0] != addr[2] || addr[0] != addr[3]) { 5505cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; 55128c645cfSHajimu UMEMOTO goto ouch; 55228c645cfSHajimu UMEMOTO } 55328c645cfSHajimu UMEMOTO break; 55428c645cfSHajimu UMEMOTO } 555346298f0SDag-Erling Smørgrav 55632425dafSDag-Erling Smørgrav /* seek to required offset */ 55732425dafSDag-Erling Smørgrav if (offset) 558dea29ca1SDag-Erling Smørgrav if (_ftp_cmd(conn, "REST %lu", (u_long)offset) != FTP_FILE_OK) 55932425dafSDag-Erling Smørgrav goto sysouch; 56032425dafSDag-Erling Smørgrav 561346298f0SDag-Erling Smørgrav /* construct sockaddr for data socket */ 562f573a5fcSDag-Erling Smørgrav l = sizeof sa; 563dea29ca1SDag-Erling Smørgrav if (getpeername(conn->sd, (struct sockaddr *)&sa, &l) == -1) 564346298f0SDag-Erling Smørgrav goto sysouch; 565f573a5fcSDag-Erling Smørgrav if (sa.ss_family == AF_INET6) 566f573a5fcSDag-Erling Smørgrav unmappedaddr((struct sockaddr_in6 *)&sa); 567f573a5fcSDag-Erling Smørgrav switch (sa.ss_family) { 56828c645cfSHajimu UMEMOTO case AF_INET6: 569f573a5fcSDag-Erling Smørgrav sin6 = (struct sockaddr_in6 *)&sa; 57028c645cfSHajimu UMEMOTO if (e == FTP_EPASSIVE_MODE) 57128c645cfSHajimu UMEMOTO sin6->sin6_port = htons(port); 57228c645cfSHajimu UMEMOTO else { 57328c645cfSHajimu UMEMOTO bcopy(addr + 2, (char *)&sin6->sin6_addr, 16); 57428c645cfSHajimu UMEMOTO bcopy(addr + 19, (char *)&sin6->sin6_port, 2); 57528c645cfSHajimu UMEMOTO } 57628c645cfSHajimu UMEMOTO break; 57728c645cfSHajimu UMEMOTO case AF_INET: 578f573a5fcSDag-Erling Smørgrav sin4 = (struct sockaddr_in *)&sa; 57928c645cfSHajimu UMEMOTO if (e == FTP_EPASSIVE_MODE) 58028c645cfSHajimu UMEMOTO sin4->sin_port = htons(port); 58128c645cfSHajimu UMEMOTO else { 58228c645cfSHajimu UMEMOTO bcopy(addr, (char *)&sin4->sin_addr, 4); 58328c645cfSHajimu UMEMOTO bcopy(addr + 4, (char *)&sin4->sin_port, 2); 58428c645cfSHajimu UMEMOTO } 58528c645cfSHajimu UMEMOTO break; 58628c645cfSHajimu UMEMOTO default: 5875cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 58828c645cfSHajimu UMEMOTO break; 58928c645cfSHajimu UMEMOTO } 590346298f0SDag-Erling Smørgrav 591346298f0SDag-Erling Smørgrav /* connect to data port */ 592f5f109a0SDag-Erling Smørgrav if (verbose) 593f5f109a0SDag-Erling Smørgrav _fetch_info("opening data connection"); 594f573a5fcSDag-Erling Smørgrav if (connect(sd, (struct sockaddr *)&sa, sa.ss_len) == -1) 595346298f0SDag-Erling Smørgrav goto sysouch; 596346298f0SDag-Erling Smørgrav 597346298f0SDag-Erling Smørgrav /* make the server initiate the transfer */ 598f5f109a0SDag-Erling Smørgrav if (verbose) 599def5f54cSDag-Erling Smørgrav _fetch_info("initiating transfer"); 600dea29ca1SDag-Erling Smørgrav e = _ftp_cmd(conn, "%s %s", oper, _ftp_filename(file)); 601ea014d85SDag-Erling Smørgrav if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) 602346298f0SDag-Erling Smørgrav goto ouch; 603346298f0SDag-Erling Smørgrav 604346298f0SDag-Erling Smørgrav } else { 605346298f0SDag-Erling Smørgrav u_int32_t a; 606346298f0SDag-Erling Smørgrav u_short p; 607f5f109a0SDag-Erling Smørgrav int arg, d; 60828c645cfSHajimu UMEMOTO char *ap; 60928c645cfSHajimu UMEMOTO char hname[INET6_ADDRSTRLEN]; 610346298f0SDag-Erling Smørgrav 611f573a5fcSDag-Erling Smørgrav switch (sa.ss_family) { 61228c645cfSHajimu UMEMOTO case AF_INET6: 613f573a5fcSDag-Erling Smørgrav ((struct sockaddr_in6 *)&sa)->sin6_port = 0; 61428c645cfSHajimu UMEMOTO #ifdef IPV6_PORTRANGE 615d5f175ceSDag-Erling Smørgrav arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH; 61628c645cfSHajimu UMEMOTO if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, 61728c645cfSHajimu UMEMOTO (char *)&arg, sizeof(arg)) == -1) 618346298f0SDag-Erling Smørgrav goto sysouch; 61928c645cfSHajimu UMEMOTO #endif 62028c645cfSHajimu UMEMOTO break; 62128c645cfSHajimu UMEMOTO case AF_INET: 622f573a5fcSDag-Erling Smørgrav ((struct sockaddr_in *)&sa)->sin_port = 0; 623d5f175ceSDag-Erling Smørgrav arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH; 624f5f109a0SDag-Erling Smørgrav if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, 62532425dafSDag-Erling Smørgrav (char *)&arg, sizeof arg) == -1) 626f5f109a0SDag-Erling Smørgrav goto sysouch; 62728c645cfSHajimu UMEMOTO break; 62828c645cfSHajimu UMEMOTO } 629f5f109a0SDag-Erling Smørgrav if (verbose) 630f5f109a0SDag-Erling Smørgrav _fetch_info("binding data socket"); 631f573a5fcSDag-Erling Smørgrav if (bind(sd, (struct sockaddr *)&sa, sa.ss_len) == -1) 632346298f0SDag-Erling Smørgrav goto sysouch; 633ecc91352SDag-Erling Smørgrav if (listen(sd, 1) == -1) 634346298f0SDag-Erling Smørgrav goto sysouch; 635346298f0SDag-Erling Smørgrav 636346298f0SDag-Erling Smørgrav /* find what port we're on and tell the server */ 637f573a5fcSDag-Erling Smørgrav if (getsockname(sd, (struct sockaddr *)&sa, &l) == -1) 638346298f0SDag-Erling Smørgrav goto sysouch; 639f573a5fcSDag-Erling Smørgrav switch (sa.ss_family) { 64028c645cfSHajimu UMEMOTO case AF_INET: 641f573a5fcSDag-Erling Smørgrav sin4 = (struct sockaddr_in *)&sa; 64228c645cfSHajimu UMEMOTO a = ntohl(sin4->sin_addr.s_addr); 64328c645cfSHajimu UMEMOTO p = ntohs(sin4->sin_port); 644dea29ca1SDag-Erling Smørgrav e = _ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d", 6455aea254fSDag-Erling Smørgrav (a >> 24) & 0xff, (a >> 16) & 0xff, 6465aea254fSDag-Erling Smørgrav (a >> 8) & 0xff, a & 0xff, 6475aea254fSDag-Erling Smørgrav (p >> 8) & 0xff, p & 0xff); 64828c645cfSHajimu UMEMOTO break; 64928c645cfSHajimu UMEMOTO case AF_INET6: 65028c645cfSHajimu UMEMOTO #define UC(b) (((int)b)&0xff) 65128c645cfSHajimu UMEMOTO e = -1; 652f573a5fcSDag-Erling Smørgrav sin6 = (struct sockaddr_in6 *)&sa; 653f8fa093eSHajimu UMEMOTO sin6->sin6_scope_id = 0; 654f573a5fcSDag-Erling Smørgrav if (getnameinfo((struct sockaddr *)&sa, sa.ss_len, 65528c645cfSHajimu UMEMOTO hname, sizeof(hname), 65628c645cfSHajimu UMEMOTO NULL, 0, NI_NUMERICHOST) == 0) { 657dea29ca1SDag-Erling Smørgrav e = _ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname, 65828c645cfSHajimu UMEMOTO htons(sin6->sin6_port)); 65928c645cfSHajimu UMEMOTO if (e == -1) 66028c645cfSHajimu UMEMOTO goto ouch; 66128c645cfSHajimu UMEMOTO } 66228c645cfSHajimu UMEMOTO if (e != FTP_OK) { 66328c645cfSHajimu UMEMOTO ap = (char *)&sin6->sin6_addr; 664dea29ca1SDag-Erling Smørgrav e = _ftp_cmd(conn, 66528c645cfSHajimu UMEMOTO "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", 66628c645cfSHajimu UMEMOTO 6, 16, 66728c645cfSHajimu UMEMOTO UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]), 66828c645cfSHajimu UMEMOTO UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]), 66928c645cfSHajimu UMEMOTO UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]), 67028c645cfSHajimu UMEMOTO UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]), 67128c645cfSHajimu UMEMOTO 2, 67228c645cfSHajimu UMEMOTO (ntohs(sin6->sin6_port) >> 8) & 0xff, 67328c645cfSHajimu UMEMOTO ntohs(sin6->sin6_port) & 0xff); 67428c645cfSHajimu UMEMOTO } 67528c645cfSHajimu UMEMOTO break; 67628c645cfSHajimu UMEMOTO default: 6775cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 67828c645cfSHajimu UMEMOTO goto ouch; 67928c645cfSHajimu UMEMOTO } 6805aea254fSDag-Erling Smørgrav if (e != FTP_OK) 681346298f0SDag-Erling Smørgrav goto ouch; 682346298f0SDag-Erling Smørgrav 683893980adSDag-Erling Smørgrav /* seek to required offset */ 684893980adSDag-Erling Smørgrav if (offset) 685dea29ca1SDag-Erling Smørgrav if (_ftp_cmd(conn, "REST %ju", (uintmax_t)offset) != FTP_FILE_OK) 686893980adSDag-Erling Smørgrav goto sysouch; 687893980adSDag-Erling Smørgrav 688346298f0SDag-Erling Smørgrav /* make the server initiate the transfer */ 689f5f109a0SDag-Erling Smørgrav if (verbose) 690f5f109a0SDag-Erling Smørgrav _fetch_info("initiating transfer"); 691dea29ca1SDag-Erling Smørgrav e = _ftp_cmd(conn, "%s %s", oper, _ftp_filename(file)); 6925aea254fSDag-Erling Smørgrav if (e != FTP_OPEN_DATA_CONNECTION) 693346298f0SDag-Erling Smørgrav goto ouch; 694346298f0SDag-Erling Smørgrav 695346298f0SDag-Erling Smørgrav /* accept the incoming connection and go to town */ 696ecc91352SDag-Erling Smørgrav if ((d = accept(sd, NULL, NULL)) == -1) 697346298f0SDag-Erling Smørgrav goto sysouch; 698346298f0SDag-Erling Smørgrav close(sd); 699346298f0SDag-Erling Smørgrav sd = d; 700346298f0SDag-Erling Smørgrav } 701346298f0SDag-Erling Smørgrav 7029601e333SDag-Erling Smørgrav if ((df = _ftp_setup(conn, _fetch_reopen(sd), mode)) == NULL) 703346298f0SDag-Erling Smørgrav goto sysouch; 704e19e6098SDag-Erling Smørgrav return (df); 705346298f0SDag-Erling Smørgrav 706346298f0SDag-Erling Smørgrav sysouch: 707842a95ccSDag-Erling Smørgrav _fetch_syserr(); 70828c645cfSHajimu UMEMOTO if (sd >= 0) 7095aea254fSDag-Erling Smørgrav close(sd); 710e19e6098SDag-Erling Smørgrav return (NULL); 7115aea254fSDag-Erling Smørgrav 712346298f0SDag-Erling Smørgrav ouch: 713fc6e9e65SDag-Erling Smørgrav if (e != -1) 7145aea254fSDag-Erling Smørgrav _ftp_seterr(e); 71528c645cfSHajimu UMEMOTO if (sd >= 0) 716346298f0SDag-Erling Smørgrav close(sd); 717e19e6098SDag-Erling Smørgrav return (NULL); 7184ca1ab94SDag-Erling Smørgrav } 7194ca1ab94SDag-Erling Smørgrav 7208e3986eaSDag-Erling Smørgrav /* 7216490b215SDag-Erling Smørgrav * Authenticate 7226490b215SDag-Erling Smørgrav */ 7236490b215SDag-Erling Smørgrav static int 724dea29ca1SDag-Erling Smørgrav _ftp_authenticate(conn_t *conn, struct url *url, struct url *purl) 7256490b215SDag-Erling Smørgrav { 726f573a5fcSDag-Erling Smørgrav const char *user, *pwd, *logname; 7276490b215SDag-Erling Smørgrav char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1]; 7286490b215SDag-Erling Smørgrav int e, len; 7296490b215SDag-Erling Smørgrav 7306490b215SDag-Erling Smørgrav /* XXX FTP_AUTH, and maybe .netrc */ 7316490b215SDag-Erling Smørgrav 7326490b215SDag-Erling Smørgrav /* send user name and password */ 7339f808a4dSDag-Erling Smørgrav if (url->user[0] == '\0') 7349f808a4dSDag-Erling Smørgrav _fetch_netrc_auth(url); 7356490b215SDag-Erling Smørgrav user = url->user; 7369f808a4dSDag-Erling Smørgrav if (*user == '\0') 7376490b215SDag-Erling Smørgrav user = getenv("FTP_LOGIN"); 7389f808a4dSDag-Erling Smørgrav if (user == NULL || *user == '\0') 7396490b215SDag-Erling Smørgrav user = FTP_ANONYMOUS_USER; 7406490b215SDag-Erling Smørgrav if (purl && url->port == _fetch_default_port(url->scheme)) 741dea29ca1SDag-Erling Smørgrav e = _ftp_cmd(conn, "USER %s@%s", user, url->host); 7426490b215SDag-Erling Smørgrav else if (purl) 743dea29ca1SDag-Erling Smørgrav e = _ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port); 7446490b215SDag-Erling Smørgrav else 745dea29ca1SDag-Erling Smørgrav e = _ftp_cmd(conn, "USER %s", user); 7466490b215SDag-Erling Smørgrav 7476490b215SDag-Erling Smørgrav /* did the server request a password? */ 7486490b215SDag-Erling Smørgrav if (e == FTP_NEED_PASSWORD) { 7496490b215SDag-Erling Smørgrav pwd = url->pwd; 7509f808a4dSDag-Erling Smørgrav if (*pwd == '\0') 7516490b215SDag-Erling Smørgrav pwd = getenv("FTP_PASSWORD"); 7529f808a4dSDag-Erling Smørgrav if (pwd == NULL || *pwd == '\0') { 7536490b215SDag-Erling Smørgrav if ((logname = getlogin()) == 0) 7546490b215SDag-Erling Smørgrav logname = FTP_ANONYMOUS_USER; 755778de359SBrian Somers if ((len = snprintf(pbuf, MAXLOGNAME + 1, "%s@", logname)) < 0) 7562449bf28SBrian Somers len = 0; 7575f328905SBrian Somers else if (len > MAXLOGNAME) 7585f328905SBrian Somers len = MAXLOGNAME; 7596490b215SDag-Erling Smørgrav gethostname(pbuf + len, sizeof pbuf - len); 7606490b215SDag-Erling Smørgrav pwd = pbuf; 7616490b215SDag-Erling Smørgrav } 762dea29ca1SDag-Erling Smørgrav e = _ftp_cmd(conn, "PASS %s", pwd); 7636490b215SDag-Erling Smørgrav } 7646490b215SDag-Erling Smørgrav 765e19e6098SDag-Erling Smørgrav return (e); 7666490b215SDag-Erling Smørgrav } 7676490b215SDag-Erling Smørgrav 7686490b215SDag-Erling Smørgrav /* 7698e3986eaSDag-Erling Smørgrav * Log on to FTP server 7704ca1ab94SDag-Erling Smørgrav */ 771dea29ca1SDag-Erling Smørgrav static conn_t * 77238c7e4a6SArchie Cobbs _ftp_connect(struct url *url, struct url *purl, const char *flags) 7738e3986eaSDag-Erling Smørgrav { 774dea29ca1SDag-Erling Smørgrav conn_t *conn; 775dea29ca1SDag-Erling Smørgrav int e, direct, verbose; 77628c645cfSHajimu UMEMOTO #ifdef INET6 77728c645cfSHajimu UMEMOTO int af = AF_UNSPEC; 77828c645cfSHajimu UMEMOTO #else 77928c645cfSHajimu UMEMOTO int af = AF_INET; 78028c645cfSHajimu UMEMOTO #endif 7818e3986eaSDag-Erling Smørgrav 782d74a913bSDag-Erling Smørgrav direct = CHECK_FLAG('d'); 783d74a913bSDag-Erling Smørgrav verbose = CHECK_FLAG('v'); 784d74a913bSDag-Erling Smørgrav if (CHECK_FLAG('4')) 78528c645cfSHajimu UMEMOTO af = AF_INET; 786d74a913bSDag-Erling Smørgrav else if (CHECK_FLAG('6')) 78728c645cfSHajimu UMEMOTO af = AF_INET6; 788f5f109a0SDag-Erling Smørgrav 7891a16ed4cSDag-Erling Smørgrav if (direct) 7901a16ed4cSDag-Erling Smørgrav purl = NULL; 79128c645cfSHajimu UMEMOTO 7921a16ed4cSDag-Erling Smørgrav /* check for proxy */ 7931a16ed4cSDag-Erling Smørgrav if (purl) { 7941a16ed4cSDag-Erling Smørgrav /* XXX proxy authentication! */ 795dea29ca1SDag-Erling Smørgrav conn = _fetch_connect(purl->host, purl->port, af, verbose); 796f62e5228SDag-Erling Smørgrav } else { 797f62e5228SDag-Erling Smørgrav /* no proxy, go straight to target */ 798dea29ca1SDag-Erling Smørgrav conn = _fetch_connect(url->host, url->port, af, verbose); 7991a16ed4cSDag-Erling Smørgrav purl = NULL; 800f62e5228SDag-Erling Smørgrav } 801f62e5228SDag-Erling Smørgrav 802f62e5228SDag-Erling Smørgrav /* check connection */ 80340cfbfd5SBill Fenner if (conn == NULL) 80440cfbfd5SBill Fenner /* _fetch_connect() has already set an error code */ 805e19e6098SDag-Erling Smørgrav return (NULL); 806f62e5228SDag-Erling Smørgrav 8078e3986eaSDag-Erling Smørgrav /* expect welcome message */ 808dea29ca1SDag-Erling Smørgrav if ((e = _ftp_chkerr(conn)) != FTP_SERVICE_READY) 8098e3986eaSDag-Erling Smørgrav goto fouch; 8108e3986eaSDag-Erling Smørgrav 8116490b215SDag-Erling Smørgrav /* authenticate */ 812dea29ca1SDag-Erling Smørgrav if ((e = _ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN) 8138e3986eaSDag-Erling Smørgrav goto fouch; 8148e3986eaSDag-Erling Smørgrav 8158e3986eaSDag-Erling Smørgrav /* might as well select mode and type at once */ 8168e3986eaSDag-Erling Smørgrav #ifdef FTP_FORCE_STREAM_MODE 817dea29ca1SDag-Erling Smørgrav if ((e = _ftp_cmd(conn, "MODE S")) != FTP_OK) /* default is S */ 8185aea254fSDag-Erling Smørgrav goto fouch; 8198e3986eaSDag-Erling Smørgrav #endif 820dea29ca1SDag-Erling Smørgrav if ((e = _ftp_cmd(conn, "TYPE I")) != FTP_OK) /* default is A */ 8215aea254fSDag-Erling Smørgrav goto fouch; 8228e3986eaSDag-Erling Smørgrav 8238e3986eaSDag-Erling Smørgrav /* done */ 824dea29ca1SDag-Erling Smørgrav return (conn); 8258e3986eaSDag-Erling Smørgrav 8268e3986eaSDag-Erling Smørgrav fouch: 827fc6e9e65SDag-Erling Smørgrav if (e != -1) 8285aea254fSDag-Erling Smørgrav _ftp_seterr(e); 829dea29ca1SDag-Erling Smørgrav _fetch_close(conn); 830e19e6098SDag-Erling Smørgrav return (NULL); 8318e3986eaSDag-Erling Smørgrav } 8328e3986eaSDag-Erling Smørgrav 8338e3986eaSDag-Erling Smørgrav /* 8348e3986eaSDag-Erling Smørgrav * Disconnect from server 8358e3986eaSDag-Erling Smørgrav */ 8368e3986eaSDag-Erling Smørgrav static void 837dea29ca1SDag-Erling Smørgrav _ftp_disconnect(conn_t *conn) 8388e3986eaSDag-Erling Smørgrav { 839dea29ca1SDag-Erling Smørgrav (void)_ftp_cmd(conn, "QUIT"); 84013cc1c83SNate Lawson if (conn == cached_connection && conn->ref == 1) 84113cc1c83SNate Lawson cached_connection = NULL; 842dea29ca1SDag-Erling Smørgrav _fetch_close(conn); 8438e3986eaSDag-Erling Smørgrav } 8448e3986eaSDag-Erling Smørgrav 8458e3986eaSDag-Erling Smørgrav /* 8468e3986eaSDag-Erling Smørgrav * Check if we're already connected 8478e3986eaSDag-Erling Smørgrav */ 8488e3986eaSDag-Erling Smørgrav static int 849d8acd8dcSDag-Erling Smørgrav _ftp_isconnected(struct url *url) 8508e3986eaSDag-Erling Smørgrav { 851dea29ca1SDag-Erling Smørgrav return (cached_connection 8528e3986eaSDag-Erling Smørgrav && (strcmp(url->host, cached_host.host) == 0) 8538e3986eaSDag-Erling Smørgrav && (strcmp(url->user, cached_host.user) == 0) 8548e3986eaSDag-Erling Smørgrav && (strcmp(url->pwd, cached_host.pwd) == 0) 8558e3986eaSDag-Erling Smørgrav && (url->port == cached_host.port)); 8568e3986eaSDag-Erling Smørgrav } 8578e3986eaSDag-Erling Smørgrav 858f62e5228SDag-Erling Smørgrav /* 8595aea254fSDag-Erling Smørgrav * Check the cache, reconnect if no luck 860f62e5228SDag-Erling Smørgrav */ 861dea29ca1SDag-Erling Smørgrav static conn_t * 86238c7e4a6SArchie Cobbs _ftp_cached_connect(struct url *url, struct url *purl, const char *flags) 8634ca1ab94SDag-Erling Smørgrav { 864dea29ca1SDag-Erling Smørgrav conn_t *conn; 865dea29ca1SDag-Erling Smørgrav int e; 8668e3986eaSDag-Erling Smørgrav 8678e3986eaSDag-Erling Smørgrav /* set default port */ 86810851dc4SDag-Erling Smørgrav if (!url->port) 869e828ada7SDag-Erling Smørgrav url->port = _fetch_default_port(url->scheme); 8708e3986eaSDag-Erling Smørgrav 8713b7a6740SDag-Erling Smørgrav /* try to use previously cached connection */ 872fc6e9e65SDag-Erling Smørgrav if (_ftp_isconnected(url)) { 873dea29ca1SDag-Erling Smørgrav e = _ftp_cmd(cached_connection, "NOOP"); 874fc6e9e65SDag-Erling Smørgrav if (e == FTP_OK || e == FTP_SYNTAX_ERROR) 8759d649c1fSTim J. Robbins return (_fetch_ref(cached_connection)); 876fc6e9e65SDag-Erling Smørgrav } 8774ca1ab94SDag-Erling Smørgrav 8788e3986eaSDag-Erling Smørgrav /* connect to server */ 879dea29ca1SDag-Erling Smørgrav if ((conn = _ftp_connect(url, purl, flags)) == NULL) 880dea29ca1SDag-Erling Smørgrav return (NULL); 881dea29ca1SDag-Erling Smørgrav if (cached_connection) 882dea29ca1SDag-Erling Smørgrav _ftp_disconnect(cached_connection); 883f606d589SDag-Erling Smørgrav cached_connection = _fetch_ref(conn); 88432425dafSDag-Erling Smørgrav memcpy(&cached_host, url, sizeof *url); 885dea29ca1SDag-Erling Smørgrav return (conn); 8868e3986eaSDag-Erling Smørgrav } 8878e3986eaSDag-Erling Smørgrav 8888e3986eaSDag-Erling Smørgrav /* 8891a16ed4cSDag-Erling Smørgrav * Check the proxy settings 890dfe7c55fSDag-Erling Smørgrav */ 8911a16ed4cSDag-Erling Smørgrav static struct url * 8921a16ed4cSDag-Erling Smørgrav _ftp_get_proxy(void) 893dfe7c55fSDag-Erling Smørgrav { 8941a16ed4cSDag-Erling Smørgrav struct url *purl; 895dfe7c55fSDag-Erling Smørgrav char *p; 896dfe7c55fSDag-Erling Smørgrav 8974cee73c8SDag-Erling Smørgrav if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) || 8984cee73c8SDag-Erling Smørgrav (p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) && 8991a16ed4cSDag-Erling Smørgrav *p && (purl = fetchParseURL(p)) != NULL) { 900882974d4SDag-Erling Smørgrav if (!*purl->scheme) { 9014cee73c8SDag-Erling Smørgrav if (getenv("FTP_PROXY") || getenv("ftp_proxy")) 902882974d4SDag-Erling Smørgrav strcpy(purl->scheme, SCHEME_FTP); 903882974d4SDag-Erling Smørgrav else 904e828ada7SDag-Erling Smørgrav strcpy(purl->scheme, SCHEME_HTTP); 905882974d4SDag-Erling Smørgrav } 9061a16ed4cSDag-Erling Smørgrav if (!purl->port) 907e828ada7SDag-Erling Smørgrav purl->port = _fetch_default_proxy_port(purl->scheme); 9081a16ed4cSDag-Erling Smørgrav if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 || 9091a16ed4cSDag-Erling Smørgrav strcasecmp(purl->scheme, SCHEME_HTTP) == 0) 910e19e6098SDag-Erling Smørgrav return (purl); 9111a16ed4cSDag-Erling Smørgrav fetchFreeURL(purl); 9121a16ed4cSDag-Erling Smørgrav } 913e19e6098SDag-Erling Smørgrav return (NULL); 914dfe7c55fSDag-Erling Smørgrav } 915dfe7c55fSDag-Erling Smørgrav 916dfe7c55fSDag-Erling Smørgrav /* 917102a87c1SDag-Erling Smørgrav * Process an FTP request 9188e3986eaSDag-Erling Smørgrav */ 9194ca1ab94SDag-Erling Smørgrav FILE * 920102a87c1SDag-Erling Smørgrav _ftp_request(struct url *url, const char *op, struct url_stat *us, 921102a87c1SDag-Erling Smørgrav struct url *purl, const char *flags) 922f62e5228SDag-Erling Smørgrav { 923dea29ca1SDag-Erling Smørgrav conn_t *conn; 924dea29ca1SDag-Erling Smørgrav int oflag; 9255aea254fSDag-Erling Smørgrav 926102a87c1SDag-Erling Smørgrav /* check if we should use HTTP instead */ 927102a87c1SDag-Erling Smørgrav if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) { 928102a87c1SDag-Erling Smørgrav if (strcmp(op, "STAT") == 0) 929e19e6098SDag-Erling Smørgrav return (_http_request(url, "HEAD", us, purl, flags)); 930102a87c1SDag-Erling Smørgrav else if (strcmp(op, "RETR") == 0) 931e19e6098SDag-Erling Smørgrav return (_http_request(url, "GET", us, purl, flags)); 932102a87c1SDag-Erling Smørgrav /* 933e19e6098SDag-Erling Smørgrav * Our HTTP code doesn't support PUT requests yet, so try 934e19e6098SDag-Erling Smørgrav * a direct connection. 935102a87c1SDag-Erling Smørgrav */ 9361a16ed4cSDag-Erling Smørgrav } 937dfe7c55fSDag-Erling Smørgrav 9385aea254fSDag-Erling Smørgrav /* connect to server */ 939dea29ca1SDag-Erling Smørgrav conn = _ftp_cached_connect(url, purl, flags); 9401a16ed4cSDag-Erling Smørgrav if (purl) 9411a16ed4cSDag-Erling Smørgrav fetchFreeURL(purl); 942dea29ca1SDag-Erling Smørgrav if (conn == NULL) 943e19e6098SDag-Erling Smørgrav return (NULL); 9445aea254fSDag-Erling Smørgrav 9451a5faa10SDag-Erling Smørgrav /* change directory */ 946dea29ca1SDag-Erling Smørgrav if (_ftp_cwd(conn, url->doc) == -1) 947e19e6098SDag-Erling Smørgrav return (NULL); 9481a5faa10SDag-Erling Smørgrav 9491a5faa10SDag-Erling Smørgrav /* stat file */ 950dea29ca1SDag-Erling Smørgrav if (us && _ftp_stat(conn, url->doc, us) == -1 9510f27c783SDag-Erling Smørgrav && fetchLastErrCode != FETCH_PROTO 952269532d9SDag-Erling Smørgrav && fetchLastErrCode != FETCH_UNAVAIL) 953e19e6098SDag-Erling Smørgrav return (NULL); 9541a5faa10SDag-Erling Smørgrav 955102a87c1SDag-Erling Smørgrav /* just a stat */ 956102a87c1SDag-Erling Smørgrav if (strcmp(op, "STAT") == 0) 957102a87c1SDag-Erling Smørgrav return (FILE *)1; /* bogus return value */ 958e0583e0cSDag-Erling Smørgrav if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0) 959e0583e0cSDag-Erling Smørgrav oflag = O_WRONLY; 960e0583e0cSDag-Erling Smørgrav else 961e0583e0cSDag-Erling Smørgrav oflag = O_RDONLY; 962102a87c1SDag-Erling Smørgrav 9635aea254fSDag-Erling Smørgrav /* initiate the transfer */ 964dea29ca1SDag-Erling Smørgrav return (_ftp_transfer(conn, op, url->doc, oflag, url->offset, flags)); 965102a87c1SDag-Erling Smørgrav } 966102a87c1SDag-Erling Smørgrav 967102a87c1SDag-Erling Smørgrav /* 968102a87c1SDag-Erling Smørgrav * Get and stat file 969102a87c1SDag-Erling Smørgrav */ 970102a87c1SDag-Erling Smørgrav FILE * 971102a87c1SDag-Erling Smørgrav fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags) 972102a87c1SDag-Erling Smørgrav { 973e19e6098SDag-Erling Smørgrav return (_ftp_request(url, "RETR", us, _ftp_get_proxy(), flags)); 974f62e5228SDag-Erling Smørgrav } 975f62e5228SDag-Erling Smørgrav 9765aea254fSDag-Erling Smørgrav /* 9771a5faa10SDag-Erling Smørgrav * Get file 9781a5faa10SDag-Erling Smørgrav */ 9791a5faa10SDag-Erling Smørgrav FILE * 98038c7e4a6SArchie Cobbs fetchGetFTP(struct url *url, const char *flags) 9811a5faa10SDag-Erling Smørgrav { 982e19e6098SDag-Erling Smørgrav return (fetchXGetFTP(url, NULL, flags)); 9831a5faa10SDag-Erling Smørgrav } 9841a5faa10SDag-Erling Smørgrav 9851a5faa10SDag-Erling Smørgrav /* 9865aea254fSDag-Erling Smørgrav * Put file 9875aea254fSDag-Erling Smørgrav */ 988f62e5228SDag-Erling Smørgrav FILE * 98938c7e4a6SArchie Cobbs fetchPutFTP(struct url *url, const char *flags) 9904ca1ab94SDag-Erling Smørgrav { 9915aea254fSDag-Erling Smørgrav 992102a87c1SDag-Erling Smørgrav return _ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL, 993102a87c1SDag-Erling Smørgrav _ftp_get_proxy(), flags); 9948e3986eaSDag-Erling Smørgrav } 995d8acd8dcSDag-Erling Smørgrav 9965aea254fSDag-Erling Smørgrav /* 9975aea254fSDag-Erling Smørgrav * Get file stats 9985aea254fSDag-Erling Smørgrav */ 999d8acd8dcSDag-Erling Smørgrav int 100038c7e4a6SArchie Cobbs fetchStatFTP(struct url *url, struct url_stat *us, const char *flags) 1001d8acd8dcSDag-Erling Smørgrav { 10025aea254fSDag-Erling Smørgrav 1003102a87c1SDag-Erling Smørgrav if (_ftp_request(url, "STAT", us, _ftp_get_proxy(), flags) == NULL) 1004e19e6098SDag-Erling Smørgrav return (-1); 1005e19e6098SDag-Erling Smørgrav return (0); 10061a16ed4cSDag-Erling Smørgrav } 1007ce71b736SDag-Erling Smørgrav 1008ce71b736SDag-Erling Smørgrav /* 1009ce71b736SDag-Erling Smørgrav * List a directory 1010ce71b736SDag-Erling Smørgrav */ 1011ce71b736SDag-Erling Smørgrav struct url_ent * 1012f573a5fcSDag-Erling Smørgrav fetchListFTP(struct url *url __unused, const char *flags __unused) 1013ce71b736SDag-Erling Smørgrav { 1014def5f54cSDag-Erling Smørgrav warnx("fetchListFTP(): not implemented"); 1015e19e6098SDag-Erling Smørgrav return (NULL); 1016ce71b736SDag-Erling Smørgrav } 1017