14ca1ab94SDag-Erling Smørgrav /*- 24ca1ab94SDag-Erling Smørgrav * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav 34ca1ab94SDag-Erling Smørgrav * All rights reserved. 44ca1ab94SDag-Erling Smørgrav * 54ca1ab94SDag-Erling Smørgrav * Redistribution and use in source and binary forms, with or without 64ca1ab94SDag-Erling Smørgrav * modification, are permitted provided that the following conditions 74ca1ab94SDag-Erling Smørgrav * are met: 84ca1ab94SDag-Erling Smørgrav * 1. Redistributions of source code must retain the above copyright 94ca1ab94SDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer 104ca1ab94SDag-Erling Smørgrav * in this position and unchanged. 114ca1ab94SDag-Erling Smørgrav * 2. Redistributions in binary form must reproduce the above copyright 124ca1ab94SDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer in the 134ca1ab94SDag-Erling Smørgrav * documentation and/or other materials provided with the distribution. 144ca1ab94SDag-Erling Smørgrav * 3. The name of the author may not be used to endorse or promote products 154ca1ab94SDag-Erling Smørgrav * derived from this software without specific prior written permission 164ca1ab94SDag-Erling Smørgrav * 174ca1ab94SDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 184ca1ab94SDag-Erling Smørgrav * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 194ca1ab94SDag-Erling Smørgrav * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 204ca1ab94SDag-Erling Smørgrav * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 214ca1ab94SDag-Erling Smørgrav * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 224ca1ab94SDag-Erling Smørgrav * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 234ca1ab94SDag-Erling Smørgrav * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 244ca1ab94SDag-Erling Smørgrav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 254ca1ab94SDag-Erling Smørgrav * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 264ca1ab94SDag-Erling Smørgrav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 274ca1ab94SDag-Erling Smørgrav * 287f3dea24SPeter Wemm * $FreeBSD$ 294ca1ab94SDag-Erling Smørgrav */ 304ca1ab94SDag-Erling Smørgrav 314ca1ab94SDag-Erling Smørgrav /* 328e3986eaSDag-Erling Smørgrav * Portions of this code were taken from or based on ftpio.c: 334ca1ab94SDag-Erling Smørgrav * 344ca1ab94SDag-Erling Smørgrav * ---------------------------------------------------------------------------- 354ca1ab94SDag-Erling Smørgrav * "THE BEER-WARE LICENSE" (Revision 42): 364ca1ab94SDag-Erling Smørgrav * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you 374ca1ab94SDag-Erling Smørgrav * can do whatever you want with this stuff. If we meet some day, and you think 384ca1ab94SDag-Erling Smørgrav * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 394ca1ab94SDag-Erling Smørgrav * ---------------------------------------------------------------------------- 404ca1ab94SDag-Erling Smørgrav * 414ca1ab94SDag-Erling Smørgrav * Major Changelog: 424ca1ab94SDag-Erling Smørgrav * 434ca1ab94SDag-Erling Smørgrav * Dag-Erling Co�dan Sm�rgrav 444ca1ab94SDag-Erling Smørgrav * 9 Jun 1998 454ca1ab94SDag-Erling Smørgrav * 464ca1ab94SDag-Erling Smørgrav * Incorporated into libfetch 474ca1ab94SDag-Erling Smørgrav * 484ca1ab94SDag-Erling Smørgrav * Jordan K. Hubbard 494ca1ab94SDag-Erling Smørgrav * 17 Jan 1996 504ca1ab94SDag-Erling Smørgrav * 514ca1ab94SDag-Erling Smørgrav * Turned inside out. Now returns xfers as new file ids, not as a special 524ca1ab94SDag-Erling Smørgrav * `state' of FTP_t 534ca1ab94SDag-Erling Smørgrav * 544ca1ab94SDag-Erling Smørgrav * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ 554ca1ab94SDag-Erling Smørgrav * 564ca1ab94SDag-Erling Smørgrav */ 574ca1ab94SDag-Erling Smørgrav 580fba3a00SDag-Erling Smørgrav #include <sys/param.h> 594ca1ab94SDag-Erling Smørgrav #include <sys/socket.h> 60fc6e9e65SDag-Erling Smørgrav #include <sys/uio.h> 614ca1ab94SDag-Erling Smørgrav #include <netinet/in.h> 624ca1ab94SDag-Erling Smørgrav 634ca1ab94SDag-Erling Smørgrav #include <ctype.h> 64fc6e9e65SDag-Erling Smørgrav #include <errno.h> 6532425dafSDag-Erling Smørgrav #include <netdb.h> 66346298f0SDag-Erling Smørgrav #include <stdarg.h> 674ca1ab94SDag-Erling Smørgrav #include <stdio.h> 688e3986eaSDag-Erling Smørgrav #include <stdlib.h> 694ca1ab94SDag-Erling Smørgrav #include <string.h> 705aea254fSDag-Erling Smørgrav #include <time.h> 718e3986eaSDag-Erling Smørgrav #include <unistd.h> 724ca1ab94SDag-Erling Smørgrav 734ca1ab94SDag-Erling Smørgrav #include "fetch.h" 74842a95ccSDag-Erling Smørgrav #include "common.h" 750fba3a00SDag-Erling Smørgrav #include "ftperr.h" 764ca1ab94SDag-Erling Smørgrav 774ca1ab94SDag-Erling Smørgrav #define FTP_ANONYMOUS_USER "ftp" 784ca1ab94SDag-Erling Smørgrav #define FTP_ANONYMOUS_PASSWORD "ftp" 79346298f0SDag-Erling Smørgrav #define FTP_DEFAULT_PORT 21 80346298f0SDag-Erling Smørgrav 81346298f0SDag-Erling Smørgrav #define FTP_OPEN_DATA_CONNECTION 150 82346298f0SDag-Erling Smørgrav #define FTP_OK 200 835aea254fSDag-Erling Smørgrav #define FTP_FILE_STATUS 213 843b7a6740SDag-Erling Smørgrav #define FTP_SERVICE_READY 220 85346298f0SDag-Erling Smørgrav #define FTP_PASSIVE_MODE 227 8628c645cfSHajimu UMEMOTO #define FTP_LPASSIVE_MODE 228 8728c645cfSHajimu UMEMOTO #define FTP_EPASSIVE_MODE 229 88346298f0SDag-Erling Smørgrav #define FTP_LOGGED_IN 230 89346298f0SDag-Erling Smørgrav #define FTP_FILE_ACTION_OK 250 90346298f0SDag-Erling Smørgrav #define FTP_NEED_PASSWORD 331 91346298f0SDag-Erling Smørgrav #define FTP_NEED_ACCOUNT 332 9232425dafSDag-Erling Smørgrav #define FTP_FILE_OK 350 93fc6e9e65SDag-Erling Smørgrav #define FTP_SYNTAX_ERROR 500 944ca1ab94SDag-Erling Smørgrav 95fc6e9e65SDag-Erling Smørgrav static char ENDL[2] = "\r\n"; 968e3986eaSDag-Erling Smørgrav 97d8acd8dcSDag-Erling Smørgrav static struct url cached_host; 98fc6e9e65SDag-Erling Smørgrav static int cached_socket; 994ca1ab94SDag-Erling Smørgrav 100fc6e9e65SDag-Erling Smørgrav static char *last_reply; 101fc6e9e65SDag-Erling Smørgrav static size_t lr_size, lr_length; 102fc6e9e65SDag-Erling Smørgrav static int last_code; 103fc6e9e65SDag-Erling Smørgrav 104fc6e9e65SDag-Erling Smørgrav #define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 1056efb30c8SDag-Erling Smørgrav && isdigit(foo[2]) \ 1066efb30c8SDag-Erling Smørgrav && (foo[3] == ' ' || foo[3] == '\0')) 107fc6e9e65SDag-Erling Smørgrav #define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 108fc6e9e65SDag-Erling Smørgrav && isdigit(foo[2]) && foo[3] == '-') 1094ca1ab94SDag-Erling Smørgrav 11028c645cfSHajimu UMEMOTO /* translate IPv4 mapped IPv6 address to IPv4 address */ 11128c645cfSHajimu UMEMOTO static void 11228c645cfSHajimu UMEMOTO unmappedaddr(struct sockaddr_in6 *sin6) 11328c645cfSHajimu UMEMOTO { 11428c645cfSHajimu UMEMOTO struct sockaddr_in *sin4; 11528c645cfSHajimu UMEMOTO u_int32_t addr; 11628c645cfSHajimu UMEMOTO int port; 11728c645cfSHajimu UMEMOTO 11828c645cfSHajimu UMEMOTO if (sin6->sin6_family != AF_INET6 || 11928c645cfSHajimu UMEMOTO !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 12028c645cfSHajimu UMEMOTO return; 12128c645cfSHajimu UMEMOTO sin4 = (struct sockaddr_in *)sin6; 12228c645cfSHajimu UMEMOTO addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12]; 12328c645cfSHajimu UMEMOTO port = sin6->sin6_port; 12428c645cfSHajimu UMEMOTO memset(sin4, 0, sizeof(struct sockaddr_in)); 12528c645cfSHajimu UMEMOTO sin4->sin_addr.s_addr = addr; 12628c645cfSHajimu UMEMOTO sin4->sin_port = port; 12728c645cfSHajimu UMEMOTO sin4->sin_family = AF_INET; 12828c645cfSHajimu UMEMOTO sin4->sin_len = sizeof(struct sockaddr_in); 12928c645cfSHajimu UMEMOTO } 13028c645cfSHajimu UMEMOTO 1314ca1ab94SDag-Erling Smørgrav /* 132fc6e9e65SDag-Erling Smørgrav * Get server response 1338e3986eaSDag-Erling Smørgrav */ 1348e3986eaSDag-Erling Smørgrav static int 135fc6e9e65SDag-Erling Smørgrav _ftp_chkerr(int cd) 1368e3986eaSDag-Erling Smørgrav { 137fc6e9e65SDag-Erling Smørgrav if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) { 138842a95ccSDag-Erling Smørgrav _fetch_syserr(); 1398e3986eaSDag-Erling Smørgrav return -1; 1408e3986eaSDag-Erling Smørgrav } 141346298f0SDag-Erling Smørgrav #ifndef NDEBUG 142fc6e9e65SDag-Erling Smørgrav _fetch_info("got reply '%.*s'", lr_length - 2, last_reply); 143346298f0SDag-Erling Smørgrav #endif 144eac7a1e0SDag-Erling Smørgrav if (isftpinfo(last_reply)) { 145eac7a1e0SDag-Erling Smørgrav while (!isftpreply(last_reply)) { 146eac7a1e0SDag-Erling Smørgrav if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) { 147eac7a1e0SDag-Erling Smørgrav _fetch_syserr(); 148eac7a1e0SDag-Erling Smørgrav return -1; 149eac7a1e0SDag-Erling Smørgrav } 150eac7a1e0SDag-Erling Smørgrav #ifndef NDEBUG 151eac7a1e0SDag-Erling Smørgrav _fetch_info("got reply '%.*s'", lr_length - 2, last_reply); 152eac7a1e0SDag-Erling Smørgrav #endif 153eac7a1e0SDag-Erling Smørgrav } 154eac7a1e0SDag-Erling Smørgrav } 155346298f0SDag-Erling Smørgrav 156fc6e9e65SDag-Erling Smørgrav while (lr_length && isspace(last_reply[lr_length-1])) 157fc6e9e65SDag-Erling Smørgrav lr_length--; 158fc6e9e65SDag-Erling Smørgrav last_reply[lr_length] = 0; 159fc6e9e65SDag-Erling Smørgrav 160fc6e9e65SDag-Erling Smørgrav if (!isftpreply(last_reply)) { 161fc6e9e65SDag-Erling Smørgrav _ftp_seterr(999); 1628e3986eaSDag-Erling Smørgrav return -1; 1638e3986eaSDag-Erling Smørgrav } 1648e3986eaSDag-Erling Smørgrav 165fc6e9e65SDag-Erling Smørgrav last_code = (last_reply[0] - '0') * 100 166fc6e9e65SDag-Erling Smørgrav + (last_reply[1] - '0') * 10 167fc6e9e65SDag-Erling Smørgrav + (last_reply[2] - '0'); 168fc6e9e65SDag-Erling Smørgrav 169fc6e9e65SDag-Erling Smørgrav return last_code; 1708e3986eaSDag-Erling Smørgrav } 1718e3986eaSDag-Erling Smørgrav 1728e3986eaSDag-Erling Smørgrav /* 173346298f0SDag-Erling Smørgrav * Send a command and check reply 1744ca1ab94SDag-Erling Smørgrav */ 1754ca1ab94SDag-Erling Smørgrav static int 176fc6e9e65SDag-Erling Smørgrav _ftp_cmd(int cd, char *fmt, ...) 1774ca1ab94SDag-Erling Smørgrav { 178346298f0SDag-Erling Smørgrav va_list ap; 179fc6e9e65SDag-Erling Smørgrav struct iovec iov[2]; 180fc6e9e65SDag-Erling Smørgrav char *msg; 181fc6e9e65SDag-Erling Smørgrav int r; 1828e3986eaSDag-Erling Smørgrav 183346298f0SDag-Erling Smørgrav va_start(ap, fmt); 184fc6e9e65SDag-Erling Smørgrav vasprintf(&msg, fmt, ap); 185346298f0SDag-Erling Smørgrav va_end(ap); 186346298f0SDag-Erling Smørgrav 187fc6e9e65SDag-Erling Smørgrav if (msg == NULL) { 188fc6e9e65SDag-Erling Smørgrav errno = ENOMEM; 189fc6e9e65SDag-Erling Smørgrav _fetch_syserr(); 190fc6e9e65SDag-Erling Smørgrav return -1; 191fc6e9e65SDag-Erling Smørgrav } 192fc6e9e65SDag-Erling Smørgrav #ifndef NDEBUG 193fc6e9e65SDag-Erling Smørgrav _fetch_info("sending '%s'", msg); 194fc6e9e65SDag-Erling Smørgrav #endif 195fc6e9e65SDag-Erling Smørgrav iov[0].iov_base = msg; 196fc6e9e65SDag-Erling Smørgrav iov[0].iov_len = strlen(msg); 197fc6e9e65SDag-Erling Smørgrav iov[1].iov_base = ENDL; 19832425dafSDag-Erling Smørgrav iov[1].iov_len = sizeof ENDL; 199fc6e9e65SDag-Erling Smørgrav r = writev(cd, iov, 2); 200fc6e9e65SDag-Erling Smørgrav free(msg); 201fc6e9e65SDag-Erling Smørgrav if (r == -1) { 202fc6e9e65SDag-Erling Smørgrav _fetch_syserr(); 203fc6e9e65SDag-Erling Smørgrav return -1; 204fc6e9e65SDag-Erling Smørgrav } 205fc6e9e65SDag-Erling Smørgrav 206fc6e9e65SDag-Erling Smørgrav return _ftp_chkerr(cd); 2074ca1ab94SDag-Erling Smørgrav } 2084ca1ab94SDag-Erling Smørgrav 2094ca1ab94SDag-Erling Smørgrav /* 210f62e5228SDag-Erling Smørgrav * Transfer file 2114ca1ab94SDag-Erling Smørgrav */ 2124ca1ab94SDag-Erling Smørgrav static FILE * 21332425dafSDag-Erling Smørgrav _ftp_transfer(int cd, char *oper, char *file, 21432425dafSDag-Erling Smørgrav char *mode, off_t offset, char *flags) 2154ca1ab94SDag-Erling Smørgrav { 21628c645cfSHajimu UMEMOTO struct sockaddr_storage sin; 21728c645cfSHajimu UMEMOTO struct sockaddr_in6 *sin6; 21828c645cfSHajimu UMEMOTO struct sockaddr_in *sin4; 219f5f109a0SDag-Erling Smørgrav int pasv, high, verbose; 220f5f109a0SDag-Erling Smørgrav int e, sd = -1; 221f5f109a0SDag-Erling Smørgrav socklen_t l; 222346298f0SDag-Erling Smørgrav char *s; 223346298f0SDag-Erling Smørgrav FILE *df; 2248e3986eaSDag-Erling Smørgrav 225f5f109a0SDag-Erling Smørgrav /* check flags */ 226f5f109a0SDag-Erling Smørgrav pasv = (flags && strchr(flags, 'p')); 227f5f109a0SDag-Erling Smørgrav high = (flags && strchr(flags, 'h')); 228f5f109a0SDag-Erling Smørgrav verbose = (flags && strchr(flags, 'v')); 229f5f109a0SDag-Erling Smørgrav 230d02e84a6SDag-Erling Smørgrav /* passive mode */ 231d02e84a6SDag-Erling Smørgrav if (!pasv && (s = getenv("FTP_PASSIVE_MODE")) != NULL) 232d02e84a6SDag-Erling Smørgrav pasv = (strncasecmp(s, "no", 2) != 0); 233d02e84a6SDag-Erling Smørgrav 2344ca1ab94SDag-Erling Smørgrav /* change directory */ 235346298f0SDag-Erling Smørgrav if (((s = strrchr(file, '/')) != NULL) && (s != file)) { 236346298f0SDag-Erling Smørgrav *s = 0; 237f5f109a0SDag-Erling Smørgrav if (verbose) 238f5f109a0SDag-Erling Smørgrav _fetch_info("changing directory to %s", file); 239fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "CWD %s", file)) != FTP_FILE_ACTION_OK) { 240346298f0SDag-Erling Smørgrav *s = '/'; 241fc6e9e65SDag-Erling Smørgrav if (e != -1) 2425aea254fSDag-Erling Smørgrav _ftp_seterr(e); 2434ca1ab94SDag-Erling Smørgrav return NULL; 2444ca1ab94SDag-Erling Smørgrav } 245346298f0SDag-Erling Smørgrav *s++ = '/'; 2464ca1ab94SDag-Erling Smørgrav } else { 247f5f109a0SDag-Erling Smørgrav if (verbose) 248f5f109a0SDag-Erling Smørgrav _fetch_info("changing directory to /"); 249fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "CWD /")) != FTP_FILE_ACTION_OK) { 250fc6e9e65SDag-Erling Smørgrav if (e != -1) 2515aea254fSDag-Erling Smørgrav _ftp_seterr(e); 2524ca1ab94SDag-Erling Smørgrav return NULL; 2534ca1ab94SDag-Erling Smørgrav } 2545aea254fSDag-Erling Smørgrav } 2554ca1ab94SDag-Erling Smørgrav 256346298f0SDag-Erling Smørgrav /* s now points to file name */ 257346298f0SDag-Erling Smørgrav 25828c645cfSHajimu UMEMOTO /* find our own address, bind, and listen */ 25928c645cfSHajimu UMEMOTO l = sizeof sin; 26028c645cfSHajimu UMEMOTO if (getsockname(cd, (struct sockaddr *)&sin, &l) == -1) 26128c645cfSHajimu UMEMOTO goto sysouch; 26228c645cfSHajimu UMEMOTO if (sin.ss_family == AF_INET6) 26328c645cfSHajimu UMEMOTO unmappedaddr((struct sockaddr_in6 *)&sin); 26428c645cfSHajimu UMEMOTO 265346298f0SDag-Erling Smørgrav /* open data socket */ 26628c645cfSHajimu UMEMOTO if ((sd = socket(sin.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { 267842a95ccSDag-Erling Smørgrav _fetch_syserr(); 268346298f0SDag-Erling Smørgrav return NULL; 269346298f0SDag-Erling Smørgrav } 270346298f0SDag-Erling Smørgrav 271346298f0SDag-Erling Smørgrav if (pasv) { 27228c645cfSHajimu UMEMOTO u_char addr[64]; 273346298f0SDag-Erling Smørgrav char *ln, *p; 274346298f0SDag-Erling Smørgrav int i; 27528c645cfSHajimu UMEMOTO int port; 276346298f0SDag-Erling Smørgrav 277346298f0SDag-Erling Smørgrav /* send PASV command */ 278f5f109a0SDag-Erling Smørgrav if (verbose) 279f5f109a0SDag-Erling Smørgrav _fetch_info("setting passive mode"); 28028c645cfSHajimu UMEMOTO switch (sin.ss_family) { 28128c645cfSHajimu UMEMOTO case AF_INET: 282fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE) 283346298f0SDag-Erling Smørgrav goto ouch; 28428c645cfSHajimu UMEMOTO break; 28528c645cfSHajimu UMEMOTO case AF_INET6: 28628c645cfSHajimu UMEMOTO if ((e = _ftp_cmd(cd, "EPSV")) != FTP_EPASSIVE_MODE) { 28728c645cfSHajimu UMEMOTO if (e == -1) 28828c645cfSHajimu UMEMOTO goto ouch; 28928c645cfSHajimu UMEMOTO if ((e = _ftp_cmd(cd, "LPSV")) != FTP_LPASSIVE_MODE) 29028c645cfSHajimu UMEMOTO goto ouch; 29128c645cfSHajimu UMEMOTO } 29228c645cfSHajimu UMEMOTO break; 29328c645cfSHajimu UMEMOTO default: 29428c645cfSHajimu UMEMOTO e = 999; /* XXX: error code should be prepared */ 29528c645cfSHajimu UMEMOTO goto ouch; 29628c645cfSHajimu UMEMOTO } 297346298f0SDag-Erling Smørgrav 298f5f109a0SDag-Erling Smørgrav /* 299f5f109a0SDag-Erling Smørgrav * Find address and port number. The reply to the PASV command 300f5f109a0SDag-Erling Smørgrav * is IMHO the one and only weak point in the FTP protocol. 301f5f109a0SDag-Erling Smørgrav */ 302fc6e9e65SDag-Erling Smørgrav ln = last_reply; 30328c645cfSHajimu UMEMOTO for (p = ln + 3; *p && *p != '('; p++) 304346298f0SDag-Erling Smørgrav /* nothing */ ; 30528c645cfSHajimu UMEMOTO if (!*p) { 3066efb30c8SDag-Erling Smørgrav e = 999; 3076efb30c8SDag-Erling Smørgrav goto ouch; 308346298f0SDag-Erling Smørgrav } 30928c645cfSHajimu UMEMOTO p++; 31028c645cfSHajimu UMEMOTO switch (e) { 31128c645cfSHajimu UMEMOTO case FTP_PASSIVE_MODE: 31228c645cfSHajimu UMEMOTO case FTP_LPASSIVE_MODE: 31328c645cfSHajimu UMEMOTO l = (e == FTP_PASSIVE_MODE ? 6 : 21); 31428c645cfSHajimu UMEMOTO for (i = 0; *p && i < l; i++, p++) 31528c645cfSHajimu UMEMOTO addr[i] = strtol(p, &p, 10); 31628c645cfSHajimu UMEMOTO if (i < l) { 31728c645cfSHajimu UMEMOTO e = 999; 31828c645cfSHajimu UMEMOTO goto ouch; 31928c645cfSHajimu UMEMOTO } 32028c645cfSHajimu UMEMOTO break; 32128c645cfSHajimu UMEMOTO case FTP_EPASSIVE_MODE: 32228c645cfSHajimu UMEMOTO if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], 32328c645cfSHajimu UMEMOTO &port, &addr[3]) != 5 || 32428c645cfSHajimu UMEMOTO addr[0] != addr[1] || 32528c645cfSHajimu UMEMOTO addr[0] != addr[2] || addr[0] != addr[3]) { 32628c645cfSHajimu UMEMOTO e = 999; 32728c645cfSHajimu UMEMOTO goto ouch; 32828c645cfSHajimu UMEMOTO } 32928c645cfSHajimu UMEMOTO break; 33028c645cfSHajimu UMEMOTO } 331346298f0SDag-Erling Smørgrav 33232425dafSDag-Erling Smørgrav /* seek to required offset */ 33332425dafSDag-Erling Smørgrav if (offset) 33432425dafSDag-Erling Smørgrav if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK) 33532425dafSDag-Erling Smørgrav goto sysouch; 33632425dafSDag-Erling Smørgrav 337346298f0SDag-Erling Smørgrav /* construct sockaddr for data socket */ 33832425dafSDag-Erling Smørgrav l = sizeof sin; 339fc6e9e65SDag-Erling Smørgrav if (getpeername(cd, (struct sockaddr *)&sin, &l) == -1) 340346298f0SDag-Erling Smørgrav goto sysouch; 34128c645cfSHajimu UMEMOTO if (sin.ss_family == AF_INET6) 34228c645cfSHajimu UMEMOTO unmappedaddr((struct sockaddr_in6 *)&sin); 34328c645cfSHajimu UMEMOTO switch (sin.ss_family) { 34428c645cfSHajimu UMEMOTO case AF_INET6: 34528c645cfSHajimu UMEMOTO sin6 = (struct sockaddr_in6 *)&sin; 34628c645cfSHajimu UMEMOTO if (e == FTP_EPASSIVE_MODE) 34728c645cfSHajimu UMEMOTO sin6->sin6_port = htons(port); 34828c645cfSHajimu UMEMOTO else { 34928c645cfSHajimu UMEMOTO bcopy(addr + 2, (char *)&sin6->sin6_addr, 16); 35028c645cfSHajimu UMEMOTO bcopy(addr + 19, (char *)&sin6->sin6_port, 2); 35128c645cfSHajimu UMEMOTO } 35228c645cfSHajimu UMEMOTO break; 35328c645cfSHajimu UMEMOTO case AF_INET: 35428c645cfSHajimu UMEMOTO sin4 = (struct sockaddr_in *)&sin; 35528c645cfSHajimu UMEMOTO if (e == FTP_EPASSIVE_MODE) 35628c645cfSHajimu UMEMOTO sin4->sin_port = htons(port); 35728c645cfSHajimu UMEMOTO else { 35828c645cfSHajimu UMEMOTO bcopy(addr, (char *)&sin4->sin_addr, 4); 35928c645cfSHajimu UMEMOTO bcopy(addr + 4, (char *)&sin4->sin_port, 2); 36028c645cfSHajimu UMEMOTO } 36128c645cfSHajimu UMEMOTO break; 36228c645cfSHajimu UMEMOTO default: 36328c645cfSHajimu UMEMOTO e = 999; /* XXX: error code should be prepared */ 36428c645cfSHajimu UMEMOTO break; 36528c645cfSHajimu UMEMOTO } 366346298f0SDag-Erling Smørgrav 367346298f0SDag-Erling Smørgrav /* connect to data port */ 368f5f109a0SDag-Erling Smørgrav if (verbose) 369f5f109a0SDag-Erling Smørgrav _fetch_info("opening data connection"); 37028c645cfSHajimu UMEMOTO if (connect(sd, (struct sockaddr *)&sin, sin.ss_len) == -1) 371346298f0SDag-Erling Smørgrav goto sysouch; 372346298f0SDag-Erling Smørgrav 373346298f0SDag-Erling Smørgrav /* make the server initiate the transfer */ 374f5f109a0SDag-Erling Smørgrav if (verbose) 375def5f54cSDag-Erling Smørgrav _fetch_info("initiating transfer"); 376fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cd, "%s %s", oper, s); 3775aea254fSDag-Erling Smørgrav if (e != FTP_OPEN_DATA_CONNECTION) 378346298f0SDag-Erling Smørgrav goto ouch; 379346298f0SDag-Erling Smørgrav 380346298f0SDag-Erling Smørgrav } else { 381346298f0SDag-Erling Smørgrav u_int32_t a; 382346298f0SDag-Erling Smørgrav u_short p; 383f5f109a0SDag-Erling Smørgrav int arg, d; 38428c645cfSHajimu UMEMOTO char *ap; 38528c645cfSHajimu UMEMOTO char hname[INET6_ADDRSTRLEN]; 386346298f0SDag-Erling Smørgrav 38728c645cfSHajimu UMEMOTO switch (sin.ss_family) { 38828c645cfSHajimu UMEMOTO case AF_INET6: 38928c645cfSHajimu UMEMOTO ((struct sockaddr_in6 *)&sin)->sin6_port = 0; 39028c645cfSHajimu UMEMOTO #ifdef IPV6_PORTRANGE 39128c645cfSHajimu UMEMOTO arg = high ? IPV6_PORTRANGE_HIGH : IPV6_PORTRANGE_DEFAULT; 39228c645cfSHajimu UMEMOTO if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, 39328c645cfSHajimu UMEMOTO (char *)&arg, sizeof(arg)) == -1) 394346298f0SDag-Erling Smørgrav goto sysouch; 39528c645cfSHajimu UMEMOTO #endif 39628c645cfSHajimu UMEMOTO break; 39728c645cfSHajimu UMEMOTO case AF_INET: 39828c645cfSHajimu UMEMOTO ((struct sockaddr_in *)&sin)->sin_port = 0; 399f5f109a0SDag-Erling Smørgrav arg = high ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; 400f5f109a0SDag-Erling Smørgrav if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, 40132425dafSDag-Erling Smørgrav (char *)&arg, sizeof arg) == -1) 402f5f109a0SDag-Erling Smørgrav goto sysouch; 40328c645cfSHajimu UMEMOTO break; 40428c645cfSHajimu UMEMOTO } 405f5f109a0SDag-Erling Smørgrav if (verbose) 406f5f109a0SDag-Erling Smørgrav _fetch_info("binding data socket"); 40728c645cfSHajimu UMEMOTO if (bind(sd, (struct sockaddr *)&sin, sin.ss_len) == -1) 408346298f0SDag-Erling Smørgrav goto sysouch; 409ecc91352SDag-Erling Smørgrav if (listen(sd, 1) == -1) 410346298f0SDag-Erling Smørgrav goto sysouch; 411346298f0SDag-Erling Smørgrav 412346298f0SDag-Erling Smørgrav /* find what port we're on and tell the server */ 413ecc91352SDag-Erling Smørgrav if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1) 414346298f0SDag-Erling Smørgrav goto sysouch; 41528c645cfSHajimu UMEMOTO switch (sin.ss_family) { 41628c645cfSHajimu UMEMOTO case AF_INET: 41728c645cfSHajimu UMEMOTO sin4 = (struct sockaddr_in *)&sin; 41828c645cfSHajimu UMEMOTO a = ntohl(sin4->sin_addr.s_addr); 41928c645cfSHajimu UMEMOTO p = ntohs(sin4->sin_port); 420fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d", 4215aea254fSDag-Erling Smørgrav (a >> 24) & 0xff, (a >> 16) & 0xff, 4225aea254fSDag-Erling Smørgrav (a >> 8) & 0xff, a & 0xff, 4235aea254fSDag-Erling Smørgrav (p >> 8) & 0xff, p & 0xff); 42428c645cfSHajimu UMEMOTO break; 42528c645cfSHajimu UMEMOTO case AF_INET6: 42628c645cfSHajimu UMEMOTO #define UC(b) (((int)b)&0xff) 42728c645cfSHajimu UMEMOTO e = -1; 42828c645cfSHajimu UMEMOTO sin6 = (struct sockaddr_in6 *)&sin; 42928c645cfSHajimu UMEMOTO if (getnameinfo((struct sockaddr *)&sin, sin.ss_len, 43028c645cfSHajimu UMEMOTO hname, sizeof(hname), 43128c645cfSHajimu UMEMOTO NULL, 0, NI_NUMERICHOST) == 0) { 43228c645cfSHajimu UMEMOTO e = _ftp_cmd(cd, "EPRT |%d|%s|%d|", 2, hname, 43328c645cfSHajimu UMEMOTO htons(sin6->sin6_port)); 43428c645cfSHajimu UMEMOTO if (e == -1) 43528c645cfSHajimu UMEMOTO goto ouch; 43628c645cfSHajimu UMEMOTO } 43728c645cfSHajimu UMEMOTO if (e != FTP_OK) { 43828c645cfSHajimu UMEMOTO ap = (char *)&sin6->sin6_addr; 43928c645cfSHajimu UMEMOTO e = _ftp_cmd(cd, 44028c645cfSHajimu UMEMOTO "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", 44128c645cfSHajimu UMEMOTO 6, 16, 44228c645cfSHajimu UMEMOTO UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]), 44328c645cfSHajimu UMEMOTO UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]), 44428c645cfSHajimu UMEMOTO UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]), 44528c645cfSHajimu UMEMOTO UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]), 44628c645cfSHajimu UMEMOTO 2, 44728c645cfSHajimu UMEMOTO (ntohs(sin6->sin6_port) >> 8) & 0xff, 44828c645cfSHajimu UMEMOTO ntohs(sin6->sin6_port) & 0xff); 44928c645cfSHajimu UMEMOTO } 45028c645cfSHajimu UMEMOTO break; 45128c645cfSHajimu UMEMOTO default: 45228c645cfSHajimu UMEMOTO e = 999; /* XXX: error code should be prepared */ 45328c645cfSHajimu UMEMOTO goto ouch; 45428c645cfSHajimu UMEMOTO } 4555aea254fSDag-Erling Smørgrav if (e != FTP_OK) 456346298f0SDag-Erling Smørgrav goto ouch; 457346298f0SDag-Erling Smørgrav 458893980adSDag-Erling Smørgrav /* seek to required offset */ 459893980adSDag-Erling Smørgrav if (offset) 460893980adSDag-Erling Smørgrav if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK) 461893980adSDag-Erling Smørgrav goto sysouch; 462893980adSDag-Erling Smørgrav 463346298f0SDag-Erling Smørgrav /* make the server initiate the transfer */ 464f5f109a0SDag-Erling Smørgrav if (verbose) 465f5f109a0SDag-Erling Smørgrav _fetch_info("initiating transfer"); 466fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cd, "%s %s", oper, s); 4675aea254fSDag-Erling Smørgrav if (e != FTP_OPEN_DATA_CONNECTION) 468346298f0SDag-Erling Smørgrav goto ouch; 469346298f0SDag-Erling Smørgrav 470346298f0SDag-Erling Smørgrav /* accept the incoming connection and go to town */ 471ecc91352SDag-Erling Smørgrav if ((d = accept(sd, NULL, NULL)) == -1) 472346298f0SDag-Erling Smørgrav goto sysouch; 473346298f0SDag-Erling Smørgrav close(sd); 474346298f0SDag-Erling Smørgrav sd = d; 475346298f0SDag-Erling Smørgrav } 476346298f0SDag-Erling Smørgrav 477f62e5228SDag-Erling Smørgrav if ((df = fdopen(sd, mode)) == NULL) 478346298f0SDag-Erling Smørgrav goto sysouch; 479346298f0SDag-Erling Smørgrav return df; 480346298f0SDag-Erling Smørgrav 481346298f0SDag-Erling Smørgrav sysouch: 482842a95ccSDag-Erling Smørgrav _fetch_syserr(); 48328c645cfSHajimu UMEMOTO if (sd >= 0) 4845aea254fSDag-Erling Smørgrav close(sd); 4855aea254fSDag-Erling Smørgrav return NULL; 4865aea254fSDag-Erling Smørgrav 487346298f0SDag-Erling Smørgrav ouch: 488fc6e9e65SDag-Erling Smørgrav if (e != -1) 4895aea254fSDag-Erling Smørgrav _ftp_seterr(e); 49028c645cfSHajimu UMEMOTO if (sd >= 0) 491346298f0SDag-Erling Smørgrav close(sd); 4924ca1ab94SDag-Erling Smørgrav return NULL; 4934ca1ab94SDag-Erling Smørgrav } 4944ca1ab94SDag-Erling Smørgrav 4958e3986eaSDag-Erling Smørgrav /* 4968e3986eaSDag-Erling Smørgrav * Log on to FTP server 4974ca1ab94SDag-Erling Smørgrav */ 498fc6e9e65SDag-Erling Smørgrav static int 499f5f109a0SDag-Erling Smørgrav _ftp_connect(char *host, int port, char *user, char *pwd, char *flags) 5008e3986eaSDag-Erling Smørgrav { 50132425dafSDag-Erling Smørgrav int cd, e, pp = 0, direct, verbose; 50228c645cfSHajimu UMEMOTO #ifdef INET6 50328c645cfSHajimu UMEMOTO int af = AF_UNSPEC; 50428c645cfSHajimu UMEMOTO #else 50528c645cfSHajimu UMEMOTO int af = AF_INET; 50628c645cfSHajimu UMEMOTO #endif 507f62e5228SDag-Erling Smørgrav char *p, *q; 5084ab587f4SHajimu UMEMOTO const char *logname; 5094ab587f4SHajimu UMEMOTO char localhost[MAXHOSTNAMELEN]; 5104ab587f4SHajimu UMEMOTO char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1]; 5118e3986eaSDag-Erling Smørgrav 512f5f109a0SDag-Erling Smørgrav direct = (flags && strchr(flags, 'd')); 513f5f109a0SDag-Erling Smørgrav verbose = (flags && strchr(flags, 'v')); 51428c645cfSHajimu UMEMOTO if ((flags && strchr(flags, '4'))) 51528c645cfSHajimu UMEMOTO af = AF_INET; 51628c645cfSHajimu UMEMOTO else if ((flags && strchr(flags, '6'))) 51728c645cfSHajimu UMEMOTO af = AF_INET6; 518f5f109a0SDag-Erling Smørgrav 519f62e5228SDag-Erling Smørgrav /* check for proxy */ 520f5f109a0SDag-Erling Smørgrav if (!direct && (p = getenv("FTP_PROXY")) != NULL) { 52128c645cfSHajimu UMEMOTO char c = 0; 52228c645cfSHajimu UMEMOTO 52328c645cfSHajimu UMEMOTO #ifdef INET6 52428c645cfSHajimu UMEMOTO if (*p != '[' || (q = strchr(p + 1, ']')) == NULL || 52528c645cfSHajimu UMEMOTO (*++q != '\0' && *q != ':')) 52628c645cfSHajimu UMEMOTO #endif 52728c645cfSHajimu UMEMOTO q = strchr(p, ':'); 52828c645cfSHajimu UMEMOTO if (q != NULL && *q == ':') { 52932425dafSDag-Erling Smørgrav if (strspn(q+1, "0123456789") != strlen(q+1) || strlen(q+1) > 5) { 53032425dafSDag-Erling Smørgrav /* XXX we should emit some kind of warning */ 53132425dafSDag-Erling Smørgrav } 532f62e5228SDag-Erling Smørgrav pp = atoi(q+1); 53332425dafSDag-Erling Smørgrav if (pp < 1 || pp > 65535) { 53432425dafSDag-Erling Smørgrav /* XXX we should emit some kind of warning */ 53532425dafSDag-Erling Smørgrav } 53632425dafSDag-Erling Smørgrav } 53732425dafSDag-Erling Smørgrav if (!pp) { 53832425dafSDag-Erling Smørgrav struct servent *se; 53932425dafSDag-Erling Smørgrav 54032425dafSDag-Erling Smørgrav if ((se = getservbyname("ftp", "tcp")) != NULL) 54132425dafSDag-Erling Smørgrav pp = ntohs(se->s_port); 54232425dafSDag-Erling Smørgrav else 54332425dafSDag-Erling Smørgrav pp = FTP_DEFAULT_PORT; 544f62e5228SDag-Erling Smørgrav } 54528c645cfSHajimu UMEMOTO if (q) { 54628c645cfSHajimu UMEMOTO #ifdef INET6 54728c645cfSHajimu UMEMOTO if (q > p && *p == '[' && *(q - 1) == ']') { 54828c645cfSHajimu UMEMOTO p++; 54928c645cfSHajimu UMEMOTO q--; 55028c645cfSHajimu UMEMOTO } 55128c645cfSHajimu UMEMOTO #endif 55228c645cfSHajimu UMEMOTO c = *q; 553f62e5228SDag-Erling Smørgrav *q = 0; 55428c645cfSHajimu UMEMOTO } 55528c645cfSHajimu UMEMOTO cd = _fetch_connect(p, pp, af, verbose); 556f62e5228SDag-Erling Smørgrav if (q) 55728c645cfSHajimu UMEMOTO *q = c; 558f62e5228SDag-Erling Smørgrav } else { 559f62e5228SDag-Erling Smørgrav /* no proxy, go straight to target */ 56028c645cfSHajimu UMEMOTO cd = _fetch_connect(host, port, af, verbose); 561f5f109a0SDag-Erling Smørgrav p = NULL; 562f62e5228SDag-Erling Smørgrav } 563f62e5228SDag-Erling Smørgrav 564f62e5228SDag-Erling Smørgrav /* check connection */ 565fc6e9e65SDag-Erling Smørgrav if (cd == -1) { 566842a95ccSDag-Erling Smørgrav _fetch_syserr(); 5678e3986eaSDag-Erling Smørgrav return NULL; 5688e3986eaSDag-Erling Smørgrav } 569f62e5228SDag-Erling Smørgrav 5708e3986eaSDag-Erling Smørgrav /* expect welcome message */ 571fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY) 5728e3986eaSDag-Erling Smørgrav goto fouch; 5738e3986eaSDag-Erling Smørgrav 5748e3986eaSDag-Erling Smørgrav /* send user name and password */ 575f62e5228SDag-Erling Smørgrav if (!user || !*user) 576f62e5228SDag-Erling Smørgrav user = FTP_ANONYMOUS_USER; 577fc6e9e65SDag-Erling Smørgrav e = p ? _ftp_cmd(cd, "USER %s@%s@%d", user, host, port) 578fc6e9e65SDag-Erling Smørgrav : _ftp_cmd(cd, "USER %s", user); 579f62e5228SDag-Erling Smørgrav 580f62e5228SDag-Erling Smørgrav /* did the server request a password? */ 581f62e5228SDag-Erling Smørgrav if (e == FTP_NEED_PASSWORD) { 582f62e5228SDag-Erling Smørgrav if (!pwd || !*pwd) 5834ab587f4SHajimu UMEMOTO pwd = getenv("FTP_PASSWORD"); 5844ab587f4SHajimu UMEMOTO if (!pwd || !*pwd) { 5854ab587f4SHajimu UMEMOTO if ((logname = getlogin()) == 0) 5864ab587f4SHajimu UMEMOTO logname = FTP_ANONYMOUS_PASSWORD; 5874ab587f4SHajimu UMEMOTO gethostname(localhost, sizeof localhost); 5884ab587f4SHajimu UMEMOTO snprintf(pbuf, sizeof pbuf, "%s@%s", logname, localhost); 5894ab587f4SHajimu UMEMOTO pwd = pbuf; 5904ab587f4SHajimu UMEMOTO } 591fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cd, "PASS %s", pwd); 592f62e5228SDag-Erling Smørgrav } 593f62e5228SDag-Erling Smørgrav 594f62e5228SDag-Erling Smørgrav /* did the server request an account? */ 5955aea254fSDag-Erling Smørgrav if (e == FTP_NEED_ACCOUNT) 5963b7a6740SDag-Erling Smørgrav goto fouch; 597f62e5228SDag-Erling Smørgrav 598f62e5228SDag-Erling Smørgrav /* we should be done by now */ 5995aea254fSDag-Erling Smørgrav if (e != FTP_LOGGED_IN) 6008e3986eaSDag-Erling Smørgrav goto fouch; 6018e3986eaSDag-Erling Smørgrav 6028e3986eaSDag-Erling Smørgrav /* might as well select mode and type at once */ 6038e3986eaSDag-Erling Smørgrav #ifdef FTP_FORCE_STREAM_MODE 604fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */ 6055aea254fSDag-Erling Smørgrav goto fouch; 6068e3986eaSDag-Erling Smørgrav #endif 607fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */ 6085aea254fSDag-Erling Smørgrav goto fouch; 6098e3986eaSDag-Erling Smørgrav 6108e3986eaSDag-Erling Smørgrav /* done */ 611fc6e9e65SDag-Erling Smørgrav return cd; 6128e3986eaSDag-Erling Smørgrav 6138e3986eaSDag-Erling Smørgrav fouch: 614fc6e9e65SDag-Erling Smørgrav if (e != -1) 6155aea254fSDag-Erling Smørgrav _ftp_seterr(e); 616fc6e9e65SDag-Erling Smørgrav close(cd); 6178e3986eaSDag-Erling Smørgrav return NULL; 6188e3986eaSDag-Erling Smørgrav } 6198e3986eaSDag-Erling Smørgrav 6208e3986eaSDag-Erling Smørgrav /* 6218e3986eaSDag-Erling Smørgrav * Disconnect from server 6228e3986eaSDag-Erling Smørgrav */ 6238e3986eaSDag-Erling Smørgrav static void 624fc6e9e65SDag-Erling Smørgrav _ftp_disconnect(int cd) 6258e3986eaSDag-Erling Smørgrav { 626fc6e9e65SDag-Erling Smørgrav (void)_ftp_cmd(cd, "QUIT"); 627fc6e9e65SDag-Erling Smørgrav close(cd); 6288e3986eaSDag-Erling Smørgrav } 6298e3986eaSDag-Erling Smørgrav 6308e3986eaSDag-Erling Smørgrav /* 6318e3986eaSDag-Erling Smørgrav * Check if we're already connected 6328e3986eaSDag-Erling Smørgrav */ 6338e3986eaSDag-Erling Smørgrav static int 634d8acd8dcSDag-Erling Smørgrav _ftp_isconnected(struct url *url) 6358e3986eaSDag-Erling Smørgrav { 6368e3986eaSDag-Erling Smørgrav return (cached_socket 6378e3986eaSDag-Erling Smørgrav && (strcmp(url->host, cached_host.host) == 0) 6388e3986eaSDag-Erling Smørgrav && (strcmp(url->user, cached_host.user) == 0) 6398e3986eaSDag-Erling Smørgrav && (strcmp(url->pwd, cached_host.pwd) == 0) 6408e3986eaSDag-Erling Smørgrav && (url->port == cached_host.port)); 6418e3986eaSDag-Erling Smørgrav } 6428e3986eaSDag-Erling Smørgrav 643f62e5228SDag-Erling Smørgrav /* 6445aea254fSDag-Erling Smørgrav * Check the cache, reconnect if no luck 645f62e5228SDag-Erling Smørgrav */ 646fc6e9e65SDag-Erling Smørgrav static int 6475aea254fSDag-Erling Smørgrav _ftp_cached_connect(struct url *url, char *flags) 6484ca1ab94SDag-Erling Smørgrav { 649fc6e9e65SDag-Erling Smørgrav int e, cd; 6505aea254fSDag-Erling Smørgrav 651fc6e9e65SDag-Erling Smørgrav cd = -1; 6528e3986eaSDag-Erling Smørgrav 6538e3986eaSDag-Erling Smørgrav /* set default port */ 65432425dafSDag-Erling Smørgrav if (!url->port) { 65532425dafSDag-Erling Smørgrav struct servent *se; 65632425dafSDag-Erling Smørgrav 65732425dafSDag-Erling Smørgrav if ((se = getservbyname("ftp", "tcp")) != NULL) 65832425dafSDag-Erling Smørgrav url->port = ntohs(se->s_port); 65932425dafSDag-Erling Smørgrav else 660346298f0SDag-Erling Smørgrav url->port = FTP_DEFAULT_PORT; 66132425dafSDag-Erling Smørgrav } 6628e3986eaSDag-Erling Smørgrav 6633b7a6740SDag-Erling Smørgrav /* try to use previously cached connection */ 664fc6e9e65SDag-Erling Smørgrav if (_ftp_isconnected(url)) { 665fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cached_socket, "NOOP"); 666fc6e9e65SDag-Erling Smørgrav if (e == FTP_OK || e == FTP_SYNTAX_ERROR) 667fc6e9e65SDag-Erling Smørgrav cd = cached_socket; 668fc6e9e65SDag-Erling Smørgrav } 6694ca1ab94SDag-Erling Smørgrav 6708e3986eaSDag-Erling Smørgrav /* connect to server */ 671fc6e9e65SDag-Erling Smørgrav if (cd == -1) { 672fc6e9e65SDag-Erling Smørgrav cd = _ftp_connect(url->host, url->port, url->user, url->pwd, flags); 673fc6e9e65SDag-Erling Smørgrav if (cd == -1) 674fc6e9e65SDag-Erling Smørgrav return -1; 6758e3986eaSDag-Erling Smørgrav if (cached_socket) 6768e3986eaSDag-Erling Smørgrav _ftp_disconnect(cached_socket); 677fc6e9e65SDag-Erling Smørgrav cached_socket = cd; 67832425dafSDag-Erling Smørgrav memcpy(&cached_host, url, sizeof *url); 6798e3986eaSDag-Erling Smørgrav } 6808e3986eaSDag-Erling Smørgrav 681fc6e9e65SDag-Erling Smørgrav return cd; 6828e3986eaSDag-Erling Smørgrav } 6838e3986eaSDag-Erling Smørgrav 6848e3986eaSDag-Erling Smørgrav /* 6855aea254fSDag-Erling Smørgrav * Get file 6868e3986eaSDag-Erling Smørgrav */ 6874ca1ab94SDag-Erling Smørgrav FILE * 688d8acd8dcSDag-Erling Smørgrav fetchGetFTP(struct url *url, char *flags) 689f62e5228SDag-Erling Smørgrav { 690fc6e9e65SDag-Erling Smørgrav int cd; 6915aea254fSDag-Erling Smørgrav 6925aea254fSDag-Erling Smørgrav /* connect to server */ 693fc6e9e65SDag-Erling Smørgrav if ((cd = _ftp_cached_connect(url, flags)) == NULL) 6945aea254fSDag-Erling Smørgrav return NULL; 6955aea254fSDag-Erling Smørgrav 6965aea254fSDag-Erling Smørgrav /* initiate the transfer */ 69732425dafSDag-Erling Smørgrav return _ftp_transfer(cd, "RETR", url->doc, "r", url->offset, flags); 698f62e5228SDag-Erling Smørgrav } 699f62e5228SDag-Erling Smørgrav 7005aea254fSDag-Erling Smørgrav /* 7015aea254fSDag-Erling Smørgrav * Put file 7025aea254fSDag-Erling Smørgrav */ 703f62e5228SDag-Erling Smørgrav FILE * 704d8acd8dcSDag-Erling Smørgrav fetchPutFTP(struct url *url, char *flags) 7054ca1ab94SDag-Erling Smørgrav { 706fc6e9e65SDag-Erling Smørgrav int cd; 7075aea254fSDag-Erling Smørgrav 7085aea254fSDag-Erling Smørgrav /* connect to server */ 709fc6e9e65SDag-Erling Smørgrav if ((cd = _ftp_cached_connect(url, flags)) == NULL) 7105aea254fSDag-Erling Smørgrav return NULL; 7115aea254fSDag-Erling Smørgrav 7125aea254fSDag-Erling Smørgrav /* initiate the transfer */ 713fc6e9e65SDag-Erling Smørgrav return _ftp_transfer(cd, (flags && strchr(flags, 'a')) ? "APPE" : "STOR", 71432425dafSDag-Erling Smørgrav url->doc, "w", url->offset, flags); 7158e3986eaSDag-Erling Smørgrav } 716d8acd8dcSDag-Erling Smørgrav 7175aea254fSDag-Erling Smørgrav /* 7185aea254fSDag-Erling Smørgrav * Get file stats 7195aea254fSDag-Erling Smørgrav */ 720d8acd8dcSDag-Erling Smørgrav int 721d8acd8dcSDag-Erling Smørgrav fetchStatFTP(struct url *url, struct url_stat *us, char *flags) 722d8acd8dcSDag-Erling Smørgrav { 7235aea254fSDag-Erling Smørgrav char *ln, *s; 7245aea254fSDag-Erling Smørgrav struct tm tm; 7255aea254fSDag-Erling Smørgrav time_t t; 726fc6e9e65SDag-Erling Smørgrav int e, cd; 7275aea254fSDag-Erling Smørgrav 7280669702cSDag-Erling Smørgrav us->size = -1; 7290669702cSDag-Erling Smørgrav us->atime = us->mtime = 0; 7300669702cSDag-Erling Smørgrav 7315aea254fSDag-Erling Smørgrav /* connect to server */ 732fc6e9e65SDag-Erling Smørgrav if ((cd = _ftp_cached_connect(url, flags)) == NULL) 7335aea254fSDag-Erling Smørgrav return -1; 7345aea254fSDag-Erling Smørgrav 7355aea254fSDag-Erling Smørgrav /* change directory */ 7365aea254fSDag-Erling Smørgrav if (((s = strrchr(url->doc, '/')) != NULL) && (s != url->doc)) { 7375aea254fSDag-Erling Smørgrav *s = 0; 738fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "CWD %s", url->doc)) != FTP_FILE_ACTION_OK) { 7395aea254fSDag-Erling Smørgrav *s = '/'; 7405aea254fSDag-Erling Smørgrav goto ouch; 7415aea254fSDag-Erling Smørgrav } 7425aea254fSDag-Erling Smørgrav *s++ = '/'; 7435aea254fSDag-Erling Smørgrav } else { 744fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "CWD /")) != FTP_FILE_ACTION_OK) 7455aea254fSDag-Erling Smørgrav goto ouch; 7465aea254fSDag-Erling Smørgrav } 7475aea254fSDag-Erling Smørgrav 7485aea254fSDag-Erling Smørgrav /* s now points to file name */ 7495aea254fSDag-Erling Smørgrav 750fc6e9e65SDag-Erling Smørgrav if (_ftp_cmd(cd, "SIZE %s", s) != FTP_FILE_STATUS) 7515aea254fSDag-Erling Smørgrav goto ouch; 752fc6e9e65SDag-Erling Smørgrav for (ln = last_reply + 4; *ln && isspace(*ln); ln++) 7535aea254fSDag-Erling Smørgrav /* nothing */ ; 7545aea254fSDag-Erling Smørgrav for (us->size = 0; *ln && isdigit(*ln); ln++) 7555aea254fSDag-Erling Smørgrav us->size = us->size * 10 + *ln - '0'; 7565aea254fSDag-Erling Smørgrav if (*ln && !isspace(*ln)) { 757fc6e9e65SDag-Erling Smørgrav _ftp_seterr(999); 7585aea254fSDag-Erling Smørgrav return -1; 7595aea254fSDag-Erling Smørgrav } 76089474d12SDag-Erling Smørgrav DEBUG(fprintf(stderr, "size: [\033[1m%lld\033[m]\n", us->size)); 7615aea254fSDag-Erling Smørgrav 762fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "MDTM %s", s)) != FTP_FILE_STATUS) 7635aea254fSDag-Erling Smørgrav goto ouch; 764fc6e9e65SDag-Erling Smørgrav for (ln = last_reply + 4; *ln && isspace(*ln); ln++) 7655aea254fSDag-Erling Smørgrav /* nothing */ ; 76689474d12SDag-Erling Smørgrav e = 999; 76789474d12SDag-Erling Smørgrav switch (strspn(ln, "0123456789")) { 76889474d12SDag-Erling Smørgrav case 14: 76989474d12SDag-Erling Smørgrav break; 77089474d12SDag-Erling Smørgrav case 15: 77189474d12SDag-Erling Smørgrav ln++; 77289474d12SDag-Erling Smørgrav ln[0] = '2'; 77389474d12SDag-Erling Smørgrav ln[1] = '0'; 77489474d12SDag-Erling Smørgrav break; 77589474d12SDag-Erling Smørgrav default: 77689474d12SDag-Erling Smørgrav goto ouch; 77789474d12SDag-Erling Smørgrav } 77889474d12SDag-Erling Smørgrav if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", 7795aea254fSDag-Erling Smørgrav &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 78089474d12SDag-Erling Smørgrav &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) 78189474d12SDag-Erling Smørgrav goto ouch; 7825aea254fSDag-Erling Smørgrav tm.tm_mon--; 7835aea254fSDag-Erling Smørgrav tm.tm_year -= 1900; 7845aea254fSDag-Erling Smørgrav tm.tm_isdst = -1; 78589474d12SDag-Erling Smørgrav t = timegm(&tm); 7865d32c97cSDag-Erling Smørgrav if (t == (time_t)-1) 7875d32c97cSDag-Erling Smørgrav t = time(NULL); 7885d32c97cSDag-Erling Smørgrav us->mtime = t; 7895d32c97cSDag-Erling Smørgrav us->atime = t; 79089474d12SDag-Erling Smørgrav DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d " 79189474d12SDag-Erling Smørgrav "%02d:%02d:%02d\033[m]\n", 79289474d12SDag-Erling Smørgrav tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 79389474d12SDag-Erling Smørgrav tm.tm_hour, tm.tm_min, tm.tm_sec)); 7945aea254fSDag-Erling Smørgrav return 0; 7955aea254fSDag-Erling Smørgrav 7965aea254fSDag-Erling Smørgrav ouch: 797fc6e9e65SDag-Erling Smørgrav if (e != -1) 7985aea254fSDag-Erling Smørgrav _ftp_seterr(e); 799d8acd8dcSDag-Erling Smørgrav return -1; 800d8acd8dcSDag-Erling Smørgrav } 801ce71b736SDag-Erling Smørgrav 802ce71b736SDag-Erling Smørgrav /* 803ce71b736SDag-Erling Smørgrav * List a directory 804ce71b736SDag-Erling Smørgrav */ 805ce71b736SDag-Erling Smørgrav extern void warnx(char *, ...); 806ce71b736SDag-Erling Smørgrav struct url_ent * 807ce71b736SDag-Erling Smørgrav fetchListFTP(struct url *url, char *flags) 808ce71b736SDag-Erling Smørgrav { 809def5f54cSDag-Erling Smørgrav warnx("fetchListFTP(): not implemented"); 810ce71b736SDag-Erling Smørgrav return NULL; 811ce71b736SDag-Erling Smørgrav } 812