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 = mc_build_filename ( qhome (*bucket), remote_path +3-f, NULL );
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 #ifdef HAVE_SYS_PARAM_H
91 #include <sys/param.h>
96 #include <sys/time.h> /* gettimeofday() */
97 #include <inttypes.h> /* uintmax_t */
99 #include "lib/global.h"
100 #include "lib/util.h"
101 #include "lib/mcconfig.h"
103 #include "lib/tty/tty.h" /* enable/disable interrupt key */
104 #include "lib/widget.h" /* message() */
106 #include "src/history.h"
107 #include "src/setup.h" /* for load_anon_passwd */
109 #include "lib/vfs/vfs.h"
110 #include "lib/vfs/utilvfs.h"
111 #include "lib/vfs/netutil.h"
112 #include "lib/vfs/xdirentry.h"
113 #include "lib/vfs/gc.h" /* vfs_stamp_create */
117 /*** global variables ****************************************************************************/
119 /* Delay to retry a connection */
120 int ftpfs_retry_seconds
= 30;
122 /* Method to use to connect to ftp sites */
123 int ftpfs_use_passive_connections
= 1;
124 int ftpfs_use_passive_connections_over_proxy
= 0;
126 /* Method used to get directory listings:
127 * 1: try 'LIST -la <path>', if it fails
128 * fall back to CWD <path>; LIST
129 * 0: always use CWD <path>; LIST
131 int ftpfs_use_unix_list_options
= 1;
133 /* First "CWD <path>", then "LIST -la ." */
134 int ftpfs_first_cd_then_ls
= 1;
136 /* Use the ~/.netrc */
137 int ftpfs_use_netrc
= 1;
139 /* Anonymous setup */
140 char *ftpfs_anonymous_passwd
= NULL
;
141 int ftpfs_directory_timeout
= 900;
144 char *ftpfs_proxy_host
= NULL
;
146 /* wether we have to use proxy by default? */
147 int ftpfs_always_use_proxy
= 0;
149 int ftpfs_ignore_chattr_errors
= 1;
151 /*** file scope macro definitions ****************************************************************/
153 #ifndef MAXHOSTNAMELEN
154 #define MAXHOSTNAMELEN 64
157 #define UPLOAD_ZERO_LENGTH_FILE
158 #define SUP ((ftp_super_data_t *) super->data)
159 #define FH_SOCK ((ftp_fh_data_t *) fh->data)->sock
162 #define INADDR_NONE 0xffffffff
165 #define RFC_AUTODETECT 0
169 /* ftpfs_command wait_flag: */
171 #define WAIT_REPLY 0x01
172 #define WANT_STRING 0x02
174 #define FTP_COMMAND_PORT 21
176 /* some defines only used by ftpfs_changetype */
177 /* These two are valid values for the second parameter */
179 #define TYPE_BINARY 1
181 /* This one is only used to initialize bucket->isbinary, don't use it as
182 second parameter to ftpfs_changetype. */
183 #define TYPE_UNKNOWN -1
185 #define ABORT_TIMEOUT 5
186 /*** file scope type declarations ****************************************************************/
188 #ifndef HAVE_SOCKLEN_T
189 typedef int socklen_t
;
192 /* This should match the keywords[] array below */
210 char *proxy
; /* proxy server, NULL if no proxy */
211 int failed_on_login
; /* used to pass the failure reason to upper levels */
212 int use_passive_connection
;
213 int remote_is_amiga
; /* No leading slash allowed for AmiTCP (Amiga) */
215 int cwd_deferred
; /* current_directory was changed but CWD command hasn't
217 int strict
; /* ftp server doesn't understand
218 * "LIST -la <path>"; use "CWD <path>"/
221 int ctl_connection_busy
;
230 /*** file scope variables ************************************************************************/
232 static int ftpfs_errno
;
235 #ifdef FIXME_LATER_ALIGATOR
236 static struct linklist
*connections_list
;
239 static char reply_str
[80];
241 static struct vfs_class vfs_ftpfs_ops
;
243 static GSList
*no_proxy
;
245 static char buffer
[BUF_MEDIUM
];
247 static const char *netrcp
;
249 /*** file scope functions ************************************************************************/
250 /* --------------------------------------------------------------------------------------------- */
252 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
253 Translate a Unix path, i.e. MC's internal path representation (e.g.
254 /somedir/somefile) to a path valid for the remote server. Every path
255 transfered to the remote server has to be mangled by this function
256 right prior to sending it.
257 Currently only Amiga ftp servers are handled in a special manner.
259 When the remote server is an amiga:
260 a) strip leading slash if necesarry
261 b) replace first occurance of ":/" with ":"
262 c) strip trailing "/."
265 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
,
266 const char *remote_path
);
267 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
,
268 const char *fmt
, ...) __attribute__ ((format (__printf__
, 4, 5)));
269 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
270 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
271 const char *netrcpass
);
272 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
274 /* --------------------------------------------------------------------------------------------- */
277 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
279 if (!SUP
->remote_is_amiga
)
280 return g_strdup (remote_path
);
287 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
288 fflush (MEDATA
->logfile
);
291 /* strip leading slash(es) */
292 while (*remote_path
== '/')
296 * Don't change "/" into "", e.g. "CWD " would be
299 if (*remote_path
== '\0')
300 return g_strdup (".");
302 ret
= g_strdup (remote_path
);
304 /* replace first occurance of ":/" with ":" */
305 p
= strchr (ret
, ':');
306 if ((p
!= NULL
) && (*(p
+ 1) == '/'))
307 memmove (p
+ 1, p
+ 2, strlen (p
+ 2) + 1);
309 /* strip trailing "/." */
310 p
= strrchr (ret
, '/');
311 if ((p
!= NULL
) && (*(p
+ 1) == '.') && (*(p
+ 2) == '\0'))
318 /* --------------------------------------------------------------------------------------------- */
319 /** Extract the hostname and username from the path */
321 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
322 * ftp://sunsite.unc.edu/pub/linux
323 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
324 * ftp://tsx-11.mit.edu:8192/
325 * ftp://joe@foo.edu:11321/private
326 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
330 static vfs_path_element_t
*
331 ftpfs_correct_url_parameters (const vfs_path_element_t
* velement
)
333 vfs_path_element_t
*path_element
= vfs_path_element_clone (velement
);
335 if (path_element
->port
== 0)
336 path_element
->port
= FTP_COMMAND_PORT
;
338 if (path_element
->user
== NULL
)
340 /* Look up user and password in netrc */
342 ftpfs_netrc_lookup (path_element
->host
, &path_element
->user
, &path_element
->password
);
344 if (path_element
->user
== NULL
)
345 path_element
->user
= g_strdup ("anonymous");
347 /* Look up password in netrc for known user */
348 if (ftpfs_use_netrc
&& path_element
->user
!= NULL
&& path_element
->password
!= NULL
)
350 char *new_user
= NULL
;
351 char *new_passwd
= NULL
;
353 ftpfs_netrc_lookup (path_element
->host
, &new_user
, &new_passwd
);
355 /* If user is different, remove password */
356 if (new_user
!= NULL
&& strcmp (path_element
->user
, new_user
) != 0)
358 g_free (path_element
->password
);
359 path_element
->password
= NULL
;
369 /* --------------------------------------------------------------------------------------------- */
370 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
373 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
380 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
387 switch (sscanf (answer
, "%d", &code
))
391 g_strlcpy (string_buf
, answer
, string_len
);
395 if (answer
[3] == '-')
399 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
406 if ((sscanf (answer
, "%d", &i
) > 0) && (code
== i
) && (answer
[3] == ' '))
411 g_strlcpy (string_buf
, answer
, string_len
);
417 /* --------------------------------------------------------------------------------------------- */
420 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
424 sock
= ftpfs_open_socket (me
, super
);
427 char *cwdir
= super
->path_element
->path
;
431 super
->path_element
->path
= NULL
;
434 if (ftpfs_login_server (me
, super
, super
->path_element
->password
) != 0)
438 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
440 return sock
== COMPLETE
? 1 : 0;
443 super
->path_element
->path
= cwdir
;
449 /* --------------------------------------------------------------------------------------------- */
452 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
,
458 static int retry
= 0;
459 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
462 cmdstr
= g_strdup_vprintf (fmt
, ap
);
465 cmdlen
= strlen (cmdstr
);
466 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
467 strcpy (cmdstr
+ cmdlen
, "\r\n");
472 if (strncmp (cmdstr
, "PASS ", 5) == 0)
474 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
479 ret
= fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
482 fflush (MEDATA
->logfile
);
486 tty_enable_interrupt_key ();
487 status
= write (SUP
->sock
, cmdstr
, cmdlen
);
494 { /* Remote server has closed connection */
498 status
= ftpfs_reconnect (me
, super
);
500 if (status
&& (write (SUP
->sock
, cmdstr
, cmdlen
) > 0))
509 tty_disable_interrupt_key ();
514 tty_disable_interrupt_key ();
518 status
= ftpfs_get_reply (me
, SUP
->sock
,
519 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
520 sizeof (reply_str
) - 1);
521 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
525 status
= ftpfs_reconnect (me
, super
);
527 if (status
&& (write (SUP
->sock
, cmdstr
, cmdlen
) > 0))
540 /* --------------------------------------------------------------------------------------------- */
543 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
547 vfs_print_message (_("ftpfs: Disconnecting from %s"), super
->path_element
->host
);
548 ftpfs_command (me
, super
, NONE
, "QUIT");
551 g_free (super
->data
);
555 /* --------------------------------------------------------------------------------------------- */
558 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
560 if (binary
!= SUP
->isbinary
)
562 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
564 SUP
->isbinary
= binary
;
569 /* --------------------------------------------------------------------------------------------- */
570 /* This routine logs the user in */
573 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
)
577 char *name
; /* login user name */
579 char reply_string
[BUF_MEDIUM
];
581 SUP
->isbinary
= TYPE_UNKNOWN
;
583 if (super
->path_element
->password
!= NULL
) /* explicit password */
584 op
= g_strdup (super
->path_element
->password
);
585 else if (netrcpass
!= NULL
) /* password from netrc */
586 op
= g_strdup (netrcpass
);
587 else if (strcmp (super
->path_element
->user
, "anonymous") == 0
588 || strcmp (super
->path_element
->user
, "ftp") == 0)
590 if (ftpfs_anonymous_passwd
== NULL
) /* default anonymous password */
591 ftpfs_init_passwd ();
592 op
= g_strdup (ftpfs_anonymous_passwd
);
599 p
= g_strdup_printf (_("FTP: Password required for %s"), super
->path_element
->user
);
600 op
= vfs_get_password (p
);
604 super
->path_element
->password
= g_strdup (op
);
607 if (!anon
|| MEDATA
->logfile
)
611 pass
= g_strconcat ("-", op
, (char *) NULL
);
615 /* Proxy server accepts: username@host-we-want-to-connect */
618 g_strconcat (super
->path_element
->user
, "@",
619 super
->path_element
->host
[0] ==
620 '!' ? super
->path_element
->host
+ 1 : super
->path_element
->host
,
623 name
= g_strdup (super
->path_element
->user
);
625 if (ftpfs_get_reply (me
, SUP
->sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
)
629 reply_up
= g_ascii_strup (reply_string
, -1);
630 SUP
->remote_is_amiga
= strstr (reply_up
, "AMIGA") != 0;
631 if (strstr (reply_up
, " SPFTP/1.0.0000 SERVER ")) /* handles `LIST -la` in a weird way */
632 SUP
->strict
= RFC_STRICT
;
637 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n", SUP
->remote_is_amiga
);
638 fflush (MEDATA
->logfile
);
641 vfs_print_message (_("ftpfs: sending login name"));
643 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
))
646 vfs_print_message (_("ftpfs: sending user password"));
647 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
648 if (code
== CONTINUE
)
652 p
= g_strdup_printf (_("FTP: Account required for user %s"),
653 super
->path_element
->user
);
654 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
658 vfs_print_message (_("ftpfs: sending user account"));
659 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
662 if (code
!= COMPLETE
)
667 vfs_print_message (_("ftpfs: logged in"));
668 wipe_password (pass
);
673 SUP
->failed_on_login
= 1;
674 wipe_password (super
->path_element
->password
);
675 super
->path_element
->password
= NULL
;
681 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
682 super
->path_element
->user
);
685 wipe_password (pass
);
690 /* --------------------------------------------------------------------------------------------- */
693 ftpfs_load_no_proxy_list (void)
695 /* FixMe: shouldn't be hardcoded!!! */
696 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
700 static char *mc_file
= NULL
;
702 mc_file
= g_build_filename (mc_global
.sysconfig_dir
, "mc.no_proxy", (char *) NULL
);
703 if (exist_file (mc_file
))
705 npf
= fopen (mc_file
, "r");
708 while (fgets (s
, sizeof (s
), npf
) != NULL
)
710 p
= strchr (s
, '\n');
711 if (p
== NULL
) /* skip bogus entries */
713 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
721 no_proxy
= g_slist_prepend (no_proxy
, g_strdup (s
));
730 /* --------------------------------------------------------------------------------------------- */
731 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
734 ftpfs_check_proxy (const char *host
)
738 if (ftpfs_proxy_host
== NULL
|| *ftpfs_proxy_host
== '\0' || host
== NULL
|| *host
== '\0')
739 return 0; /* sanity check */
744 if (!ftpfs_always_use_proxy
)
747 if (strchr (host
, '.') == NULL
)
750 ftpfs_load_no_proxy_list ();
751 for (npe
= no_proxy
; npe
!= NULL
; npe
= g_slist_next (npe
))
753 const char *domain
= (const char *) npe
->data
;
755 if (domain
[0] == '.')
757 size_t ld
= strlen (domain
);
758 size_t lh
= strlen (host
);
760 while (ld
!= 0 && lh
!= 0 && host
[lh
- 1] == domain
[ld
- 1])
769 else if (g_ascii_strcasecmp (host
, domain
) == 0)
776 /* --------------------------------------------------------------------------------------------- */
779 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
781 vfs_path_element_t
*path_element
;
783 path_element
= vfs_url_split (proxy
, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
784 *host
= g_strdup (path_element
->host
);
785 *port
= path_element
->port
;
786 vfs_path_element_free (path_element
);
789 /* --------------------------------------------------------------------------------------------- */
792 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
794 struct addrinfo hints
, *res
, *curr_res
;
803 /* Use a proxy host? */
804 host
= g_strdup (super
->path_element
->host
);
806 if (host
== NULL
|| *host
== '\0')
808 vfs_print_message (_("ftpfs: Invalid host name."));
809 ftpfs_errno
= EINVAL
;
814 /* Hosts to connect to that start with a ! should use proxy */
815 tmp_port
= super
->path_element
->port
;
817 if (SUP
->proxy
!= NULL
)
818 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
820 g_snprintf (port
, sizeof (port
), "%hu", (unsigned short) tmp_port
);
828 tty_enable_interrupt_key (); /* clear the interrupt flag */
830 memset (&hints
, 0, sizeof (struct addrinfo
));
831 hints
.ai_family
= AF_UNSPEC
;
832 hints
.ai_socktype
= SOCK_STREAM
;
835 /* By default, only look up addresses using address types for
836 * which a local interface is configured (i.e. no IPv6 if no IPv6
837 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
838 hints
.ai_flags
= AI_ADDRCONFIG
;
841 e
= getaddrinfo (host
, port
, &hints
, &res
);
844 if (e
== EAI_BADFLAGS
)
846 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
848 e
= getaddrinfo (host
, port
, &hints
, &res
);
856 tty_disable_interrupt_key ();
857 vfs_print_message (_("ftpfs: %s"), gai_strerror (e
));
859 ftpfs_errno
= EINVAL
;
863 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
)
865 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
869 if (curr_res
->ai_next
!= NULL
)
872 tty_disable_interrupt_key ();
873 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno
));
880 vfs_print_message (_("ftpfs: making connection to %s"), host
);
884 if (connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0)
890 if (errno
== EINTR
&& tty_got_interrupt ())
891 vfs_print_message (_("ftpfs: connection interrupted by user"));
892 else if (res
->ai_next
== NULL
)
893 vfs_print_message (_("ftpfs: connection to server failed: %s"),
894 unix_error_string (errno
));
899 tty_disable_interrupt_key ();
904 tty_disable_interrupt_key ();
908 /* --------------------------------------------------------------------------------------------- */
911 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
913 int retry_seconds
= 0;
916 /* We do not want to use the passive if we are using proxies */
918 SUP
->use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
922 SUP
->failed_on_login
= 0;
924 SUP
->sock
= ftpfs_open_socket (me
, super
);
928 if (ftpfs_login_server (me
, super
, NULL
) != 0)
930 /* Logged in, no need to retry the connection */
935 if (!SUP
->failed_on_login
)
938 /* Close only the socket descriptor */
941 if (ftpfs_retry_seconds
!= 0)
943 retry_seconds
= ftpfs_retry_seconds
;
944 tty_enable_interrupt_key ();
945 for (count_down
= retry_seconds
; count_down
; count_down
--)
947 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
950 if (tty_got_interrupt ())
952 /* ftpfs_errno = E; */
953 tty_disable_interrupt_key ();
957 tty_disable_interrupt_key ();
961 while (retry_seconds
!= 0);
966 /* --------------------------------------------------------------------------------------------- */
969 ftpfs_open_archive (struct vfs_s_super
*super
,
970 const vfs_path_t
* vpath
, const vfs_path_element_t
* vpath_element
)
974 super
->data
= g_new0 (ftp_super_data_t
, 1);
976 super
->path_element
= ftpfs_correct_url_parameters (vpath_element
);
978 if (ftpfs_check_proxy (super
->path_element
->host
))
979 SUP
->proxy
= ftpfs_proxy_host
;
980 SUP
->use_passive_connection
= ftpfs_use_passive_connections
;
981 SUP
->strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
982 SUP
->isbinary
= TYPE_UNKNOWN
;
983 SUP
->remote_is_amiga
= 0;
984 super
->name
= g_strdup ("/");
986 vfs_s_new_inode (vpath_element
->class, super
,
987 vfs_s_default_stat (vpath_element
->class, S_IFDIR
| 0755));
989 return ftpfs_open_archive_int (vpath_element
->class, super
);
992 /* --------------------------------------------------------------------------------------------- */
995 ftpfs_archive_same (const vfs_path_element_t
* vpath_element
, struct vfs_s_super
*super
,
996 const vfs_path_t
* vpath
, void *cookie
)
998 vfs_path_element_t
*path_element
;
1004 path_element
= ftpfs_correct_url_parameters (vpath_element
);
1006 result
= ((strcmp (path_element
->host
, super
->path_element
->host
) == 0)
1007 && (strcmp (path_element
->user
, super
->path_element
->user
) == 0)
1008 && (path_element
->port
== super
->path_element
->port
)) ? 1 : 0;
1010 vfs_path_element_free (path_element
);
1014 /* --------------------------------------------------------------------------------------------- */
1015 /* Setup Passive PASV FTP connection */
1018 ftpfs_setup_passive_pasv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1019 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1023 int xa
, xb
, xc
, xd
, xe
, xf
;
1025 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
1028 /* Parse remote parameters */
1029 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
1033 if (!isdigit ((unsigned char) *c
))
1035 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
1038 n
[0] = (unsigned char) xa
;
1039 n
[1] = (unsigned char) xb
;
1040 n
[2] = (unsigned char) xc
;
1041 n
[3] = (unsigned char) xd
;
1042 n
[4] = (unsigned char) xe
;
1043 n
[5] = (unsigned char) xf
;
1045 memcpy (&(((struct sockaddr_in
*) sa
)->sin_addr
.s_addr
), (void *) n
, 4);
1046 memcpy (&(((struct sockaddr_in
*) sa
)->sin_port
), (void *) &n
[4], 2);
1048 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
1054 /* --------------------------------------------------------------------------------------------- */
1055 /* Setup Passive EPSV FTP connection */
1058 ftpfs_setup_passive_epsv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1059 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1064 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") != COMPLETE
)
1068 c
= strchr (reply_str
, '|');
1077 if (port
< 0 || port
> 65535)
1079 port
= htons (port
);
1081 switch (sa
->ss_family
)
1084 ((struct sockaddr_in
*) sa
)->sin_port
= port
;
1087 ((struct sockaddr_in6
*) sa
)->sin6_port
= port
;
1091 return (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0) ? 0 : 1;
1094 /* --------------------------------------------------------------------------------------------- */
1095 /* Setup Passive ftp connection, we use it for source routed connections */
1098 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
1099 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1101 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1102 if (sa
->ss_family
== AF_INET
)
1104 if (!ftpfs_setup_passive_pasv (me
, super
, my_socket
, sa
, salen
))
1105 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1106 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1109 /* It's IPV6, so EPSV is our only hope */
1112 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1119 /* --------------------------------------------------------------------------------------------- */
1120 /* Setup Active PORT or EPRT FTP connection */
1123 ftpfs_setup_active (struct vfs_class
*me
, struct vfs_s_super
*super
,
1124 struct sockaddr_storage data_addr
, socklen_t data_addrlen
)
1126 unsigned short int port
;
1130 switch (data_addr
.ss_family
)
1134 port
= ((struct sockaddr_in
*) &data_addr
)->sin_port
;
1138 port
= ((struct sockaddr_in6
*) &data_addr
)->sin6_port
;
1140 /* Not implemented */
1145 addr
= g_try_malloc (NI_MAXHOST
);
1147 ERRNOR (ENOMEM
, -1);
1150 ((struct sockaddr
*) &data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0,
1151 NI_NUMERICHOST
) != 0)
1157 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1160 unsigned char *a
= (unsigned char *) &((struct sockaddr_in
*) &data_addr
)->sin_addr
;
1161 unsigned char *p
= (unsigned char *) &port
;
1163 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1164 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1165 p
[0], p
[1]) == COMPLETE
)
1173 * Converts network MSB first order to host byte order (LSB
1174 * first on i386). If we do it earlier, we will run into an
1175 * endianness issue, because the server actually expects to see
1176 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1178 port
= ntohs (port
);
1180 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1181 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
)
1191 /* --------------------------------------------------------------------------------------------- */
1192 /* Initialize a socket for FTP DATA connection */
1195 ftpfs_init_data_socket (struct vfs_class
*me
, struct vfs_s_super
*super
,
1196 struct sockaddr_storage
*data_addr
, socklen_t
* data_addrlen
)
1200 memset (data_addr
, 0, sizeof (struct sockaddr_storage
));
1201 *data_addrlen
= sizeof (struct sockaddr_storage
);
1203 if (SUP
->use_passive_connection
)
1204 result
= getpeername (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1206 result
= getsockname (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1211 switch (data_addr
->ss_family
)
1214 ((struct sockaddr_in
*) data_addr
)->sin_port
= 0;
1217 ((struct sockaddr_in6
*) data_addr
)->sin6_port
= 0;
1220 vfs_print_message (_("ftpfs: invalid address family"));
1221 ERRNOR (EINVAL
, -1);
1224 result
= socket (data_addr
->ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1228 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1235 /* --------------------------------------------------------------------------------------------- */
1236 /* Initialize FTP DATA connection */
1239 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
1241 struct sockaddr_storage data_addr
;
1242 socklen_t data_addrlen
;
1245 * Don't factor socket initialization out of these conditionals,
1246 * because ftpfs_init_data_socket initializes it in different way
1247 * depending on use_passive_connection flag.
1250 /* Try to establish a passive connection first (if requested) */
1251 if (SUP
->use_passive_connection
)
1255 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1259 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1262 vfs_print_message (_("ftpfs: could not setup passive mode"));
1263 SUP
->use_passive_connection
= 0;
1268 /* If passive setup is diabled or failed, fallback to active connections */
1269 if (!SUP
->use_passive_connection
)
1273 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1277 if ((bind (data_sock
, (struct sockaddr
*) &data_addr
, data_addrlen
) == 0) &&
1278 (getsockname (data_sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == 0) &&
1279 (listen (data_sock
, 1) == 0) &&
1280 (ftpfs_setup_active (me
, super
, data_addr
, data_addrlen
) != 0))
1286 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1287 SUP
->use_passive_connection
= SUP
->proxy
!= NULL
? ftpfs_use_passive_connections_over_proxy
:
1288 ftpfs_use_passive_connections
;
1294 /* --------------------------------------------------------------------------------------------- */
1297 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1298 const char *remote
, int isbinary
, int reget
)
1300 struct sockaddr_storage from
;
1302 socklen_t fromlen
= sizeof (from
);
1304 s
= ftpfs_initconn (me
, super
);
1308 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1312 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1318 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1319 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1320 /* WarFtpD can't STORE //filename */
1321 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1322 g_free (remote_path
);
1325 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1329 tty_enable_interrupt_key ();
1330 if (SUP
->use_passive_connection
)
1334 data
= accept (s
, (struct sockaddr
*) &from
, &fromlen
);
1337 ftpfs_errno
= errno
;
1343 tty_disable_interrupt_key ();
1347 /* --------------------------------------------------------------------------------------------- */
1350 ftpfs_linear_abort (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1352 struct vfs_s_super
*super
= FH_SUPER
;
1353 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1356 int dsock
= FH_SOCK
;
1358 SUP
->ctl_connection_busy
= 0;
1360 vfs_print_message (_("ftpfs: aborting transfer."));
1361 if (send (SUP
->sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
))
1363 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno
));
1369 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
)
1371 vfs_print_message (_("ftpfs: abort failed"));
1379 FD_SET (dsock
, &mask
);
1380 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0)
1382 struct timeval start_tim
, tim
;
1383 gettimeofday (&start_tim
, NULL
);
1384 /* flush the remaining data */
1385 while (read (dsock
, buf
, sizeof (buf
)) > 0)
1387 gettimeofday (&tim
, NULL
);
1388 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
)
1390 /* server keeps sending, drop the connection and ftpfs_reconnect */
1392 ftpfs_reconnect (me
, super
);
1399 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1400 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1403 /* --------------------------------------------------------------------------------------------- */
1407 resolve_symlink_without_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1408 struct vfs_s_inode
*dir
)
1410 struct linklist
*flist
;
1411 struct direntry
*fe
, *fel
;
1412 char tmp
[MC_MAXPATHLEN
];
1415 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1416 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
)
1418 /* flist->data->l_stat is alread initialized with 0 */
1420 if (S_ISLNK (fel
->s
.st_mode
) && fel
->linkname
)
1422 if (fel
->linkname
[0] == '/')
1424 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1426 strcpy (tmp
, fel
->linkname
);
1430 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1432 strcpy (tmp
, dir
->remote_path
);
1435 strcat (tmp
+ 1, fel
->linkname
);
1437 for (depth
= 0; depth
< 100; depth
++)
1438 { /* depth protects against recursive symbolic links */
1439 canonicalize_pathname (tmp
);
1440 fe
= _get_file_entry (bucket
, tmp
, 0, 0);
1443 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0)
1445 /* Symlink points to link which isn't resolved, yet. */
1446 if (fe
->linkname
[0] == '/')
1448 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1450 strcpy (tmp
, fe
->linkname
);
1454 /* at this point tmp looks always like this
1455 /directory/filename, i.e. no need to check
1456 strrchr's return value */
1457 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1458 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1460 strcat (tmp
, fe
->linkname
);
1466 fel
->l_stat
= g_new (struct stat
, 1);
1467 if (S_ISLNK (fe
->s
.st_mode
))
1468 *fel
->l_stat
= *fe
->l_stat
;
1470 *fel
->l_stat
= fe
->s
;
1471 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1478 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1481 /* --------------------------------------------------------------------------------------------- */
1484 resolve_symlink_with_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1485 struct vfs_s_inode
*dir
)
1487 char buffer
[2048] = "", *filename
;
1491 struct linklist
*flist
;
1492 struct direntry
*fe
;
1493 int switch_method
= 0;
1495 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1496 if (strchr (dir
->remote_path
, ' '))
1498 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
)
1500 vfs_print_message (_("ftpfs: CWD failed."));
1503 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1506 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", dir
->remote_path
, TYPE_ASCII
, 0);
1510 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1514 fp
= fdopen (sock
, "r");
1518 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1521 tty_enable_interrupt_key ();
1522 flist
= dir
->file_list
->next
;
1527 if (flist
== dir
->file_list
)
1530 flist
= flist
->next
;
1532 while (!S_ISLNK (fe
->s
.st_mode
));
1535 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1537 if (MEDATA
->logfile
)
1539 fputs (buffer
, MEDATA
->logfile
);
1540 fflush (MEDATA
->logfile
);
1542 vfs_die ("This code should be commented out\n");
1543 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
))
1545 int r
= strcmp (fe
->name
, filename
);
1549 if (S_ISLNK (s
.st_mode
))
1551 /* This server doesn't understand LIST -lLa */
1555 fe
->l_stat
= g_new (struct stat
, 1);
1556 if (fe
->l_stat
== NULL
)
1559 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1568 while (fgets (buffer
, sizeof (buffer
), fp
) != NULL
);
1569 tty_disable_interrupt_key ();
1571 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1574 /* --------------------------------------------------------------------------------------------- */
1577 resolve_symlink (struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1579 vfs_print_message (_("Resolving symlink..."));
1581 if (SUP
->strict_rfc959_list_cmd
)
1582 resolve_symlink_without_ls_options (me
, super
, dir
);
1584 resolve_symlink_with_ls_options (me
, super
, dir
);
1588 /* --------------------------------------------------------------------------------------------- */
1591 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1593 struct vfs_s_entry
*ent
;
1594 struct vfs_s_super
*super
= dir
->super
;
1595 int sock
, num_entries
= 0;
1596 char lc_buffer
[BUF_8K
];
1599 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
->strict
== RFC_STRICT
)
1600 || (strchr (remote_path
, ' ') != NULL
);
1603 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1606 RFC_STRICT
? _("(strict rfc959)") : "", cd_first
? _("(chdir first)") : "");
1610 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
)
1612 ftpfs_errno
= ENOENT
;
1613 vfs_print_message (_("ftpfs: CWD failed."));
1618 gettimeofday (&dir
->timestamp
, NULL
);
1619 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1621 if (SUP
->strict
== RFC_STRICT
)
1622 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1624 /* Dirty hack to avoid autoprepending / to . */
1625 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1626 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1631 /* Trailing "/." is necessary if remote_path is a symlink */
1632 path
= mc_build_filename (remote_path
, ".", NULL
);
1633 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1640 /* Clear the interrupt flag */
1641 tty_enable_interrupt_key ();
1643 vfs_parse_ls_lga_init ();
1647 size_t count_spaces
= 0;
1648 int res
= vfs_s_get_line_interruptible (me
, lc_buffer
, sizeof (lc_buffer
),
1655 me
->verrno
= ECONNRESET
;
1657 tty_disable_interrupt_key ();
1658 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1659 vfs_print_message (_("%s: failure"), me
->name
);
1663 if (MEDATA
->logfile
)
1665 fputs (lc_buffer
, MEDATA
->logfile
);
1666 fputs ("\n", MEDATA
->logfile
);
1667 fflush (MEDATA
->logfile
);
1670 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1671 i
= ent
->ino
->st
.st_nlink
;
1672 if (!vfs_parse_ls_lga
1673 (lc_buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
, &count_spaces
))
1675 vfs_s_free_entry (me
, ent
);
1678 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1680 vfs_s_store_filename_leading_spaces (ent
, count_spaces
);
1681 vfs_s_insert_entry (me
, dir
, ent
);
1685 me
->verrno
= E_REMOTE
;
1686 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1689 if (num_entries
== 0 && cd_first
== 0)
1691 /* The LIST command may produce an empty output. In such scenario
1692 it is not clear whether this is caused by `remote_path' being
1693 a non-existent path or for some other reason (listing emtpy
1694 directory without the -a option, non-readable directory, etc.).
1696 Since `dir_load' is a crucial method, when it comes to determine
1697 whether a given path is a _directory_, the code must try its best
1698 to determine the type of `remote_path'. The only reliable way to
1699 achieve this is trough issuing a CWD command. */
1705 vfs_s_normalize_filename_leading_spaces (dir
, vfs_parse_ls_lga_get_final_spaces ());
1707 if (SUP
->strict
== RFC_AUTODETECT
)
1708 SUP
->strict
= RFC_DARING
;
1710 vfs_print_message (_("%s: done."), me
->name
);
1714 if (SUP
->strict
== RFC_AUTODETECT
)
1716 /* It's our first attempt to get a directory listing from this
1717 server (UNIX style LIST command) */
1718 SUP
->strict
= RFC_STRICT
;
1719 /* I hate goto, but recursive call needs another 8K on stack */
1720 /* return ftpfs_dir_load (me, dir, remote_path); */
1724 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1725 ERRNOR (EACCES
, -1);
1728 /* --------------------------------------------------------------------------------------------- */
1731 ftpfs_file_store (struct vfs_class
*me
, vfs_file_handler_t
* fh
, char *name
, char *localname
)
1733 int h
, sock
, n_read
, n_written
;
1735 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1740 char lc_buffer
[BUF_8K
];
1743 struct vfs_s_super
*super
= FH_SUPER
;
1744 ftp_fh_data_t
*ftp
= (ftp_fh_data_t
*) fh
->data
;
1746 h
= open (localname
, O_RDONLY
);
1751 ftpfs_open_data_connection (me
, super
, ftp
->append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1752 if (sock
< 0 || fstat (h
, &s
) == -1)
1757 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1760 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1762 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1766 tty_enable_interrupt_key ();
1769 while ((n_read
= read (h
, lc_buffer
, sizeof (lc_buffer
))) == -1)
1773 if (tty_got_interrupt ())
1775 ftpfs_errno
= EINTR
;
1781 ftpfs_errno
= errno
;
1788 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
)
1790 if (n_written
== -1)
1792 if (errno
== EINTR
&& !tty_got_interrupt ())
1796 ftpfs_errno
= errno
;
1800 n_read
-= n_written
;
1802 vfs_print_message ("%s: %" PRIuMAX
"/%" PRIuMAX
,
1803 _("ftpfs: storing file"), (uintmax_t) n_stored
, (uintmax_t) s
.st_size
);
1805 tty_disable_interrupt_key ();
1808 if (ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
)
1812 tty_disable_interrupt_key ();
1815 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1819 /* --------------------------------------------------------------------------------------------- */
1822 ftpfs_linear_start (struct vfs_class
*me
, vfs_file_handler_t
* fh
, off_t offset
)
1826 if (fh
->data
== NULL
)
1827 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
1829 name
= vfs_s_fullpath (me
, fh
->ino
);
1832 FH_SOCK
= ftpfs_open_data_connection (me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1836 fh
->linear
= LS_LINEAR_OPEN
;
1837 ((ftp_super_data_t
*) (FH_SUPER
->data
))->ctl_connection_busy
= 1;
1838 ((ftp_fh_data_t
*) fh
->data
)->append
= 0;
1842 /* --------------------------------------------------------------------------------------------- */
1845 ftpfs_linear_read (struct vfs_class
*me
, vfs_file_handler_t
* fh
, void *buf
, size_t len
)
1848 struct vfs_s_super
*super
= FH_SUPER
;
1850 while ((n
= read (FH_SOCK
, buf
, len
)) < 0)
1852 if ((errno
== EINTR
) && !tty_got_interrupt ())
1858 ftpfs_linear_abort (me
, fh
);
1862 SUP
->ctl_connection_busy
= 0;
1865 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1866 ERRNOR (E_REMOTE
, -1);
1872 /* --------------------------------------------------------------------------------------------- */
1875 ftpfs_linear_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1878 ftpfs_linear_abort (me
, fh
);
1881 /* --------------------------------------------------------------------------------------------- */
1884 ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1890 case VFS_CTL_IS_NOTREADY
:
1895 vfs_die ("You may not do this");
1896 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1899 v
= vfs_s_select_on_two (((ftp_fh_data_t
*) (FH
->data
))->sock
, 0);
1900 return (((v
< 0) && (errno
== EINTR
)) || v
== 0) ? 1 : 0;
1907 /* --------------------------------------------------------------------------------------------- */
1910 ftpfs_send_command (const vfs_path_t
* vpath
, const char *cmd
, int flags
)
1914 struct vfs_s_super
*super
;
1916 const vfs_path_element_t
*path_element
;
1917 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1919 path_element
= vfs_path_get_by_index (vpath
, -1);
1921 rpath
= vfs_s_get_path (vpath
, &super
, 0);
1925 p
= ftpfs_translate_path (path_element
->class, super
, rpath
);
1926 r
= ftpfs_command (path_element
->class, super
, WAIT_REPLY
, cmd
, p
);
1928 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1929 if (flags
& OPT_IGNORE_ERROR
)
1933 path_element
->class->verrno
= EPERM
;
1936 if (flush_directory_cache
)
1937 vfs_s_invalidate (path_element
->class, super
);
1941 /* --------------------------------------------------------------------------------------------- */
1944 ftpfs_chmod (const vfs_path_t
* vpath
, mode_t mode
)
1946 char buf
[BUF_SMALL
];
1949 g_snprintf (buf
, sizeof (buf
), "SITE CHMOD %4.4o /%%s", (int) (mode
& 07777));
1951 ret
= ftpfs_send_command (vpath
, buf
, OPT_FLUSH
);
1953 return ftpfs_ignore_chattr_errors
? 0 : ret
;
1956 /* --------------------------------------------------------------------------------------------- */
1959 ftpfs_chown (const vfs_path_t
* vpath
, uid_t owner
, gid_t group
)
1966 ftpfs_errno
= EPERM
;
1969 /* Everyone knows it is not possible to chown remotely, so why bother them.
1970 If someone's root, then copy/move will always try to chown it... */
1978 /* --------------------------------------------------------------------------------------------- */
1981 ftpfs_unlink (const vfs_path_t
* vpath
)
1983 return ftpfs_send_command (vpath
, "DELE /%s", OPT_FLUSH
);
1986 /* --------------------------------------------------------------------------------------------- */
1988 /* Return 1 if path is the same directory as the one we are in now */
1990 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1994 if (super
->path_element
->path
== NULL
)
1996 return (strcmp (path
, super
->path_element
->path
) == 0);
1999 /* --------------------------------------------------------------------------------------------- */
2002 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
2007 if (!SUP
->cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
2010 p
= ftpfs_translate_path (me
, super
, remote_path
);
2011 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
2017 SUP
->cwd_deferred
= 0;
2021 /* --------------------------------------------------------------------------------------------- */
2024 ftpfs_rename (const vfs_path_t
* vpath1
, const vfs_path_t
* vpath2
)
2026 ftpfs_send_command (vpath1
, "RNFR /%s", OPT_FLUSH
);
2027 return ftpfs_send_command (vpath2
, "RNTO /%s", OPT_FLUSH
);
2030 /* --------------------------------------------------------------------------------------------- */
2033 ftpfs_mkdir (const vfs_path_t
* vpath
, mode_t mode
)
2035 (void) mode
; /* FIXME: should be used */
2037 return ftpfs_send_command (vpath
, "MKD /%s", OPT_FLUSH
);
2040 /* --------------------------------------------------------------------------------------------- */
2043 ftpfs_rmdir (const vfs_path_t
* vpath
)
2045 return ftpfs_send_command (vpath
, "RMD /%s", OPT_FLUSH
);
2048 /* --------------------------------------------------------------------------------------------- */
2051 ftpfs_fh_free_data (vfs_file_handler_t
* fh
)
2060 /* --------------------------------------------------------------------------------------------- */
2063 ftpfs_fh_open (struct vfs_class
*me
, vfs_file_handler_t
* fh
, int flags
, mode_t mode
)
2069 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
2070 ftp
= (ftp_fh_data_t
*) fh
->data
;
2071 /* File will be written only, so no need to retrieve it from ftp server */
2072 if (((flags
& O_WRONLY
) == O_WRONLY
) && ((flags
& (O_RDONLY
| O_RDWR
)) == 0))
2074 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2081 /* ftpfs_linear_start() called, so data will be written
2082 * to local temporary file and stored to ftp server
2083 * by vfs_s_close later
2085 if (((ftp_super_data_t
*) (FH_SUPER
->data
))->ctl_connection_busy
)
2087 if (!fh
->ino
->localname
)
2092 handle
= vfs_mkstemps (&vpath
, me
->name
, fh
->ino
->ent
->name
);
2095 vfs_path_free (vpath
);
2099 fh
->ino
->localname
= vfs_path_to_str (vpath
);
2100 vfs_path_free (vpath
);
2101 ftp
->append
= flags
& O_APPEND
;
2105 name
= vfs_s_fullpath (me
, fh
->ino
);
2109 ftpfs_open_data_connection (me
, fh
->ino
->super
,
2110 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
2115 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2119 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
2121 if (fh
->ino
->localname
)
2123 unlink (fh
->ino
->localname
);
2124 g_free (fh
->ino
->localname
);
2125 fh
->ino
->localname
= NULL
;
2130 if (!fh
->ino
->localname
&& vfs_s_retrieve_file (me
, fh
->ino
) == -1)
2132 if (!fh
->ino
->localname
)
2133 vfs_die ("retrieve_file failed to fill in localname");
2137 ftpfs_fh_free_data (fh
);
2141 /* --------------------------------------------------------------------------------------------- */
2144 ftpfs_fh_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
2146 if (fh
->handle
!= -1 && !fh
->ino
->localname
)
2148 ftp_super_data_t
*ftp
= (ftp_super_data_t
*) fh
->ino
->super
->data
;
2152 /* File is stored to destination already, so
2153 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2156 if (ftpfs_get_reply (me
, ftp
->sock
, NULL
, 0) != COMPLETE
)
2158 vfs_s_invalidate (me
, FH_SUPER
);
2164 /* --------------------------------------------------------------------------------------------- */
2167 ftpfs_done (struct vfs_class
*me
)
2171 g_slist_foreach (no_proxy
, (GFunc
) g_free
, NULL
);
2172 g_slist_free (no_proxy
);
2174 g_free (ftpfs_anonymous_passwd
);
2175 g_free (ftpfs_proxy_host
);
2178 /* --------------------------------------------------------------------------------------------- */
2181 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
2185 for (iter
= MEDATA
->supers
; iter
!= NULL
; iter
= g_list_next (iter
))
2187 const struct vfs_s_super
*super
= (const struct vfs_s_super
*) iter
->data
;
2190 name
= vfs_path_element_build_pretty_path_str (super
->path_element
);
2196 /* --------------------------------------------------------------------------------------------- */
2199 ftpfs_netrc_next (void)
2203 static const char *const keywords
[] = { "default", "machine",
2204 "login", "password", "passwd", "account", "macdef", NULL
2209 netrcp
= skip_separators (netrcp
);
2210 if (*netrcp
!= '\n')
2219 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++)
2221 if (*netrcp
== '\\')
2228 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
2229 *netrcp
!= ',' && *netrcp
; netrcp
++)
2231 if (*netrcp
== '\\')
2240 for (i
= NETRC_DEFAULT
; keywords
[i
- 1] != NULL
; i
++)
2241 if (strcmp (keywords
[i
- 1], buffer
) == 0)
2244 return NETRC_UNKNOWN
;
2247 /* --------------------------------------------------------------------------------------------- */
2250 ftpfs_netrc_bad_mode (const char *netrcname
)
2252 static int be_angry
= 1;
2255 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077))
2259 message (D_ERROR
, MSG_ERROR
,
2260 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2268 /* --------------------------------------------------------------------------------------------- */
2269 /* Scan .netrc until we find matching "machine" or "default"
2270 * domain is used for additional matching
2271 * No search is done after "default" in compliance with "man netrc"
2272 * Return 0 if found, -1 otherwise */
2275 ftpfs_find_machine (const char *host
, const char *domain
)
2284 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
)
2286 if (keyword
== NETRC_DEFAULT
)
2289 if (keyword
== NETRC_MACDEF
)
2291 /* Scan for an empty line, which concludes "macdef" */
2294 while (*netrcp
&& *netrcp
!= '\n')
2296 if (*netrcp
!= '\n')
2300 while (*netrcp
&& *netrcp
!= '\n');
2304 if (keyword
!= NETRC_MACHINE
)
2307 /* Take machine name */
2308 if (ftpfs_netrc_next () == NETRC_NONE
)
2311 if (g_ascii_strcasecmp (host
, buffer
) != 0)
2313 /* Try adding our domain to short names in .netrc */
2314 const char *host_domain
= strchr (host
, '.');
2318 /* Compare domain part */
2319 if (g_ascii_strcasecmp (host_domain
, domain
) != 0)
2322 /* Compare local part */
2323 if (g_ascii_strncasecmp (host
, buffer
, host_domain
- host
) != 0)
2334 /* --------------------------------------------------------------------------------------------- */
2335 /* Extract login and password from .netrc for the host.
2337 * Returns 0 for success, -1 for error */
2340 ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
2343 char *tmp_pass
= NULL
;
2344 char hostname
[MAXHOSTNAMELEN
];
2347 static struct rupcache
2349 struct rupcache
*next
;
2353 } *rup_cache
= NULL
, *rupp
;
2355 /* Initialize *login and *pass */
2361 /* Look up in the cache first */
2362 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
)
2364 if (!strcmp (host
, rupp
->host
))
2367 *login
= g_strdup (rupp
->login
);
2368 if (pass
&& rupp
->pass
)
2369 *pass
= g_strdup (rupp
->pass
);
2374 /* Load current .netrc */
2375 netrcname
= g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL
);
2376 if (!g_file_get_contents (netrcname
, &netrc
, NULL
, NULL
))
2384 /* Find our own domain name */
2385 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2388 domain
= strchr (hostname
, '.');
2392 /* Scan for "default" and matching "machine" keywords */
2393 ftpfs_find_machine (host
, domain
);
2395 /* Scan for keywords following "default" and "machine" */
2399 keyword
= ftpfs_netrc_next ();
2404 if (ftpfs_netrc_next () == NETRC_NONE
)
2410 /* We have another name already - should not happen */
2417 /* We have login name now */
2418 *login
= g_strdup (buffer
);
2421 case NETRC_PASSWORD
:
2423 if (ftpfs_netrc_next () == NETRC_NONE
)
2429 /* Ignore unsafe passwords */
2430 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2431 && ftpfs_netrc_bad_mode (netrcname
))
2437 /* Remember password. pass may be NULL, so use tmp_pass */
2438 if (tmp_pass
== NULL
)
2439 tmp_pass
= g_strdup (buffer
);
2443 /* "account" is followed by a token which we ignore */
2444 if (ftpfs_netrc_next () == NETRC_NONE
)
2450 /* Ignore account, but warn user anyways */
2451 ftpfs_netrc_bad_mode (netrcname
);
2455 /* Unexpected keyword or end of file */
2467 rupp
= g_new (struct rupcache
, 1);
2468 rupp
->host
= g_strdup (host
);
2469 rupp
->login
= g_strdup (*login
);
2470 rupp
->pass
= g_strdup (tmp_pass
);
2472 rupp
->next
= rup_cache
;
2480 /* --------------------------------------------------------------------------------------------- */
2481 /*** public functions ****************************************************************************/
2482 /* --------------------------------------------------------------------------------------------- */
2484 /** This routine is called as the last step in load_setup */
2486 ftpfs_init_passwd (void)
2488 ftpfs_anonymous_passwd
= load_anon_passwd ();
2489 if (ftpfs_anonymous_passwd
)
2492 /* If there is no anonymous ftp password specified
2493 * then we'll just use anonymous@
2494 * We don't send any other thing because:
2495 * - We want to remain anonymous
2496 * - We want to stop SPAM
2497 * - We don't want to let ftp sites to discriminate by the user,
2500 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
2503 /* --------------------------------------------------------------------------------------------- */
2508 static struct vfs_s_subclass ftpfs_subclass
;
2512 ftpfs_subclass
.flags
= VFS_S_REMOTE
| VFS_S_USETMP
;
2513 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2514 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2515 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2516 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2517 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2518 ftpfs_subclass
.fh_free_data
= ftpfs_fh_free_data
;
2519 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2520 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2521 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2522 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2523 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2525 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2526 vfs_ftpfs_ops
.name
= "ftpfs";
2527 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2528 vfs_ftpfs_ops
.prefix
= "ftp";
2529 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2530 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2531 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2532 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2533 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2534 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2535 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2536 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2537 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2538 vfs_register_class (&vfs_ftpfs_ops
);
2541 /* --------------------------------------------------------------------------------------------- */