1 /* $NetBSD: ftp.c,v 1.35 2010/03/21 16:48:43 joerg Exp $ */
3 * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
4 * Copyright (c) 2008, 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer
12 * in this position and unchanged.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * $FreeBSD: ftp.c,v 1.101 2008/01/23 20:57:59 des Exp $
34 * Portions of this code were taken from or based on ftpio.c:
36 * ----------------------------------------------------------------------------
37 * "THE BEER-WARE LICENSE" (Revision 42):
38 * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
39 * can do whatever you want with this stuff. If we meet some day, and you think
40 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
41 * ----------------------------------------------------------------------------
45 * Dag-Erling Coïdan Smørgrav
48 * Incorporated into libfetch
53 * Turned inside out. Now returns xfers as new file ids, not as a special
56 * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $
61 /* Keep this down to Linux, it can create surprises else where. */
68 #if !defined(NETBSD) && !defined(__minix)
72 #include <sys/types.h>
73 #include <sys/socket.h>
75 #include <netinet/in.h>
76 #include <arpa/inet.h>
81 #if defined(HAVE_INTTYPES_H) || defined(NETBSD)
85 #if !defined(NETBSD) && !defined(__minix)
86 #include <nbcompat/netdb.h>
87 #include <nbcompat/stdio.h>
100 #define FTP_ANONYMOUS_USER "anonymous"
102 #define FTP_CONNECTION_ALREADY_OPEN 125
103 #define FTP_OPEN_DATA_CONNECTION 150
105 #define FTP_FILE_STATUS 213
106 #define FTP_SERVICE_READY 220
107 #define FTP_TRANSFER_COMPLETE 226
108 #define FTP_PASSIVE_MODE 227
109 #define FTP_LPASSIVE_MODE 228
110 #define FTP_EPASSIVE_MODE 229
111 #define FTP_LOGGED_IN 230
112 #define FTP_FILE_ACTION_OK 250
113 #define FTP_DIRECTORY_CREATED 257 /* multiple meanings */
114 #define FTP_FILE_CREATED 257 /* multiple meanings */
115 #define FTP_WORKING_DIRECTORY 257 /* multiple meanings */
116 #define FTP_NEED_PASSWORD 331
117 #define FTP_NEED_ACCOUNT 332
118 #define FTP_FILE_OK 350
119 #define FTP_SYNTAX_ERROR 500
120 #define FTP_PROTOCOL_ERROR 999
122 #define isftpreply(foo) \
123 (isdigit((unsigned char)foo[0]) && \
124 isdigit((unsigned char)foo[1]) && \
125 isdigit((unsigned char)foo[2]) && \
126 (foo[3] == ' ' || foo[3] == '\0'))
127 #define isftpinfo(foo) \
128 (isdigit((unsigned char)foo[0]) && \
129 isdigit((unsigned char)foo[1]) && \
130 isdigit((unsigned char)foo[2]) && \
133 #define MINBUFSIZE 4096
137 * Translate IPv4 mapped IPv6 address to IPv4 address
140 unmappedaddr(struct sockaddr_in6
*sin6
, socklen_t
*len
)
142 struct sockaddr_in
*sin4
;
146 if (sin6
->sin6_family
!= AF_INET6
||
147 !IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
))
149 sin4
= (struct sockaddr_in
*)sin6
;
150 addr
= *(uint32_t *)&sin6
->sin6_addr
.s6_addr
[12];
151 port
= sin6
->sin6_port
;
152 memset(sin4
, 0, sizeof(struct sockaddr_in
));
153 sin4
->sin_addr
.s_addr
= addr
;
154 sin4
->sin_port
= port
;
155 sin4
->sin_family
= AF_INET
;
156 *len
= sizeof(struct sockaddr_in
);
158 sin4
->sin_len
= sizeof(struct sockaddr_in
);
164 * Get server response
167 ftp_chkerr(conn_t
*conn
)
169 if (fetch_getln(conn
) == -1) {
173 if (isftpinfo(conn
->buf
)) {
174 while (conn
->buflen
&& !isftpreply(conn
->buf
)) {
175 if (fetch_getln(conn
) == -1) {
182 while (conn
->buflen
&&
183 isspace((unsigned char)conn
->buf
[conn
->buflen
- 1]))
185 conn
->buf
[conn
->buflen
] = '\0';
187 if (!isftpreply(conn
->buf
)) {
188 ftp_seterr(FTP_PROTOCOL_ERROR
);
192 conn
->err
= (conn
->buf
[0] - '0') * 100
193 + (conn
->buf
[1] - '0') * 10
194 + (conn
->buf
[2] - '0');
200 * Send a command and check reply
204 ftp_cmd(conn_t
*conn
, const char *fmt
, ...)
212 len
= vasprintf(&msg
, fmt
, ap
);
221 r
= fetch_write(conn
, msg
, len
);
229 return (ftp_chkerr(conn
));
233 ftp_cmd(conn_t
*conn
, const char *fmt
, ...)
237 char msg
[MINBUFSIZE
];
241 len
= vsnprintf(&msg
[0], MINBUFSIZE
, fmt
, ap
);
244 if (len
>= MINBUFSIZE
) {
250 r
= fetch_write(conn
, msg
, len
);
257 return (ftp_chkerr(conn
));
261 * Return a pointer to the filename part of a path
264 ftp_filename(const char *file
, int *len
, int *type
, int subdir
)
268 if ((s
= strrchr(file
, '/')) == NULL
|| subdir
)
273 if (*len
> 7 && strncmp(s
+ *len
- 7, ";type=", 6) == 0) {
283 * Get current working directory from the reply to a CWD, PWD or CDUP
287 ftp_pwd(conn_t
*conn
, char **pwd
)
289 char *src
, *dst
, *end
;
292 if (conn
->err
!= FTP_WORKING_DIRECTORY
&&
293 conn
->err
!= FTP_FILE_ACTION_OK
)
294 return (FTP_PROTOCOL_ERROR
);
295 end
= conn
->buf
+ conn
->buflen
;
297 if (src
>= end
|| *src
++ != '"')
298 return (FTP_PROTOCOL_ERROR
);
299 *pwd
= malloc(end
- src
+ 1);
301 return (FTP_PROTOCOL_ERROR
);
302 for (q
= 0, dst
= *pwd
; src
< end
; ++src
) {
303 if (!q
&& *src
== '"')
305 else if (q
&& *src
!= '"')
316 return (FTP_PROTOCOL_ERROR
);
322 * Change working directory to the directory that contains the specified
326 ftp_cwd(conn_t
*conn
, const char *path
, int subdir
)
328 const char *beg
, *end
;
338 /* Simple case: still in the home directory and no directory change. */
339 if (conn
->ftp_home
== NULL
&& strchr(path
, '/') == NULL
&&
340 (!subdir
|| *path
== '\0'))
343 if ((e
= ftp_cmd(conn
, "PWD\r\n")) != FTP_WORKING_DIRECTORY
||
344 (e
= ftp_pwd(conn
, &pwd
)) != FTP_OK
) {
348 if (conn
->ftp_home
== NULL
&& (conn
->ftp_home
= strdup(pwd
)) == NULL
) {
354 while (path
[1] == '/')
357 } else if (strcmp(conn
->ftp_home
, "/") == 0) {
358 dst
= strdup(path
- 1);
361 asprintf(&dst
, "%s/%s", conn
->ftp_home
, path
);
363 if((dst
= malloc(sizeof(char)*MINBUFSIZE
)) != NULL
) {
364 len
= snprintf(dst
, MINBUFSIZE
, "%s/%s", conn
->ftp_home
, path
);
366 if(len
>= MINBUFSIZE
) {
380 end
= dst
+ strlen(dst
);
382 end
= strrchr(dst
, '/');
387 /* Look for a common prefix between PWD and dir to fetch. */
388 for (i
= 0; i
<= len
&& i
<= end
- dst
; ++i
)
389 if (pwd
[i
] != dst
[i
])
391 /* Keep going up a dir until we have a matching prefix. */
392 if (strcmp(pwd
, "/") == 0)
394 if (pwd
[i
] == '\0' && (dst
[i
- 1] == '/' || dst
[i
] == '/'))
397 if ((e
= ftp_cmd(conn
, "CDUP\r\n")) != FTP_FILE_ACTION_OK
||
398 (e
= ftp_cmd(conn
, "PWD\r\n")) != FTP_WORKING_DIRECTORY
||
399 (e
= ftp_pwd(conn
, &pwd
)) != FTP_OK
) {
407 #ifdef FTP_COMBINE_CWDS
408 /* Skip leading slashes, even "////". */
409 for (beg
= dst
+ i
; beg
< end
&& *beg
== '/'; ++beg
, ++i
)
412 /* If there is no trailing dir, we're already there. */
418 /* Change to the directory all in one chunk (e.g., foo/bar/baz). */
419 e
= ftp_cmd(conn
, "CWD %.*s\r\n", (int)(end
- beg
), beg
);
420 if (e
== FTP_FILE_ACTION_OK
) {
424 #endif /* FTP_COMBINE_CWDS */
426 /* That didn't work so go back to legacy behavior (multiple CWDs). */
427 for (beg
= dst
+ i
; beg
< end
; beg
= dst
+ i
+ 1) {
430 for (++i
; dst
+ i
< end
&& dst
[i
] != '/'; ++i
)
432 e
= ftp_cmd(conn
, "CWD %.*s\r\n", dst
+ i
- beg
, beg
);
433 if (e
!= FTP_FILE_ACTION_OK
) {
444 * Set transfer mode and data type
447 ftp_mode_type(conn_t
*conn
, int mode
, int type
)
458 return (FTP_PROTOCOL_ERROR
);
460 if ((e
= ftp_cmd(conn
, "MODE %c\r\n", mode
)) != FTP_OK
) {
463 * Stream mode is supposed to be the default - so
464 * much so that some servers not only do not
465 * support any other mode, but do not support the
466 * MODE command at all.
468 * If "MODE S" fails, it is unlikely that we
469 * previously succeeded in setting a different
470 * mode. Therefore, we simply hope that the
471 * server is already in the correct mode, and
472 * silently ignore the failure.
492 /* can't handle yet */
494 return (FTP_PROTOCOL_ERROR
);
496 if ((e
= ftp_cmd(conn
, "TYPE %c\r\n", type
)) != FTP_OK
)
503 * Request and parse file stats
506 ftp_stat(conn_t
*conn
, const char *file
, struct url_stat
*us
)
509 const char *filename
;
510 int filenamelen
, type
;
516 us
->atime
= us
->mtime
= 0;
518 filename
= ftp_filename(file
, &filenamelen
, &type
, 0);
520 if ((e
= ftp_mode_type(conn
, 0, type
)) != FTP_OK
) {
525 e
= ftp_cmd(conn
, "SIZE %.*s\r\n", filenamelen
, filename
);
526 if (e
!= FTP_FILE_STATUS
) {
530 for (ln
= conn
->buf
+ 4; *ln
&& isspace((unsigned char)*ln
); ln
++)
532 for (us
->size
= 0; *ln
&& isdigit((unsigned char)*ln
); ln
++)
533 us
->size
= us
->size
* 10 + *ln
- '0';
534 if (*ln
&& !isspace((unsigned char)*ln
)) {
535 ftp_seterr(FTP_PROTOCOL_ERROR
);
542 e
= ftp_cmd(conn
, "MDTM %.*s\r\n", filenamelen
, filename
);
543 if (e
!= FTP_FILE_STATUS
) {
547 for (ln
= conn
->buf
+ 4; *ln
&& isspace((unsigned char)*ln
); ln
++)
549 switch (strspn(ln
, "0123456789")) {
558 ftp_seterr(FTP_PROTOCOL_ERROR
);
561 if (sscanf(ln
, "%04d%02d%02d%02d%02d%02d",
562 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
563 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
564 ftp_seterr(FTP_PROTOCOL_ERROR
);
580 * I/O functions for FTP
583 conn_t
*cconn
; /* Control connection */
584 conn_t
*dconn
; /* Data connection */
585 int dir
; /* Direction */
586 int eof
; /* EOF reached */
587 int err
; /* Error code */
590 static ssize_t
ftp_readfn(void *, void *, size_t);
591 static ssize_t
ftp_writefn(void *, const void *, size_t);
592 static void ftp_closefn(void *);
595 ftp_readfn(void *v
, void *buf
, size_t len
)
600 io
= (struct ftpio
*)v
;
605 if (io
->cconn
== NULL
|| io
->dconn
== NULL
|| io
->dir
== O_WRONLY
) {
615 r
= fetch_read(io
->dconn
, buf
, len
);
628 ftp_writefn(void *v
, const void *buf
, size_t len
)
633 io
= (struct ftpio
*)v
;
638 if (io
->cconn
== NULL
|| io
->dconn
== NULL
|| io
->dir
== O_RDONLY
) {
646 w
= fetch_write(io
->dconn
, buf
, len
);
655 ftp_disconnect(conn_t
*conn
)
657 ftp_cmd(conn
, "QUIT\r\n");
658 return fetch_close(conn
);
667 io
= (struct ftpio
*)v
;
674 if (io
->cconn
== NULL
|| io
->dconn
== NULL
) {
678 fetch_close(io
->dconn
);
681 r
= ftp_chkerr(io
->cconn
);
682 fetch_cache_put(io
->cconn
, ftp_disconnect
);
688 ftp_setup(conn_t
*cconn
, conn_t
*dconn
, int mode
)
693 if (cconn
== NULL
|| dconn
== NULL
)
695 if ((io
= malloc(sizeof(*io
))) == NULL
)
700 io
->eof
= io
->err
= 0;
701 f
= fetchIO_unopen(io
, ftp_readfn
, ftp_writefn
, ftp_closefn
);
711 ftp_transfer(conn_t
*conn
, const char *oper
, const char *file
, const char *op_arg
,
712 int mode
, off_t offset
, const char *flags
)
715 struct sockaddr_storage ss
;
717 struct sockaddr_in6 sin6
;
718 struct sockaddr_in sin4
;
720 const char *bindaddr
;
721 const char *filename
;
722 int filenamelen
, type
;
723 int low
, pasv
, verbose
;
730 low
= CHECK_FLAG('l');
731 pasv
= !CHECK_FLAG('a');
732 verbose
= CHECK_FLAG('v');
736 pasv
= ((s
= getenv("FTP_PASSIVE_MODE")) != NULL
&&
737 strncasecmp(s
, "no", 2) != 0);
739 /* isolate filename */
740 filename
= ftp_filename(file
, &filenamelen
, &type
, op_arg
!= NULL
);
742 /* set transfer mode and data type */
743 if ((e
= ftp_mode_type(conn
, 0, type
)) != FTP_OK
)
746 /* find our own address, bind, and listen */
748 if (getsockname(conn
->sd
, &u
.sa
, &l
) == -1)
751 if (u
.ss
.ss_family
== AF_INET6
)
752 unmappedaddr(&u
.sin6
, &l
);
757 /* open data socket */
758 if ((sd
= socket(u
.ss
.ss_family
, SOCK_STREAM
, IPPROTO_TCP
)) == -1) {
764 unsigned char addr
[64];
769 /* send PASV command */
771 fetch_info("setting passive mode");
772 switch (u
.ss
.ss_family
) {
774 if ((e
= ftp_cmd(conn
, "PASV\r\n")) != FTP_PASSIVE_MODE
)
779 if ((e
= ftp_cmd(conn
, "EPSV\r\n")) != FTP_EPASSIVE_MODE
) {
782 if ((e
= ftp_cmd(conn
, "LPSV\r\n")) !=
789 e
= FTP_PROTOCOL_ERROR
; /* XXX: error code should be prepared */
794 * Find address and port number. The reply to the PASV command
795 * is IMHO the one and only weak point in the FTP protocol.
799 case FTP_PASSIVE_MODE
:
800 case FTP_LPASSIVE_MODE
:
801 for (p
= ln
+ 3; *p
&& !isdigit((unsigned char)*p
); p
++)
804 e
= FTP_PROTOCOL_ERROR
;
807 l
= (e
== FTP_PASSIVE_MODE
? 6 : 21);
808 for (i
= 0; *p
&& i
< l
; i
++, p
++)
809 addr
[i
] = strtol(p
, &p
, 10);
811 e
= FTP_PROTOCOL_ERROR
;
815 case FTP_EPASSIVE_MODE
:
816 for (p
= ln
+ 3; *p
&& *p
!= '('; p
++)
819 e
= FTP_PROTOCOL_ERROR
;
823 if (sscanf(p
, "%c%c%c%d%c", &addr
[0], &addr
[1], &addr
[2],
824 &port
, &addr
[3]) != 5 ||
825 addr
[0] != addr
[1] ||
826 addr
[0] != addr
[2] || addr
[0] != addr
[3]) {
827 e
= FTP_PROTOCOL_ERROR
;
831 case FTP_SYNTAX_ERROR
:
833 fetch_info("passive mode failed");
834 /* Close socket and retry with passive mode. */
841 /* seek to required offset */
843 if (ftp_cmd(conn
, "REST %lu\r\n", (unsigned long)offset
) != FTP_FILE_OK
)
846 /* construct sockaddr for data socket */
848 if (getpeername(conn
->sd
, &u
.sa
, &l
) == -1)
851 if (u
.ss
.ss_family
== AF_INET6
)
852 unmappedaddr(&u
.sin6
, &l
);
854 switch (u
.ss
.ss_family
) {
857 if (e
== FTP_EPASSIVE_MODE
)
858 u
.sin6
.sin6_port
= htons(port
);
860 memcpy(&u
.sin6
.sin6_addr
, addr
+ 2, 16);
861 memcpy(&u
.sin6
.sin6_port
, addr
+ 19, 2);
866 if (e
== FTP_EPASSIVE_MODE
)
867 u
.sin4
.sin_port
= htons(port
);
869 memcpy(&u
.sin4
.sin_addr
, addr
, 4);
870 memcpy(&u
.sin4
.sin_port
, addr
+ 4, 2);
874 e
= FTP_PROTOCOL_ERROR
; /* XXX: error code should be prepared */
878 /* connect to data port */
880 fetch_info("opening data connection");
881 bindaddr
= getenv("FETCH_BIND_ADDRESS");
882 if (bindaddr
!= NULL
&& *bindaddr
!= '\0' &&
883 fetch_bind(sd
, u
.ss
.ss_family
, bindaddr
) != 0)
885 if (connect(sd
, &u
.sa
, l
) == -1)
888 /* make the server initiate the transfer */
890 fetch_info("initiating transfer");
892 e
= ftp_cmd(conn
, "%s%s%s\r\n", oper
, *op_arg
? " " : "", op_arg
);
894 e
= ftp_cmd(conn
, "%s %.*s\r\n", oper
,
895 filenamelen
, filename
);
896 if (e
!= FTP_CONNECTION_ALREADY_OPEN
&& e
!= FTP_OPEN_DATA_CONNECTION
)
902 #if defined(IPV6_PORTRANGE) || defined(IP_PORTRANGE)
908 char hname
[INET6_ADDRSTRLEN
];
911 switch (u
.ss
.ss_family
) {
914 u
.sin6
.sin6_port
= 0;
915 #ifdef IPV6_PORTRANGE
916 arg
= low
? IPV6_PORTRANGE_DEFAULT
: IPV6_PORTRANGE_HIGH
;
917 if (setsockopt(sd
, IPPROTO_IPV6
, IPV6_PORTRANGE
,
918 (char *)&arg
, sizeof(arg
)) == -1)
926 arg
= low
? IP_PORTRANGE_DEFAULT
: IP_PORTRANGE_HIGH
;
927 if (setsockopt(sd
, IPPROTO_IP
, IP_PORTRANGE
,
928 (char *)&arg
, sizeof(arg
)) == -1)
934 fetch_info("binding data socket");
935 if (bind(sd
, &u
.sa
, l
) == -1)
937 if (listen(sd
, 1) == -1)
940 /* find what port we're on and tell the server */
941 if (getsockname(sd
, &u
.sa
, &l
) == -1)
943 switch (u
.ss
.ss_family
) {
945 a
= ntohl(u
.sin4
.sin_addr
.s_addr
);
946 p
= ntohs(u
.sin4
.sin_port
);
947 e
= ftp_cmd(conn
, "PORT %d,%d,%d,%d,%d,%d\r\n",
948 (a
>> 24) & 0xff, (a
>> 16) & 0xff,
949 (a
>> 8) & 0xff, a
& 0xff,
950 (p
>> 8) & 0xff, p
& 0xff);
954 #define UC(b) (((int)b)&0xff)
956 u
.sin6
.sin6_scope_id
= 0;
957 if (getnameinfo(&u
.sa
, l
,
958 hname
, sizeof(hname
),
959 NULL
, 0, NI_NUMERICHOST
) == 0) {
960 e
= ftp_cmd(conn
, "EPRT |%d|%s|%d|\r\n", 2, hname
,
961 htons(u
.sin6
.sin6_port
));
966 ap
= (char *)&u
.sin6
.sin6_addr
;
968 "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\r\n",
970 UC(ap
[0]), UC(ap
[1]), UC(ap
[2]), UC(ap
[3]),
971 UC(ap
[4]), UC(ap
[5]), UC(ap
[6]), UC(ap
[7]),
972 UC(ap
[8]), UC(ap
[9]), UC(ap
[10]), UC(ap
[11]),
973 UC(ap
[12]), UC(ap
[13]), UC(ap
[14]), UC(ap
[15]),
975 (ntohs(u
.sin6
.sin6_port
) >> 8) & 0xff,
976 ntohs(u
.sin6
.sin6_port
) & 0xff);
981 e
= FTP_PROTOCOL_ERROR
; /* XXX: error code should be prepared */
988 /* seek to required offset */
990 if (ftp_cmd(conn
, "REST %llu\r\n", (unsigned long long)offset
) != FTP_FILE_OK
)
993 /* seek to required offset */
995 if (ftp_cmd(conn
, "REST %lu\r\n", (unsigned long)offset
) != FTP_FILE_OK
)
999 /* make the server initiate the transfer */
1001 fetch_info("initiating transfer");
1003 e
= ftp_cmd(conn
, "%s%s%s\r\n", oper
, *op_arg
? " " : "", op_arg
);
1005 e
= ftp_cmd(conn
, "%s %.*s\r\n", oper
,
1006 filenamelen
, filename
);
1007 if (e
!= FTP_CONNECTION_ALREADY_OPEN
&& e
!= FTP_OPEN_DATA_CONNECTION
)
1010 /* accept the incoming connection and go to town */
1011 if ((d
= accept(sd
, NULL
, NULL
)) == -1)
1017 if ((df
= ftp_setup(conn
, fetch_reopen(sd
), mode
)) == NULL
)
1039 ftp_authenticate(conn_t
*conn
, struct url
*url
, struct url
*purl
)
1041 const char *user
, *pwd
, *login_name
;
1042 char pbuf
[URL_USERLEN
+ 1 + URL_HOSTLEN
+ 1];
1045 /* XXX FTP_AUTH, and maybe .netrc */
1047 /* send user name and password */
1048 if (url
->user
[0] == '\0')
1049 fetch_netrc_auth(url
);
1052 user
= getenv("FTP_LOGIN");
1053 if (user
== NULL
|| *user
== '\0')
1054 user
= FTP_ANONYMOUS_USER
;
1055 if (purl
&& url
->port
== fetch_default_port(url
->scheme
))
1056 e
= ftp_cmd(conn
, "USER %s@%s\r\n", user
, url
->host
);
1058 e
= ftp_cmd(conn
, "USER %s@%s@%d\r\n", user
, url
->host
, url
->port
);
1060 e
= ftp_cmd(conn
, "USER %s\r\n", user
);
1062 /* did the server request a password? */
1063 if (e
== FTP_NEED_PASSWORD
) {
1066 pwd
= getenv("FTP_PASSWORD");
1067 if (pwd
== NULL
|| *pwd
== '\0') {
1068 if ((login_name
= getlogin()) == 0)
1069 login_name
= FTP_ANONYMOUS_USER
;
1070 if ((len
= snprintf(pbuf
, URL_USERLEN
+ 2, "%s@", login_name
)) < 0)
1072 else if (len
> URL_USERLEN
+ 1)
1073 len
= URL_USERLEN
+ 1;
1074 gethostname(pbuf
+ len
, sizeof(pbuf
) - len
);
1075 /* MAXHOSTNAMELEN can differ from URL_HOSTLEN + 1 */
1076 pbuf
[sizeof(pbuf
) - 1] = '\0';
1079 e
= ftp_cmd(conn
, "PASS %s\r\n", pwd
);
1086 * Log on to FTP server
1089 ftp_connect(struct url
*url
, struct url
*purl
, const char *flags
)
1092 int e
, direct
, verbose
;
1099 direct
= CHECK_FLAG('d');
1100 verbose
= CHECK_FLAG('v');
1101 if (CHECK_FLAG('4'))
1104 else if (CHECK_FLAG('6'))
1110 /* check for proxy */
1112 /* XXX proxy authentication! */
1113 /* XXX connetion caching */
1115 purl
->port
= fetch_default_port(purl
->scheme
);
1117 conn
= fetch_connect(purl
, af
, verbose
);
1119 /* no proxy, go straight to target */
1121 url
->port
= fetch_default_port(url
->scheme
);
1123 while ((conn
= fetch_cache_get(url
, af
)) != NULL
) {
1124 e
= ftp_cmd(conn
, "NOOP\r\n");
1129 conn
= fetch_connect(url
, af
, verbose
);
1133 /* check connection */
1135 /* fetch_connect() has already set an error code */
1138 /* expect welcome message */
1139 if ((e
= ftp_chkerr(conn
)) != FTP_SERVICE_READY
)
1143 if ((e
= ftp_authenticate(conn
, url
, purl
)) != FTP_LOGGED_IN
)
1146 /* TODO: Request extended features supported, if any (RFC 3659). */
1159 * Check the proxy settings
1162 ftp_get_proxy(struct url
* url
, const char *flags
)
1167 if (flags
!= NULL
&& strchr(flags
, 'd') != NULL
)
1169 if (fetch_no_proxy_match(url
->host
))
1171 if (((p
= getenv("FTP_PROXY")) || (p
= getenv("ftp_proxy")) ||
1172 (p
= getenv("HTTP_PROXY")) || (p
= getenv("http_proxy"))) &&
1173 *p
&& (purl
= fetchParseURL(p
)) != NULL
) {
1174 if (!*purl
->scheme
) {
1175 if (getenv("FTP_PROXY") || getenv("ftp_proxy"))
1176 strcpy(purl
->scheme
, SCHEME_FTP
);
1178 strcpy(purl
->scheme
, SCHEME_HTTP
);
1181 purl
->port
= fetch_default_proxy_port(purl
->scheme
);
1182 if (strcasecmp(purl
->scheme
, SCHEME_FTP
) == 0 ||
1183 strcasecmp(purl
->scheme
, SCHEME_HTTP
) == 0)
1191 * Process an FTP request
1194 ftp_request(struct url
*url
, const char *op
, const char *op_arg
,
1195 struct url_stat
*us
, struct url
*purl
, const char *flags
)
1200 int if_modified_since
, oflag
;
1201 struct url_stat local_us
;
1203 /* check if we should use HTTP instead */
1204 if (purl
&& strcasecmp(purl
->scheme
, SCHEME_HTTP
) == 0) {
1205 if (strcmp(op
, "STAT") == 0)
1206 return (http_request(url
, "HEAD", us
, purl
, flags
));
1207 else if (strcmp(op
, "RETR") == 0)
1208 return (http_request(url
, "GET", us
, purl
, flags
));
1210 * Our HTTP code doesn't support PUT requests yet, so try
1211 * a direct connection.
1215 /* connect to server */
1216 conn
= ftp_connect(url
, purl
, flags
);
1222 if ((path
= fetchUnquotePath(url
)) == NULL
) {
1227 /* change directory */
1228 if (ftp_cwd(conn
, path
, op_arg
!= NULL
) == -1) {
1233 if_modified_since
= CHECK_FLAG('i');
1234 if (if_modified_since
&& us
== NULL
)
1238 if (us
&& ftp_stat(conn
, path
, us
) == -1
1239 && fetchLastErrCode
!= FETCH_PROTO
1240 && fetchLastErrCode
!= FETCH_UNAVAIL
) {
1245 if (if_modified_since
&& url
->last_modified
> 0 &&
1246 url
->last_modified
>= us
->mtime
) {
1248 fetchLastErrCode
= FETCH_UNCHANGED
;
1249 snprintf(fetchLastErrString
, MAXERRSTRING
, "Unchanged");
1254 if (strcmp(op
, "STAT") == 0) {
1256 return fetchIO_unopen(NULL
, NULL
, NULL
, NULL
);
1258 if (strcmp(op
, "STOR") == 0 || strcmp(op
, "APPE") == 0)
1263 /* initiate the transfer */
1264 f
= (ftp_transfer(conn
, op
, path
, op_arg
, oflag
, url
->offset
, flags
));
1273 fetchXGetFTP(struct url
*url
, struct url_stat
*us
, const char *flags
)
1275 return (ftp_request(url
, "RETR", NULL
, us
, ftp_get_proxy(url
, flags
), flags
));
1282 fetchGetFTP(struct url
*url
, const char *flags
)
1284 return (fetchXGetFTP(url
, NULL
, flags
));
1291 fetchPutFTP(struct url
*url
, const char *flags
)
1293 return (ftp_request(url
, CHECK_FLAG('a') ? "APPE" : "STOR", NULL
, NULL
,
1294 ftp_get_proxy(url
, flags
), flags
));
1301 fetchStatFTP(struct url
*url
, struct url_stat
*us
, const char *flags
)
1305 f
= ftp_request(url
, "STAT", NULL
, us
, ftp_get_proxy(url
, flags
), flags
);
1316 fetchListFTP(struct url_list
*ue
, struct url
*url
, const char *pattern
, const char *flags
)
1319 char buf
[2 * PATH_MAX
], *eol
, *eos
;
1324 /* XXX What about proxies? */
1325 if (pattern
== NULL
|| strcmp(pattern
, "*") == 0)
1327 f
= ftp_request(url
, "NLST", pattern
, NULL
, ftp_get_proxy(url
, flags
), flags
);
1334 while ((len
= fetchIO_read(f
, buf
+ cur_off
, sizeof(buf
) - cur_off
)) > 0) {
1336 while ((eol
= memchr(buf
, '\n', cur_off
)) != NULL
) {
1337 if (len
== eol
- buf
)
1340 if (eol
[-1] == '\r')
1345 ret
= fetch_add_entry(ue
, url
, buf
, 0);
1348 cur_off
-= eol
- buf
+ 1;
1349 memmove(buf
, eol
+ 1, cur_off
);
1355 if (cur_off
!= 0 || len
< 0) {
1356 /* Not RFC conform, bail out. */