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 86346298f0SDag-Erling Smørgrav #define FTP_LOGGED_IN 230 87346298f0SDag-Erling Smørgrav #define FTP_FILE_ACTION_OK 250 88346298f0SDag-Erling Smørgrav #define FTP_NEED_PASSWORD 331 89346298f0SDag-Erling Smørgrav #define FTP_NEED_ACCOUNT 332 9032425dafSDag-Erling Smørgrav #define FTP_FILE_OK 350 91fc6e9e65SDag-Erling Smørgrav #define FTP_SYNTAX_ERROR 500 924ca1ab94SDag-Erling Smørgrav 93fc6e9e65SDag-Erling Smørgrav static char ENDL[2] = "\r\n"; 948e3986eaSDag-Erling Smørgrav 95d8acd8dcSDag-Erling Smørgrav static struct url cached_host; 96fc6e9e65SDag-Erling Smørgrav static int cached_socket; 974ca1ab94SDag-Erling Smørgrav 98fc6e9e65SDag-Erling Smørgrav static char *last_reply; 99fc6e9e65SDag-Erling Smørgrav static size_t lr_size, lr_length; 100fc6e9e65SDag-Erling Smørgrav static int last_code; 101fc6e9e65SDag-Erling Smørgrav 102fc6e9e65SDag-Erling Smørgrav #define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 1036efb30c8SDag-Erling Smørgrav && isdigit(foo[2]) \ 1046efb30c8SDag-Erling Smørgrav && (foo[3] == ' ' || foo[3] == '\0')) 105fc6e9e65SDag-Erling Smørgrav #define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 106fc6e9e65SDag-Erling Smørgrav && isdigit(foo[2]) && foo[3] == '-') 1074ca1ab94SDag-Erling Smørgrav 1084ca1ab94SDag-Erling Smørgrav /* 109fc6e9e65SDag-Erling Smørgrav * Get server response 1108e3986eaSDag-Erling Smørgrav */ 1118e3986eaSDag-Erling Smørgrav static int 112fc6e9e65SDag-Erling Smørgrav _ftp_chkerr(int cd) 1138e3986eaSDag-Erling Smørgrav { 1148e3986eaSDag-Erling Smørgrav do { 115fc6e9e65SDag-Erling Smørgrav if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) { 116842a95ccSDag-Erling Smørgrav _fetch_syserr(); 1178e3986eaSDag-Erling Smørgrav return -1; 1188e3986eaSDag-Erling Smørgrav } 119346298f0SDag-Erling Smørgrav #ifndef NDEBUG 120fc6e9e65SDag-Erling Smørgrav _fetch_info("got reply '%.*s'", lr_length - 2, last_reply); 121346298f0SDag-Erling Smørgrav #endif 122fc6e9e65SDag-Erling Smørgrav } while (isftpinfo(last_reply)); 123346298f0SDag-Erling Smørgrav 124fc6e9e65SDag-Erling Smørgrav while (lr_length && isspace(last_reply[lr_length-1])) 125fc6e9e65SDag-Erling Smørgrav lr_length--; 126fc6e9e65SDag-Erling Smørgrav last_reply[lr_length] = 0; 127fc6e9e65SDag-Erling Smørgrav 128fc6e9e65SDag-Erling Smørgrav if (!isftpreply(last_reply)) { 129fc6e9e65SDag-Erling Smørgrav _ftp_seterr(999); 1308e3986eaSDag-Erling Smørgrav return -1; 1318e3986eaSDag-Erling Smørgrav } 1328e3986eaSDag-Erling Smørgrav 133fc6e9e65SDag-Erling Smørgrav last_code = (last_reply[0] - '0') * 100 134fc6e9e65SDag-Erling Smørgrav + (last_reply[1] - '0') * 10 135fc6e9e65SDag-Erling Smørgrav + (last_reply[2] - '0'); 136fc6e9e65SDag-Erling Smørgrav 137fc6e9e65SDag-Erling Smørgrav return last_code; 1388e3986eaSDag-Erling Smørgrav } 1398e3986eaSDag-Erling Smørgrav 1408e3986eaSDag-Erling Smørgrav /* 141346298f0SDag-Erling Smørgrav * Send a command and check reply 1424ca1ab94SDag-Erling Smørgrav */ 1434ca1ab94SDag-Erling Smørgrav static int 144fc6e9e65SDag-Erling Smørgrav _ftp_cmd(int cd, char *fmt, ...) 1454ca1ab94SDag-Erling Smørgrav { 146346298f0SDag-Erling Smørgrav va_list ap; 147fc6e9e65SDag-Erling Smørgrav struct iovec iov[2]; 148fc6e9e65SDag-Erling Smørgrav char *msg; 149fc6e9e65SDag-Erling Smørgrav int r; 1508e3986eaSDag-Erling Smørgrav 151346298f0SDag-Erling Smørgrav va_start(ap, fmt); 152fc6e9e65SDag-Erling Smørgrav vasprintf(&msg, fmt, ap); 153346298f0SDag-Erling Smørgrav va_end(ap); 154346298f0SDag-Erling Smørgrav 155fc6e9e65SDag-Erling Smørgrav if (msg == NULL) { 156fc6e9e65SDag-Erling Smørgrav errno = ENOMEM; 157fc6e9e65SDag-Erling Smørgrav _fetch_syserr(); 158fc6e9e65SDag-Erling Smørgrav return -1; 159fc6e9e65SDag-Erling Smørgrav } 160fc6e9e65SDag-Erling Smørgrav #ifndef NDEBUG 161fc6e9e65SDag-Erling Smørgrav _fetch_info("sending '%s'", msg); 162fc6e9e65SDag-Erling Smørgrav #endif 163fc6e9e65SDag-Erling Smørgrav iov[0].iov_base = msg; 164fc6e9e65SDag-Erling Smørgrav iov[0].iov_len = strlen(msg); 165fc6e9e65SDag-Erling Smørgrav iov[1].iov_base = ENDL; 16632425dafSDag-Erling Smørgrav iov[1].iov_len = sizeof ENDL; 167fc6e9e65SDag-Erling Smørgrav r = writev(cd, iov, 2); 168fc6e9e65SDag-Erling Smørgrav free(msg); 169fc6e9e65SDag-Erling Smørgrav if (r == -1) { 170fc6e9e65SDag-Erling Smørgrav _fetch_syserr(); 171fc6e9e65SDag-Erling Smørgrav return -1; 172fc6e9e65SDag-Erling Smørgrav } 173fc6e9e65SDag-Erling Smørgrav 174fc6e9e65SDag-Erling Smørgrav return _ftp_chkerr(cd); 1754ca1ab94SDag-Erling Smørgrav } 1764ca1ab94SDag-Erling Smørgrav 1774ca1ab94SDag-Erling Smørgrav /* 178f62e5228SDag-Erling Smørgrav * Transfer file 1794ca1ab94SDag-Erling Smørgrav */ 1804ca1ab94SDag-Erling Smørgrav static FILE * 18132425dafSDag-Erling Smørgrav _ftp_transfer(int cd, char *oper, char *file, 18232425dafSDag-Erling Smørgrav char *mode, off_t offset, char *flags) 1834ca1ab94SDag-Erling Smørgrav { 184346298f0SDag-Erling Smørgrav struct sockaddr_in sin; 185f5f109a0SDag-Erling Smørgrav int pasv, high, verbose; 186f5f109a0SDag-Erling Smørgrav int e, sd = -1; 187f5f109a0SDag-Erling Smørgrav socklen_t l; 188346298f0SDag-Erling Smørgrav char *s; 189346298f0SDag-Erling Smørgrav FILE *df; 1908e3986eaSDag-Erling Smørgrav 191f5f109a0SDag-Erling Smørgrav /* check flags */ 192f5f109a0SDag-Erling Smørgrav pasv = (flags && strchr(flags, 'p')); 193f5f109a0SDag-Erling Smørgrav high = (flags && strchr(flags, 'h')); 194f5f109a0SDag-Erling Smørgrav verbose = (flags && strchr(flags, 'v')); 195f5f109a0SDag-Erling Smørgrav 1964ca1ab94SDag-Erling Smørgrav /* change directory */ 197346298f0SDag-Erling Smørgrav if (((s = strrchr(file, '/')) != NULL) && (s != file)) { 198346298f0SDag-Erling Smørgrav *s = 0; 199f5f109a0SDag-Erling Smørgrav if (verbose) 200f5f109a0SDag-Erling Smørgrav _fetch_info("changing directory to %s", file); 201fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "CWD %s", file)) != FTP_FILE_ACTION_OK) { 202346298f0SDag-Erling Smørgrav *s = '/'; 203fc6e9e65SDag-Erling Smørgrav if (e != -1) 2045aea254fSDag-Erling Smørgrav _ftp_seterr(e); 2054ca1ab94SDag-Erling Smørgrav return NULL; 2064ca1ab94SDag-Erling Smørgrav } 207346298f0SDag-Erling Smørgrav *s++ = '/'; 2084ca1ab94SDag-Erling Smørgrav } else { 209f5f109a0SDag-Erling Smørgrav if (verbose) 210f5f109a0SDag-Erling Smørgrav _fetch_info("changing directory to /"); 211fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "CWD /")) != FTP_FILE_ACTION_OK) { 212fc6e9e65SDag-Erling Smørgrav if (e != -1) 2135aea254fSDag-Erling Smørgrav _ftp_seterr(e); 2144ca1ab94SDag-Erling Smørgrav return NULL; 2154ca1ab94SDag-Erling Smørgrav } 2165aea254fSDag-Erling Smørgrav } 2174ca1ab94SDag-Erling Smørgrav 218346298f0SDag-Erling Smørgrav /* s now points to file name */ 219346298f0SDag-Erling Smørgrav 220346298f0SDag-Erling Smørgrav /* open data socket */ 221ecc91352SDag-Erling Smørgrav if ((sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { 222842a95ccSDag-Erling Smørgrav _fetch_syserr(); 223346298f0SDag-Erling Smørgrav return NULL; 224346298f0SDag-Erling Smørgrav } 225346298f0SDag-Erling Smørgrav 226346298f0SDag-Erling Smørgrav if (pasv) { 227346298f0SDag-Erling Smørgrav u_char addr[6]; 228346298f0SDag-Erling Smørgrav char *ln, *p; 229346298f0SDag-Erling Smørgrav int i; 230346298f0SDag-Erling Smørgrav 231346298f0SDag-Erling Smørgrav /* send PASV command */ 232f5f109a0SDag-Erling Smørgrav if (verbose) 233f5f109a0SDag-Erling Smørgrav _fetch_info("setting passive mode"); 234fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE) 235346298f0SDag-Erling Smørgrav goto ouch; 236346298f0SDag-Erling Smørgrav 237f5f109a0SDag-Erling Smørgrav /* 238f5f109a0SDag-Erling Smørgrav * Find address and port number. The reply to the PASV command 239f5f109a0SDag-Erling Smørgrav * is IMHO the one and only weak point in the FTP protocol. 240f5f109a0SDag-Erling Smørgrav */ 241fc6e9e65SDag-Erling Smørgrav ln = last_reply; 2426efb30c8SDag-Erling Smørgrav for (p = ln + 3; *p && !isdigit(*p); p++) 243346298f0SDag-Erling Smørgrav /* nothing */ ; 2446efb30c8SDag-Erling Smørgrav for (i = 0; *p, i < 6; i++, p++) 245346298f0SDag-Erling Smørgrav addr[i] = strtol(p, &p, 10); 2466efb30c8SDag-Erling Smørgrav if (i < 6) { 2476efb30c8SDag-Erling Smørgrav e = 999; 2486efb30c8SDag-Erling Smørgrav goto ouch; 249346298f0SDag-Erling Smørgrav } 250346298f0SDag-Erling Smørgrav 25132425dafSDag-Erling Smørgrav /* seek to required offset */ 25232425dafSDag-Erling Smørgrav if (offset) 25332425dafSDag-Erling Smørgrav if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK) 25432425dafSDag-Erling Smørgrav goto sysouch; 25532425dafSDag-Erling Smørgrav 256346298f0SDag-Erling Smørgrav /* construct sockaddr for data socket */ 25732425dafSDag-Erling Smørgrav l = sizeof sin; 258fc6e9e65SDag-Erling Smørgrav if (getpeername(cd, (struct sockaddr *)&sin, &l) == -1) 259346298f0SDag-Erling Smørgrav goto sysouch; 260346298f0SDag-Erling Smørgrav bcopy(addr, (char *)&sin.sin_addr, 4); 261346298f0SDag-Erling Smørgrav bcopy(addr + 4, (char *)&sin.sin_port, 2); 262346298f0SDag-Erling Smørgrav 263346298f0SDag-Erling Smørgrav /* connect to data port */ 264f5f109a0SDag-Erling Smørgrav if (verbose) 265f5f109a0SDag-Erling Smørgrav _fetch_info("opening data connection"); 26632425dafSDag-Erling Smørgrav if (connect(sd, (struct sockaddr *)&sin, sizeof sin) == -1) 267346298f0SDag-Erling Smørgrav goto sysouch; 268346298f0SDag-Erling Smørgrav 269346298f0SDag-Erling Smørgrav /* make the server initiate the transfer */ 270f5f109a0SDag-Erling Smørgrav if (verbose) 271f5f109a0SDag-Erling Smørgrav _fetch_info("initiating transfer"); 272fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cd, "%s %s", oper, s); 2735aea254fSDag-Erling Smørgrav if (e != FTP_OPEN_DATA_CONNECTION) 274346298f0SDag-Erling Smørgrav goto ouch; 275346298f0SDag-Erling Smørgrav 276346298f0SDag-Erling Smørgrav } else { 277346298f0SDag-Erling Smørgrav u_int32_t a; 278346298f0SDag-Erling Smørgrav u_short p; 279f5f109a0SDag-Erling Smørgrav int arg, d; 280346298f0SDag-Erling Smørgrav 281346298f0SDag-Erling Smørgrav /* find our own address, bind, and listen */ 28232425dafSDag-Erling Smørgrav l = sizeof sin; 283fc6e9e65SDag-Erling Smørgrav if (getsockname(cd, (struct sockaddr *)&sin, &l) == -1) 284346298f0SDag-Erling Smørgrav goto sysouch; 285346298f0SDag-Erling Smørgrav sin.sin_port = 0; 286f5f109a0SDag-Erling Smørgrav arg = high ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; 287f5f109a0SDag-Erling Smørgrav if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, 28832425dafSDag-Erling Smørgrav (char *)&arg, sizeof arg) == -1) 289f5f109a0SDag-Erling Smørgrav goto sysouch; 290f5f109a0SDag-Erling Smørgrav if (verbose) 291f5f109a0SDag-Erling Smørgrav _fetch_info("binding data socket"); 292ecc91352SDag-Erling Smørgrav if (bind(sd, (struct sockaddr *)&sin, l) == -1) 293346298f0SDag-Erling Smørgrav goto sysouch; 294ecc91352SDag-Erling Smørgrav if (listen(sd, 1) == -1) 295346298f0SDag-Erling Smørgrav goto sysouch; 296346298f0SDag-Erling Smørgrav 297346298f0SDag-Erling Smørgrav /* find what port we're on and tell the server */ 298ecc91352SDag-Erling Smørgrav if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1) 299346298f0SDag-Erling Smørgrav goto sysouch; 300346298f0SDag-Erling Smørgrav a = ntohl(sin.sin_addr.s_addr); 301346298f0SDag-Erling Smørgrav p = ntohs(sin.sin_port); 302fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d", 3035aea254fSDag-Erling Smørgrav (a >> 24) & 0xff, (a >> 16) & 0xff, 3045aea254fSDag-Erling Smørgrav (a >> 8) & 0xff, a & 0xff, 3055aea254fSDag-Erling Smørgrav (p >> 8) & 0xff, p & 0xff); 3065aea254fSDag-Erling Smørgrav if (e != FTP_OK) 307346298f0SDag-Erling Smørgrav goto ouch; 308346298f0SDag-Erling Smørgrav 309346298f0SDag-Erling Smørgrav /* make the server initiate the transfer */ 310f5f109a0SDag-Erling Smørgrav if (verbose) 311f5f109a0SDag-Erling Smørgrav _fetch_info("initiating transfer"); 312fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cd, "%s %s", oper, s); 3135aea254fSDag-Erling Smørgrav if (e != FTP_OPEN_DATA_CONNECTION) 314346298f0SDag-Erling Smørgrav goto ouch; 315346298f0SDag-Erling Smørgrav 316346298f0SDag-Erling Smørgrav /* accept the incoming connection and go to town */ 317ecc91352SDag-Erling Smørgrav if ((d = accept(sd, NULL, NULL)) == -1) 318346298f0SDag-Erling Smørgrav goto sysouch; 319346298f0SDag-Erling Smørgrav close(sd); 320346298f0SDag-Erling Smørgrav sd = d; 321346298f0SDag-Erling Smørgrav } 322346298f0SDag-Erling Smørgrav 323f62e5228SDag-Erling Smørgrav if ((df = fdopen(sd, mode)) == NULL) 324346298f0SDag-Erling Smørgrav goto sysouch; 325346298f0SDag-Erling Smørgrav return df; 326346298f0SDag-Erling Smørgrav 327346298f0SDag-Erling Smørgrav sysouch: 328842a95ccSDag-Erling Smørgrav _fetch_syserr(); 3295aea254fSDag-Erling Smørgrav close(sd); 3305aea254fSDag-Erling Smørgrav return NULL; 3315aea254fSDag-Erling Smørgrav 332346298f0SDag-Erling Smørgrav ouch: 333fc6e9e65SDag-Erling Smørgrav if (e != -1) 3345aea254fSDag-Erling Smørgrav _ftp_seterr(e); 335346298f0SDag-Erling Smørgrav close(sd); 3364ca1ab94SDag-Erling Smørgrav return NULL; 3374ca1ab94SDag-Erling Smørgrav } 3384ca1ab94SDag-Erling Smørgrav 3398e3986eaSDag-Erling Smørgrav /* 3408e3986eaSDag-Erling Smørgrav * Log on to FTP server 3414ca1ab94SDag-Erling Smørgrav */ 342fc6e9e65SDag-Erling Smørgrav static int 343f5f109a0SDag-Erling Smørgrav _ftp_connect(char *host, int port, char *user, char *pwd, char *flags) 3448e3986eaSDag-Erling Smørgrav { 34532425dafSDag-Erling Smørgrav int cd, e, pp = 0, direct, verbose; 346f62e5228SDag-Erling Smørgrav char *p, *q; 3478e3986eaSDag-Erling Smørgrav 348f5f109a0SDag-Erling Smørgrav direct = (flags && strchr(flags, 'd')); 349f5f109a0SDag-Erling Smørgrav verbose = (flags && strchr(flags, 'v')); 350f5f109a0SDag-Erling Smørgrav 351f62e5228SDag-Erling Smørgrav /* check for proxy */ 352f5f109a0SDag-Erling Smørgrav if (!direct && (p = getenv("FTP_PROXY")) != NULL) { 353f62e5228SDag-Erling Smørgrav if ((q = strchr(p, ':')) != NULL) { 35432425dafSDag-Erling Smørgrav if (strspn(q+1, "0123456789") != strlen(q+1) || strlen(q+1) > 5) { 35532425dafSDag-Erling Smørgrav /* XXX we should emit some kind of warning */ 35632425dafSDag-Erling Smørgrav } 357f62e5228SDag-Erling Smørgrav pp = atoi(q+1); 35832425dafSDag-Erling Smørgrav if (pp < 1 || pp > 65535) { 35932425dafSDag-Erling Smørgrav /* XXX we should emit some kind of warning */ 36032425dafSDag-Erling Smørgrav } 36132425dafSDag-Erling Smørgrav } 36232425dafSDag-Erling Smørgrav if (!pp) { 36332425dafSDag-Erling Smørgrav struct servent *se; 36432425dafSDag-Erling Smørgrav 36532425dafSDag-Erling Smørgrav if ((se = getservbyname("ftp", "tcp")) != NULL) 36632425dafSDag-Erling Smørgrav pp = ntohs(se->s_port); 36732425dafSDag-Erling Smørgrav else 36832425dafSDag-Erling Smørgrav pp = FTP_DEFAULT_PORT; 369f62e5228SDag-Erling Smørgrav } 370f62e5228SDag-Erling Smørgrav if (q) 371f62e5228SDag-Erling Smørgrav *q = 0; 372fc6e9e65SDag-Erling Smørgrav cd = _fetch_connect(p, pp, verbose); 373f62e5228SDag-Erling Smørgrav if (q) 374f62e5228SDag-Erling Smørgrav *q = ':'; 375f62e5228SDag-Erling Smørgrav } else { 376f62e5228SDag-Erling Smørgrav /* no proxy, go straight to target */ 377fc6e9e65SDag-Erling Smørgrav cd = _fetch_connect(host, port, verbose); 378f5f109a0SDag-Erling Smørgrav p = NULL; 379f62e5228SDag-Erling Smørgrav } 380f62e5228SDag-Erling Smørgrav 381f62e5228SDag-Erling Smørgrav /* check connection */ 382fc6e9e65SDag-Erling Smørgrav if (cd == -1) { 383842a95ccSDag-Erling Smørgrav _fetch_syserr(); 3848e3986eaSDag-Erling Smørgrav return NULL; 3858e3986eaSDag-Erling Smørgrav } 386f62e5228SDag-Erling Smørgrav 3878e3986eaSDag-Erling Smørgrav /* expect welcome message */ 388fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY) 3898e3986eaSDag-Erling Smørgrav goto fouch; 3908e3986eaSDag-Erling Smørgrav 3918e3986eaSDag-Erling Smørgrav /* send user name and password */ 392f62e5228SDag-Erling Smørgrav if (!user || !*user) 393f62e5228SDag-Erling Smørgrav user = FTP_ANONYMOUS_USER; 394fc6e9e65SDag-Erling Smørgrav e = p ? _ftp_cmd(cd, "USER %s@%s@%d", user, host, port) 395fc6e9e65SDag-Erling Smørgrav : _ftp_cmd(cd, "USER %s", user); 396f62e5228SDag-Erling Smørgrav 397f62e5228SDag-Erling Smørgrav /* did the server request a password? */ 398f62e5228SDag-Erling Smørgrav if (e == FTP_NEED_PASSWORD) { 399f62e5228SDag-Erling Smørgrav if (!pwd || !*pwd) 400f62e5228SDag-Erling Smørgrav pwd = FTP_ANONYMOUS_PASSWORD; 401fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cd, "PASS %s", pwd); 402f62e5228SDag-Erling Smørgrav } 403f62e5228SDag-Erling Smørgrav 404f62e5228SDag-Erling Smørgrav /* did the server request an account? */ 4055aea254fSDag-Erling Smørgrav if (e == FTP_NEED_ACCOUNT) 4063b7a6740SDag-Erling Smørgrav goto fouch; 407f62e5228SDag-Erling Smørgrav 408f62e5228SDag-Erling Smørgrav /* we should be done by now */ 4095aea254fSDag-Erling Smørgrav if (e != FTP_LOGGED_IN) 4108e3986eaSDag-Erling Smørgrav goto fouch; 4118e3986eaSDag-Erling Smørgrav 4128e3986eaSDag-Erling Smørgrav /* might as well select mode and type at once */ 4138e3986eaSDag-Erling Smørgrav #ifdef FTP_FORCE_STREAM_MODE 414fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */ 4155aea254fSDag-Erling Smørgrav goto fouch; 4168e3986eaSDag-Erling Smørgrav #endif 417fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */ 4185aea254fSDag-Erling Smørgrav goto fouch; 4198e3986eaSDag-Erling Smørgrav 4208e3986eaSDag-Erling Smørgrav /* done */ 421fc6e9e65SDag-Erling Smørgrav return cd; 4228e3986eaSDag-Erling Smørgrav 4238e3986eaSDag-Erling Smørgrav fouch: 424fc6e9e65SDag-Erling Smørgrav if (e != -1) 4255aea254fSDag-Erling Smørgrav _ftp_seterr(e); 426fc6e9e65SDag-Erling Smørgrav close(cd); 4278e3986eaSDag-Erling Smørgrav return NULL; 4288e3986eaSDag-Erling Smørgrav } 4298e3986eaSDag-Erling Smørgrav 4308e3986eaSDag-Erling Smørgrav /* 4318e3986eaSDag-Erling Smørgrav * Disconnect from server 4328e3986eaSDag-Erling Smørgrav */ 4338e3986eaSDag-Erling Smørgrav static void 434fc6e9e65SDag-Erling Smørgrav _ftp_disconnect(int cd) 4358e3986eaSDag-Erling Smørgrav { 436fc6e9e65SDag-Erling Smørgrav (void)_ftp_cmd(cd, "QUIT"); 437fc6e9e65SDag-Erling Smørgrav close(cd); 4388e3986eaSDag-Erling Smørgrav } 4398e3986eaSDag-Erling Smørgrav 4408e3986eaSDag-Erling Smørgrav /* 4418e3986eaSDag-Erling Smørgrav * Check if we're already connected 4428e3986eaSDag-Erling Smørgrav */ 4438e3986eaSDag-Erling Smørgrav static int 444d8acd8dcSDag-Erling Smørgrav _ftp_isconnected(struct url *url) 4458e3986eaSDag-Erling Smørgrav { 4468e3986eaSDag-Erling Smørgrav return (cached_socket 4478e3986eaSDag-Erling Smørgrav && (strcmp(url->host, cached_host.host) == 0) 4488e3986eaSDag-Erling Smørgrav && (strcmp(url->user, cached_host.user) == 0) 4498e3986eaSDag-Erling Smørgrav && (strcmp(url->pwd, cached_host.pwd) == 0) 4508e3986eaSDag-Erling Smørgrav && (url->port == cached_host.port)); 4518e3986eaSDag-Erling Smørgrav } 4528e3986eaSDag-Erling Smørgrav 453f62e5228SDag-Erling Smørgrav /* 4545aea254fSDag-Erling Smørgrav * Check the cache, reconnect if no luck 455f62e5228SDag-Erling Smørgrav */ 456fc6e9e65SDag-Erling Smørgrav static int 4575aea254fSDag-Erling Smørgrav _ftp_cached_connect(struct url *url, char *flags) 4584ca1ab94SDag-Erling Smørgrav { 459fc6e9e65SDag-Erling Smørgrav int e, cd; 4605aea254fSDag-Erling Smørgrav 461fc6e9e65SDag-Erling Smørgrav cd = -1; 4628e3986eaSDag-Erling Smørgrav 4638e3986eaSDag-Erling Smørgrav /* set default port */ 46432425dafSDag-Erling Smørgrav if (!url->port) { 46532425dafSDag-Erling Smørgrav struct servent *se; 46632425dafSDag-Erling Smørgrav 46732425dafSDag-Erling Smørgrav if ((se = getservbyname("ftp", "tcp")) != NULL) 46832425dafSDag-Erling Smørgrav url->port = ntohs(se->s_port); 46932425dafSDag-Erling Smørgrav else 470346298f0SDag-Erling Smørgrav url->port = FTP_DEFAULT_PORT; 47132425dafSDag-Erling Smørgrav } 4728e3986eaSDag-Erling Smørgrav 4733b7a6740SDag-Erling Smørgrav /* try to use previously cached connection */ 474fc6e9e65SDag-Erling Smørgrav if (_ftp_isconnected(url)) { 475fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cached_socket, "NOOP"); 476fc6e9e65SDag-Erling Smørgrav if (e == FTP_OK || e == FTP_SYNTAX_ERROR) 477fc6e9e65SDag-Erling Smørgrav cd = cached_socket; 478fc6e9e65SDag-Erling Smørgrav } 4794ca1ab94SDag-Erling Smørgrav 4808e3986eaSDag-Erling Smørgrav /* connect to server */ 481fc6e9e65SDag-Erling Smørgrav if (cd == -1) { 482fc6e9e65SDag-Erling Smørgrav cd = _ftp_connect(url->host, url->port, url->user, url->pwd, flags); 483fc6e9e65SDag-Erling Smørgrav if (cd == -1) 484fc6e9e65SDag-Erling Smørgrav return -1; 4858e3986eaSDag-Erling Smørgrav if (cached_socket) 4868e3986eaSDag-Erling Smørgrav _ftp_disconnect(cached_socket); 487fc6e9e65SDag-Erling Smørgrav cached_socket = cd; 48832425dafSDag-Erling Smørgrav memcpy(&cached_host, url, sizeof *url); 4898e3986eaSDag-Erling Smørgrav } 4908e3986eaSDag-Erling Smørgrav 491fc6e9e65SDag-Erling Smørgrav return cd; 4928e3986eaSDag-Erling Smørgrav } 4938e3986eaSDag-Erling Smørgrav 4948e3986eaSDag-Erling Smørgrav /* 4955aea254fSDag-Erling Smørgrav * Get file 4968e3986eaSDag-Erling Smørgrav */ 4974ca1ab94SDag-Erling Smørgrav FILE * 498d8acd8dcSDag-Erling Smørgrav fetchGetFTP(struct url *url, char *flags) 499f62e5228SDag-Erling Smørgrav { 500fc6e9e65SDag-Erling Smørgrav int cd; 5015aea254fSDag-Erling Smørgrav 5025aea254fSDag-Erling Smørgrav /* connect to server */ 503fc6e9e65SDag-Erling Smørgrav if ((cd = _ftp_cached_connect(url, flags)) == NULL) 5045aea254fSDag-Erling Smørgrav return NULL; 5055aea254fSDag-Erling Smørgrav 5065aea254fSDag-Erling Smørgrav /* initiate the transfer */ 50732425dafSDag-Erling Smørgrav return _ftp_transfer(cd, "RETR", url->doc, "r", url->offset, flags); 508f62e5228SDag-Erling Smørgrav } 509f62e5228SDag-Erling Smørgrav 5105aea254fSDag-Erling Smørgrav /* 5115aea254fSDag-Erling Smørgrav * Put file 5125aea254fSDag-Erling Smørgrav */ 513f62e5228SDag-Erling Smørgrav FILE * 514d8acd8dcSDag-Erling Smørgrav fetchPutFTP(struct url *url, char *flags) 5154ca1ab94SDag-Erling Smørgrav { 516fc6e9e65SDag-Erling Smørgrav int cd; 5175aea254fSDag-Erling Smørgrav 5185aea254fSDag-Erling Smørgrav /* connect to server */ 519fc6e9e65SDag-Erling Smørgrav if ((cd = _ftp_cached_connect(url, flags)) == NULL) 5205aea254fSDag-Erling Smørgrav return NULL; 5215aea254fSDag-Erling Smørgrav 5225aea254fSDag-Erling Smørgrav /* initiate the transfer */ 523fc6e9e65SDag-Erling Smørgrav return _ftp_transfer(cd, (flags && strchr(flags, 'a')) ? "APPE" : "STOR", 52432425dafSDag-Erling Smørgrav url->doc, "w", url->offset, flags); 5258e3986eaSDag-Erling Smørgrav } 526d8acd8dcSDag-Erling Smørgrav 5275aea254fSDag-Erling Smørgrav /* 5285aea254fSDag-Erling Smørgrav * Get file stats 5295aea254fSDag-Erling Smørgrav */ 530d8acd8dcSDag-Erling Smørgrav int 531d8acd8dcSDag-Erling Smørgrav fetchStatFTP(struct url *url, struct url_stat *us, char *flags) 532d8acd8dcSDag-Erling Smørgrav { 5335aea254fSDag-Erling Smørgrav char *ln, *s; 5345aea254fSDag-Erling Smørgrav struct tm tm; 5355aea254fSDag-Erling Smørgrav time_t t; 536fc6e9e65SDag-Erling Smørgrav int e, cd; 5375aea254fSDag-Erling Smørgrav 5380669702cSDag-Erling Smørgrav us->size = -1; 5390669702cSDag-Erling Smørgrav us->atime = us->mtime = 0; 5400669702cSDag-Erling Smørgrav 5415aea254fSDag-Erling Smørgrav /* connect to server */ 542fc6e9e65SDag-Erling Smørgrav if ((cd = _ftp_cached_connect(url, flags)) == NULL) 5435aea254fSDag-Erling Smørgrav return -1; 5445aea254fSDag-Erling Smørgrav 5455aea254fSDag-Erling Smørgrav /* change directory */ 5465aea254fSDag-Erling Smørgrav if (((s = strrchr(url->doc, '/')) != NULL) && (s != url->doc)) { 5475aea254fSDag-Erling Smørgrav *s = 0; 548fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "CWD %s", url->doc)) != FTP_FILE_ACTION_OK) { 5495aea254fSDag-Erling Smørgrav *s = '/'; 5505aea254fSDag-Erling Smørgrav goto ouch; 5515aea254fSDag-Erling Smørgrav } 5525aea254fSDag-Erling Smørgrav *s++ = '/'; 5535aea254fSDag-Erling Smørgrav } else { 554fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "CWD /")) != FTP_FILE_ACTION_OK) 5555aea254fSDag-Erling Smørgrav goto ouch; 5565aea254fSDag-Erling Smørgrav } 5575aea254fSDag-Erling Smørgrav 5585aea254fSDag-Erling Smørgrav /* s now points to file name */ 5595aea254fSDag-Erling Smørgrav 560fc6e9e65SDag-Erling Smørgrav if (_ftp_cmd(cd, "SIZE %s", s) != FTP_FILE_STATUS) 5615aea254fSDag-Erling Smørgrav goto ouch; 562fc6e9e65SDag-Erling Smørgrav for (ln = last_reply + 4; *ln && isspace(*ln); ln++) 5635aea254fSDag-Erling Smørgrav /* nothing */ ; 5645aea254fSDag-Erling Smørgrav for (us->size = 0; *ln && isdigit(*ln); ln++) 5655aea254fSDag-Erling Smørgrav us->size = us->size * 10 + *ln - '0'; 5665aea254fSDag-Erling Smørgrav if (*ln && !isspace(*ln)) { 567fc6e9e65SDag-Erling Smørgrav _ftp_seterr(999); 5685aea254fSDag-Erling Smørgrav return -1; 5695aea254fSDag-Erling Smørgrav } 57089474d12SDag-Erling Smørgrav DEBUG(fprintf(stderr, "size: [\033[1m%lld\033[m]\n", us->size)); 5715aea254fSDag-Erling Smørgrav 572fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "MDTM %s", s)) != FTP_FILE_STATUS) 5735aea254fSDag-Erling Smørgrav goto ouch; 574fc6e9e65SDag-Erling Smørgrav for (ln = last_reply + 4; *ln && isspace(*ln); ln++) 5755aea254fSDag-Erling Smørgrav /* nothing */ ; 57689474d12SDag-Erling Smørgrav e = 999; 57789474d12SDag-Erling Smørgrav switch (strspn(ln, "0123456789")) { 57889474d12SDag-Erling Smørgrav case 14: 57989474d12SDag-Erling Smørgrav break; 58089474d12SDag-Erling Smørgrav case 15: 58189474d12SDag-Erling Smørgrav ln++; 58289474d12SDag-Erling Smørgrav ln[0] = '2'; 58389474d12SDag-Erling Smørgrav ln[1] = '0'; 58489474d12SDag-Erling Smørgrav break; 58589474d12SDag-Erling Smørgrav default: 58689474d12SDag-Erling Smørgrav goto ouch; 58789474d12SDag-Erling Smørgrav } 58889474d12SDag-Erling Smørgrav if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", 5895aea254fSDag-Erling Smørgrav &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 59089474d12SDag-Erling Smørgrav &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) 59189474d12SDag-Erling Smørgrav goto ouch; 5925aea254fSDag-Erling Smørgrav tm.tm_mon--; 5935aea254fSDag-Erling Smørgrav tm.tm_year -= 1900; 5945aea254fSDag-Erling Smørgrav tm.tm_isdst = -1; 59589474d12SDag-Erling Smørgrav t = timegm(&tm); 5965d32c97cSDag-Erling Smørgrav if (t == (time_t)-1) 5975d32c97cSDag-Erling Smørgrav t = time(NULL); 5985d32c97cSDag-Erling Smørgrav us->mtime = t; 5995d32c97cSDag-Erling Smørgrav us->atime = t; 60089474d12SDag-Erling Smørgrav DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d " 60189474d12SDag-Erling Smørgrav "%02d:%02d:%02d\033[m]\n", 60289474d12SDag-Erling Smørgrav tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 60389474d12SDag-Erling Smørgrav tm.tm_hour, tm.tm_min, tm.tm_sec)); 6045aea254fSDag-Erling Smørgrav return 0; 6055aea254fSDag-Erling Smørgrav 6065aea254fSDag-Erling Smørgrav ouch: 607fc6e9e65SDag-Erling Smørgrav if (e != -1) 6085aea254fSDag-Erling Smørgrav _ftp_seterr(e); 609d8acd8dcSDag-Erling Smørgrav return -1; 610d8acd8dcSDag-Erling Smørgrav } 611ce71b736SDag-Erling Smørgrav 612ce71b736SDag-Erling Smørgrav /* 613ce71b736SDag-Erling Smørgrav * List a directory 614ce71b736SDag-Erling Smørgrav */ 615ce71b736SDag-Erling Smørgrav extern void warnx(char *, ...); 616ce71b736SDag-Erling Smørgrav struct url_ent * 617ce71b736SDag-Erling Smørgrav fetchListFTP(struct url *url, char *flags) 618ce71b736SDag-Erling Smørgrav { 619ce71b736SDag-Erling Smørgrav warnx("fetchListFTP(): not implemented"); 620ce71b736SDag-Erling Smørgrav return NULL; 621ce71b736SDag-Erling Smørgrav } 622