2 Virtual File System: FTP file system.
4 Copyright (C) 1995-2018
5 Free Software Foundation, Inc.
10 Miguel de Icaza, 1995, 1996, 1997
14 Slava Zanko <slavazanko@gmail.com>, 2010, 2013
15 Andrew Borodin <aborodin@vmail.ru>, 2010
17 This file is part of the Midnight Commander.
19 The Midnight Commander is free software: you can redistribute it
20 and/or modify it under the terms of the GNU General Public License as
21 published by the Free Software Foundation, either version 3 of the License,
22 or (at your option) any later version.
24 The Midnight Commander is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
29 You should have received a copy of the GNU General Public License
30 along with this program. If not, see <http://www.gnu.org/licenses/>.
35 * \brief Source: Virtual File System: FTP file system
37 * \author Jakub Jelinek
38 * \author Miguel de Icaza
39 * \author Norbert Warmuth
40 * \author Pavel Machek
41 * \date 1995, 1997, 1998
44 - make it more robust - all the connects etc. should handle EADDRINUSE and
45 ERETRY (have I spelled these names correctly?)
46 - make the user able to flush a connection - all the caches will get empty
47 etc., (tarfs as well), we should give there a user selectable timeout
48 and assign a key sequence.
49 - use hash table instead of linklist to cache ftpfs directory.
54 * NOTE: Usage of tildes is deprecated, consider:
59 * And now: what do I want to do? Do I want to go to /home/pavel or to
60 * ftp://hobit/home/pavel? I think first has better sense...
64 int f = !strcmp( remote_path, "/~" );
65 if (f || !strncmp( remote_path, "/~/", 3 )) {
67 s = mc_build_filename ( qhome (*bucket), remote_path +3-f, (char *) NULL );
75 /* \todo Fix: Namespace pollution: horrible */
78 #include <stdio.h> /* sscanf() */
79 #include <stdlib.h> /* atoi() */
80 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
81 #include <netdb.h> /* struct hostent */
82 #include <sys/socket.h> /* AF_INET */
83 #include <netinet/in.h> /* struct in_addr */
84 #ifdef HAVE_ARPA_INET_H
85 #include <arpa/inet.h>
88 #include <arpa/telnet.h>
89 #ifdef HAVE_SYS_PARAM_H
90 #include <sys/param.h>
94 #include <sys/time.h> /* gettimeofday() */
95 #include <inttypes.h> /* uintmax_t */
97 #include "lib/global.h"
99 #include "lib/strutil.h" /* str_move() */
100 #include "lib/mcconfig.h"
102 #include "lib/tty/tty.h" /* enable/disable interrupt key */
103 #include "lib/widget.h" /* message() */
105 #include "src/history.h"
106 #include "src/setup.h" /* for load_anon_passwd */
108 #include "lib/vfs/vfs.h"
109 #include "lib/vfs/utilvfs.h"
110 #include "lib/vfs/netutil.h"
111 #include "lib/vfs/xdirentry.h"
112 #include "lib/vfs/gc.h" /* vfs_stamp_create */
116 /*** global variables ****************************************************************************/
118 /* Delay to retry a connection */
119 int ftpfs_retry_seconds
= 30;
121 /* Method to use to connect to ftp sites */
122 gboolean ftpfs_use_passive_connections
= TRUE
;
123 gboolean ftpfs_use_passive_connections_over_proxy
= FALSE
;
125 /* Method used to get directory listings:
126 * 1: try 'LIST -la <path>', if it fails
127 * fall back to CWD <path>; LIST
128 * 0: always use CWD <path>; LIST
130 gboolean ftpfs_use_unix_list_options
= TRUE
;
132 /* First "CWD <path>", then "LIST -la ." */
133 gboolean ftpfs_first_cd_then_ls
= TRUE
;
135 /* Use the ~/.netrc */
136 gboolean ftpfs_use_netrc
= TRUE
;
138 /* Anonymous setup */
139 char *ftpfs_anonymous_passwd
= NULL
;
140 int ftpfs_directory_timeout
= 900;
143 char *ftpfs_proxy_host
= NULL
;
145 /* whether we have to use proxy by default? */
146 gboolean ftpfs_always_use_proxy
= FALSE
;
148 gboolean ftpfs_ignore_chattr_errors
= TRUE
;
150 /*** file scope macro definitions ****************************************************************/
152 #ifndef MAXHOSTNAMELEN
153 #define MAXHOSTNAMELEN 64
156 #define SUP ((ftp_super_data_t *) super->data)
157 #define FH_SOCK ((ftp_fh_data_t *) fh->data)->sock
160 #define INADDR_NONE 0xffffffff
163 #define RFC_AUTODETECT 0
167 /* ftpfs_command wait_flag: */
169 #define WAIT_REPLY 0x01
170 #define WANT_STRING 0x02
172 #define FTP_COMMAND_PORT 21
174 /* some defines only used by ftpfs_changetype */
175 /* These two are valid values for the second parameter */
177 #define TYPE_BINARY 1
179 /* This one is only used to initialize bucket->isbinary, don't use it as
180 second parameter to ftpfs_changetype. */
181 #define TYPE_UNKNOWN -1
183 #define ABORT_TIMEOUT 5
184 /*** file scope type declarations ****************************************************************/
186 #ifndef HAVE_SOCKLEN_T
187 typedef int socklen_t
;
190 /* This should match the keywords[] array below */
208 char *proxy
; /* proxy server, NULL if no proxy */
209 int failed_on_login
; /* used to pass the failure reason to upper levels */
210 gboolean use_passive_connection
;
211 int remote_is_amiga
; /* No leading slash allowed for AmiTCP (Amiga) */
213 int cwd_deferred
; /* current_directory was changed but CWD command hasn't
215 int strict
; /* ftp server doesn't understand
216 * "LIST -la <path>"; use "CWD <path>"/
219 int ctl_connection_busy
;
229 /*** file scope variables ************************************************************************/
231 static int ftpfs_errno
;
234 #ifdef FIXME_LATER_ALIGATOR
235 static struct linklist
*connections_list
;
238 static char reply_str
[80];
240 static struct vfs_class vfs_ftpfs_ops
;
242 static GSList
*no_proxy
;
244 static char buffer
[BUF_MEDIUM
];
246 static const char *netrcp
;
248 /* --------------------------------------------------------------------------------------------- */
249 /*** file scope functions ************************************************************************/
250 /* --------------------------------------------------------------------------------------------- */
252 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
253 Translate a Unix path, i.e. MC's internal path representation (e.g.
254 /somedir/somefile) to a path valid for the remote server. Every path
255 transferred to the remote server has to be mangled by this function
256 right prior to sending it.
257 Currently only Amiga ftp servers are handled in a special manner.
259 When the remote server is an amiga:
260 a) strip leading slash if necesarry
261 b) replace first occurrence of ":/" with ":"
262 c) strip trailing "/."
265 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
266 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
,
267 const char *remote_path
);
268 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
269 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
270 const char *netrcpass
);
271 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
273 /* --------------------------------------------------------------------------------------------- */
276 ftpfs_set_blksize (struct stat
*s
)
278 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
279 /* redefine block size */
280 s
->st_blksize
= 64 * 1024; /* FIXME */
284 /* --------------------------------------------------------------------------------------------- */
287 ftpfs_default_stat (struct vfs_class
*me
)
291 s
= vfs_s_default_stat (me
, S_IFDIR
| 0755);
292 ftpfs_set_blksize (s
);
298 /* --------------------------------------------------------------------------------------------- */
301 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
303 if (!SUP
->remote_is_amiga
)
304 return g_strdup (remote_path
);
311 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
312 fflush (MEDATA
->logfile
);
315 /* strip leading slash(es) */
316 while (IS_PATH_SEP (*remote_path
))
320 * Don't change "/" into "", e.g. "CWD " would be
323 if (*remote_path
== '\0')
324 return g_strdup (".");
326 ret
= g_strdup (remote_path
);
328 /* replace first occurrence of ":/" with ":" */
329 p
= strchr (ret
, ':');
330 if (p
!= NULL
&& IS_PATH_SEP (p
[1]))
331 str_move (p
+ 1, p
+ 2);
333 /* strip trailing "/." */
334 p
= strrchr (ret
, PATH_SEP
);
335 if ((p
!= NULL
) && (*(p
+ 1) == '.') && (*(p
+ 2) == '\0'))
342 /* --------------------------------------------------------------------------------------------- */
343 /** Extract the hostname and username from the path */
345 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
346 * ftp://sunsite.unc.edu/pub/linux
347 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
348 * ftp://tsx-11.mit.edu:8192/
349 * ftp://joe@foo.edu:11321/private
350 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
354 static vfs_path_element_t
*
355 ftpfs_correct_url_parameters (const vfs_path_element_t
* velement
)
357 vfs_path_element_t
*path_element
= vfs_path_element_clone (velement
);
359 if (path_element
->port
== 0)
360 path_element
->port
= FTP_COMMAND_PORT
;
362 if (path_element
->user
== NULL
)
364 /* Look up user and password in netrc */
366 ftpfs_netrc_lookup (path_element
->host
, &path_element
->user
, &path_element
->password
);
368 if (path_element
->user
== NULL
)
369 path_element
->user
= g_strdup ("anonymous");
371 /* Look up password in netrc for known user */
372 if (ftpfs_use_netrc
&& path_element
->password
== NULL
)
374 char *new_user
= NULL
;
375 char *new_passwd
= NULL
;
377 ftpfs_netrc_lookup (path_element
->host
, &new_user
, &new_passwd
);
379 /* If user is different, remove password */
380 if (new_user
!= NULL
&& strcmp (path_element
->user
, new_user
) != 0)
381 MC_PTR_FREE (path_element
->password
);
390 /* --------------------------------------------------------------------------------------------- */
391 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
394 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
401 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
403 if (string_buf
!= NULL
)
408 /* cppcheck-suppress invalidscanf */
409 switch (sscanf (answer
, "%d", &code
))
412 if (string_buf
!= NULL
)
413 g_strlcpy (string_buf
, answer
, string_len
);
417 if (answer
[3] == '-')
421 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
423 if (string_buf
!= NULL
)
428 /* cppcheck-suppress invalidscanf */
429 if ((sscanf (answer
, "%d", &i
) > 0) && (code
== i
) && (answer
[3] == ' '))
433 if (string_buf
!= NULL
)
434 g_strlcpy (string_buf
, answer
, string_len
);
442 /* --------------------------------------------------------------------------------------------- */
445 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
449 sock
= ftpfs_open_socket (me
, super
);
452 char *cwdir
= SUP
->current_dir
;
456 SUP
->current_dir
= NULL
;
458 if (ftpfs_login_server (me
, super
, super
->path_element
->password
) != 0)
462 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
464 return sock
== COMPLETE
? 1 : 0;
467 SUP
->current_dir
= cwdir
;
473 /* --------------------------------------------------------------------------------------------- */
477 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
,
483 static int retry
= 0;
484 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
486 cmdstr
= g_string_sized_new (32);
488 g_string_vprintf (cmdstr
, fmt
, ap
);
490 g_string_append (cmdstr
, "\r\n");
492 if (MEDATA
->logfile
!= NULL
)
494 if (strncmp (cmdstr
->str
, "PASS ", 5) == 0)
495 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
500 ret
= fwrite (cmdstr
->str
, cmdstr
->len
, 1, MEDATA
->logfile
);
504 fflush (MEDATA
->logfile
);
508 tty_enable_interrupt_key ();
509 status
= write (SUP
->sock
, cmdstr
->str
, cmdstr
->len
);
516 { /* Remote server has closed connection */
520 status
= ftpfs_reconnect (me
, super
);
522 if (status
&& (write (SUP
->sock
, cmdstr
->str
, cmdstr
->len
) > 0))
528 g_string_free (cmdstr
, TRUE
);
529 tty_disable_interrupt_key ();
534 tty_disable_interrupt_key ();
538 status
= ftpfs_get_reply (me
, SUP
->sock
,
539 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
540 sizeof (reply_str
) - 1);
541 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
545 status
= ftpfs_reconnect (me
, super
);
547 if (status
&& (write (SUP
->sock
, cmdstr
->str
, cmdstr
->len
) > 0))
551 g_string_free (cmdstr
, TRUE
);
555 g_string_free (cmdstr
, TRUE
);
559 /* --------------------------------------------------------------------------------------------- */
562 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
566 vfs_print_message (_("ftpfs: Disconnecting from %s"), super
->path_element
->host
);
567 ftpfs_command (me
, super
, NONE
, "%s", "QUIT");
570 g_free (SUP
->current_dir
);
571 MC_PTR_FREE (super
->data
);
574 /* --------------------------------------------------------------------------------------------- */
577 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
579 if (binary
!= SUP
->isbinary
)
581 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
583 SUP
->isbinary
= binary
;
588 /* --------------------------------------------------------------------------------------------- */
589 /* This routine logs the user in */
592 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
)
596 char *name
; /* login user name */
598 char reply_string
[BUF_MEDIUM
];
600 SUP
->isbinary
= TYPE_UNKNOWN
;
602 if (super
->path_element
->password
!= NULL
) /* explicit password */
603 op
= g_strdup (super
->path_element
->password
);
604 else if (netrcpass
!= NULL
) /* password from netrc */
605 op
= g_strdup (netrcpass
);
606 else if (strcmp (super
->path_element
->user
, "anonymous") == 0
607 || strcmp (super
->path_element
->user
, "ftp") == 0)
609 if (ftpfs_anonymous_passwd
== NULL
) /* default anonymous password */
610 ftpfs_init_passwd ();
611 op
= g_strdup (ftpfs_anonymous_passwd
);
618 p
= g_strdup_printf (_("FTP: Password required for %s"), super
->path_element
->user
);
619 op
= vfs_get_password (p
);
623 super
->path_element
->password
= g_strdup (op
);
626 if (!anon
|| MEDATA
->logfile
)
630 pass
= g_strconcat ("-", op
, (char *) NULL
);
634 /* Proxy server accepts: username@host-we-want-to-connect */
637 g_strconcat (super
->path_element
->user
, "@",
638 super
->path_element
->host
[0] ==
639 '!' ? super
->path_element
->host
+ 1 : super
->path_element
->host
,
642 name
= g_strdup (super
->path_element
->user
);
644 if (ftpfs_get_reply (me
, SUP
->sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
)
648 reply_up
= g_ascii_strup (reply_string
, -1);
649 SUP
->remote_is_amiga
= strstr (reply_up
, "AMIGA") != 0;
650 if (strstr (reply_up
, " SPFTP/1.0.0000 SERVER ")) /* handles `LIST -la` in a weird way */
651 SUP
->strict
= RFC_STRICT
;
656 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n", SUP
->remote_is_amiga
);
657 fflush (MEDATA
->logfile
);
660 vfs_print_message ("%s", _("ftpfs: sending login name"));
662 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
))
665 vfs_print_message ("%s", _("ftpfs: sending user password"));
666 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
667 if (code
== CONTINUE
)
671 p
= g_strdup_printf (_("FTP: Account required for user %s"),
672 super
->path_element
->user
);
673 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "",
674 INPUT_COMPLETE_USERNAMES
);
678 vfs_print_message ("%s", _("ftpfs: sending user account"));
679 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
682 if (code
!= COMPLETE
)
687 vfs_print_message ("%s", _("ftpfs: logged in"));
688 wipe_password (pass
);
693 SUP
->failed_on_login
= 1;
694 wipe_password (super
->path_element
->password
);
695 super
->path_element
->password
= NULL
;
701 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
702 super
->path_element
->user
);
705 wipe_password (pass
);
710 /* --------------------------------------------------------------------------------------------- */
713 ftpfs_load_no_proxy_list (void)
715 /* FixMe: shouldn't be hardcoded!!! */
718 mc_file
= g_build_filename (mc_global
.sysconfig_dir
, "mc.no_proxy", (char *) NULL
);
719 if (exist_file (mc_file
))
723 npf
= fopen (mc_file
, "r");
726 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
728 while (fgets (s
, sizeof (s
), npf
) != NULL
)
732 p
= strchr (s
, '\n');
733 if (p
== NULL
) /* skip bogus entries */
737 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
745 no_proxy
= g_slist_prepend (no_proxy
, g_strdup (s
));
754 /* --------------------------------------------------------------------------------------------- */
755 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
758 ftpfs_check_proxy (const char *host
)
762 if (ftpfs_proxy_host
== NULL
|| *ftpfs_proxy_host
== '\0' || host
== NULL
|| *host
== '\0')
763 return 0; /* sanity check */
768 if (!ftpfs_always_use_proxy
)
771 if (strchr (host
, '.') == NULL
)
774 ftpfs_load_no_proxy_list ();
775 for (npe
= no_proxy
; npe
!= NULL
; npe
= g_slist_next (npe
))
777 const char *domain
= (const char *) npe
->data
;
779 if (domain
[0] == '.')
781 size_t ld
= strlen (domain
);
782 size_t lh
= strlen (host
);
784 while (ld
!= 0 && lh
!= 0 && host
[lh
- 1] == domain
[ld
- 1])
793 else if (g_ascii_strcasecmp (host
, domain
) == 0)
800 /* --------------------------------------------------------------------------------------------- */
803 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
805 vfs_path_element_t
*path_element
;
807 path_element
= vfs_url_split (proxy
, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
808 *host
= g_strdup (path_element
->host
);
809 *port
= path_element
->port
;
810 vfs_path_element_free (path_element
);
813 /* --------------------------------------------------------------------------------------------- */
816 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
818 struct addrinfo hints
, *res
, *curr_res
;
827 /* Use a proxy host? */
828 host
= g_strdup (super
->path_element
->host
);
830 if (host
== NULL
|| *host
== '\0')
832 vfs_print_message ("%s", _("ftpfs: Invalid host name."));
833 ftpfs_errno
= EINVAL
;
838 /* Hosts to connect to that start with a ! should use proxy */
839 tmp_port
= super
->path_element
->port
;
841 if (SUP
->proxy
!= NULL
)
842 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
844 g_snprintf (port
, sizeof (port
), "%hu", (unsigned short) tmp_port
);
852 tty_enable_interrupt_key (); /* clear the interrupt flag */
854 memset (&hints
, 0, sizeof (hints
));
855 hints
.ai_family
= AF_UNSPEC
;
856 hints
.ai_socktype
= SOCK_STREAM
;
859 /* By default, only look up addresses using address types for
860 * which a local interface is configured (i.e. no IPv6 if no IPv6
861 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
862 hints
.ai_flags
= AI_ADDRCONFIG
;
865 e
= getaddrinfo (host
, port
, &hints
, &res
);
868 if (e
== EAI_BADFLAGS
)
870 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
872 e
= getaddrinfo (host
, port
, &hints
, &res
);
880 tty_disable_interrupt_key ();
881 vfs_print_message (_("ftpfs: %s"), gai_strerror (e
));
883 ftpfs_errno
= EINVAL
;
887 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
)
889 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
893 if (curr_res
->ai_next
!= NULL
)
896 tty_disable_interrupt_key ();
897 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno
));
904 vfs_print_message (_("ftpfs: making connection to %s"), host
);
907 if (connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0)
913 if (errno
== EINTR
&& tty_got_interrupt ())
914 vfs_print_message ("%s", _("ftpfs: connection interrupted by user"));
915 else if (res
->ai_next
== NULL
)
916 vfs_print_message (_("ftpfs: connection to server failed: %s"),
917 unix_error_string (errno
));
922 tty_disable_interrupt_key ();
927 tty_disable_interrupt_key ();
931 /* --------------------------------------------------------------------------------------------- */
934 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
936 int retry_seconds
= 0;
939 /* We do not want to use the passive if we are using proxies */
941 SUP
->use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
945 SUP
->failed_on_login
= 0;
947 SUP
->sock
= ftpfs_open_socket (me
, super
);
951 if (ftpfs_login_server (me
, super
, NULL
) != 0)
953 /* Logged in, no need to retry the connection */
958 if (!SUP
->failed_on_login
)
961 /* Close only the socket descriptor */
964 if (ftpfs_retry_seconds
!= 0)
966 retry_seconds
= ftpfs_retry_seconds
;
967 tty_enable_interrupt_key ();
968 for (count_down
= retry_seconds
; count_down
; count_down
--)
970 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
973 if (tty_got_interrupt ())
975 /* ftpfs_errno = E; */
976 tty_disable_interrupt_key ();
980 tty_disable_interrupt_key ();
984 while (retry_seconds
!= 0);
986 SUP
->current_dir
= ftpfs_get_current_directory (me
, super
);
987 if (SUP
->current_dir
== NULL
)
988 SUP
->current_dir
= g_strdup (PATH_SEP_STR
);
993 /* --------------------------------------------------------------------------------------------- */
996 ftpfs_open_archive (struct vfs_s_super
*super
,
997 const vfs_path_t
* vpath
, const vfs_path_element_t
* vpath_element
)
1001 super
->data
= g_new0 (ftp_super_data_t
, 1);
1003 super
->path_element
= ftpfs_correct_url_parameters (vpath_element
);
1005 if (ftpfs_check_proxy (super
->path_element
->host
))
1006 SUP
->proxy
= ftpfs_proxy_host
;
1007 SUP
->use_passive_connection
= ftpfs_use_passive_connections
;
1008 SUP
->strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
1009 SUP
->isbinary
= TYPE_UNKNOWN
;
1010 SUP
->remote_is_amiga
= 0;
1011 SUP
->ctl_connection_busy
= 0;
1012 super
->name
= g_strdup (PATH_SEP_STR
);
1014 vfs_s_new_inode (vpath_element
->class, super
, ftpfs_default_stat (vpath_element
->class));
1016 return ftpfs_open_archive_int (vpath_element
->class, super
);
1019 /* --------------------------------------------------------------------------------------------- */
1022 ftpfs_archive_same (const vfs_path_element_t
* vpath_element
, struct vfs_s_super
*super
,
1023 const vfs_path_t
* vpath
, void *cookie
)
1025 vfs_path_element_t
*path_element
;
1031 path_element
= ftpfs_correct_url_parameters (vpath_element
);
1033 result
= ((strcmp (path_element
->host
, super
->path_element
->host
) == 0)
1034 && (strcmp (path_element
->user
, super
->path_element
->user
) == 0)
1035 && (path_element
->port
== super
->path_element
->port
)) ? 1 : 0;
1037 vfs_path_element_free (path_element
);
1041 /* --------------------------------------------------------------------------------------------- */
1042 /* The returned directory should always contain a trailing slash */
1045 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
1047 char buf
[MC_MAXPATHLEN
+ 1];
1049 if (ftpfs_command (me
, super
, NONE
, "%s", "PWD") == COMPLETE
&&
1050 ftpfs_get_reply (me
, SUP
->sock
, buf
, sizeof (buf
)) == COMPLETE
)
1055 for (bufq
= buf
; *bufq
!= '\0'; bufq
++)
1066 if (!IS_PATH_SEP (bufq
[-1]))
1072 if (IS_PATH_SEP (*bufp
))
1073 return g_strdup (bufp
);
1075 /* If the remote server is an Amiga a leading slash
1076 might be missing. MC needs it because it is used
1077 as separator between hostname and path internally. */
1078 return g_strconcat (PATH_SEP_STR
, bufp
, (char *) NULL
);
1090 /* --------------------------------------------------------------------------------------------- */
1091 /* Setup Passive PASV FTP connection */
1094 ftpfs_setup_passive_pasv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1095 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1099 int xa
, xb
, xc
, xd
, xe
, xf
;
1101 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "%s", "PASV") != COMPLETE
)
1104 /* Parse remote parameters */
1105 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
1109 if (!isdigit ((unsigned char) *c
))
1111 /* cppcheck-suppress invalidscanf */
1112 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
1115 n
[0] = (unsigned char) xa
;
1116 n
[1] = (unsigned char) xb
;
1117 n
[2] = (unsigned char) xc
;
1118 n
[3] = (unsigned char) xd
;
1119 n
[4] = (unsigned char) xe
;
1120 n
[5] = (unsigned char) xf
;
1122 memcpy (&(((struct sockaddr_in
*) sa
)->sin_addr
.s_addr
), (void *) n
, 4);
1123 memcpy (&(((struct sockaddr_in
*) sa
)->sin_port
), (void *) &n
[4], 2);
1125 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
1131 /* --------------------------------------------------------------------------------------------- */
1132 /* Setup Passive EPSV FTP connection */
1135 ftpfs_setup_passive_epsv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1136 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1141 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "%s", "EPSV") != COMPLETE
)
1145 c
= strchr (reply_str
, '|');
1154 if (port
< 0 || port
> 65535)
1156 port
= htons (port
);
1158 switch (sa
->ss_family
)
1161 ((struct sockaddr_in
*) sa
)->sin_port
= port
;
1164 ((struct sockaddr_in6
*) sa
)->sin6_port
= port
;
1170 return (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0) ? 0 : 1;
1173 /* --------------------------------------------------------------------------------------------- */
1174 /* Setup Passive ftp connection, we use it for source routed connections */
1177 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
1178 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1180 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1181 if (sa
->ss_family
== AF_INET
)
1183 if (!ftpfs_setup_passive_pasv (me
, super
, my_socket
, sa
, salen
))
1184 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1185 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1188 /* It's IPV6, so EPSV is our only hope */
1191 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1198 /* --------------------------------------------------------------------------------------------- */
1199 /* Setup Active PORT or EPRT FTP connection */
1202 ftpfs_setup_active (struct vfs_class
*me
, struct vfs_s_super
*super
,
1203 struct sockaddr_storage data_addr
, socklen_t data_addrlen
)
1205 unsigned short int port
;
1209 switch (data_addr
.ss_family
)
1213 port
= ((struct sockaddr_in
*) &data_addr
)->sin_port
;
1217 port
= ((struct sockaddr_in6
*) &data_addr
)->sin6_port
;
1219 /* Not implemented */
1224 addr
= g_try_malloc (NI_MAXHOST
);
1226 ERRNOR (ENOMEM
, -1);
1229 ((struct sockaddr
*) &data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0,
1230 NI_NUMERICHOST
) != 0)
1236 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1239 unsigned char *a
= (unsigned char *) &((struct sockaddr_in
*) &data_addr
)->sin_addr
;
1240 unsigned char *p
= (unsigned char *) &port
;
1242 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1243 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1244 p
[0], p
[1]) == COMPLETE
)
1252 * Converts network MSB first order to host byte order (LSB
1253 * first on i386). If we do it earlier, we will run into an
1254 * endianness issue, because the server actually expects to see
1255 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1257 port
= ntohs (port
);
1259 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1260 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
)
1270 /* --------------------------------------------------------------------------------------------- */
1271 /* Initialize a socket for FTP DATA connection */
1274 ftpfs_init_data_socket (struct vfs_class
*me
, struct vfs_s_super
*super
,
1275 struct sockaddr_storage
*data_addr
, socklen_t
* data_addrlen
)
1279 memset (data_addr
, 0, sizeof (*data_addr
));
1280 *data_addrlen
= sizeof (*data_addr
);
1282 if (SUP
->use_passive_connection
)
1283 result
= getpeername (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1285 result
= getsockname (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1290 switch (data_addr
->ss_family
)
1293 ((struct sockaddr_in
*) data_addr
)->sin_port
= 0;
1296 ((struct sockaddr_in6
*) data_addr
)->sin6_port
= 0;
1299 vfs_print_message ("%s", _("ftpfs: invalid address family"));
1300 ERRNOR (EINVAL
, -1);
1303 result
= socket (data_addr
->ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1307 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1314 /* --------------------------------------------------------------------------------------------- */
1315 /* Initialize FTP DATA connection */
1318 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
1320 struct sockaddr_storage data_addr
;
1321 socklen_t data_addrlen
;
1324 * Don't factor socket initialization out of these conditionals,
1325 * because ftpfs_init_data_socket initializes it in different way
1326 * depending on use_passive_connection flag.
1329 /* Try to establish a passive connection first (if requested) */
1330 if (SUP
->use_passive_connection
)
1334 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1338 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1341 vfs_print_message ("%s", _("ftpfs: could not setup passive mode"));
1342 SUP
->use_passive_connection
= FALSE
;
1347 /* If passive setup is diabled or failed, fallback to active connections */
1348 if (!SUP
->use_passive_connection
)
1352 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1356 if ((bind (data_sock
, (struct sockaddr
*) &data_addr
, data_addrlen
) == 0) &&
1357 (getsockname (data_sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == 0) &&
1358 (listen (data_sock
, 1) == 0) &&
1359 (ftpfs_setup_active (me
, super
, data_addr
, data_addrlen
) != 0))
1365 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1366 SUP
->use_passive_connection
= SUP
->proxy
!= NULL
? ftpfs_use_passive_connections_over_proxy
:
1367 ftpfs_use_passive_connections
;
1373 /* --------------------------------------------------------------------------------------------- */
1376 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1377 const char *remote
, int isbinary
, int reget
)
1379 struct sockaddr_storage from
;
1381 socklen_t fromlen
= sizeof (from
);
1383 /* FTP doesn't allow to open more than one file at a time */
1384 if (SUP
->ctl_connection_busy
)
1387 s
= ftpfs_initconn (me
, super
);
1391 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1399 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1409 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1410 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1411 /* WarFtpD can't STORE //filename */
1412 IS_PATH_SEP (*remote_path
) ? remote_path
+ 1 : remote_path
);
1413 g_free (remote_path
);
1416 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1424 if (SUP
->use_passive_connection
)
1428 tty_enable_interrupt_key ();
1429 data
= accept (s
, (struct sockaddr
*) &from
, &fromlen
);
1431 ftpfs_errno
= errno
;
1432 tty_disable_interrupt_key ();
1437 SUP
->ctl_connection_busy
= 1;
1441 /* --------------------------------------------------------------------------------------------- */
1444 ftpfs_linear_abort (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1446 struct vfs_s_super
*super
= FH_SUPER
;
1447 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1449 int dsock
= FH_SOCK
;
1451 SUP
->ctl_connection_busy
= 0;
1453 vfs_print_message ("%s", _("ftpfs: aborting transfer."));
1454 if (send (SUP
->sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
))
1456 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno
));
1462 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
)
1464 vfs_print_message ("%s", _("ftpfs: abort failed"));
1472 FD_SET (dsock
, &mask
);
1473 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0)
1475 struct timeval start_tim
, tim
;
1478 gettimeofday (&start_tim
, NULL
);
1479 /* flush the remaining data */
1480 while (read (dsock
, buf
, sizeof (buf
)) > 0)
1482 gettimeofday (&tim
, NULL
);
1483 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
)
1485 /* server keeps sending, drop the connection and ftpfs_reconnect */
1487 ftpfs_reconnect (me
, super
);
1494 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1495 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1498 /* --------------------------------------------------------------------------------------------- */
1502 resolve_symlink_without_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1503 struct vfs_s_inode
*dir
)
1505 struct linklist
*flist
;
1506 struct direntry
*fe
, *fel
;
1507 char tmp
[MC_MAXPATHLEN
];
1510 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1511 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
)
1513 /* flist->data->l_stat is alread initialized with 0 */
1515 if (S_ISLNK (fel
->s
.st_mode
) && fel
->linkname
)
1517 if (IS_PATH_SEP (fel
->linkname
[0]))
1519 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1521 strcpy (tmp
, fel
->linkname
);
1525 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1527 strcpy (tmp
, dir
->remote_path
);
1529 strcat (tmp
, PATH_SEP_STR
);
1530 strcat (tmp
+ 1, fel
->linkname
);
1532 for (depth
= 0; depth
< 100; depth
++)
1533 { /* depth protects against recursive symbolic links */
1534 canonicalize_pathname (tmp
);
1535 fe
= _get_file_entry_t (bucket
, tmp
, 0, 0);
1538 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0)
1540 /* Symlink points to link which isn't resolved, yet. */
1541 if (IS_PATH_SEP (fe
->linkname
[0]))
1543 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1545 strcpy (tmp
, fe
->linkname
);
1549 /* at this point tmp looks always like this
1550 /directory/filename, i.e. no need to check
1551 strrchr's return value */
1552 *(strrchr (tmp
, PATH_SEP
) + 1) = '\0'; /* dirname */
1553 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1555 strcat (tmp
, fe
->linkname
);
1561 fel
->l_stat
= g_new (struct stat
, 1);
1562 if (S_ISLNK (fe
->s
.st_mode
))
1563 *fel
->l_stat
= *fe
->l_stat
;
1565 *fel
->l_stat
= fe
->s
;
1566 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1573 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1576 /* --------------------------------------------------------------------------------------------- */
1579 resolve_symlink_with_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1580 struct vfs_s_inode
*dir
)
1582 char buffer
[2048] = "", *filename
;
1586 struct linklist
*flist
;
1587 struct direntry
*fe
;
1588 int switch_method
= 0;
1590 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1591 if (strchr (dir
->remote_path
, ' '))
1593 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
)
1595 vfs_print_message ("%s", _("ftpfs: CWD failed."));
1598 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1601 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", dir
->remote_path
, TYPE_ASCII
, 0);
1605 vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink"));
1609 fp
= fdopen (sock
, "r");
1613 vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink"));
1616 tty_enable_interrupt_key ();
1617 flist
= dir
->file_list
->next
;
1622 if (flist
== dir
->file_list
)
1625 flist
= flist
->next
;
1627 while (!S_ISLNK (fe
->s
.st_mode
));
1630 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1632 if (MEDATA
->logfile
)
1634 fputs (buffer
, MEDATA
->logfile
);
1635 fflush (MEDATA
->logfile
);
1637 vfs_die ("This code should be commented out\n");
1638 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
))
1640 int r
= strcmp (fe
->name
, filename
);
1644 if (S_ISLNK (s
.st_mode
))
1646 /* This server doesn't understand LIST -lLa */
1650 fe
->l_stat
= g_new (struct stat
, 1);
1651 if (fe
->l_stat
== NULL
)
1654 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1663 while (fgets (buffer
, sizeof (buffer
), fp
) != NULL
);
1664 tty_disable_interrupt_key ();
1666 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1669 /* --------------------------------------------------------------------------------------------- */
1672 resolve_symlink (struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1674 vfs_print_message ("%s", _("Resolving symlink..."));
1676 if (SUP
->strict_rfc959_list_cmd
)
1677 resolve_symlink_without_ls_options (me
, super
, dir
);
1679 resolve_symlink_with_ls_options (me
, super
, dir
);
1683 /* --------------------------------------------------------------------------------------------- */
1686 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1688 struct vfs_s_entry
*ent
;
1689 struct vfs_s_super
*super
= dir
->super
;
1690 int sock
, num_entries
= 0;
1693 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
->strict
== RFC_STRICT
)
1694 || (strchr (remote_path
, ' ') != NULL
);
1697 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1700 RFC_STRICT
? _("(strict rfc959)") : "", cd_first
? _("(chdir first)") : "");
1704 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
)
1706 ftpfs_errno
= ENOENT
;
1707 vfs_print_message ("%s", _("ftpfs: CWD failed."));
1712 gettimeofday (&dir
->timestamp
, NULL
);
1713 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1715 if (SUP
->strict
== RFC_STRICT
)
1716 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1718 /* Dirty hack to avoid autoprepending / to . */
1719 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1720 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1725 /* Trailing "/." is necessary if remote_path is a symlink */
1726 path
= mc_build_filename (remote_path
, ".", (char *) NULL
);
1727 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1734 /* Clear the interrupt flag */
1735 tty_enable_interrupt_key ();
1737 vfs_parse_ls_lga_init ();
1741 size_t count_spaces
= 0;
1743 char lc_buffer
[BUF_8K
] = "\0";
1745 res
= vfs_s_get_line_interruptible (me
, lc_buffer
, sizeof (lc_buffer
), sock
);
1751 me
->verrno
= ECONNRESET
;
1753 SUP
->ctl_connection_busy
= 0;
1754 tty_disable_interrupt_key ();
1755 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1756 vfs_print_message (_("%s: failure"), me
->name
);
1760 if (MEDATA
->logfile
)
1762 fputs (lc_buffer
, MEDATA
->logfile
);
1763 fputs ("\n", MEDATA
->logfile
);
1764 fflush (MEDATA
->logfile
);
1767 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1768 i
= ent
->ino
->st
.st_nlink
;
1769 if (!vfs_parse_ls_lga
1770 (lc_buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
, &count_spaces
))
1772 vfs_s_free_entry (me
, ent
);
1775 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1777 vfs_s_store_filename_leading_spaces (ent
, count_spaces
);
1778 vfs_s_insert_entry (me
, dir
, ent
);
1782 SUP
->ctl_connection_busy
= 0;
1783 me
->verrno
= E_REMOTE
;
1784 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1787 if (num_entries
== 0 && !cd_first
)
1789 /* The LIST command may produce an empty output. In such scenario
1790 it is not clear whether this is caused by 'remote_path' being
1791 a non-existent path or for some other reason (listing emtpy
1792 directory without the -a option, non-readable directory, etc.).
1794 Since 'dir_load' is a crucial method, when it comes to determine
1795 whether a given path is a _directory_, the code must try its best
1796 to determine the type of 'remote_path'. The only reliable way to
1797 achieve this is trough issuing a CWD command. */
1803 vfs_s_normalize_filename_leading_spaces (dir
, vfs_parse_ls_lga_get_final_spaces ());
1805 if (SUP
->strict
== RFC_AUTODETECT
)
1806 SUP
->strict
= RFC_DARING
;
1808 vfs_print_message (_("%s: done."), me
->name
);
1812 if (SUP
->strict
== RFC_AUTODETECT
)
1814 /* It's our first attempt to get a directory listing from this
1815 server (UNIX style LIST command) */
1816 SUP
->strict
= RFC_STRICT
;
1817 /* I hate goto, but recursive call needs another 8K on stack */
1818 /* return ftpfs_dir_load (me, dir, remote_path); */
1822 vfs_print_message ("%s", _("ftpfs: failed; nowhere to fallback to"));
1823 ERRNOR (EACCES
, -1);
1826 /* --------------------------------------------------------------------------------------------- */
1829 ftpfs_file_store (struct vfs_class
*me
, vfs_file_handler_t
* fh
, char *name
, char *localname
)
1833 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1838 char lc_buffer
[BUF_8K
];
1841 struct vfs_s_super
*super
= FH_SUPER
;
1842 ftp_fh_data_t
*ftp
= (ftp_fh_data_t
*) fh
->data
;
1844 h
= open (localname
, O_RDONLY
);
1848 if (fstat (h
, &s
) == -1 ||
1850 ftpfs_open_data_connection (me
, super
, ftp
->append
? "APPE" : "STOR", name
, TYPE_BINARY
,
1856 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1859 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1861 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1865 tty_enable_interrupt_key ();
1868 ssize_t n_read
, n_written
;
1870 while ((n_read
= read (h
, lc_buffer
, sizeof (lc_buffer
))) == -1)
1874 ftpfs_errno
= errno
;
1877 if (tty_got_interrupt ())
1879 ftpfs_errno
= EINTR
;
1887 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
)
1889 if (n_written
== -1)
1891 if (errno
== EINTR
&& !tty_got_interrupt ())
1894 ftpfs_errno
= errno
;
1898 n_read
-= n_written
;
1900 vfs_print_message ("%s: %" PRIuMAX
"/%" PRIuMAX
,
1901 _("ftpfs: storing file"), (uintmax_t) n_stored
, (uintmax_t) s
.st_size
);
1903 tty_disable_interrupt_key ();
1905 SUP
->ctl_connection_busy
= 0;
1907 if (ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
)
1911 tty_disable_interrupt_key ();
1913 SUP
->ctl_connection_busy
= 0;
1915 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1919 /* --------------------------------------------------------------------------------------------- */
1922 ftpfs_linear_start (struct vfs_class
*me
, vfs_file_handler_t
* fh
, off_t offset
)
1926 if (fh
->data
== NULL
)
1927 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
1929 name
= vfs_s_fullpath (me
, fh
->ino
);
1932 FH_SOCK
= ftpfs_open_data_connection (me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1936 fh
->linear
= LS_LINEAR_OPEN
;
1937 ((ftp_fh_data_t
*) fh
->data
)->append
= 0;
1941 /* --------------------------------------------------------------------------------------------- */
1944 ftpfs_linear_read (struct vfs_class
*me
, vfs_file_handler_t
* fh
, void *buf
, size_t len
)
1947 struct vfs_s_super
*super
= FH_SUPER
;
1949 while ((n
= read (FH_SOCK
, buf
, len
)) < 0)
1951 if ((errno
== EINTR
) && !tty_got_interrupt ())
1957 ftpfs_linear_abort (me
, fh
);
1961 SUP
->ctl_connection_busy
= 0;
1964 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1965 ERRNOR (E_REMOTE
, -1);
1971 /* --------------------------------------------------------------------------------------------- */
1974 ftpfs_linear_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1977 ftpfs_linear_abort (me
, fh
);
1980 /* --------------------------------------------------------------------------------------------- */
1983 ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1989 case VFS_CTL_IS_NOTREADY
:
1993 if (FH
->linear
== LS_NOT_LINEAR
)
1994 vfs_die ("You may not do this");
1995 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1998 v
= vfs_s_select_on_two (((ftp_fh_data_t
*) (FH
->data
))->sock
, 0);
1999 return (((v
< 0) && (errno
== EINTR
)) || v
== 0) ? 1 : 0;
2006 /* --------------------------------------------------------------------------------------------- */
2009 ftpfs_send_command (const vfs_path_t
* vpath
, const char *cmd
, int flags
)
2013 struct vfs_s_super
*super
;
2015 const vfs_path_element_t
*path_element
;
2016 int flush_directory_cache
= (flags
& OPT_FLUSH
);
2018 path_element
= vfs_path_get_by_index (vpath
, -1);
2020 rpath
= vfs_s_get_path (vpath
, &super
, 0);
2024 p
= ftpfs_translate_path (path_element
->class, super
, rpath
);
2025 r
= ftpfs_command (path_element
->class, super
, WAIT_REPLY
, cmd
, p
);
2027 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
2028 if (flags
& OPT_IGNORE_ERROR
)
2032 path_element
->class->verrno
= EPERM
;
2035 if (flush_directory_cache
)
2036 vfs_s_invalidate (path_element
->class, super
);
2040 /* --------------------------------------------------------------------------------------------- */
2043 ftpfs_stat (const vfs_path_t
* vpath
, struct stat
*buf
)
2047 ret
= vfs_s_stat (vpath
, buf
);
2048 ftpfs_set_blksize (buf
);
2052 /* --------------------------------------------------------------------------------------------- */
2055 ftpfs_lstat (const vfs_path_t
* vpath
, struct stat
*buf
)
2059 ret
= vfs_s_lstat (vpath
, buf
);
2060 ftpfs_set_blksize (buf
);
2064 /* --------------------------------------------------------------------------------------------- */
2067 ftpfs_fstat (void *vfs_info
, struct stat
*buf
)
2071 ret
= vfs_s_fstat (vfs_info
, buf
);
2072 ftpfs_set_blksize (buf
);
2076 /* --------------------------------------------------------------------------------------------- */
2079 ftpfs_chmod (const vfs_path_t
* vpath
, mode_t mode
)
2081 char buf
[BUF_SMALL
];
2084 g_snprintf (buf
, sizeof (buf
), "SITE CHMOD %4.4o /%%s", (unsigned int) (mode
& 07777));
2086 ret
= ftpfs_send_command (vpath
, buf
, OPT_FLUSH
);
2088 return ftpfs_ignore_chattr_errors
? 0 : ret
;
2091 /* --------------------------------------------------------------------------------------------- */
2094 ftpfs_chown (const vfs_path_t
* vpath
, uid_t owner
, gid_t group
)
2101 ftpfs_errno
= EPERM
;
2104 /* Everyone knows it is not possible to chown remotely, so why bother them.
2105 If someone's root, then copy/move will always try to chown it... */
2113 /* --------------------------------------------------------------------------------------------- */
2116 ftpfs_unlink (const vfs_path_t
* vpath
)
2118 return ftpfs_send_command (vpath
, "DELE /%s", OPT_FLUSH
);
2121 /* --------------------------------------------------------------------------------------------- */
2123 /* Return 1 if path is the same directory as the one we are in now */
2125 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
2129 if (SUP
->current_dir
== NULL
)
2131 return (strcmp (path
, SUP
->current_dir
) == 0);
2134 /* --------------------------------------------------------------------------------------------- */
2137 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
2142 if (!SUP
->cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
2145 p
= ftpfs_translate_path (me
, super
, remote_path
);
2146 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
2153 g_free (SUP
->current_dir
);
2154 SUP
->current_dir
= g_strdup (remote_path
);
2155 SUP
->cwd_deferred
= 0;
2160 /* --------------------------------------------------------------------------------------------- */
2163 ftpfs_rename (const vfs_path_t
* vpath1
, const vfs_path_t
* vpath2
)
2165 ftpfs_send_command (vpath1
, "RNFR /%s", OPT_FLUSH
);
2166 return ftpfs_send_command (vpath2
, "RNTO /%s", OPT_FLUSH
);
2169 /* --------------------------------------------------------------------------------------------- */
2172 ftpfs_mkdir (const vfs_path_t
* vpath
, mode_t mode
)
2174 (void) mode
; /* FIXME: should be used */
2176 return ftpfs_send_command (vpath
, "MKD /%s", OPT_FLUSH
);
2179 /* --------------------------------------------------------------------------------------------- */
2182 ftpfs_rmdir (const vfs_path_t
* vpath
)
2184 return ftpfs_send_command (vpath
, "RMD /%s", OPT_FLUSH
);
2187 /* --------------------------------------------------------------------------------------------- */
2190 ftpfs_fh_free_data (vfs_file_handler_t
* fh
)
2193 MC_PTR_FREE (fh
->data
);
2196 /* --------------------------------------------------------------------------------------------- */
2199 ftpfs_fh_open (struct vfs_class
*me
, vfs_file_handler_t
* fh
, int flags
, mode_t mode
)
2205 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
2206 ftp
= (ftp_fh_data_t
*) fh
->data
;
2207 /* File will be written only, so no need to retrieve it from ftp server */
2208 if (((flags
& O_WRONLY
) == O_WRONLY
) && ((flags
& (O_RDONLY
| O_RDWR
)) == 0))
2210 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2217 /* ftpfs_linear_start() called, so data will be written
2218 * to local temporary file and stored to ftp server
2219 * by vfs_s_close later
2221 if (((ftp_super_data_t
*) (FH_SUPER
->data
))->ctl_connection_busy
)
2223 if (!fh
->ino
->localname
)
2228 handle
= vfs_mkstemps (&vpath
, me
->name
, fh
->ino
->ent
->name
);
2231 vfs_path_free (vpath
);
2235 fh
->ino
->localname
= g_strdup (vfs_path_as_str (vpath
));
2236 vfs_path_free (vpath
);
2237 ftp
->append
= flags
& O_APPEND
;
2241 name
= vfs_s_fullpath (me
, fh
->ino
);
2245 ftpfs_open_data_connection (me
, fh
->ino
->super
,
2246 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
2251 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2255 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
2257 if (fh
->ino
->localname
)
2259 unlink (fh
->ino
->localname
);
2260 MC_PTR_FREE (fh
->ino
->localname
);
2265 if (!fh
->ino
->localname
&& vfs_s_retrieve_file (me
, fh
->ino
) == -1)
2267 if (!fh
->ino
->localname
)
2268 vfs_die ("retrieve_file failed to fill in localname");
2272 ftpfs_fh_free_data (fh
);
2276 /* --------------------------------------------------------------------------------------------- */
2279 ftpfs_fh_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
2281 if (fh
->handle
!= -1 && !fh
->ino
->localname
)
2283 ftp_super_data_t
*ftp
= (ftp_super_data_t
*) fh
->ino
->super
->data
;
2287 ftp
->ctl_connection_busy
= 0;
2288 /* File is stored to destination already, so
2289 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2292 if (ftpfs_get_reply (me
, ftp
->sock
, NULL
, 0) != COMPLETE
)
2294 vfs_s_invalidate (me
, FH_SUPER
);
2300 /* --------------------------------------------------------------------------------------------- */
2303 ftpfs_done (struct vfs_class
*me
)
2307 g_slist_free_full (no_proxy
, g_free
);
2309 g_free (ftpfs_anonymous_passwd
);
2310 g_free (ftpfs_proxy_host
);
2313 /* --------------------------------------------------------------------------------------------- */
2316 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
2320 for (iter
= MEDATA
->supers
; iter
!= NULL
; iter
= g_list_next (iter
))
2322 const struct vfs_s_super
*super
= (const struct vfs_s_super
*) iter
->data
;
2325 name
= vfs_path_element_build_pretty_path_str (super
->path_element
);
2332 /* --------------------------------------------------------------------------------------------- */
2335 ftpfs_netrc_next (void)
2339 static const char *const keywords
[] = { "default", "machine",
2340 "login", "password", "passwd", "account", "macdef", NULL
2345 netrcp
= skip_separators (netrcp
);
2346 if (*netrcp
!= '\n')
2355 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++)
2357 if (*netrcp
== '\\')
2364 for (; *netrcp
!= '\0' && !whiteness (*netrcp
) && *netrcp
!= ','; netrcp
++)
2366 if (*netrcp
== '\\')
2375 for (i
= NETRC_DEFAULT
; keywords
[i
- 1] != NULL
; i
++)
2376 if (strcmp (keywords
[i
- 1], buffer
) == 0)
2379 return NETRC_UNKNOWN
;
2382 /* --------------------------------------------------------------------------------------------- */
2385 ftpfs_netrc_bad_mode (const char *netrcname
)
2389 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077))
2391 static int be_angry
= 1;
2395 message (D_ERROR
, MSG_ERROR
,
2396 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2404 /* --------------------------------------------------------------------------------------------- */
2405 /* Scan .netrc until we find matching "machine" or "default"
2406 * domain is used for additional matching
2407 * No search is done after "default" in compliance with "man netrc"
2408 * Return 0 if found, -1 otherwise */
2411 ftpfs_find_machine (const char *host
, const char *domain
)
2420 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
)
2422 if (keyword
== NETRC_DEFAULT
)
2425 if (keyword
== NETRC_MACDEF
)
2427 /* Scan for an empty line, which concludes "macdef" */
2430 while (*netrcp
&& *netrcp
!= '\n')
2432 if (*netrcp
!= '\n')
2436 while (*netrcp
&& *netrcp
!= '\n');
2440 if (keyword
!= NETRC_MACHINE
)
2443 /* Take machine name */
2444 if (ftpfs_netrc_next () == NETRC_NONE
)
2447 if (g_ascii_strcasecmp (host
, buffer
) != 0)
2449 /* Try adding our domain to short names in .netrc */
2450 const char *host_domain
= strchr (host
, '.');
2454 /* Compare domain part */
2455 if (g_ascii_strcasecmp (host_domain
, domain
) != 0)
2458 /* Compare local part */
2459 if (g_ascii_strncasecmp (host
, buffer
, host_domain
- host
) != 0)
2470 /* --------------------------------------------------------------------------------------------- */
2471 /* Extract login and password from .netrc for the host.
2473 * Returns 0 for success, -1 for error */
2476 ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
2479 char *tmp_pass
= NULL
;
2480 char hostname
[MAXHOSTNAMELEN
];
2482 static struct rupcache
2484 struct rupcache
*next
;
2488 } *rup_cache
= NULL
, *rupp
;
2490 /* Initialize *login and *pass */
2491 MC_PTR_FREE (*login
);
2492 MC_PTR_FREE (*pass
);
2494 /* Look up in the cache first */
2495 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
)
2497 if (strcmp (host
, rupp
->host
) == 0)
2499 *login
= g_strdup (rupp
->login
);
2500 *pass
= g_strdup (rupp
->pass
);
2505 /* Load current .netrc */
2506 netrcname
= g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL
);
2507 if (!g_file_get_contents (netrcname
, &netrc
, NULL
, NULL
))
2515 /* Find our own domain name */
2516 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2519 domain
= strchr (hostname
, '.');
2523 /* Scan for "default" and matching "machine" keywords */
2524 ftpfs_find_machine (host
, domain
);
2526 /* Scan for keywords following "default" and "machine" */
2532 keyword
= ftpfs_netrc_next ();
2537 if (ftpfs_netrc_next () == NETRC_NONE
)
2543 /* We have another name already - should not happen */
2550 /* We have login name now */
2551 *login
= g_strdup (buffer
);
2554 case NETRC_PASSWORD
:
2556 if (ftpfs_netrc_next () == NETRC_NONE
)
2562 /* Ignore unsafe passwords */
2563 if (*login
!= NULL
&&
2564 strcmp (*login
, "anonymous") != 0 && strcmp (*login
, "ftp") != 0
2565 && ftpfs_netrc_bad_mode (netrcname
))
2571 /* Remember password. pass may be NULL, so use tmp_pass */
2572 if (tmp_pass
== NULL
)
2573 tmp_pass
= g_strdup (buffer
);
2577 /* "account" is followed by a token which we ignore */
2578 if (ftpfs_netrc_next () == NETRC_NONE
)
2584 /* Ignore account, but warn user anyways */
2585 ftpfs_netrc_bad_mode (netrcname
);
2589 /* Unexpected keyword or end of file */
2601 rupp
= g_new (struct rupcache
, 1);
2602 rupp
->host
= g_strdup (host
);
2603 rupp
->login
= g_strdup (*login
);
2604 rupp
->pass
= g_strdup (tmp_pass
);
2606 rupp
->next
= rup_cache
;
2614 /* --------------------------------------------------------------------------------------------- */
2615 /*** public functions ****************************************************************************/
2616 /* --------------------------------------------------------------------------------------------- */
2618 /** This routine is called as the last step in load_setup */
2620 ftpfs_init_passwd (void)
2622 ftpfs_anonymous_passwd
= load_anon_passwd ();
2623 if (ftpfs_anonymous_passwd
)
2626 /* If there is no anonymous ftp password specified
2627 * then we'll just use anonymous@
2628 * We don't send any other thing because:
2629 * - We want to remain anonymous
2630 * - We want to stop SPAM
2631 * - We don't want to let ftp sites to discriminate by the user,
2634 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
2637 /* --------------------------------------------------------------------------------------------- */
2642 static struct vfs_s_subclass ftpfs_subclass
;
2646 ftpfs_subclass
.flags
= VFS_S_REMOTE
| VFS_S_USETMP
;
2647 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2648 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2649 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2650 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2651 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2652 ftpfs_subclass
.fh_free_data
= ftpfs_fh_free_data
;
2653 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2654 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2655 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2656 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2657 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2659 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2660 vfs_ftpfs_ops
.name
= "ftpfs";
2661 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2662 vfs_ftpfs_ops
.prefix
= "ftp";
2663 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2664 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2665 vfs_ftpfs_ops
.stat
= ftpfs_stat
;
2666 vfs_ftpfs_ops
.lstat
= ftpfs_lstat
;
2667 vfs_ftpfs_ops
.fstat
= ftpfs_fstat
;
2668 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2669 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2670 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2671 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2672 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2673 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2674 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2675 vfs_register_class (&vfs_ftpfs_ops
);
2678 /* --------------------------------------------------------------------------------------------- */