1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1995 Ching Hui
7 1995, 1996, 1997 Miguel de Icaza
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Library General Public License
13 as published by the Free Software Foundation; either version 2 of
14 the License, or (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU Library General Public License for more details.
21 You should have received a copy of the GNU Library General Public
22 License along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
27 * \brief Source: Virtual File System: FTP file system
29 * \author Jakub Jelinek
30 * \author Miguel de Icaza
31 * \author Norbert Warmuth
32 * \author Pavel Machek
33 * \date 1995, 1997, 1998
36 - make it more robust - all the connects etc. should handle EADDRINUSE and
37 ERETRY (have I spelled these names correctly?)
38 - make the user able to flush a connection - all the caches will get empty
39 etc., (tarfs as well), we should give there a user selectable timeout
40 and assign a key sequence.
41 - use hash table instead of linklist to cache ftpfs directory.
46 * NOTE: Usage of tildes is deprecated, consider:
51 * And now: what do I want to do? Do I want to go to /home/pavel or to
52 * /#ftp:hobit/home/pavel? I think first has better sense...
56 int f = !strcmp( remote_path, "/~" );
57 if (f || !strncmp( remote_path, "/~/", 3 )) {
59 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
67 /* \todo Fix: Namespace pollution: horrible */
70 #include <stdlib.h> /* atoi() */
71 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
72 #include <netdb.h> /* struct hostent */
73 #include <sys/socket.h> /* AF_INET */
74 #include <netinet/in.h> /* struct in_addr */
75 #ifdef HAVE_ARPA_INET_H
76 #include <arpa/inet.h>
79 #include <arpa/telnet.h>
80 #include <sys/param.h>
84 #include <sys/time.h> /* gettimeofday() */
86 #include "../src/global.h"
88 #include "../src/tty/tty.h" /* enable/disable interrupt key */
90 #include "../src/wtools.h" /* message() */
91 #include "../src/main.h" /* print_vfs_message */
92 #include "../src/history.h"
93 #include "../src/setup.h" /* for load_anon_passwd */
94 #include "../src/mcconfig/mcconfig.h"
97 #include "xdirentry.h"
100 #include "gc.h" /* vfs_stamp_create */
103 #ifndef MAXHOSTNAMELEN
104 # define MAXHOSTNAMELEN 64
107 #define UPLOAD_ZERO_LENGTH_FILE
108 #define SUP super->u.ftp
109 #define FH_SOCK fh->u.ftp.sock
112 #define INADDR_NONE 0xffffffff
115 /* for uclibc < 0.9.29 */
116 #ifndef AI_ADDRCONFIG
117 #define AI_ADDRCONFIG 0x0020
120 #define RFC_AUTODETECT 0
124 #ifndef HAVE_SOCKLEN_T
125 typedef int socklen_t
;
128 static int ftpfs_errno
;
131 /* Delay to retry a connection */
132 int ftpfs_retry_seconds
= 30;
134 /* Method to use to connect to ftp sites */
135 int ftpfs_use_passive_connections
= 1;
136 int ftpfs_use_passive_connections_over_proxy
= 0;
138 /* Method used to get directory listings:
139 * 1: try 'LIST -la <path>', if it fails
140 * fall back to CWD <path>; LIST
141 * 0: always use CWD <path>; LIST
143 int ftpfs_use_unix_list_options
= 1;
145 /* First "CWD <path>", then "LIST -la ." */
146 int ftpfs_first_cd_then_ls
= 1;
148 /* Use the ~/.netrc */
151 /* Anonymous setup */
152 char *ftpfs_anonymous_passwd
= NULL
;
153 int ftpfs_directory_timeout
= 900;
156 char *ftpfs_proxy_host
= NULL
;
158 /* wether we have to use proxy by default? */
159 int ftpfs_always_use_proxy
;
161 #ifdef FIXME_LATER_ALIGATOR
162 static struct linklist
*connections_list
;
165 /* ftpfs_command wait_flag: */
167 #define WAIT_REPLY 0x01
168 #define WANT_STRING 0x02
169 static char reply_str
[80];
171 static struct vfs_class vfs_ftpfs_ops
;
173 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
174 Translate a Unix path, i.e. MC's internal path representation (e.g.
175 /somedir/somefile) to a path valid for the remote server. Every path
176 transfered to the remote server has to be mangled by this function
177 right prior to sending it.
178 Currently only Amiga ftp servers are handled in a special manner.
180 When the remote server is an amiga:
181 a) strip leading slash if necesarry
182 b) replace first occurance of ":/" with ":"
183 c) strip trailing "/."
186 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
187 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
);
188 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
189 __attribute__ ((format (__printf__
, 4, 5)));
190 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
191 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
);
192 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
195 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
197 if (!SUP
.remote_is_amiga
)
198 return g_strdup (remote_path
);
202 if (MEDATA
->logfile
) {
203 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
204 fflush (MEDATA
->logfile
);
207 /* strip leading slash(es) */
208 while (*remote_path
== '/')
212 * Don't change "/" into "", e.g. "CWD " would be
215 if (*remote_path
== '\0')
216 return g_strdup (".");
218 ret
= g_strdup (remote_path
);
220 /* replace first occurance of ":/" with ":" */
221 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
222 strcpy (p
+ 1, p
+ 2);
224 /* strip trailing "/." */
225 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
231 /* Extract the hostname and username from the path */
234 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
235 * ftp://sunsite.unc.edu/pub/linux
236 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
237 * ftp://tsx-11.mit.edu:8192/
238 * ftp://joe@foo.edu:11321/private
239 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
244 #define FTP_COMMAND_PORT 21
247 ftpfs_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
251 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
255 /* Look up user and password in netrc */
257 ftpfs_netrc_lookup (*host
, user
, pass
);
259 *user
= g_strdup ("anonymous");
262 /* Look up password in netrc for known user */
263 if (use_netrc
&& *user
&& pass
&& !*pass
) {
266 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
268 /* If user is different, remove password */
269 if (new_user
&& strcmp (*user
, new_user
)) {
280 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
282 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
288 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
294 switch (sscanf(answer
, "%d", &code
)){
297 g_strlcpy (string_buf
, answer
, string_len
);
301 if (answer
[3] == '-') {
303 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
309 if ((sscanf (answer
, "%d", &i
) > 0) &&
310 (code
== i
) && (answer
[3] == ' '))
315 g_strlcpy (string_buf
, answer
, string_len
);
322 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
324 int sock
= ftpfs_open_socket (me
, super
);
326 char *cwdir
= SUP
.cwdir
;
330 if (ftpfs_login_server (me
, super
, SUP
.password
)){
333 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
335 return sock
== COMPLETE
;
343 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
348 static int retry
= 0;
349 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
352 cmdstr
= g_strdup_vprintf (fmt
, ap
);
355 cmdlen
= strlen (cmdstr
);
356 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
357 strcpy (cmdstr
+ cmdlen
, "\r\n");
360 if (MEDATA
->logfile
) {
361 if (strncmp (cmdstr
, "PASS ", 5) == 0) {
362 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
364 fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
366 fflush (MEDATA
->logfile
);
370 tty_enable_interrupt_key ();
371 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
376 if (errno
== EPIPE
) { /* Remote server has closed connection */
379 status
= ftpfs_reconnect (me
, super
);
381 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
389 tty_disable_interrupt_key ();
394 tty_disable_interrupt_key ();
398 status
= ftpfs_get_reply (me
, SUP
.sock
,
399 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
400 sizeof (reply_str
) - 1);
401 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
405 status
= ftpfs_reconnect (me
, super
);
407 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
420 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
423 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
424 ftpfs_command(me
, super
, NONE
, "QUIT");
430 g_free (SUP
.password
);
433 /* some defines only used by ftpfs_changetype */
434 /* These two are valid values for the second parameter */
436 #define TYPE_BINARY 1
438 /* This one is only used to initialize bucket->isbinary, don't use it as
439 second parameter to ftpfs_changetype. */
440 #define TYPE_UNKNOWN -1
443 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
445 if (binary
!= SUP
.isbinary
) {
446 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
448 SUP
.isbinary
= binary
;
453 /* This routine logs the user in */
455 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
456 const char *netrcpass
)
460 char *name
; /* login user name */
462 char reply_string
[BUF_MEDIUM
];
464 SUP
.isbinary
= TYPE_UNKNOWN
;
466 if (SUP
.password
) /* explicit password */
467 op
= g_strdup (SUP
.password
);
468 else if (netrcpass
) /* password from netrc */
469 op
= g_strdup (netrcpass
);
470 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp")) {
471 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
472 ftpfs_init_passwd ();
473 op
= g_strdup (ftpfs_anonymous_passwd
);
475 } else { /* ask user */
478 p
= g_strconcat (_(" FTP: Password required for "),
479 SUP
.user
, " ", (char *) NULL
);
480 op
= vfs_get_password (p
);
484 SUP
.password
= g_strdup (op
);
487 if (!anon
|| MEDATA
->logfile
)
490 pass
= g_strconcat ("-", op
, (char *) NULL
);
494 /* Proxy server accepts: username@host-we-want-to-connect */
497 g_strconcat (SUP
.user
, "@",
498 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
,
501 name
= g_strdup (SUP
.user
);
504 (me
, SUP
.sock
, reply_string
,
505 sizeof (reply_string
) - 1) == COMPLETE
) {
506 g_strup (reply_string
);
507 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
508 if (MEDATA
->logfile
) {
509 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n",
510 SUP
.remote_is_amiga
);
511 fflush (MEDATA
->logfile
);
514 print_vfs_message (_("ftpfs: sending login name"));
516 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
)) {
518 print_vfs_message (_("ftpfs: sending user password"));
519 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
520 if (code
== CONTINUE
) {
523 p
= g_strdup_printf (_
524 ("FTP: Account required for user %s"),
526 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
530 print_vfs_message (_("ftpfs: sending user account"));
532 ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
535 if (code
!= COMPLETE
)
540 print_vfs_message (_("ftpfs: logged in"));
541 wipe_password (pass
);
546 SUP
.failed_on_login
= 1;
548 wipe_password (SUP
.password
);
554 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
557 wipe_password (pass
);
562 static struct no_proxy_entry
{
568 ftpfs_load_no_proxy_list (void)
570 /* FixMe: shouldn't be hardcoded!!! */
571 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
572 struct no_proxy_entry
*np
, *current
= 0;
576 static char *mc_file
;
581 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
582 if (exist_file (mc_file
) &&
583 (npf
= fopen (mc_file
, "r"))) {
584 while (fgets (s
, sizeof (s
), npf
)) {
585 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
586 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
596 np
= g_new (struct no_proxy_entry
, 1);
597 np
->domain
= g_strdup (s
);
611 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
613 ftpfs_check_proxy (const char *host
)
615 struct no_proxy_entry
*npe
;
617 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
618 return 0; /* sanity check */
623 if (!ftpfs_always_use_proxy
)
626 if (!strchr (host
, '.'))
629 ftpfs_load_no_proxy_list ();
630 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
631 char *domain
= npe
->domain
;
633 if (domain
[0] == '.') {
634 int ld
= strlen (domain
);
635 int lh
= strlen (host
);
637 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
645 if (!g_strcasecmp (host
, domain
))
653 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
658 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
665 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
667 struct addrinfo hints
, *res
, *curr_res
;
676 /* Use a proxy host? */
677 host
= g_strdup(SUP
.host
);
679 if (!host
|| !*host
){
680 print_vfs_message (_("ftpfs: Invalid host name."));
681 ftpfs_errno
= EINVAL
;
686 /* Hosts to connect to that start with a ! should use proxy */
690 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
693 port
= g_strdup_printf("%hu", (unsigned short) tmp_port
);
700 tty_enable_interrupt_key(); /* clear the interrupt flag */
702 memset (&hints
, 0, sizeof (struct addrinfo
));
703 hints
.ai_socktype
= SOCK_STREAM
;
704 hints
.ai_flags
= AI_ADDRCONFIG
;
707 e
= getaddrinfo (host
, port
, &hints
, &res
);
712 tty_disable_interrupt_key ();
713 print_vfs_message (_("ftpfs: %s"), gai_strerror (e
));
715 ftpfs_errno
= EINVAL
;
719 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
) {
721 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
725 if (curr_res
->ai_next
!= NULL
)
728 tty_disable_interrupt_key();
729 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno
));
736 print_vfs_message (_("ftpfs: making connection to %s"), host
);
740 if ( connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0 )
746 if (errno
== EINTR
&& tty_got_interrupt ()) {
747 print_vfs_message (_("ftpfs: connection interrupted by user"));
748 } else if (res
->ai_next
== NULL
) {
749 print_vfs_message (_("ftpfs: connection to server failed: %s"),
750 unix_error_string (errno
));
756 tty_disable_interrupt_key ();
761 tty_disable_interrupt_key ();
766 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
768 int retry_seconds
, count_down
;
770 /* We do not want to use the passive if we are using proxies */
772 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
776 SUP
.failed_on_login
= 0;
778 SUP
.sock
= ftpfs_open_socket (me
, super
);
782 if (ftpfs_login_server (me
, super
, NULL
)) {
783 /* Logged in, no need to retry the connection */
786 if (SUP
.failed_on_login
){
787 /* Close only the socket descriptor */
792 if (ftpfs_retry_seconds
){
793 retry_seconds
= ftpfs_retry_seconds
;
794 tty_enable_interrupt_key ();
795 for (count_down
= retry_seconds
; count_down
; count_down
--){
796 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
798 if (tty_got_interrupt ()) {
799 /* ftpfs_errno = E; */
800 tty_disable_interrupt_key ();
804 tty_disable_interrupt_key ();
807 } while (retry_seconds
);
809 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
811 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
816 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
817 const char *archive_name
, char *op
)
819 char *host
, *user
, *password
;
824 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
831 if (ftpfs_check_proxy (host
))
832 SUP
.proxy
= ftpfs_proxy_host
;
833 SUP
.password
= password
;
834 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
835 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
836 SUP
.isbinary
= TYPE_UNKNOWN
;
837 SUP
.remote_is_amiga
= 0;
838 super
->name
= g_strdup ("/");
840 vfs_s_new_inode (me
, super
,
841 vfs_s_default_stat (me
, S_IFDIR
| 0755));
843 return ftpfs_open_archive_int (me
, super
);
847 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
848 const char *archive_name
, char *op
, void *cookie
)
857 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
859 port
= ((strcmp (host
, SUP
.host
) == 0)
860 && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
868 /* The returned directory should always contain a trailing slash */
870 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
872 char buf
[BUF_8K
], *bufp
, *bufq
;
874 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
875 ftpfs_get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
877 for (bufq
= buf
; *bufq
; bufq
++)
884 if (*(bufq
- 1) != '/') {
889 return g_strdup (bufp
);
891 /* If the remote server is an Amiga a leading slash
892 might be missing. MC needs it because it is used
893 as separator between hostname and path internally. */
894 return g_strconcat( "/", bufp
, (char *) NULL
);
908 /* Setup Passive ftp connection, we use it for source routed connections */
910 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
911 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
*salen
)
915 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") == COMPLETE
) {
918 c
= strchr (reply_str
, '|');
927 if (port
< 0 || port
> 65535)
931 switch (sa
->ss_family
) {
933 ((struct sockaddr_in
*)sa
)->sin_port
= port
;
936 ((struct sockaddr_in6
*)sa
)->sin6_port
= port
;
939 print_vfs_message (_("ftpfs: invalid address family"));
942 } else if (sa
->ss_family
== AF_INET
) {
943 int xa
, xb
, xc
, xd
, xe
, xf
;
946 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
949 /* Parse remote parameters */
950 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
954 if (!isdigit ((unsigned char) *c
))
956 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
959 n
[0] = (unsigned char) xa
;
960 n
[1] = (unsigned char) xb
;
961 n
[2] = (unsigned char) xc
;
962 n
[3] = (unsigned char) xd
;
963 n
[4] = (unsigned char) xe
;
964 n
[5] = (unsigned char) xf
;
966 memcpy (&(((struct sockaddr_in
*)sa
)->sin_addr
.s_addr
), (void *)n
, 4);
967 memcpy (&(((struct sockaddr_in
*)sa
)->sin_port
), (void *)&n
[4], 2);
971 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
978 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
980 struct sockaddr_storage data_addr
;
981 socklen_t data_addrlen
;
982 int data_sock
, result
;
985 memset (&data_addr
, 0, sizeof (struct sockaddr_storage
));
986 data_addrlen
= sizeof (struct sockaddr_storage
);
988 if (SUP
.use_passive_connection
)
989 result
= getpeername (SUP
.sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
);
991 result
= getsockname (SUP
.sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
);
996 switch (data_addr
.ss_family
) {
998 ((struct sockaddr_in
*)&data_addr
)->sin_port
= 0;
1001 ((struct sockaddr_in6
*)&data_addr
)->sin6_port
= 0;
1004 print_vfs_message (_("ftpfs: invalid address family"));
1008 data_sock
= socket (data_addr
.ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1009 if (data_sock
< 0) {
1010 if (SUP
.use_passive_connection
) {
1011 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno
));
1012 SUP
.use_passive_connection
= 0;
1016 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1020 if (SUP
.use_passive_connection
) {
1022 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1025 SUP
.use_passive_connection
= 0;
1026 print_vfs_message (_("ftpfs: could not setup passive mode"));
1032 /* If passive setup fails, fallback to active connections */
1033 /* Active FTP connection */
1034 if ((bind (data_sock
, (struct sockaddr
*)&data_addr
, data_addrlen
) == 0) &&
1035 (getsockname (data_sock
, (struct sockaddr
*)&data_addr
, &data_addrlen
) == 0) &&
1036 (listen (data_sock
, 1) == 0)) {
1037 unsigned short int port
;
1041 switch (data_addr
.ss_family
) {
1044 port
= ((struct sockaddr_in
*)&data_addr
)->sin_port
;
1048 port
= ((struct sockaddr_in6
*)&data_addr
)->sin6_port
;
1051 print_vfs_message (_("ftpfs: invalid address family"));
1052 ERRNOR (EINVAL
, -1);
1055 port
= ntohs (port
);
1057 addr
= g_try_malloc (NI_MAXHOST
);
1059 ERRNOR (ENOMEM
, -1);
1061 if (getnameinfo ((struct sockaddr
*)&data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0, NI_NUMERICHOST
) != 0) {
1066 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
) {
1072 if (FTP_INET
== af
) {
1073 unsigned char *a
= (unsigned char *)&((struct sockaddr_in
*)&data_addr
)->sin_addr
;
1074 unsigned char *p
= (unsigned char *)&port
;
1076 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1077 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1078 p
[0], p
[1]) == COMPLETE
)
1088 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1089 const char *remote
, int isbinary
, int reget
)
1091 struct sockaddr_storage from
;
1093 socklen_t fromlen
= sizeof(from
);
1095 if ((s
= ftpfs_initconn (me
, super
)) == -1)
1097 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1100 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1105 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1106 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1107 /* WarFtpD can't STORE //filename */
1108 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1109 g_free (remote_path
);
1111 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1114 tty_enable_interrupt_key ();
1115 if (SUP
.use_passive_connection
)
1118 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
1120 ftpfs_errno
= errno
;
1126 tty_disable_interrupt_key ();
1130 #define ABORT_TIMEOUT 5
1132 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1134 struct vfs_s_super
*super
= FH_SUPER
;
1135 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1138 int dsock
= FH_SOCK
;
1140 SUP
.ctl_connection_busy
= 0;
1142 print_vfs_message (_("ftpfs: aborting transfer."));
1143 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
1144 print_vfs_message (_("ftpfs: abort error: %s"),
1145 unix_error_string (errno
));
1151 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1152 print_vfs_message (_("ftpfs: abort failed"));
1159 FD_SET (dsock
, &mask
);
1160 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1161 struct timeval start_tim
, tim
;
1162 gettimeofday (&start_tim
, NULL
);
1163 /* flush the remaining data */
1164 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1165 gettimeofday (&tim
, NULL
);
1166 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1167 /* server keeps sending, drop the connection and ftpfs_reconnect */
1169 ftpfs_reconnect (me
, super
);
1176 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1177 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1182 resolve_symlink_without_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1184 struct linklist
*flist
;
1185 struct direntry
*fe
, *fel
;
1186 char tmp
[MC_MAXPATHLEN
];
1189 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1190 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1191 /* flist->data->l_stat is alread initialized with 0 */
1193 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1194 if (fel
->linkname
[0] == '/') {
1195 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1197 strcpy (tmp
, fel
->linkname
);
1199 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1201 strcpy (tmp
, dir
->remote_path
);
1204 strcat (tmp
+ 1, fel
->linkname
);
1206 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1207 canonicalize_pathname (tmp
);
1208 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1210 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1211 /* Symlink points to link which isn't resolved, yet. */
1212 if (fe
->linkname
[0] == '/') {
1213 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1215 strcpy (tmp
, fe
->linkname
);
1217 /* at this point tmp looks always like this
1218 /directory/filename, i.e. no need to check
1219 strrchr's return value */
1220 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1221 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1223 strcat (tmp
, fe
->linkname
);
1227 fel
->l_stat
= g_new (struct stat
, 1);
1228 if ( S_ISLNK (fe
->s
.st_mode
))
1229 *fel
->l_stat
= *fe
->l_stat
;
1231 *fel
->l_stat
= fe
->s
;
1232 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1239 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1243 resolve_symlink_with_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1245 char buffer
[2048] = "", *filename
;
1249 struct linklist
*flist
;
1250 struct direntry
*fe
;
1251 int switch_method
= 0;
1253 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1254 if (strchr (dir
->remote_path
, ' ')) {
1255 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1256 print_vfs_message(_("ftpfs: CWD failed."));
1259 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1262 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa",
1263 dir
->remote_path
, TYPE_ASCII
, 0);
1266 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1270 fp
= fdopen(sock
, "r");
1273 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1276 tty_enable_interrupt_key ();
1277 flist
= dir
->file_list
->next
;
1280 if (flist
== dir
->file_list
)
1283 flist
= flist
->next
;
1284 } while (!S_ISLNK(fe
->s
.st_mode
));
1286 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1288 if (MEDATA
->logfile
){
1289 fputs (buffer
, MEDATA
->logfile
);
1290 fflush (MEDATA
->logfile
);
1292 vfs_die("This code should be commented out\n");
1293 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1294 int r
= strcmp(fe
->name
, filename
);
1297 if (S_ISLNK (s
.st_mode
)) {
1298 /* This server doesn't understand LIST -lLa */
1302 fe
->l_stat
= g_new (struct stat
, 1);
1303 if (fe
->l_stat
== NULL
)
1306 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1315 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1316 tty_disable_interrupt_key ();
1318 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1322 resolve_symlink(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1324 print_vfs_message(_("Resolving symlink..."));
1326 if (SUP
.strict_rfc959_list_cmd
)
1327 resolve_symlink_without_ls_options(me
, super
, dir
);
1329 resolve_symlink_with_ls_options(me
, super
, dir
);
1334 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1336 struct vfs_s_entry
*ent
;
1337 struct vfs_s_super
*super
= dir
->super
;
1338 int sock
, num_entries
= 0;
1339 char buffer
[BUF_8K
];
1342 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1343 || (strchr (remote_path
, ' ') != NULL
);
1346 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1349 RFC_STRICT
? _("(strict rfc959)") : "",
1350 cd_first
? _("(chdir first)") : "");
1353 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
) {
1354 ftpfs_errno
= ENOENT
;
1355 print_vfs_message (_("ftpfs: CWD failed."));
1360 gettimeofday (&dir
->timestamp
, NULL
);
1361 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1363 if (SUP
.strict
== RFC_STRICT
)
1364 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1366 /* Dirty hack to avoid autoprepending / to . */
1367 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1369 ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1371 /* Trailing "/." is necessary if remote_path is a symlink */
1372 char *path
= concat_dir_and_file (remote_path
, ".");
1374 ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
,
1382 /* Clear the interrupt flag */
1383 tty_enable_interrupt_key ();
1388 vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1394 me
->verrno
= ECONNRESET
;
1396 tty_disable_interrupt_key ();
1397 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1398 print_vfs_message (_("%s: failure"), me
->name
);
1402 if (MEDATA
->logfile
) {
1403 fputs (buffer
, MEDATA
->logfile
);
1404 fputs ("\n", MEDATA
->logfile
);
1405 fflush (MEDATA
->logfile
);
1408 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1409 i
= ent
->ino
->st
.st_nlink
;
1410 if (!vfs_parse_ls_lga
1411 (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1412 vfs_s_free_entry (me
, ent
);
1415 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1417 vfs_s_insert_entry (me
, dir
, ent
);
1421 me
->verrno
= E_REMOTE
;
1422 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1425 if (num_entries
== 0 && cd_first
== 0) {
1426 /* The LIST command may produce an empty output. In such scenario
1427 it is not clear whether this is caused by `remote_path' being
1428 a non-existent path or for some other reason (listing emtpy
1429 directory without the -a option, non-readable directory, etc.).
1431 Since `dir_load' is a crucial method, when it comes to determine
1432 whether a given path is a _directory_, the code must try its best
1433 to determine the type of `remote_path'. The only reliable way to
1434 achieve this is trough issuing a CWD command. */
1440 if (SUP
.strict
== RFC_AUTODETECT
)
1441 SUP
.strict
= RFC_DARING
;
1443 print_vfs_message (_("%s: done."), me
->name
);
1447 if (SUP
.strict
== RFC_AUTODETECT
) {
1448 /* It's our first attempt to get a directory listing from this
1449 server (UNIX style LIST command) */
1450 SUP
.strict
= RFC_STRICT
;
1451 /* I hate goto, but recursive call needs another 8K on stack */
1452 /* return ftpfs_dir_load (me, dir, remote_path); */
1456 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1457 ERRNOR (EACCES
, -1);
1461 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
,
1464 int h
, sock
, n_read
, n_written
;
1466 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1474 struct vfs_s_super
*super
= FH_SUPER
;
1476 h
= open (localname
, O_RDONLY
);
1480 ftpfs_open_data_connection (me
, super
,
1481 fh
->u
.ftp
.append
? "APPE" : "STOR", name
,
1483 if (sock
< 0 || fstat (h
, &s
) == -1) {
1487 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1490 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1492 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1496 tty_enable_interrupt_key ();
1498 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1) {
1499 if (errno
== EINTR
) {
1500 if (tty_got_interrupt ()) {
1501 ftpfs_errno
= EINTR
;
1506 ftpfs_errno
= errno
;
1513 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
) {
1514 if (n_written
== -1) {
1515 if (errno
== EINTR
&& !tty_got_interrupt ()) {
1518 ftpfs_errno
= errno
;
1522 n_read
-= n_written
;
1524 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1525 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1527 tty_disable_interrupt_key ();
1530 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1534 tty_disable_interrupt_key ();
1537 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1542 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1544 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1548 FH_SOCK
= ftpfs_open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1552 fh
->linear
= LS_LINEAR_OPEN
;
1553 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1554 fh
->u
.ftp
.append
= 0;
1559 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1562 struct vfs_s_super
*super
= FH_SUPER
;
1564 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1565 if ((errno
== EINTR
) && !tty_got_interrupt ())
1571 ftpfs_linear_abort(me
, fh
);
1574 SUP
.ctl_connection_busy
= 0;
1577 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1578 ERRNOR (E_REMOTE
, -1);
1585 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1588 ftpfs_linear_abort(me
, fh
);
1591 static int ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1596 case VFS_CTL_IS_NOTREADY
:
1601 vfs_die ("You may not do this");
1602 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1605 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1606 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1616 ftpfs_send_command(struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1619 char *p
, *mpath
= g_strdup(filename
);
1620 struct vfs_s_super
*super
;
1622 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1624 if (!(rpath
= vfs_s_get_path_mangle(me
, mpath
, &super
, 0))) {
1628 p
= ftpfs_translate_path (me
, super
, rpath
);
1629 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1631 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1632 if (flags
& OPT_IGNORE_ERROR
)
1634 if (r
!= COMPLETE
) {
1639 if (flush_directory_cache
)
1640 vfs_s_invalidate(me
, super
);
1645 /* This routine is called as the last step in load_setup */
1647 ftpfs_init_passwd(void)
1649 ftpfs_anonymous_passwd
= load_anon_passwd ();
1650 if (ftpfs_anonymous_passwd
)
1653 /* If there is no anonymous ftp password specified
1654 * then we'll just use anonymous@
1655 * We don't send any other thing because:
1656 * - We want to remain anonymous
1657 * - We want to stop SPAM
1658 * - We don't want to let ftp sites to discriminate by the user,
1661 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1664 static int ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1666 char buf
[BUF_SMALL
];
1669 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1671 ret
= ftpfs_send_command(me
, path
, buf
, OPT_FLUSH
);
1673 if ( mc_config_get_bool (mc_main_config
, CONFIG_APP_SECTION
,
1674 "ignore_ftp_chattr_errors", TRUE
)) {
1681 static int ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1684 ftpfs_errno
= EPERM
;
1687 /* Everyone knows it is not possible to chown remotely, so why bother them.
1688 If someone's root, then copy/move will always try to chown it... */
1697 static int ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1699 return ftpfs_send_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1702 /* Return 1 if path is the same directory as the one we are in now */
1704 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1710 if (strcmp (path
, SUP
.cwdir
) == 0)
1716 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1721 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1724 p
= ftpfs_translate_path (me
, super
, remote_path
);
1725 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1728 if (r
!= COMPLETE
) {
1732 SUP
.cwdir
= g_strdup (remote_path
);
1733 SUP
.cwd_deferred
= 0;
1738 static int ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1740 ftpfs_send_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1741 return ftpfs_send_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1744 static int ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1746 (void) mode
; /* FIXME: should be used */
1748 return ftpfs_send_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1751 static int ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1753 return ftpfs_send_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1757 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
,
1762 fh
->u
.ftp
.append
= 0;
1763 /* File will be written only, so no need to retrieve it from ftp server */
1764 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
))) {
1765 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1772 /* ftpfs_linear_start() called, so data will be written
1773 * to local temporary file and stored to ftp server
1774 * by vfs_s_close later
1776 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
) {
1777 if (!fh
->ino
->localname
) {
1778 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1779 fh
->ino
->ent
->name
);
1783 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1787 name
= vfs_s_fullpath (me
, fh
->ino
);
1791 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1792 (flags
& O_APPEND
) ? "APPE" :
1793 "STOR", name
, TYPE_BINARY
, 0);
1798 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1802 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1804 if (fh
->ino
->localname
) {
1805 unlink (fh
->ino
->localname
);
1806 g_free (fh
->ino
->localname
);
1807 fh
->ino
->localname
= NULL
;
1812 if (!fh
->ino
->localname
)
1813 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1815 if (!fh
->ino
->localname
)
1816 vfs_die ("retrieve_file failed to fill in localname");
1820 static int ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1822 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1825 /* File is stored to destination already, so
1826 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1829 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1831 vfs_s_invalidate (me
, FH_SUPER
);
1837 ftpfs_done (struct vfs_class
*me
)
1839 struct no_proxy_entry
*np
;
1844 np
= no_proxy
->next
;
1845 g_free (no_proxy
->domain
);
1849 g_free (ftpfs_anonymous_passwd
);
1850 g_free (ftpfs_proxy_host
);
1854 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
1856 struct vfs_s_super
*super
= MEDATA
->supers
;
1860 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
1863 super
= super
->next
;
1867 static char buffer
[BUF_MEDIUM
];
1869 static const char *netrcp
;
1871 /* This should match the keywords[] array below */
1884 static keyword_t
ftpfs_netrc_next (void)
1888 static const char *const keywords
[] = { "default", "machine",
1889 "login", "password", "passwd", "account", "macdef", NULL
1894 netrcp
= skip_separators (netrcp
);
1895 if (*netrcp
!= '\n')
1902 if (*netrcp
== '"') {
1903 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1904 if (*netrcp
== '\\')
1909 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1910 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1911 if (*netrcp
== '\\')
1921 while (keywords
[i
- 1]) {
1922 if (!strcmp (keywords
[i
- 1], buffer
))
1928 return NETRC_UNKNOWN
;
1931 static int ftpfs_netrc_bad_mode (const char *netrcname
)
1933 static int be_angry
= 1;
1936 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1938 message (D_ERROR
, MSG_ERROR
,
1939 _("~/.netrc file has incorrect mode.\n"
1940 "Remove password or correct mode."));
1948 /* Scan .netrc until we find matching "machine" or "default"
1949 * domain is used for additional matching
1950 * No search is done after "default" in compliance with "man netrc"
1951 * Return 0 if found, -1 otherwise */
1952 static int ftpfs_find_machine (const char *host
, const char *domain
)
1956 if (!host
) host
= "";
1957 if (!domain
) domain
= "";
1959 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
) {
1960 if (keyword
== NETRC_DEFAULT
)
1963 if (keyword
== NETRC_MACDEF
) {
1964 /* Scan for an empty line, which concludes "macdef" */
1966 while (*netrcp
&& *netrcp
!= '\n')
1968 if (*netrcp
!= '\n')
1971 } while (*netrcp
&& *netrcp
!= '\n');
1975 if (keyword
!= NETRC_MACHINE
)
1978 /* Take machine name */
1979 if (ftpfs_netrc_next () == NETRC_NONE
)
1982 if (g_strcasecmp (host
, buffer
)) {
1983 /* Try adding our domain to short names in .netrc */
1984 const char *host_domain
= strchr (host
, '.');
1988 /* Compare domain part */
1989 if (g_strcasecmp (host_domain
, domain
))
1992 /* Compare local part */
1993 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
2004 /* Extract login and password from .netrc for the host.
2006 * Returns 0 for success, -1 for error */
2007 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
2010 char *tmp_pass
= NULL
;
2011 char hostname
[MAXHOSTNAMELEN
];
2014 static struct rupcache
{
2015 struct rupcache
*next
;
2019 } *rup_cache
= NULL
, *rupp
;
2021 /* Initialize *login and *pass */
2028 /* Look up in the cache first */
2029 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
2030 if (!strcmp (host
, rupp
->host
)) {
2032 *login
= g_strdup (rupp
->login
);
2033 if (pass
&& rupp
->pass
)
2034 *pass
= g_strdup (rupp
->pass
);
2039 /* Load current .netrc */
2040 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
2041 netrcp
= netrc
= load_file (netrcname
);
2042 if (netrc
== NULL
) {
2047 /* Find our own domain name */
2048 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2050 if (!(domain
= strchr (hostname
, '.')))
2053 /* Scan for "default" and matching "machine" keywords */
2054 ftpfs_find_machine (host
, domain
);
2056 /* Scan for keywords following "default" and "machine" */
2059 keyword
= ftpfs_netrc_next ();
2063 if (ftpfs_netrc_next () == NETRC_NONE
) {
2068 /* We have another name already - should not happen */
2074 /* We have login name now */
2075 *login
= g_strdup (buffer
);
2078 case NETRC_PASSWORD
:
2080 if (ftpfs_netrc_next () == NETRC_NONE
) {
2085 /* Ignore unsafe passwords */
2086 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2087 && ftpfs_netrc_bad_mode (netrcname
)) {
2092 /* Remember password. pass may be NULL, so use tmp_pass */
2093 if (tmp_pass
== NULL
)
2094 tmp_pass
= g_strdup (buffer
);
2098 /* "account" is followed by a token which we ignore */
2099 if (ftpfs_netrc_next () == NETRC_NONE
) {
2104 /* Ignore account, but warn user anyways */
2105 ftpfs_netrc_bad_mode (netrcname
);
2109 /* Unexpected keyword or end of file */
2121 rupp
= g_new (struct rupcache
, 1);
2122 rupp
->host
= g_strdup (host
);
2123 rupp
->login
= rupp
->pass
= 0;
2125 if (*login
!= NULL
) {
2126 rupp
->login
= g_strdup (*login
);
2128 if (tmp_pass
!= NULL
)
2129 rupp
->pass
= g_strdup (tmp_pass
);
2130 rupp
->next
= rup_cache
;
2142 static struct vfs_s_subclass ftpfs_subclass
;
2146 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
2147 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2148 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2149 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2150 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2151 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2152 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2153 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2154 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2155 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2156 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2158 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2159 vfs_ftpfs_ops
.name
= "ftpfs";
2160 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2161 vfs_ftpfs_ops
.prefix
= "ftp:";
2162 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2163 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2164 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2165 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2166 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2167 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2168 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2169 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2170 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2171 vfs_register_class (&vfs_ftpfs_ops
);