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 "lib/global.h"
88 #include "lib/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 "lib/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
,
188 const char *remote_path
);
189 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
,
190 const char *fmt
, ...) __attribute__ ((format (__printf__
, 4, 5)));
191 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
192 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
193 const char *netrcpass
);
194 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
197 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
199 if (!SUP
.remote_is_amiga
)
200 return g_strdup (remote_path
);
207 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
208 fflush (MEDATA
->logfile
);
211 /* strip leading slash(es) */
212 while (*remote_path
== '/')
216 * Don't change "/" into "", e.g. "CWD " would be
219 if (*remote_path
== '\0')
220 return g_strdup (".");
222 ret
= g_strdup (remote_path
);
224 /* replace first occurance of ":/" with ":" */
225 p
= strchr (ret
, ':');
226 if ((p
!= NULL
) && (*(p
+ 1) == '/'))
227 memmove (p
+ 1, p
+ 2, strlen (p
+ 2) + 1);
229 /* strip trailing "/." */
230 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
236 /* Extract the hostname and username from the path */
239 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
240 * ftp://sunsite.unc.edu/pub/linux
241 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
242 * ftp://tsx-11.mit.edu:8192/
243 * ftp://joe@foo.edu:11321/private
244 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
249 #define FTP_COMMAND_PORT 21
252 ftpfs_split_url (char *path
, char **host
, char **user
, int *port
, char **pass
)
256 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
260 /* Look up user and password in netrc */
262 ftpfs_netrc_lookup (*host
, user
, pass
);
264 *user
= g_strdup ("anonymous");
267 /* Look up password in netrc for known user */
268 if (use_netrc
&& *user
&& pass
&& !*pass
)
272 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
274 /* If user is different, remove password */
275 if (new_user
&& strcmp (*user
, new_user
))
287 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
289 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
296 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
303 switch (sscanf (answer
, "%d", &code
))
307 g_strlcpy (string_buf
, answer
, string_len
);
311 if (answer
[3] == '-')
315 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
322 if ((sscanf (answer
, "%d", &i
) > 0) && (code
== i
) && (answer
[3] == ' '))
327 g_strlcpy (string_buf
, answer
, string_len
);
334 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
336 int sock
= ftpfs_open_socket (me
, super
);
339 char *cwdir
= SUP
.cwdir
;
343 if (ftpfs_login_server (me
, super
, SUP
.password
))
347 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
349 return sock
== COMPLETE
;
357 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
,
363 static int retry
= 0;
364 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
367 cmdstr
= g_strdup_vprintf (fmt
, ap
);
370 cmdlen
= strlen (cmdstr
);
371 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
372 strcpy (cmdstr
+ cmdlen
, "\r\n");
377 if (strncmp (cmdstr
, "PASS ", 5) == 0)
379 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
384 ret
= fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
387 fflush (MEDATA
->logfile
);
391 tty_enable_interrupt_key ();
392 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
399 { /* Remote server has closed connection */
403 status
= ftpfs_reconnect (me
, super
);
405 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0))
414 tty_disable_interrupt_key ();
419 tty_disable_interrupt_key ();
423 status
= ftpfs_get_reply (me
, SUP
.sock
,
424 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
425 sizeof (reply_str
) - 1);
426 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
430 status
= ftpfs_reconnect (me
, super
);
432 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0))
446 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
450 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
451 ftpfs_command (me
, super
, NONE
, "QUIT");
457 g_free (SUP
.password
);
460 /* some defines only used by ftpfs_changetype */
461 /* These two are valid values for the second parameter */
463 #define TYPE_BINARY 1
465 /* This one is only used to initialize bucket->isbinary, don't use it as
466 second parameter to ftpfs_changetype. */
467 #define TYPE_UNKNOWN -1
470 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
472 if (binary
!= SUP
.isbinary
)
474 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
476 SUP
.isbinary
= binary
;
481 /* This routine logs the user in */
483 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
)
487 char *name
; /* login user name */
489 char reply_string
[BUF_MEDIUM
];
491 SUP
.isbinary
= TYPE_UNKNOWN
;
493 if (SUP
.password
) /* explicit password */
494 op
= g_strdup (SUP
.password
);
495 else if (netrcpass
) /* password from netrc */
496 op
= g_strdup (netrcpass
);
497 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp"))
499 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
500 ftpfs_init_passwd ();
501 op
= g_strdup (ftpfs_anonymous_passwd
);
508 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
, " ", (char *) NULL
);
509 op
= vfs_get_password (p
);
513 SUP
.password
= g_strdup (op
);
516 if (!anon
|| MEDATA
->logfile
)
520 pass
= g_strconcat ("-", op
, (char *) NULL
);
524 /* Proxy server accepts: username@host-we-want-to-connect */
528 g_strconcat (SUP
.user
, "@",
529 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
, (char *) NULL
);
532 name
= g_strdup (SUP
.user
);
534 if (ftpfs_get_reply (me
, SUP
.sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
)
536 g_strup (reply_string
);
537 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
540 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n", SUP
.remote_is_amiga
);
541 fflush (MEDATA
->logfile
);
544 print_vfs_message (_("ftpfs: sending login name"));
546 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
))
549 print_vfs_message (_("ftpfs: sending user password"));
550 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
551 if (code
== CONTINUE
)
555 p
= g_strdup_printf (_("FTP: Account required for user %s"), SUP
.user
);
556 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
560 print_vfs_message (_("ftpfs: sending user account"));
561 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
564 if (code
!= COMPLETE
)
569 print_vfs_message (_("ftpfs: logged in"));
570 wipe_password (pass
);
575 SUP
.failed_on_login
= 1;
577 wipe_password (SUP
.password
);
583 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "), SUP
.user
);
585 wipe_password (pass
);
590 static struct no_proxy_entry
597 ftpfs_load_no_proxy_list (void)
599 /* FixMe: shouldn't be hardcoded!!! */
600 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
601 struct no_proxy_entry
*np
, *current
= 0;
605 static char *mc_file
;
610 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
611 if (exist_file (mc_file
) && (npf
= fopen (mc_file
, "r")))
613 while (fgets (s
, sizeof (s
), npf
))
615 if (!(p
= strchr (s
, '\n')))
616 { /* skip bogus entries */
617 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
627 np
= g_new (struct no_proxy_entry
, 1);
628 np
->domain
= g_strdup (s
);
642 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
644 ftpfs_check_proxy (const char *host
)
646 struct no_proxy_entry
*npe
;
648 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
649 return 0; /* sanity check */
654 if (!ftpfs_always_use_proxy
)
657 if (!strchr (host
, '.'))
660 ftpfs_load_no_proxy_list ();
661 for (npe
= no_proxy
; npe
; npe
= npe
->next
)
663 char *domain
= npe
->domain
;
665 if (domain
[0] == '.')
667 int ld
= strlen (domain
);
668 int lh
= strlen (host
);
670 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1])
679 else if (!g_strcasecmp (host
, domain
))
687 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
691 dir
= vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
697 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
699 struct addrinfo hints
, *res
, *curr_res
;
708 /* Use a proxy host? */
709 host
= g_strdup (SUP
.host
);
713 print_vfs_message (_("ftpfs: Invalid host name."));
714 ftpfs_errno
= EINVAL
;
719 /* Hosts to connect to that start with a ! should use proxy */
724 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
727 port
= g_strdup_printf ("%hu", (unsigned short) tmp_port
);
735 tty_enable_interrupt_key (); /* clear the interrupt flag */
737 memset (&hints
, 0, sizeof (struct addrinfo
));
738 hints
.ai_socktype
= SOCK_STREAM
;
739 hints
.ai_flags
= AI_ADDRCONFIG
;
742 e
= getaddrinfo (host
, port
, &hints
, &res
);
748 tty_disable_interrupt_key ();
749 print_vfs_message (_("ftpfs: %s"), gai_strerror (e
));
751 ftpfs_errno
= EINVAL
;
755 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
)
758 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
763 if (curr_res
->ai_next
!= NULL
)
766 tty_disable_interrupt_key ();
767 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno
));
774 print_vfs_message (_("ftpfs: making connection to %s"), host
);
778 if (connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0)
784 if (errno
== EINTR
&& tty_got_interrupt ())
786 print_vfs_message (_("ftpfs: connection interrupted by user"));
788 else if (res
->ai_next
== NULL
)
790 print_vfs_message (_("ftpfs: connection to server failed: %s"),
791 unix_error_string (errno
));
799 tty_disable_interrupt_key ();
804 tty_disable_interrupt_key ();
809 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
811 int retry_seconds
, count_down
;
813 /* We do not want to use the passive if we are using proxies */
815 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
820 SUP
.failed_on_login
= 0;
822 SUP
.sock
= ftpfs_open_socket (me
, super
);
826 if (ftpfs_login_server (me
, super
, NULL
))
828 /* Logged in, no need to retry the connection */
833 if (SUP
.failed_on_login
)
835 /* Close only the socket descriptor */
842 if (ftpfs_retry_seconds
)
844 retry_seconds
= ftpfs_retry_seconds
;
845 tty_enable_interrupt_key ();
846 for (count_down
= retry_seconds
; count_down
; count_down
--)
848 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"),
851 if (tty_got_interrupt ())
853 /* ftpfs_errno = E; */
854 tty_disable_interrupt_key ();
858 tty_disable_interrupt_key ();
862 while (retry_seconds
);
864 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
866 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
871 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
872 const char *archive_name
, char *op
)
874 char *host
, *user
, *password
;
879 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
886 if (ftpfs_check_proxy (host
))
887 SUP
.proxy
= ftpfs_proxy_host
;
888 SUP
.password
= password
;
889 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
890 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
891 SUP
.isbinary
= TYPE_UNKNOWN
;
892 SUP
.remote_is_amiga
= 0;
893 super
->name
= g_strdup ("/");
894 super
->root
= vfs_s_new_inode (me
, super
, vfs_s_default_stat (me
, S_IFDIR
| 0755));
896 return ftpfs_open_archive_int (me
, super
);
900 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
901 const char *archive_name
, char *op
, void *cookie
)
910 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
912 port
= ((strcmp (host
, SUP
.host
) == 0) && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
920 /* The returned directory should always contain a trailing slash */
922 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
924 char buf
[BUF_8K
], *bufp
, *bufq
;
926 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
927 ftpfs_get_reply (me
, SUP
.sock
, buf
, sizeof (buf
)) == COMPLETE
)
930 for (bufq
= buf
; *bufq
; bufq
++)
942 if (*(bufq
- 1) != '/')
948 return g_strdup (bufp
);
951 /* If the remote server is an Amiga a leading slash
952 might be missing. MC needs it because it is used
953 as separator between hostname and path internally. */
954 return g_strconcat ("/", bufp
, (char *) NULL
);
970 /* Setup Passive ftp connection, we use it for source routed connections */
972 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
973 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
977 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") == COMPLETE
)
981 c
= strchr (reply_str
, '|');
990 if (port
< 0 || port
> 65535)
994 switch (sa
->ss_family
)
997 ((struct sockaddr_in
*) sa
)->sin_port
= port
;
1000 ((struct sockaddr_in6
*) sa
)->sin6_port
= port
;
1003 print_vfs_message (_("ftpfs: invalid address family"));
1004 ERRNOR (EINVAL
, -1);
1007 else if (sa
->ss_family
== AF_INET
)
1009 int xa
, xb
, xc
, xd
, xe
, xf
;
1012 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
1015 /* Parse remote parameters */
1016 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
1020 if (!isdigit ((unsigned char) *c
))
1022 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
1025 n
[0] = (unsigned char) xa
;
1026 n
[1] = (unsigned char) xb
;
1027 n
[2] = (unsigned char) xc
;
1028 n
[3] = (unsigned char) xd
;
1029 n
[4] = (unsigned char) xe
;
1030 n
[5] = (unsigned char) xf
;
1032 memcpy (&(((struct sockaddr_in
*) sa
)->sin_addr
.s_addr
), (void *) n
, 4);
1033 memcpy (&(((struct sockaddr_in
*) sa
)->sin_port
), (void *) &n
[4], 2);
1038 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
1045 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
1047 struct sockaddr_storage data_addr
;
1048 socklen_t data_addrlen
;
1049 int data_sock
, result
;
1052 memset (&data_addr
, 0, sizeof (struct sockaddr_storage
));
1053 data_addrlen
= sizeof (struct sockaddr_storage
);
1055 if (SUP
.use_passive_connection
)
1056 result
= getpeername (SUP
.sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
);
1058 result
= getsockname (SUP
.sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
);
1063 switch (data_addr
.ss_family
)
1066 ((struct sockaddr_in
*) &data_addr
)->sin_port
= 0;
1069 ((struct sockaddr_in6
*) &data_addr
)->sin6_port
= 0;
1072 print_vfs_message (_("ftpfs: invalid address family"));
1073 ERRNOR (EINVAL
, -1);
1076 data_sock
= socket (data_addr
.ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1079 if (SUP
.use_passive_connection
)
1081 print_vfs_message (_("ftpfs: could not setup passive mode: %s"),
1082 unix_error_string (errno
));
1083 SUP
.use_passive_connection
= 0;
1087 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1091 if (SUP
.use_passive_connection
)
1094 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1097 SUP
.use_passive_connection
= 0;
1098 print_vfs_message (_("ftpfs: could not setup passive mode"));
1104 /* If passive setup fails, fallback to active connections */
1105 /* Active FTP connection */
1106 if ((bind (data_sock
, (struct sockaddr
*) &data_addr
, data_addrlen
) == 0) &&
1107 (getsockname (data_sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == 0) &&
1108 (listen (data_sock
, 1) == 0))
1110 unsigned short int port
;
1114 switch (data_addr
.ss_family
)
1118 port
= ((struct sockaddr_in
*) &data_addr
)->sin_port
;
1122 port
= ((struct sockaddr_in6
*) &data_addr
)->sin6_port
;
1125 print_vfs_message (_("ftpfs: invalid address family"));
1126 ERRNOR (EINVAL
, -1);
1129 port
= ntohs (port
);
1131 addr
= g_try_malloc (NI_MAXHOST
);
1133 ERRNOR (ENOMEM
, -1);
1136 ((struct sockaddr
*) &data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0,
1137 NI_NUMERICHOST
) != 0)
1143 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
)
1152 unsigned char *a
= (unsigned char *) &((struct sockaddr_in
*) &data_addr
)->sin_addr
;
1153 unsigned char *p
= (unsigned char *) &port
;
1155 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1156 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1157 p
[0], p
[1]) == COMPLETE
)
1167 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1168 const char *remote
, int isbinary
, int reget
)
1170 struct sockaddr_storage from
;
1172 socklen_t fromlen
= sizeof (from
);
1174 if ((s
= ftpfs_initconn (me
, super
)) == -1)
1176 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1180 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1186 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1187 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1188 /* WarFtpD can't STORE //filename */
1189 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1190 g_free (remote_path
);
1193 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1196 tty_enable_interrupt_key ();
1197 if (SUP
.use_passive_connection
)
1201 data
= accept (s
, (struct sockaddr
*) &from
, &fromlen
);
1204 ftpfs_errno
= errno
;
1210 tty_disable_interrupt_key ();
1214 #define ABORT_TIMEOUT 5
1216 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1218 struct vfs_s_super
*super
= FH_SUPER
;
1219 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1222 int dsock
= FH_SOCK
;
1224 SUP
.ctl_connection_busy
= 0;
1226 print_vfs_message (_("ftpfs: aborting transfer."));
1227 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
))
1229 print_vfs_message (_("ftpfs: abort error: %s"), unix_error_string (errno
));
1235 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
)
1237 print_vfs_message (_("ftpfs: abort failed"));
1245 FD_SET (dsock
, &mask
);
1246 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0)
1248 struct timeval start_tim
, tim
;
1249 gettimeofday (&start_tim
, NULL
);
1250 /* flush the remaining data */
1251 while (read (dsock
, buf
, sizeof (buf
)) > 0)
1253 gettimeofday (&tim
, NULL
);
1254 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
)
1256 /* server keeps sending, drop the connection and ftpfs_reconnect */
1258 ftpfs_reconnect (me
, super
);
1265 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1266 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1271 resolve_symlink_without_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1272 struct vfs_s_inode
*dir
)
1274 struct linklist
*flist
;
1275 struct direntry
*fe
, *fel
;
1276 char tmp
[MC_MAXPATHLEN
];
1279 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1280 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
)
1282 /* flist->data->l_stat is alread initialized with 0 */
1284 if (S_ISLNK (fel
->s
.st_mode
) && fel
->linkname
)
1286 if (fel
->linkname
[0] == '/')
1288 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1290 strcpy (tmp
, fel
->linkname
);
1294 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1296 strcpy (tmp
, dir
->remote_path
);
1299 strcat (tmp
+ 1, fel
->linkname
);
1301 for (depth
= 0; depth
< 100; depth
++)
1302 { /* depth protects against recursive symbolic links */
1303 canonicalize_pathname (tmp
);
1304 fe
= _get_file_entry (bucket
, tmp
, 0, 0);
1307 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0)
1309 /* Symlink points to link which isn't resolved, yet. */
1310 if (fe
->linkname
[0] == '/')
1312 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1314 strcpy (tmp
, fe
->linkname
);
1318 /* at this point tmp looks always like this
1319 /directory/filename, i.e. no need to check
1320 strrchr's return value */
1321 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1322 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1324 strcat (tmp
, fe
->linkname
);
1330 fel
->l_stat
= g_new (struct stat
, 1);
1331 if (S_ISLNK (fe
->s
.st_mode
))
1332 *fel
->l_stat
= *fe
->l_stat
;
1334 *fel
->l_stat
= fe
->s
;
1335 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1342 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1346 resolve_symlink_with_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1347 struct vfs_s_inode
*dir
)
1349 char buffer
[2048] = "", *filename
;
1353 struct linklist
*flist
;
1354 struct direntry
*fe
;
1355 int switch_method
= 0;
1357 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1358 if (strchr (dir
->remote_path
, ' '))
1360 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
)
1362 print_vfs_message (_("ftpfs: CWD failed."));
1365 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1368 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", dir
->remote_path
, TYPE_ASCII
, 0);
1372 print_vfs_message (_("ftpfs: couldn't resolve symlink"));
1376 fp
= fdopen (sock
, "r");
1380 print_vfs_message (_("ftpfs: couldn't resolve symlink"));
1383 tty_enable_interrupt_key ();
1384 flist
= dir
->file_list
->next
;
1389 if (flist
== dir
->file_list
)
1392 flist
= flist
->next
;
1394 while (!S_ISLNK (fe
->s
.st_mode
));
1397 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1399 if (MEDATA
->logfile
)
1401 fputs (buffer
, MEDATA
->logfile
);
1402 fflush (MEDATA
->logfile
);
1404 vfs_die ("This code should be commented out\n");
1405 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
))
1407 int r
= strcmp (fe
->name
, filename
);
1411 if (S_ISLNK (s
.st_mode
))
1413 /* This server doesn't understand LIST -lLa */
1417 fe
->l_stat
= g_new (struct stat
, 1);
1418 if (fe
->l_stat
== NULL
)
1421 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1430 while (fgets (buffer
, sizeof (buffer
), fp
) != NULL
);
1431 tty_disable_interrupt_key ();
1433 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1437 resolve_symlink (struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1439 print_vfs_message (_("Resolving symlink..."));
1441 if (SUP
.strict_rfc959_list_cmd
)
1442 resolve_symlink_without_ls_options (me
, super
, dir
);
1444 resolve_symlink_with_ls_options (me
, super
, dir
);
1449 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1451 struct vfs_s_entry
*ent
;
1452 struct vfs_s_super
*super
= dir
->super
;
1453 int sock
, num_entries
= 0;
1454 char buffer
[BUF_8K
];
1457 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1458 || (strchr (remote_path
, ' ') != NULL
);
1461 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1464 RFC_STRICT
? _("(strict rfc959)") : "", cd_first
? _("(chdir first)") : "");
1468 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
)
1470 ftpfs_errno
= ENOENT
;
1471 print_vfs_message (_("ftpfs: CWD failed."));
1476 gettimeofday (&dir
->timestamp
, NULL
);
1477 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1479 if (SUP
.strict
== RFC_STRICT
)
1480 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1482 /* Dirty hack to avoid autoprepending / to . */
1483 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1484 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1487 /* Trailing "/." is necessary if remote_path is a symlink */
1488 char *path
= concat_dir_and_file (remote_path
, ".");
1489 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1496 /* Clear the interrupt flag */
1497 tty_enable_interrupt_key ();
1502 int res
= vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1509 me
->verrno
= ECONNRESET
;
1511 tty_disable_interrupt_key ();
1512 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1513 print_vfs_message (_("%s: failure"), me
->name
);
1517 if (MEDATA
->logfile
)
1519 fputs (buffer
, MEDATA
->logfile
);
1520 fputs ("\n", MEDATA
->logfile
);
1521 fflush (MEDATA
->logfile
);
1524 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1525 i
= ent
->ino
->st
.st_nlink
;
1526 if (!vfs_parse_ls_lga (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
))
1528 vfs_s_free_entry (me
, ent
);
1531 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1533 vfs_s_insert_entry (me
, dir
, ent
);
1537 me
->verrno
= E_REMOTE
;
1538 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1541 if (num_entries
== 0 && cd_first
== 0)
1543 /* The LIST command may produce an empty output. In such scenario
1544 it is not clear whether this is caused by `remote_path' being
1545 a non-existent path or for some other reason (listing emtpy
1546 directory without the -a option, non-readable directory, etc.).
1548 Since `dir_load' is a crucial method, when it comes to determine
1549 whether a given path is a _directory_, the code must try its best
1550 to determine the type of `remote_path'. The only reliable way to
1551 achieve this is trough issuing a CWD command. */
1557 if (SUP
.strict
== RFC_AUTODETECT
)
1558 SUP
.strict
= RFC_DARING
;
1560 print_vfs_message (_("%s: done."), me
->name
);
1564 if (SUP
.strict
== RFC_AUTODETECT
)
1566 /* It's our first attempt to get a directory listing from this
1567 server (UNIX style LIST command) */
1568 SUP
.strict
= RFC_STRICT
;
1569 /* I hate goto, but recursive call needs another 8K on stack */
1570 /* return ftpfs_dir_load (me, dir, remote_path); */
1574 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1575 ERRNOR (EACCES
, -1);
1579 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
, char *localname
)
1581 int h
, sock
, n_read
, n_written
;
1583 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1591 struct vfs_s_super
*super
= FH_SUPER
;
1593 h
= open (localname
, O_RDONLY
);
1597 ftpfs_open_data_connection (me
, super
,
1598 fh
->u
.ftp
.append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1599 if (sock
< 0 || fstat (h
, &s
) == -1)
1604 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1607 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1609 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1613 tty_enable_interrupt_key ();
1616 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1)
1620 if (tty_got_interrupt ())
1622 ftpfs_errno
= EINTR
;
1628 ftpfs_errno
= errno
;
1635 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
)
1637 if (n_written
== -1)
1639 if (errno
== EINTR
&& !tty_got_interrupt ())
1643 ftpfs_errno
= errno
;
1647 n_read
-= n_written
;
1649 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1650 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1652 tty_disable_interrupt_key ();
1655 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1659 tty_disable_interrupt_key ();
1662 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1667 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1669 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1673 FH_SOCK
= ftpfs_open_data_connection (me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1677 fh
->linear
= LS_LINEAR_OPEN
;
1678 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1679 fh
->u
.ftp
.append
= 0;
1684 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1687 struct vfs_s_super
*super
= FH_SUPER
;
1689 while ((n
= read (FH_SOCK
, buf
, len
)) < 0)
1691 if ((errno
== EINTR
) && !tty_got_interrupt ())
1697 ftpfs_linear_abort (me
, fh
);
1701 SUP
.ctl_connection_busy
= 0;
1704 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1705 ERRNOR (E_REMOTE
, -1);
1712 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1715 ftpfs_linear_abort (me
, fh
);
1719 ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1725 case VFS_CTL_IS_NOTREADY
:
1730 vfs_die ("You may not do this");
1731 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1734 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1735 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1745 ftpfs_send_command (struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1748 char *p
, *mpath
= g_strdup (filename
);
1749 struct vfs_s_super
*super
;
1751 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1753 if (!(rpath
= vfs_s_get_path_mangle (me
, mpath
, &super
, 0)))
1758 p
= ftpfs_translate_path (me
, super
, rpath
);
1759 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1761 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1762 if (flags
& OPT_IGNORE_ERROR
)
1770 if (flush_directory_cache
)
1771 vfs_s_invalidate (me
, super
);
1776 /* This routine is called as the last step in load_setup */
1778 ftpfs_init_passwd (void)
1780 ftpfs_anonymous_passwd
= load_anon_passwd ();
1781 if (ftpfs_anonymous_passwd
)
1784 /* If there is no anonymous ftp password specified
1785 * then we'll just use anonymous@
1786 * We don't send any other thing because:
1787 * - We want to remain anonymous
1788 * - We want to stop SPAM
1789 * - We don't want to let ftp sites to discriminate by the user,
1792 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1796 ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1798 char buf
[BUF_SMALL
];
1801 g_snprintf (buf
, sizeof (buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1803 ret
= ftpfs_send_command (me
, path
, buf
, OPT_FLUSH
);
1805 if (mc_config_get_bool (mc_main_config
, CONFIG_APP_SECTION
, "ignore_ftp_chattr_errors", TRUE
))
1814 ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1817 ftpfs_errno
= EPERM
;
1820 /* Everyone knows it is not possible to chown remotely, so why bother them.
1821 If someone's root, then copy/move will always try to chown it... */
1831 ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1833 return ftpfs_send_command (me
, path
, "DELE /%s", OPT_FLUSH
);
1836 /* Return 1 if path is the same directory as the one we are in now */
1838 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1844 if (strcmp (path
, SUP
.cwdir
) == 0)
1850 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1855 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1858 p
= ftpfs_translate_path (me
, super
, remote_path
);
1859 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1869 SUP
.cwdir
= g_strdup (remote_path
);
1870 SUP
.cwd_deferred
= 0;
1876 ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1878 ftpfs_send_command (me
, path1
, "RNFR /%s", OPT_FLUSH
);
1879 return ftpfs_send_command (me
, path2
, "RNTO /%s", OPT_FLUSH
);
1883 ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1885 (void) mode
; /* FIXME: should be used */
1887 return ftpfs_send_command (me
, path
, "MKD /%s", OPT_FLUSH
);
1891 ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1893 return ftpfs_send_command (me
, path
, "RMD /%s", OPT_FLUSH
);
1897 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
, int mode
)
1901 fh
->u
.ftp
.append
= 0;
1902 /* File will be written only, so no need to retrieve it from ftp server */
1903 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
)))
1905 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1912 /* ftpfs_linear_start() called, so data will be written
1913 * to local temporary file and stored to ftp server
1914 * by vfs_s_close later
1916 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
)
1918 if (!fh
->ino
->localname
)
1920 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1921 fh
->ino
->ent
->name
);
1925 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1929 name
= vfs_s_fullpath (me
, fh
->ino
);
1933 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1934 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1939 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1943 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1945 if (fh
->ino
->localname
)
1947 unlink (fh
->ino
->localname
);
1948 g_free (fh
->ino
->localname
);
1949 fh
->ino
->localname
= NULL
;
1954 if (!fh
->ino
->localname
)
1955 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1957 if (!fh
->ino
->localname
)
1958 vfs_die ("retrieve_file failed to fill in localname");
1963 ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1965 if (fh
->handle
!= -1 && !fh
->ino
->localname
)
1969 /* File is stored to destination already, so
1970 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1973 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1975 vfs_s_invalidate (me
, FH_SUPER
);
1981 ftpfs_done (struct vfs_class
*me
)
1983 struct no_proxy_entry
*np
;
1989 np
= no_proxy
->next
;
1990 g_free (no_proxy
->domain
);
1994 g_free (ftpfs_anonymous_passwd
);
1995 g_free (ftpfs_proxy_host
);
1999 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
2001 struct vfs_s_super
*super
= MEDATA
->supers
;
2006 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
2009 super
= super
->next
;
2013 static char buffer
[BUF_MEDIUM
];
2015 static const char *netrcp
;
2017 /* This should match the keywords[] array below */
2032 ftpfs_netrc_next (void)
2036 static const char *const keywords
[] = { "default", "machine",
2037 "login", "password", "passwd", "account", "macdef", NULL
2043 netrcp
= skip_separators (netrcp
);
2044 if (*netrcp
!= '\n')
2053 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++)
2055 if (*netrcp
== '\\')
2062 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
2063 *netrcp
!= ',' && *netrcp
; netrcp
++)
2065 if (*netrcp
== '\\')
2075 while (keywords
[i
- 1])
2077 if (!strcmp (keywords
[i
- 1], buffer
))
2083 return NETRC_UNKNOWN
;
2087 ftpfs_netrc_bad_mode (const char *netrcname
)
2089 static int be_angry
= 1;
2092 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077))
2096 message (D_ERROR
, MSG_ERROR
,
2097 _("~/.netrc file has incorrect mode.\n" "Remove password or correct mode."));
2105 /* Scan .netrc until we find matching "machine" or "default"
2106 * domain is used for additional matching
2107 * No search is done after "default" in compliance with "man netrc"
2108 * Return 0 if found, -1 otherwise */
2110 ftpfs_find_machine (const char *host
, const char *domain
)
2119 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
)
2121 if (keyword
== NETRC_DEFAULT
)
2124 if (keyword
== NETRC_MACDEF
)
2126 /* Scan for an empty line, which concludes "macdef" */
2129 while (*netrcp
&& *netrcp
!= '\n')
2131 if (*netrcp
!= '\n')
2135 while (*netrcp
&& *netrcp
!= '\n');
2139 if (keyword
!= NETRC_MACHINE
)
2142 /* Take machine name */
2143 if (ftpfs_netrc_next () == NETRC_NONE
)
2146 if (g_strcasecmp (host
, buffer
))
2148 /* Try adding our domain to short names in .netrc */
2149 const char *host_domain
= strchr (host
, '.');
2153 /* Compare domain part */
2154 if (g_strcasecmp (host_domain
, domain
))
2157 /* Compare local part */
2158 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
2169 /* Extract login and password from .netrc for the host.
2171 * Returns 0 for success, -1 for error */
2173 ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
2176 char *tmp_pass
= NULL
;
2177 char hostname
[MAXHOSTNAMELEN
];
2180 static struct rupcache
2182 struct rupcache
*next
;
2186 } *rup_cache
= NULL
, *rupp
;
2188 /* Initialize *login and *pass */
2195 /* Look up in the cache first */
2196 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
)
2198 if (!strcmp (host
, rupp
->host
))
2201 *login
= g_strdup (rupp
->login
);
2202 if (pass
&& rupp
->pass
)
2203 *pass
= g_strdup (rupp
->pass
);
2208 /* Load current .netrc */
2209 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
2210 netrcp
= netrc
= load_file (netrcname
);
2217 /* Find our own domain name */
2218 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2220 if (!(domain
= strchr (hostname
, '.')))
2223 /* Scan for "default" and matching "machine" keywords */
2224 ftpfs_find_machine (host
, domain
);
2226 /* Scan for keywords following "default" and "machine" */
2230 keyword
= ftpfs_netrc_next ();
2235 if (ftpfs_netrc_next () == NETRC_NONE
)
2241 /* We have another name already - should not happen */
2248 /* We have login name now */
2249 *login
= g_strdup (buffer
);
2252 case NETRC_PASSWORD
:
2254 if (ftpfs_netrc_next () == NETRC_NONE
)
2260 /* Ignore unsafe passwords */
2261 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2262 && ftpfs_netrc_bad_mode (netrcname
))
2268 /* Remember password. pass may be NULL, so use tmp_pass */
2269 if (tmp_pass
== NULL
)
2270 tmp_pass
= g_strdup (buffer
);
2274 /* "account" is followed by a token which we ignore */
2275 if (ftpfs_netrc_next () == NETRC_NONE
)
2281 /* Ignore account, but warn user anyways */
2282 ftpfs_netrc_bad_mode (netrcname
);
2286 /* Unexpected keyword or end of file */
2298 rupp
= g_new (struct rupcache
, 1);
2299 rupp
->host
= g_strdup (host
);
2300 rupp
->login
= rupp
->pass
= 0;
2304 rupp
->login
= g_strdup (*login
);
2306 if (tmp_pass
!= NULL
)
2307 rupp
->pass
= g_strdup (tmp_pass
);
2308 rupp
->next
= rup_cache
;
2320 static struct vfs_s_subclass ftpfs_subclass
;
2324 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
2325 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2326 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2327 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2328 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2329 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2330 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2331 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2332 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2333 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2334 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2336 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2337 vfs_ftpfs_ops
.name
= "ftpfs";
2338 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2339 vfs_ftpfs_ops
.prefix
= "ftp:";
2340 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2341 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2342 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2343 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2344 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2345 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2346 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2347 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2348 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2349 vfs_register_class (&vfs_ftpfs_ops
);