1 /* Internal "ftp" protocol implementation */
12 #include <sys/stat.h> /* For converting permissions to strings */
13 #include <sys/types.h>
14 #ifdef HAVE_SYS_SOCKET_H
15 #include <sys/socket.h>
21 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
24 /* We need to have it here. Stupid BSD. */
25 #ifdef HAVE_NETINET_IN_H
26 #include <netinet/in.h>
28 #ifdef HAVE_ARPA_INET_H
29 #include <arpa/inet.h>
34 #include "cache/cache.h"
35 #include "config/options.h"
36 #include "intl/gettext/libintl.h"
37 #include "main/select.h"
38 #include "main/module.h"
39 #include "network/connection.h"
40 #include "network/progress.h"
41 #include "network/socket.h"
42 #include "osdep/osdep.h"
43 #include "osdep/stat.h"
44 #include "protocol/auth/auth.h"
45 #include "protocol/common.h"
46 #include "protocol/ftp/ftp.h"
47 #include "protocol/ftp/parse.h"
48 #include "protocol/uri.h"
49 #include "util/conv.h"
50 #include "util/error.h"
51 #include "util/memory.h"
52 #include "util/string.h"
55 union option_info ftp_options
[] = {
56 INIT_OPT_TREE("protocol", N_("FTP"),
58 N_("FTP specific options.")),
60 INIT_OPT_TREE("protocol.ftp", N_("Proxy configuration"),
62 N_("FTP proxy configuration.")),
64 INIT_OPT_STRING("protocol.ftp.proxy", N_("Host and port-number"),
66 N_("Host and port-number (host:port) of the FTP proxy, "
67 "or blank. If it's blank, FTP_PROXY environment variable "
68 "is checked as well.")),
70 INIT_OPT_STRING("protocol.ftp", N_("Anonymous password"),
71 "anon_passwd", 0, "some@host.domain",
72 N_("FTP anonymous password to be sent.")),
74 INIT_OPT_BOOL("protocol.ftp", N_("Use passive mode (IPv4)"),
76 N_("Use PASV instead of PORT (passive vs active mode, "
79 INIT_OPT_BOOL("protocol.ftp", N_("Use passive mode (IPv6)"),
81 N_("Use EPSV instead of EPRT (passive vs active mode, "
83 #endif /* CONFIG_IPV6 */
88 struct module ftp_protocol_module
= struct_module(
89 /* name: */ N_("FTP"),
90 /* options: */ ftp_options
,
92 /* submodules: */ NULL
,
101 #define FTP_BUF_SIZE 16384
104 /* Types and structs */
106 struct ftp_connection_info
{
107 int pending_commands
; /* Num of commands queued */
108 int opc
; /* Total num of commands queued */
112 unsigned int dir
:1; /* Directory listing in progress */
113 unsigned int rest_sent
:1; /* Sent RESTore command */
114 unsigned int use_pasv
:1; /* Use PASV (yes or no) */
116 unsigned int use_epsv
:1; /* Use EPSV */
118 unsigned char ftp_buffer
[FTP_BUF_SIZE
];
119 unsigned char cmd_buffer
[1]; /* Must be last field !! */
124 static void ftp_login(struct socket
*);
125 static void ftp_send_retr_req(struct connection
*, struct connection_state
);
126 static void ftp_got_info(struct socket
*, struct read_buffer
*);
127 static void ftp_got_user_info(struct socket
*, struct read_buffer
*);
128 static void ftp_pass(struct connection
*);
129 static void ftp_pass_info(struct socket
*, struct read_buffer
*);
130 static void ftp_retr_file(struct socket
*, struct read_buffer
*);
131 static void ftp_got_final_response(struct socket
*, struct read_buffer
*);
132 static void got_something_from_data_connection(struct connection
*);
133 static void ftp_end_request(struct connection
*, struct connection_state
);
134 static struct ftp_connection_info
*add_file_cmd_to_str(struct connection
*);
135 static void ftp_data_accept(struct connection
*conn
);
137 /* Parse EPSV or PASV response for address and/or port.
138 * int *n should point to a sizeof(int) * 6 space.
139 * It returns zero on error or count of parsed numbers.
140 * It returns an error if:
141 * - there's more than 6 or less than 1 numbers.
142 * - a number is strictly greater than max.
144 * On success, array of integers *n is filled with numbers starting
145 * from end of array (ie. if we found one number, you can access it using
149 * Negative numbers aren't handled so -123 is taken as 123.
150 * We don't take care about separators.
153 parse_psv_resp(unsigned char *data
, int *n
, int max_value
)
155 unsigned char *p
= data
;
158 memset(n
, 0, 6 * sizeof(*n
));
160 if (*p
< ' ') return 0;
163 while (*p
>= ' ') p
++;
165 /* Ignore non-numeric ending chars. */
166 while (p
!= data
&& !isdigit(*p
)) p
--;
167 if (p
== data
) return 0;
172 /* Parse one number. */
173 while (p
!= data
&& isdigit(*p
)) {
174 n
[i
] += (*p
- '0') * x
;
175 if (n
[i
] > max_value
) return 0;
179 /* Ignore non-numeric chars. */
180 while (p
!= data
&& !isdigit(*p
)) p
--;
181 if (p
== data
) return (6 - i
);
182 /* Get the next one. */
189 /* Returns 0 if there's no numeric response, -1 if error, the positive response
190 * number otherwise. */
192 get_ftp_response(struct connection
*conn
, struct read_buffer
*rb
, int part
,
193 struct sockaddr_storage
*sa
, off_t
*est_length
)
196 unsigned char *num_end
;
201 eol
= memchr(rb
->data
, ASCII_LF
, rb
->length
);
204 pos
= eol
- rb
->data
;
207 response
= strtoul(rb
->data
, (char **) &num_end
, 10);
209 if (errno
|| num_end
!= rb
->data
+ 3 || response
< 100)
212 if (sa
&& response
== 227) { /* PASV response parsing. */
213 struct sockaddr_in
*s
= (struct sockaddr_in
*) sa
;
216 if (parse_psv_resp(num_end
, (int *) &n
, 255) != 6)
219 memset(s
, 0, sizeof(*s
));
220 s
->sin_family
= AF_INET
;
221 s
->sin_addr
.s_addr
= htonl((n
[0] << 24) + (n
[1] << 16) + (n
[2] << 8) + n
[3]);
222 s
->sin_port
= htons((n
[4] << 8) + n
[5]);
226 if (sa
&& response
== 229) { /* EPSV response parsing. */
228 struct sockaddr_in6
*s
= (struct sockaddr_in6
*) sa
;
229 int sal
= sizeof(*s
);
232 if (parse_psv_resp(num_end
, (int *) &n
, 65535) != 1) {
236 memset(s
, 0, sizeof(*s
));
237 if (getpeername(conn
->socket
->fd
, (struct sockaddr
*) sa
, &sal
)) {
240 s
->sin6_family
= AF_INET6
;
241 s
->sin6_port
= htons(n
[5]);
245 if (*num_end
== '-') {
248 for (i
= 0; i
< rb
->length
- 5; i
++)
249 if (rb
->data
[i
] == ASCII_LF
250 && !memcmp(rb
->data
+i
+1, rb
->data
, 3)
251 && rb
->data
[i
+4] == ' ') {
252 for (i
++; i
< rb
->length
; i
++)
253 if (rb
->data
[i
] == ASCII_LF
)
262 if (response
== 213 && est_length
) {
263 off_t size
= strtoull(num_end
+ 1, NULL
, 10);
271 kill_buffer_data(rb
, pos
+ 1);
273 if (!part
&& response
>= 100 && response
< 200) {
281 /* Initialize or continue ftp connection. */
283 ftp_protocol_handler(struct connection
*conn
)
285 if (!has_keepalive_connection(conn
)) {
286 make_connection(conn
->socket
, conn
->uri
, ftp_login
,
287 conn
->cache_mode
>= CACHE_MODE_FORCE_RELOAD
);
290 ftp_send_retr_req(conn
, connection_state(S_SENT
));
294 /* Send command, set connection state and free cmd string. */
296 send_cmd(struct connection
*conn
, struct string
*cmd
, void *callback
,
297 struct connection_state state
)
299 request_from_socket(conn
->socket
, cmd
->source
, cmd
->length
, state
,
300 SOCKET_RETRY_ONCLOSE
, callback
);
305 /* Check if this auth token really belongs to this URI. */
307 auth_user_matching_uri(struct auth_entry
*auth
, struct uri
*uri
)
309 if (!uri
->userlen
) /* Noone said it doesn't. */
311 return !c_strlcasecmp(auth
->user
, -1, uri
->user
, uri
->userlen
);
315 /* Kill the current connection and ask for a username/password for the next
318 prompt_username_pw(struct connection
*conn
)
321 conn
->cached
= get_cache_entry(conn
->uri
);
323 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
328 mem_free_set(&conn
->cached
->content_type
, stracpy("text/html"));
329 if (!conn
->cached
->content_type
) {
330 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
334 add_auth_entry(conn
->uri
, "FTP Login", NULL
, NULL
, 0);
336 abort_connection(conn
, connection_state(S_OK
));
339 /* Send USER command. */
341 ftp_login(struct socket
*socket
)
343 struct connection
*conn
= socket
->conn
;
345 struct auth_entry
* auth
;
347 auth
= find_auth(conn
->uri
);
349 if (!init_string(&cmd
)) {
350 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
354 add_to_string(&cmd
, "USER ");
355 if (conn
->uri
->userlen
) {
356 struct uri
*uri
= conn
->uri
;
358 add_bytes_to_string(&cmd
, uri
->user
, uri
->userlen
);
360 } else if (auth
&& auth
->valid
) {
361 add_to_string(&cmd
, auth
->user
);
364 add_to_string(&cmd
, "anonymous");
366 add_crlf_to_string(&cmd
);
368 send_cmd(conn
, &cmd
, (void *) ftp_got_info
, connection_state(S_SENT
));
371 /* Parse connection response. */
373 ftp_got_info(struct socket
*socket
, struct read_buffer
*rb
)
375 struct connection
*conn
= socket
->conn
;
376 int response
= get_ftp_response(conn
, rb
, 0, NULL
, NULL
);
378 if (response
== -1) {
379 abort_connection(conn
, connection_state(S_FTP_ERROR
));
384 read_from_socket(conn
->socket
, rb
, conn
->state
, ftp_got_info
);
388 /* RFC959 says that possible response codes on connection are:
389 * 120 Service ready in nnn minutes.
390 * 220 Service ready for new user.
391 * 421 Service not available, closing control connection. */
393 if (response
!= 220) {
394 /* TODO? Retry in case of ... ?? */
395 retry_connection(conn
, connection_state(S_FTP_UNAVAIL
));
399 ftp_got_user_info(socket
, rb
);
403 /* Parse USER response and send PASS command if needed. */
405 ftp_got_user_info(struct socket
*socket
, struct read_buffer
*rb
)
407 struct connection
*conn
= socket
->conn
;
408 int response
= get_ftp_response(conn
, rb
, 0, NULL
, NULL
);
410 if (response
== -1) {
411 abort_connection(conn
, connection_state(S_FTP_ERROR
));
416 read_from_socket(conn
->socket
, rb
, conn
->state
, ftp_got_user_info
);
420 /* RFC959 says that possible response codes for USER are:
421 * 230 User logged in, proceed.
422 * 331 User name okay, need password.
423 * 332 Need account for login.
424 * 421 Service not available, closing control connection.
425 * 500 Syntax error, command unrecognized.
426 * 501 Syntax error in parameters or arguments.
427 * 530 Not logged in. */
429 /* FIXME? Since ACCT command isn't implemented, login to a ftp server
430 * requiring it will fail (332). */
432 if (response
== 332 || response
>= 500) {
433 prompt_username_pw(conn
);
437 /* We don't require exact match here, as this is always error and some
438 * non-RFC compliant servers may return even something other than 421.
440 if (response
>= 400) {
441 abort_connection(conn
, connection_state(S_FTP_UNAVAIL
));
445 if (response
== 230) {
446 ftp_send_retr_req(conn
, connection_state(S_GETH
));
453 /* Send PASS command. */
455 ftp_pass(struct connection
*conn
)
458 struct auth_entry
*auth
;
460 auth
= find_auth(conn
->uri
);
462 if (!init_string(&cmd
)) {
463 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
467 add_to_string(&cmd
, "PASS ");
468 if (conn
->uri
->passwordlen
) {
469 struct uri
*uri
= conn
->uri
;
471 add_bytes_to_string(&cmd
, uri
->password
, uri
->passwordlen
);
473 } else if (auth
&& auth
->valid
) {
474 if (!auth_user_matching_uri(auth
, conn
->uri
)) {
475 prompt_username_pw(conn
);
478 add_to_string(&cmd
, auth
->password
);
481 add_to_string(&cmd
, get_opt_str("protocol.ftp.anon_passwd",
484 add_crlf_to_string(&cmd
);
486 send_cmd(conn
, &cmd
, (void *) ftp_pass_info
, connection_state(S_LOGIN
));
489 /* Parse PASS command response. */
491 ftp_pass_info(struct socket
*socket
, struct read_buffer
*rb
)
493 struct connection
*conn
= socket
->conn
;
494 int response
= get_ftp_response(conn
, rb
, 0, NULL
, NULL
);
496 if (response
== -1) {
497 abort_connection(conn
, connection_state(S_FTP_ERROR
));
502 read_from_socket(conn
->socket
, rb
, connection_state(S_LOGIN
),
507 /* RFC959 says that possible response codes for PASS are:
508 * 202 Command not implemented, superfluous at this site.
509 * 230 User logged in, proceed.
510 * 332 Need account for login.
511 * 421 Service not available, closing control connection.
512 * 500 Syntax error, command unrecognized.
513 * 501 Syntax error in parameters or arguments.
514 * 503 Bad sequence of commands.
515 * 530 Not logged in. */
517 if (response
== 332 || response
>= 500) {
518 /* If we didn't have a user, we tried anonymous. But it failed, so ask for a
519 * user and password */
520 prompt_username_pw(conn
);
524 if (response
>= 400) {
525 abort_connection(conn
, connection_state(S_FTP_UNAVAIL
));
529 ftp_send_retr_req(conn
, connection_state(S_GETH
));
532 /* Construct PORT command. */
534 add_portcmd_to_string(struct string
*string
, unsigned char *pc
)
536 /* From RFC 959: DATA PORT (PORT)
538 * The argument is a HOST-PORT specification for the data port
539 * to be used in data connection. There are defaults for both
540 * the user and server data ports, and under normal
541 * circumstances this command and its reply are not needed. If
542 * this command is used, the argument is the concatenation of a
543 * 32-bit internet host address and a 16-bit TCP port address.
544 * This address information is broken into 8-bit fields and the
545 * value of each field is transmitted as a decimal number (in
546 * character string representation). The fields are separated
547 * by commas. A port command would be:
549 * PORT h1,h2,h3,h4,p1,p2
551 * where h1 is the high order 8 bits of the internet host
553 add_to_string(string
, "PORT ");
554 add_long_to_string(string
, pc
[0]);
555 add_char_to_string(string
, ',');
556 add_long_to_string(string
, pc
[1]);
557 add_char_to_string(string
, ',');
558 add_long_to_string(string
, pc
[2]);
559 add_char_to_string(string
, ',');
560 add_long_to_string(string
, pc
[3]);
561 add_char_to_string(string
, ',');
562 add_long_to_string(string
, pc
[4]);
563 add_char_to_string(string
, ',');
564 add_long_to_string(string
, pc
[5]);
568 /* Construct EPRT command. */
570 add_eprtcmd_to_string(struct string
*string
, struct sockaddr_in6
*addr
)
572 unsigned char addr_str
[INET6_ADDRSTRLEN
];
574 inet_ntop(AF_INET6
, &addr
->sin6_addr
, addr_str
, INET6_ADDRSTRLEN
);
576 /* From RFC 2428: EPRT
578 * The format of EPRT is:
580 * EPRT<space><d><net-prt><d><net-addr><d><tcp-port><d>
585 * 1 Internet Protocol, Version 4 [Pos81a]
586 * 2 Internet Protocol, Version 6 [DH96] */
587 add_to_string(string
, "EPRT |2|");
588 add_to_string(string
, addr_str
);
589 add_char_to_string(string
, '|');
590 add_long_to_string(string
, ntohs(addr
->sin6_port
));
591 add_char_to_string(string
, '|');
595 /* Depending on options, get proper ftp data socket and command.
596 * It appends ftp command (either PASV,PORT,EPSV or EPRT) to @command
598 * When PORT or EPRT are used, related sockets are created.
599 * It returns 0 on error (data socket creation failure). */
601 get_ftp_data_socket(struct connection
*conn
, struct string
*command
)
603 struct ftp_connection_info
*ftp
= conn
->info
;
605 ftp
->use_pasv
= get_opt_bool("protocol.ftp.use_pasv", NULL
);
608 ftp
->use_epsv
= get_opt_bool("protocol.ftp.use_epsv", NULL
);
610 if (conn
->socket
->protocol_family
== EL_PF_INET6
) {
612 add_to_string(command
, "EPSV");
615 struct sockaddr_storage data_addr
;
618 memset(&data_addr
, 0, sizeof(data_addr
));
619 data_sock
= get_pasv_socket(conn
->socket
, &data_addr
);
620 if (data_sock
< 0) return 0;
622 conn
->data_socket
->fd
= data_sock
;
623 add_eprtcmd_to_string(command
,
624 (struct sockaddr_in6
*) &data_addr
);
631 add_to_string(command
, "PASV");
634 struct sockaddr_in sa
;
638 memset(pc
, 0, sizeof(pc
));
639 data_sock
= get_pasv_socket(conn
->socket
,
640 (struct sockaddr_storage
*) &sa
);
641 if (data_sock
< 0) return 0;
643 memcpy(pc
, &sa
.sin_addr
.s_addr
, 4);
644 memcpy(pc
+ 4, &sa
.sin_port
, 2);
645 conn
->data_socket
->fd
= data_sock
;
646 add_portcmd_to_string(command
, pc
);
650 add_crlf_to_string(command
);
655 /* Check if the file or directory name @s can be safely sent to the
656 * FTP server. To prevent command injection attacks, this function
657 * must reject CR LF sequences. */
659 is_ftp_pathname_safe(const struct string
*s
)
663 /* RFC 959 says the argument of CWD and RETR is a <pathname>,
664 * which consists of <char>s, "any of the 128 ASCII characters
665 * except <CR> and <LF>". So other control characters, such
666 * as 0x00 and 0x7F, are allowed here. Bytes 0x80...0xFF
667 * should not be allowed, but if we reject them, users will
668 * probably complain. */
669 for (i
= 0; i
< s
->length
; i
++) {
670 if (s
->source
[i
] == 0x0A || s
->source
[i
] == 0x0D)
676 /* Create passive socket and add appropriate announcing commands to str. Then
677 * go and retrieve appropriate object from server.
678 * Returns NULL if error. */
679 static struct ftp_connection_info
*
680 add_file_cmd_to_str(struct connection
*conn
)
683 struct ftp_connection_info
*ftp
= NULL
;
684 struct string command
= NULL_STRING
;
685 struct string ftp_data_command
= NULL_STRING
;
686 struct string pathname
= NULL_STRING
;
688 if (!conn
->uri
->data
) {
689 INTERNAL("conn->uri->data empty");
690 abort_connection(conn
, connection_state(S_INTERNAL
));
694 assert(conn
->info
== NULL
);
695 assert(conn
->done
== NULL
);
697 abort_connection(conn
, connection_state(S_INTERNAL
));
701 /* This will be reallocated below when we know how long the
702 * command string should be. Error handling could be
703 * simplified a little by allocating this initial structure on
704 * the stack, but it's several kilobytes long so that might be
706 ftp
= mem_calloc(1, sizeof(*ftp
));
708 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
712 /* conn->info and conn->done were asserted as NULL above. */
713 conn
->info
= ftp
; /* Freed when connection is destroyed. */
715 if (!init_string(&command
)
716 || !init_string(&ftp_data_command
)
717 || !init_string(&pathname
)) {
718 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
722 if (!get_ftp_data_socket(conn
, &ftp_data_command
)) {
723 INTERNAL("Ftp data socket failure");
724 abort_connection(conn
, connection_state(S_INTERNAL
));
728 if (!add_uri_to_string(&pathname
, conn
->uri
, URI_PATH
)) {
729 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
733 decode_uri_string(&pathname
);
734 if (!is_ftp_pathname_safe(&pathname
)) {
735 abort_connection(conn
, connection_state(S_BAD_URL
));
739 if (!conn
->uri
->datalen
740 || conn
->uri
->data
[conn
->uri
->datalen
- 1] == '/') {
741 /* Commands to get directory listing. */
744 ftp
->pending_commands
= 4;
746 if (!add_to_string(&command
, "TYPE A") /* ASCII */
747 || !add_crlf_to_string(&command
)
749 || !add_string_to_string(&command
, &ftp_data_command
)
751 || !add_to_string(&command
, "CWD ")
752 || !add_string_to_string(&command
, &pathname
)
753 || !add_crlf_to_string(&command
)
755 || !add_to_string(&command
, "LIST")
756 || !add_crlf_to_string(&command
)) {
757 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
764 /* Commands to get a file. */
767 ftp
->pending_commands
= 4;
769 if (!add_to_string(&command
, "TYPE I") /* BINARY */
770 || !add_crlf_to_string(&command
)
772 || !add_string_to_string(&command
, &ftp_data_command
)) {
773 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
777 if (conn
->from
|| conn
->progress
->start
> 0) {
778 const off_t offset
= conn
->from
780 : conn
->progress
->start
;
782 if (!add_to_string(&command
, "REST ")
783 || !add_long_to_string(&command
, offset
)
784 || !add_crlf_to_string(&command
)) {
785 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
790 ftp
->pending_commands
++;
793 if (!add_to_string(&command
, "SIZE ")
794 || !add_string_to_string(&command
, &pathname
)
795 || !add_crlf_to_string(&command
)) {
796 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
800 if (!add_to_string(&command
, "RETR ")
801 || !add_string_to_string(&command
, &pathname
)
802 || !add_crlf_to_string(&command
)) {
803 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
808 ftp
->opc
= ftp
->pending_commands
;
810 /* 1 byte is already reserved for cmd_buffer in struct ftp_connection_info. */
811 ftp
= mem_realloc(ftp
, sizeof(*ftp
) + command
.length
);
813 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
816 conn
->info
= ftp
; /* in case mem_realloc moved the buffer */
818 memcpy(ftp
->cmd_buffer
, command
.source
, command
.length
+ 1);
822 /* If @ok is false here, then abort_connection has already
823 * freed @ftp, which now is a dangling pointer. */
824 done_string(&pathname
);
825 done_string(&ftp_data_command
);
826 done_string(&command
);
827 return ok
? ftp
: NULL
;
831 send_it_line_by_line(struct connection
*conn
, struct string
*cmd
)
833 struct ftp_connection_info
*ftp
= conn
->info
;
834 unsigned char *nl
= strchr(ftp
->cmd_buffer
, '\n');
837 add_to_string(cmd
, ftp
->cmd_buffer
);
842 add_bytes_to_string(cmd
, ftp
->cmd_buffer
, nl
- ftp
->cmd_buffer
);
843 memmove(ftp
->cmd_buffer
, nl
, strlen(nl
) + 1);
846 /* Send commands to retrieve file or directory. */
848 ftp_send_retr_req(struct connection
*conn
, struct connection_state state
)
852 if (!init_string(&cmd
)) {
853 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
857 /* We don't save return value from add_file_cmd_to_str(), as it's saved
858 * in conn->info as well. */
859 if (!conn
->info
&& !add_file_cmd_to_str(conn
)) {
864 /* Send it line-by-line. */
865 send_it_line_by_line(conn
, &cmd
);
867 send_cmd(conn
, &cmd
, (void *) ftp_retr_file
, state
);
870 /* Parse RETR response and return file size or -1 on error. */
872 get_filesize_from_RETR(unsigned char *data
, int data_len
, int *resume
)
876 int pos_file_len
= 0;
878 /* Getting file size from text response.. */
879 /* 150 Opening BINARY mode data connection for hello-1.0-1.1.diff.gz (16452 bytes). */
882 for (pos
= 0; pos
< data_len
&& data
[pos
] != ASCII_LF
; pos
++)
883 if (data
[pos
] == '(')
886 if (!pos_file_len
|| pos_file_len
== data_len
- 1) {
887 /* Getting file size from ftp.task.gda.pl */
888 /* 150 5676.4 kbytes to download */
889 unsigned char tmp
= data
[data_len
- 1];
890 unsigned char *kbytes
;
894 data
[data_len
- 1] = '\0';
895 kbytes
= strstr(data
, "kbytes");
896 data
[data_len
- 1] = tmp
;
897 if (!kbytes
) return -1;
899 for (kbytes
-= 2; kbytes
>= data
; kbytes
--) {
900 if (*kbytes
== ' ') break;
902 if (*kbytes
!= ' ') return -1;
904 size
= strtod((const char *)kbytes
, &endptr
);
905 if (endptr
== (char *)kbytes
) return -1;
907 return (off_t
)(size
* 1024.0);
911 if (!isdigit(data
[pos_file_len
]))
914 for (pos
= pos_file_len
; pos
< data_len
; pos
++)
915 if (!isdigit(data
[pos
]))
921 for (; pos
< data_len
; pos
++)
922 if (data
[pos
] != ' ')
925 if (pos
+ 4 > data_len
)
928 if (c_strncasecmp(&data
[pos
], "byte", 4))
932 file_len
= (off_t
) strtoll(&data
[pos_file_len
], NULL
, 10);
933 if (errno
) return -1;
938 /* Connect to the host and port specified by a passive FTP server. */
940 ftp_data_connect(struct connection
*conn
, int pf
, struct sockaddr_storage
*sa
,
941 int size_of_sockaddr
)
945 if (conn
->data_socket
->fd
!= -1) {
946 /* The server maliciously sent multiple 227 or 229
947 * responses. Do not leak the previous data_socket. */
948 abort_connection(conn
, connection_state(S_FTP_ERROR
));
952 fd
= socket(pf
, SOCK_STREAM
, 0);
953 if (fd
< 0 || set_nonblocking_fd(fd
) < 0) {
954 abort_connection(conn
, connection_state(S_FTP_ERROR
));
958 set_ip_tos_throughput(fd
);
960 conn
->data_socket
->fd
= fd
;
961 /* XXX: We ignore connect() errors here. */
962 connect(fd
, (struct sockaddr
*) sa
, size_of_sockaddr
);
967 ftp_retr_file(struct socket
*socket
, struct read_buffer
*rb
)
969 struct connection
*conn
= socket
->conn
;
970 struct ftp_connection_info
*ftp
= conn
->info
;
974 if (ftp
->pending_commands
> 1) {
975 struct sockaddr_storage sa
;
977 response
= get_ftp_response(conn
, rb
, 0, &sa
, &size
);
979 if (response
== -1) {
980 abort_connection(conn
, connection_state(S_FTP_ERROR
));
985 read_from_socket(conn
->socket
, rb
,
986 connection_state(S_GETH
),
991 if (response
== 227) {
992 if (ftp_data_connect(conn
, PF_INET
, &sa
, sizeof(struct sockaddr_in
)))
997 if (response
== 229) {
998 if (ftp_data_connect(conn
, PF_INET6
, &sa
, sizeof(struct sockaddr_in6
)))
1003 ftp
->pending_commands
--;
1005 /* XXX: The case values are order numbers of commands. */
1006 switch (ftp
->opc
- ftp
->pending_commands
) {
1011 if (response
>= 400) {
1012 abort_connection(conn
,
1013 connection_state(S_FTP_PORT
));
1018 case 3: /* REST / CWD */
1020 if (response
== 213 && size
!= -1 && conn
->est_length
== -1) {
1021 conn
->est_length
= size
;
1022 } else if (response
>= 400) {
1024 abort_connection(conn
,
1025 connection_state(S_FTP_NO_FILE
));
1029 } else if (ftp
->rest_sent
) {
1030 /* Following code is related to resume
1032 if (response
== 350)
1033 conn
->from
= conn
->progress
->start
;
1034 /* Come on, don't be nervous ;-). */
1035 if (conn
->progress
->start
>= 0) {
1036 /* Update to the real value
1037 * which we've got from
1039 conn
->progress
->seek
= conn
->from
;
1041 conn
->progress
->start
= conn
->from
;
1046 INTERNAL("WHAT???");
1049 ftp_send_retr_req(conn
, connection_state(S_GETH
));
1053 response
= get_ftp_response(conn
, rb
, 2, NULL
, NULL
);
1055 if (response
== -1) {
1056 abort_connection(conn
, connection_state(S_FTP_ERROR
));
1061 read_from_socket(conn
->socket
, rb
, connection_state(S_GETH
),
1066 if (response
>= 100 && response
< 200) {
1067 /* We only need to parse response after RETR to
1068 * get filesize if needed. */
1069 if (!ftp
->dir
&& conn
->est_length
== -1) {
1073 file_len
= get_filesize_from_RETR(rb
->data
, rb
->length
, &res
);
1075 /* FIXME: ..when downloads resuming
1077 /* This is right for vsftpd.
1078 * Show me urls where this is wrong. --witekfl */
1079 conn
->est_length
= res
?
1080 file_len
+ conn
->progress
->start
: file_len
;
1085 if (conn
->data_socket
->fd
== -1) {
1086 /* The passive FTP server did not send a 227 or 229
1087 * response. We check this down here, rather than
1088 * immediately after getting the response to the PASV
1089 * or EPSV command, to make sure that nothing can
1090 * close the socket between the check and the
1091 * following set_handlers call.
1093 * If we were using active FTP, then
1094 * get_ftp_data_socket would have created the
1095 * data_socket without waiting for anything from the
1097 abort_connection(conn
, connection_state(S_FTP_ERROR
));
1100 set_handlers(conn
->data_socket
->fd
, (select_handler_T
) ftp_data_accept
,
1103 /* read_from_socket(conn->socket, rb, ftp_got_final_response); */
1104 ftp_got_final_response(socket
, rb
);
1108 ftp_got_final_response(struct socket
*socket
, struct read_buffer
*rb
)
1110 struct connection
*conn
= socket
->conn
;
1111 struct ftp_connection_info
*ftp
= conn
->info
;
1112 int response
= get_ftp_response(conn
, rb
, 0, NULL
, NULL
);
1114 if (response
== -1) {
1115 abort_connection(conn
, connection_state(S_FTP_ERROR
));
1120 struct connection_state state
= !is_in_state(conn
->state
, S_TRANS
)
1121 ? connection_state(S_GETH
) : conn
->state
;
1123 read_from_socket(conn
->socket
, rb
, state
, ftp_got_final_response
);
1127 if (response
>= 550 || response
== 450) {
1128 /* Requested action not taken.
1129 * File unavailable (e.g., file not found, no access). */
1132 conn
->cached
= get_cache_entry(conn
->uri
);
1135 || !redirect_cache(conn
->cached
, "/", 1, 0)) {
1136 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
1140 abort_connection(conn
, connection_state(S_OK
));
1144 if (response
>= 400) {
1145 abort_connection(conn
, connection_state(S_FTP_FILE_ERROR
));
1149 if (ftp
->conn_state
== 2) {
1150 ftp_end_request(conn
, connection_state(S_OK
));
1152 ftp
->conn_state
= 1;
1153 if (!is_in_state(conn
->state
, S_TRANS
))
1154 set_connection_state(conn
, connection_state(S_GETH
));
1159 /** How to format an FTP directory listing in HTML. */
1160 struct ftp_dir_html_format
{
1161 /** Codepage used by C library functions such as strftime().
1162 * If the FTP server sends non-ASCII bytes in file names or
1163 * such, ELinks normally passes them straight through to the
1164 * generated HTML, which will eventually be parsed using the
1165 * codepage specified in the document.codepage.assume option.
1166 * However, when ELinks itself generates strings with
1167 * strftime(), it turns non-ASCII bytes into entity references
1168 * based on libc_codepage, to make sure they become the right
1169 * characters again. */
1172 /** Nonzero if directories should be displayed in a different
1173 * color. From the document.browse.links.color_dirs option. */
1176 /** The color of directories, in "#rrggbb" format. This is
1177 * initialized and used only if colorize_dir is nonzero. */
1178 unsigned char dircolor
[8];
1181 /* Display directory entry formatted in HTML. */
1183 display_dir_entry(struct cache_entry
*cached
, off_t
*pos
, int *tries
,
1184 const struct ftp_dir_html_format
*format
,
1185 struct ftp_file_info
*ftp_info
)
1187 struct string string
;
1188 unsigned char permissions
[10] = "---------";
1190 if (!init_string(&string
)) return -1;
1192 add_char_to_string(&string
, ftp_info
->type
);
1194 if (ftp_info
->permissions
) {
1195 mode_t p
= ftp_info
->permissions
;
1197 #define FTP_PERM(perms, buffer, flag, index, id) \
1198 if ((perms) & (flag)) (buffer)[(index)] = (id);
1200 FTP_PERM(p
, permissions
, S_IRUSR
, 0, 'r');
1201 FTP_PERM(p
, permissions
, S_IWUSR
, 1, 'w');
1202 FTP_PERM(p
, permissions
, S_IXUSR
, 2, 'x');
1203 FTP_PERM(p
, permissions
, S_ISUID
, 2, (p
& S_IXUSR
? 's' : 'S'));
1205 FTP_PERM(p
, permissions
, S_IRGRP
, 3, 'r');
1206 FTP_PERM(p
, permissions
, S_IWGRP
, 4, 'w');
1207 FTP_PERM(p
, permissions
, S_IXGRP
, 5, 'x');
1208 FTP_PERM(p
, permissions
, S_ISGID
, 5, (p
& S_IXGRP
? 's' : 'S'));
1210 FTP_PERM(p
, permissions
, S_IROTH
, 6, 'r');
1211 FTP_PERM(p
, permissions
, S_IWOTH
, 7, 'w');
1212 FTP_PERM(p
, permissions
, S_IXOTH
, 8, 'x');
1213 FTP_PERM(p
, permissions
, S_ISVTX
, 8, (p
& 0001 ? 't' : 'T'));
1219 add_to_string(&string
, permissions
);
1220 add_char_to_string(&string
, ' ');
1222 add_to_string(&string
, " 1 ftp ftp ");
1224 if (ftp_info
->size
!= FTP_SIZE_UNKNOWN
) {
1225 add_format_to_string(&string
, "%12" OFF_PRINT_FORMAT
" ",
1226 (off_print_T
) ftp_info
->size
);
1228 add_to_string(&string
, " - ");
1231 #ifdef HAVE_STRFTIME
1232 if (ftp_info
->mtime
> 0) {
1233 time_t current_time
= time(NULL
);
1234 time_t when
= ftp_info
->mtime
;
1237 /* LC_TIME=fi_FI.UTF_8 can generate "elo___ 31 23:59"
1238 * where each _ denotes U+00A0 encoded as 0xC2 0xA0,
1239 * thus needing a 19-byte buffer. */
1240 unsigned char date
[80];
1243 if (ftp_info
->local_time_zone
)
1244 when_tm
= localtime(&when
);
1246 when_tm
= gmtime(&when
);
1248 if (current_time
> when
+ 6L * 30L * 24L * 60L * 60L
1249 || current_time
< when
- 60L * 60L)
1252 fmt
= "%b %e %H:%M";
1254 wr
= strftime(date
, sizeof(date
), fmt
, when_tm
);
1255 add_cp_html_to_string(&string
, format
->libc_codepage
,
1259 add_to_string(&string
, " ");
1260 /* TODO: Above, the number of spaces might not match the width
1261 * of the string generated by strftime. It depends on the
1262 * locale. So if ELinks knows the timestamps of some FTP
1263 * files but not others, it may misalign the file names.
1264 * Potential solutions:
1265 * - Pad the strings to a compile-time fixed width.
1266 * Con: If we choose a width that suffices for all known
1267 * locales, then it will be stupidly large for most of them.
1268 * - Generate an HTML table.
1269 * Con: Bloats the HTML source.
1270 * Any solution chosen here should also be applied to the
1271 * file: protocol handler. */
1273 add_char_to_string(&string
, ' ');
1275 if (ftp_info
->type
== FTP_FILE_DIRECTORY
&& format
->colorize_dir
) {
1276 add_to_string(&string
, "<font color=\"");
1277 add_to_string(&string
, format
->dircolor
);
1278 add_to_string(&string
, "\"><b>");
1281 add_to_string(&string
, "<a href=\"");
1282 encode_uri_string(&string
, ftp_info
->name
.source
, ftp_info
->name
.length
, 0);
1283 if (ftp_info
->type
== FTP_FILE_DIRECTORY
)
1284 add_char_to_string(&string
, '/');
1285 add_to_string(&string
, "\">");
1286 add_html_to_string(&string
, ftp_info
->name
.source
, ftp_info
->name
.length
);
1287 add_to_string(&string
, "</a>");
1289 if (ftp_info
->type
== FTP_FILE_DIRECTORY
&& format
->colorize_dir
) {
1290 add_to_string(&string
, "</b></font>");
1293 if (ftp_info
->symlink
.length
) {
1294 add_to_string(&string
, " -> ");
1295 add_html_to_string(&string
, ftp_info
->symlink
.source
,
1296 ftp_info
->symlink
.length
);
1299 add_char_to_string(&string
, '\n');
1301 if (add_fragment(cached
, *pos
, string
.source
, string
.length
)) *tries
= 0;
1302 *pos
+= string
.length
;
1304 done_string(&string
);
1308 /* Get the next line of input and set *@len to the length of the line.
1309 * Return the number of newline characters at the end of the line or -1
1310 * if we must wait for more input. */
1312 ftp_get_line(struct cache_entry
*cached
, unsigned char *buf
, int bufl
,
1315 unsigned char *newline
;
1317 if (!bufl
) return -1;
1319 newline
= memchr(buf
, ASCII_LF
, bufl
);
1322 *len
= newline
- buf
;
1323 if (*len
&& buf
[*len
- 1] == ASCII_CR
) {
1331 if (last
|| bufl
>= FTP_BUF_SIZE
) {
1339 /** Generate HTML for a line that was received from the FTP server but
1340 * could not be parsed. The caller is supposed to have added a \<pre>
1341 * start tag. (At the time of writing, init_directory_listing() was
1344 * @return -1 if out of memory, or 0 if successful. */
1346 ftp_add_unparsed_line(struct cache_entry
*cached
, off_t
*pos
, int *tries
,
1347 const unsigned char *line
, int line_length
)
1350 struct string string
;
1353 our_ret
= -1; /* assume out of memory if returning early */
1354 if (!init_string(&string
)) goto out
;
1355 if (!add_html_to_string(&string
, line
, line_length
)) goto out
;
1356 if (!add_char_to_string(&string
, '\n')) goto out
;
1358 frag_ret
= add_fragment(cached
, *pos
, string
.source
, string
.length
);
1359 if (frag_ret
== -1) goto out
;
1360 *pos
+= string
.length
;
1361 if (frag_ret
== 1) *tries
= 0;
1363 our_ret
= 0; /* success */
1366 done_string(&string
); /* safe even if init_string failed */
1370 /** List a directory in html format.
1372 * @return the number of bytes used from the beginning of @a buffer,
1373 * or -1 if out of memory. */
1375 ftp_process_dirlist(struct cache_entry
*cached
, off_t
*pos
,
1376 unsigned char *buffer
, int buflen
, int last
,
1377 int *tries
, const struct ftp_dir_html_format
*format
)
1382 struct ftp_file_info ftp_info
= INIT_FTP_FILE_INFO
;
1383 unsigned char *buf
= buffer
+ ret
;
1384 int bufl
= buflen
- ret
;
1385 int line_length
, eol
;
1387 eol
= ftp_get_line(cached
, buf
, bufl
, last
, &line_length
);
1391 ret
+= line_length
+ eol
;
1393 /* Process line whose end we've already found. */
1395 if (parse_ftp_file_info(&ftp_info
, buf
, line_length
)) {
1398 if (ftp_info
.name
.source
[0] == '.'
1399 && (ftp_info
.name
.length
== 1
1400 || (ftp_info
.name
.length
== 2
1401 && ftp_info
.name
.source
[1] == '.')))
1404 retv
= display_dir_entry(cached
, pos
, tries
,
1411 int retv
= ftp_add_unparsed_line(cached
, pos
, tries
,
1414 if (retv
== -1) /* out of memory; propagate to caller */
1420 /* This is the initial read handler for conn->data_socket->fd,
1421 * which may be either trying to connect to a passive FTP server or
1422 * listening for a connection from an active FTP server. In active
1423 * FTP, this function then accepts the connection and replaces
1424 * conn->data_socket->fd with the resulting socket. In any case,
1425 * this function does not read any data from the FTP server, but
1426 * rather hands the socket over to got_something_from_data_connection,
1427 * which then does the reads. */
1429 ftp_data_accept(struct connection
*conn
)
1431 struct ftp_connection_info
*ftp
= conn
->info
;
1434 /* Because this function is called only as a read handler of
1435 * conn->data_socket->fd, the socket must be valid if we get
1437 assert(conn
->data_socket
->fd
>= 0);
1438 if_assert_failed
return;
1440 set_connection_timeout(conn
);
1441 clear_handlers(conn
->data_socket
->fd
);
1443 if ((conn
->socket
->protocol_family
!= EL_PF_INET6
&& ftp
->use_pasv
)
1445 || (conn
->socket
->protocol_family
== EL_PF_INET6
&& ftp
->use_epsv
)
1448 newsock
= conn
->data_socket
->fd
;
1450 newsock
= accept(conn
->data_socket
->fd
, NULL
, NULL
);
1452 retry_connection(conn
, connection_state_for_errno(errno
));
1455 close(conn
->data_socket
->fd
);
1458 conn
->data_socket
->fd
= newsock
;
1460 set_handlers(newsock
,
1461 (select_handler_T
) got_something_from_data_connection
,
1465 /* A read handler for conn->data_socket->fd. This function reads
1466 * data from the FTP server, reformats it to HTML if it's a directory
1467 * listing, and adds the result to the cache entry. */
1469 got_something_from_data_connection(struct connection
*conn
)
1471 struct ftp_connection_info
*ftp
= conn
->info
;
1472 struct ftp_dir_html_format format
;
1475 /* Because this function is called only as a read handler of
1476 * conn->data_socket->fd, the socket must be valid if we get
1478 assert(conn
->data_socket
->fd
>= 0);
1479 if_assert_failed
return;
1481 /* XXX: This probably belongs rather to connect.c ? */
1483 set_connection_timeout(conn
);
1485 if (!conn
->cached
) conn
->cached
= get_cache_entry(conn
->uri
);
1486 if (!conn
->cached
) {
1488 abort_connection(conn
, connection_state(S_OUT_OF_MEM
));
1493 format
.libc_codepage
= get_cp_index("System");
1495 format
.colorize_dir
= get_opt_bool("document.browse.links.color_dirs", NULL
);
1497 if (format
.colorize_dir
) {
1498 color_to_string(get_opt_color("document.colors.dirs", NULL
),
1503 if (ftp
->dir
&& !conn
->from
) {
1504 struct string string
;
1505 struct connection_state state
;
1507 if (!conn
->uri
->data
) {
1508 abort_connection(conn
, connection_state(S_FTP_ERROR
));
1512 state
= init_directory_listing(&string
, conn
->uri
);
1513 if (!is_in_state(state
, S_OK
)) {
1514 abort_connection(conn
, state
);
1518 add_fragment(conn
->cached
, conn
->from
, string
.source
, string
.length
);
1519 conn
->from
+= string
.length
;
1521 done_string(&string
);
1523 if (conn
->uri
->datalen
) {
1524 struct ftp_file_info ftp_info
= INIT_FTP_FILE_INFO_ROOT
;
1526 display_dir_entry(conn
->cached
, &conn
->from
, &conn
->tries
,
1527 &format
, &ftp_info
);
1530 mem_free_set(&conn
->cached
->content_type
, stracpy("text/html"));
1533 len
= safe_read(conn
->data_socket
->fd
, ftp
->ftp_buffer
+ ftp
->buf_pos
,
1534 FTP_BUF_SIZE
- ftp
->buf_pos
);
1536 retry_connection(conn
, connection_state_for_errno(errno
));
1541 conn
->received
+= len
;
1544 if (add_fragment(conn
->cached
, conn
->from
,
1545 ftp
->ftp_buffer
, len
) == 1)
1552 proceeded
= ftp_process_dirlist(conn
->cached
,
1559 if (proceeded
== -1) goto out_of_mem
;
1561 ftp
->buf_pos
+= len
- proceeded
;
1563 memmove(ftp
->ftp_buffer
, ftp
->ftp_buffer
+ proceeded
,
1568 set_connection_state(conn
, connection_state(S_TRANS
));
1572 if (ftp_process_dirlist(conn
->cached
, &conn
->from
,
1573 ftp
->ftp_buffer
, ftp
->buf_pos
, 1,
1574 &conn
->tries
, &format
) == -1)
1577 #define ADD_CONST(str) { \
1578 add_fragment(conn->cached, conn->from, str, sizeof(str) - 1); \
1579 conn->from += (sizeof(str) - 1); }
1581 if (ftp
->dir
) ADD_CONST("</pre>\n<hr/>\n</body>\n</html>");
1583 close_socket(conn
->data_socket
);
1585 if (ftp
->conn_state
== 1) {
1586 ftp_end_request(conn
, connection_state(S_OK
));
1588 ftp
->conn_state
= 2;
1589 set_connection_state(conn
, connection_state(S_TRANS
));
1594 ftp_end_request(struct connection
*conn
, struct connection_state state
)
1596 if (is_in_state(state
, S_OK
) && conn
->cached
) {
1597 normalize_cache_entry(conn
->cached
, conn
->from
);
1600 set_connection_state(conn
, state
);
1601 add_keepalive_connection(conn
, FTP_KEEPALIVE_TIMEOUT
, NULL
);