1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
8 1995, 1996, 1997 Miguel de Icaza
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU Library General Public License
17 as published by the Free Software Foundation; either version 2 of
18 the License, or (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU Library General Public License for more details.
25 You should have received a copy of the GNU Library General Public
26 License along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
31 * \brief Source: Virtual File System: FTP file system
33 * \author Jakub Jelinek
34 * \author Miguel de Icaza
35 * \author Norbert Warmuth
36 * \author Pavel Machek
37 * \date 1995, 1997, 1998
40 - make it more robust - all the connects etc. should handle EADDRINUSE and
41 ERETRY (have I spelled these names correctly?)
42 - make the user able to flush a connection - all the caches will get empty
43 etc., (tarfs as well), we should give there a user selectable timeout
44 and assign a key sequence.
45 - use hash table instead of linklist to cache ftpfs directory.
50 * NOTE: Usage of tildes is deprecated, consider:
55 * And now: what do I want to do? Do I want to go to /home/pavel or to
56 * /#ftp:hobit/home/pavel? I think first has better sense...
60 int f = !strcmp( remote_path, "/~" );
61 if (f || !strncmp( remote_path, "/~/", 3 )) {
63 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
71 /* \todo Fix: Namespace pollution: horrible */
74 #include <stdio.h> /* sscanf() */
75 #include <stdlib.h> /* atoi() */
76 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
77 #include <netdb.h> /* struct hostent */
78 #include <sys/socket.h> /* AF_INET */
79 #include <netinet/in.h> /* struct in_addr */
80 #ifdef HAVE_ARPA_INET_H
81 #include <arpa/inet.h>
84 #include <arpa/telnet.h>
85 #include <sys/param.h>
89 #include <sys/time.h> /* gettimeofday() */
90 #include <inttypes.h> /* uintmax_t */
92 #include "lib/global.h"
94 #include "lib/mcconfig.h"
96 #include "lib/tty/tty.h" /* enable/disable interrupt key */
97 #include "lib/widget.h" /* message() */
99 #include "src/history.h"
100 #include "src/setup.h" /* for load_anon_passwd */
102 #include "lib/vfs/vfs.h"
103 #include "lib/vfs/utilvfs.h"
104 #include "lib/vfs/netutil.h"
105 #include "lib/vfs/xdirentry.h"
106 #include "lib/vfs/gc.h" /* vfs_stamp_create */
110 /*** global variables ****************************************************************************/
112 /* Delay to retry a connection */
113 int ftpfs_retry_seconds
= 30;
115 /* Method to use to connect to ftp sites */
116 int ftpfs_use_passive_connections
= 1;
117 int ftpfs_use_passive_connections_over_proxy
= 0;
119 /* Method used to get directory listings:
120 * 1: try 'LIST -la <path>', if it fails
121 * fall back to CWD <path>; LIST
122 * 0: always use CWD <path>; LIST
124 int ftpfs_use_unix_list_options
= 1;
126 /* First "CWD <path>", then "LIST -la ." */
127 int ftpfs_first_cd_then_ls
= 1;
129 /* Use the ~/.netrc */
130 int ftpfs_use_netrc
= 1;
132 /* Anonymous setup */
133 char *ftpfs_anonymous_passwd
= NULL
;
134 int ftpfs_directory_timeout
= 900;
137 char *ftpfs_proxy_host
= NULL
;
139 /* wether we have to use proxy by default? */
140 int ftpfs_always_use_proxy
= 0;
142 int ftpfs_ignore_chattr_errors
= 1;
144 /*** file scope macro definitions ****************************************************************/
146 #ifndef MAXHOSTNAMELEN
147 #define MAXHOSTNAMELEN 64
150 #define UPLOAD_ZERO_LENGTH_FILE
151 #define SUP ((ftp_super_data_t *) super->data)
152 #define FH_SOCK ((ftp_fh_data_t *) fh->data)->sock
155 #define INADDR_NONE 0xffffffff
158 #define RFC_AUTODETECT 0
162 /* ftpfs_command wait_flag: */
164 #define WAIT_REPLY 0x01
165 #define WANT_STRING 0x02
167 #define FTP_COMMAND_PORT 21
169 /* some defines only used by ftpfs_changetype */
170 /* These two are valid values for the second parameter */
172 #define TYPE_BINARY 1
174 /* This one is only used to initialize bucket->isbinary, don't use it as
175 second parameter to ftpfs_changetype. */
176 #define TYPE_UNKNOWN -1
178 #define ABORT_TIMEOUT 5
179 /*** file scope type declarations ****************************************************************/
181 #ifndef HAVE_SOCKLEN_T
182 typedef int socklen_t
;
185 /* This should match the keywords[] array below */
203 char *proxy
; /* proxy server, NULL if no proxy */
204 int failed_on_login
; /* used to pass the failure reason to upper levels */
205 int use_passive_connection
;
206 int remote_is_amiga
; /* No leading slash allowed for AmiTCP (Amiga) */
208 int cwd_deferred
; /* current_directory was changed but CWD command hasn't
210 int strict
; /* ftp server doesn't understand
211 * "LIST -la <path>"; use "CWD <path>"/
214 int ctl_connection_busy
;
223 /*** file scope variables ************************************************************************/
225 static int ftpfs_errno
;
228 #ifdef FIXME_LATER_ALIGATOR
229 static struct linklist
*connections_list
;
232 static char reply_str
[80];
234 static struct vfs_class vfs_ftpfs_ops
;
236 static GSList
*no_proxy
;
238 static char buffer
[BUF_MEDIUM
];
240 static const char *netrcp
;
242 /*** file scope functions ************************************************************************/
243 /* --------------------------------------------------------------------------------------------- */
245 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
246 Translate a Unix path, i.e. MC's internal path representation (e.g.
247 /somedir/somefile) to a path valid for the remote server. Every path
248 transfered to the remote server has to be mangled by this function
249 right prior to sending it.
250 Currently only Amiga ftp servers are handled in a special manner.
252 When the remote server is an amiga:
253 a) strip leading slash if necesarry
254 b) replace first occurance of ":/" with ":"
255 c) strip trailing "/."
258 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
259 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
,
260 const char *remote_path
);
261 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
,
262 const char *fmt
, ...) __attribute__ ((format (__printf__
, 4, 5)));
263 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
264 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
265 const char *netrcpass
);
266 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
268 /* --------------------------------------------------------------------------------------------- */
271 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
273 if (!SUP
->remote_is_amiga
)
274 return g_strdup (remote_path
);
281 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
282 fflush (MEDATA
->logfile
);
285 /* strip leading slash(es) */
286 while (*remote_path
== '/')
290 * Don't change "/" into "", e.g. "CWD " would be
293 if (*remote_path
== '\0')
294 return g_strdup (".");
296 ret
= g_strdup (remote_path
);
298 /* replace first occurance of ":/" with ":" */
299 p
= strchr (ret
, ':');
300 if ((p
!= NULL
) && (*(p
+ 1) == '/'))
301 memmove (p
+ 1, p
+ 2, strlen (p
+ 2) + 1);
303 /* strip trailing "/." */
304 p
= strrchr (ret
, '/');
305 if ((p
!= NULL
) && (*(p
+ 1) == '.') && (*(p
+ 2) == '\0'))
312 /* --------------------------------------------------------------------------------------------- */
313 /** Extract the hostname and username from the path */
315 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
316 * ftp://sunsite.unc.edu/pub/linux
317 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
318 * ftp://tsx-11.mit.edu:8192/
319 * ftp://joe@foo.edu:11321/private
320 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
324 static vfs_path_element_t
*
325 ftpfs_split_url (const char *path
)
327 vfs_path_element_t
*path_element
;
329 path_element
= vfs_url_split (path
, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
331 if (path_element
->user
!= NULL
)
333 /* Look up user and password in netrc */
335 ftpfs_netrc_lookup (path_element
->host
, &path_element
->user
, &path_element
->password
);
337 if (path_element
->user
== NULL
)
338 path_element
->user
= g_strdup ("anonymous");
340 /* Look up password in netrc for known user */
341 if (ftpfs_use_netrc
&& path_element
->user
!= NULL
&& path_element
->password
!= NULL
)
343 char *new_user
= NULL
;
345 ftpfs_netrc_lookup (path_element
->host
, &new_user
, &path_element
->password
);
347 /* If user is different, remove password */
348 if (new_user
!= NULL
&& strcmp (path_element
->user
, new_user
) != 0)
350 g_free (path_element
->password
);
351 path_element
->password
= NULL
;
360 /* --------------------------------------------------------------------------------------------- */
361 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
364 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
371 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
378 switch (sscanf (answer
, "%d", &code
))
382 g_strlcpy (string_buf
, answer
, string_len
);
386 if (answer
[3] == '-')
390 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
397 if ((sscanf (answer
, "%d", &i
) > 0) && (code
== i
) && (answer
[3] == ' '))
402 g_strlcpy (string_buf
, answer
, string_len
);
408 /* --------------------------------------------------------------------------------------------- */
411 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
415 sock
= ftpfs_open_socket (me
, super
);
418 char *cwdir
= super
->path_element
->path
;
422 super
->path_element
->path
= NULL
;
425 if (ftpfs_login_server (me
, super
, super
->path_element
->password
) != 0)
429 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
431 return sock
== COMPLETE
? 1 : 0;
434 super
->path_element
->path
= cwdir
;
440 /* --------------------------------------------------------------------------------------------- */
443 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
,
449 static int retry
= 0;
450 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
453 cmdstr
= g_strdup_vprintf (fmt
, ap
);
456 cmdlen
= strlen (cmdstr
);
457 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
458 strcpy (cmdstr
+ cmdlen
, "\r\n");
463 if (strncmp (cmdstr
, "PASS ", 5) == 0)
465 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
470 ret
= fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
473 fflush (MEDATA
->logfile
);
477 tty_enable_interrupt_key ();
478 status
= write (SUP
->sock
, cmdstr
, cmdlen
);
485 { /* Remote server has closed connection */
489 status
= ftpfs_reconnect (me
, super
);
491 if (status
&& (write (SUP
->sock
, cmdstr
, cmdlen
) > 0))
500 tty_disable_interrupt_key ();
505 tty_disable_interrupt_key ();
509 status
= ftpfs_get_reply (me
, SUP
->sock
,
510 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
511 sizeof (reply_str
) - 1);
512 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
516 status
= ftpfs_reconnect (me
, super
);
518 if (status
&& (write (SUP
->sock
, cmdstr
, cmdlen
) > 0))
531 /* --------------------------------------------------------------------------------------------- */
534 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
538 vfs_print_message (_("ftpfs: Disconnecting from %s"), super
->path_element
->host
);
539 ftpfs_command (me
, super
, NONE
, "QUIT");
542 g_free (super
->data
);
546 /* --------------------------------------------------------------------------------------------- */
549 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
551 if (binary
!= SUP
->isbinary
)
553 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
555 SUP
->isbinary
= binary
;
560 /* --------------------------------------------------------------------------------------------- */
561 /* This routine logs the user in */
564 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
)
568 char *name
; /* login user name */
570 char reply_string
[BUF_MEDIUM
];
572 SUP
->isbinary
= TYPE_UNKNOWN
;
574 if (super
->path_element
->password
!= NULL
) /* explicit password */
575 op
= g_strdup (super
->path_element
->password
);
576 else if (netrcpass
!= NULL
) /* password from netrc */
577 op
= g_strdup (netrcpass
);
578 else if (strcmp (super
->path_element
->user
, "anonymous") == 0
579 || strcmp (super
->path_element
->user
, "ftp") == 0)
581 if (ftpfs_anonymous_passwd
== NULL
) /* default anonymous password */
582 ftpfs_init_passwd ();
583 op
= g_strdup (ftpfs_anonymous_passwd
);
590 p
= g_strdup_printf (_("FTP: Password required for %s"), super
->path_element
->user
);
591 op
= vfs_get_password (p
);
595 super
->path_element
->password
= g_strdup (op
);
598 if (!anon
|| MEDATA
->logfile
)
602 pass
= g_strconcat ("-", op
, (char *) NULL
);
606 /* Proxy server accepts: username@host-we-want-to-connect */
609 g_strconcat (super
->path_element
->user
, "@",
610 super
->path_element
->host
[0] ==
611 '!' ? super
->path_element
->host
+ 1 : super
->path_element
->host
,
614 name
= g_strdup (super
->path_element
->user
);
616 if (ftpfs_get_reply (me
, SUP
->sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
)
620 reply_up
= g_ascii_strup (reply_string
, -1);
621 SUP
->remote_is_amiga
= strstr (reply_up
, "AMIGA") != 0;
626 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n", SUP
->remote_is_amiga
);
627 fflush (MEDATA
->logfile
);
630 vfs_print_message (_("ftpfs: sending login name"));
632 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
))
635 vfs_print_message (_("ftpfs: sending user password"));
636 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
637 if (code
== CONTINUE
)
641 p
= g_strdup_printf (_("FTP: Account required for user %s"),
642 super
->path_element
->user
);
643 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
647 vfs_print_message (_("ftpfs: sending user account"));
648 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
651 if (code
!= COMPLETE
)
656 vfs_print_message (_("ftpfs: logged in"));
657 wipe_password (pass
);
662 SUP
->failed_on_login
= 1;
663 wipe_password (super
->path_element
->password
);
664 super
->path_element
->password
= NULL
;
670 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
671 super
->path_element
->user
);
674 wipe_password (pass
);
679 /* --------------------------------------------------------------------------------------------- */
682 ftpfs_load_no_proxy_list (void)
684 /* FixMe: shouldn't be hardcoded!!! */
685 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
689 static char *mc_file
= NULL
;
691 mc_file
= g_build_filename (mc_global
.sysconfig_dir
, "mc.no_proxy", (char *) NULL
);
692 if (exist_file (mc_file
))
694 npf
= fopen (mc_file
, "r");
697 while (fgets (s
, sizeof (s
), npf
) != NULL
)
699 p
= strchr (s
, '\n');
700 if (p
== NULL
) /* skip bogus entries */
702 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
710 no_proxy
= g_slist_prepend (no_proxy
, g_strdup (s
));
719 /* --------------------------------------------------------------------------------------------- */
720 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
723 ftpfs_check_proxy (const char *host
)
727 if (ftpfs_proxy_host
== NULL
|| *ftpfs_proxy_host
== '\0' || host
== NULL
|| *host
== '\0')
728 return 0; /* sanity check */
733 if (!ftpfs_always_use_proxy
)
736 if (strchr (host
, '.') == NULL
)
739 ftpfs_load_no_proxy_list ();
740 for (npe
= no_proxy
; npe
!= NULL
; npe
= g_slist_next (npe
))
742 const char *domain
= (const char *) npe
->data
;
744 if (domain
[0] == '.')
746 size_t ld
= strlen (domain
);
747 size_t lh
= strlen (host
);
749 while (ld
!= 0 && lh
!= 0 && host
[lh
- 1] == domain
[ld
- 1])
758 else if (g_ascii_strcasecmp (host
, domain
) == 0)
765 /* --------------------------------------------------------------------------------------------- */
768 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
770 vfs_path_element_t
*path_element
;
772 path_element
= vfs_url_split (proxy
, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
773 *host
= g_strdup (path_element
->host
);
774 *port
= path_element
->port
;
775 vfs_path_element_free (path_element
);
778 /* --------------------------------------------------------------------------------------------- */
781 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
783 struct addrinfo hints
, *res
, *curr_res
;
792 /* Use a proxy host? */
793 host
= g_strdup (super
->path_element
->host
);
795 if (host
== NULL
|| *host
== '\0')
797 vfs_print_message (_("ftpfs: Invalid host name."));
798 ftpfs_errno
= EINVAL
;
803 /* Hosts to connect to that start with a ! should use proxy */
804 tmp_port
= super
->path_element
->port
;
806 if (SUP
->proxy
!= NULL
)
807 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
809 g_snprintf (port
, sizeof (port
), "%hu", (unsigned short) tmp_port
);
817 tty_enable_interrupt_key (); /* clear the interrupt flag */
819 memset (&hints
, 0, sizeof (struct addrinfo
));
820 hints
.ai_family
= AF_UNSPEC
;
821 hints
.ai_socktype
= SOCK_STREAM
;
824 /* By default, only look up addresses using address types for
825 * which a local interface is configured (i.e. no IPv6 if no IPv6
826 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
827 hints
.ai_flags
= AI_ADDRCONFIG
;
830 e
= getaddrinfo (host
, port
, &hints
, &res
);
833 if (e
== EAI_BADFLAGS
)
835 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
837 e
= getaddrinfo (host
, port
, &hints
, &res
);
845 tty_disable_interrupt_key ();
846 vfs_print_message (_("ftpfs: %s"), gai_strerror (e
));
848 ftpfs_errno
= EINVAL
;
852 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
)
854 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
858 if (curr_res
->ai_next
!= NULL
)
861 tty_disable_interrupt_key ();
862 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno
));
869 vfs_print_message (_("ftpfs: making connection to %s"), host
);
873 if (connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0)
879 if (errno
== EINTR
&& tty_got_interrupt ())
880 vfs_print_message (_("ftpfs: connection interrupted by user"));
881 else if (res
->ai_next
== NULL
)
882 vfs_print_message (_("ftpfs: connection to server failed: %s"),
883 unix_error_string (errno
));
888 tty_disable_interrupt_key ();
893 tty_disable_interrupt_key ();
897 /* --------------------------------------------------------------------------------------------- */
900 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
902 int retry_seconds
= 0;
905 /* We do not want to use the passive if we are using proxies */
907 SUP
->use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
911 SUP
->failed_on_login
= 0;
913 SUP
->sock
= ftpfs_open_socket (me
, super
);
917 if (ftpfs_login_server (me
, super
, NULL
) != 0)
919 /* Logged in, no need to retry the connection */
924 if (!SUP
->failed_on_login
)
927 /* Close only the socket descriptor */
930 if (ftpfs_retry_seconds
!= 0)
932 retry_seconds
= ftpfs_retry_seconds
;
933 tty_enable_interrupt_key ();
934 for (count_down
= retry_seconds
; count_down
; count_down
--)
936 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
939 if (tty_got_interrupt ())
941 /* ftpfs_errno = E; */
942 tty_disable_interrupt_key ();
946 tty_disable_interrupt_key ();
950 while (retry_seconds
!= 0);
952 super
->path_element
->path
= ftpfs_get_current_directory (me
, super
);
953 if (super
->path_element
->path
== NULL
)
954 super
->path_element
->path
= g_strdup (PATH_SEP_STR
);
959 /* --------------------------------------------------------------------------------------------- */
962 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
963 const char *archive_name
, char *op
)
967 super
->data
= g_new0 (ftp_super_data_t
, 1);
969 super
->path_element
= ftpfs_split_url (strchr (op
, ':') + 1);
971 if (ftpfs_check_proxy (super
->path_element
->host
))
972 SUP
->proxy
= ftpfs_proxy_host
;
973 SUP
->use_passive_connection
= ftpfs_use_passive_connections
;
974 SUP
->strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
975 SUP
->isbinary
= TYPE_UNKNOWN
;
976 SUP
->remote_is_amiga
= 0;
977 super
->name
= g_strdup ("/");
978 super
->root
= vfs_s_new_inode (me
, super
, vfs_s_default_stat (me
, S_IFDIR
| 0755));
980 return ftpfs_open_archive_int (me
, super
);
983 /* --------------------------------------------------------------------------------------------- */
986 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
987 const char *archive_name
, char *op
, void *cookie
)
989 vfs_path_element_t
*path_element
;
996 path_element
= ftpfs_split_url (strchr (op
, ':') + 1);
998 result
= ((strcmp (path_element
->host
, super
->path_element
->host
) == 0)
999 && (strcmp (path_element
->user
, super
->path_element
->user
) == 0)
1000 && (path_element
->port
== super
->path_element
->port
)) ? 1 : 0;
1002 vfs_path_element_free (path_element
);
1006 /* --------------------------------------------------------------------------------------------- */
1007 /* The returned directory should always contain a trailing slash */
1010 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
1012 char buf
[BUF_8K
], *bufp
, *bufq
;
1014 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
1015 ftpfs_get_reply (me
, SUP
->sock
, buf
, sizeof (buf
)) == COMPLETE
)
1018 for (bufq
= buf
; *bufq
; bufq
++)
1030 if (*(bufq
- 1) != '/')
1036 return g_strdup (bufp
);
1039 /* If the remote server is an Amiga a leading slash
1040 might be missing. MC needs it because it is used
1041 as separator between hostname and path internally. */
1042 return g_strconcat ("/", bufp
, (char *) NULL
);
1057 /* --------------------------------------------------------------------------------------------- */
1058 /* Setup Passive PASV FTP connection */
1061 ftpfs_setup_passive_pasv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1062 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1066 int xa
, xb
, xc
, xd
, xe
, xf
;
1068 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
1071 /* Parse remote parameters */
1072 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
1076 if (!isdigit ((unsigned char) *c
))
1078 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
1081 n
[0] = (unsigned char) xa
;
1082 n
[1] = (unsigned char) xb
;
1083 n
[2] = (unsigned char) xc
;
1084 n
[3] = (unsigned char) xd
;
1085 n
[4] = (unsigned char) xe
;
1086 n
[5] = (unsigned char) xf
;
1088 memcpy (&(((struct sockaddr_in
*) sa
)->sin_addr
.s_addr
), (void *) n
, 4);
1089 memcpy (&(((struct sockaddr_in
*) sa
)->sin_port
), (void *) &n
[4], 2);
1091 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
1097 /* --------------------------------------------------------------------------------------------- */
1098 /* Setup Passive EPSV FTP connection */
1101 ftpfs_setup_passive_epsv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1102 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1107 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") != COMPLETE
)
1111 c
= strchr (reply_str
, '|');
1120 if (port
< 0 || port
> 65535)
1122 port
= htons (port
);
1124 switch (sa
->ss_family
)
1127 ((struct sockaddr_in
*) sa
)->sin_port
= port
;
1130 ((struct sockaddr_in6
*) sa
)->sin6_port
= port
;
1134 return (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0) ? 0 : 1;
1137 /* --------------------------------------------------------------------------------------------- */
1138 /* Setup Passive ftp connection, we use it for source routed connections */
1141 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
1142 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1144 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1145 if (sa
->ss_family
== AF_INET
)
1147 if (!ftpfs_setup_passive_pasv (me
, super
, my_socket
, sa
, salen
))
1148 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1149 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1152 /* It's IPV6, so EPSV is our only hope */
1155 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1162 /* --------------------------------------------------------------------------------------------- */
1163 /* Setup Active PORT or EPRT FTP connection */
1166 ftpfs_setup_active (struct vfs_class
*me
, struct vfs_s_super
*super
,
1167 struct sockaddr_storage data_addr
, socklen_t data_addrlen
)
1169 unsigned short int port
;
1173 switch (data_addr
.ss_family
)
1177 port
= ((struct sockaddr_in
*) &data_addr
)->sin_port
;
1181 port
= ((struct sockaddr_in6
*) &data_addr
)->sin6_port
;
1183 /* Not implemented */
1188 addr
= g_try_malloc (NI_MAXHOST
);
1190 ERRNOR (ENOMEM
, -1);
1193 ((struct sockaddr
*) &data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0,
1194 NI_NUMERICHOST
) != 0)
1200 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1203 unsigned char *a
= (unsigned char *) &((struct sockaddr_in
*) &data_addr
)->sin_addr
;
1204 unsigned char *p
= (unsigned char *) &port
;
1206 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1207 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1208 p
[0], p
[1]) == COMPLETE
)
1216 * Converts network MSB first order to host byte order (LSB
1217 * first on i386). If we do it earlier, we will run into an
1218 * endianness issue, because the server actually expects to see
1219 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1221 port
= ntohs (port
);
1223 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1224 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
)
1234 /* --------------------------------------------------------------------------------------------- */
1235 /* Initialize a socket for FTP DATA connection */
1238 ftpfs_init_data_socket (struct vfs_class
*me
, struct vfs_s_super
*super
,
1239 struct sockaddr_storage
*data_addr
, socklen_t
* data_addrlen
)
1243 memset (data_addr
, 0, sizeof (struct sockaddr_storage
));
1244 *data_addrlen
= sizeof (struct sockaddr_storage
);
1246 if (SUP
->use_passive_connection
)
1247 result
= getpeername (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1249 result
= getsockname (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1254 switch (data_addr
->ss_family
)
1257 ((struct sockaddr_in
*) data_addr
)->sin_port
= 0;
1260 ((struct sockaddr_in6
*) data_addr
)->sin6_port
= 0;
1263 vfs_print_message (_("ftpfs: invalid address family"));
1264 ERRNOR (EINVAL
, -1);
1267 result
= socket (data_addr
->ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1271 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1278 /* --------------------------------------------------------------------------------------------- */
1279 /* Initialize FTP DATA connection */
1282 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
1284 struct sockaddr_storage data_addr
;
1285 socklen_t data_addrlen
;
1288 * Don't factor socket initialization out of these conditionals,
1289 * because ftpfs_init_data_socket initializes it in different way
1290 * depending on use_passive_connection flag.
1293 /* Try to establish a passive connection first (if requested) */
1294 if (SUP
->use_passive_connection
)
1298 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1302 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1305 vfs_print_message (_("ftpfs: could not setup passive mode"));
1306 SUP
->use_passive_connection
= 0;
1311 /* If passive setup is diabled or failed, fallback to active connections */
1312 if (!SUP
->use_passive_connection
)
1316 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1320 if ((bind (data_sock
, (struct sockaddr
*) &data_addr
, data_addrlen
) == 0) &&
1321 (getsockname (data_sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == 0) &&
1322 (listen (data_sock
, 1) == 0) &&
1323 (ftpfs_setup_active (me
, super
, data_addr
, data_addrlen
) != 0))
1329 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1330 SUP
->use_passive_connection
= SUP
->proxy
!= NULL
? ftpfs_use_passive_connections_over_proxy
:
1331 ftpfs_use_passive_connections
;
1337 /* --------------------------------------------------------------------------------------------- */
1340 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1341 const char *remote
, int isbinary
, int reget
)
1343 struct sockaddr_storage from
;
1345 socklen_t fromlen
= sizeof (from
);
1347 s
= ftpfs_initconn (me
, super
);
1351 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1355 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1361 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1362 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1363 /* WarFtpD can't STORE //filename */
1364 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1365 g_free (remote_path
);
1368 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1372 tty_enable_interrupt_key ();
1373 if (SUP
->use_passive_connection
)
1377 data
= accept (s
, (struct sockaddr
*) &from
, &fromlen
);
1380 ftpfs_errno
= errno
;
1386 tty_disable_interrupt_key ();
1390 /* --------------------------------------------------------------------------------------------- */
1393 ftpfs_linear_abort (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1395 struct vfs_s_super
*super
= FH_SUPER
;
1396 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1399 int dsock
= FH_SOCK
;
1401 SUP
->ctl_connection_busy
= 0;
1403 vfs_print_message (_("ftpfs: aborting transfer."));
1404 if (send (SUP
->sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
))
1406 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno
));
1412 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
)
1414 vfs_print_message (_("ftpfs: abort failed"));
1422 FD_SET (dsock
, &mask
);
1423 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0)
1425 struct timeval start_tim
, tim
;
1426 gettimeofday (&start_tim
, NULL
);
1427 /* flush the remaining data */
1428 while (read (dsock
, buf
, sizeof (buf
)) > 0)
1430 gettimeofday (&tim
, NULL
);
1431 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
)
1433 /* server keeps sending, drop the connection and ftpfs_reconnect */
1435 ftpfs_reconnect (me
, super
);
1442 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1443 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1446 /* --------------------------------------------------------------------------------------------- */
1450 resolve_symlink_without_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1451 struct vfs_s_inode
*dir
)
1453 struct linklist
*flist
;
1454 struct direntry
*fe
, *fel
;
1455 char tmp
[MC_MAXPATHLEN
];
1458 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1459 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
)
1461 /* flist->data->l_stat is alread initialized with 0 */
1463 if (S_ISLNK (fel
->s
.st_mode
) && fel
->linkname
)
1465 if (fel
->linkname
[0] == '/')
1467 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1469 strcpy (tmp
, fel
->linkname
);
1473 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1475 strcpy (tmp
, dir
->remote_path
);
1478 strcat (tmp
+ 1, fel
->linkname
);
1480 for (depth
= 0; depth
< 100; depth
++)
1481 { /* depth protects against recursive symbolic links */
1482 canonicalize_pathname (tmp
);
1483 fe
= _get_file_entry (bucket
, tmp
, 0, 0);
1486 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0)
1488 /* Symlink points to link which isn't resolved, yet. */
1489 if (fe
->linkname
[0] == '/')
1491 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1493 strcpy (tmp
, fe
->linkname
);
1497 /* at this point tmp looks always like this
1498 /directory/filename, i.e. no need to check
1499 strrchr's return value */
1500 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1501 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1503 strcat (tmp
, fe
->linkname
);
1509 fel
->l_stat
= g_new (struct stat
, 1);
1510 if (S_ISLNK (fe
->s
.st_mode
))
1511 *fel
->l_stat
= *fe
->l_stat
;
1513 *fel
->l_stat
= fe
->s
;
1514 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1521 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1524 /* --------------------------------------------------------------------------------------------- */
1527 resolve_symlink_with_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1528 struct vfs_s_inode
*dir
)
1530 char buffer
[2048] = "", *filename
;
1534 struct linklist
*flist
;
1535 struct direntry
*fe
;
1536 int switch_method
= 0;
1538 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1539 if (strchr (dir
->remote_path
, ' '))
1541 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
)
1543 vfs_print_message (_("ftpfs: CWD failed."));
1546 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1549 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", dir
->remote_path
, TYPE_ASCII
, 0);
1553 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1557 fp
= fdopen (sock
, "r");
1561 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1564 tty_enable_interrupt_key ();
1565 flist
= dir
->file_list
->next
;
1570 if (flist
== dir
->file_list
)
1573 flist
= flist
->next
;
1575 while (!S_ISLNK (fe
->s
.st_mode
));
1578 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1580 if (MEDATA
->logfile
)
1582 fputs (buffer
, MEDATA
->logfile
);
1583 fflush (MEDATA
->logfile
);
1585 vfs_die ("This code should be commented out\n");
1586 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
))
1588 int r
= strcmp (fe
->name
, filename
);
1592 if (S_ISLNK (s
.st_mode
))
1594 /* This server doesn't understand LIST -lLa */
1598 fe
->l_stat
= g_new (struct stat
, 1);
1599 if (fe
->l_stat
== NULL
)
1602 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1611 while (fgets (buffer
, sizeof (buffer
), fp
) != NULL
);
1612 tty_disable_interrupt_key ();
1614 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1617 /* --------------------------------------------------------------------------------------------- */
1620 resolve_symlink (struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1622 vfs_print_message (_("Resolving symlink..."));
1624 if (SUP
->strict_rfc959_list_cmd
)
1625 resolve_symlink_without_ls_options (me
, super
, dir
);
1627 resolve_symlink_with_ls_options (me
, super
, dir
);
1631 /* --------------------------------------------------------------------------------------------- */
1634 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1636 struct vfs_s_entry
*ent
;
1637 struct vfs_s_super
*super
= dir
->super
;
1638 int sock
, num_entries
= 0;
1639 char lc_buffer
[BUF_8K
];
1642 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
->strict
== RFC_STRICT
)
1643 || (strchr (remote_path
, ' ') != NULL
);
1646 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1649 RFC_STRICT
? _("(strict rfc959)") : "", cd_first
? _("(chdir first)") : "");
1653 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
)
1655 ftpfs_errno
= ENOENT
;
1656 vfs_print_message (_("ftpfs: CWD failed."));
1661 gettimeofday (&dir
->timestamp
, NULL
);
1662 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1664 if (SUP
->strict
== RFC_STRICT
)
1665 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1667 /* Dirty hack to avoid autoprepending / to . */
1668 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1669 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1672 /* Trailing "/." is necessary if remote_path is a symlink */
1673 char *path
= concat_dir_and_file (remote_path
, ".");
1674 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1681 /* Clear the interrupt flag */
1682 tty_enable_interrupt_key ();
1687 int res
= vfs_s_get_line_interruptible (me
, lc_buffer
, sizeof (lc_buffer
),
1694 me
->verrno
= ECONNRESET
;
1696 tty_disable_interrupt_key ();
1697 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1698 vfs_print_message (_("%s: failure"), me
->name
);
1702 if (MEDATA
->logfile
)
1704 fputs (lc_buffer
, MEDATA
->logfile
);
1705 fputs ("\n", MEDATA
->logfile
);
1706 fflush (MEDATA
->logfile
);
1709 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1710 i
= ent
->ino
->st
.st_nlink
;
1711 if (!vfs_parse_ls_lga (lc_buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
))
1713 vfs_s_free_entry (me
, ent
);
1716 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1718 vfs_s_insert_entry (me
, dir
, ent
);
1722 me
->verrno
= E_REMOTE
;
1723 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1726 if (num_entries
== 0 && cd_first
== 0)
1728 /* The LIST command may produce an empty output. In such scenario
1729 it is not clear whether this is caused by `remote_path' being
1730 a non-existent path or for some other reason (listing emtpy
1731 directory without the -a option, non-readable directory, etc.).
1733 Since `dir_load' is a crucial method, when it comes to determine
1734 whether a given path is a _directory_, the code must try its best
1735 to determine the type of `remote_path'. The only reliable way to
1736 achieve this is trough issuing a CWD command. */
1742 if (SUP
->strict
== RFC_AUTODETECT
)
1743 SUP
->strict
= RFC_DARING
;
1745 vfs_print_message (_("%s: done."), me
->name
);
1749 if (SUP
->strict
== RFC_AUTODETECT
)
1751 /* It's our first attempt to get a directory listing from this
1752 server (UNIX style LIST command) */
1753 SUP
->strict
= RFC_STRICT
;
1754 /* I hate goto, but recursive call needs another 8K on stack */
1755 /* return ftpfs_dir_load (me, dir, remote_path); */
1759 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1760 ERRNOR (EACCES
, -1);
1763 /* --------------------------------------------------------------------------------------------- */
1766 ftpfs_file_store (struct vfs_class
*me
, vfs_file_handler_t
* fh
, char *name
, char *localname
)
1768 int h
, sock
, n_read
, n_written
;
1770 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1775 char lc_buffer
[BUF_8K
];
1778 struct vfs_s_super
*super
= FH_SUPER
;
1779 ftp_fh_data_t
*ftp
= (ftp_fh_data_t
*) fh
->data
;
1781 h
= open (localname
, O_RDONLY
);
1786 ftpfs_open_data_connection (me
, super
, ftp
->append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1787 if (sock
< 0 || fstat (h
, &s
) == -1)
1792 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1795 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1797 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1801 tty_enable_interrupt_key ();
1804 while ((n_read
= read (h
, lc_buffer
, sizeof (lc_buffer
))) == -1)
1808 if (tty_got_interrupt ())
1810 ftpfs_errno
= EINTR
;
1816 ftpfs_errno
= errno
;
1823 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
)
1825 if (n_written
== -1)
1827 if (errno
== EINTR
&& !tty_got_interrupt ())
1831 ftpfs_errno
= errno
;
1835 n_read
-= n_written
;
1837 vfs_print_message ("%s: %" PRIuMAX
"/%" PRIuMAX
,
1838 _("ftpfs: storing file"), (uintmax_t) n_stored
, (uintmax_t) s
.st_size
);
1840 tty_disable_interrupt_key ();
1843 if (ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
)
1847 tty_disable_interrupt_key ();
1850 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1854 /* --------------------------------------------------------------------------------------------- */
1857 ftpfs_linear_start (struct vfs_class
*me
, vfs_file_handler_t
* fh
, off_t offset
)
1861 if (fh
->data
== NULL
)
1862 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
1864 name
= vfs_s_fullpath (me
, fh
->ino
);
1867 FH_SOCK
= ftpfs_open_data_connection (me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1871 fh
->linear
= LS_LINEAR_OPEN
;
1872 ((ftp_super_data_t
*) (FH_SUPER
->data
))->ctl_connection_busy
= 1;
1873 ((ftp_fh_data_t
*) fh
->data
)->append
= 0;
1877 /* --------------------------------------------------------------------------------------------- */
1880 ftpfs_linear_read (struct vfs_class
*me
, vfs_file_handler_t
* fh
, void *buf
, size_t len
)
1883 struct vfs_s_super
*super
= FH_SUPER
;
1885 while ((n
= read (FH_SOCK
, buf
, len
)) < 0)
1887 if ((errno
== EINTR
) && !tty_got_interrupt ())
1893 ftpfs_linear_abort (me
, fh
);
1897 SUP
->ctl_connection_busy
= 0;
1900 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1901 ERRNOR (E_REMOTE
, -1);
1907 /* --------------------------------------------------------------------------------------------- */
1910 ftpfs_linear_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1913 ftpfs_linear_abort (me
, fh
);
1916 /* --------------------------------------------------------------------------------------------- */
1919 ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1925 case VFS_CTL_IS_NOTREADY
:
1930 vfs_die ("You may not do this");
1931 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1934 v
= vfs_s_select_on_two (((ftp_fh_data_t
*) (FH
->data
))->sock
, 0);
1935 return (((v
< 0) && (errno
== EINTR
)) || v
== 0) ? 1 : 0;
1942 /* --------------------------------------------------------------------------------------------- */
1945 ftpfs_send_command (const vfs_path_t
* vpath
, const char *cmd
, int flags
)
1949 struct vfs_s_super
*super
;
1951 vfs_path_element_t
*path_element
;
1953 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1955 path_element
= vfs_path_get_by_index (vpath
, -1);
1957 rpath
= vfs_s_get_path_mangle (vpath
, &super
, 0);
1961 p
= ftpfs_translate_path (path_element
->class, super
, rpath
);
1962 r
= ftpfs_command (path_element
->class, super
, WAIT_REPLY
, cmd
, p
);
1964 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1965 if (flags
& OPT_IGNORE_ERROR
)
1969 path_element
->class->verrno
= EPERM
;
1972 if (flush_directory_cache
)
1973 vfs_s_invalidate (path_element
->class, super
);
1977 /* --------------------------------------------------------------------------------------------- */
1980 ftpfs_chmod (const vfs_path_t
* vpath
, int mode
)
1982 char buf
[BUF_SMALL
];
1985 g_snprintf (buf
, sizeof (buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1987 ret
= ftpfs_send_command (vpath
, buf
, OPT_FLUSH
);
1989 return ftpfs_ignore_chattr_errors
? 0 : ret
;
1992 /* --------------------------------------------------------------------------------------------- */
1995 ftpfs_chown (const vfs_path_t
* vpath
, uid_t owner
, gid_t group
)
2002 ftpfs_errno
= EPERM
;
2005 /* Everyone knows it is not possible to chown remotely, so why bother them.
2006 If someone's root, then copy/move will always try to chown it... */
2014 /* --------------------------------------------------------------------------------------------- */
2017 ftpfs_unlink (const vfs_path_t
* vpath
)
2019 return ftpfs_send_command (vpath
, "DELE /%s", OPT_FLUSH
);
2022 /* --------------------------------------------------------------------------------------------- */
2024 /* Return 1 if path is the same directory as the one we are in now */
2026 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
2030 if (super
->path_element
->path
== NULL
)
2032 return (strcmp (path
, super
->path_element
->path
) == 0);
2035 /* --------------------------------------------------------------------------------------------- */
2038 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
2043 if (!SUP
->cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
2046 p
= ftpfs_translate_path (me
, super
, remote_path
);
2047 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
2054 g_free (super
->path_element
->path
);
2055 super
->path_element
->path
= g_strdup (remote_path
);
2056 SUP
->cwd_deferred
= 0;
2061 /* --------------------------------------------------------------------------------------------- */
2064 ftpfs_rename (const vfs_path_t
* vpath1
, const vfs_path_t
* vpath2
)
2066 ftpfs_send_command (vpath1
, "RNFR /%s", OPT_FLUSH
);
2067 return ftpfs_send_command (vpath2
, "RNTO /%s", OPT_FLUSH
);
2070 /* --------------------------------------------------------------------------------------------- */
2073 ftpfs_mkdir (const vfs_path_t
* vpath
, mode_t mode
)
2075 (void) mode
; /* FIXME: should be used */
2077 return ftpfs_send_command (vpath
, "MKD /%s", OPT_FLUSH
);
2080 /* --------------------------------------------------------------------------------------------- */
2083 ftpfs_rmdir (const vfs_path_t
* vpath
)
2085 return ftpfs_send_command (vpath
, "RMD /%s", OPT_FLUSH
);
2088 /* --------------------------------------------------------------------------------------------- */
2091 ftpfs_fh_open (struct vfs_class
*me
, vfs_file_handler_t
* fh
, int flags
, mode_t mode
)
2097 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
2098 ftp
= (ftp_fh_data_t
*) fh
->data
;
2099 /* File will be written only, so no need to retrieve it from ftp server */
2100 if (((flags
& O_WRONLY
) == O_WRONLY
) && ((flags
& (O_RDONLY
| O_RDWR
)) == 0))
2102 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2109 /* ftpfs_linear_start() called, so data will be written
2110 * to local temporary file and stored to ftp server
2111 * by vfs_s_close later
2113 if (((ftp_super_data_t
*) (FH_SUPER
->data
))->ctl_connection_busy
)
2115 if (!fh
->ino
->localname
)
2117 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
2118 fh
->ino
->ent
->name
);
2122 ftp
->append
= flags
& O_APPEND
;
2126 name
= vfs_s_fullpath (me
, fh
->ino
);
2130 ftpfs_open_data_connection (me
, fh
->ino
->super
,
2131 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
2136 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2140 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
2142 if (fh
->ino
->localname
)
2144 unlink (fh
->ino
->localname
);
2145 g_free (fh
->ino
->localname
);
2146 fh
->ino
->localname
= NULL
;
2151 if (!fh
->ino
->localname
&& vfs_s_retrieve_file (me
, fh
->ino
) == -1)
2153 if (!fh
->ino
->localname
)
2154 vfs_die ("retrieve_file failed to fill in localname");
2162 /* --------------------------------------------------------------------------------------------- */
2165 ftpfs_fh_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
2167 if (fh
->handle
!= -1 && !fh
->ino
->localname
)
2169 ftp_super_data_t
*ftp
= (ftp_super_data_t
*) fh
->ino
->super
->data
;
2173 /* File is stored to destination already, so
2174 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2177 if (ftpfs_get_reply (me
, ftp
->sock
, NULL
, 0) != COMPLETE
)
2179 vfs_s_invalidate (me
, FH_SUPER
);
2187 /* --------------------------------------------------------------------------------------------- */
2190 ftpfs_done (struct vfs_class
*me
)
2194 g_slist_foreach (no_proxy
, (GFunc
) g_free
, NULL
);
2195 g_slist_free (no_proxy
);
2197 g_free (ftpfs_anonymous_passwd
);
2198 g_free (ftpfs_proxy_host
);
2201 /* --------------------------------------------------------------------------------------------- */
2204 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
2208 for (iter
= MEDATA
->supers
; iter
!= NULL
; iter
= g_list_next (iter
))
2210 const struct vfs_s_super
*super
= (const struct vfs_s_super
*) iter
->data
;
2214 g_strconcat ("/#ftp:", super
->path_element
->user
, "@", super
->path_element
->host
, "/",
2215 super
->path_element
->path
, (char *) NULL
);
2221 /* --------------------------------------------------------------------------------------------- */
2224 ftpfs_netrc_next (void)
2228 static const char *const keywords
[] = { "default", "machine",
2229 "login", "password", "passwd", "account", "macdef", NULL
2234 netrcp
= skip_separators (netrcp
);
2235 if (*netrcp
!= '\n')
2244 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++)
2246 if (*netrcp
== '\\')
2253 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
2254 *netrcp
!= ',' && *netrcp
; netrcp
++)
2256 if (*netrcp
== '\\')
2265 for (i
= NETRC_DEFAULT
; keywords
[i
- 1] != NULL
; i
++)
2266 if (strcmp (keywords
[i
- 1], buffer
) == 0)
2269 return NETRC_UNKNOWN
;
2272 /* --------------------------------------------------------------------------------------------- */
2275 ftpfs_netrc_bad_mode (const char *netrcname
)
2277 static int be_angry
= 1;
2280 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077))
2284 message (D_ERROR
, MSG_ERROR
,
2285 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2293 /* --------------------------------------------------------------------------------------------- */
2294 /* Scan .netrc until we find matching "machine" or "default"
2295 * domain is used for additional matching
2296 * No search is done after "default" in compliance with "man netrc"
2297 * Return 0 if found, -1 otherwise */
2300 ftpfs_find_machine (const char *host
, const char *domain
)
2309 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
)
2311 if (keyword
== NETRC_DEFAULT
)
2314 if (keyword
== NETRC_MACDEF
)
2316 /* Scan for an empty line, which concludes "macdef" */
2319 while (*netrcp
&& *netrcp
!= '\n')
2321 if (*netrcp
!= '\n')
2325 while (*netrcp
&& *netrcp
!= '\n');
2329 if (keyword
!= NETRC_MACHINE
)
2332 /* Take machine name */
2333 if (ftpfs_netrc_next () == NETRC_NONE
)
2336 if (g_ascii_strcasecmp (host
, buffer
) != 0)
2338 /* Try adding our domain to short names in .netrc */
2339 const char *host_domain
= strchr (host
, '.');
2343 /* Compare domain part */
2344 if (g_ascii_strcasecmp (host_domain
, domain
) != 0)
2347 /* Compare local part */
2348 if (g_ascii_strncasecmp (host
, buffer
, host_domain
- host
) != 0)
2359 /* --------------------------------------------------------------------------------------------- */
2360 /* Extract login and password from .netrc for the host.
2362 * Returns 0 for success, -1 for error */
2365 ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
2368 char *tmp_pass
= NULL
;
2369 char hostname
[MAXHOSTNAMELEN
];
2372 static struct rupcache
2374 struct rupcache
*next
;
2378 } *rup_cache
= NULL
, *rupp
;
2380 /* Initialize *login and *pass */
2386 /* Look up in the cache first */
2387 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
)
2389 if (!strcmp (host
, rupp
->host
))
2392 *login
= g_strdup (rupp
->login
);
2393 if (pass
&& rupp
->pass
)
2394 *pass
= g_strdup (rupp
->pass
);
2399 /* Load current .netrc */
2400 netrcname
= g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL
);
2401 if (!g_file_get_contents (netrcname
, &netrc
, NULL
, NULL
))
2409 /* Find our own domain name */
2410 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2413 domain
= strchr (hostname
, '.');
2417 /* Scan for "default" and matching "machine" keywords */
2418 ftpfs_find_machine (host
, domain
);
2420 /* Scan for keywords following "default" and "machine" */
2424 keyword
= ftpfs_netrc_next ();
2429 if (ftpfs_netrc_next () == NETRC_NONE
)
2435 /* We have another name already - should not happen */
2442 /* We have login name now */
2443 *login
= g_strdup (buffer
);
2446 case NETRC_PASSWORD
:
2448 if (ftpfs_netrc_next () == NETRC_NONE
)
2454 /* Ignore unsafe passwords */
2455 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2456 && ftpfs_netrc_bad_mode (netrcname
))
2462 /* Remember password. pass may be NULL, so use tmp_pass */
2463 if (tmp_pass
== NULL
)
2464 tmp_pass
= g_strdup (buffer
);
2468 /* "account" is followed by a token which we ignore */
2469 if (ftpfs_netrc_next () == NETRC_NONE
)
2475 /* Ignore account, but warn user anyways */
2476 ftpfs_netrc_bad_mode (netrcname
);
2480 /* Unexpected keyword or end of file */
2492 rupp
= g_new (struct rupcache
, 1);
2493 rupp
->host
= g_strdup (host
);
2494 rupp
->login
= g_strdup (*login
);
2495 rupp
->pass
= g_strdup (tmp_pass
);
2497 rupp
->next
= rup_cache
;
2505 /* --------------------------------------------------------------------------------------------- */
2506 /*** public functions ****************************************************************************/
2507 /* --------------------------------------------------------------------------------------------- */
2509 /** This routine is called as the last step in load_setup */
2511 ftpfs_init_passwd (void)
2513 ftpfs_anonymous_passwd
= load_anon_passwd ();
2514 if (ftpfs_anonymous_passwd
)
2517 /* If there is no anonymous ftp password specified
2518 * then we'll just use anonymous@
2519 * We don't send any other thing because:
2520 * - We want to remain anonymous
2521 * - We want to stop SPAM
2522 * - We don't want to let ftp sites to discriminate by the user,
2525 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
2528 /* --------------------------------------------------------------------------------------------- */
2533 static struct vfs_s_subclass ftpfs_subclass
;
2537 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
2538 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2539 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2540 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2541 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2542 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2543 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2544 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2545 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2546 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2547 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2549 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2550 vfs_ftpfs_ops
.name
= "ftpfs";
2551 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2552 vfs_ftpfs_ops
.prefix
= "ftp:";
2553 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2554 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2555 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2556 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2557 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2558 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2559 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2560 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2561 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2562 vfs_register_class (&vfs_ftpfs_ops
);
2565 /* --------------------------------------------------------------------------------------------- */