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:
47 * cd /#ftp:pavel@hobit
49 * And now: what do I want to do? Do I want to go to /home/pavel or to
50 * /#ftp:hobit/home/pavel? I think first has better sense...
53 int f = !strcmp( remote_path, "/~" );
54 if (f || !strncmp( remote_path, "/~/", 3 )) {
56 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
65 /* \todo Fix: Namespace pollution: horrible */
68 #include <stdlib.h> /* atoi() */
69 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
70 #include <netdb.h> /* struct hostent */
71 #include <sys/socket.h> /* AF_INET */
72 #include <netinet/in.h> /* struct in_addr */
73 #ifdef HAVE_ARPA_INET_H
74 #include <arpa/inet.h>
77 #include <arpa/telnet.h>
78 #include <sys/param.h>
82 #include <sys/time.h> /* gettimeofday() */
84 #include "../src/global.h"
86 #include "../src/tty/tty.h" /* enable/disable interrupt key */
88 #include "../src/wtools.h" /* message() */
89 #include "../src/main.h" /* print_vfs_message */
90 #include "../src/history.h"
91 #include "../src/setup.h" /* for load_anon_passwd */
92 #include "../src/mcconfig/mcconfig.h"
95 #include "xdirentry.h"
98 #include "gc.h" /* vfs_stamp_create */
101 #ifndef MAXHOSTNAMELEN
102 # define MAXHOSTNAMELEN 64
105 #define UPLOAD_ZERO_LENGTH_FILE
106 #define SUP super->u.ftp
107 #define FH_SOCK fh->u.ftp.sock
110 #define INADDR_NONE 0xffffffff
113 /* for uclibc < 0.9.29 */
114 #ifndef AI_ADDRCONFIG
115 #define AI_ADDRCONFIG 0x0020
118 #define RFC_AUTODETECT 0
122 #ifndef HAVE_SOCKLEN_T
123 typedef int socklen_t
;
126 static int ftpfs_errno
;
129 /* Delay to retry a connection */
130 int ftpfs_retry_seconds
= 30;
132 /* Method to use to connect to ftp sites */
133 int ftpfs_use_passive_connections
= 1;
134 int ftpfs_use_passive_connections_over_proxy
= 0;
136 /* Method used to get directory listings:
137 * 1: try 'LIST -la <path>', if it fails
138 * fall back to CWD <path>; LIST
139 * 0: always use CWD <path>; LIST
141 int ftpfs_use_unix_list_options
= 1;
143 /* First "CWD <path>", then "LIST -la ." */
144 int ftpfs_first_cd_then_ls
= 1;
146 /* Use the ~/.netrc */
149 /* Anonymous setup */
150 char *ftpfs_anonymous_passwd
= NULL
;
151 int ftpfs_directory_timeout
= 900;
154 char *ftpfs_proxy_host
= NULL
;
156 /* wether we have to use proxy by default? */
157 int ftpfs_always_use_proxy
;
159 #ifdef FIXME_LATER_ALIGATOR
160 static struct linklist
*connections_list
;
163 /* ftpfs_command wait_flag: */
165 #define WAIT_REPLY 0x01
166 #define WANT_STRING 0x02
167 static char reply_str
[80];
169 static struct vfs_class vfs_ftpfs_ops
;
171 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
172 Translate a Unix path, i.e. MC's internal path representation (e.g.
173 /somedir/somefile) to a path valid for the remote server. Every path
174 transfered to the remote server has to be mangled by this function
175 right prior to sending it.
176 Currently only Amiga ftp servers are handled in a special manner.
178 When the remote server is an amiga:
179 a) strip leading slash if necesarry
180 b) replace first occurance of ":/" with ":"
181 c) strip trailing "/."
184 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
185 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
);
186 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
187 __attribute__ ((format (__printf__
, 4, 5)));
188 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
189 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
);
190 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
193 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
195 if (!SUP
.remote_is_amiga
)
196 return g_strdup (remote_path
);
200 if (MEDATA
->logfile
) {
201 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
202 fflush (MEDATA
->logfile
);
205 /* strip leading slash(es) */
206 while (*remote_path
== '/')
210 * Don't change "/" into "", e.g. "CWD " would be
213 if (*remote_path
== '\0')
214 return g_strdup (".");
216 ret
= g_strdup (remote_path
);
218 /* replace first occurance of ":/" with ":" */
219 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
220 strcpy (p
+ 1, p
+ 2);
222 /* strip trailing "/." */
223 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
229 /* Extract the hostname and username from the path */
232 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
233 * ftp://sunsite.unc.edu/pub/linux
234 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
235 * ftp://tsx-11.mit.edu:8192/
236 * ftp://joe@foo.edu:11321/private
237 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
242 #define FTP_COMMAND_PORT 21
245 ftpfs_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
249 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
253 /* Look up user and password in netrc */
255 ftpfs_netrc_lookup (*host
, user
, pass
);
257 *user
= g_strdup ("anonymous");
260 /* Look up password in netrc for known user */
261 if (use_netrc
&& *user
&& pass
&& !*pass
) {
264 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
266 /* If user is different, remove password */
267 if (new_user
&& strcmp (*user
, new_user
)) {
278 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
280 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
286 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
292 switch (sscanf(answer
, "%d", &code
)){
295 g_strlcpy (string_buf
, answer
, string_len
);
299 if (answer
[3] == '-') {
301 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
307 if ((sscanf (answer
, "%d", &i
) > 0) &&
308 (code
== i
) && (answer
[3] == ' '))
313 g_strlcpy (string_buf
, answer
, string_len
);
320 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
322 int sock
= ftpfs_open_socket (me
, super
);
324 char *cwdir
= SUP
.cwdir
;
328 if (ftpfs_login_server (me
, super
, SUP
.password
)){
331 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
333 return sock
== COMPLETE
;
341 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
346 static int retry
= 0;
347 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
350 cmdstr
= g_strdup_vprintf (fmt
, ap
);
353 cmdlen
= strlen (cmdstr
);
354 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
355 strcpy (cmdstr
+ cmdlen
, "\r\n");
358 if (MEDATA
->logfile
) {
359 if (strncmp (cmdstr
, "PASS ", 5) == 0) {
360 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
362 fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
364 fflush (MEDATA
->logfile
);
368 tty_enable_interrupt_key ();
369 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
374 if (errno
== EPIPE
) { /* Remote server has closed connection */
377 status
= ftpfs_reconnect (me
, super
);
379 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
387 tty_disable_interrupt_key ();
392 tty_disable_interrupt_key ();
396 status
= ftpfs_get_reply (me
, SUP
.sock
,
397 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
398 sizeof (reply_str
) - 1);
399 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
403 status
= ftpfs_reconnect (me
, super
);
405 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
418 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
421 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
422 ftpfs_command(me
, super
, NONE
, "QUIT");
428 g_free (SUP
.password
);
431 /* some defines only used by ftpfs_changetype */
432 /* These two are valid values for the second parameter */
434 #define TYPE_BINARY 1
436 /* This one is only used to initialize bucket->isbinary, don't use it as
437 second parameter to ftpfs_changetype. */
438 #define TYPE_UNKNOWN -1
441 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
443 if (binary
!= SUP
.isbinary
) {
444 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
446 SUP
.isbinary
= binary
;
451 /* This routine logs the user in */
453 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
454 const char *netrcpass
)
458 char *name
; /* login user name */
460 char reply_string
[BUF_MEDIUM
];
462 SUP
.isbinary
= TYPE_UNKNOWN
;
464 if (SUP
.password
) /* explicit password */
465 op
= g_strdup (SUP
.password
);
466 else if (netrcpass
) /* password from netrc */
467 op
= g_strdup (netrcpass
);
468 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp")) {
469 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
470 ftpfs_init_passwd ();
471 op
= g_strdup (ftpfs_anonymous_passwd
);
473 } else { /* ask user */
476 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
, " ",
478 op
= vfs_get_password (p
);
482 SUP
.password
= g_strdup (op
);
485 if (!anon
|| MEDATA
->logfile
)
488 pass
= g_strconcat ("-", op
, (char *) NULL
);
492 /* Proxy server accepts: username@host-we-want-to-connect */
495 g_strconcat (SUP
.user
, "@",
496 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
,
499 name
= g_strdup (SUP
.user
);
502 (me
, SUP
.sock
, reply_string
,
503 sizeof (reply_string
) - 1) == COMPLETE
) {
504 g_strup (reply_string
);
505 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
506 if (MEDATA
->logfile
) {
507 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n",
508 SUP
.remote_is_amiga
);
509 fflush (MEDATA
->logfile
);
512 print_vfs_message (_("ftpfs: sending login name"));
514 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
)) {
516 print_vfs_message (_("ftpfs: sending user password"));
517 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
518 if (code
== CONTINUE
) {
521 p
= g_strdup_printf (_
522 ("FTP: Account required for user %s"),
524 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
528 print_vfs_message (_("ftpfs: sending user account"));
530 ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
533 if (code
!= COMPLETE
)
538 print_vfs_message (_("ftpfs: logged in"));
539 wipe_password (pass
);
544 SUP
.failed_on_login
= 1;
546 wipe_password (SUP
.password
);
552 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
555 wipe_password (pass
);
560 static struct no_proxy_entry
{
566 ftpfs_load_no_proxy_list (void)
568 /* FixMe: shouldn't be hardcoded!!! */
569 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
570 struct no_proxy_entry
*np
, *current
= 0;
574 static char *mc_file
;
579 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
580 if (exist_file (mc_file
) &&
581 (npf
= fopen (mc_file
, "r"))) {
582 while (fgets (s
, sizeof (s
), npf
)) {
583 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
584 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
594 np
= g_new (struct no_proxy_entry
, 1);
595 np
->domain
= g_strdup (s
);
609 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
611 ftpfs_check_proxy (const char *host
)
613 struct no_proxy_entry
*npe
;
615 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
616 return 0; /* sanity check */
621 if (!ftpfs_always_use_proxy
)
624 if (!strchr (host
, '.'))
627 ftpfs_load_no_proxy_list ();
628 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
629 char *domain
= npe
->domain
;
631 if (domain
[0] == '.') {
632 int ld
= strlen (domain
);
633 int lh
= strlen (host
);
635 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
643 if (!g_strcasecmp (host
, domain
))
651 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
656 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
663 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
665 struct addrinfo hints
, *res
, *curr_res
;
674 /* Use a proxy host? */
675 host
= g_strdup(SUP
.host
);
677 if (!host
|| !*host
){
678 print_vfs_message (_("ftpfs: Invalid host name."));
679 ftpfs_errno
= EINVAL
;
683 /* Hosts to connect to that start with a ! should use proxy */
687 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
690 port
= g_strdup_printf("%hu", (unsigned short) tmp_port
);
697 tty_enable_interrupt_key(); /* clear the interrupt flag */
699 memset (&hints
, 0, sizeof (struct addrinfo
));
700 hints
.ai_socktype
= SOCK_STREAM
;
701 hints
.ai_flags
= AI_ADDRCONFIG
;
704 e
= getaddrinfo (host
, port
, &hints
, &res
);
709 tty_disable_interrupt_key ();
710 print_vfs_message (_("ftpfs: %s"), gai_strerror (e
));
712 ftpfs_errno
= EINVAL
;
716 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
) {
718 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
722 if (curr_res
->ai_next
!= NULL
)
725 tty_disable_interrupt_key();
726 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno
));
733 print_vfs_message (_("ftpfs: making connection to %s"), host
);
737 if ( connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0 )
743 if (errno
== EINTR
&& tty_got_interrupt ()) {
744 print_vfs_message (_("ftpfs: connection interrupted by user"));
745 } else if (res
->ai_next
== NULL
) {
746 print_vfs_message (_("ftpfs: connection to server failed: %s"),
747 unix_error_string (errno
));
753 tty_disable_interrupt_key ();
758 tty_disable_interrupt_key ();
763 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
765 int retry_seconds
, count_down
;
767 /* We do not want to use the passive if we are using proxies */
769 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
773 SUP
.failed_on_login
= 0;
775 SUP
.sock
= ftpfs_open_socket (me
, super
);
779 if (ftpfs_login_server (me
, super
, NULL
)) {
780 /* Logged in, no need to retry the connection */
783 if (SUP
.failed_on_login
){
784 /* Close only the socket descriptor */
789 if (ftpfs_retry_seconds
){
790 retry_seconds
= ftpfs_retry_seconds
;
791 tty_enable_interrupt_key ();
792 for (count_down
= retry_seconds
; count_down
; count_down
--){
793 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
795 if (tty_got_interrupt ()) {
796 /* ftpfs_errno = E; */
797 tty_disable_interrupt_key ();
801 tty_disable_interrupt_key ();
804 } while (retry_seconds
);
806 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
808 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
813 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
814 const char *archive_name
, char *op
)
816 char *host
, *user
, *password
;
821 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
828 if (ftpfs_check_proxy (host
))
829 SUP
.proxy
= ftpfs_proxy_host
;
830 SUP
.password
= password
;
831 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
832 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
833 SUP
.isbinary
= TYPE_UNKNOWN
;
834 SUP
.remote_is_amiga
= 0;
835 super
->name
= g_strdup ("/");
837 vfs_s_new_inode (me
, super
,
838 vfs_s_default_stat (me
, S_IFDIR
| 0755));
840 return ftpfs_open_archive_int (me
, super
);
844 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
845 const char *archive_name
, char *op
, void *cookie
)
854 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
856 port
= ((strcmp (host
, SUP
.host
) == 0)
857 && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
865 /* The returned directory should always contain a trailing slash */
867 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
869 char buf
[BUF_8K
], *bufp
, *bufq
;
871 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
872 ftpfs_get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
874 for (bufq
= buf
; *bufq
; bufq
++)
881 if (*(bufq
- 1) != '/') {
886 return g_strdup (bufp
);
888 /* If the remote server is an Amiga a leading slash
889 might be missing. MC needs it because it is used
890 as separator between hostname and path internally. */
891 return g_strconcat( "/", bufp
, NULL
);
905 /* Setup Passive ftp connection, we use it for source routed connections */
907 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
908 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
*salen
)
912 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") == COMPLETE
) {
915 c
= strchr (reply_str
, '|');
924 if (port
< 0 || port
> 65535)
928 switch (sa
->ss_family
) {
930 ((struct sockaddr_in
*)sa
)->sin_port
= port
;
933 ((struct sockaddr_in6
*)sa
)->sin6_port
= port
;
936 print_vfs_message (_("ftpfs: invalid address family"));
939 } else if (sa
->ss_family
== AF_INET
) {
940 int xa
, xb
, xc
, xd
, xe
, xf
;
943 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
946 /* Parse remote parameters */
947 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
951 if (!isdigit ((unsigned char) *c
))
953 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
956 n
[0] = (unsigned char) xa
;
957 n
[1] = (unsigned char) xb
;
958 n
[2] = (unsigned char) xc
;
959 n
[3] = (unsigned char) xd
;
960 n
[4] = (unsigned char) xe
;
961 n
[5] = (unsigned char) xf
;
963 memcpy (&(((struct sockaddr_in
*)sa
)->sin_addr
.s_addr
), (void *)n
, 4);
964 memcpy (&(((struct sockaddr_in
*)sa
)->sin_port
), (void *)&n
[4], 2);
968 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
975 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
977 struct sockaddr_storage data_addr
;
978 socklen_t data_addrlen
;
982 memset (&data_addr
, 0, sizeof (struct sockaddr_storage
));
983 data_addrlen
= sizeof (struct sockaddr_storage
);
985 if (getpeername (SUP
.sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == -1)
988 switch (data_addr
.ss_family
) {
990 ((struct sockaddr_in
*)&data_addr
)->sin_port
= 0;
993 ((struct sockaddr_in6
*)&data_addr
)->sin6_port
= 0;
996 print_vfs_message (_("ftpfs: invalid address family"));
1000 data_sock
= socket (data_addr
.ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1001 if (data_sock
< 0) {
1002 if (SUP
.use_passive_connection
) {
1003 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno
));
1004 SUP
.use_passive_connection
= 0;
1008 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1012 if (SUP
.use_passive_connection
) {
1014 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1017 SUP
.use_passive_connection
= 0;
1018 print_vfs_message (_("ftpfs: could not setup passive mode"));
1024 /* If passive setup fails, fallback to active connections */
1025 /* Active FTP connection */
1026 if ((bind (data_sock
, (struct sockaddr
*)&data_addr
, data_addrlen
) == 0) &&
1027 (getsockname (data_sock
, (struct sockaddr
*)&data_addr
, &data_addrlen
) == 0) &&
1028 (listen (data_sock
, 1) == 0)) {
1029 unsigned short int port
;
1033 switch (data_addr
.ss_family
) {
1036 port
= ((struct sockaddr_in
*)&data_addr
)->sin_port
;
1040 port
= ((struct sockaddr_in6
*)&data_addr
)->sin6_port
;
1043 print_vfs_message (_("ftpfs: invalid address family"));
1044 ERRNOR (EINVAL
, -1);
1047 port
= ntohs (port
);
1049 addr
= g_malloc (NI_MAXHOST
);
1051 ERRNOR (ENOMEM
, -1);
1053 if (getnameinfo ((struct sockaddr
*)&data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0, NI_NUMERICHOST
) != 0) {
1058 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
) {
1064 if (FTP_INET
== af
) {
1065 unsigned char *a
= (unsigned char *)&((struct sockaddr_in
*)&data_addr
)->sin_addr
;
1066 unsigned char *p
= (unsigned char *)&port
;
1068 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1069 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1070 p
[0], p
[1]) == COMPLETE
)
1080 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1081 const char *remote
, int isbinary
, int reget
)
1083 struct sockaddr_storage from
;
1085 socklen_t fromlen
= sizeof(from
);
1087 if ((s
= ftpfs_initconn (me
, super
)) == -1)
1089 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1092 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1097 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1098 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1099 /* WarFtpD can't STORE //filename */
1100 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1101 g_free (remote_path
);
1103 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1106 tty_enable_interrupt_key ();
1107 if (SUP
.use_passive_connection
)
1110 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
1112 ftpfs_errno
= errno
;
1118 tty_disable_interrupt_key ();
1122 #define ABORT_TIMEOUT 5
1124 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1126 struct vfs_s_super
*super
= FH_SUPER
;
1127 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1130 int dsock
= FH_SOCK
;
1132 SUP
.ctl_connection_busy
= 0;
1134 print_vfs_message (_("ftpfs: aborting transfer."));
1135 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
1136 print_vfs_message (_("ftpfs: abort error: %s"),
1137 unix_error_string (errno
));
1143 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1144 print_vfs_message (_("ftpfs: abort failed"));
1151 FD_SET (dsock
, &mask
);
1152 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1153 struct timeval start_tim
, tim
;
1154 gettimeofday (&start_tim
, NULL
);
1155 /* flush the remaining data */
1156 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1157 gettimeofday (&tim
, NULL
);
1158 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1159 /* server keeps sending, drop the connection and ftpfs_reconnect */
1161 ftpfs_reconnect (me
, super
);
1168 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1169 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1174 resolve_symlink_without_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1176 struct linklist
*flist
;
1177 struct direntry
*fe
, *fel
;
1178 char tmp
[MC_MAXPATHLEN
];
1181 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1182 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1183 /* flist->data->l_stat is alread initialized with 0 */
1185 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1186 if (fel
->linkname
[0] == '/') {
1187 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1189 strcpy (tmp
, fel
->linkname
);
1191 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1193 strcpy (tmp
, dir
->remote_path
);
1196 strcat (tmp
+ 1, fel
->linkname
);
1198 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1199 canonicalize_pathname (tmp
);
1200 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1202 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1203 /* Symlink points to link which isn't resolved, yet. */
1204 if (fe
->linkname
[0] == '/') {
1205 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1207 strcpy (tmp
, fe
->linkname
);
1209 /* at this point tmp looks always like this
1210 /directory/filename, i.e. no need to check
1211 strrchr's return value */
1212 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1213 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1215 strcat (tmp
, fe
->linkname
);
1219 fel
->l_stat
= g_new (struct stat
, 1);
1220 if ( S_ISLNK (fe
->s
.st_mode
))
1221 *fel
->l_stat
= *fe
->l_stat
;
1223 *fel
->l_stat
= fe
->s
;
1224 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1231 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1235 resolve_symlink_with_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1237 char buffer
[2048] = "", *filename
;
1241 struct linklist
*flist
;
1242 struct direntry
*fe
;
1243 int switch_method
= 0;
1245 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1246 if (strchr (dir
->remote_path
, ' ')) {
1247 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1248 print_vfs_message(_("ftpfs: CWD failed."));
1251 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1254 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa",
1255 dir
->remote_path
, TYPE_ASCII
, 0);
1258 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1262 fp
= fdopen(sock
, "r");
1265 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1268 tty_enable_interrupt_key ();
1269 flist
= dir
->file_list
->next
;
1272 if (flist
== dir
->file_list
)
1275 flist
= flist
->next
;
1276 } while (!S_ISLNK(fe
->s
.st_mode
));
1278 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1280 if (MEDATA
->logfile
){
1281 fputs (buffer
, MEDATA
->logfile
);
1282 fflush (MEDATA
->logfile
);
1284 vfs_die("This code should be commented out\n");
1285 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1286 int r
= strcmp(fe
->name
, filename
);
1289 if (S_ISLNK (s
.st_mode
)) {
1290 /* This server doesn't understand LIST -lLa */
1294 fe
->l_stat
= g_new (struct stat
, 1);
1295 if (fe
->l_stat
== NULL
)
1298 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1307 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1308 tty_disable_interrupt_key ();
1310 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1314 resolve_symlink(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1316 print_vfs_message(_("Resolving symlink..."));
1318 if (SUP
.strict_rfc959_list_cmd
)
1319 resolve_symlink_without_ls_options(me
, super
, dir
);
1321 resolve_symlink_with_ls_options(me
, super
, dir
);
1326 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1328 struct vfs_s_entry
*ent
;
1329 struct vfs_s_super
*super
= dir
->super
;
1330 int sock
, num_entries
= 0;
1331 char buffer
[BUF_8K
];
1334 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1335 || (strchr (remote_path
, ' ') != NULL
);
1338 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1341 RFC_STRICT
? _("(strict rfc959)") : "",
1342 cd_first
? _("(chdir first)") : "");
1345 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
) {
1346 ftpfs_errno
= ENOENT
;
1347 print_vfs_message (_("ftpfs: CWD failed."));
1352 gettimeofday (&dir
->timestamp
, NULL
);
1353 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1355 if (SUP
.strict
== RFC_STRICT
)
1356 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1358 /* Dirty hack to avoid autoprepending / to . */
1359 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1361 ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1363 /* Trailing "/." is necessary if remote_path is a symlink */
1364 char *path
= concat_dir_and_file (remote_path
, ".");
1366 ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
,
1374 /* Clear the interrupt flag */
1375 tty_enable_interrupt_key ();
1380 vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1386 me
->verrno
= ECONNRESET
;
1388 tty_disable_interrupt_key ();
1389 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1390 print_vfs_message (_("%s: failure"), me
->name
);
1394 if (MEDATA
->logfile
) {
1395 fputs (buffer
, MEDATA
->logfile
);
1396 fputs ("\n", MEDATA
->logfile
);
1397 fflush (MEDATA
->logfile
);
1400 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1401 i
= ent
->ino
->st
.st_nlink
;
1402 if (!vfs_parse_ls_lga
1403 (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1404 vfs_s_free_entry (me
, ent
);
1407 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1409 vfs_s_insert_entry (me
, dir
, ent
);
1413 me
->verrno
= E_REMOTE
;
1414 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1417 if (num_entries
== 0 && cd_first
== 0) {
1418 /* The LIST command may produce an empty output. In such scenario
1419 it is not clear whether this is caused by `remote_path' being
1420 a non-existent path or for some other reason (listing emtpy
1421 directory without the -a option, non-readable directory, etc.).
1423 Since `dir_load' is a crucial method, when it comes to determine
1424 whether a given path is a _directory_, the code must try its best
1425 to determine the type of `remote_path'. The only reliable way to
1426 achieve this is trough issuing a CWD command. */
1432 if (SUP
.strict
== RFC_AUTODETECT
)
1433 SUP
.strict
= RFC_DARING
;
1435 print_vfs_message (_("%s: done."), me
->name
);
1439 if (SUP
.strict
== RFC_AUTODETECT
) {
1440 /* It's our first attempt to get a directory listing from this
1441 server (UNIX style LIST command) */
1442 SUP
.strict
= RFC_STRICT
;
1443 /* I hate goto, but recursive call needs another 8K on stack */
1444 /* return ftpfs_dir_load (me, dir, remote_path); */
1448 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1449 ERRNOR (EACCES
, -1);
1453 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
,
1456 int h
, sock
, n_read
, n_written
;
1458 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1466 struct vfs_s_super
*super
= FH_SUPER
;
1468 h
= open (localname
, O_RDONLY
);
1472 ftpfs_open_data_connection (me
, super
,
1473 fh
->u
.ftp
.append
? "APPE" : "STOR", name
,
1475 if (sock
< 0 || fstat (h
, &s
) == -1) {
1479 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1482 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1484 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1488 tty_enable_interrupt_key ();
1490 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1) {
1491 if (errno
== EINTR
) {
1492 if (tty_got_interrupt ()) {
1493 ftpfs_errno
= EINTR
;
1498 ftpfs_errno
= errno
;
1505 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
) {
1506 if (n_written
== -1) {
1507 if (errno
== EINTR
&& !tty_got_interrupt ()) {
1510 ftpfs_errno
= errno
;
1514 n_read
-= n_written
;
1516 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1517 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1519 tty_disable_interrupt_key ();
1522 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1526 tty_disable_interrupt_key ();
1529 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1534 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1536 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1540 FH_SOCK
= ftpfs_open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1544 fh
->linear
= LS_LINEAR_OPEN
;
1545 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1546 fh
->u
.ftp
.append
= 0;
1551 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1554 struct vfs_s_super
*super
= FH_SUPER
;
1556 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1557 if ((errno
== EINTR
) && !tty_got_interrupt ())
1563 ftpfs_linear_abort(me
, fh
);
1566 SUP
.ctl_connection_busy
= 0;
1569 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1570 ERRNOR (E_REMOTE
, -1);
1577 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1580 ftpfs_linear_abort(me
, fh
);
1583 static int ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1588 case VFS_CTL_IS_NOTREADY
:
1593 vfs_die ("You may not do this");
1594 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1597 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1598 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1608 ftpfs_send_command(struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1611 char *p
, *mpath
= g_strdup(filename
);
1612 struct vfs_s_super
*super
;
1614 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1616 if (!(rpath
= vfs_s_get_path_mangle(me
, mpath
, &super
, 0))) {
1620 p
= ftpfs_translate_path (me
, super
, rpath
);
1621 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1623 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1624 if (flags
& OPT_IGNORE_ERROR
)
1626 if (r
!= COMPLETE
) {
1631 if (flush_directory_cache
)
1632 vfs_s_invalidate(me
, super
);
1637 /* This routine is called as the last step in load_setup */
1639 ftpfs_init_passwd(void)
1641 ftpfs_anonymous_passwd
= load_anon_passwd ();
1642 if (ftpfs_anonymous_passwd
)
1645 /* If there is no anonymous ftp password specified
1646 * then we'll just use anonymous@
1647 * We don't send any other thing because:
1648 * - We want to remain anonymous
1649 * - We want to stop SPAM
1650 * - We don't want to let ftp sites to discriminate by the user,
1653 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1656 static int ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1658 char buf
[BUF_SMALL
];
1661 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1663 ret
= ftpfs_send_command(me
, path
, buf
, OPT_FLUSH
);
1665 if ( mc_config_get_bool (mc_main_config
, CONFIG_APP_SECTION
,
1666 "ignore_ftp_chattr_errors", TRUE
)) {
1673 static int ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1676 ftpfs_errno
= EPERM
;
1679 /* Everyone knows it is not possible to chown remotely, so why bother them.
1680 If someone's root, then copy/move will always try to chown it... */
1689 static int ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1691 return ftpfs_send_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1694 /* Return 1 if path is the same directory as the one we are in now */
1696 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1702 if (strcmp (path
, SUP
.cwdir
) == 0)
1708 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1713 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1716 p
= ftpfs_translate_path (me
, super
, remote_path
);
1717 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1720 if (r
!= COMPLETE
) {
1724 SUP
.cwdir
= g_strdup (remote_path
);
1725 SUP
.cwd_deferred
= 0;
1730 static int ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1732 ftpfs_send_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1733 return ftpfs_send_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1736 static int ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1738 (void) mode
; /* FIXME: should be used */
1740 return ftpfs_send_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1743 static int ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1745 return ftpfs_send_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1749 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
,
1754 fh
->u
.ftp
.append
= 0;
1755 /* File will be written only, so no need to retrieve it from ftp server */
1756 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
))) {
1757 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1764 /* ftpfs_linear_start() called, so data will be written
1765 * to local temporary file and stored to ftp server
1766 * by vfs_s_close later
1768 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
) {
1769 if (!fh
->ino
->localname
) {
1770 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1771 fh
->ino
->ent
->name
);
1775 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1779 name
= vfs_s_fullpath (me
, fh
->ino
);
1783 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1784 (flags
& O_APPEND
) ? "APPE" :
1785 "STOR", name
, TYPE_BINARY
, 0);
1790 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1794 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1796 if (fh
->ino
->localname
) {
1797 unlink (fh
->ino
->localname
);
1798 g_free (fh
->ino
->localname
);
1799 fh
->ino
->localname
= NULL
;
1804 if (!fh
->ino
->localname
)
1805 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1807 if (!fh
->ino
->localname
)
1808 vfs_die ("retrieve_file failed to fill in localname");
1812 static int ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1814 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1817 /* File is stored to destination already, so
1818 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1821 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1823 vfs_s_invalidate (me
, FH_SUPER
);
1829 ftpfs_done (struct vfs_class
*me
)
1831 struct no_proxy_entry
*np
;
1836 np
= no_proxy
->next
;
1837 g_free (no_proxy
->domain
);
1841 g_free (ftpfs_anonymous_passwd
);
1842 g_free (ftpfs_proxy_host
);
1846 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
1848 struct vfs_s_super
*super
= MEDATA
->supers
;
1852 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
1855 super
= super
->next
;
1859 static char buffer
[BUF_MEDIUM
];
1861 static const char *netrcp
;
1863 /* This should match the keywords[] array below */
1876 static keyword_t
ftpfs_netrc_next (void)
1880 static const char *const keywords
[] = { "default", "machine",
1881 "login", "password", "passwd", "account", "macdef", NULL
1886 netrcp
= skip_separators (netrcp
);
1887 if (*netrcp
!= '\n')
1894 if (*netrcp
== '"') {
1895 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1896 if (*netrcp
== '\\')
1901 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1902 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1903 if (*netrcp
== '\\')
1913 while (keywords
[i
- 1]) {
1914 if (!strcmp (keywords
[i
- 1], buffer
))
1920 return NETRC_UNKNOWN
;
1923 static int ftpfs_netrc_bad_mode (const char *netrcname
)
1925 static int be_angry
= 1;
1928 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1930 message (D_ERROR
, MSG_ERROR
,
1931 _("~/.netrc file has incorrect mode.\n"
1932 "Remove password or correct mode."));
1940 /* Scan .netrc until we find matching "machine" or "default"
1941 * domain is used for additional matching
1942 * No search is done after "default" in compliance with "man netrc"
1943 * Return 0 if found, -1 otherwise */
1944 static int ftpfs_find_machine (const char *host
, const char *domain
)
1948 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
) {
1949 if (keyword
== NETRC_DEFAULT
)
1952 if (keyword
== NETRC_MACDEF
) {
1953 /* Scan for an empty line, which concludes "macdef" */
1955 while (*netrcp
&& *netrcp
!= '\n')
1957 if (*netrcp
!= '\n')
1960 } while (*netrcp
&& *netrcp
!= '\n');
1964 if (keyword
!= NETRC_MACHINE
)
1967 /* Take machine name */
1968 if (ftpfs_netrc_next () == NETRC_NONE
)
1971 if (g_strcasecmp (host
, buffer
)) {
1972 /* Try adding our domain to short names in .netrc */
1973 const char *host_domain
= strchr (host
, '.');
1977 /* Compare domain part */
1978 if (g_strcasecmp (host_domain
, domain
))
1981 /* Compare local part */
1982 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1993 /* Extract login and password from .netrc for the host.
1995 * Returns 0 for success, -1 for error */
1996 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
1999 char *tmp_pass
= NULL
;
2000 char hostname
[MAXHOSTNAMELEN
];
2003 static struct rupcache
{
2004 struct rupcache
*next
;
2008 } *rup_cache
= NULL
, *rupp
;
2010 /* Initialize *login and *pass */
2017 /* Look up in the cache first */
2018 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
2019 if (!strcmp (host
, rupp
->host
)) {
2021 *login
= g_strdup (rupp
->login
);
2022 if (pass
&& rupp
->pass
)
2023 *pass
= g_strdup (rupp
->pass
);
2028 /* Load current .netrc */
2029 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
2030 netrcp
= netrc
= load_file (netrcname
);
2031 if (netrc
== NULL
) {
2036 /* Find our own domain name */
2037 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2039 if (!(domain
= strchr (hostname
, '.')))
2042 /* Scan for "default" and matching "machine" keywords */
2043 ftpfs_find_machine (host
, domain
);
2045 /* Scan for keywords following "default" and "machine" */
2048 keyword
= ftpfs_netrc_next ();
2052 if (ftpfs_netrc_next () == NETRC_NONE
) {
2057 /* We have another name already - should not happen */
2063 /* We have login name now */
2064 *login
= g_strdup (buffer
);
2067 case NETRC_PASSWORD
:
2069 if (ftpfs_netrc_next () == NETRC_NONE
) {
2074 /* Ignore unsafe passwords */
2075 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2076 && ftpfs_netrc_bad_mode (netrcname
)) {
2081 /* Remember password. pass may be NULL, so use tmp_pass */
2082 if (tmp_pass
== NULL
)
2083 tmp_pass
= g_strdup (buffer
);
2087 /* "account" is followed by a token which we ignore */
2088 if (ftpfs_netrc_next () == NETRC_NONE
) {
2093 /* Ignore account, but warn user anyways */
2094 ftpfs_netrc_bad_mode (netrcname
);
2098 /* Unexpected keyword or end of file */
2110 rupp
= g_new (struct rupcache
, 1);
2111 rupp
->host
= g_strdup (host
);
2112 rupp
->login
= rupp
->pass
= 0;
2114 if (*login
!= NULL
) {
2115 rupp
->login
= g_strdup (*login
);
2117 if (tmp_pass
!= NULL
)
2118 rupp
->pass
= g_strdup (tmp_pass
);
2119 rupp
->next
= rup_cache
;
2131 static struct vfs_s_subclass ftpfs_subclass
;
2133 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
2134 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2135 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2136 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2137 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2138 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2139 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2140 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2141 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2142 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2143 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2145 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2146 vfs_ftpfs_ops
.name
= "ftpfs";
2147 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2148 vfs_ftpfs_ops
.prefix
= "ftp:";
2149 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2150 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2151 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2152 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2153 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2154 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2155 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2156 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2157 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2158 vfs_register_class (&vfs_ftpfs_ops
);