1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1995 Ching Hui
7 1995, 1996, 1997 Miguel de Icaza
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Library General Public License
13 as published by the Free Software Foundation; either version 2 of
14 the License, or (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU Library General Public License for more details.
21 You should have received a copy of the GNU Library General Public
22 License along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
27 * \brief Source: Virtual File System: FTP file system
29 * \author Jakub Jelinek
30 * \author Miguel de Icaza
31 * \author Norbert Warmuth
32 * \author Pavel Machek
33 * \date 1995, 1997, 1998
36 - make it more robust - all the connects etc. should handle EADDRINUSE and
37 ERETRY (have I spelled these names correctly?)
38 - make the user able to flush a connection - all the caches will get empty
39 etc., (tarfs as well), we should give there a user selectable timeout
40 and assign a key sequence.
41 - use hash table instead of linklist to cache ftpfs directory.
46 * NOTE: Usage of tildes is deprecated, consider:
51 * And now: what do I want to do? Do I want to go to /home/pavel or to
52 * /#ftp:hobit/home/pavel? I think first has better sense...
56 int f = !strcmp( remote_path, "/~" );
57 if (f || !strncmp( remote_path, "/~/", 3 )) {
59 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
67 /* \todo Fix: Namespace pollution: horrible */
70 #include <stdlib.h> /* atoi() */
71 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
72 #include <netdb.h> /* struct hostent */
73 #include <sys/socket.h> /* AF_INET */
74 #include <netinet/in.h> /* struct in_addr */
75 #ifdef HAVE_ARPA_INET_H
76 #include <arpa/inet.h>
79 #include <arpa/telnet.h>
80 #include <sys/param.h>
84 #include <sys/time.h> /* gettimeofday() */
86 #include "lib/global.h"
88 #include "lib/tty/tty.h" /* enable/disable interrupt key */
90 #include "src/wtools.h" /* message() */
91 #include "src/main.h" /* print_vfs_message */
92 #include "src/history.h"
93 #include "src/setup.h" /* for load_anon_passwd */
94 #include "lib/mcconfig.h"
97 #include "xdirentry.h"
100 #include "gc.h" /* vfs_stamp_create */
103 #ifndef MAXHOSTNAMELEN
104 # define MAXHOSTNAMELEN 64
107 #define UPLOAD_ZERO_LENGTH_FILE
108 #define SUP super->u.ftp
109 #define FH_SOCK fh->u.ftp.sock
112 #define INADDR_NONE 0xffffffff
115 /* for uclibc < 0.9.29 */
116 #ifndef AI_ADDRCONFIG
117 #define AI_ADDRCONFIG 0x0020
120 #define RFC_AUTODETECT 0
124 #ifndef HAVE_SOCKLEN_T
125 typedef int socklen_t
;
128 static int ftpfs_errno
;
131 /* Delay to retry a connection */
132 int ftpfs_retry_seconds
= 30;
134 /* Method to use to connect to ftp sites */
135 int ftpfs_use_passive_connections
= 1;
136 int ftpfs_use_passive_connections_over_proxy
= 0;
138 /* Method used to get directory listings:
139 * 1: try 'LIST -la <path>', if it fails
140 * fall back to CWD <path>; LIST
141 * 0: always use CWD <path>; LIST
143 int ftpfs_use_unix_list_options
= 1;
145 /* First "CWD <path>", then "LIST -la ." */
146 int ftpfs_first_cd_then_ls
= 1;
148 /* Use the ~/.netrc */
151 /* Anonymous setup */
152 char *ftpfs_anonymous_passwd
= NULL
;
153 int ftpfs_directory_timeout
= 900;
156 char *ftpfs_proxy_host
= NULL
;
158 /* wether we have to use proxy by default? */
159 int ftpfs_always_use_proxy
;
161 #ifdef FIXME_LATER_ALIGATOR
162 static struct linklist
*connections_list
;
165 /* ftpfs_command wait_flag: */
167 #define WAIT_REPLY 0x01
168 #define WANT_STRING 0x02
169 static char reply_str
[80];
171 static struct vfs_class vfs_ftpfs_ops
;
173 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
174 Translate a Unix path, i.e. MC's internal path representation (e.g.
175 /somedir/somefile) to a path valid for the remote server. Every path
176 transfered to the remote server has to be mangled by this function
177 right prior to sending it.
178 Currently only Amiga ftp servers are handled in a special manner.
180 When the remote server is an amiga:
181 a) strip leading slash if necesarry
182 b) replace first occurance of ":/" with ":"
183 c) strip trailing "/."
186 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
187 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
);
188 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
189 __attribute__ ((format (__printf__
, 4, 5)));
190 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
191 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
);
192 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
195 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
197 if (!SUP
.remote_is_amiga
)
198 return g_strdup (remote_path
);
202 if (MEDATA
->logfile
) {
203 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
204 fflush (MEDATA
->logfile
);
207 /* strip leading slash(es) */
208 while (*remote_path
== '/')
212 * Don't change "/" into "", e.g. "CWD " would be
215 if (*remote_path
== '\0')
216 return g_strdup (".");
218 ret
= g_strdup (remote_path
);
220 /* replace first occurance of ":/" with ":" */
221 p
= strchr (ret
, ':');
222 if ((p
!= NULL
) && (*(p
+ 1) == '/'))
223 memmove (p
+ 1, p
+ 2, strlen (p
+ 2) + 1);
225 /* strip trailing "/." */
226 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
232 /* Extract the hostname and username from the path */
235 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
236 * ftp://sunsite.unc.edu/pub/linux
237 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
238 * ftp://tsx-11.mit.edu:8192/
239 * ftp://joe@foo.edu:11321/private
240 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
245 #define FTP_COMMAND_PORT 21
248 ftpfs_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
252 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
256 /* Look up user and password in netrc */
258 ftpfs_netrc_lookup (*host
, user
, pass
);
260 *user
= g_strdup ("anonymous");
263 /* Look up password in netrc for known user */
264 if (use_netrc
&& *user
&& pass
&& !*pass
) {
267 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
269 /* If user is different, remove password */
270 if (new_user
&& strcmp (*user
, new_user
)) {
281 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
283 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
289 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
295 switch (sscanf(answer
, "%d", &code
)){
298 g_strlcpy (string_buf
, answer
, string_len
);
302 if (answer
[3] == '-') {
304 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
310 if ((sscanf (answer
, "%d", &i
) > 0) &&
311 (code
== i
) && (answer
[3] == ' '))
316 g_strlcpy (string_buf
, answer
, string_len
);
323 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
325 int sock
= ftpfs_open_socket (me
, super
);
327 char *cwdir
= SUP
.cwdir
;
331 if (ftpfs_login_server (me
, super
, SUP
.password
)){
334 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
336 return sock
== COMPLETE
;
344 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
349 static int retry
= 0;
350 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
353 cmdstr
= g_strdup_vprintf (fmt
, ap
);
356 cmdlen
= strlen (cmdstr
);
357 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
358 strcpy (cmdstr
+ cmdlen
, "\r\n");
361 if (MEDATA
->logfile
) {
362 if (strncmp (cmdstr
, "PASS ", 5) == 0) {
363 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
365 fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
367 fflush (MEDATA
->logfile
);
371 tty_enable_interrupt_key ();
372 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
377 if (errno
== EPIPE
) { /* Remote server has closed connection */
380 status
= ftpfs_reconnect (me
, super
);
382 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
390 tty_disable_interrupt_key ();
395 tty_disable_interrupt_key ();
399 status
= ftpfs_get_reply (me
, SUP
.sock
,
400 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
401 sizeof (reply_str
) - 1);
402 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
406 status
= ftpfs_reconnect (me
, super
);
408 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
421 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
424 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
425 ftpfs_command(me
, super
, NONE
, "QUIT");
431 g_free (SUP
.password
);
434 /* some defines only used by ftpfs_changetype */
435 /* These two are valid values for the second parameter */
437 #define TYPE_BINARY 1
439 /* This one is only used to initialize bucket->isbinary, don't use it as
440 second parameter to ftpfs_changetype. */
441 #define TYPE_UNKNOWN -1
444 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
446 if (binary
!= SUP
.isbinary
) {
447 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
449 SUP
.isbinary
= binary
;
454 /* This routine logs the user in */
456 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
457 const char *netrcpass
)
461 char *name
; /* login user name */
463 char reply_string
[BUF_MEDIUM
];
465 SUP
.isbinary
= TYPE_UNKNOWN
;
467 if (SUP
.password
) /* explicit password */
468 op
= g_strdup (SUP
.password
);
469 else if (netrcpass
) /* password from netrc */
470 op
= g_strdup (netrcpass
);
471 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp")) {
472 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
473 ftpfs_init_passwd ();
474 op
= g_strdup (ftpfs_anonymous_passwd
);
476 } else { /* ask user */
479 p
= g_strconcat (_(" FTP: Password required for "),
480 SUP
.user
, " ", (char *) NULL
);
481 op
= vfs_get_password (p
);
485 SUP
.password
= g_strdup (op
);
488 if (!anon
|| MEDATA
->logfile
)
491 pass
= g_strconcat ("-", op
, (char *) NULL
);
495 /* Proxy server accepts: username@host-we-want-to-connect */
498 g_strconcat (SUP
.user
, "@",
499 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
,
502 name
= g_strdup (SUP
.user
);
505 (me
, SUP
.sock
, reply_string
,
506 sizeof (reply_string
) - 1) == COMPLETE
) {
507 g_strup (reply_string
);
508 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
509 if (MEDATA
->logfile
) {
510 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n",
511 SUP
.remote_is_amiga
);
512 fflush (MEDATA
->logfile
);
515 print_vfs_message (_("ftpfs: sending login name"));
517 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
)) {
519 print_vfs_message (_("ftpfs: sending user password"));
520 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
521 if (code
== CONTINUE
) {
524 p
= g_strdup_printf (_
525 ("FTP: Account required for user %s"),
527 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
531 print_vfs_message (_("ftpfs: sending user account"));
533 ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
536 if (code
!= COMPLETE
)
541 print_vfs_message (_("ftpfs: logged in"));
542 wipe_password (pass
);
547 SUP
.failed_on_login
= 1;
549 wipe_password (SUP
.password
);
555 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
558 wipe_password (pass
);
563 static struct no_proxy_entry
{
569 ftpfs_load_no_proxy_list (void)
571 /* FixMe: shouldn't be hardcoded!!! */
572 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
573 struct no_proxy_entry
*np
, *current
= 0;
577 static char *mc_file
;
582 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
583 if (exist_file (mc_file
) &&
584 (npf
= fopen (mc_file
, "r"))) {
585 while (fgets (s
, sizeof (s
), npf
)) {
586 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
587 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
597 np
= g_new (struct no_proxy_entry
, 1);
598 np
->domain
= g_strdup (s
);
612 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
614 ftpfs_check_proxy (const char *host
)
616 struct no_proxy_entry
*npe
;
618 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
619 return 0; /* sanity check */
624 if (!ftpfs_always_use_proxy
)
627 if (!strchr (host
, '.'))
630 ftpfs_load_no_proxy_list ();
631 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
632 char *domain
= npe
->domain
;
634 if (domain
[0] == '.') {
635 int ld
= strlen (domain
);
636 int lh
= strlen (host
);
638 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
646 if (!g_strcasecmp (host
, domain
))
654 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
659 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
666 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
668 struct addrinfo hints
, *res
, *curr_res
;
677 /* Use a proxy host? */
678 host
= g_strdup(SUP
.host
);
680 if (!host
|| !*host
){
681 print_vfs_message (_("ftpfs: Invalid host name."));
682 ftpfs_errno
= EINVAL
;
687 /* Hosts to connect to that start with a ! should use proxy */
691 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
694 port
= g_strdup_printf("%hu", (unsigned short) tmp_port
);
701 tty_enable_interrupt_key(); /* clear the interrupt flag */
703 memset (&hints
, 0, sizeof (struct addrinfo
));
704 hints
.ai_socktype
= SOCK_STREAM
;
705 hints
.ai_flags
= AI_ADDRCONFIG
;
708 e
= getaddrinfo (host
, port
, &hints
, &res
);
713 tty_disable_interrupt_key ();
714 print_vfs_message (_("ftpfs: %s"), gai_strerror (e
));
716 ftpfs_errno
= EINVAL
;
720 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
) {
722 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
726 if (curr_res
->ai_next
!= NULL
)
729 tty_disable_interrupt_key();
730 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno
));
737 print_vfs_message (_("ftpfs: making connection to %s"), host
);
741 if ( connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0 )
747 if (errno
== EINTR
&& tty_got_interrupt ()) {
748 print_vfs_message (_("ftpfs: connection interrupted by user"));
749 } else if (res
->ai_next
== NULL
) {
750 print_vfs_message (_("ftpfs: connection to server failed: %s"),
751 unix_error_string (errno
));
757 tty_disable_interrupt_key ();
762 tty_disable_interrupt_key ();
767 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
769 int retry_seconds
, count_down
;
771 /* We do not want to use the passive if we are using proxies */
773 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
777 SUP
.failed_on_login
= 0;
779 SUP
.sock
= ftpfs_open_socket (me
, super
);
783 if (ftpfs_login_server (me
, super
, NULL
)) {
784 /* Logged in, no need to retry the connection */
787 if (SUP
.failed_on_login
){
788 /* Close only the socket descriptor */
793 if (ftpfs_retry_seconds
){
794 retry_seconds
= ftpfs_retry_seconds
;
795 tty_enable_interrupt_key ();
796 for (count_down
= retry_seconds
; count_down
; count_down
--){
797 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
799 if (tty_got_interrupt ()) {
800 /* ftpfs_errno = E; */
801 tty_disable_interrupt_key ();
805 tty_disable_interrupt_key ();
808 } while (retry_seconds
);
810 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
812 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
817 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
818 const char *archive_name
, char *op
)
820 char *host
, *user
, *password
;
825 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
832 if (ftpfs_check_proxy (host
))
833 SUP
.proxy
= ftpfs_proxy_host
;
834 SUP
.password
= password
;
835 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
836 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
837 SUP
.isbinary
= TYPE_UNKNOWN
;
838 SUP
.remote_is_amiga
= 0;
839 super
->name
= g_strdup ("/");
841 vfs_s_new_inode (me
, super
,
842 vfs_s_default_stat (me
, S_IFDIR
| 0755));
844 return ftpfs_open_archive_int (me
, super
);
848 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
849 const char *archive_name
, char *op
, void *cookie
)
858 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
860 port
= ((strcmp (host
, SUP
.host
) == 0)
861 && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
869 /* The returned directory should always contain a trailing slash */
871 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
873 char buf
[BUF_8K
], *bufp
, *bufq
;
875 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
876 ftpfs_get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
878 for (bufq
= buf
; *bufq
; bufq
++)
885 if (*(bufq
- 1) != '/') {
890 return g_strdup (bufp
);
892 /* If the remote server is an Amiga a leading slash
893 might be missing. MC needs it because it is used
894 as separator between hostname and path internally. */
895 return g_strconcat( "/", bufp
, (char *) NULL
);
909 /* Setup Passive ftp connection, we use it for source routed connections */
911 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
912 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
*salen
)
916 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") == COMPLETE
) {
919 c
= strchr (reply_str
, '|');
928 if (port
< 0 || port
> 65535)
932 switch (sa
->ss_family
) {
934 ((struct sockaddr_in
*)sa
)->sin_port
= port
;
937 ((struct sockaddr_in6
*)sa
)->sin6_port
= port
;
940 print_vfs_message (_("ftpfs: invalid address family"));
943 } else if (sa
->ss_family
== AF_INET
) {
944 int xa
, xb
, xc
, xd
, xe
, xf
;
947 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
950 /* Parse remote parameters */
951 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
955 if (!isdigit ((unsigned char) *c
))
957 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
960 n
[0] = (unsigned char) xa
;
961 n
[1] = (unsigned char) xb
;
962 n
[2] = (unsigned char) xc
;
963 n
[3] = (unsigned char) xd
;
964 n
[4] = (unsigned char) xe
;
965 n
[5] = (unsigned char) xf
;
967 memcpy (&(((struct sockaddr_in
*)sa
)->sin_addr
.s_addr
), (void *)n
, 4);
968 memcpy (&(((struct sockaddr_in
*)sa
)->sin_port
), (void *)&n
[4], 2);
972 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
979 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
981 struct sockaddr_storage data_addr
;
982 socklen_t data_addrlen
;
983 int data_sock
, result
;
986 memset (&data_addr
, 0, sizeof (struct sockaddr_storage
));
987 data_addrlen
= sizeof (struct sockaddr_storage
);
989 if (SUP
.use_passive_connection
)
990 result
= getpeername (SUP
.sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
);
992 result
= getsockname (SUP
.sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
);
997 switch (data_addr
.ss_family
) {
999 ((struct sockaddr_in
*)&data_addr
)->sin_port
= 0;
1002 ((struct sockaddr_in6
*)&data_addr
)->sin6_port
= 0;
1005 print_vfs_message (_("ftpfs: invalid address family"));
1009 data_sock
= socket (data_addr
.ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1010 if (data_sock
< 0) {
1011 if (SUP
.use_passive_connection
) {
1012 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno
));
1013 SUP
.use_passive_connection
= 0;
1017 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1021 if (SUP
.use_passive_connection
) {
1023 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1026 SUP
.use_passive_connection
= 0;
1027 print_vfs_message (_("ftpfs: could not setup passive mode"));
1033 /* If passive setup fails, fallback to active connections */
1034 /* Active FTP connection */
1035 if ((bind (data_sock
, (struct sockaddr
*)&data_addr
, data_addrlen
) == 0) &&
1036 (getsockname (data_sock
, (struct sockaddr
*)&data_addr
, &data_addrlen
) == 0) &&
1037 (listen (data_sock
, 1) == 0)) {
1038 unsigned short int port
;
1042 switch (data_addr
.ss_family
) {
1045 port
= ((struct sockaddr_in
*)&data_addr
)->sin_port
;
1049 port
= ((struct sockaddr_in6
*)&data_addr
)->sin6_port
;
1052 print_vfs_message (_("ftpfs: invalid address family"));
1053 ERRNOR (EINVAL
, -1);
1056 port
= ntohs (port
);
1058 addr
= g_try_malloc (NI_MAXHOST
);
1060 ERRNOR (ENOMEM
, -1);
1062 if (getnameinfo ((struct sockaddr
*)&data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0, NI_NUMERICHOST
) != 0) {
1067 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
) {
1073 if (FTP_INET
== af
) {
1074 unsigned char *a
= (unsigned char *)&((struct sockaddr_in
*)&data_addr
)->sin_addr
;
1075 unsigned char *p
= (unsigned char *)&port
;
1077 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1078 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1079 p
[0], p
[1]) == COMPLETE
)
1089 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1090 const char *remote
, int isbinary
, int reget
)
1092 struct sockaddr_storage from
;
1094 socklen_t fromlen
= sizeof(from
);
1096 if ((s
= ftpfs_initconn (me
, super
)) == -1)
1098 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1101 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1106 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1107 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1108 /* WarFtpD can't STORE //filename */
1109 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1110 g_free (remote_path
);
1112 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1115 tty_enable_interrupt_key ();
1116 if (SUP
.use_passive_connection
)
1119 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
1121 ftpfs_errno
= errno
;
1127 tty_disable_interrupt_key ();
1131 #define ABORT_TIMEOUT 5
1133 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1135 struct vfs_s_super
*super
= FH_SUPER
;
1136 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1139 int dsock
= FH_SOCK
;
1141 SUP
.ctl_connection_busy
= 0;
1143 print_vfs_message (_("ftpfs: aborting transfer."));
1144 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
1145 print_vfs_message (_("ftpfs: abort error: %s"),
1146 unix_error_string (errno
));
1152 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1153 print_vfs_message (_("ftpfs: abort failed"));
1160 FD_SET (dsock
, &mask
);
1161 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1162 struct timeval start_tim
, tim
;
1163 gettimeofday (&start_tim
, NULL
);
1164 /* flush the remaining data */
1165 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1166 gettimeofday (&tim
, NULL
);
1167 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1168 /* server keeps sending, drop the connection and ftpfs_reconnect */
1170 ftpfs_reconnect (me
, super
);
1177 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1178 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1183 resolve_symlink_without_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1185 struct linklist
*flist
;
1186 struct direntry
*fe
, *fel
;
1187 char tmp
[MC_MAXPATHLEN
];
1190 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1191 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1192 /* flist->data->l_stat is alread initialized with 0 */
1194 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1195 if (fel
->linkname
[0] == '/') {
1196 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1198 strcpy (tmp
, fel
->linkname
);
1200 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1202 strcpy (tmp
, dir
->remote_path
);
1205 strcat (tmp
+ 1, fel
->linkname
);
1207 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1208 canonicalize_pathname (tmp
);
1209 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1211 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1212 /* Symlink points to link which isn't resolved, yet. */
1213 if (fe
->linkname
[0] == '/') {
1214 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1216 strcpy (tmp
, fe
->linkname
);
1218 /* at this point tmp looks always like this
1219 /directory/filename, i.e. no need to check
1220 strrchr's return value */
1221 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1222 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1224 strcat (tmp
, fe
->linkname
);
1228 fel
->l_stat
= g_new (struct stat
, 1);
1229 if ( S_ISLNK (fe
->s
.st_mode
))
1230 *fel
->l_stat
= *fe
->l_stat
;
1232 *fel
->l_stat
= fe
->s
;
1233 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1240 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1244 resolve_symlink_with_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1246 char buffer
[2048] = "", *filename
;
1250 struct linklist
*flist
;
1251 struct direntry
*fe
;
1252 int switch_method
= 0;
1254 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1255 if (strchr (dir
->remote_path
, ' ')) {
1256 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1257 print_vfs_message(_("ftpfs: CWD failed."));
1260 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1263 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa",
1264 dir
->remote_path
, TYPE_ASCII
, 0);
1267 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1271 fp
= fdopen(sock
, "r");
1274 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1277 tty_enable_interrupt_key ();
1278 flist
= dir
->file_list
->next
;
1281 if (flist
== dir
->file_list
)
1284 flist
= flist
->next
;
1285 } while (!S_ISLNK(fe
->s
.st_mode
));
1287 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1289 if (MEDATA
->logfile
){
1290 fputs (buffer
, MEDATA
->logfile
);
1291 fflush (MEDATA
->logfile
);
1293 vfs_die("This code should be commented out\n");
1294 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1295 int r
= strcmp(fe
->name
, filename
);
1298 if (S_ISLNK (s
.st_mode
)) {
1299 /* This server doesn't understand LIST -lLa */
1303 fe
->l_stat
= g_new (struct stat
, 1);
1304 if (fe
->l_stat
== NULL
)
1307 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1316 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1317 tty_disable_interrupt_key ();
1319 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1323 resolve_symlink(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1325 print_vfs_message(_("Resolving symlink..."));
1327 if (SUP
.strict_rfc959_list_cmd
)
1328 resolve_symlink_without_ls_options(me
, super
, dir
);
1330 resolve_symlink_with_ls_options(me
, super
, dir
);
1335 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1337 struct vfs_s_entry
*ent
;
1338 struct vfs_s_super
*super
= dir
->super
;
1339 int sock
, num_entries
= 0;
1340 char buffer
[BUF_8K
];
1343 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1344 || (strchr (remote_path
, ' ') != NULL
);
1347 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1350 RFC_STRICT
? _("(strict rfc959)") : "",
1351 cd_first
? _("(chdir first)") : "");
1354 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
) {
1355 ftpfs_errno
= ENOENT
;
1356 print_vfs_message (_("ftpfs: CWD failed."));
1361 gettimeofday (&dir
->timestamp
, NULL
);
1362 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1364 if (SUP
.strict
== RFC_STRICT
)
1365 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1367 /* Dirty hack to avoid autoprepending / to . */
1368 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1370 ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1372 /* Trailing "/." is necessary if remote_path is a symlink */
1373 char *path
= concat_dir_and_file (remote_path
, ".");
1375 ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
,
1383 /* Clear the interrupt flag */
1384 tty_enable_interrupt_key ();
1389 vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1395 me
->verrno
= ECONNRESET
;
1397 tty_disable_interrupt_key ();
1398 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1399 print_vfs_message (_("%s: failure"), me
->name
);
1403 if (MEDATA
->logfile
) {
1404 fputs (buffer
, MEDATA
->logfile
);
1405 fputs ("\n", MEDATA
->logfile
);
1406 fflush (MEDATA
->logfile
);
1409 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1410 i
= ent
->ino
->st
.st_nlink
;
1411 if (!vfs_parse_ls_lga
1412 (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1413 vfs_s_free_entry (me
, ent
);
1416 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1418 vfs_s_insert_entry (me
, dir
, ent
);
1422 me
->verrno
= E_REMOTE
;
1423 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1426 if (num_entries
== 0 && cd_first
== 0) {
1427 /* The LIST command may produce an empty output. In such scenario
1428 it is not clear whether this is caused by `remote_path' being
1429 a non-existent path or for some other reason (listing emtpy
1430 directory without the -a option, non-readable directory, etc.).
1432 Since `dir_load' is a crucial method, when it comes to determine
1433 whether a given path is a _directory_, the code must try its best
1434 to determine the type of `remote_path'. The only reliable way to
1435 achieve this is trough issuing a CWD command. */
1441 if (SUP
.strict
== RFC_AUTODETECT
)
1442 SUP
.strict
= RFC_DARING
;
1444 print_vfs_message (_("%s: done."), me
->name
);
1448 if (SUP
.strict
== RFC_AUTODETECT
) {
1449 /* It's our first attempt to get a directory listing from this
1450 server (UNIX style LIST command) */
1451 SUP
.strict
= RFC_STRICT
;
1452 /* I hate goto, but recursive call needs another 8K on stack */
1453 /* return ftpfs_dir_load (me, dir, remote_path); */
1457 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1458 ERRNOR (EACCES
, -1);
1462 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
,
1465 int h
, sock
, n_read
, n_written
;
1467 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1475 struct vfs_s_super
*super
= FH_SUPER
;
1477 h
= open (localname
, O_RDONLY
);
1481 ftpfs_open_data_connection (me
, super
,
1482 fh
->u
.ftp
.append
? "APPE" : "STOR", name
,
1484 if (sock
< 0 || fstat (h
, &s
) == -1) {
1488 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1491 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1493 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1497 tty_enable_interrupt_key ();
1499 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1) {
1500 if (errno
== EINTR
) {
1501 if (tty_got_interrupt ()) {
1502 ftpfs_errno
= EINTR
;
1507 ftpfs_errno
= errno
;
1514 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
) {
1515 if (n_written
== -1) {
1516 if (errno
== EINTR
&& !tty_got_interrupt ()) {
1519 ftpfs_errno
= errno
;
1523 n_read
-= n_written
;
1525 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1526 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1528 tty_disable_interrupt_key ();
1531 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1535 tty_disable_interrupt_key ();
1538 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1543 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1545 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1549 FH_SOCK
= ftpfs_open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1553 fh
->linear
= LS_LINEAR_OPEN
;
1554 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1555 fh
->u
.ftp
.append
= 0;
1560 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1563 struct vfs_s_super
*super
= FH_SUPER
;
1565 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1566 if ((errno
== EINTR
) && !tty_got_interrupt ())
1572 ftpfs_linear_abort(me
, fh
);
1575 SUP
.ctl_connection_busy
= 0;
1578 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1579 ERRNOR (E_REMOTE
, -1);
1586 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1589 ftpfs_linear_abort(me
, fh
);
1592 static int ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1597 case VFS_CTL_IS_NOTREADY
:
1602 vfs_die ("You may not do this");
1603 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1606 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1607 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1617 ftpfs_send_command(struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1620 char *p
, *mpath
= g_strdup(filename
);
1621 struct vfs_s_super
*super
;
1623 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1625 if (!(rpath
= vfs_s_get_path_mangle(me
, mpath
, &super
, 0))) {
1629 p
= ftpfs_translate_path (me
, super
, rpath
);
1630 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1632 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1633 if (flags
& OPT_IGNORE_ERROR
)
1635 if (r
!= COMPLETE
) {
1640 if (flush_directory_cache
)
1641 vfs_s_invalidate(me
, super
);
1646 /* This routine is called as the last step in load_setup */
1648 ftpfs_init_passwd(void)
1650 ftpfs_anonymous_passwd
= load_anon_passwd ();
1651 if (ftpfs_anonymous_passwd
)
1654 /* If there is no anonymous ftp password specified
1655 * then we'll just use anonymous@
1656 * We don't send any other thing because:
1657 * - We want to remain anonymous
1658 * - We want to stop SPAM
1659 * - We don't want to let ftp sites to discriminate by the user,
1662 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1665 static int ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1667 char buf
[BUF_SMALL
];
1670 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1672 ret
= ftpfs_send_command(me
, path
, buf
, OPT_FLUSH
);
1674 if ( mc_config_get_bool (mc_main_config
, CONFIG_APP_SECTION
,
1675 "ignore_ftp_chattr_errors", TRUE
)) {
1682 static int ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1685 ftpfs_errno
= EPERM
;
1688 /* Everyone knows it is not possible to chown remotely, so why bother them.
1689 If someone's root, then copy/move will always try to chown it... */
1698 static int ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1700 return ftpfs_send_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1703 /* Return 1 if path is the same directory as the one we are in now */
1705 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1711 if (strcmp (path
, SUP
.cwdir
) == 0)
1717 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1722 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1725 p
= ftpfs_translate_path (me
, super
, remote_path
);
1726 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1729 if (r
!= COMPLETE
) {
1733 SUP
.cwdir
= g_strdup (remote_path
);
1734 SUP
.cwd_deferred
= 0;
1739 static int ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1741 ftpfs_send_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1742 return ftpfs_send_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1745 static int ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1747 (void) mode
; /* FIXME: should be used */
1749 return ftpfs_send_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1752 static int ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1754 return ftpfs_send_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1758 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
,
1763 fh
->u
.ftp
.append
= 0;
1764 /* File will be written only, so no need to retrieve it from ftp server */
1765 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
))) {
1766 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1773 /* ftpfs_linear_start() called, so data will be written
1774 * to local temporary file and stored to ftp server
1775 * by vfs_s_close later
1777 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
) {
1778 if (!fh
->ino
->localname
) {
1779 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1780 fh
->ino
->ent
->name
);
1784 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1788 name
= vfs_s_fullpath (me
, fh
->ino
);
1792 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1793 (flags
& O_APPEND
) ? "APPE" :
1794 "STOR", name
, TYPE_BINARY
, 0);
1799 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1803 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1805 if (fh
->ino
->localname
) {
1806 unlink (fh
->ino
->localname
);
1807 g_free (fh
->ino
->localname
);
1808 fh
->ino
->localname
= NULL
;
1813 if (!fh
->ino
->localname
)
1814 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1816 if (!fh
->ino
->localname
)
1817 vfs_die ("retrieve_file failed to fill in localname");
1821 static int ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1823 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1826 /* File is stored to destination already, so
1827 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1830 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1832 vfs_s_invalidate (me
, FH_SUPER
);
1838 ftpfs_done (struct vfs_class
*me
)
1840 struct no_proxy_entry
*np
;
1845 np
= no_proxy
->next
;
1846 g_free (no_proxy
->domain
);
1850 g_free (ftpfs_anonymous_passwd
);
1851 g_free (ftpfs_proxy_host
);
1855 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
1857 struct vfs_s_super
*super
= MEDATA
->supers
;
1861 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
1864 super
= super
->next
;
1868 static char buffer
[BUF_MEDIUM
];
1870 static const char *netrcp
;
1872 /* This should match the keywords[] array below */
1885 static keyword_t
ftpfs_netrc_next (void)
1889 static const char *const keywords
[] = { "default", "machine",
1890 "login", "password", "passwd", "account", "macdef", NULL
1895 netrcp
= skip_separators (netrcp
);
1896 if (*netrcp
!= '\n')
1903 if (*netrcp
== '"') {
1904 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1905 if (*netrcp
== '\\')
1910 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1911 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1912 if (*netrcp
== '\\')
1922 while (keywords
[i
- 1]) {
1923 if (!strcmp (keywords
[i
- 1], buffer
))
1929 return NETRC_UNKNOWN
;
1932 static int ftpfs_netrc_bad_mode (const char *netrcname
)
1934 static int be_angry
= 1;
1937 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1939 message (D_ERROR
, MSG_ERROR
,
1940 _("~/.netrc file has incorrect mode.\n"
1941 "Remove password or correct mode."));
1949 /* Scan .netrc until we find matching "machine" or "default"
1950 * domain is used for additional matching
1951 * No search is done after "default" in compliance with "man netrc"
1952 * Return 0 if found, -1 otherwise */
1953 static int ftpfs_find_machine (const char *host
, const char *domain
)
1957 if (!host
) host
= "";
1958 if (!domain
) domain
= "";
1960 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
) {
1961 if (keyword
== NETRC_DEFAULT
)
1964 if (keyword
== NETRC_MACDEF
) {
1965 /* Scan for an empty line, which concludes "macdef" */
1967 while (*netrcp
&& *netrcp
!= '\n')
1969 if (*netrcp
!= '\n')
1972 } while (*netrcp
&& *netrcp
!= '\n');
1976 if (keyword
!= NETRC_MACHINE
)
1979 /* Take machine name */
1980 if (ftpfs_netrc_next () == NETRC_NONE
)
1983 if (g_strcasecmp (host
, buffer
)) {
1984 /* Try adding our domain to short names in .netrc */
1985 const char *host_domain
= strchr (host
, '.');
1989 /* Compare domain part */
1990 if (g_strcasecmp (host_domain
, domain
))
1993 /* Compare local part */
1994 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
2005 /* Extract login and password from .netrc for the host.
2007 * Returns 0 for success, -1 for error */
2008 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
2011 char *tmp_pass
= NULL
;
2012 char hostname
[MAXHOSTNAMELEN
];
2015 static struct rupcache
{
2016 struct rupcache
*next
;
2020 } *rup_cache
= NULL
, *rupp
;
2022 /* Initialize *login and *pass */
2029 /* Look up in the cache first */
2030 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
2031 if (!strcmp (host
, rupp
->host
)) {
2033 *login
= g_strdup (rupp
->login
);
2034 if (pass
&& rupp
->pass
)
2035 *pass
= g_strdup (rupp
->pass
);
2040 /* Load current .netrc */
2041 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
2042 netrcp
= netrc
= load_file (netrcname
);
2043 if (netrc
== NULL
) {
2048 /* Find our own domain name */
2049 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2051 if (!(domain
= strchr (hostname
, '.')))
2054 /* Scan for "default" and matching "machine" keywords */
2055 ftpfs_find_machine (host
, domain
);
2057 /* Scan for keywords following "default" and "machine" */
2060 keyword
= ftpfs_netrc_next ();
2064 if (ftpfs_netrc_next () == NETRC_NONE
) {
2069 /* We have another name already - should not happen */
2075 /* We have login name now */
2076 *login
= g_strdup (buffer
);
2079 case NETRC_PASSWORD
:
2081 if (ftpfs_netrc_next () == NETRC_NONE
) {
2086 /* Ignore unsafe passwords */
2087 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2088 && ftpfs_netrc_bad_mode (netrcname
)) {
2093 /* Remember password. pass may be NULL, so use tmp_pass */
2094 if (tmp_pass
== NULL
)
2095 tmp_pass
= g_strdup (buffer
);
2099 /* "account" is followed by a token which we ignore */
2100 if (ftpfs_netrc_next () == NETRC_NONE
) {
2105 /* Ignore account, but warn user anyways */
2106 ftpfs_netrc_bad_mode (netrcname
);
2110 /* Unexpected keyword or end of file */
2122 rupp
= g_new (struct rupcache
, 1);
2123 rupp
->host
= g_strdup (host
);
2124 rupp
->login
= rupp
->pass
= 0;
2126 if (*login
!= NULL
) {
2127 rupp
->login
= g_strdup (*login
);
2129 if (tmp_pass
!= NULL
)
2130 rupp
->pass
= g_strdup (tmp_pass
);
2131 rupp
->next
= rup_cache
;
2143 static struct vfs_s_subclass ftpfs_subclass
;
2147 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
2148 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2149 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2150 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2151 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2152 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2153 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2154 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2155 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2156 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2157 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2159 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2160 vfs_ftpfs_ops
.name
= "ftpfs";
2161 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2162 vfs_ftpfs_ops
.prefix
= "ftp:";
2163 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2164 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2165 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2166 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2167 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2168 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2169 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2170 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2171 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2172 vfs_register_class (&vfs_ftpfs_ops
);