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_s_subclass ftpfs_subclass
;
241 static struct vfs_class
*vfs_ftpfs_ops
= (struct vfs_class
*) &ftpfs_subclass
;
243 static GSList
*no_proxy
;
245 static char buffer
[BUF_MEDIUM
];
247 static const char *netrcp
;
249 /* --------------------------------------------------------------------------------------------- */
250 /*** file scope functions ************************************************************************/
251 /* --------------------------------------------------------------------------------------------- */
253 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
254 Translate a Unix path, i.e. MC's internal path representation (e.g.
255 /somedir/somefile) to a path valid for the remote server. Every path
256 transferred to the remote server has to be mangled by this function
257 right prior to sending it.
258 Currently only Amiga ftp servers are handled in a special manner.
260 When the remote server is an amiga:
261 a) strip leading slash if necesarry
262 b) replace first occurrence of ":/" with ":"
263 c) strip trailing "/."
266 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
267 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
,
268 const char *remote_path
);
269 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
270 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
271 const char *netrcpass
);
272 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
274 /* --------------------------------------------------------------------------------------------- */
277 ftpfs_set_blksize (struct stat
*s
)
279 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
280 /* redefine block size */
281 s
->st_blksize
= 64 * 1024; /* FIXME */
285 /* --------------------------------------------------------------------------------------------- */
288 ftpfs_default_stat (struct vfs_class
*me
)
292 s
= vfs_s_default_stat (me
, S_IFDIR
| 0755);
293 ftpfs_set_blksize (s
);
299 /* --------------------------------------------------------------------------------------------- */
302 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
304 if (!SUP
->remote_is_amiga
)
305 return g_strdup (remote_path
);
312 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
313 fflush (MEDATA
->logfile
);
316 /* strip leading slash(es) */
317 while (IS_PATH_SEP (*remote_path
))
321 * Don't change "/" into "", e.g. "CWD " would be
324 if (*remote_path
== '\0')
325 return g_strdup (".");
327 ret
= g_strdup (remote_path
);
329 /* replace first occurrence of ":/" with ":" */
330 p
= strchr (ret
, ':');
331 if (p
!= NULL
&& IS_PATH_SEP (p
[1]))
332 str_move (p
+ 1, p
+ 2);
334 /* strip trailing "/." */
335 p
= strrchr (ret
, PATH_SEP
);
336 if ((p
!= NULL
) && (*(p
+ 1) == '.') && (*(p
+ 2) == '\0'))
343 /* --------------------------------------------------------------------------------------------- */
344 /** Extract the hostname and username from the path */
346 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
347 * ftp://sunsite.unc.edu/pub/linux
348 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
349 * ftp://tsx-11.mit.edu:8192/
350 * ftp://joe@foo.edu:11321/private
351 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
355 static vfs_path_element_t
*
356 ftpfs_correct_url_parameters (const vfs_path_element_t
* velement
)
358 vfs_path_element_t
*path_element
= vfs_path_element_clone (velement
);
360 if (path_element
->port
== 0)
361 path_element
->port
= FTP_COMMAND_PORT
;
363 if (path_element
->user
== NULL
)
365 /* Look up user and password in netrc */
367 ftpfs_netrc_lookup (path_element
->host
, &path_element
->user
, &path_element
->password
);
369 if (path_element
->user
== NULL
)
370 path_element
->user
= g_strdup ("anonymous");
372 /* Look up password in netrc for known user */
373 if (ftpfs_use_netrc
&& path_element
->password
== NULL
)
375 char *new_user
= NULL
;
376 char *new_passwd
= NULL
;
378 ftpfs_netrc_lookup (path_element
->host
, &new_user
, &new_passwd
);
380 /* If user is different, remove password */
381 if (new_user
!= NULL
&& strcmp (path_element
->user
, new_user
) != 0)
382 MC_PTR_FREE (path_element
->password
);
391 /* --------------------------------------------------------------------------------------------- */
392 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
395 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
402 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
404 if (string_buf
!= NULL
)
409 /* cppcheck-suppress invalidscanf */
410 switch (sscanf (answer
, "%d", &code
))
413 if (string_buf
!= NULL
)
414 g_strlcpy (string_buf
, answer
, string_len
);
418 if (answer
[3] == '-')
422 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
424 if (string_buf
!= NULL
)
429 /* cppcheck-suppress invalidscanf */
430 if ((sscanf (answer
, "%d", &i
) > 0) && (code
== i
) && (answer
[3] == ' '))
434 if (string_buf
!= NULL
)
435 g_strlcpy (string_buf
, answer
, string_len
);
443 /* --------------------------------------------------------------------------------------------- */
446 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
450 sock
= ftpfs_open_socket (me
, super
);
453 char *cwdir
= SUP
->current_dir
;
457 SUP
->current_dir
= NULL
;
459 if (ftpfs_login_server (me
, super
, super
->path_element
->password
) != 0)
463 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
465 return sock
== COMPLETE
? 1 : 0;
468 SUP
->current_dir
= cwdir
;
474 /* --------------------------------------------------------------------------------------------- */
478 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
,
484 static int retry
= 0;
485 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
487 cmdstr
= g_string_sized_new (32);
489 g_string_vprintf (cmdstr
, fmt
, ap
);
491 g_string_append (cmdstr
, "\r\n");
493 if (MEDATA
->logfile
!= NULL
)
495 if (strncmp (cmdstr
->str
, "PASS ", 5) == 0)
496 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
501 ret
= fwrite (cmdstr
->str
, cmdstr
->len
, 1, MEDATA
->logfile
);
505 fflush (MEDATA
->logfile
);
509 tty_enable_interrupt_key ();
510 status
= write (SUP
->sock
, cmdstr
->str
, cmdstr
->len
);
517 { /* Remote server has closed connection */
521 status
= ftpfs_reconnect (me
, super
);
523 if (status
&& (write (SUP
->sock
, cmdstr
->str
, cmdstr
->len
) > 0))
529 g_string_free (cmdstr
, TRUE
);
530 tty_disable_interrupt_key ();
535 tty_disable_interrupt_key ();
539 status
= ftpfs_get_reply (me
, SUP
->sock
,
540 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
541 sizeof (reply_str
) - 1);
542 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
546 status
= ftpfs_reconnect (me
, super
);
548 if (status
&& (write (SUP
->sock
, cmdstr
->str
, cmdstr
->len
) > 0))
552 g_string_free (cmdstr
, TRUE
);
556 g_string_free (cmdstr
, TRUE
);
560 /* --------------------------------------------------------------------------------------------- */
563 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
567 vfs_print_message (_("ftpfs: Disconnecting from %s"), super
->path_element
->host
);
568 ftpfs_command (me
, super
, NONE
, "%s", "QUIT");
571 g_free (SUP
->current_dir
);
572 MC_PTR_FREE (super
->data
);
575 /* --------------------------------------------------------------------------------------------- */
578 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
580 if (binary
!= SUP
->isbinary
)
582 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
584 SUP
->isbinary
= binary
;
589 /* --------------------------------------------------------------------------------------------- */
590 /* This routine logs the user in */
593 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
)
597 char *name
; /* login user name */
599 char reply_string
[BUF_MEDIUM
];
601 SUP
->isbinary
= TYPE_UNKNOWN
;
603 if (super
->path_element
->password
!= NULL
) /* explicit password */
604 op
= g_strdup (super
->path_element
->password
);
605 else if (netrcpass
!= NULL
) /* password from netrc */
606 op
= g_strdup (netrcpass
);
607 else if (strcmp (super
->path_element
->user
, "anonymous") == 0
608 || strcmp (super
->path_element
->user
, "ftp") == 0)
610 if (ftpfs_anonymous_passwd
== NULL
) /* default anonymous password */
611 ftpfs_init_passwd ();
612 op
= g_strdup (ftpfs_anonymous_passwd
);
619 p
= g_strdup_printf (_("FTP: Password required for %s"), super
->path_element
->user
);
620 op
= vfs_get_password (p
);
624 super
->path_element
->password
= g_strdup (op
);
627 if (!anon
|| MEDATA
->logfile
)
631 pass
= g_strconcat ("-", op
, (char *) NULL
);
635 /* Proxy server accepts: username@host-we-want-to-connect */
638 g_strconcat (super
->path_element
->user
, "@",
639 super
->path_element
->host
[0] ==
640 '!' ? super
->path_element
->host
+ 1 : super
->path_element
->host
,
643 name
= g_strdup (super
->path_element
->user
);
645 if (ftpfs_get_reply (me
, SUP
->sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
)
649 reply_up
= g_ascii_strup (reply_string
, -1);
650 SUP
->remote_is_amiga
= strstr (reply_up
, "AMIGA") != 0;
651 if (strstr (reply_up
, " SPFTP/1.0.0000 SERVER ")) /* handles `LIST -la` in a weird way */
652 SUP
->strict
= RFC_STRICT
;
657 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n", SUP
->remote_is_amiga
);
658 fflush (MEDATA
->logfile
);
661 vfs_print_message ("%s", _("ftpfs: sending login name"));
663 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
))
666 vfs_print_message ("%s", _("ftpfs: sending user password"));
667 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
668 if (code
== CONTINUE
)
672 p
= g_strdup_printf (_("FTP: Account required for user %s"),
673 super
->path_element
->user
);
674 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "",
675 INPUT_COMPLETE_USERNAMES
);
679 vfs_print_message ("%s", _("ftpfs: sending user account"));
680 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
683 if (code
!= COMPLETE
)
688 vfs_print_message ("%s", _("ftpfs: logged in"));
689 wipe_password (pass
);
694 SUP
->failed_on_login
= 1;
695 wipe_password (super
->path_element
->password
);
696 super
->path_element
->password
= NULL
;
702 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
703 super
->path_element
->user
);
706 wipe_password (pass
);
711 /* --------------------------------------------------------------------------------------------- */
714 ftpfs_load_no_proxy_list (void)
716 /* FixMe: shouldn't be hardcoded!!! */
719 mc_file
= g_build_filename (mc_global
.sysconfig_dir
, "mc.no_proxy", (char *) NULL
);
720 if (exist_file (mc_file
))
724 npf
= fopen (mc_file
, "r");
727 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
729 while (fgets (s
, sizeof (s
), npf
) != NULL
)
733 p
= strchr (s
, '\n');
734 if (p
== NULL
) /* skip bogus entries */
738 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
746 no_proxy
= g_slist_prepend (no_proxy
, g_strdup (s
));
755 /* --------------------------------------------------------------------------------------------- */
756 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
759 ftpfs_check_proxy (const char *host
)
763 if (ftpfs_proxy_host
== NULL
|| *ftpfs_proxy_host
== '\0' || host
== NULL
|| *host
== '\0')
764 return 0; /* sanity check */
769 if (!ftpfs_always_use_proxy
)
772 if (strchr (host
, '.') == NULL
)
775 ftpfs_load_no_proxy_list ();
776 for (npe
= no_proxy
; npe
!= NULL
; npe
= g_slist_next (npe
))
778 const char *domain
= (const char *) npe
->data
;
780 if (domain
[0] == '.')
782 size_t ld
= strlen (domain
);
783 size_t lh
= strlen (host
);
785 while (ld
!= 0 && lh
!= 0 && host
[lh
- 1] == domain
[ld
- 1])
794 else if (g_ascii_strcasecmp (host
, domain
) == 0)
801 /* --------------------------------------------------------------------------------------------- */
804 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
806 vfs_path_element_t
*path_element
;
808 path_element
= vfs_url_split (proxy
, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
809 *host
= g_strdup (path_element
->host
);
810 *port
= path_element
->port
;
811 vfs_path_element_free (path_element
);
814 /* --------------------------------------------------------------------------------------------- */
817 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
819 struct addrinfo hints
, *res
, *curr_res
;
828 /* Use a proxy host? */
829 host
= g_strdup (super
->path_element
->host
);
831 if (host
== NULL
|| *host
== '\0')
833 vfs_print_message ("%s", _("ftpfs: Invalid host name."));
834 ftpfs_errno
= EINVAL
;
839 /* Hosts to connect to that start with a ! should use proxy */
840 tmp_port
= super
->path_element
->port
;
842 if (SUP
->proxy
!= NULL
)
843 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
845 g_snprintf (port
, sizeof (port
), "%hu", (unsigned short) tmp_port
);
853 tty_enable_interrupt_key (); /* clear the interrupt flag */
855 memset (&hints
, 0, sizeof (hints
));
856 hints
.ai_family
= AF_UNSPEC
;
857 hints
.ai_socktype
= SOCK_STREAM
;
860 /* By default, only look up addresses using address types for
861 * which a local interface is configured (i.e. no IPv6 if no IPv6
862 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
863 hints
.ai_flags
= AI_ADDRCONFIG
;
866 e
= getaddrinfo (host
, port
, &hints
, &res
);
869 if (e
== EAI_BADFLAGS
)
871 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
873 e
= getaddrinfo (host
, port
, &hints
, &res
);
881 tty_disable_interrupt_key ();
882 vfs_print_message (_("ftpfs: %s"), gai_strerror (e
));
884 ftpfs_errno
= EINVAL
;
888 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
)
890 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
894 if (curr_res
->ai_next
!= NULL
)
897 tty_disable_interrupt_key ();
898 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno
));
905 vfs_print_message (_("ftpfs: making connection to %s"), host
);
908 if (connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0)
914 if (errno
== EINTR
&& tty_got_interrupt ())
915 vfs_print_message ("%s", _("ftpfs: connection interrupted by user"));
916 else if (res
->ai_next
== NULL
)
917 vfs_print_message (_("ftpfs: connection to server failed: %s"),
918 unix_error_string (errno
));
923 tty_disable_interrupt_key ();
928 tty_disable_interrupt_key ();
932 /* --------------------------------------------------------------------------------------------- */
935 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
937 int retry_seconds
= 0;
940 /* We do not want to use the passive if we are using proxies */
942 SUP
->use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
946 SUP
->failed_on_login
= 0;
948 SUP
->sock
= ftpfs_open_socket (me
, super
);
952 if (ftpfs_login_server (me
, super
, NULL
) != 0)
954 /* Logged in, no need to retry the connection */
959 if (!SUP
->failed_on_login
)
962 /* Close only the socket descriptor */
965 if (ftpfs_retry_seconds
!= 0)
967 retry_seconds
= ftpfs_retry_seconds
;
968 tty_enable_interrupt_key ();
969 for (count_down
= retry_seconds
; count_down
; count_down
--)
971 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
974 if (tty_got_interrupt ())
976 /* ftpfs_errno = E; */
977 tty_disable_interrupt_key ();
981 tty_disable_interrupt_key ();
985 while (retry_seconds
!= 0);
987 SUP
->current_dir
= ftpfs_get_current_directory (me
, super
);
988 if (SUP
->current_dir
== NULL
)
989 SUP
->current_dir
= g_strdup (PATH_SEP_STR
);
994 /* --------------------------------------------------------------------------------------------- */
997 ftpfs_open_archive (struct vfs_s_super
*super
,
998 const vfs_path_t
* vpath
, const vfs_path_element_t
* vpath_element
)
1002 super
->data
= g_new0 (ftp_super_data_t
, 1);
1004 super
->path_element
= ftpfs_correct_url_parameters (vpath_element
);
1006 if (ftpfs_check_proxy (super
->path_element
->host
))
1007 SUP
->proxy
= ftpfs_proxy_host
;
1008 SUP
->use_passive_connection
= ftpfs_use_passive_connections
;
1009 SUP
->strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
1010 SUP
->isbinary
= TYPE_UNKNOWN
;
1011 SUP
->remote_is_amiga
= 0;
1012 SUP
->ctl_connection_busy
= 0;
1013 super
->name
= g_strdup (PATH_SEP_STR
);
1015 vfs_s_new_inode (vpath_element
->class, super
, ftpfs_default_stat (vpath_element
->class));
1017 return ftpfs_open_archive_int (vpath_element
->class, super
);
1020 /* --------------------------------------------------------------------------------------------- */
1023 ftpfs_archive_same (const vfs_path_element_t
* vpath_element
, struct vfs_s_super
*super
,
1024 const vfs_path_t
* vpath
, void *cookie
)
1026 vfs_path_element_t
*path_element
;
1032 path_element
= ftpfs_correct_url_parameters (vpath_element
);
1034 result
= ((strcmp (path_element
->host
, super
->path_element
->host
) == 0)
1035 && (strcmp (path_element
->user
, super
->path_element
->user
) == 0)
1036 && (path_element
->port
== super
->path_element
->port
)) ? 1 : 0;
1038 vfs_path_element_free (path_element
);
1042 /* --------------------------------------------------------------------------------------------- */
1043 /* The returned directory should always contain a trailing slash */
1046 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
1048 char buf
[MC_MAXPATHLEN
+ 1];
1050 if (ftpfs_command (me
, super
, NONE
, "%s", "PWD") == COMPLETE
&&
1051 ftpfs_get_reply (me
, SUP
->sock
, buf
, sizeof (buf
)) == COMPLETE
)
1056 for (bufq
= buf
; *bufq
!= '\0'; bufq
++)
1067 if (!IS_PATH_SEP (bufq
[-1]))
1073 if (IS_PATH_SEP (*bufp
))
1074 return g_strdup (bufp
);
1076 /* If the remote server is an Amiga a leading slash
1077 might be missing. MC needs it because it is used
1078 as separator between hostname and path internally. */
1079 return g_strconcat (PATH_SEP_STR
, bufp
, (char *) NULL
);
1091 /* --------------------------------------------------------------------------------------------- */
1092 /* Setup Passive PASV FTP connection */
1095 ftpfs_setup_passive_pasv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1096 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1100 int xa
, xb
, xc
, xd
, xe
, xf
;
1102 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "%s", "PASV") != COMPLETE
)
1105 /* Parse remote parameters */
1106 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
1110 if (!isdigit ((unsigned char) *c
))
1112 /* cppcheck-suppress invalidscanf */
1113 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
1116 n
[0] = (unsigned char) xa
;
1117 n
[1] = (unsigned char) xb
;
1118 n
[2] = (unsigned char) xc
;
1119 n
[3] = (unsigned char) xd
;
1120 n
[4] = (unsigned char) xe
;
1121 n
[5] = (unsigned char) xf
;
1123 memcpy (&(((struct sockaddr_in
*) sa
)->sin_addr
.s_addr
), (void *) n
, 4);
1124 memcpy (&(((struct sockaddr_in
*) sa
)->sin_port
), (void *) &n
[4], 2);
1126 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
1132 /* --------------------------------------------------------------------------------------------- */
1133 /* Setup Passive EPSV FTP connection */
1136 ftpfs_setup_passive_epsv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1137 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1142 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "%s", "EPSV") != COMPLETE
)
1146 c
= strchr (reply_str
, '|');
1155 if (port
< 0 || port
> 65535)
1157 port
= htons (port
);
1159 switch (sa
->ss_family
)
1162 ((struct sockaddr_in
*) sa
)->sin_port
= port
;
1165 ((struct sockaddr_in6
*) sa
)->sin6_port
= port
;
1171 return (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0) ? 0 : 1;
1174 /* --------------------------------------------------------------------------------------------- */
1175 /* Setup Passive ftp connection, we use it for source routed connections */
1178 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
1179 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1181 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1182 if (sa
->ss_family
== AF_INET
)
1184 if (!ftpfs_setup_passive_pasv (me
, super
, my_socket
, sa
, salen
))
1185 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1186 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1189 /* It's IPV6, so EPSV is our only hope */
1192 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1199 /* --------------------------------------------------------------------------------------------- */
1200 /* Setup Active PORT or EPRT FTP connection */
1203 ftpfs_setup_active (struct vfs_class
*me
, struct vfs_s_super
*super
,
1204 struct sockaddr_storage data_addr
, socklen_t data_addrlen
)
1206 unsigned short int port
;
1210 switch (data_addr
.ss_family
)
1214 port
= ((struct sockaddr_in
*) &data_addr
)->sin_port
;
1218 port
= ((struct sockaddr_in6
*) &data_addr
)->sin6_port
;
1220 /* Not implemented */
1225 addr
= g_try_malloc (NI_MAXHOST
);
1227 ERRNOR (ENOMEM
, -1);
1230 ((struct sockaddr
*) &data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0,
1231 NI_NUMERICHOST
) != 0)
1237 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1240 unsigned char *a
= (unsigned char *) &((struct sockaddr_in
*) &data_addr
)->sin_addr
;
1241 unsigned char *p
= (unsigned char *) &port
;
1243 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1244 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1245 p
[0], p
[1]) == COMPLETE
)
1253 * Converts network MSB first order to host byte order (LSB
1254 * first on i386). If we do it earlier, we will run into an
1255 * endianness issue, because the server actually expects to see
1256 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1258 port
= ntohs (port
);
1260 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1261 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
)
1271 /* --------------------------------------------------------------------------------------------- */
1272 /* Initialize a socket for FTP DATA connection */
1275 ftpfs_init_data_socket (struct vfs_class
*me
, struct vfs_s_super
*super
,
1276 struct sockaddr_storage
*data_addr
, socklen_t
* data_addrlen
)
1280 memset (data_addr
, 0, sizeof (*data_addr
));
1281 *data_addrlen
= sizeof (*data_addr
);
1283 if (SUP
->use_passive_connection
)
1284 result
= getpeername (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1286 result
= getsockname (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1291 switch (data_addr
->ss_family
)
1294 ((struct sockaddr_in
*) data_addr
)->sin_port
= 0;
1297 ((struct sockaddr_in6
*) data_addr
)->sin6_port
= 0;
1300 vfs_print_message ("%s", _("ftpfs: invalid address family"));
1301 ERRNOR (EINVAL
, -1);
1304 result
= socket (data_addr
->ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1308 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1315 /* --------------------------------------------------------------------------------------------- */
1316 /* Initialize FTP DATA connection */
1319 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
1321 struct sockaddr_storage data_addr
;
1322 socklen_t data_addrlen
;
1325 * Don't factor socket initialization out of these conditionals,
1326 * because ftpfs_init_data_socket initializes it in different way
1327 * depending on use_passive_connection flag.
1330 /* Try to establish a passive connection first (if requested) */
1331 if (SUP
->use_passive_connection
)
1335 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1339 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1342 vfs_print_message ("%s", _("ftpfs: could not setup passive mode"));
1343 SUP
->use_passive_connection
= FALSE
;
1348 /* If passive setup is diabled or failed, fallback to active connections */
1349 if (!SUP
->use_passive_connection
)
1353 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1357 if ((bind (data_sock
, (struct sockaddr
*) &data_addr
, data_addrlen
) == 0) &&
1358 (getsockname (data_sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == 0) &&
1359 (listen (data_sock
, 1) == 0) &&
1360 (ftpfs_setup_active (me
, super
, data_addr
, data_addrlen
) != 0))
1366 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1367 SUP
->use_passive_connection
= SUP
->proxy
!= NULL
? ftpfs_use_passive_connections_over_proxy
:
1368 ftpfs_use_passive_connections
;
1374 /* --------------------------------------------------------------------------------------------- */
1377 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1378 const char *remote
, int isbinary
, int reget
)
1380 struct sockaddr_storage from
;
1382 socklen_t fromlen
= sizeof (from
);
1384 /* FTP doesn't allow to open more than one file at a time */
1385 if (SUP
->ctl_connection_busy
)
1388 s
= ftpfs_initconn (me
, super
);
1392 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1400 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1410 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1411 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1412 /* WarFtpD can't STORE //filename */
1413 IS_PATH_SEP (*remote_path
) ? remote_path
+ 1 : remote_path
);
1414 g_free (remote_path
);
1417 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1425 if (SUP
->use_passive_connection
)
1429 tty_enable_interrupt_key ();
1430 data
= accept (s
, (struct sockaddr
*) &from
, &fromlen
);
1432 ftpfs_errno
= errno
;
1433 tty_disable_interrupt_key ();
1438 SUP
->ctl_connection_busy
= 1;
1442 /* --------------------------------------------------------------------------------------------- */
1445 ftpfs_linear_abort (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1447 struct vfs_s_super
*super
= FH_SUPER
;
1448 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1450 int dsock
= FH_SOCK
;
1452 SUP
->ctl_connection_busy
= 0;
1454 vfs_print_message ("%s", _("ftpfs: aborting transfer."));
1455 if (send (SUP
->sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
))
1457 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno
));
1463 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
)
1465 vfs_print_message ("%s", _("ftpfs: abort failed"));
1473 FD_SET (dsock
, &mask
);
1474 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0)
1476 struct timeval start_tim
, tim
;
1479 gettimeofday (&start_tim
, NULL
);
1480 /* flush the remaining data */
1481 while (read (dsock
, buf
, sizeof (buf
)) > 0)
1483 gettimeofday (&tim
, NULL
);
1484 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
)
1486 /* server keeps sending, drop the connection and ftpfs_reconnect */
1488 ftpfs_reconnect (me
, super
);
1495 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1496 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1499 /* --------------------------------------------------------------------------------------------- */
1503 resolve_symlink_without_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1504 struct vfs_s_inode
*dir
)
1506 struct linklist
*flist
;
1507 struct direntry
*fe
, *fel
;
1508 char tmp
[MC_MAXPATHLEN
];
1511 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1512 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
)
1514 /* flist->data->l_stat is alread initialized with 0 */
1516 if (S_ISLNK (fel
->s
.st_mode
) && fel
->linkname
)
1518 if (IS_PATH_SEP (fel
->linkname
[0]))
1520 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1522 strcpy (tmp
, fel
->linkname
);
1526 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1528 strcpy (tmp
, dir
->remote_path
);
1530 strcat (tmp
, PATH_SEP_STR
);
1531 strcat (tmp
+ 1, fel
->linkname
);
1533 for (depth
= 0; depth
< 100; depth
++)
1534 { /* depth protects against recursive symbolic links */
1535 canonicalize_pathname (tmp
);
1536 fe
= _get_file_entry_t (bucket
, tmp
, 0, 0);
1539 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0)
1541 /* Symlink points to link which isn't resolved, yet. */
1542 if (IS_PATH_SEP (fe
->linkname
[0]))
1544 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1546 strcpy (tmp
, fe
->linkname
);
1550 /* at this point tmp looks always like this
1551 /directory/filename, i.e. no need to check
1552 strrchr's return value */
1553 *(strrchr (tmp
, PATH_SEP
) + 1) = '\0'; /* dirname */
1554 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1556 strcat (tmp
, fe
->linkname
);
1562 fel
->l_stat
= g_new (struct stat
, 1);
1563 if (S_ISLNK (fe
->s
.st_mode
))
1564 *fel
->l_stat
= *fe
->l_stat
;
1566 *fel
->l_stat
= fe
->s
;
1567 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1574 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1577 /* --------------------------------------------------------------------------------------------- */
1580 resolve_symlink_with_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1581 struct vfs_s_inode
*dir
)
1583 char buffer
[2048] = "", *filename
;
1587 struct linklist
*flist
;
1588 struct direntry
*fe
;
1589 int switch_method
= 0;
1591 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1592 if (strchr (dir
->remote_path
, ' '))
1594 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
)
1596 vfs_print_message ("%s", _("ftpfs: CWD failed."));
1599 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1602 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", dir
->remote_path
, TYPE_ASCII
, 0);
1606 vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink"));
1610 fp
= fdopen (sock
, "r");
1614 vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink"));
1617 tty_enable_interrupt_key ();
1618 flist
= dir
->file_list
->next
;
1623 if (flist
== dir
->file_list
)
1626 flist
= flist
->next
;
1628 while (!S_ISLNK (fe
->s
.st_mode
));
1631 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1633 if (MEDATA
->logfile
)
1635 fputs (buffer
, MEDATA
->logfile
);
1636 fflush (MEDATA
->logfile
);
1638 vfs_die ("This code should be commented out\n");
1639 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
))
1641 int r
= strcmp (fe
->name
, filename
);
1645 if (S_ISLNK (s
.st_mode
))
1647 /* This server doesn't understand LIST -lLa */
1651 fe
->l_stat
= g_new (struct stat
, 1);
1652 if (fe
->l_stat
== NULL
)
1655 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1664 while (fgets (buffer
, sizeof (buffer
), fp
) != NULL
);
1665 tty_disable_interrupt_key ();
1667 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1670 /* --------------------------------------------------------------------------------------------- */
1673 resolve_symlink (struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1675 vfs_print_message ("%s", _("Resolving symlink..."));
1677 if (SUP
->strict_rfc959_list_cmd
)
1678 resolve_symlink_without_ls_options (me
, super
, dir
);
1680 resolve_symlink_with_ls_options (me
, super
, dir
);
1684 /* --------------------------------------------------------------------------------------------- */
1687 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1689 struct vfs_s_entry
*ent
;
1690 struct vfs_s_super
*super
= dir
->super
;
1691 int sock
, num_entries
= 0;
1694 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
->strict
== RFC_STRICT
)
1695 || (strchr (remote_path
, ' ') != NULL
);
1698 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1701 RFC_STRICT
? _("(strict rfc959)") : "", cd_first
? _("(chdir first)") : "");
1705 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
)
1707 ftpfs_errno
= ENOENT
;
1708 vfs_print_message ("%s", _("ftpfs: CWD failed."));
1713 gettimeofday (&dir
->timestamp
, NULL
);
1714 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1716 if (SUP
->strict
== RFC_STRICT
)
1717 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1719 /* Dirty hack to avoid autoprepending / to . */
1720 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1721 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1726 /* Trailing "/." is necessary if remote_path is a symlink */
1727 path
= mc_build_filename (remote_path
, ".", (char *) NULL
);
1728 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1735 /* Clear the interrupt flag */
1736 tty_enable_interrupt_key ();
1738 vfs_parse_ls_lga_init ();
1742 size_t count_spaces
= 0;
1744 char lc_buffer
[BUF_8K
] = "\0";
1746 res
= vfs_s_get_line_interruptible (me
, lc_buffer
, sizeof (lc_buffer
), sock
);
1752 me
->verrno
= ECONNRESET
;
1754 SUP
->ctl_connection_busy
= 0;
1755 tty_disable_interrupt_key ();
1756 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1757 vfs_print_message (_("%s: failure"), me
->name
);
1761 if (MEDATA
->logfile
)
1763 fputs (lc_buffer
, MEDATA
->logfile
);
1764 fputs ("\n", MEDATA
->logfile
);
1765 fflush (MEDATA
->logfile
);
1768 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1769 i
= ent
->ino
->st
.st_nlink
;
1770 if (!vfs_parse_ls_lga
1771 (lc_buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
, &count_spaces
))
1773 vfs_s_free_entry (me
, ent
);
1776 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1778 vfs_s_store_filename_leading_spaces (ent
, count_spaces
);
1779 vfs_s_insert_entry (me
, dir
, ent
);
1783 SUP
->ctl_connection_busy
= 0;
1784 me
->verrno
= E_REMOTE
;
1785 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1788 if (num_entries
== 0 && !cd_first
)
1790 /* The LIST command may produce an empty output. In such scenario
1791 it is not clear whether this is caused by 'remote_path' being
1792 a non-existent path or for some other reason (listing emtpy
1793 directory without the -a option, non-readable directory, etc.).
1795 Since 'dir_load' is a crucial method, when it comes to determine
1796 whether a given path is a _directory_, the code must try its best
1797 to determine the type of 'remote_path'. The only reliable way to
1798 achieve this is trough issuing a CWD command. */
1804 vfs_s_normalize_filename_leading_spaces (dir
, vfs_parse_ls_lga_get_final_spaces ());
1806 if (SUP
->strict
== RFC_AUTODETECT
)
1807 SUP
->strict
= RFC_DARING
;
1809 vfs_print_message (_("%s: done."), me
->name
);
1813 if (SUP
->strict
== RFC_AUTODETECT
)
1815 /* It's our first attempt to get a directory listing from this
1816 server (UNIX style LIST command) */
1817 SUP
->strict
= RFC_STRICT
;
1818 /* I hate goto, but recursive call needs another 8K on stack */
1819 /* return ftpfs_dir_load (me, dir, remote_path); */
1823 vfs_print_message ("%s", _("ftpfs: failed; nowhere to fallback to"));
1824 ERRNOR (EACCES
, -1);
1827 /* --------------------------------------------------------------------------------------------- */
1830 ftpfs_file_store (struct vfs_class
*me
, vfs_file_handler_t
* fh
, char *name
, char *localname
)
1834 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1839 char lc_buffer
[BUF_8K
];
1842 struct vfs_s_super
*super
= FH_SUPER
;
1843 ftp_fh_data_t
*ftp
= (ftp_fh_data_t
*) fh
->data
;
1845 h
= open (localname
, O_RDONLY
);
1849 if (fstat (h
, &s
) == -1 ||
1851 ftpfs_open_data_connection (me
, super
, ftp
->append
? "APPE" : "STOR", name
, TYPE_BINARY
,
1857 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1860 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1862 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1866 tty_enable_interrupt_key ();
1869 ssize_t n_read
, n_written
;
1871 while ((n_read
= read (h
, lc_buffer
, sizeof (lc_buffer
))) == -1)
1875 ftpfs_errno
= errno
;
1878 if (tty_got_interrupt ())
1880 ftpfs_errno
= EINTR
;
1888 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
)
1890 if (n_written
== -1)
1892 if (errno
== EINTR
&& !tty_got_interrupt ())
1895 ftpfs_errno
= errno
;
1899 n_read
-= n_written
;
1901 vfs_print_message ("%s: %" PRIuMAX
"/%" PRIuMAX
,
1902 _("ftpfs: storing file"), (uintmax_t) n_stored
, (uintmax_t) s
.st_size
);
1904 tty_disable_interrupt_key ();
1906 SUP
->ctl_connection_busy
= 0;
1908 if (ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
)
1912 tty_disable_interrupt_key ();
1914 SUP
->ctl_connection_busy
= 0;
1916 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1920 /* --------------------------------------------------------------------------------------------- */
1923 ftpfs_linear_start (struct vfs_class
*me
, vfs_file_handler_t
* fh
, off_t offset
)
1927 if (fh
->data
== NULL
)
1928 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
1930 name
= vfs_s_fullpath (me
, fh
->ino
);
1933 FH_SOCK
= ftpfs_open_data_connection (me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1937 fh
->linear
= LS_LINEAR_OPEN
;
1938 ((ftp_fh_data_t
*) fh
->data
)->append
= 0;
1942 /* --------------------------------------------------------------------------------------------- */
1945 ftpfs_linear_read (struct vfs_class
*me
, vfs_file_handler_t
* fh
, void *buf
, size_t len
)
1948 struct vfs_s_super
*super
= FH_SUPER
;
1950 while ((n
= read (FH_SOCK
, buf
, len
)) < 0)
1952 if ((errno
== EINTR
) && !tty_got_interrupt ())
1958 ftpfs_linear_abort (me
, fh
);
1962 SUP
->ctl_connection_busy
= 0;
1965 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1966 ERRNOR (E_REMOTE
, -1);
1972 /* --------------------------------------------------------------------------------------------- */
1975 ftpfs_linear_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1978 ftpfs_linear_abort (me
, fh
);
1981 /* --------------------------------------------------------------------------------------------- */
1984 ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1990 case VFS_CTL_IS_NOTREADY
:
1994 if (FH
->linear
== LS_NOT_LINEAR
)
1995 vfs_die ("You may not do this");
1996 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1999 v
= vfs_s_select_on_two (((ftp_fh_data_t
*) (FH
->data
))->sock
, 0);
2000 return (((v
< 0) && (errno
== EINTR
)) || v
== 0) ? 1 : 0;
2007 /* --------------------------------------------------------------------------------------------- */
2010 ftpfs_send_command (const vfs_path_t
* vpath
, const char *cmd
, int flags
)
2014 struct vfs_s_super
*super
;
2016 const vfs_path_element_t
*path_element
;
2017 int flush_directory_cache
= (flags
& OPT_FLUSH
);
2019 path_element
= vfs_path_get_by_index (vpath
, -1);
2021 rpath
= vfs_s_get_path (vpath
, &super
, 0);
2025 p
= ftpfs_translate_path (path_element
->class, super
, rpath
);
2026 r
= ftpfs_command (path_element
->class, super
, WAIT_REPLY
, cmd
, p
);
2028 vfs_stamp_create (vfs_ftpfs_ops
, super
);
2029 if (flags
& OPT_IGNORE_ERROR
)
2033 path_element
->class->verrno
= EPERM
;
2036 if (flush_directory_cache
)
2037 vfs_s_invalidate (path_element
->class, super
);
2041 /* --------------------------------------------------------------------------------------------- */
2044 ftpfs_stat (const vfs_path_t
* vpath
, struct stat
*buf
)
2048 ret
= vfs_s_stat (vpath
, buf
);
2049 ftpfs_set_blksize (buf
);
2053 /* --------------------------------------------------------------------------------------------- */
2056 ftpfs_lstat (const vfs_path_t
* vpath
, struct stat
*buf
)
2060 ret
= vfs_s_lstat (vpath
, buf
);
2061 ftpfs_set_blksize (buf
);
2065 /* --------------------------------------------------------------------------------------------- */
2068 ftpfs_fstat (void *vfs_info
, struct stat
*buf
)
2072 ret
= vfs_s_fstat (vfs_info
, buf
);
2073 ftpfs_set_blksize (buf
);
2077 /* --------------------------------------------------------------------------------------------- */
2080 ftpfs_chmod (const vfs_path_t
* vpath
, mode_t mode
)
2082 char buf
[BUF_SMALL
];
2085 g_snprintf (buf
, sizeof (buf
), "SITE CHMOD %4.4o /%%s", (unsigned int) (mode
& 07777));
2087 ret
= ftpfs_send_command (vpath
, buf
, OPT_FLUSH
);
2089 return ftpfs_ignore_chattr_errors
? 0 : ret
;
2092 /* --------------------------------------------------------------------------------------------- */
2095 ftpfs_chown (const vfs_path_t
* vpath
, uid_t owner
, gid_t group
)
2102 ftpfs_errno
= EPERM
;
2105 /* Everyone knows it is not possible to chown remotely, so why bother them.
2106 If someone's root, then copy/move will always try to chown it... */
2114 /* --------------------------------------------------------------------------------------------- */
2117 ftpfs_unlink (const vfs_path_t
* vpath
)
2119 return ftpfs_send_command (vpath
, "DELE /%s", OPT_FLUSH
);
2122 /* --------------------------------------------------------------------------------------------- */
2124 /* Return 1 if path is the same directory as the one we are in now */
2126 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
2130 if (SUP
->current_dir
== NULL
)
2132 return (strcmp (path
, SUP
->current_dir
) == 0);
2135 /* --------------------------------------------------------------------------------------------- */
2138 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
2143 if (!SUP
->cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
2146 p
= ftpfs_translate_path (me
, super
, remote_path
);
2147 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
2154 g_free (SUP
->current_dir
);
2155 SUP
->current_dir
= g_strdup (remote_path
);
2156 SUP
->cwd_deferred
= 0;
2161 /* --------------------------------------------------------------------------------------------- */
2164 ftpfs_rename (const vfs_path_t
* vpath1
, const vfs_path_t
* vpath2
)
2166 ftpfs_send_command (vpath1
, "RNFR /%s", OPT_FLUSH
);
2167 return ftpfs_send_command (vpath2
, "RNTO /%s", OPT_FLUSH
);
2170 /* --------------------------------------------------------------------------------------------- */
2173 ftpfs_mkdir (const vfs_path_t
* vpath
, mode_t mode
)
2175 (void) mode
; /* FIXME: should be used */
2177 return ftpfs_send_command (vpath
, "MKD /%s", OPT_FLUSH
);
2180 /* --------------------------------------------------------------------------------------------- */
2183 ftpfs_rmdir (const vfs_path_t
* vpath
)
2185 return ftpfs_send_command (vpath
, "RMD /%s", OPT_FLUSH
);
2188 /* --------------------------------------------------------------------------------------------- */
2191 ftpfs_fh_free_data (vfs_file_handler_t
* fh
)
2194 MC_PTR_FREE (fh
->data
);
2197 /* --------------------------------------------------------------------------------------------- */
2200 ftpfs_fh_open (struct vfs_class
*me
, vfs_file_handler_t
* fh
, int flags
, mode_t mode
)
2206 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
2207 ftp
= (ftp_fh_data_t
*) fh
->data
;
2208 /* File will be written only, so no need to retrieve it from ftp server */
2209 if (((flags
& O_WRONLY
) == O_WRONLY
) && ((flags
& (O_RDONLY
| O_RDWR
)) == 0))
2211 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2218 /* ftpfs_linear_start() called, so data will be written
2219 * to local temporary file and stored to ftp server
2220 * by vfs_s_close later
2222 if (((ftp_super_data_t
*) (FH_SUPER
->data
))->ctl_connection_busy
)
2224 if (!fh
->ino
->localname
)
2229 handle
= vfs_mkstemps (&vpath
, me
->name
, fh
->ino
->ent
->name
);
2232 vfs_path_free (vpath
);
2236 fh
->ino
->localname
= g_strdup (vfs_path_as_str (vpath
));
2237 vfs_path_free (vpath
);
2238 ftp
->append
= flags
& O_APPEND
;
2242 name
= vfs_s_fullpath (me
, fh
->ino
);
2246 ftpfs_open_data_connection (me
, fh
->ino
->super
,
2247 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
2252 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2256 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
2258 if (fh
->ino
->localname
)
2260 unlink (fh
->ino
->localname
);
2261 MC_PTR_FREE (fh
->ino
->localname
);
2266 if (!fh
->ino
->localname
&& vfs_s_retrieve_file (me
, fh
->ino
) == -1)
2268 if (!fh
->ino
->localname
)
2269 vfs_die ("retrieve_file failed to fill in localname");
2273 ftpfs_fh_free_data (fh
);
2277 /* --------------------------------------------------------------------------------------------- */
2280 ftpfs_fh_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
2282 if (fh
->handle
!= -1 && !fh
->ino
->localname
)
2284 ftp_super_data_t
*ftp
= (ftp_super_data_t
*) fh
->ino
->super
->data
;
2288 ftp
->ctl_connection_busy
= 0;
2289 /* File is stored to destination already, so
2290 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2293 if (ftpfs_get_reply (me
, ftp
->sock
, NULL
, 0) != COMPLETE
)
2295 vfs_s_invalidate (me
, FH_SUPER
);
2301 /* --------------------------------------------------------------------------------------------- */
2304 ftpfs_done (struct vfs_class
*me
)
2308 g_slist_free_full (no_proxy
, g_free
);
2310 g_free (ftpfs_anonymous_passwd
);
2311 g_free (ftpfs_proxy_host
);
2314 /* --------------------------------------------------------------------------------------------- */
2317 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
2321 for (iter
= MEDATA
->supers
; iter
!= NULL
; iter
= g_list_next (iter
))
2323 const struct vfs_s_super
*super
= (const struct vfs_s_super
*) iter
->data
;
2326 name
= vfs_path_element_build_pretty_path_str (super
->path_element
);
2333 /* --------------------------------------------------------------------------------------------- */
2336 ftpfs_netrc_next (void)
2340 static const char *const keywords
[] = { "default", "machine",
2341 "login", "password", "passwd", "account", "macdef", NULL
2346 netrcp
= skip_separators (netrcp
);
2347 if (*netrcp
!= '\n')
2356 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++)
2358 if (*netrcp
== '\\')
2365 for (; *netrcp
!= '\0' && !whiteness (*netrcp
) && *netrcp
!= ','; netrcp
++)
2367 if (*netrcp
== '\\')
2376 for (i
= NETRC_DEFAULT
; keywords
[i
- 1] != NULL
; i
++)
2377 if (strcmp (keywords
[i
- 1], buffer
) == 0)
2380 return NETRC_UNKNOWN
;
2383 /* --------------------------------------------------------------------------------------------- */
2386 ftpfs_netrc_bad_mode (const char *netrcname
)
2390 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077))
2392 static int be_angry
= 1;
2396 message (D_ERROR
, MSG_ERROR
,
2397 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2405 /* --------------------------------------------------------------------------------------------- */
2406 /* Scan .netrc until we find matching "machine" or "default"
2407 * domain is used for additional matching
2408 * No search is done after "default" in compliance with "man netrc"
2409 * Return 0 if found, -1 otherwise */
2412 ftpfs_find_machine (const char *host
, const char *domain
)
2421 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
)
2423 if (keyword
== NETRC_DEFAULT
)
2426 if (keyword
== NETRC_MACDEF
)
2428 /* Scan for an empty line, which concludes "macdef" */
2431 while (*netrcp
&& *netrcp
!= '\n')
2433 if (*netrcp
!= '\n')
2437 while (*netrcp
&& *netrcp
!= '\n');
2441 if (keyword
!= NETRC_MACHINE
)
2444 /* Take machine name */
2445 if (ftpfs_netrc_next () == NETRC_NONE
)
2448 if (g_ascii_strcasecmp (host
, buffer
) != 0)
2450 /* Try adding our domain to short names in .netrc */
2451 const char *host_domain
= strchr (host
, '.');
2455 /* Compare domain part */
2456 if (g_ascii_strcasecmp (host_domain
, domain
) != 0)
2459 /* Compare local part */
2460 if (g_ascii_strncasecmp (host
, buffer
, host_domain
- host
) != 0)
2471 /* --------------------------------------------------------------------------------------------- */
2472 /* Extract login and password from .netrc for the host.
2474 * Returns 0 for success, -1 for error */
2477 ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
2480 char *tmp_pass
= NULL
;
2481 char hostname
[MAXHOSTNAMELEN
];
2483 static struct rupcache
2485 struct rupcache
*next
;
2489 } *rup_cache
= NULL
, *rupp
;
2491 /* Initialize *login and *pass */
2492 MC_PTR_FREE (*login
);
2493 MC_PTR_FREE (*pass
);
2495 /* Look up in the cache first */
2496 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
)
2498 if (strcmp (host
, rupp
->host
) == 0)
2500 *login
= g_strdup (rupp
->login
);
2501 *pass
= g_strdup (rupp
->pass
);
2506 /* Load current .netrc */
2507 netrcname
= g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL
);
2508 if (!g_file_get_contents (netrcname
, &netrc
, NULL
, NULL
))
2516 /* Find our own domain name */
2517 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2520 domain
= strchr (hostname
, '.');
2524 /* Scan for "default" and matching "machine" keywords */
2525 ftpfs_find_machine (host
, domain
);
2527 /* Scan for keywords following "default" and "machine" */
2533 keyword
= ftpfs_netrc_next ();
2538 if (ftpfs_netrc_next () == NETRC_NONE
)
2544 /* We have another name already - should not happen */
2551 /* We have login name now */
2552 *login
= g_strdup (buffer
);
2555 case NETRC_PASSWORD
:
2557 if (ftpfs_netrc_next () == NETRC_NONE
)
2563 /* Ignore unsafe passwords */
2564 if (*login
!= NULL
&&
2565 strcmp (*login
, "anonymous") != 0 && strcmp (*login
, "ftp") != 0
2566 && ftpfs_netrc_bad_mode (netrcname
))
2572 /* Remember password. pass may be NULL, so use tmp_pass */
2573 if (tmp_pass
== NULL
)
2574 tmp_pass
= g_strdup (buffer
);
2578 /* "account" is followed by a token which we ignore */
2579 if (ftpfs_netrc_next () == NETRC_NONE
)
2585 /* Ignore account, but warn user anyways */
2586 ftpfs_netrc_bad_mode (netrcname
);
2590 /* Unexpected keyword or end of file */
2602 rupp
= g_new (struct rupcache
, 1);
2603 rupp
->host
= g_strdup (host
);
2604 rupp
->login
= g_strdup (*login
);
2605 rupp
->pass
= g_strdup (tmp_pass
);
2607 rupp
->next
= rup_cache
;
2615 /* --------------------------------------------------------------------------------------------- */
2616 /*** public functions ****************************************************************************/
2617 /* --------------------------------------------------------------------------------------------- */
2619 /** This routine is called as the last step in load_setup */
2621 ftpfs_init_passwd (void)
2623 ftpfs_anonymous_passwd
= load_anon_passwd ();
2624 if (ftpfs_anonymous_passwd
)
2627 /* If there is no anonymous ftp password specified
2628 * then we'll just use anonymous@
2629 * We don't send any other thing because:
2630 * - We want to remain anonymous
2631 * - We want to stop SPAM
2632 * - We don't want to let ftp sites to discriminate by the user,
2635 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
2638 /* --------------------------------------------------------------------------------------------- */
2645 ftpfs_subclass
.flags
= VFS_S_REMOTE
| VFS_S_USETMP
;
2646 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2647 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2648 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2649 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2650 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2651 ftpfs_subclass
.fh_free_data
= ftpfs_fh_free_data
;
2652 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2653 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2654 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2655 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2656 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2658 vfs_s_init_class (&ftpfs_subclass
);
2659 vfs_ftpfs_ops
->name
= "ftpfs";
2660 vfs_ftpfs_ops
->flags
= VFSF_NOLINKS
;
2661 vfs_ftpfs_ops
->prefix
= "ftp";
2662 vfs_ftpfs_ops
->done
= &ftpfs_done
;
2663 vfs_ftpfs_ops
->fill_names
= ftpfs_fill_names
;
2664 vfs_ftpfs_ops
->stat
= ftpfs_stat
;
2665 vfs_ftpfs_ops
->lstat
= ftpfs_lstat
;
2666 vfs_ftpfs_ops
->fstat
= ftpfs_fstat
;
2667 vfs_ftpfs_ops
->chmod
= ftpfs_chmod
;
2668 vfs_ftpfs_ops
->chown
= ftpfs_chown
;
2669 vfs_ftpfs_ops
->unlink
= ftpfs_unlink
;
2670 vfs_ftpfs_ops
->rename
= ftpfs_rename
;
2671 vfs_ftpfs_ops
->mkdir
= ftpfs_mkdir
;
2672 vfs_ftpfs_ops
->rmdir
= ftpfs_rmdir
;
2673 vfs_ftpfs_ops
->ctl
= ftpfs_ctl
;
2674 vfs_register_class (vfs_ftpfs_ops
);
2677 /* --------------------------------------------------------------------------------------------- */