2 Virtual File System: FTP file system.
4 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2006, 2007, 2008, 2009, 2010, 2011
6 The Free Software Foundation, Inc.
11 Miguel de Icaza, 1995, 1996, 1997
15 Slava Zanko <slavazanko@gmail.com>, 2010
16 Andrew Borodin <aborodin@vmail.ru>, 2010
18 This file is part of the Midnight Commander.
20 The Midnight Commander is free software: you can redistribute it
21 and/or modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation, either version 3 of the License,
23 or (at your option) any later version.
25 The Midnight Commander is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <http://www.gnu.org/licenses/>.
36 * \brief Source: Virtual File System: FTP file system
38 * \author Jakub Jelinek
39 * \author Miguel de Icaza
40 * \author Norbert Warmuth
41 * \author Pavel Machek
42 * \date 1995, 1997, 1998
45 - make it more robust - all the connects etc. should handle EADDRINUSE and
46 ERETRY (have I spelled these names correctly?)
47 - make the user able to flush a connection - all the caches will get empty
48 etc., (tarfs as well), we should give there a user selectable timeout
49 and assign a key sequence.
50 - use hash table instead of linklist to cache ftpfs directory.
55 * NOTE: Usage of tildes is deprecated, consider:
60 * And now: what do I want to do? Do I want to go to /home/pavel or to
61 * /#ftp:hobit/home/pavel? I think first has better sense...
65 int f = !strcmp( remote_path, "/~" );
66 if (f || !strncmp( remote_path, "/~/", 3 )) {
68 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
76 /* \todo Fix: Namespace pollution: horrible */
79 #include <stdio.h> /* sscanf() */
80 #include <stdlib.h> /* atoi() */
81 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
82 #include <netdb.h> /* struct hostent */
83 #include <sys/socket.h> /* AF_INET */
84 #include <netinet/in.h> /* struct in_addr */
85 #ifdef HAVE_ARPA_INET_H
86 #include <arpa/inet.h>
89 #include <arpa/telnet.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/mcconfig.h"
101 #include "lib/tty/tty.h" /* enable/disable interrupt key */
102 #include "lib/widget.h" /* message() */
104 #include "src/history.h"
105 #include "src/setup.h" /* for load_anon_passwd */
107 #include "lib/vfs/vfs.h"
108 #include "lib/vfs/utilvfs.h"
109 #include "lib/vfs/netutil.h"
110 #include "lib/vfs/xdirentry.h"
111 #include "lib/vfs/gc.h" /* vfs_stamp_create */
115 /*** global variables ****************************************************************************/
117 /* Delay to retry a connection */
118 int ftpfs_retry_seconds
= 30;
120 /* Method to use to connect to ftp sites */
121 int ftpfs_use_passive_connections
= 1;
122 int ftpfs_use_passive_connections_over_proxy
= 0;
124 /* Method used to get directory listings:
125 * 1: try 'LIST -la <path>', if it fails
126 * fall back to CWD <path>; LIST
127 * 0: always use CWD <path>; LIST
129 int ftpfs_use_unix_list_options
= 1;
131 /* First "CWD <path>", then "LIST -la ." */
132 int ftpfs_first_cd_then_ls
= 1;
134 /* Use the ~/.netrc */
135 int ftpfs_use_netrc
= 1;
137 /* Anonymous setup */
138 char *ftpfs_anonymous_passwd
= NULL
;
139 int ftpfs_directory_timeout
= 900;
142 char *ftpfs_proxy_host
= NULL
;
144 /* wether we have to use proxy by default? */
145 int ftpfs_always_use_proxy
= 0;
147 int ftpfs_ignore_chattr_errors
= 1;
149 /*** file scope macro definitions ****************************************************************/
151 #ifndef MAXHOSTNAMELEN
152 #define MAXHOSTNAMELEN 64
155 #define UPLOAD_ZERO_LENGTH_FILE
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 int 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
;
228 /*** file scope variables ************************************************************************/
230 static int ftpfs_errno
;
233 #ifdef FIXME_LATER_ALIGATOR
234 static struct linklist
*connections_list
;
237 static char reply_str
[80];
239 static struct vfs_class vfs_ftpfs_ops
;
241 static GSList
*no_proxy
;
243 static char buffer
[BUF_MEDIUM
];
245 static const char *netrcp
;
247 /*** file scope functions ************************************************************************/
248 /* --------------------------------------------------------------------------------------------- */
250 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
251 Translate a Unix path, i.e. MC's internal path representation (e.g.
252 /somedir/somefile) to a path valid for the remote server. Every path
253 transfered to the remote server has to be mangled by this function
254 right prior to sending it.
255 Currently only Amiga ftp servers are handled in a special manner.
257 When the remote server is an amiga:
258 a) strip leading slash if necesarry
259 b) replace first occurance of ":/" with ":"
260 c) strip trailing "/."
263 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
264 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
,
265 const char *remote_path
);
266 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
,
267 const char *fmt
, ...) __attribute__ ((format (__printf__
, 4, 5)));
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_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
278 if (!SUP
->remote_is_amiga
)
279 return g_strdup (remote_path
);
286 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
287 fflush (MEDATA
->logfile
);
290 /* strip leading slash(es) */
291 while (*remote_path
== '/')
295 * Don't change "/" into "", e.g. "CWD " would be
298 if (*remote_path
== '\0')
299 return g_strdup (".");
301 ret
= g_strdup (remote_path
);
303 /* replace first occurance of ":/" with ":" */
304 p
= strchr (ret
, ':');
305 if ((p
!= NULL
) && (*(p
+ 1) == '/'))
306 memmove (p
+ 1, p
+ 2, strlen (p
+ 2) + 1);
308 /* strip trailing "/." */
309 p
= strrchr (ret
, '/');
310 if ((p
!= NULL
) && (*(p
+ 1) == '.') && (*(p
+ 2) == '\0'))
317 /* --------------------------------------------------------------------------------------------- */
318 /** Extract the hostname and username from the path */
320 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
321 * ftp://sunsite.unc.edu/pub/linux
322 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
323 * ftp://tsx-11.mit.edu:8192/
324 * ftp://joe@foo.edu:11321/private
325 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
329 static vfs_path_element_t
*
330 ftpfs_correct_url_parameters (const vfs_path_element_t
* velement
)
332 vfs_path_element_t
*path_element
= vfs_path_element_clone (velement
);
334 if (path_element
->port
== 0)
335 path_element
->port
= FTP_COMMAND_PORT
;
337 if (path_element
->user
== NULL
)
339 /* Look up user and password in netrc */
341 ftpfs_netrc_lookup (path_element
->host
, &path_element
->user
, &path_element
->password
);
343 if (path_element
->user
== NULL
)
344 path_element
->user
= g_strdup ("anonymous");
346 /* Look up password in netrc for known user */
347 if (ftpfs_use_netrc
&& path_element
->user
!= NULL
&& path_element
->password
!= NULL
)
349 char *new_user
= NULL
;
350 char *new_passwd
= NULL
;
352 ftpfs_netrc_lookup (path_element
->host
, &new_user
, &new_passwd
);
354 /* If user is different, remove password */
355 if (new_user
!= NULL
&& strcmp (path_element
->user
, new_user
) != 0)
357 g_free (path_element
->password
);
358 path_element
->password
= NULL
;
368 /* --------------------------------------------------------------------------------------------- */
369 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
372 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
379 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
386 switch (sscanf (answer
, "%d", &code
))
390 g_strlcpy (string_buf
, answer
, string_len
);
394 if (answer
[3] == '-')
398 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
405 if ((sscanf (answer
, "%d", &i
) > 0) && (code
== i
) && (answer
[3] == ' '))
410 g_strlcpy (string_buf
, answer
, string_len
);
416 /* --------------------------------------------------------------------------------------------- */
419 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
423 sock
= ftpfs_open_socket (me
, super
);
426 char *cwdir
= super
->path_element
->path
;
430 super
->path_element
->path
= NULL
;
433 if (ftpfs_login_server (me
, super
, super
->path_element
->password
) != 0)
437 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
439 return sock
== COMPLETE
? 1 : 0;
442 super
->path_element
->path
= cwdir
;
448 /* --------------------------------------------------------------------------------------------- */
451 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
,
457 static int retry
= 0;
458 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
461 cmdstr
= g_strdup_vprintf (fmt
, ap
);
464 cmdlen
= strlen (cmdstr
);
465 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
466 strcpy (cmdstr
+ cmdlen
, "\r\n");
471 if (strncmp (cmdstr
, "PASS ", 5) == 0)
473 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
478 ret
= fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
481 fflush (MEDATA
->logfile
);
485 tty_enable_interrupt_key ();
486 status
= write (SUP
->sock
, cmdstr
, cmdlen
);
493 { /* Remote server has closed connection */
497 status
= ftpfs_reconnect (me
, super
);
499 if (status
&& (write (SUP
->sock
, cmdstr
, cmdlen
) > 0))
508 tty_disable_interrupt_key ();
513 tty_disable_interrupt_key ();
517 status
= ftpfs_get_reply (me
, SUP
->sock
,
518 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
519 sizeof (reply_str
) - 1);
520 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
524 status
= ftpfs_reconnect (me
, super
);
526 if (status
&& (write (SUP
->sock
, cmdstr
, cmdlen
) > 0))
539 /* --------------------------------------------------------------------------------------------- */
542 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
546 vfs_print_message (_("ftpfs: Disconnecting from %s"), super
->path_element
->host
);
547 ftpfs_command (me
, super
, NONE
, "QUIT");
550 g_free (super
->data
);
554 /* --------------------------------------------------------------------------------------------- */
557 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
559 if (binary
!= SUP
->isbinary
)
561 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
563 SUP
->isbinary
= binary
;
568 /* --------------------------------------------------------------------------------------------- */
569 /* This routine logs the user in */
572 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
)
576 char *name
; /* login user name */
578 char reply_string
[BUF_MEDIUM
];
580 SUP
->isbinary
= TYPE_UNKNOWN
;
582 if (super
->path_element
->password
!= NULL
) /* explicit password */
583 op
= g_strdup (super
->path_element
->password
);
584 else if (netrcpass
!= NULL
) /* password from netrc */
585 op
= g_strdup (netrcpass
);
586 else if (strcmp (super
->path_element
->user
, "anonymous") == 0
587 || strcmp (super
->path_element
->user
, "ftp") == 0)
589 if (ftpfs_anonymous_passwd
== NULL
) /* default anonymous password */
590 ftpfs_init_passwd ();
591 op
= g_strdup (ftpfs_anonymous_passwd
);
598 p
= g_strdup_printf (_("FTP: Password required for %s"), super
->path_element
->user
);
599 op
= vfs_get_password (p
);
603 super
->path_element
->password
= g_strdup (op
);
606 if (!anon
|| MEDATA
->logfile
)
610 pass
= g_strconcat ("-", op
, (char *) NULL
);
614 /* Proxy server accepts: username@host-we-want-to-connect */
617 g_strconcat (super
->path_element
->user
, "@",
618 super
->path_element
->host
[0] ==
619 '!' ? super
->path_element
->host
+ 1 : super
->path_element
->host
,
622 name
= g_strdup (super
->path_element
->user
);
624 if (ftpfs_get_reply (me
, SUP
->sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
)
628 reply_up
= g_ascii_strup (reply_string
, -1);
629 SUP
->remote_is_amiga
= strstr (reply_up
, "AMIGA") != 0;
634 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n", SUP
->remote_is_amiga
);
635 fflush (MEDATA
->logfile
);
638 vfs_print_message (_("ftpfs: sending login name"));
640 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
))
643 vfs_print_message (_("ftpfs: sending user password"));
644 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
645 if (code
== CONTINUE
)
649 p
= g_strdup_printf (_("FTP: Account required for user %s"),
650 super
->path_element
->user
);
651 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
655 vfs_print_message (_("ftpfs: sending user account"));
656 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
659 if (code
!= COMPLETE
)
664 vfs_print_message (_("ftpfs: logged in"));
665 wipe_password (pass
);
670 SUP
->failed_on_login
= 1;
671 wipe_password (super
->path_element
->password
);
672 super
->path_element
->password
= NULL
;
678 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
679 super
->path_element
->user
);
682 wipe_password (pass
);
687 /* --------------------------------------------------------------------------------------------- */
690 ftpfs_load_no_proxy_list (void)
692 /* FixMe: shouldn't be hardcoded!!! */
693 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
697 static char *mc_file
= NULL
;
699 mc_file
= g_build_filename (mc_global
.sysconfig_dir
, "mc.no_proxy", (char *) NULL
);
700 if (exist_file (mc_file
))
702 npf
= fopen (mc_file
, "r");
705 while (fgets (s
, sizeof (s
), npf
) != NULL
)
707 p
= strchr (s
, '\n');
708 if (p
== NULL
) /* skip bogus entries */
710 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
718 no_proxy
= g_slist_prepend (no_proxy
, g_strdup (s
));
727 /* --------------------------------------------------------------------------------------------- */
728 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
731 ftpfs_check_proxy (const char *host
)
735 if (ftpfs_proxy_host
== NULL
|| *ftpfs_proxy_host
== '\0' || host
== NULL
|| *host
== '\0')
736 return 0; /* sanity check */
741 if (!ftpfs_always_use_proxy
)
744 if (strchr (host
, '.') == NULL
)
747 ftpfs_load_no_proxy_list ();
748 for (npe
= no_proxy
; npe
!= NULL
; npe
= g_slist_next (npe
))
750 const char *domain
= (const char *) npe
->data
;
752 if (domain
[0] == '.')
754 size_t ld
= strlen (domain
);
755 size_t lh
= strlen (host
);
757 while (ld
!= 0 && lh
!= 0 && host
[lh
- 1] == domain
[ld
- 1])
766 else if (g_ascii_strcasecmp (host
, domain
) == 0)
773 /* --------------------------------------------------------------------------------------------- */
776 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
778 vfs_path_element_t
*path_element
;
780 path_element
= vfs_url_split (proxy
, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
781 *host
= g_strdup (path_element
->host
);
782 *port
= path_element
->port
;
783 vfs_path_element_free (path_element
);
786 /* --------------------------------------------------------------------------------------------- */
789 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
791 struct addrinfo hints
, *res
, *curr_res
;
800 /* Use a proxy host? */
801 host
= g_strdup (super
->path_element
->host
);
803 if (host
== NULL
|| *host
== '\0')
805 vfs_print_message (_("ftpfs: Invalid host name."));
806 ftpfs_errno
= EINVAL
;
811 /* Hosts to connect to that start with a ! should use proxy */
812 tmp_port
= super
->path_element
->port
;
814 if (SUP
->proxy
!= NULL
)
815 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
817 g_snprintf (port
, sizeof (port
), "%hu", (unsigned short) tmp_port
);
825 tty_enable_interrupt_key (); /* clear the interrupt flag */
827 memset (&hints
, 0, sizeof (struct addrinfo
));
828 hints
.ai_family
= AF_UNSPEC
;
829 hints
.ai_socktype
= SOCK_STREAM
;
832 /* By default, only look up addresses using address types for
833 * which a local interface is configured (i.e. no IPv6 if no IPv6
834 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
835 hints
.ai_flags
= AI_ADDRCONFIG
;
838 e
= getaddrinfo (host
, port
, &hints
, &res
);
841 if (e
== EAI_BADFLAGS
)
843 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
845 e
= getaddrinfo (host
, port
, &hints
, &res
);
853 tty_disable_interrupt_key ();
854 vfs_print_message (_("ftpfs: %s"), gai_strerror (e
));
856 ftpfs_errno
= EINVAL
;
860 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
)
862 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
866 if (curr_res
->ai_next
!= NULL
)
869 tty_disable_interrupt_key ();
870 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno
));
877 vfs_print_message (_("ftpfs: making connection to %s"), host
);
881 if (connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0)
887 if (errno
== EINTR
&& tty_got_interrupt ())
888 vfs_print_message (_("ftpfs: connection interrupted by user"));
889 else if (res
->ai_next
== NULL
)
890 vfs_print_message (_("ftpfs: connection to server failed: %s"),
891 unix_error_string (errno
));
896 tty_disable_interrupt_key ();
901 tty_disable_interrupt_key ();
905 /* --------------------------------------------------------------------------------------------- */
908 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
910 int retry_seconds
= 0;
913 /* We do not want to use the passive if we are using proxies */
915 SUP
->use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
919 SUP
->failed_on_login
= 0;
921 SUP
->sock
= ftpfs_open_socket (me
, super
);
925 if (ftpfs_login_server (me
, super
, NULL
) != 0)
927 /* Logged in, no need to retry the connection */
932 if (!SUP
->failed_on_login
)
935 /* Close only the socket descriptor */
938 if (ftpfs_retry_seconds
!= 0)
940 retry_seconds
= ftpfs_retry_seconds
;
941 tty_enable_interrupt_key ();
942 for (count_down
= retry_seconds
; count_down
; count_down
--)
944 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
947 if (tty_got_interrupt ())
949 /* ftpfs_errno = E; */
950 tty_disable_interrupt_key ();
954 tty_disable_interrupt_key ();
958 while (retry_seconds
!= 0);
960 super
->path_element
->path
= ftpfs_get_current_directory (me
, super
);
961 if (super
->path_element
->path
== NULL
)
962 super
->path_element
->path
= g_strdup (PATH_SEP_STR
);
967 /* --------------------------------------------------------------------------------------------- */
970 ftpfs_open_archive (struct vfs_s_super
*super
,
971 const vfs_path_t
* vpath
, const vfs_path_element_t
* vpath_element
)
975 super
->data
= g_new0 (ftp_super_data_t
, 1);
977 super
->path_element
= ftpfs_correct_url_parameters (vpath_element
);
979 if (ftpfs_check_proxy (super
->path_element
->host
))
980 SUP
->proxy
= ftpfs_proxy_host
;
981 SUP
->use_passive_connection
= ftpfs_use_passive_connections
;
982 SUP
->strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
983 SUP
->isbinary
= TYPE_UNKNOWN
;
984 SUP
->remote_is_amiga
= 0;
985 super
->name
= g_strdup ("/");
987 vfs_s_new_inode (vpath_element
->class, super
,
988 vfs_s_default_stat (vpath_element
->class, S_IFDIR
| 0755));
990 return ftpfs_open_archive_int (vpath_element
->class, super
);
993 /* --------------------------------------------------------------------------------------------- */
996 ftpfs_archive_same (const vfs_path_element_t
* vpath_element
, struct vfs_s_super
*super
,
997 const vfs_path_t
* vpath
, void *cookie
)
999 vfs_path_element_t
*path_element
;
1005 path_element
= ftpfs_correct_url_parameters (vpath_element
);
1007 result
= ((strcmp (path_element
->host
, super
->path_element
->host
) == 0)
1008 && (strcmp (path_element
->user
, super
->path_element
->user
) == 0)
1009 && (path_element
->port
== super
->path_element
->port
)) ? 1 : 0;
1011 vfs_path_element_free (path_element
);
1015 /* --------------------------------------------------------------------------------------------- */
1016 /* The returned directory should always contain a trailing slash */
1019 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
1021 char buf
[BUF_8K
], *bufp
, *bufq
;
1023 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
1024 ftpfs_get_reply (me
, SUP
->sock
, buf
, sizeof (buf
)) == COMPLETE
)
1027 for (bufq
= buf
; *bufq
; bufq
++)
1039 if (*(bufq
- 1) != '/')
1045 return g_strdup (bufp
);
1048 /* If the remote server is an Amiga a leading slash
1049 might be missing. MC needs it because it is used
1050 as separator between hostname and path internally. */
1051 return g_strconcat ("/", bufp
, (char *) NULL
);
1066 /* --------------------------------------------------------------------------------------------- */
1067 /* Setup Passive PASV FTP connection */
1070 ftpfs_setup_passive_pasv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1071 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1075 int xa
, xb
, xc
, xd
, xe
, xf
;
1077 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
1080 /* Parse remote parameters */
1081 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
1085 if (!isdigit ((unsigned char) *c
))
1087 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
1090 n
[0] = (unsigned char) xa
;
1091 n
[1] = (unsigned char) xb
;
1092 n
[2] = (unsigned char) xc
;
1093 n
[3] = (unsigned char) xd
;
1094 n
[4] = (unsigned char) xe
;
1095 n
[5] = (unsigned char) xf
;
1097 memcpy (&(((struct sockaddr_in
*) sa
)->sin_addr
.s_addr
), (void *) n
, 4);
1098 memcpy (&(((struct sockaddr_in
*) sa
)->sin_port
), (void *) &n
[4], 2);
1100 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
1106 /* --------------------------------------------------------------------------------------------- */
1107 /* Setup Passive EPSV FTP connection */
1110 ftpfs_setup_passive_epsv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1111 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1116 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") != COMPLETE
)
1120 c
= strchr (reply_str
, '|');
1129 if (port
< 0 || port
> 65535)
1131 port
= htons (port
);
1133 switch (sa
->ss_family
)
1136 ((struct sockaddr_in
*) sa
)->sin_port
= port
;
1139 ((struct sockaddr_in6
*) sa
)->sin6_port
= port
;
1143 return (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0) ? 0 : 1;
1146 /* --------------------------------------------------------------------------------------------- */
1147 /* Setup Passive ftp connection, we use it for source routed connections */
1150 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
1151 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1153 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1154 if (sa
->ss_family
== AF_INET
)
1156 if (!ftpfs_setup_passive_pasv (me
, super
, my_socket
, sa
, salen
))
1157 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1158 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1161 /* It's IPV6, so EPSV is our only hope */
1164 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1171 /* --------------------------------------------------------------------------------------------- */
1172 /* Setup Active PORT or EPRT FTP connection */
1175 ftpfs_setup_active (struct vfs_class
*me
, struct vfs_s_super
*super
,
1176 struct sockaddr_storage data_addr
, socklen_t data_addrlen
)
1178 unsigned short int port
;
1182 switch (data_addr
.ss_family
)
1186 port
= ((struct sockaddr_in
*) &data_addr
)->sin_port
;
1190 port
= ((struct sockaddr_in6
*) &data_addr
)->sin6_port
;
1192 /* Not implemented */
1197 addr
= g_try_malloc (NI_MAXHOST
);
1199 ERRNOR (ENOMEM
, -1);
1202 ((struct sockaddr
*) &data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0,
1203 NI_NUMERICHOST
) != 0)
1209 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1212 unsigned char *a
= (unsigned char *) &((struct sockaddr_in
*) &data_addr
)->sin_addr
;
1213 unsigned char *p
= (unsigned char *) &port
;
1215 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1216 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1217 p
[0], p
[1]) == COMPLETE
)
1225 * Converts network MSB first order to host byte order (LSB
1226 * first on i386). If we do it earlier, we will run into an
1227 * endianness issue, because the server actually expects to see
1228 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1230 port
= ntohs (port
);
1232 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1233 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
)
1243 /* --------------------------------------------------------------------------------------------- */
1244 /* Initialize a socket for FTP DATA connection */
1247 ftpfs_init_data_socket (struct vfs_class
*me
, struct vfs_s_super
*super
,
1248 struct sockaddr_storage
*data_addr
, socklen_t
* data_addrlen
)
1252 memset (data_addr
, 0, sizeof (struct sockaddr_storage
));
1253 *data_addrlen
= sizeof (struct sockaddr_storage
);
1255 if (SUP
->use_passive_connection
)
1256 result
= getpeername (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1258 result
= getsockname (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1263 switch (data_addr
->ss_family
)
1266 ((struct sockaddr_in
*) data_addr
)->sin_port
= 0;
1269 ((struct sockaddr_in6
*) data_addr
)->sin6_port
= 0;
1272 vfs_print_message (_("ftpfs: invalid address family"));
1273 ERRNOR (EINVAL
, -1);
1276 result
= socket (data_addr
->ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1280 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1287 /* --------------------------------------------------------------------------------------------- */
1288 /* Initialize FTP DATA connection */
1291 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
1293 struct sockaddr_storage data_addr
;
1294 socklen_t data_addrlen
;
1297 * Don't factor socket initialization out of these conditionals,
1298 * because ftpfs_init_data_socket initializes it in different way
1299 * depending on use_passive_connection flag.
1302 /* Try to establish a passive connection first (if requested) */
1303 if (SUP
->use_passive_connection
)
1307 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1311 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1314 vfs_print_message (_("ftpfs: could not setup passive mode"));
1315 SUP
->use_passive_connection
= 0;
1320 /* If passive setup is diabled or failed, fallback to active connections */
1321 if (!SUP
->use_passive_connection
)
1325 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1329 if ((bind (data_sock
, (struct sockaddr
*) &data_addr
, data_addrlen
) == 0) &&
1330 (getsockname (data_sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == 0) &&
1331 (listen (data_sock
, 1) == 0) &&
1332 (ftpfs_setup_active (me
, super
, data_addr
, data_addrlen
) != 0))
1338 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1339 SUP
->use_passive_connection
= SUP
->proxy
!= NULL
? ftpfs_use_passive_connections_over_proxy
:
1340 ftpfs_use_passive_connections
;
1346 /* --------------------------------------------------------------------------------------------- */
1349 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1350 const char *remote
, int isbinary
, int reget
)
1352 struct sockaddr_storage from
;
1354 socklen_t fromlen
= sizeof (from
);
1356 s
= ftpfs_initconn (me
, super
);
1360 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1364 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1370 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1371 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1372 /* WarFtpD can't STORE //filename */
1373 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1374 g_free (remote_path
);
1377 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1381 tty_enable_interrupt_key ();
1382 if (SUP
->use_passive_connection
)
1386 data
= accept (s
, (struct sockaddr
*) &from
, &fromlen
);
1389 ftpfs_errno
= errno
;
1395 tty_disable_interrupt_key ();
1399 /* --------------------------------------------------------------------------------------------- */
1402 ftpfs_linear_abort (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1404 struct vfs_s_super
*super
= FH_SUPER
;
1405 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1408 int dsock
= FH_SOCK
;
1410 SUP
->ctl_connection_busy
= 0;
1412 vfs_print_message (_("ftpfs: aborting transfer."));
1413 if (send (SUP
->sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
))
1415 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno
));
1421 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
)
1423 vfs_print_message (_("ftpfs: abort failed"));
1431 FD_SET (dsock
, &mask
);
1432 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0)
1434 struct timeval start_tim
, tim
;
1435 gettimeofday (&start_tim
, NULL
);
1436 /* flush the remaining data */
1437 while (read (dsock
, buf
, sizeof (buf
)) > 0)
1439 gettimeofday (&tim
, NULL
);
1440 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
)
1442 /* server keeps sending, drop the connection and ftpfs_reconnect */
1444 ftpfs_reconnect (me
, super
);
1451 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1452 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1455 /* --------------------------------------------------------------------------------------------- */
1459 resolve_symlink_without_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1460 struct vfs_s_inode
*dir
)
1462 struct linklist
*flist
;
1463 struct direntry
*fe
, *fel
;
1464 char tmp
[MC_MAXPATHLEN
];
1467 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1468 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
)
1470 /* flist->data->l_stat is alread initialized with 0 */
1472 if (S_ISLNK (fel
->s
.st_mode
) && fel
->linkname
)
1474 if (fel
->linkname
[0] == '/')
1476 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1478 strcpy (tmp
, fel
->linkname
);
1482 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1484 strcpy (tmp
, dir
->remote_path
);
1487 strcat (tmp
+ 1, fel
->linkname
);
1489 for (depth
= 0; depth
< 100; depth
++)
1490 { /* depth protects against recursive symbolic links */
1491 canonicalize_pathname (tmp
);
1492 fe
= _get_file_entry (bucket
, tmp
, 0, 0);
1495 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0)
1497 /* Symlink points to link which isn't resolved, yet. */
1498 if (fe
->linkname
[0] == '/')
1500 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1502 strcpy (tmp
, fe
->linkname
);
1506 /* at this point tmp looks always like this
1507 /directory/filename, i.e. no need to check
1508 strrchr's return value */
1509 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1510 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1512 strcat (tmp
, fe
->linkname
);
1518 fel
->l_stat
= g_new (struct stat
, 1);
1519 if (S_ISLNK (fe
->s
.st_mode
))
1520 *fel
->l_stat
= *fe
->l_stat
;
1522 *fel
->l_stat
= fe
->s
;
1523 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1530 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1533 /* --------------------------------------------------------------------------------------------- */
1536 resolve_symlink_with_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1537 struct vfs_s_inode
*dir
)
1539 char buffer
[2048] = "", *filename
;
1543 struct linklist
*flist
;
1544 struct direntry
*fe
;
1545 int switch_method
= 0;
1547 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1548 if (strchr (dir
->remote_path
, ' '))
1550 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
)
1552 vfs_print_message (_("ftpfs: CWD failed."));
1555 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1558 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", dir
->remote_path
, TYPE_ASCII
, 0);
1562 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1566 fp
= fdopen (sock
, "r");
1570 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1573 tty_enable_interrupt_key ();
1574 flist
= dir
->file_list
->next
;
1579 if (flist
== dir
->file_list
)
1582 flist
= flist
->next
;
1584 while (!S_ISLNK (fe
->s
.st_mode
));
1587 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1589 if (MEDATA
->logfile
)
1591 fputs (buffer
, MEDATA
->logfile
);
1592 fflush (MEDATA
->logfile
);
1594 vfs_die ("This code should be commented out\n");
1595 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
))
1597 int r
= strcmp (fe
->name
, filename
);
1601 if (S_ISLNK (s
.st_mode
))
1603 /* This server doesn't understand LIST -lLa */
1607 fe
->l_stat
= g_new (struct stat
, 1);
1608 if (fe
->l_stat
== NULL
)
1611 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1620 while (fgets (buffer
, sizeof (buffer
), fp
) != NULL
);
1621 tty_disable_interrupt_key ();
1623 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1626 /* --------------------------------------------------------------------------------------------- */
1629 resolve_symlink (struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1631 vfs_print_message (_("Resolving symlink..."));
1633 if (SUP
->strict_rfc959_list_cmd
)
1634 resolve_symlink_without_ls_options (me
, super
, dir
);
1636 resolve_symlink_with_ls_options (me
, super
, dir
);
1640 /* --------------------------------------------------------------------------------------------- */
1643 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1645 struct vfs_s_entry
*ent
;
1646 struct vfs_s_super
*super
= dir
->super
;
1647 int sock
, num_entries
= 0;
1648 char lc_buffer
[BUF_8K
];
1651 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
->strict
== RFC_STRICT
)
1652 || (strchr (remote_path
, ' ') != NULL
);
1655 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1658 RFC_STRICT
? _("(strict rfc959)") : "", cd_first
? _("(chdir first)") : "");
1662 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
)
1664 ftpfs_errno
= ENOENT
;
1665 vfs_print_message (_("ftpfs: CWD failed."));
1670 gettimeofday (&dir
->timestamp
, NULL
);
1671 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1673 if (SUP
->strict
== RFC_STRICT
)
1674 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1676 /* Dirty hack to avoid autoprepending / to . */
1677 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1678 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1681 /* Trailing "/." is necessary if remote_path is a symlink */
1682 char *path
= concat_dir_and_file (remote_path
, ".");
1683 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1690 /* Clear the interrupt flag */
1691 tty_enable_interrupt_key ();
1693 vfs_parse_ls_lga_init ();
1697 size_t count_spaces
= 0;
1698 int res
= vfs_s_get_line_interruptible (me
, lc_buffer
, sizeof (lc_buffer
),
1705 me
->verrno
= ECONNRESET
;
1707 tty_disable_interrupt_key ();
1708 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1709 vfs_print_message (_("%s: failure"), me
->name
);
1713 if (MEDATA
->logfile
)
1715 fputs (lc_buffer
, MEDATA
->logfile
);
1716 fputs ("\n", MEDATA
->logfile
);
1717 fflush (MEDATA
->logfile
);
1720 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1721 i
= ent
->ino
->st
.st_nlink
;
1722 if (!vfs_parse_ls_lga
1723 (lc_buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
, &count_spaces
))
1725 vfs_s_free_entry (me
, ent
);
1728 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1730 vfs_s_store_filename_leading_spaces (ent
, count_spaces
);
1731 vfs_s_insert_entry (me
, dir
, ent
);
1735 me
->verrno
= E_REMOTE
;
1736 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1739 if (num_entries
== 0 && cd_first
== 0)
1741 /* The LIST command may produce an empty output. In such scenario
1742 it is not clear whether this is caused by `remote_path' being
1743 a non-existent path or for some other reason (listing emtpy
1744 directory without the -a option, non-readable directory, etc.).
1746 Since `dir_load' is a crucial method, when it comes to determine
1747 whether a given path is a _directory_, the code must try its best
1748 to determine the type of `remote_path'. The only reliable way to
1749 achieve this is trough issuing a CWD command. */
1755 vfs_s_normalize_filename_leading_spaces (dir
, vfs_parse_ls_lga_get_final_spaces ());
1757 if (SUP
->strict
== RFC_AUTODETECT
)
1758 SUP
->strict
= RFC_DARING
;
1760 vfs_print_message (_("%s: done."), me
->name
);
1764 if (SUP
->strict
== RFC_AUTODETECT
)
1766 /* It's our first attempt to get a directory listing from this
1767 server (UNIX style LIST command) */
1768 SUP
->strict
= RFC_STRICT
;
1769 /* I hate goto, but recursive call needs another 8K on stack */
1770 /* return ftpfs_dir_load (me, dir, remote_path); */
1774 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1775 ERRNOR (EACCES
, -1);
1778 /* --------------------------------------------------------------------------------------------- */
1781 ftpfs_file_store (struct vfs_class
*me
, vfs_file_handler_t
* fh
, char *name
, char *localname
)
1783 int h
, sock
, n_read
, n_written
;
1785 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1790 char lc_buffer
[BUF_8K
];
1793 struct vfs_s_super
*super
= FH_SUPER
;
1794 ftp_fh_data_t
*ftp
= (ftp_fh_data_t
*) fh
->data
;
1796 h
= open (localname
, O_RDONLY
);
1801 ftpfs_open_data_connection (me
, super
, ftp
->append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1802 if (sock
< 0 || fstat (h
, &s
) == -1)
1807 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1810 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1812 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1816 tty_enable_interrupt_key ();
1819 while ((n_read
= read (h
, lc_buffer
, sizeof (lc_buffer
))) == -1)
1823 if (tty_got_interrupt ())
1825 ftpfs_errno
= EINTR
;
1831 ftpfs_errno
= errno
;
1838 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
)
1840 if (n_written
== -1)
1842 if (errno
== EINTR
&& !tty_got_interrupt ())
1846 ftpfs_errno
= errno
;
1850 n_read
-= n_written
;
1852 vfs_print_message ("%s: %" PRIuMAX
"/%" PRIuMAX
,
1853 _("ftpfs: storing file"), (uintmax_t) n_stored
, (uintmax_t) s
.st_size
);
1855 tty_disable_interrupt_key ();
1858 if (ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
)
1862 tty_disable_interrupt_key ();
1865 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1869 /* --------------------------------------------------------------------------------------------- */
1872 ftpfs_linear_start (struct vfs_class
*me
, vfs_file_handler_t
* fh
, off_t offset
)
1876 if (fh
->data
== NULL
)
1877 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
1879 name
= vfs_s_fullpath (me
, fh
->ino
);
1882 FH_SOCK
= ftpfs_open_data_connection (me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1886 fh
->linear
= LS_LINEAR_OPEN
;
1887 ((ftp_super_data_t
*) (FH_SUPER
->data
))->ctl_connection_busy
= 1;
1888 ((ftp_fh_data_t
*) fh
->data
)->append
= 0;
1892 /* --------------------------------------------------------------------------------------------- */
1895 ftpfs_linear_read (struct vfs_class
*me
, vfs_file_handler_t
* fh
, void *buf
, size_t len
)
1898 struct vfs_s_super
*super
= FH_SUPER
;
1900 while ((n
= read (FH_SOCK
, buf
, len
)) < 0)
1902 if ((errno
== EINTR
) && !tty_got_interrupt ())
1908 ftpfs_linear_abort (me
, fh
);
1912 SUP
->ctl_connection_busy
= 0;
1915 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1916 ERRNOR (E_REMOTE
, -1);
1922 /* --------------------------------------------------------------------------------------------- */
1925 ftpfs_linear_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1928 ftpfs_linear_abort (me
, fh
);
1931 /* --------------------------------------------------------------------------------------------- */
1934 ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1940 case VFS_CTL_IS_NOTREADY
:
1945 vfs_die ("You may not do this");
1946 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1949 v
= vfs_s_select_on_two (((ftp_fh_data_t
*) (FH
->data
))->sock
, 0);
1950 return (((v
< 0) && (errno
== EINTR
)) || v
== 0) ? 1 : 0;
1957 /* --------------------------------------------------------------------------------------------- */
1960 ftpfs_send_command (const vfs_path_t
* vpath
, const char *cmd
, int flags
)
1964 struct vfs_s_super
*super
;
1966 vfs_path_element_t
*path_element
;
1968 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1970 path_element
= vfs_path_get_by_index (vpath
, -1);
1972 rpath
= vfs_s_get_path (vpath
, &super
, 0);
1976 p
= ftpfs_translate_path (path_element
->class, super
, rpath
);
1977 r
= ftpfs_command (path_element
->class, super
, WAIT_REPLY
, cmd
, p
);
1979 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1980 if (flags
& OPT_IGNORE_ERROR
)
1984 path_element
->class->verrno
= EPERM
;
1987 if (flush_directory_cache
)
1988 vfs_s_invalidate (path_element
->class, super
);
1992 /* --------------------------------------------------------------------------------------------- */
1995 ftpfs_chmod (const vfs_path_t
* vpath
, mode_t mode
)
1997 char buf
[BUF_SMALL
];
2000 g_snprintf (buf
, sizeof (buf
), "SITE CHMOD %4.4o /%%s", (int) (mode
& 07777));
2002 ret
= ftpfs_send_command (vpath
, buf
, OPT_FLUSH
);
2004 return ftpfs_ignore_chattr_errors
? 0 : ret
;
2007 /* --------------------------------------------------------------------------------------------- */
2010 ftpfs_chown (const vfs_path_t
* vpath
, uid_t owner
, gid_t group
)
2017 ftpfs_errno
= EPERM
;
2020 /* Everyone knows it is not possible to chown remotely, so why bother them.
2021 If someone's root, then copy/move will always try to chown it... */
2029 /* --------------------------------------------------------------------------------------------- */
2032 ftpfs_unlink (const vfs_path_t
* vpath
)
2034 return ftpfs_send_command (vpath
, "DELE /%s", OPT_FLUSH
);
2037 /* --------------------------------------------------------------------------------------------- */
2039 /* Return 1 if path is the same directory as the one we are in now */
2041 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
2045 if (super
->path_element
->path
== NULL
)
2047 return (strcmp (path
, super
->path_element
->path
) == 0);
2050 /* --------------------------------------------------------------------------------------------- */
2053 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
2058 if (!SUP
->cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
2061 p
= ftpfs_translate_path (me
, super
, remote_path
);
2062 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
2069 g_free (super
->path_element
->path
);
2070 super
->path_element
->path
= g_strdup (remote_path
);
2071 SUP
->cwd_deferred
= 0;
2076 /* --------------------------------------------------------------------------------------------- */
2079 ftpfs_rename (const vfs_path_t
* vpath1
, const vfs_path_t
* vpath2
)
2081 ftpfs_send_command (vpath1
, "RNFR /%s", OPT_FLUSH
);
2082 return ftpfs_send_command (vpath2
, "RNTO /%s", OPT_FLUSH
);
2085 /* --------------------------------------------------------------------------------------------- */
2088 ftpfs_mkdir (const vfs_path_t
* vpath
, mode_t mode
)
2090 (void) mode
; /* FIXME: should be used */
2092 return ftpfs_send_command (vpath
, "MKD /%s", OPT_FLUSH
);
2095 /* --------------------------------------------------------------------------------------------- */
2098 ftpfs_rmdir (const vfs_path_t
* vpath
)
2100 return ftpfs_send_command (vpath
, "RMD /%s", OPT_FLUSH
);
2103 /* --------------------------------------------------------------------------------------------- */
2106 ftpfs_fh_free_data (vfs_file_handler_t
*fh
)
2115 /* --------------------------------------------------------------------------------------------- */
2118 ftpfs_fh_open (struct vfs_class
*me
, vfs_file_handler_t
* fh
, int flags
, mode_t mode
)
2124 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
2125 ftp
= (ftp_fh_data_t
*) fh
->data
;
2126 /* File will be written only, so no need to retrieve it from ftp server */
2127 if (((flags
& O_WRONLY
) == O_WRONLY
) && ((flags
& (O_RDONLY
| O_RDWR
)) == 0))
2129 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2136 /* ftpfs_linear_start() called, so data will be written
2137 * to local temporary file and stored to ftp server
2138 * by vfs_s_close later
2140 if (((ftp_super_data_t
*) (FH_SUPER
->data
))->ctl_connection_busy
)
2142 if (!fh
->ino
->localname
)
2144 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
2145 fh
->ino
->ent
->name
);
2149 ftp
->append
= flags
& O_APPEND
;
2153 name
= vfs_s_fullpath (me
, fh
->ino
);
2157 ftpfs_open_data_connection (me
, fh
->ino
->super
,
2158 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
2163 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2167 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
2169 if (fh
->ino
->localname
)
2171 unlink (fh
->ino
->localname
);
2172 g_free (fh
->ino
->localname
);
2173 fh
->ino
->localname
= NULL
;
2178 if (!fh
->ino
->localname
&& vfs_s_retrieve_file (me
, fh
->ino
) == -1)
2180 if (!fh
->ino
->localname
)
2181 vfs_die ("retrieve_file failed to fill in localname");
2185 ftpfs_fh_free_data (fh
);
2189 /* --------------------------------------------------------------------------------------------- */
2192 ftpfs_fh_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
2194 if (fh
->handle
!= -1 && !fh
->ino
->localname
)
2196 ftp_super_data_t
*ftp
= (ftp_super_data_t
*) fh
->ino
->super
->data
;
2200 /* File is stored to destination already, so
2201 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2204 if (ftpfs_get_reply (me
, ftp
->sock
, NULL
, 0) != COMPLETE
)
2206 vfs_s_invalidate (me
, FH_SUPER
);
2212 /* --------------------------------------------------------------------------------------------- */
2215 ftpfs_done (struct vfs_class
*me
)
2219 g_slist_foreach (no_proxy
, (GFunc
) g_free
, NULL
);
2220 g_slist_free (no_proxy
);
2222 g_free (ftpfs_anonymous_passwd
);
2223 g_free (ftpfs_proxy_host
);
2226 /* --------------------------------------------------------------------------------------------- */
2229 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
2233 for (iter
= MEDATA
->supers
; iter
!= NULL
; iter
= g_list_next (iter
))
2235 const struct vfs_s_super
*super
= (const struct vfs_s_super
*) iter
->data
;
2239 g_strconcat (vfs_ftpfs_ops
.prefix
, VFS_PATH_URL_DELIMITER
, super
->path_element
->user
,
2240 "@", super
->path_element
->host
, "/", super
->path_element
->path
,
2247 /* --------------------------------------------------------------------------------------------- */
2250 ftpfs_netrc_next (void)
2254 static const char *const keywords
[] = { "default", "machine",
2255 "login", "password", "passwd", "account", "macdef", NULL
2260 netrcp
= skip_separators (netrcp
);
2261 if (*netrcp
!= '\n')
2270 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++)
2272 if (*netrcp
== '\\')
2279 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
2280 *netrcp
!= ',' && *netrcp
; netrcp
++)
2282 if (*netrcp
== '\\')
2291 for (i
= NETRC_DEFAULT
; keywords
[i
- 1] != NULL
; i
++)
2292 if (strcmp (keywords
[i
- 1], buffer
) == 0)
2295 return NETRC_UNKNOWN
;
2298 /* --------------------------------------------------------------------------------------------- */
2301 ftpfs_netrc_bad_mode (const char *netrcname
)
2303 static int be_angry
= 1;
2306 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077))
2310 message (D_ERROR
, MSG_ERROR
,
2311 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2319 /* --------------------------------------------------------------------------------------------- */
2320 /* Scan .netrc until we find matching "machine" or "default"
2321 * domain is used for additional matching
2322 * No search is done after "default" in compliance with "man netrc"
2323 * Return 0 if found, -1 otherwise */
2326 ftpfs_find_machine (const char *host
, const char *domain
)
2335 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
)
2337 if (keyword
== NETRC_DEFAULT
)
2340 if (keyword
== NETRC_MACDEF
)
2342 /* Scan for an empty line, which concludes "macdef" */
2345 while (*netrcp
&& *netrcp
!= '\n')
2347 if (*netrcp
!= '\n')
2351 while (*netrcp
&& *netrcp
!= '\n');
2355 if (keyword
!= NETRC_MACHINE
)
2358 /* Take machine name */
2359 if (ftpfs_netrc_next () == NETRC_NONE
)
2362 if (g_ascii_strcasecmp (host
, buffer
) != 0)
2364 /* Try adding our domain to short names in .netrc */
2365 const char *host_domain
= strchr (host
, '.');
2369 /* Compare domain part */
2370 if (g_ascii_strcasecmp (host_domain
, domain
) != 0)
2373 /* Compare local part */
2374 if (g_ascii_strncasecmp (host
, buffer
, host_domain
- host
) != 0)
2385 /* --------------------------------------------------------------------------------------------- */
2386 /* Extract login and password from .netrc for the host.
2388 * Returns 0 for success, -1 for error */
2391 ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
2394 char *tmp_pass
= NULL
;
2395 char hostname
[MAXHOSTNAMELEN
];
2398 static struct rupcache
2400 struct rupcache
*next
;
2404 } *rup_cache
= NULL
, *rupp
;
2406 /* Initialize *login and *pass */
2412 /* Look up in the cache first */
2413 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
)
2415 if (!strcmp (host
, rupp
->host
))
2418 *login
= g_strdup (rupp
->login
);
2419 if (pass
&& rupp
->pass
)
2420 *pass
= g_strdup (rupp
->pass
);
2425 /* Load current .netrc */
2426 netrcname
= g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL
);
2427 if (!g_file_get_contents (netrcname
, &netrc
, NULL
, NULL
))
2435 /* Find our own domain name */
2436 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2439 domain
= strchr (hostname
, '.');
2443 /* Scan for "default" and matching "machine" keywords */
2444 ftpfs_find_machine (host
, domain
);
2446 /* Scan for keywords following "default" and "machine" */
2450 keyword
= ftpfs_netrc_next ();
2455 if (ftpfs_netrc_next () == NETRC_NONE
)
2461 /* We have another name already - should not happen */
2468 /* We have login name now */
2469 *login
= g_strdup (buffer
);
2472 case NETRC_PASSWORD
:
2474 if (ftpfs_netrc_next () == NETRC_NONE
)
2480 /* Ignore unsafe passwords */
2481 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2482 && ftpfs_netrc_bad_mode (netrcname
))
2488 /* Remember password. pass may be NULL, so use tmp_pass */
2489 if (tmp_pass
== NULL
)
2490 tmp_pass
= g_strdup (buffer
);
2494 /* "account" is followed by a token which we ignore */
2495 if (ftpfs_netrc_next () == NETRC_NONE
)
2501 /* Ignore account, but warn user anyways */
2502 ftpfs_netrc_bad_mode (netrcname
);
2506 /* Unexpected keyword or end of file */
2518 rupp
= g_new (struct rupcache
, 1);
2519 rupp
->host
= g_strdup (host
);
2520 rupp
->login
= g_strdup (*login
);
2521 rupp
->pass
= g_strdup (tmp_pass
);
2523 rupp
->next
= rup_cache
;
2531 /* --------------------------------------------------------------------------------------------- */
2532 /*** public functions ****************************************************************************/
2533 /* --------------------------------------------------------------------------------------------- */
2535 /** This routine is called as the last step in load_setup */
2537 ftpfs_init_passwd (void)
2539 ftpfs_anonymous_passwd
= load_anon_passwd ();
2540 if (ftpfs_anonymous_passwd
)
2543 /* If there is no anonymous ftp password specified
2544 * then we'll just use anonymous@
2545 * We don't send any other thing because:
2546 * - We want to remain anonymous
2547 * - We want to stop SPAM
2548 * - We don't want to let ftp sites to discriminate by the user,
2551 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
2554 /* --------------------------------------------------------------------------------------------- */
2559 static struct vfs_s_subclass ftpfs_subclass
;
2563 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
2564 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2565 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2566 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2567 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2568 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2569 ftpfs_subclass
.fh_free_data
= ftpfs_fh_free_data
;
2570 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2571 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2572 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2573 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2574 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2576 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2577 vfs_ftpfs_ops
.name
= "ftpfs";
2578 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2579 vfs_ftpfs_ops
.prefix
= "ftp";
2580 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2581 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2582 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2583 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2584 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2585 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2586 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2587 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2588 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2589 vfs_register_class (&vfs_ftpfs_ops
);
2592 /* --------------------------------------------------------------------------------------------- */