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