1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
8 1995, 1996, 1997 Miguel de Icaza
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU Library General Public License
17 as published by the Free Software Foundation; either version 2 of
18 the License, or (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU Library General Public License for more details.
25 You should have received a copy of the GNU Library General Public
26 License along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
31 * \brief Source: Virtual File System: FTP file system
33 * \author Jakub Jelinek
34 * \author Miguel de Icaza
35 * \author Norbert Warmuth
36 * \author Pavel Machek
37 * \date 1995, 1997, 1998
40 - make it more robust - all the connects etc. should handle EADDRINUSE and
41 ERETRY (have I spelled these names correctly?)
42 - make the user able to flush a connection - all the caches will get empty
43 etc., (tarfs as well), we should give there a user selectable timeout
44 and assign a key sequence.
45 - use hash table instead of linklist to cache ftpfs directory.
50 * NOTE: Usage of tildes is deprecated, consider:
55 * And now: what do I want to do? Do I want to go to /home/pavel or to
56 * /#ftp:hobit/home/pavel? I think first has better sense...
60 int f = !strcmp( remote_path, "/~" );
61 if (f || !strncmp( remote_path, "/~/", 3 )) {
63 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
71 /* \todo Fix: Namespace pollution: horrible */
74 #include <stdio.h> /* sscanf() */
75 #include <stdlib.h> /* atoi() */
76 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
77 #include <netdb.h> /* struct hostent */
78 #include <sys/socket.h> /* AF_INET */
79 #include <netinet/in.h> /* struct in_addr */
80 #ifdef HAVE_ARPA_INET_H
81 #include <arpa/inet.h>
84 #include <arpa/telnet.h>
85 #include <sys/param.h>
89 #include <sys/time.h> /* gettimeofday() */
90 #include <inttypes.h> /* uintmax_t */
92 #include "lib/global.h"
94 #include "lib/mcconfig.h"
96 #include "lib/tty/tty.h" /* enable/disable interrupt key */
97 #include "lib/widget.h" /* message() */
99 #include "src/history.h"
100 #include "src/setup.h" /* for load_anon_passwd */
102 #include "lib/vfs/vfs.h"
103 #include "lib/vfs/utilvfs.h"
104 #include "lib/vfs/netutil.h"
105 #include "lib/vfs/xdirentry.h"
106 #include "lib/vfs/gc.h" /* vfs_stamp_create */
110 /*** global variables ****************************************************************************/
112 /* Delay to retry a connection */
113 int ftpfs_retry_seconds
= 30;
115 /* Method to use to connect to ftp sites */
116 int ftpfs_use_passive_connections
= 1;
117 int ftpfs_use_passive_connections_over_proxy
= 0;
119 /* Method used to get directory listings:
120 * 1: try 'LIST -la <path>', if it fails
121 * fall back to CWD <path>; LIST
122 * 0: always use CWD <path>; LIST
124 int ftpfs_use_unix_list_options
= 1;
126 /* First "CWD <path>", then "LIST -la ." */
127 int ftpfs_first_cd_then_ls
= 1;
129 /* Use the ~/.netrc */
130 int ftpfs_use_netrc
= 1;
132 /* Anonymous setup */
133 char *ftpfs_anonymous_passwd
= NULL
;
134 int ftpfs_directory_timeout
= 900;
137 char *ftpfs_proxy_host
= NULL
;
139 /* wether we have to use proxy by default? */
140 int ftpfs_always_use_proxy
= 0;
142 int ftpfs_ignore_chattr_errors
= 1;
144 /*** file scope macro definitions ****************************************************************/
146 #ifndef MAXHOSTNAMELEN
147 #define MAXHOSTNAMELEN 64
150 #define UPLOAD_ZERO_LENGTH_FILE
151 #define SUP ((ftp_super_data_t *) super->data)
152 #define FH_SOCK ((ftp_fh_data_t *) fh->data)->sock
155 #define INADDR_NONE 0xffffffff
158 #define RFC_AUTODETECT 0
162 /* ftpfs_command wait_flag: */
164 #define WAIT_REPLY 0x01
165 #define WANT_STRING 0x02
167 #define FTP_COMMAND_PORT 21
169 /* some defines only used by ftpfs_changetype */
170 /* These two are valid values for the second parameter */
172 #define TYPE_BINARY 1
174 /* This one is only used to initialize bucket->isbinary, don't use it as
175 second parameter to ftpfs_changetype. */
176 #define TYPE_UNKNOWN -1
178 #define ABORT_TIMEOUT 5
179 /*** file scope type declarations ****************************************************************/
181 #ifndef HAVE_SOCKLEN_T
182 typedef int socklen_t
;
185 /* This should match the keywords[] array below */
203 char *proxy
; /* proxy server, NULL if no proxy */
204 int failed_on_login
; /* used to pass the failure reason to upper levels */
205 int use_passive_connection
;
206 int remote_is_amiga
; /* No leading slash allowed for AmiTCP (Amiga) */
208 int cwd_deferred
; /* current_directory was changed but CWD command hasn't
210 int strict
; /* ftp server doesn't understand
211 * "LIST -la <path>"; use "CWD <path>"/
214 int ctl_connection_busy
;
223 /*** file scope variables ************************************************************************/
225 static int ftpfs_errno
;
228 #ifdef FIXME_LATER_ALIGATOR
229 static struct linklist
*connections_list
;
232 static char reply_str
[80];
234 static struct vfs_class vfs_ftpfs_ops
;
236 static GSList
*no_proxy
;
238 static char buffer
[BUF_MEDIUM
];
240 static const char *netrcp
;
242 /*** file scope functions ************************************************************************/
243 /* --------------------------------------------------------------------------------------------- */
245 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
246 Translate a Unix path, i.e. MC's internal path representation (e.g.
247 /somedir/somefile) to a path valid for the remote server. Every path
248 transfered to the remote server has to be mangled by this function
249 right prior to sending it.
250 Currently only Amiga ftp servers are handled in a special manner.
252 When the remote server is an amiga:
253 a) strip leading slash if necesarry
254 b) replace first occurance of ":/" with ":"
255 c) strip trailing "/."
258 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
259 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
,
260 const char *remote_path
);
261 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
,
262 const char *fmt
, ...) __attribute__ ((format (__printf__
, 4, 5)));
263 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
264 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
265 const char *netrcpass
);
266 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
268 /* --------------------------------------------------------------------------------------------- */
271 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
273 if (!SUP
->remote_is_amiga
)
274 return g_strdup (remote_path
);
281 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
282 fflush (MEDATA
->logfile
);
285 /* strip leading slash(es) */
286 while (*remote_path
== '/')
290 * Don't change "/" into "", e.g. "CWD " would be
293 if (*remote_path
== '\0')
294 return g_strdup (".");
296 ret
= g_strdup (remote_path
);
298 /* replace first occurance of ":/" with ":" */
299 p
= strchr (ret
, ':');
300 if ((p
!= NULL
) && (*(p
+ 1) == '/'))
301 memmove (p
+ 1, p
+ 2, strlen (p
+ 2) + 1);
303 /* strip trailing "/." */
304 p
= strrchr (ret
, '/');
305 if ((p
!= NULL
) && (*(p
+ 1) == '.') && (*(p
+ 2) == '\0'))
312 /* --------------------------------------------------------------------------------------------- */
313 /** Extract the hostname and username from the path */
315 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
316 * ftp://sunsite.unc.edu/pub/linux
317 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
318 * ftp://tsx-11.mit.edu:8192/
319 * ftp://joe@foo.edu:11321/private
320 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
325 ftpfs_split_url (const char *path
)
329 p
= vfs_url_split (path
, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
333 /* Look up user and password in netrc */
335 ftpfs_netrc_lookup (p
->host
, &p
->user
, &p
->password
);
338 p
->user
= g_strdup ("anonymous");
340 /* Look up password in netrc for known user */
341 if (ftpfs_use_netrc
&& p
->user
!= NULL
&& p
->password
!= NULL
)
343 char *new_user
= NULL
;
345 ftpfs_netrc_lookup (p
->host
, &new_user
, &p
->password
);
347 /* If user is different, remove password */
348 if (new_user
!= NULL
&& strcmp (p
->user
, new_user
) != 0)
350 g_free (p
->password
);
360 /* --------------------------------------------------------------------------------------------- */
361 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
364 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
371 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
378 switch (sscanf (answer
, "%d", &code
))
382 g_strlcpy (string_buf
, answer
, string_len
);
386 if (answer
[3] == '-')
390 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
397 if ((sscanf (answer
, "%d", &i
) > 0) && (code
== i
) && (answer
[3] == ' '))
402 g_strlcpy (string_buf
, answer
, string_len
);
408 /* --------------------------------------------------------------------------------------------- */
411 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
415 sock
= ftpfs_open_socket (me
, super
);
418 char *cwdir
= super
->url
->path
;
422 super
->url
->path
= NULL
;
425 if (ftpfs_login_server (me
, super
, super
->url
->password
) != 0)
429 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
431 return sock
== COMPLETE
? 1 : 0;
434 super
->url
->path
= cwdir
;
440 /* --------------------------------------------------------------------------------------------- */
443 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
,
449 static int retry
= 0;
450 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
453 cmdstr
= g_strdup_vprintf (fmt
, ap
);
456 cmdlen
= strlen (cmdstr
);
457 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
458 strcpy (cmdstr
+ cmdlen
, "\r\n");
463 if (strncmp (cmdstr
, "PASS ", 5) == 0)
465 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
470 ret
= fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
473 fflush (MEDATA
->logfile
);
477 tty_enable_interrupt_key ();
478 status
= write (SUP
->sock
, cmdstr
, cmdlen
);
485 { /* Remote server has closed connection */
489 status
= ftpfs_reconnect (me
, super
);
491 if (status
&& (write (SUP
->sock
, cmdstr
, cmdlen
) > 0))
500 tty_disable_interrupt_key ();
505 tty_disable_interrupt_key ();
509 status
= ftpfs_get_reply (me
, SUP
->sock
,
510 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
511 sizeof (reply_str
) - 1);
512 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
516 status
= ftpfs_reconnect (me
, super
);
518 if (status
&& (write (SUP
->sock
, cmdstr
, cmdlen
) > 0))
531 /* --------------------------------------------------------------------------------------------- */
534 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
538 vfs_print_message (_("ftpfs: Disconnecting from %s"), super
->url
->host
);
539 ftpfs_command (me
, super
, NONE
, "QUIT");
542 g_free (super
->data
);
546 /* --------------------------------------------------------------------------------------------- */
549 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
551 if (binary
!= SUP
->isbinary
)
553 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
555 SUP
->isbinary
= binary
;
560 /* --------------------------------------------------------------------------------------------- */
561 /* This routine logs the user in */
564 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
)
568 char *name
; /* login user name */
570 char reply_string
[BUF_MEDIUM
];
572 SUP
->isbinary
= TYPE_UNKNOWN
;
574 if (super
->url
->password
!= NULL
) /* explicit password */
575 op
= g_strdup (super
->url
->password
);
576 else if (netrcpass
!= NULL
) /* password from netrc */
577 op
= g_strdup (netrcpass
);
578 else if (strcmp (super
->url
->user
, "anonymous") == 0 || strcmp (super
->url
->user
, "ftp") == 0)
580 if (ftpfs_anonymous_passwd
== NULL
) /* default anonymous password */
581 ftpfs_init_passwd ();
582 op
= g_strdup (ftpfs_anonymous_passwd
);
589 p
= g_strdup_printf (_("FTP: Password required for %s"), super
->url
->user
);
590 op
= vfs_get_password (p
);
594 super
->url
->password
= g_strdup (op
);
597 if (!anon
|| MEDATA
->logfile
)
601 pass
= g_strconcat ("-", op
, (char *) NULL
);
605 /* Proxy server accepts: username@host-we-want-to-connect */
608 g_strconcat (super
->url
->user
, "@",
609 super
->url
->host
[0] == '!' ? super
->url
->host
+ 1 : super
->url
->host
,
612 name
= g_strdup (super
->url
->user
);
614 if (ftpfs_get_reply (me
, SUP
->sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
)
618 reply_up
= g_ascii_strup (reply_string
, -1);
619 SUP
->remote_is_amiga
= strstr (reply_up
, "AMIGA") != 0;
624 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n", SUP
->remote_is_amiga
);
625 fflush (MEDATA
->logfile
);
628 vfs_print_message (_("ftpfs: sending login name"));
630 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
))
633 vfs_print_message (_("ftpfs: sending user password"));
634 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
635 if (code
== CONTINUE
)
639 p
= g_strdup_printf (_("FTP: Account required for user %s"), super
->url
->user
);
640 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
644 vfs_print_message (_("ftpfs: sending user account"));
645 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
648 if (code
!= COMPLETE
)
653 vfs_print_message (_("ftpfs: logged in"));
654 wipe_password (pass
);
659 SUP
->failed_on_login
= 1;
660 wipe_password (super
->url
->password
);
661 super
->url
->password
= NULL
;
667 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "), super
->url
->user
);
670 wipe_password (pass
);
675 /* --------------------------------------------------------------------------------------------- */
678 ftpfs_load_no_proxy_list (void)
680 /* FixMe: shouldn't be hardcoded!!! */
681 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
685 static char *mc_file
= NULL
;
687 mc_file
= g_build_filename (mc_global
.sysconfig_dir
, "mc.no_proxy", (char *) NULL
);
688 if (exist_file (mc_file
))
690 npf
= fopen (mc_file
, "r");
693 while (fgets (s
, sizeof (s
), npf
) != NULL
)
695 p
= strchr (s
, '\n');
696 if (p
== NULL
) /* skip bogus entries */
698 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
706 no_proxy
= g_slist_prepend (no_proxy
, g_strdup (s
));
715 /* --------------------------------------------------------------------------------------------- */
716 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
719 ftpfs_check_proxy (const char *host
)
723 if (ftpfs_proxy_host
== NULL
|| *ftpfs_proxy_host
== '\0' || host
== NULL
|| *host
== '\0')
724 return 0; /* sanity check */
729 if (!ftpfs_always_use_proxy
)
732 if (strchr (host
, '.') == NULL
)
735 ftpfs_load_no_proxy_list ();
736 for (npe
= no_proxy
; npe
!= NULL
; npe
= g_slist_next (npe
))
738 const char *domain
= (const char *) npe
->data
;
740 if (domain
[0] == '.')
742 size_t ld
= strlen (domain
);
743 size_t lh
= strlen (host
);
745 while (ld
!= 0 && lh
!= 0 && host
[lh
- 1] == domain
[ld
- 1])
754 else if (g_ascii_strcasecmp (host
, domain
) == 0)
761 /* --------------------------------------------------------------------------------------------- */
764 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
768 url
= vfs_url_split (proxy
, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
769 *host
= g_strdup (url
->host
);
774 /* --------------------------------------------------------------------------------------------- */
777 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
779 struct addrinfo hints
, *res
, *curr_res
;
788 /* Use a proxy host? */
789 host
= g_strdup (super
->url
->host
);
791 if (host
== NULL
|| *host
== '\0')
793 vfs_print_message (_("ftpfs: Invalid host name."));
794 ftpfs_errno
= EINVAL
;
799 /* Hosts to connect to that start with a ! should use proxy */
800 tmp_port
= super
->url
->port
;
802 if (SUP
->proxy
!= NULL
)
803 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
805 g_snprintf (port
, sizeof (port
), "%hu", (unsigned short) tmp_port
);
813 tty_enable_interrupt_key (); /* clear the interrupt flag */
815 memset (&hints
, 0, sizeof (struct addrinfo
));
816 hints
.ai_family
= AF_UNSPEC
;
817 hints
.ai_socktype
= SOCK_STREAM
;
820 /* By default, only look up addresses using address types for
821 * which a local interface is configured (i.e. no IPv6 if no IPv6
822 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
823 hints
.ai_flags
= AI_ADDRCONFIG
;
826 e
= getaddrinfo (host
, port
, &hints
, &res
);
829 if (e
== EAI_BADFLAGS
)
831 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
833 e
= getaddrinfo (host
, port
, &hints
, &res
);
841 tty_disable_interrupt_key ();
842 vfs_print_message (_("ftpfs: %s"), gai_strerror (e
));
844 ftpfs_errno
= EINVAL
;
848 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
)
850 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
854 if (curr_res
->ai_next
!= NULL
)
857 tty_disable_interrupt_key ();
858 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno
));
865 vfs_print_message (_("ftpfs: making connection to %s"), host
);
869 if (connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0)
875 if (errno
== EINTR
&& tty_got_interrupt ())
876 vfs_print_message (_("ftpfs: connection interrupted by user"));
877 else if (res
->ai_next
== NULL
)
878 vfs_print_message (_("ftpfs: connection to server failed: %s"),
879 unix_error_string (errno
));
884 tty_disable_interrupt_key ();
889 tty_disable_interrupt_key ();
893 /* --------------------------------------------------------------------------------------------- */
896 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
898 int retry_seconds
= 0;
901 /* We do not want to use the passive if we are using proxies */
903 SUP
->use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
907 SUP
->failed_on_login
= 0;
909 SUP
->sock
= ftpfs_open_socket (me
, super
);
913 if (ftpfs_login_server (me
, super
, NULL
) != 0)
915 /* Logged in, no need to retry the connection */
920 if (!SUP
->failed_on_login
)
923 /* Close only the socket descriptor */
926 if (ftpfs_retry_seconds
!= 0)
928 retry_seconds
= ftpfs_retry_seconds
;
929 tty_enable_interrupt_key ();
930 for (count_down
= retry_seconds
; count_down
; count_down
--)
932 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
935 if (tty_got_interrupt ())
937 /* ftpfs_errno = E; */
938 tty_disable_interrupt_key ();
942 tty_disable_interrupt_key ();
946 while (retry_seconds
!= 0);
948 super
->url
->path
= ftpfs_get_current_directory (me
, super
);
949 if (super
->url
->path
== NULL
)
950 super
->url
->path
= g_strdup (PATH_SEP_STR
);
955 /* --------------------------------------------------------------------------------------------- */
958 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
959 const char *archive_name
, char *op
)
963 super
->data
= g_new0 (ftp_super_data_t
, 1);
965 super
->url
= ftpfs_split_url (strchr (op
, ':') + 1);
967 if (ftpfs_check_proxy (super
->url
->host
))
968 SUP
->proxy
= ftpfs_proxy_host
;
969 SUP
->use_passive_connection
= ftpfs_use_passive_connections
;
970 SUP
->strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
971 SUP
->isbinary
= TYPE_UNKNOWN
;
972 SUP
->remote_is_amiga
= 0;
973 super
->name
= g_strdup ("/");
974 super
->root
= vfs_s_new_inode (me
, super
, vfs_s_default_stat (me
, S_IFDIR
| 0755));
976 return ftpfs_open_archive_int (me
, super
);
979 /* --------------------------------------------------------------------------------------------- */
982 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
983 const char *archive_name
, char *op
, void *cookie
)
992 url
= ftpfs_split_url (strchr (op
, ':') + 1);
994 result
= ((strcmp (url
->host
, super
->url
->host
) == 0)
995 && (strcmp (url
->user
, super
->url
->user
) == 0)
996 && (url
->port
== super
->url
->port
)) ? 1 : 0;
1002 /* --------------------------------------------------------------------------------------------- */
1003 /* The returned directory should always contain a trailing slash */
1006 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
1008 char buf
[BUF_8K
], *bufp
, *bufq
;
1010 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
1011 ftpfs_get_reply (me
, SUP
->sock
, buf
, sizeof (buf
)) == COMPLETE
)
1014 for (bufq
= buf
; *bufq
; bufq
++)
1026 if (*(bufq
- 1) != '/')
1032 return g_strdup (bufp
);
1035 /* If the remote server is an Amiga a leading slash
1036 might be missing. MC needs it because it is used
1037 as separator between hostname and path internally. */
1038 return g_strconcat ("/", bufp
, (char *) NULL
);
1053 /* --------------------------------------------------------------------------------------------- */
1054 /* Setup Passive PASV FTP connection */
1057 ftpfs_setup_passive_pasv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1058 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1062 int xa
, xb
, xc
, xd
, xe
, xf
;
1064 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
1067 /* Parse remote parameters */
1068 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
1072 if (!isdigit ((unsigned char) *c
))
1074 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
1077 n
[0] = (unsigned char) xa
;
1078 n
[1] = (unsigned char) xb
;
1079 n
[2] = (unsigned char) xc
;
1080 n
[3] = (unsigned char) xd
;
1081 n
[4] = (unsigned char) xe
;
1082 n
[5] = (unsigned char) xf
;
1084 memcpy (&(((struct sockaddr_in
*) sa
)->sin_addr
.s_addr
), (void *) n
, 4);
1085 memcpy (&(((struct sockaddr_in
*) sa
)->sin_port
), (void *) &n
[4], 2);
1087 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
1093 /* --------------------------------------------------------------------------------------------- */
1094 /* Setup Passive EPSV FTP connection */
1097 ftpfs_setup_passive_epsv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1098 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1103 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") != COMPLETE
)
1107 c
= strchr (reply_str
, '|');
1116 if (port
< 0 || port
> 65535)
1118 port
= htons (port
);
1120 switch (sa
->ss_family
)
1123 ((struct sockaddr_in
*) sa
)->sin_port
= port
;
1126 ((struct sockaddr_in6
*) sa
)->sin6_port
= port
;
1130 return (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0) ? 0 : 1;
1133 /* --------------------------------------------------------------------------------------------- */
1134 /* Setup Passive ftp connection, we use it for source routed connections */
1137 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
1138 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1140 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1141 if (sa
->ss_family
== AF_INET
)
1143 if (!ftpfs_setup_passive_pasv (me
, super
, my_socket
, sa
, salen
))
1144 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1145 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1148 /* It's IPV6, so EPSV is our only hope */
1151 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1158 /* --------------------------------------------------------------------------------------------- */
1159 /* Setup Active PORT or EPRT FTP connection */
1162 ftpfs_setup_active (struct vfs_class
*me
, struct vfs_s_super
*super
,
1163 struct sockaddr_storage data_addr
, socklen_t data_addrlen
)
1165 unsigned short int port
;
1169 switch (data_addr
.ss_family
)
1173 port
= ((struct sockaddr_in
*) &data_addr
)->sin_port
;
1177 port
= ((struct sockaddr_in6
*) &data_addr
)->sin6_port
;
1179 /* Not implemented */
1184 addr
= g_try_malloc (NI_MAXHOST
);
1186 ERRNOR (ENOMEM
, -1);
1189 ((struct sockaddr
*) &data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0,
1190 NI_NUMERICHOST
) != 0)
1196 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1199 unsigned char *a
= (unsigned char *) &((struct sockaddr_in
*) &data_addr
)->sin_addr
;
1200 unsigned char *p
= (unsigned char *) &port
;
1202 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1203 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1204 p
[0], p
[1]) == COMPLETE
)
1212 * Converts network MSB first order to host byte order (LSB
1213 * first on i386). If we do it earlier, we will run into an
1214 * endianness issue, because the server actually expects to see
1215 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1217 port
= ntohs (port
);
1219 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1220 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
)
1230 /* --------------------------------------------------------------------------------------------- */
1231 /* Initialize a socket for FTP DATA connection */
1234 ftpfs_init_data_socket (struct vfs_class
*me
, struct vfs_s_super
*super
,
1235 struct sockaddr_storage
*data_addr
, socklen_t
* data_addrlen
)
1239 memset (data_addr
, 0, sizeof (struct sockaddr_storage
));
1240 *data_addrlen
= sizeof (struct sockaddr_storage
);
1242 if (SUP
->use_passive_connection
)
1243 result
= getpeername (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1245 result
= getsockname (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1250 switch (data_addr
->ss_family
)
1253 ((struct sockaddr_in
*) data_addr
)->sin_port
= 0;
1256 ((struct sockaddr_in6
*) data_addr
)->sin6_port
= 0;
1259 vfs_print_message (_("ftpfs: invalid address family"));
1260 ERRNOR (EINVAL
, -1);
1263 result
= socket (data_addr
->ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1267 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1274 /* --------------------------------------------------------------------------------------------- */
1275 /* Initialize FTP DATA connection */
1278 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
1280 struct sockaddr_storage data_addr
;
1281 socklen_t data_addrlen
;
1284 * Don't factor socket initialization out of these conditionals,
1285 * because ftpfs_init_data_socket initializes it in different way
1286 * depending on use_passive_connection flag.
1289 /* Try to establish a passive connection first (if requested) */
1290 if (SUP
->use_passive_connection
)
1294 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1298 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1301 vfs_print_message (_("ftpfs: could not setup passive mode"));
1302 SUP
->use_passive_connection
= 0;
1307 /* If passive setup is diabled or failed, fallback to active connections */
1308 if (!SUP
->use_passive_connection
)
1312 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1316 if ((bind (data_sock
, (struct sockaddr
*) &data_addr
, data_addrlen
) == 0) &&
1317 (getsockname (data_sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == 0) &&
1318 (listen (data_sock
, 1) == 0) &&
1319 (ftpfs_setup_active (me
, super
, data_addr
, data_addrlen
) != 0))
1325 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1326 SUP
->use_passive_connection
= SUP
->proxy
!= NULL
? ftpfs_use_passive_connections_over_proxy
:
1327 ftpfs_use_passive_connections
;
1333 /* --------------------------------------------------------------------------------------------- */
1336 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1337 const char *remote
, int isbinary
, int reget
)
1339 struct sockaddr_storage from
;
1341 socklen_t fromlen
= sizeof (from
);
1343 s
= ftpfs_initconn (me
, super
);
1347 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1351 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1357 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1358 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1359 /* WarFtpD can't STORE //filename */
1360 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1361 g_free (remote_path
);
1364 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1368 tty_enable_interrupt_key ();
1369 if (SUP
->use_passive_connection
)
1373 data
= accept (s
, (struct sockaddr
*) &from
, &fromlen
);
1376 ftpfs_errno
= errno
;
1382 tty_disable_interrupt_key ();
1386 /* --------------------------------------------------------------------------------------------- */
1389 ftpfs_linear_abort (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1391 struct vfs_s_super
*super
= FH_SUPER
;
1392 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1395 int dsock
= FH_SOCK
;
1397 SUP
->ctl_connection_busy
= 0;
1399 vfs_print_message (_("ftpfs: aborting transfer."));
1400 if (send (SUP
->sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
))
1402 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno
));
1408 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
)
1410 vfs_print_message (_("ftpfs: abort failed"));
1418 FD_SET (dsock
, &mask
);
1419 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0)
1421 struct timeval start_tim
, tim
;
1422 gettimeofday (&start_tim
, NULL
);
1423 /* flush the remaining data */
1424 while (read (dsock
, buf
, sizeof (buf
)) > 0)
1426 gettimeofday (&tim
, NULL
);
1427 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
)
1429 /* server keeps sending, drop the connection and ftpfs_reconnect */
1431 ftpfs_reconnect (me
, super
);
1438 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1439 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1442 /* --------------------------------------------------------------------------------------------- */
1446 resolve_symlink_without_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1447 struct vfs_s_inode
*dir
)
1449 struct linklist
*flist
;
1450 struct direntry
*fe
, *fel
;
1451 char tmp
[MC_MAXPATHLEN
];
1454 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1455 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
)
1457 /* flist->data->l_stat is alread initialized with 0 */
1459 if (S_ISLNK (fel
->s
.st_mode
) && fel
->linkname
)
1461 if (fel
->linkname
[0] == '/')
1463 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1465 strcpy (tmp
, fel
->linkname
);
1469 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1471 strcpy (tmp
, dir
->remote_path
);
1474 strcat (tmp
+ 1, fel
->linkname
);
1476 for (depth
= 0; depth
< 100; depth
++)
1477 { /* depth protects against recursive symbolic links */
1478 canonicalize_pathname (tmp
);
1479 fe
= _get_file_entry (bucket
, tmp
, 0, 0);
1482 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0)
1484 /* Symlink points to link which isn't resolved, yet. */
1485 if (fe
->linkname
[0] == '/')
1487 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1489 strcpy (tmp
, fe
->linkname
);
1493 /* at this point tmp looks always like this
1494 /directory/filename, i.e. no need to check
1495 strrchr's return value */
1496 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1497 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1499 strcat (tmp
, fe
->linkname
);
1505 fel
->l_stat
= g_new (struct stat
, 1);
1506 if (S_ISLNK (fe
->s
.st_mode
))
1507 *fel
->l_stat
= *fe
->l_stat
;
1509 *fel
->l_stat
= fe
->s
;
1510 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1517 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1520 /* --------------------------------------------------------------------------------------------- */
1523 resolve_symlink_with_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1524 struct vfs_s_inode
*dir
)
1526 char buffer
[2048] = "", *filename
;
1530 struct linklist
*flist
;
1531 struct direntry
*fe
;
1532 int switch_method
= 0;
1534 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1535 if (strchr (dir
->remote_path
, ' '))
1537 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
)
1539 vfs_print_message (_("ftpfs: CWD failed."));
1542 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1545 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", dir
->remote_path
, TYPE_ASCII
, 0);
1549 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1553 fp
= fdopen (sock
, "r");
1557 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1560 tty_enable_interrupt_key ();
1561 flist
= dir
->file_list
->next
;
1566 if (flist
== dir
->file_list
)
1569 flist
= flist
->next
;
1571 while (!S_ISLNK (fe
->s
.st_mode
));
1574 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1576 if (MEDATA
->logfile
)
1578 fputs (buffer
, MEDATA
->logfile
);
1579 fflush (MEDATA
->logfile
);
1581 vfs_die ("This code should be commented out\n");
1582 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
))
1584 int r
= strcmp (fe
->name
, filename
);
1588 if (S_ISLNK (s
.st_mode
))
1590 /* This server doesn't understand LIST -lLa */
1594 fe
->l_stat
= g_new (struct stat
, 1);
1595 if (fe
->l_stat
== NULL
)
1598 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1607 while (fgets (buffer
, sizeof (buffer
), fp
) != NULL
);
1608 tty_disable_interrupt_key ();
1610 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1613 /* --------------------------------------------------------------------------------------------- */
1616 resolve_symlink (struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1618 vfs_print_message (_("Resolving symlink..."));
1620 if (SUP
->strict_rfc959_list_cmd
)
1621 resolve_symlink_without_ls_options (me
, super
, dir
);
1623 resolve_symlink_with_ls_options (me
, super
, dir
);
1627 /* --------------------------------------------------------------------------------------------- */
1630 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1632 struct vfs_s_entry
*ent
;
1633 struct vfs_s_super
*super
= dir
->super
;
1634 int sock
, num_entries
= 0;
1635 char lc_buffer
[BUF_8K
];
1638 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
->strict
== RFC_STRICT
)
1639 || (strchr (remote_path
, ' ') != NULL
);
1642 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1645 RFC_STRICT
? _("(strict rfc959)") : "", cd_first
? _("(chdir first)") : "");
1649 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
)
1651 ftpfs_errno
= ENOENT
;
1652 vfs_print_message (_("ftpfs: CWD failed."));
1657 gettimeofday (&dir
->timestamp
, NULL
);
1658 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1660 if (SUP
->strict
== RFC_STRICT
)
1661 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1663 /* Dirty hack to avoid autoprepending / to . */
1664 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1665 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1668 /* Trailing "/." is necessary if remote_path is a symlink */
1669 char *path
= concat_dir_and_file (remote_path
, ".");
1670 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1677 /* Clear the interrupt flag */
1678 tty_enable_interrupt_key ();
1683 int res
= vfs_s_get_line_interruptible (me
, lc_buffer
, sizeof (lc_buffer
),
1690 me
->verrno
= ECONNRESET
;
1692 tty_disable_interrupt_key ();
1693 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1694 vfs_print_message (_("%s: failure"), me
->name
);
1698 if (MEDATA
->logfile
)
1700 fputs (lc_buffer
, MEDATA
->logfile
);
1701 fputs ("\n", MEDATA
->logfile
);
1702 fflush (MEDATA
->logfile
);
1705 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1706 i
= ent
->ino
->st
.st_nlink
;
1707 if (!vfs_parse_ls_lga (lc_buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
))
1709 vfs_s_free_entry (me
, ent
);
1712 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1714 vfs_s_insert_entry (me
, dir
, ent
);
1718 me
->verrno
= E_REMOTE
;
1719 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1722 if (num_entries
== 0 && cd_first
== 0)
1724 /* The LIST command may produce an empty output. In such scenario
1725 it is not clear whether this is caused by `remote_path' being
1726 a non-existent path or for some other reason (listing emtpy
1727 directory without the -a option, non-readable directory, etc.).
1729 Since `dir_load' is a crucial method, when it comes to determine
1730 whether a given path is a _directory_, the code must try its best
1731 to determine the type of `remote_path'. The only reliable way to
1732 achieve this is trough issuing a CWD command. */
1738 if (SUP
->strict
== RFC_AUTODETECT
)
1739 SUP
->strict
= RFC_DARING
;
1741 vfs_print_message (_("%s: done."), me
->name
);
1745 if (SUP
->strict
== RFC_AUTODETECT
)
1747 /* It's our first attempt to get a directory listing from this
1748 server (UNIX style LIST command) */
1749 SUP
->strict
= RFC_STRICT
;
1750 /* I hate goto, but recursive call needs another 8K on stack */
1751 /* return ftpfs_dir_load (me, dir, remote_path); */
1755 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1756 ERRNOR (EACCES
, -1);
1759 /* --------------------------------------------------------------------------------------------- */
1762 ftpfs_file_store (struct vfs_class
*me
, vfs_file_handler_t
* fh
, char *name
, char *localname
)
1764 int h
, sock
, n_read
, n_written
;
1766 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1771 char lc_buffer
[BUF_8K
];
1774 struct vfs_s_super
*super
= FH_SUPER
;
1775 ftp_fh_data_t
*ftp
= (ftp_fh_data_t
*) fh
->data
;
1777 h
= open (localname
, O_RDONLY
);
1782 ftpfs_open_data_connection (me
, super
, ftp
->append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1783 if (sock
< 0 || fstat (h
, &s
) == -1)
1788 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1791 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1793 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1797 tty_enable_interrupt_key ();
1800 while ((n_read
= read (h
, lc_buffer
, sizeof (lc_buffer
))) == -1)
1804 if (tty_got_interrupt ())
1806 ftpfs_errno
= EINTR
;
1812 ftpfs_errno
= errno
;
1819 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
)
1821 if (n_written
== -1)
1823 if (errno
== EINTR
&& !tty_got_interrupt ())
1827 ftpfs_errno
= errno
;
1831 n_read
-= n_written
;
1833 vfs_print_message ("%s: %" PRIuMAX
"/%" PRIuMAX
,
1834 _("ftpfs: storing file"), (uintmax_t) n_stored
, (uintmax_t) s
.st_size
);
1836 tty_disable_interrupt_key ();
1839 if (ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
)
1843 tty_disable_interrupt_key ();
1846 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1850 /* --------------------------------------------------------------------------------------------- */
1853 ftpfs_linear_start (struct vfs_class
*me
, vfs_file_handler_t
* fh
, off_t offset
)
1857 if (fh
->data
== NULL
)
1858 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
1860 name
= vfs_s_fullpath (me
, fh
->ino
);
1863 FH_SOCK
= ftpfs_open_data_connection (me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1867 fh
->linear
= LS_LINEAR_OPEN
;
1868 ((ftp_super_data_t
*) (FH_SUPER
->data
))->ctl_connection_busy
= 1;
1869 ((ftp_fh_data_t
*) fh
->data
)->append
= 0;
1873 /* --------------------------------------------------------------------------------------------- */
1876 ftpfs_linear_read (struct vfs_class
*me
, vfs_file_handler_t
* fh
, void *buf
, size_t len
)
1879 struct vfs_s_super
*super
= FH_SUPER
;
1881 while ((n
= read (FH_SOCK
, buf
, len
)) < 0)
1883 if ((errno
== EINTR
) && !tty_got_interrupt ())
1889 ftpfs_linear_abort (me
, fh
);
1893 SUP
->ctl_connection_busy
= 0;
1896 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1897 ERRNOR (E_REMOTE
, -1);
1903 /* --------------------------------------------------------------------------------------------- */
1906 ftpfs_linear_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1909 ftpfs_linear_abort (me
, fh
);
1912 /* --------------------------------------------------------------------------------------------- */
1915 ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1921 case VFS_CTL_IS_NOTREADY
:
1926 vfs_die ("You may not do this");
1927 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1930 v
= vfs_s_select_on_two (((ftp_fh_data_t
*) (FH
->data
))->sock
, 0);
1931 return (((v
< 0) && (errno
== EINTR
)) || v
== 0) ? 1 : 0;
1938 /* --------------------------------------------------------------------------------------------- */
1941 ftpfs_send_command (struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1945 struct vfs_s_super
*super
;
1947 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1949 mpath
= g_strdup (filename
);
1950 rpath
= vfs_s_get_path_mangle (me
, mpath
, &super
, 0);
1956 p
= ftpfs_translate_path (me
, super
, rpath
);
1957 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1959 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1960 if (flags
& OPT_IGNORE_ERROR
)
1968 if (flush_directory_cache
)
1969 vfs_s_invalidate (me
, super
);
1974 /* --------------------------------------------------------------------------------------------- */
1977 ftpfs_chmod (const vfs_path_t
* vpath
, int mode
)
1979 char buf
[BUF_SMALL
];
1981 vfs_path_element_t
*path_element
;
1983 path_element
= vfs_path_get_by_index (vpath
, vfs_path_length (vpath
) - 1);
1985 g_snprintf (buf
, sizeof (buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1987 ret
= ftpfs_send_command (path_element
->class, vpath
->unparsed
, buf
, OPT_FLUSH
);
1989 return ftpfs_ignore_chattr_errors
? 0 : ret
;
1992 /* --------------------------------------------------------------------------------------------- */
1995 ftpfs_chown (const vfs_path_t
* vpath
, uid_t owner
, gid_t group
)
2002 ftpfs_errno
= EPERM
;
2005 /* Everyone knows it is not possible to chown remotely, so why bother them.
2006 If someone's root, then copy/move will always try to chown it... */
2014 /* --------------------------------------------------------------------------------------------- */
2017 ftpfs_unlink (const vfs_path_t
* vpath
)
2019 vfs_path_element_t
*path_element
;
2021 path_element
= vfs_path_get_by_index (vpath
, vfs_path_length (vpath
) - 1);
2022 return ftpfs_send_command (path_element
->class, vpath
->unparsed
, "DELE /%s", OPT_FLUSH
);
2025 /* --------------------------------------------------------------------------------------------- */
2027 /* Return 1 if path is the same directory as the one we are in now */
2029 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
2033 if (super
->url
->path
== NULL
)
2035 return (strcmp (path
, super
->url
->path
) == 0);
2038 /* --------------------------------------------------------------------------------------------- */
2041 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
2046 if (!SUP
->cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
2049 p
= ftpfs_translate_path (me
, super
, remote_path
);
2050 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
2057 g_free (super
->url
->path
);
2058 super
->url
->path
= g_strdup (remote_path
);
2059 SUP
->cwd_deferred
= 0;
2064 /* --------------------------------------------------------------------------------------------- */
2067 ftpfs_rename (const vfs_path_t
* vpath1
, const vfs_path_t
* vpath2
)
2069 vfs_path_element_t
*path_element
;
2071 path_element
= vfs_path_get_by_index (vpath1
, vfs_path_length (vpath1
) - 1);
2072 ftpfs_send_command (path_element
->class, vpath1
->unparsed
, "RNFR /%s", OPT_FLUSH
);
2073 return ftpfs_send_command (path_element
->class, vpath2
->unparsed
, "RNTO /%s", OPT_FLUSH
);
2076 /* --------------------------------------------------------------------------------------------- */
2079 ftpfs_mkdir (const vfs_path_t
* vpath
, mode_t mode
)
2081 vfs_path_element_t
*path_element
;
2082 (void) mode
; /* FIXME: should be used */
2084 path_element
= vfs_path_get_by_index (vpath
, vfs_path_length (vpath
) - 1);
2085 return ftpfs_send_command (path_element
->class, vpath
->unparsed
, "MKD /%s", OPT_FLUSH
);
2088 /* --------------------------------------------------------------------------------------------- */
2091 ftpfs_rmdir (const vfs_path_t
* vpath
)
2093 vfs_path_element_t
*path_element
;
2095 path_element
= vfs_path_get_by_index (vpath
, vfs_path_length (vpath
) - 1);
2096 return ftpfs_send_command (path_element
->class, vpath
->unparsed
, "RMD /%s", OPT_FLUSH
);
2099 /* --------------------------------------------------------------------------------------------- */
2102 ftpfs_fh_open (struct vfs_class
*me
, vfs_file_handler_t
* fh
, int flags
, mode_t mode
)
2108 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
2109 ftp
= (ftp_fh_data_t
*) fh
->data
;
2110 /* File will be written only, so no need to retrieve it from ftp server */
2111 if (((flags
& O_WRONLY
) == O_WRONLY
) && ((flags
& (O_RDONLY
| O_RDWR
)) == 0))
2113 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2120 /* ftpfs_linear_start() called, so data will be written
2121 * to local temporary file and stored to ftp server
2122 * by vfs_s_close later
2124 if (((ftp_super_data_t
*) (FH_SUPER
->data
))->ctl_connection_busy
)
2126 if (!fh
->ino
->localname
)
2128 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
2129 fh
->ino
->ent
->name
);
2133 ftp
->append
= flags
& O_APPEND
;
2137 name
= vfs_s_fullpath (me
, fh
->ino
);
2141 ftpfs_open_data_connection (me
, fh
->ino
->super
,
2142 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
2147 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2151 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
2153 if (fh
->ino
->localname
)
2155 unlink (fh
->ino
->localname
);
2156 g_free (fh
->ino
->localname
);
2157 fh
->ino
->localname
= NULL
;
2162 if (!fh
->ino
->localname
&& vfs_s_retrieve_file (me
, fh
->ino
) == -1)
2164 if (!fh
->ino
->localname
)
2165 vfs_die ("retrieve_file failed to fill in localname");
2173 /* --------------------------------------------------------------------------------------------- */
2176 ftpfs_fh_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
2178 if (fh
->handle
!= -1 && !fh
->ino
->localname
)
2180 ftp_super_data_t
*ftp
= (ftp_super_data_t
*) fh
->ino
->super
->data
;
2184 /* File is stored to destination already, so
2185 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2188 if (ftpfs_get_reply (me
, ftp
->sock
, NULL
, 0) != COMPLETE
)
2190 vfs_s_invalidate (me
, FH_SUPER
);
2198 /* --------------------------------------------------------------------------------------------- */
2201 ftpfs_done (struct vfs_class
*me
)
2205 g_slist_foreach (no_proxy
, (GFunc
) g_free
, NULL
);
2206 g_slist_free (no_proxy
);
2208 g_free (ftpfs_anonymous_passwd
);
2209 g_free (ftpfs_proxy_host
);
2212 /* --------------------------------------------------------------------------------------------- */
2215 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
2219 for (iter
= MEDATA
->supers
; iter
!= NULL
; iter
= g_list_next (iter
))
2221 const struct vfs_s_super
*super
= (const struct vfs_s_super
*) iter
->data
;
2224 name
= g_strconcat ("/#ftp:", super
->url
->user
, "@", super
->url
->host
, "/",
2225 super
->url
->path
, (char *) NULL
);
2231 /* --------------------------------------------------------------------------------------------- */
2234 ftpfs_netrc_next (void)
2238 static const char *const keywords
[] = { "default", "machine",
2239 "login", "password", "passwd", "account", "macdef", NULL
2244 netrcp
= skip_separators (netrcp
);
2245 if (*netrcp
!= '\n')
2254 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++)
2256 if (*netrcp
== '\\')
2263 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
2264 *netrcp
!= ',' && *netrcp
; netrcp
++)
2266 if (*netrcp
== '\\')
2275 for (i
= NETRC_DEFAULT
; keywords
[i
- 1] != NULL
; i
++)
2276 if (strcmp (keywords
[i
- 1], buffer
) == 0)
2279 return NETRC_UNKNOWN
;
2282 /* --------------------------------------------------------------------------------------------- */
2285 ftpfs_netrc_bad_mode (const char *netrcname
)
2287 static int be_angry
= 1;
2290 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077))
2294 message (D_ERROR
, MSG_ERROR
,
2295 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2303 /* --------------------------------------------------------------------------------------------- */
2304 /* Scan .netrc until we find matching "machine" or "default"
2305 * domain is used for additional matching
2306 * No search is done after "default" in compliance with "man netrc"
2307 * Return 0 if found, -1 otherwise */
2310 ftpfs_find_machine (const char *host
, const char *domain
)
2319 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
)
2321 if (keyword
== NETRC_DEFAULT
)
2324 if (keyword
== NETRC_MACDEF
)
2326 /* Scan for an empty line, which concludes "macdef" */
2329 while (*netrcp
&& *netrcp
!= '\n')
2331 if (*netrcp
!= '\n')
2335 while (*netrcp
&& *netrcp
!= '\n');
2339 if (keyword
!= NETRC_MACHINE
)
2342 /* Take machine name */
2343 if (ftpfs_netrc_next () == NETRC_NONE
)
2346 if (g_ascii_strcasecmp (host
, buffer
) != 0)
2348 /* Try adding our domain to short names in .netrc */
2349 const char *host_domain
= strchr (host
, '.');
2353 /* Compare domain part */
2354 if (g_ascii_strcasecmp (host_domain
, domain
) != 0)
2357 /* Compare local part */
2358 if (g_ascii_strncasecmp (host
, buffer
, host_domain
- host
) != 0)
2369 /* --------------------------------------------------------------------------------------------- */
2370 /* Extract login and password from .netrc for the host.
2372 * Returns 0 for success, -1 for error */
2375 ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
2378 char *tmp_pass
= NULL
;
2379 char hostname
[MAXHOSTNAMELEN
];
2382 static struct rupcache
2384 struct rupcache
*next
;
2388 } *rup_cache
= NULL
, *rupp
;
2390 /* Initialize *login and *pass */
2396 /* Look up in the cache first */
2397 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
)
2399 if (!strcmp (host
, rupp
->host
))
2402 *login
= g_strdup (rupp
->login
);
2403 if (pass
&& rupp
->pass
)
2404 *pass
= g_strdup (rupp
->pass
);
2409 /* Load current .netrc */
2410 netrcname
= g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL
);
2411 if (!g_file_get_contents (netrcname
, &netrc
, NULL
, NULL
))
2419 /* Find our own domain name */
2420 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2423 domain
= strchr (hostname
, '.');
2427 /* Scan for "default" and matching "machine" keywords */
2428 ftpfs_find_machine (host
, domain
);
2430 /* Scan for keywords following "default" and "machine" */
2434 keyword
= ftpfs_netrc_next ();
2439 if (ftpfs_netrc_next () == NETRC_NONE
)
2445 /* We have another name already - should not happen */
2452 /* We have login name now */
2453 *login
= g_strdup (buffer
);
2456 case NETRC_PASSWORD
:
2458 if (ftpfs_netrc_next () == NETRC_NONE
)
2464 /* Ignore unsafe passwords */
2465 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2466 && ftpfs_netrc_bad_mode (netrcname
))
2472 /* Remember password. pass may be NULL, so use tmp_pass */
2473 if (tmp_pass
== NULL
)
2474 tmp_pass
= g_strdup (buffer
);
2478 /* "account" is followed by a token which we ignore */
2479 if (ftpfs_netrc_next () == NETRC_NONE
)
2485 /* Ignore account, but warn user anyways */
2486 ftpfs_netrc_bad_mode (netrcname
);
2490 /* Unexpected keyword or end of file */
2502 rupp
= g_new (struct rupcache
, 1);
2503 rupp
->host
= g_strdup (host
);
2504 rupp
->login
= g_strdup (*login
);
2505 rupp
->pass
= g_strdup (tmp_pass
);
2507 rupp
->next
= rup_cache
;
2515 /* --------------------------------------------------------------------------------------------- */
2516 /*** public functions ****************************************************************************/
2517 /* --------------------------------------------------------------------------------------------- */
2519 /** This routine is called as the last step in load_setup */
2521 ftpfs_init_passwd (void)
2523 ftpfs_anonymous_passwd
= load_anon_passwd ();
2524 if (ftpfs_anonymous_passwd
)
2527 /* If there is no anonymous ftp password specified
2528 * then we'll just use anonymous@
2529 * We don't send any other thing because:
2530 * - We want to remain anonymous
2531 * - We want to stop SPAM
2532 * - We don't want to let ftp sites to discriminate by the user,
2535 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
2538 /* --------------------------------------------------------------------------------------------- */
2543 static struct vfs_s_subclass ftpfs_subclass
;
2547 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
2548 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2549 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2550 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2551 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2552 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2553 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2554 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2555 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2556 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2557 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2559 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2560 vfs_ftpfs_ops
.name
= "ftpfs";
2561 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2562 vfs_ftpfs_ops
.prefix
= "ftp:";
2563 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2564 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2565 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2566 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2567 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2568 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2569 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2570 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2571 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2572 vfs_register_class (&vfs_ftpfs_ops
);
2575 /* --------------------------------------------------------------------------------------------- */