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 char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
266 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
,
267 const char *remote_path
);
268 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
,
269 const char *fmt
, ...) __attribute__ ((format (__printf__
, 4, 5)));
270 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
271 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
272 const char *netrcpass
);
273 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
275 /* --------------------------------------------------------------------------------------------- */
278 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
280 if (!SUP
->remote_is_amiga
)
281 return g_strdup (remote_path
);
288 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
289 fflush (MEDATA
->logfile
);
292 /* strip leading slash(es) */
293 while (*remote_path
== '/')
297 * Don't change "/" into "", e.g. "CWD " would be
300 if (*remote_path
== '\0')
301 return g_strdup (".");
303 ret
= g_strdup (remote_path
);
305 /* replace first occurance of ":/" with ":" */
306 p
= strchr (ret
, ':');
307 if ((p
!= NULL
) && (*(p
+ 1) == '/'))
308 memmove (p
+ 1, p
+ 2, strlen (p
+ 2) + 1);
310 /* strip trailing "/." */
311 p
= strrchr (ret
, '/');
312 if ((p
!= NULL
) && (*(p
+ 1) == '.') && (*(p
+ 2) == '\0'))
319 /* --------------------------------------------------------------------------------------------- */
320 /** Extract the hostname and username from the path */
322 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
323 * ftp://sunsite.unc.edu/pub/linux
324 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
325 * ftp://tsx-11.mit.edu:8192/
326 * ftp://joe@foo.edu:11321/private
327 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
331 static vfs_path_element_t
*
332 ftpfs_correct_url_parameters (const vfs_path_element_t
* velement
)
334 vfs_path_element_t
*path_element
= vfs_path_element_clone (velement
);
336 if (path_element
->port
== 0)
337 path_element
->port
= FTP_COMMAND_PORT
;
339 if (path_element
->user
== NULL
)
341 /* Look up user and password in netrc */
343 ftpfs_netrc_lookup (path_element
->host
, &path_element
->user
, &path_element
->password
);
345 if (path_element
->user
== NULL
)
346 path_element
->user
= g_strdup ("anonymous");
348 /* Look up password in netrc for known user */
349 if (ftpfs_use_netrc
&& path_element
->user
!= NULL
&& path_element
->password
!= NULL
)
351 char *new_user
= NULL
;
352 char *new_passwd
= NULL
;
354 ftpfs_netrc_lookup (path_element
->host
, &new_user
, &new_passwd
);
356 /* If user is different, remove password */
357 if (new_user
!= NULL
&& strcmp (path_element
->user
, new_user
) != 0)
359 g_free (path_element
->password
);
360 path_element
->password
= NULL
;
370 /* --------------------------------------------------------------------------------------------- */
371 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
374 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
381 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
388 switch (sscanf (answer
, "%d", &code
))
392 g_strlcpy (string_buf
, answer
, string_len
);
396 if (answer
[3] == '-')
400 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
407 if ((sscanf (answer
, "%d", &i
) > 0) && (code
== i
) && (answer
[3] == ' '))
412 g_strlcpy (string_buf
, answer
, string_len
);
418 /* --------------------------------------------------------------------------------------------- */
421 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
425 sock
= ftpfs_open_socket (me
, super
);
428 char *cwdir
= super
->path_element
->path
;
432 super
->path_element
->path
= NULL
;
435 if (ftpfs_login_server (me
, super
, super
->path_element
->password
) != 0)
439 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
441 return sock
== COMPLETE
? 1 : 0;
444 super
->path_element
->path
= cwdir
;
450 /* --------------------------------------------------------------------------------------------- */
453 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
,
459 static int retry
= 0;
460 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
463 cmdstr
= g_strdup_vprintf (fmt
, ap
);
466 cmdlen
= strlen (cmdstr
);
467 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
468 strcpy (cmdstr
+ cmdlen
, "\r\n");
473 if (strncmp (cmdstr
, "PASS ", 5) == 0)
475 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
480 ret
= fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
483 fflush (MEDATA
->logfile
);
487 tty_enable_interrupt_key ();
488 status
= write (SUP
->sock
, cmdstr
, cmdlen
);
495 { /* Remote server has closed connection */
499 status
= ftpfs_reconnect (me
, super
);
501 if (status
&& (write (SUP
->sock
, cmdstr
, cmdlen
) > 0))
510 tty_disable_interrupt_key ();
515 tty_disable_interrupt_key ();
519 status
= ftpfs_get_reply (me
, SUP
->sock
,
520 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
521 sizeof (reply_str
) - 1);
522 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
526 status
= ftpfs_reconnect (me
, super
);
528 if (status
&& (write (SUP
->sock
, cmdstr
, cmdlen
) > 0))
541 /* --------------------------------------------------------------------------------------------- */
544 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
548 vfs_print_message (_("ftpfs: Disconnecting from %s"), super
->path_element
->host
);
549 ftpfs_command (me
, super
, NONE
, "QUIT");
552 g_free (super
->data
);
556 /* --------------------------------------------------------------------------------------------- */
559 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
561 if (binary
!= SUP
->isbinary
)
563 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
565 SUP
->isbinary
= binary
;
570 /* --------------------------------------------------------------------------------------------- */
571 /* This routine logs the user in */
574 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
)
578 char *name
; /* login user name */
580 char reply_string
[BUF_MEDIUM
];
582 SUP
->isbinary
= TYPE_UNKNOWN
;
584 if (super
->path_element
->password
!= NULL
) /* explicit password */
585 op
= g_strdup (super
->path_element
->password
);
586 else if (netrcpass
!= NULL
) /* password from netrc */
587 op
= g_strdup (netrcpass
);
588 else if (strcmp (super
->path_element
->user
, "anonymous") == 0
589 || strcmp (super
->path_element
->user
, "ftp") == 0)
591 if (ftpfs_anonymous_passwd
== NULL
) /* default anonymous password */
592 ftpfs_init_passwd ();
593 op
= g_strdup (ftpfs_anonymous_passwd
);
600 p
= g_strdup_printf (_("FTP: Password required for %s"), super
->path_element
->user
);
601 op
= vfs_get_password (p
);
605 super
->path_element
->password
= g_strdup (op
);
608 if (!anon
|| MEDATA
->logfile
)
612 pass
= g_strconcat ("-", op
, (char *) NULL
);
616 /* Proxy server accepts: username@host-we-want-to-connect */
619 g_strconcat (super
->path_element
->user
, "@",
620 super
->path_element
->host
[0] ==
621 '!' ? super
->path_element
->host
+ 1 : super
->path_element
->host
,
624 name
= g_strdup (super
->path_element
->user
);
626 if (ftpfs_get_reply (me
, SUP
->sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
)
630 reply_up
= g_ascii_strup (reply_string
, -1);
631 SUP
->remote_is_amiga
= strstr (reply_up
, "AMIGA") != 0;
632 if (strstr (reply_up
, " SPFTP/1.0.0000 SERVER ")) /* handles `LIST -la` in a weird way */
633 SUP
->strict
= RFC_STRICT
;
638 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n", SUP
->remote_is_amiga
);
639 fflush (MEDATA
->logfile
);
642 vfs_print_message (_("ftpfs: sending login name"));
644 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
))
647 vfs_print_message (_("ftpfs: sending user password"));
648 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
649 if (code
== CONTINUE
)
653 p
= g_strdup_printf (_("FTP: Account required for user %s"),
654 super
->path_element
->user
);
655 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
659 vfs_print_message (_("ftpfs: sending user account"));
660 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
663 if (code
!= COMPLETE
)
668 vfs_print_message (_("ftpfs: logged in"));
669 wipe_password (pass
);
674 SUP
->failed_on_login
= 1;
675 wipe_password (super
->path_element
->password
);
676 super
->path_element
->password
= NULL
;
682 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
683 super
->path_element
->user
);
686 wipe_password (pass
);
691 /* --------------------------------------------------------------------------------------------- */
694 ftpfs_load_no_proxy_list (void)
696 /* FixMe: shouldn't be hardcoded!!! */
697 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
701 static char *mc_file
= NULL
;
703 mc_file
= g_build_filename (mc_global
.sysconfig_dir
, "mc.no_proxy", (char *) NULL
);
704 if (exist_file (mc_file
))
706 npf
= fopen (mc_file
, "r");
709 while (fgets (s
, sizeof (s
), npf
) != NULL
)
711 p
= strchr (s
, '\n');
712 if (p
== NULL
) /* skip bogus entries */
714 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
722 no_proxy
= g_slist_prepend (no_proxy
, g_strdup (s
));
731 /* --------------------------------------------------------------------------------------------- */
732 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
735 ftpfs_check_proxy (const char *host
)
739 if (ftpfs_proxy_host
== NULL
|| *ftpfs_proxy_host
== '\0' || host
== NULL
|| *host
== '\0')
740 return 0; /* sanity check */
745 if (!ftpfs_always_use_proxy
)
748 if (strchr (host
, '.') == NULL
)
751 ftpfs_load_no_proxy_list ();
752 for (npe
= no_proxy
; npe
!= NULL
; npe
= g_slist_next (npe
))
754 const char *domain
= (const char *) npe
->data
;
756 if (domain
[0] == '.')
758 size_t ld
= strlen (domain
);
759 size_t lh
= strlen (host
);
761 while (ld
!= 0 && lh
!= 0 && host
[lh
- 1] == domain
[ld
- 1])
770 else if (g_ascii_strcasecmp (host
, domain
) == 0)
777 /* --------------------------------------------------------------------------------------------- */
780 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
782 vfs_path_element_t
*path_element
;
784 path_element
= vfs_url_split (proxy
, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
785 *host
= g_strdup (path_element
->host
);
786 *port
= path_element
->port
;
787 vfs_path_element_free (path_element
);
790 /* --------------------------------------------------------------------------------------------- */
793 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
795 struct addrinfo hints
, *res
, *curr_res
;
804 /* Use a proxy host? */
805 host
= g_strdup (super
->path_element
->host
);
807 if (host
== NULL
|| *host
== '\0')
809 vfs_print_message (_("ftpfs: Invalid host name."));
810 ftpfs_errno
= EINVAL
;
815 /* Hosts to connect to that start with a ! should use proxy */
816 tmp_port
= super
->path_element
->port
;
818 if (SUP
->proxy
!= NULL
)
819 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
821 g_snprintf (port
, sizeof (port
), "%hu", (unsigned short) tmp_port
);
829 tty_enable_interrupt_key (); /* clear the interrupt flag */
831 memset (&hints
, 0, sizeof (struct addrinfo
));
832 hints
.ai_family
= AF_UNSPEC
;
833 hints
.ai_socktype
= SOCK_STREAM
;
836 /* By default, only look up addresses using address types for
837 * which a local interface is configured (i.e. no IPv6 if no IPv6
838 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
839 hints
.ai_flags
= AI_ADDRCONFIG
;
842 e
= getaddrinfo (host
, port
, &hints
, &res
);
845 if (e
== EAI_BADFLAGS
)
847 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
849 e
= getaddrinfo (host
, port
, &hints
, &res
);
857 tty_disable_interrupt_key ();
858 vfs_print_message (_("ftpfs: %s"), gai_strerror (e
));
860 ftpfs_errno
= EINVAL
;
864 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
)
866 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
870 if (curr_res
->ai_next
!= NULL
)
873 tty_disable_interrupt_key ();
874 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno
));
881 vfs_print_message (_("ftpfs: making connection to %s"), host
);
885 if (connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0)
891 if (errno
== EINTR
&& tty_got_interrupt ())
892 vfs_print_message (_("ftpfs: connection interrupted by user"));
893 else if (res
->ai_next
== NULL
)
894 vfs_print_message (_("ftpfs: connection to server failed: %s"),
895 unix_error_string (errno
));
900 tty_disable_interrupt_key ();
905 tty_disable_interrupt_key ();
909 /* --------------------------------------------------------------------------------------------- */
912 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
914 int retry_seconds
= 0;
917 /* We do not want to use the passive if we are using proxies */
919 SUP
->use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
923 SUP
->failed_on_login
= 0;
925 SUP
->sock
= ftpfs_open_socket (me
, super
);
929 if (ftpfs_login_server (me
, super
, NULL
) != 0)
931 /* Logged in, no need to retry the connection */
936 if (!SUP
->failed_on_login
)
939 /* Close only the socket descriptor */
942 if (ftpfs_retry_seconds
!= 0)
944 retry_seconds
= ftpfs_retry_seconds
;
945 tty_enable_interrupt_key ();
946 for (count_down
= retry_seconds
; count_down
; count_down
--)
948 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
951 if (tty_got_interrupt ())
953 /* ftpfs_errno = E; */
954 tty_disable_interrupt_key ();
958 tty_disable_interrupt_key ();
962 while (retry_seconds
!= 0);
964 super
->path_element
->path
= ftpfs_get_current_directory (me
, super
);
965 if (super
->path_element
->path
== NULL
)
966 super
->path_element
->path
= g_strdup (PATH_SEP_STR
);
971 /* --------------------------------------------------------------------------------------------- */
974 ftpfs_open_archive (struct vfs_s_super
*super
,
975 const vfs_path_t
* vpath
, const vfs_path_element_t
* vpath_element
)
979 super
->data
= g_new0 (ftp_super_data_t
, 1);
981 super
->path_element
= ftpfs_correct_url_parameters (vpath_element
);
983 if (ftpfs_check_proxy (super
->path_element
->host
))
984 SUP
->proxy
= ftpfs_proxy_host
;
985 SUP
->use_passive_connection
= ftpfs_use_passive_connections
;
986 SUP
->strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
987 SUP
->isbinary
= TYPE_UNKNOWN
;
988 SUP
->remote_is_amiga
= 0;
989 super
->name
= g_strdup ("/");
991 vfs_s_new_inode (vpath_element
->class, super
,
992 vfs_s_default_stat (vpath_element
->class, S_IFDIR
| 0755));
994 return ftpfs_open_archive_int (vpath_element
->class, super
);
997 /* --------------------------------------------------------------------------------------------- */
1000 ftpfs_archive_same (const vfs_path_element_t
* vpath_element
, struct vfs_s_super
*super
,
1001 const vfs_path_t
* vpath
, void *cookie
)
1003 vfs_path_element_t
*path_element
;
1009 path_element
= ftpfs_correct_url_parameters (vpath_element
);
1011 result
= ((strcmp (path_element
->host
, super
->path_element
->host
) == 0)
1012 && (strcmp (path_element
->user
, super
->path_element
->user
) == 0)
1013 && (path_element
->port
== super
->path_element
->port
)) ? 1 : 0;
1015 vfs_path_element_free (path_element
);
1019 /* --------------------------------------------------------------------------------------------- */
1020 /* The returned directory should always contain a trailing slash */
1023 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
1025 char buf
[BUF_8K
], *bufp
, *bufq
;
1027 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
1028 ftpfs_get_reply (me
, SUP
->sock
, buf
, sizeof (buf
)) == COMPLETE
)
1031 for (bufq
= buf
; *bufq
; bufq
++)
1043 if (*(bufq
- 1) != '/')
1049 return g_strdup (bufp
);
1052 /* If the remote server is an Amiga a leading slash
1053 might be missing. MC needs it because it is used
1054 as separator between hostname and path internally. */
1055 return g_strconcat ("/", bufp
, (char *) NULL
);
1070 /* --------------------------------------------------------------------------------------------- */
1071 /* Setup Passive PASV FTP connection */
1074 ftpfs_setup_passive_pasv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1075 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1079 int xa
, xb
, xc
, xd
, xe
, xf
;
1081 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
1084 /* Parse remote parameters */
1085 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
1089 if (!isdigit ((unsigned char) *c
))
1091 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
1094 n
[0] = (unsigned char) xa
;
1095 n
[1] = (unsigned char) xb
;
1096 n
[2] = (unsigned char) xc
;
1097 n
[3] = (unsigned char) xd
;
1098 n
[4] = (unsigned char) xe
;
1099 n
[5] = (unsigned char) xf
;
1101 memcpy (&(((struct sockaddr_in
*) sa
)->sin_addr
.s_addr
), (void *) n
, 4);
1102 memcpy (&(((struct sockaddr_in
*) sa
)->sin_port
), (void *) &n
[4], 2);
1104 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
1110 /* --------------------------------------------------------------------------------------------- */
1111 /* Setup Passive EPSV FTP connection */
1114 ftpfs_setup_passive_epsv (struct vfs_class
*me
, struct vfs_s_super
*super
,
1115 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1120 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") != COMPLETE
)
1124 c
= strchr (reply_str
, '|');
1133 if (port
< 0 || port
> 65535)
1135 port
= htons (port
);
1137 switch (sa
->ss_family
)
1140 ((struct sockaddr_in
*) sa
)->sin_port
= port
;
1143 ((struct sockaddr_in6
*) sa
)->sin6_port
= port
;
1147 return (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0) ? 0 : 1;
1150 /* --------------------------------------------------------------------------------------------- */
1151 /* Setup Passive ftp connection, we use it for source routed connections */
1154 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
1155 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
1157 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1158 if (sa
->ss_family
== AF_INET
)
1160 if (!ftpfs_setup_passive_pasv (me
, super
, my_socket
, sa
, salen
))
1161 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1162 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1165 /* It's IPV6, so EPSV is our only hope */
1168 if (!ftpfs_setup_passive_epsv (me
, super
, my_socket
, sa
, salen
))
1175 /* --------------------------------------------------------------------------------------------- */
1176 /* Setup Active PORT or EPRT FTP connection */
1179 ftpfs_setup_active (struct vfs_class
*me
, struct vfs_s_super
*super
,
1180 struct sockaddr_storage data_addr
, socklen_t data_addrlen
)
1182 unsigned short int port
;
1186 switch (data_addr
.ss_family
)
1190 port
= ((struct sockaddr_in
*) &data_addr
)->sin_port
;
1194 port
= ((struct sockaddr_in6
*) &data_addr
)->sin6_port
;
1196 /* Not implemented */
1201 addr
= g_try_malloc (NI_MAXHOST
);
1203 ERRNOR (ENOMEM
, -1);
1206 ((struct sockaddr
*) &data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0,
1207 NI_NUMERICHOST
) != 0)
1213 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1216 unsigned char *a
= (unsigned char *) &((struct sockaddr_in
*) &data_addr
)->sin_addr
;
1217 unsigned char *p
= (unsigned char *) &port
;
1219 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1220 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1221 p
[0], p
[1]) == COMPLETE
)
1229 * Converts network MSB first order to host byte order (LSB
1230 * first on i386). If we do it earlier, we will run into an
1231 * endianness issue, because the server actually expects to see
1232 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1234 port
= ntohs (port
);
1236 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1237 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
)
1247 /* --------------------------------------------------------------------------------------------- */
1248 /* Initialize a socket for FTP DATA connection */
1251 ftpfs_init_data_socket (struct vfs_class
*me
, struct vfs_s_super
*super
,
1252 struct sockaddr_storage
*data_addr
, socklen_t
* data_addrlen
)
1256 memset (data_addr
, 0, sizeof (struct sockaddr_storage
));
1257 *data_addrlen
= sizeof (struct sockaddr_storage
);
1259 if (SUP
->use_passive_connection
)
1260 result
= getpeername (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1262 result
= getsockname (SUP
->sock
, (struct sockaddr
*) data_addr
, data_addrlen
);
1267 switch (data_addr
->ss_family
)
1270 ((struct sockaddr_in
*) data_addr
)->sin_port
= 0;
1273 ((struct sockaddr_in6
*) data_addr
)->sin6_port
= 0;
1276 vfs_print_message (_("ftpfs: invalid address family"));
1277 ERRNOR (EINVAL
, -1);
1280 result
= socket (data_addr
->ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1284 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1291 /* --------------------------------------------------------------------------------------------- */
1292 /* Initialize FTP DATA connection */
1295 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
1297 struct sockaddr_storage data_addr
;
1298 socklen_t data_addrlen
;
1301 * Don't factor socket initialization out of these conditionals,
1302 * because ftpfs_init_data_socket initializes it in different way
1303 * depending on use_passive_connection flag.
1306 /* Try to establish a passive connection first (if requested) */
1307 if (SUP
->use_passive_connection
)
1311 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1315 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1318 vfs_print_message (_("ftpfs: could not setup passive mode"));
1319 SUP
->use_passive_connection
= 0;
1324 /* If passive setup is diabled or failed, fallback to active connections */
1325 if (!SUP
->use_passive_connection
)
1329 data_sock
= ftpfs_init_data_socket (me
, super
, &data_addr
, &data_addrlen
);
1333 if ((bind (data_sock
, (struct sockaddr
*) &data_addr
, data_addrlen
) == 0) &&
1334 (getsockname (data_sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == 0) &&
1335 (listen (data_sock
, 1) == 0) &&
1336 (ftpfs_setup_active (me
, super
, data_addr
, data_addrlen
) != 0))
1342 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1343 SUP
->use_passive_connection
= SUP
->proxy
!= NULL
? ftpfs_use_passive_connections_over_proxy
:
1344 ftpfs_use_passive_connections
;
1350 /* --------------------------------------------------------------------------------------------- */
1353 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1354 const char *remote
, int isbinary
, int reget
)
1356 struct sockaddr_storage from
;
1358 socklen_t fromlen
= sizeof (from
);
1360 s
= ftpfs_initconn (me
, super
);
1364 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1368 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1374 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1375 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1376 /* WarFtpD can't STORE //filename */
1377 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1378 g_free (remote_path
);
1381 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1385 tty_enable_interrupt_key ();
1386 if (SUP
->use_passive_connection
)
1390 data
= accept (s
, (struct sockaddr
*) &from
, &fromlen
);
1393 ftpfs_errno
= errno
;
1399 tty_disable_interrupt_key ();
1403 /* --------------------------------------------------------------------------------------------- */
1406 ftpfs_linear_abort (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1408 struct vfs_s_super
*super
= FH_SUPER
;
1409 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1412 int dsock
= FH_SOCK
;
1414 SUP
->ctl_connection_busy
= 0;
1416 vfs_print_message (_("ftpfs: aborting transfer."));
1417 if (send (SUP
->sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
))
1419 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno
));
1425 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
)
1427 vfs_print_message (_("ftpfs: abort failed"));
1435 FD_SET (dsock
, &mask
);
1436 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0)
1438 struct timeval start_tim
, tim
;
1439 gettimeofday (&start_tim
, NULL
);
1440 /* flush the remaining data */
1441 while (read (dsock
, buf
, sizeof (buf
)) > 0)
1443 gettimeofday (&tim
, NULL
);
1444 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
)
1446 /* server keeps sending, drop the connection and ftpfs_reconnect */
1448 ftpfs_reconnect (me
, super
);
1455 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1456 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1459 /* --------------------------------------------------------------------------------------------- */
1463 resolve_symlink_without_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1464 struct vfs_s_inode
*dir
)
1466 struct linklist
*flist
;
1467 struct direntry
*fe
, *fel
;
1468 char tmp
[MC_MAXPATHLEN
];
1471 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1472 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
)
1474 /* flist->data->l_stat is alread initialized with 0 */
1476 if (S_ISLNK (fel
->s
.st_mode
) && fel
->linkname
)
1478 if (fel
->linkname
[0] == '/')
1480 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1482 strcpy (tmp
, fel
->linkname
);
1486 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1488 strcpy (tmp
, dir
->remote_path
);
1491 strcat (tmp
+ 1, fel
->linkname
);
1493 for (depth
= 0; depth
< 100; depth
++)
1494 { /* depth protects against recursive symbolic links */
1495 canonicalize_pathname (tmp
);
1496 fe
= _get_file_entry (bucket
, tmp
, 0, 0);
1499 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0)
1501 /* Symlink points to link which isn't resolved, yet. */
1502 if (fe
->linkname
[0] == '/')
1504 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1506 strcpy (tmp
, fe
->linkname
);
1510 /* at this point tmp looks always like this
1511 /directory/filename, i.e. no need to check
1512 strrchr's return value */
1513 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1514 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1516 strcat (tmp
, fe
->linkname
);
1522 fel
->l_stat
= g_new (struct stat
, 1);
1523 if (S_ISLNK (fe
->s
.st_mode
))
1524 *fel
->l_stat
= *fe
->l_stat
;
1526 *fel
->l_stat
= fe
->s
;
1527 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1534 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1537 /* --------------------------------------------------------------------------------------------- */
1540 resolve_symlink_with_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1541 struct vfs_s_inode
*dir
)
1543 char buffer
[2048] = "", *filename
;
1547 struct linklist
*flist
;
1548 struct direntry
*fe
;
1549 int switch_method
= 0;
1551 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1552 if (strchr (dir
->remote_path
, ' '))
1554 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
)
1556 vfs_print_message (_("ftpfs: CWD failed."));
1559 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1562 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", dir
->remote_path
, TYPE_ASCII
, 0);
1566 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1570 fp
= fdopen (sock
, "r");
1574 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1577 tty_enable_interrupt_key ();
1578 flist
= dir
->file_list
->next
;
1583 if (flist
== dir
->file_list
)
1586 flist
= flist
->next
;
1588 while (!S_ISLNK (fe
->s
.st_mode
));
1591 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1593 if (MEDATA
->logfile
)
1595 fputs (buffer
, MEDATA
->logfile
);
1596 fflush (MEDATA
->logfile
);
1598 vfs_die ("This code should be commented out\n");
1599 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
))
1601 int r
= strcmp (fe
->name
, filename
);
1605 if (S_ISLNK (s
.st_mode
))
1607 /* This server doesn't understand LIST -lLa */
1611 fe
->l_stat
= g_new (struct stat
, 1);
1612 if (fe
->l_stat
== NULL
)
1615 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1624 while (fgets (buffer
, sizeof (buffer
), fp
) != NULL
);
1625 tty_disable_interrupt_key ();
1627 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1630 /* --------------------------------------------------------------------------------------------- */
1633 resolve_symlink (struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1635 vfs_print_message (_("Resolving symlink..."));
1637 if (SUP
->strict_rfc959_list_cmd
)
1638 resolve_symlink_without_ls_options (me
, super
, dir
);
1640 resolve_symlink_with_ls_options (me
, super
, dir
);
1644 /* --------------------------------------------------------------------------------------------- */
1647 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1649 struct vfs_s_entry
*ent
;
1650 struct vfs_s_super
*super
= dir
->super
;
1651 int sock
, num_entries
= 0;
1652 char lc_buffer
[BUF_8K
];
1655 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
->strict
== RFC_STRICT
)
1656 || (strchr (remote_path
, ' ') != NULL
);
1659 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1662 RFC_STRICT
? _("(strict rfc959)") : "", cd_first
? _("(chdir first)") : "");
1666 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
)
1668 ftpfs_errno
= ENOENT
;
1669 vfs_print_message (_("ftpfs: CWD failed."));
1674 gettimeofday (&dir
->timestamp
, NULL
);
1675 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1677 if (SUP
->strict
== RFC_STRICT
)
1678 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1680 /* Dirty hack to avoid autoprepending / to . */
1681 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1682 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1687 /* Trailing "/." is necessary if remote_path is a symlink */
1688 path
= mc_build_filename (remote_path
, ".", NULL
);
1689 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1696 /* Clear the interrupt flag */
1697 tty_enable_interrupt_key ();
1699 vfs_parse_ls_lga_init ();
1703 size_t count_spaces
= 0;
1704 int res
= vfs_s_get_line_interruptible (me
, lc_buffer
, sizeof (lc_buffer
),
1711 me
->verrno
= ECONNRESET
;
1713 tty_disable_interrupt_key ();
1714 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1715 vfs_print_message (_("%s: failure"), me
->name
);
1719 if (MEDATA
->logfile
)
1721 fputs (lc_buffer
, MEDATA
->logfile
);
1722 fputs ("\n", MEDATA
->logfile
);
1723 fflush (MEDATA
->logfile
);
1726 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1727 i
= ent
->ino
->st
.st_nlink
;
1728 if (!vfs_parse_ls_lga
1729 (lc_buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
, &count_spaces
))
1731 vfs_s_free_entry (me
, ent
);
1734 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1736 vfs_s_store_filename_leading_spaces (ent
, count_spaces
);
1737 vfs_s_insert_entry (me
, dir
, ent
);
1741 me
->verrno
= E_REMOTE
;
1742 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1745 if (num_entries
== 0 && cd_first
== 0)
1747 /* The LIST command may produce an empty output. In such scenario
1748 it is not clear whether this is caused by `remote_path' being
1749 a non-existent path or for some other reason (listing emtpy
1750 directory without the -a option, non-readable directory, etc.).
1752 Since `dir_load' is a crucial method, when it comes to determine
1753 whether a given path is a _directory_, the code must try its best
1754 to determine the type of `remote_path'. The only reliable way to
1755 achieve this is trough issuing a CWD command. */
1761 vfs_s_normalize_filename_leading_spaces (dir
, vfs_parse_ls_lga_get_final_spaces ());
1763 if (SUP
->strict
== RFC_AUTODETECT
)
1764 SUP
->strict
= RFC_DARING
;
1766 vfs_print_message (_("%s: done."), me
->name
);
1770 if (SUP
->strict
== RFC_AUTODETECT
)
1772 /* It's our first attempt to get a directory listing from this
1773 server (UNIX style LIST command) */
1774 SUP
->strict
= RFC_STRICT
;
1775 /* I hate goto, but recursive call needs another 8K on stack */
1776 /* return ftpfs_dir_load (me, dir, remote_path); */
1780 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1781 ERRNOR (EACCES
, -1);
1784 /* --------------------------------------------------------------------------------------------- */
1787 ftpfs_file_store (struct vfs_class
*me
, vfs_file_handler_t
* fh
, char *name
, char *localname
)
1789 int h
, sock
, n_read
, n_written
;
1791 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1796 char lc_buffer
[BUF_8K
];
1799 struct vfs_s_super
*super
= FH_SUPER
;
1800 ftp_fh_data_t
*ftp
= (ftp_fh_data_t
*) fh
->data
;
1802 h
= open (localname
, O_RDONLY
);
1807 ftpfs_open_data_connection (me
, super
, ftp
->append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1808 if (sock
< 0 || fstat (h
, &s
) == -1)
1813 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1816 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1818 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1822 tty_enable_interrupt_key ();
1825 while ((n_read
= read (h
, lc_buffer
, sizeof (lc_buffer
))) == -1)
1829 if (tty_got_interrupt ())
1831 ftpfs_errno
= EINTR
;
1837 ftpfs_errno
= errno
;
1844 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
)
1846 if (n_written
== -1)
1848 if (errno
== EINTR
&& !tty_got_interrupt ())
1852 ftpfs_errno
= errno
;
1856 n_read
-= n_written
;
1858 vfs_print_message ("%s: %" PRIuMAX
"/%" PRIuMAX
,
1859 _("ftpfs: storing file"), (uintmax_t) n_stored
, (uintmax_t) s
.st_size
);
1861 tty_disable_interrupt_key ();
1864 if (ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
)
1868 tty_disable_interrupt_key ();
1871 ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0);
1875 /* --------------------------------------------------------------------------------------------- */
1878 ftpfs_linear_start (struct vfs_class
*me
, vfs_file_handler_t
* fh
, off_t offset
)
1882 if (fh
->data
== NULL
)
1883 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
1885 name
= vfs_s_fullpath (me
, fh
->ino
);
1888 FH_SOCK
= ftpfs_open_data_connection (me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1892 fh
->linear
= LS_LINEAR_OPEN
;
1893 ((ftp_super_data_t
*) (FH_SUPER
->data
))->ctl_connection_busy
= 1;
1894 ((ftp_fh_data_t
*) fh
->data
)->append
= 0;
1898 /* --------------------------------------------------------------------------------------------- */
1901 ftpfs_linear_read (struct vfs_class
*me
, vfs_file_handler_t
* fh
, void *buf
, size_t len
)
1904 struct vfs_s_super
*super
= FH_SUPER
;
1906 while ((n
= read (FH_SOCK
, buf
, len
)) < 0)
1908 if ((errno
== EINTR
) && !tty_got_interrupt ())
1914 ftpfs_linear_abort (me
, fh
);
1918 SUP
->ctl_connection_busy
= 0;
1921 if ((ftpfs_get_reply (me
, SUP
->sock
, NULL
, 0) != COMPLETE
))
1922 ERRNOR (E_REMOTE
, -1);
1928 /* --------------------------------------------------------------------------------------------- */
1931 ftpfs_linear_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
1934 ftpfs_linear_abort (me
, fh
);
1937 /* --------------------------------------------------------------------------------------------- */
1940 ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1946 case VFS_CTL_IS_NOTREADY
:
1951 vfs_die ("You may not do this");
1952 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1955 v
= vfs_s_select_on_two (((ftp_fh_data_t
*) (FH
->data
))->sock
, 0);
1956 return (((v
< 0) && (errno
== EINTR
)) || v
== 0) ? 1 : 0;
1963 /* --------------------------------------------------------------------------------------------- */
1966 ftpfs_send_command (const vfs_path_t
* vpath
, const char *cmd
, int flags
)
1970 struct vfs_s_super
*super
;
1972 vfs_path_element_t
*path_element
;
1974 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1976 path_element
= vfs_path_get_by_index (vpath
, -1);
1978 rpath
= vfs_s_get_path (vpath
, &super
, 0);
1982 p
= ftpfs_translate_path (path_element
->class, super
, rpath
);
1983 r
= ftpfs_command (path_element
->class, super
, WAIT_REPLY
, cmd
, p
);
1985 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1986 if (flags
& OPT_IGNORE_ERROR
)
1990 path_element
->class->verrno
= EPERM
;
1993 if (flush_directory_cache
)
1994 vfs_s_invalidate (path_element
->class, super
);
1998 /* --------------------------------------------------------------------------------------------- */
2001 ftpfs_chmod (const vfs_path_t
* vpath
, mode_t mode
)
2003 char buf
[BUF_SMALL
];
2006 g_snprintf (buf
, sizeof (buf
), "SITE CHMOD %4.4o /%%s", (int) (mode
& 07777));
2008 ret
= ftpfs_send_command (vpath
, buf
, OPT_FLUSH
);
2010 return ftpfs_ignore_chattr_errors
? 0 : ret
;
2013 /* --------------------------------------------------------------------------------------------- */
2016 ftpfs_chown (const vfs_path_t
* vpath
, uid_t owner
, gid_t group
)
2023 ftpfs_errno
= EPERM
;
2026 /* Everyone knows it is not possible to chown remotely, so why bother them.
2027 If someone's root, then copy/move will always try to chown it... */
2035 /* --------------------------------------------------------------------------------------------- */
2038 ftpfs_unlink (const vfs_path_t
* vpath
)
2040 return ftpfs_send_command (vpath
, "DELE /%s", OPT_FLUSH
);
2043 /* --------------------------------------------------------------------------------------------- */
2045 /* Return 1 if path is the same directory as the one we are in now */
2047 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
2051 if (super
->path_element
->path
== NULL
)
2053 return (strcmp (path
, super
->path_element
->path
) == 0);
2056 /* --------------------------------------------------------------------------------------------- */
2059 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
2064 if (!SUP
->cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
2067 p
= ftpfs_translate_path (me
, super
, remote_path
);
2068 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
2075 g_free (super
->path_element
->path
);
2076 super
->path_element
->path
= g_strdup (remote_path
);
2077 SUP
->cwd_deferred
= 0;
2082 /* --------------------------------------------------------------------------------------------- */
2085 ftpfs_rename (const vfs_path_t
* vpath1
, const vfs_path_t
* vpath2
)
2087 ftpfs_send_command (vpath1
, "RNFR /%s", OPT_FLUSH
);
2088 return ftpfs_send_command (vpath2
, "RNTO /%s", OPT_FLUSH
);
2091 /* --------------------------------------------------------------------------------------------- */
2094 ftpfs_mkdir (const vfs_path_t
* vpath
, mode_t mode
)
2096 (void) mode
; /* FIXME: should be used */
2098 return ftpfs_send_command (vpath
, "MKD /%s", OPT_FLUSH
);
2101 /* --------------------------------------------------------------------------------------------- */
2104 ftpfs_rmdir (const vfs_path_t
* vpath
)
2106 return ftpfs_send_command (vpath
, "RMD /%s", OPT_FLUSH
);
2109 /* --------------------------------------------------------------------------------------------- */
2112 ftpfs_fh_free_data (vfs_file_handler_t
* fh
)
2121 /* --------------------------------------------------------------------------------------------- */
2124 ftpfs_fh_open (struct vfs_class
*me
, vfs_file_handler_t
* fh
, int flags
, mode_t mode
)
2130 fh
->data
= g_new0 (ftp_fh_data_t
, 1);
2131 ftp
= (ftp_fh_data_t
*) fh
->data
;
2132 /* File will be written only, so no need to retrieve it from ftp server */
2133 if (((flags
& O_WRONLY
) == O_WRONLY
) && ((flags
& (O_RDONLY
| O_RDWR
)) == 0))
2135 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2142 /* ftpfs_linear_start() called, so data will be written
2143 * to local temporary file and stored to ftp server
2144 * by vfs_s_close later
2146 if (((ftp_super_data_t
*) (FH_SUPER
->data
))->ctl_connection_busy
)
2148 if (!fh
->ino
->localname
)
2153 handle
= vfs_mkstemps (&vpath
, me
->name
, fh
->ino
->ent
->name
);
2156 vfs_path_free (vpath
);
2160 fh
->ino
->localname
= vfs_path_to_str (vpath
);
2161 vfs_path_free (vpath
);
2162 ftp
->append
= flags
& O_APPEND
;
2166 name
= vfs_s_fullpath (me
, fh
->ino
);
2170 ftpfs_open_data_connection (me
, fh
->ino
->super
,
2171 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
2176 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2180 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
2182 if (fh
->ino
->localname
)
2184 unlink (fh
->ino
->localname
);
2185 g_free (fh
->ino
->localname
);
2186 fh
->ino
->localname
= NULL
;
2191 if (!fh
->ino
->localname
&& vfs_s_retrieve_file (me
, fh
->ino
) == -1)
2193 if (!fh
->ino
->localname
)
2194 vfs_die ("retrieve_file failed to fill in localname");
2198 ftpfs_fh_free_data (fh
);
2202 /* --------------------------------------------------------------------------------------------- */
2205 ftpfs_fh_close (struct vfs_class
*me
, vfs_file_handler_t
* fh
)
2207 if (fh
->handle
!= -1 && !fh
->ino
->localname
)
2209 ftp_super_data_t
*ftp
= (ftp_super_data_t
*) fh
->ino
->super
->data
;
2213 /* File is stored to destination already, so
2214 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2217 if (ftpfs_get_reply (me
, ftp
->sock
, NULL
, 0) != COMPLETE
)
2219 vfs_s_invalidate (me
, FH_SUPER
);
2225 /* --------------------------------------------------------------------------------------------- */
2228 ftpfs_done (struct vfs_class
*me
)
2232 g_slist_foreach (no_proxy
, (GFunc
) g_free
, NULL
);
2233 g_slist_free (no_proxy
);
2235 g_free (ftpfs_anonymous_passwd
);
2236 g_free (ftpfs_proxy_host
);
2239 /* --------------------------------------------------------------------------------------------- */
2242 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
2246 for (iter
= MEDATA
->supers
; iter
!= NULL
; iter
= g_list_next (iter
))
2248 const struct vfs_s_super
*super
= (const struct vfs_s_super
*) iter
->data
;
2252 g_strconcat (vfs_ftpfs_ops
.prefix
, VFS_PATH_URL_DELIMITER
, super
->path_element
->user
,
2253 "@", super
->path_element
->host
, "/", super
->path_element
->path
,
2260 /* --------------------------------------------------------------------------------------------- */
2263 ftpfs_netrc_next (void)
2267 static const char *const keywords
[] = { "default", "machine",
2268 "login", "password", "passwd", "account", "macdef", NULL
2273 netrcp
= skip_separators (netrcp
);
2274 if (*netrcp
!= '\n')
2283 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++)
2285 if (*netrcp
== '\\')
2292 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
2293 *netrcp
!= ',' && *netrcp
; netrcp
++)
2295 if (*netrcp
== '\\')
2304 for (i
= NETRC_DEFAULT
; keywords
[i
- 1] != NULL
; i
++)
2305 if (strcmp (keywords
[i
- 1], buffer
) == 0)
2308 return NETRC_UNKNOWN
;
2311 /* --------------------------------------------------------------------------------------------- */
2314 ftpfs_netrc_bad_mode (const char *netrcname
)
2316 static int be_angry
= 1;
2319 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077))
2323 message (D_ERROR
, MSG_ERROR
,
2324 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2332 /* --------------------------------------------------------------------------------------------- */
2333 /* Scan .netrc until we find matching "machine" or "default"
2334 * domain is used for additional matching
2335 * No search is done after "default" in compliance with "man netrc"
2336 * Return 0 if found, -1 otherwise */
2339 ftpfs_find_machine (const char *host
, const char *domain
)
2348 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
)
2350 if (keyword
== NETRC_DEFAULT
)
2353 if (keyword
== NETRC_MACDEF
)
2355 /* Scan for an empty line, which concludes "macdef" */
2358 while (*netrcp
&& *netrcp
!= '\n')
2360 if (*netrcp
!= '\n')
2364 while (*netrcp
&& *netrcp
!= '\n');
2368 if (keyword
!= NETRC_MACHINE
)
2371 /* Take machine name */
2372 if (ftpfs_netrc_next () == NETRC_NONE
)
2375 if (g_ascii_strcasecmp (host
, buffer
) != 0)
2377 /* Try adding our domain to short names in .netrc */
2378 const char *host_domain
= strchr (host
, '.');
2382 /* Compare domain part */
2383 if (g_ascii_strcasecmp (host_domain
, domain
) != 0)
2386 /* Compare local part */
2387 if (g_ascii_strncasecmp (host
, buffer
, host_domain
- host
) != 0)
2398 /* --------------------------------------------------------------------------------------------- */
2399 /* Extract login and password from .netrc for the host.
2401 * Returns 0 for success, -1 for error */
2404 ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
2407 char *tmp_pass
= NULL
;
2408 char hostname
[MAXHOSTNAMELEN
];
2411 static struct rupcache
2413 struct rupcache
*next
;
2417 } *rup_cache
= NULL
, *rupp
;
2419 /* Initialize *login and *pass */
2425 /* Look up in the cache first */
2426 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
)
2428 if (!strcmp (host
, rupp
->host
))
2431 *login
= g_strdup (rupp
->login
);
2432 if (pass
&& rupp
->pass
)
2433 *pass
= g_strdup (rupp
->pass
);
2438 /* Load current .netrc */
2439 netrcname
= g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL
);
2440 if (!g_file_get_contents (netrcname
, &netrc
, NULL
, NULL
))
2448 /* Find our own domain name */
2449 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2452 domain
= strchr (hostname
, '.');
2456 /* Scan for "default" and matching "machine" keywords */
2457 ftpfs_find_machine (host
, domain
);
2459 /* Scan for keywords following "default" and "machine" */
2463 keyword
= ftpfs_netrc_next ();
2468 if (ftpfs_netrc_next () == NETRC_NONE
)
2474 /* We have another name already - should not happen */
2481 /* We have login name now */
2482 *login
= g_strdup (buffer
);
2485 case NETRC_PASSWORD
:
2487 if (ftpfs_netrc_next () == NETRC_NONE
)
2493 /* Ignore unsafe passwords */
2494 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2495 && ftpfs_netrc_bad_mode (netrcname
))
2501 /* Remember password. pass may be NULL, so use tmp_pass */
2502 if (tmp_pass
== NULL
)
2503 tmp_pass
= g_strdup (buffer
);
2507 /* "account" is followed by a token which we ignore */
2508 if (ftpfs_netrc_next () == NETRC_NONE
)
2514 /* Ignore account, but warn user anyways */
2515 ftpfs_netrc_bad_mode (netrcname
);
2519 /* Unexpected keyword or end of file */
2531 rupp
= g_new (struct rupcache
, 1);
2532 rupp
->host
= g_strdup (host
);
2533 rupp
->login
= g_strdup (*login
);
2534 rupp
->pass
= g_strdup (tmp_pass
);
2536 rupp
->next
= rup_cache
;
2544 /* --------------------------------------------------------------------------------------------- */
2545 /*** public functions ****************************************************************************/
2546 /* --------------------------------------------------------------------------------------------- */
2548 /** This routine is called as the last step in load_setup */
2550 ftpfs_init_passwd (void)
2552 ftpfs_anonymous_passwd
= load_anon_passwd ();
2553 if (ftpfs_anonymous_passwd
)
2556 /* If there is no anonymous ftp password specified
2557 * then we'll just use anonymous@
2558 * We don't send any other thing because:
2559 * - We want to remain anonymous
2560 * - We want to stop SPAM
2561 * - We don't want to let ftp sites to discriminate by the user,
2564 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
2567 /* --------------------------------------------------------------------------------------------- */
2572 static struct vfs_s_subclass ftpfs_subclass
;
2576 ftpfs_subclass
.flags
= VFS_S_REMOTE
| VFS_S_USETMP
;
2577 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2578 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2579 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2580 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2581 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2582 ftpfs_subclass
.fh_free_data
= ftpfs_fh_free_data
;
2583 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2584 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2585 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2586 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2587 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2589 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2590 vfs_ftpfs_ops
.name
= "ftpfs";
2591 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2592 vfs_ftpfs_ops
.prefix
= "ftp";
2593 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2594 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2595 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2596 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2597 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2598 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2599 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2600 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2601 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2602 vfs_register_class (&vfs_ftpfs_ops
);
2605 /* --------------------------------------------------------------------------------------------- */