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
,
188 const char *remote_path
);
189 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
,
190 const char *fmt
, ...) __attribute__ ((format (__printf__
, 4, 5)));
191 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
192 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
193 const char *netrcpass
);
194 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
197 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
199 if (!SUP
.remote_is_amiga
)
200 return g_strdup (remote_path
);
207 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
208 fflush (MEDATA
->logfile
);
211 /* strip leading slash(es) */
212 while (*remote_path
== '/')
216 * Don't change "/" into "", e.g. "CWD " would be
219 if (*remote_path
== '\0')
220 return g_strdup (".");
222 ret
= g_strdup (remote_path
);
224 /* replace first occurance of ":/" with ":" */
225 p
= strchr (ret
, ':');
226 if ((p
!= NULL
) && (*(p
+ 1) == '/'))
227 memmove (p
+ 1, p
+ 2, strlen (p
+ 2) + 1);
229 /* strip trailing "/." */
230 p
= strrchr (ret
, '/');
231 if ((p
!= NULL
) && (*(p
+ 1) == '.') && (*(p
+ 2) == '\0'))
238 /* Extract the hostname and username from the path */
241 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
242 * ftp://sunsite.unc.edu/pub/linux
243 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
244 * ftp://tsx-11.mit.edu:8192/
245 * ftp://joe@foo.edu:11321/private
246 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
251 #define FTP_COMMAND_PORT 21
254 ftpfs_split_url (char *path
, char **host
, char **user
, int *port
, char **pass
)
258 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
262 /* Look up user and password in netrc */
264 ftpfs_netrc_lookup (*host
, user
, pass
);
266 *user
= g_strdup ("anonymous");
269 /* Look up password in netrc for known user */
270 if (use_netrc
&& *user
&& pass
&& !*pass
)
274 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
276 /* If user is different, remove password */
277 if (new_user
&& strcmp (*user
, new_user
))
289 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
291 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
298 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
305 switch (sscanf (answer
, "%d", &code
))
309 g_strlcpy (string_buf
, answer
, string_len
);
313 if (answer
[3] == '-')
317 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n'))
324 if ((sscanf (answer
, "%d", &i
) > 0) && (code
== i
) && (answer
[3] == ' '))
329 g_strlcpy (string_buf
, answer
, string_len
);
336 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
338 int sock
= ftpfs_open_socket (me
, super
);
341 char *cwdir
= SUP
.cwdir
;
345 if (ftpfs_login_server (me
, super
, SUP
.password
))
349 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
351 return sock
== COMPLETE
;
359 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
,
365 static int retry
= 0;
366 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
369 cmdstr
= g_strdup_vprintf (fmt
, ap
);
372 cmdlen
= strlen (cmdstr
);
373 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
374 strcpy (cmdstr
+ cmdlen
, "\r\n");
379 if (strncmp (cmdstr
, "PASS ", 5) == 0)
381 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
386 ret
= fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
389 fflush (MEDATA
->logfile
);
393 tty_enable_interrupt_key ();
394 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
401 { /* Remote server has closed connection */
405 status
= ftpfs_reconnect (me
, super
);
407 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0))
416 tty_disable_interrupt_key ();
421 tty_disable_interrupt_key ();
425 status
= ftpfs_get_reply (me
, SUP
.sock
,
426 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
427 sizeof (reply_str
) - 1);
428 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
432 status
= ftpfs_reconnect (me
, super
);
434 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0))
448 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
452 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
453 ftpfs_command (me
, super
, NONE
, "QUIT");
459 g_free (SUP
.password
);
462 /* some defines only used by ftpfs_changetype */
463 /* These two are valid values for the second parameter */
465 #define TYPE_BINARY 1
467 /* This one is only used to initialize bucket->isbinary, don't use it as
468 second parameter to ftpfs_changetype. */
469 #define TYPE_UNKNOWN -1
472 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
474 if (binary
!= SUP
.isbinary
)
476 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
478 SUP
.isbinary
= binary
;
483 /* This routine logs the user in */
485 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
)
489 char *name
; /* login user name */
491 char reply_string
[BUF_MEDIUM
];
493 SUP
.isbinary
= TYPE_UNKNOWN
;
495 if (SUP
.password
) /* explicit password */
496 op
= g_strdup (SUP
.password
);
497 else if (netrcpass
) /* password from netrc */
498 op
= g_strdup (netrcpass
);
499 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp"))
501 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
502 ftpfs_init_passwd ();
503 op
= g_strdup (ftpfs_anonymous_passwd
);
510 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
, " ", (char *) NULL
);
511 op
= vfs_get_password (p
);
515 SUP
.password
= g_strdup (op
);
518 if (!anon
|| MEDATA
->logfile
)
522 pass
= g_strconcat ("-", op
, (char *) NULL
);
526 /* Proxy server accepts: username@host-we-want-to-connect */
530 g_strconcat (SUP
.user
, "@",
531 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
, (char *) NULL
);
534 name
= g_strdup (SUP
.user
);
536 if (ftpfs_get_reply (me
, SUP
.sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
)
538 g_strup (reply_string
);
539 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
542 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n", SUP
.remote_is_amiga
);
543 fflush (MEDATA
->logfile
);
546 print_vfs_message (_("ftpfs: sending login name"));
548 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
))
551 print_vfs_message (_("ftpfs: sending user password"));
552 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
553 if (code
== CONTINUE
)
557 p
= g_strdup_printf (_("FTP: Account required for user %s"), SUP
.user
);
558 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
562 print_vfs_message (_("ftpfs: sending user account"));
563 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
566 if (code
!= COMPLETE
)
571 print_vfs_message (_("ftpfs: logged in"));
572 wipe_password (pass
);
577 SUP
.failed_on_login
= 1;
579 wipe_password (SUP
.password
);
585 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "), SUP
.user
);
587 wipe_password (pass
);
592 static struct no_proxy_entry
599 ftpfs_load_no_proxy_list (void)
601 /* FixMe: shouldn't be hardcoded!!! */
602 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
603 struct no_proxy_entry
*np
, *current
= 0;
607 static char *mc_file
;
612 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
613 if (exist_file (mc_file
))
615 npf
= fopen (mc_file
, "r");
618 while (fgets (s
, sizeof (s
), npf
) != NULL
)
620 p
= strchr (s
, '\n');
621 if (p
== NULL
) { /* skip bogus entries */
623 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
633 np
= g_new (struct no_proxy_entry
, 1);
634 np
->domain
= g_strdup (s
);
648 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
650 ftpfs_check_proxy (const char *host
)
652 struct no_proxy_entry
*npe
;
654 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
655 return 0; /* sanity check */
660 if (!ftpfs_always_use_proxy
)
663 if (!strchr (host
, '.'))
666 ftpfs_load_no_proxy_list ();
667 for (npe
= no_proxy
; npe
; npe
= npe
->next
)
669 char *domain
= npe
->domain
;
671 if (domain
[0] == '.')
673 int ld
= strlen (domain
);
674 int lh
= strlen (host
);
676 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1])
685 else if (!g_strcasecmp (host
, domain
))
693 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
697 dir
= vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
, URL_USE_ANONYMOUS
);
703 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
705 struct addrinfo hints
, *res
, *curr_res
;
714 /* Use a proxy host? */
715 host
= g_strdup (SUP
.host
);
719 print_vfs_message (_("ftpfs: Invalid host name."));
720 ftpfs_errno
= EINVAL
;
725 /* Hosts to connect to that start with a ! should use proxy */
730 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
733 port
= g_strdup_printf ("%hu", (unsigned short) tmp_port
);
741 tty_enable_interrupt_key (); /* clear the interrupt flag */
743 memset (&hints
, 0, sizeof (struct addrinfo
));
744 hints
.ai_socktype
= SOCK_STREAM
;
745 hints
.ai_flags
= AI_ADDRCONFIG
;
748 e
= getaddrinfo (host
, port
, &hints
, &res
);
754 tty_disable_interrupt_key ();
755 print_vfs_message (_("ftpfs: %s"), gai_strerror (e
));
757 ftpfs_errno
= EINVAL
;
761 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
)
764 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
769 if (curr_res
->ai_next
!= NULL
)
772 tty_disable_interrupt_key ();
773 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno
));
780 print_vfs_message (_("ftpfs: making connection to %s"), host
);
784 if (connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0)
790 if (errno
== EINTR
&& tty_got_interrupt ())
792 print_vfs_message (_("ftpfs: connection interrupted by user"));
794 else if (res
->ai_next
== NULL
)
796 print_vfs_message (_("ftpfs: connection to server failed: %s"),
797 unix_error_string (errno
));
805 tty_disable_interrupt_key ();
810 tty_disable_interrupt_key ();
815 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
817 int retry_seconds
, count_down
;
819 /* We do not want to use the passive if we are using proxies */
821 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
826 SUP
.failed_on_login
= 0;
828 SUP
.sock
= ftpfs_open_socket (me
, super
);
832 if (ftpfs_login_server (me
, super
, NULL
))
834 /* Logged in, no need to retry the connection */
839 if (SUP
.failed_on_login
)
841 /* Close only the socket descriptor */
848 if (ftpfs_retry_seconds
)
850 retry_seconds
= ftpfs_retry_seconds
;
851 tty_enable_interrupt_key ();
852 for (count_down
= retry_seconds
; count_down
; count_down
--)
854 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"),
857 if (tty_got_interrupt ())
859 /* ftpfs_errno = E; */
860 tty_disable_interrupt_key ();
864 tty_disable_interrupt_key ();
868 while (retry_seconds
);
870 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
872 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
877 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
878 const char *archive_name
, char *op
)
880 char *host
, *user
, *password
;
885 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
892 if (ftpfs_check_proxy (host
))
893 SUP
.proxy
= ftpfs_proxy_host
;
894 SUP
.password
= password
;
895 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
896 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
897 SUP
.isbinary
= TYPE_UNKNOWN
;
898 SUP
.remote_is_amiga
= 0;
899 super
->name
= g_strdup ("/");
900 super
->root
= vfs_s_new_inode (me
, super
, vfs_s_default_stat (me
, S_IFDIR
| 0755));
902 return ftpfs_open_archive_int (me
, super
);
906 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
907 const char *archive_name
, char *op
, void *cookie
)
916 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
918 port
= ((strcmp (host
, SUP
.host
) == 0) && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
926 /* The returned directory should always contain a trailing slash */
928 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
930 char buf
[BUF_8K
], *bufp
, *bufq
;
932 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
933 ftpfs_get_reply (me
, SUP
.sock
, buf
, sizeof (buf
)) == COMPLETE
)
936 for (bufq
= buf
; *bufq
; bufq
++)
948 if (*(bufq
- 1) != '/')
954 return g_strdup (bufp
);
957 /* If the remote server is an Amiga a leading slash
958 might be missing. MC needs it because it is used
959 as separator between hostname and path internally. */
960 return g_strconcat ("/", bufp
, (char *) NULL
);
976 /* Setup Passive ftp connection, we use it for source routed connections */
978 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
979 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
* salen
)
983 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") == COMPLETE
)
987 c
= strchr (reply_str
, '|');
996 if (port
< 0 || port
> 65535)
1000 switch (sa
->ss_family
)
1003 ((struct sockaddr_in
*) sa
)->sin_port
= port
;
1006 ((struct sockaddr_in6
*) sa
)->sin6_port
= port
;
1009 print_vfs_message (_("ftpfs: invalid address family"));
1010 ERRNOR (EINVAL
, -1);
1013 else if (sa
->ss_family
== AF_INET
)
1015 int xa
, xb
, xc
, xd
, xe
, xf
;
1018 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
1021 /* Parse remote parameters */
1022 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
1026 if (!isdigit ((unsigned char) *c
))
1028 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
1031 n
[0] = (unsigned char) xa
;
1032 n
[1] = (unsigned char) xb
;
1033 n
[2] = (unsigned char) xc
;
1034 n
[3] = (unsigned char) xd
;
1035 n
[4] = (unsigned char) xe
;
1036 n
[5] = (unsigned char) xf
;
1038 memcpy (&(((struct sockaddr_in
*) sa
)->sin_addr
.s_addr
), (void *) n
, 4);
1039 memcpy (&(((struct sockaddr_in
*) sa
)->sin_port
), (void *) &n
[4], 2);
1044 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
1051 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
1053 struct sockaddr_storage data_addr
;
1054 socklen_t data_addrlen
;
1055 int data_sock
, result
;
1058 memset (&data_addr
, 0, sizeof (struct sockaddr_storage
));
1059 data_addrlen
= sizeof (struct sockaddr_storage
);
1061 if (SUP
.use_passive_connection
)
1062 result
= getpeername (SUP
.sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
);
1064 result
= getsockname (SUP
.sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
);
1069 switch (data_addr
.ss_family
)
1072 ((struct sockaddr_in
*) &data_addr
)->sin_port
= 0;
1075 ((struct sockaddr_in6
*) &data_addr
)->sin6_port
= 0;
1078 print_vfs_message (_("ftpfs: invalid address family"));
1079 ERRNOR (EINVAL
, -1);
1082 data_sock
= socket (data_addr
.ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1085 if (SUP
.use_passive_connection
)
1087 print_vfs_message (_("ftpfs: could not setup passive mode: %s"),
1088 unix_error_string (errno
));
1089 SUP
.use_passive_connection
= 0;
1093 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1097 if (SUP
.use_passive_connection
)
1100 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1103 SUP
.use_passive_connection
= 0;
1104 print_vfs_message (_("ftpfs: could not setup passive mode"));
1110 /* If passive setup fails, fallback to active connections */
1111 /* Active FTP connection */
1112 if ((bind (data_sock
, (struct sockaddr
*) &data_addr
, data_addrlen
) == 0) &&
1113 (getsockname (data_sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == 0) &&
1114 (listen (data_sock
, 1) == 0))
1116 unsigned short int port
;
1120 switch (data_addr
.ss_family
)
1124 port
= ((struct sockaddr_in
*) &data_addr
)->sin_port
;
1128 port
= ((struct sockaddr_in6
*) &data_addr
)->sin6_port
;
1131 print_vfs_message (_("ftpfs: invalid address family"));
1132 ERRNOR (EINVAL
, -1);
1135 port
= ntohs (port
);
1137 addr
= g_try_malloc (NI_MAXHOST
);
1139 ERRNOR (ENOMEM
, -1);
1142 ((struct sockaddr
*) &data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0,
1143 NI_NUMERICHOST
) != 0)
1149 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
)
1158 unsigned char *a
= (unsigned char *) &((struct sockaddr_in
*) &data_addr
)->sin_addr
;
1159 unsigned char *p
= (unsigned char *) &port
;
1161 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1162 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1163 p
[0], p
[1]) == COMPLETE
)
1173 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1174 const char *remote
, int isbinary
, int reget
)
1176 struct sockaddr_storage from
;
1178 socklen_t fromlen
= sizeof(from
);
1180 s
= ftpfs_initconn (me
, super
);
1184 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1188 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1194 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1195 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1196 /* WarFtpD can't STORE //filename */
1197 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1198 g_free (remote_path
);
1201 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1205 tty_enable_interrupt_key ();
1206 if (SUP
.use_passive_connection
)
1210 data
= accept (s
, (struct sockaddr
*) &from
, &fromlen
);
1213 ftpfs_errno
= errno
;
1219 tty_disable_interrupt_key ();
1223 #define ABORT_TIMEOUT 5
1225 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1227 struct vfs_s_super
*super
= FH_SUPER
;
1228 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1231 int dsock
= FH_SOCK
;
1233 SUP
.ctl_connection_busy
= 0;
1235 print_vfs_message (_("ftpfs: aborting transfer."));
1236 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
))
1238 print_vfs_message (_("ftpfs: abort error: %s"), unix_error_string (errno
));
1244 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
)
1246 print_vfs_message (_("ftpfs: abort failed"));
1254 FD_SET (dsock
, &mask
);
1255 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0)
1257 struct timeval start_tim
, tim
;
1258 gettimeofday (&start_tim
, NULL
);
1259 /* flush the remaining data */
1260 while (read (dsock
, buf
, sizeof (buf
)) > 0)
1262 gettimeofday (&tim
, NULL
);
1263 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
)
1265 /* server keeps sending, drop the connection and ftpfs_reconnect */
1267 ftpfs_reconnect (me
, super
);
1274 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1275 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1280 resolve_symlink_without_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1281 struct vfs_s_inode
*dir
)
1283 struct linklist
*flist
;
1284 struct direntry
*fe
, *fel
;
1285 char tmp
[MC_MAXPATHLEN
];
1288 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1289 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
)
1291 /* flist->data->l_stat is alread initialized with 0 */
1293 if (S_ISLNK (fel
->s
.st_mode
) && fel
->linkname
)
1295 if (fel
->linkname
[0] == '/')
1297 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1299 strcpy (tmp
, fel
->linkname
);
1303 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1305 strcpy (tmp
, dir
->remote_path
);
1308 strcat (tmp
+ 1, fel
->linkname
);
1310 for (depth
= 0; depth
< 100; depth
++)
1311 { /* depth protects against recursive symbolic links */
1312 canonicalize_pathname (tmp
);
1313 fe
= _get_file_entry (bucket
, tmp
, 0, 0);
1316 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0)
1318 /* Symlink points to link which isn't resolved, yet. */
1319 if (fe
->linkname
[0] == '/')
1321 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1323 strcpy (tmp
, fe
->linkname
);
1327 /* at this point tmp looks always like this
1328 /directory/filename, i.e. no need to check
1329 strrchr's return value */
1330 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1331 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1333 strcat (tmp
, fe
->linkname
);
1339 fel
->l_stat
= g_new (struct stat
, 1);
1340 if (S_ISLNK (fe
->s
.st_mode
))
1341 *fel
->l_stat
= *fe
->l_stat
;
1343 *fel
->l_stat
= fe
->s
;
1344 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1351 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1355 resolve_symlink_with_ls_options (struct vfs_class
*me
, struct vfs_s_super
*super
,
1356 struct vfs_s_inode
*dir
)
1358 char buffer
[2048] = "", *filename
;
1362 struct linklist
*flist
;
1363 struct direntry
*fe
;
1364 int switch_method
= 0;
1366 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1367 if (strchr (dir
->remote_path
, ' '))
1369 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
)
1371 print_vfs_message (_("ftpfs: CWD failed."));
1374 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1377 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", dir
->remote_path
, TYPE_ASCII
, 0);
1381 print_vfs_message (_("ftpfs: couldn't resolve symlink"));
1385 fp
= fdopen (sock
, "r");
1389 print_vfs_message (_("ftpfs: couldn't resolve symlink"));
1392 tty_enable_interrupt_key ();
1393 flist
= dir
->file_list
->next
;
1398 if (flist
== dir
->file_list
)
1401 flist
= flist
->next
;
1403 while (!S_ISLNK (fe
->s
.st_mode
));
1406 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1408 if (MEDATA
->logfile
)
1410 fputs (buffer
, MEDATA
->logfile
);
1411 fflush (MEDATA
->logfile
);
1413 vfs_die ("This code should be commented out\n");
1414 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
))
1416 int r
= strcmp (fe
->name
, filename
);
1420 if (S_ISLNK (s
.st_mode
))
1422 /* This server doesn't understand LIST -lLa */
1426 fe
->l_stat
= g_new (struct stat
, 1);
1427 if (fe
->l_stat
== NULL
)
1430 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1439 while (fgets (buffer
, sizeof (buffer
), fp
) != NULL
);
1440 tty_disable_interrupt_key ();
1442 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1446 resolve_symlink (struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1448 print_vfs_message (_("Resolving symlink..."));
1450 if (SUP
.strict_rfc959_list_cmd
)
1451 resolve_symlink_without_ls_options (me
, super
, dir
);
1453 resolve_symlink_with_ls_options (me
, super
, dir
);
1458 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1460 struct vfs_s_entry
*ent
;
1461 struct vfs_s_super
*super
= dir
->super
;
1462 int sock
, num_entries
= 0;
1463 char buffer
[BUF_8K
];
1466 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1467 || (strchr (remote_path
, ' ') != NULL
);
1470 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1473 RFC_STRICT
? _("(strict rfc959)") : "", cd_first
? _("(chdir first)") : "");
1477 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
)
1479 ftpfs_errno
= ENOENT
;
1480 print_vfs_message (_("ftpfs: CWD failed."));
1485 gettimeofday (&dir
->timestamp
, NULL
);
1486 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1488 if (SUP
.strict
== RFC_STRICT
)
1489 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1491 /* Dirty hack to avoid autoprepending / to . */
1492 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1493 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1496 /* Trailing "/." is necessary if remote_path is a symlink */
1497 char *path
= concat_dir_and_file (remote_path
, ".");
1498 sock
= ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1505 /* Clear the interrupt flag */
1506 tty_enable_interrupt_key ();
1511 int res
= vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1518 me
->verrno
= ECONNRESET
;
1520 tty_disable_interrupt_key ();
1521 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1522 print_vfs_message (_("%s: failure"), me
->name
);
1526 if (MEDATA
->logfile
)
1528 fputs (buffer
, MEDATA
->logfile
);
1529 fputs ("\n", MEDATA
->logfile
);
1530 fflush (MEDATA
->logfile
);
1533 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1534 i
= ent
->ino
->st
.st_nlink
;
1535 if (!vfs_parse_ls_lga (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
))
1537 vfs_s_free_entry (me
, ent
);
1540 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1542 vfs_s_insert_entry (me
, dir
, ent
);
1546 me
->verrno
= E_REMOTE
;
1547 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1550 if (num_entries
== 0 && cd_first
== 0)
1552 /* The LIST command may produce an empty output. In such scenario
1553 it is not clear whether this is caused by `remote_path' being
1554 a non-existent path or for some other reason (listing emtpy
1555 directory without the -a option, non-readable directory, etc.).
1557 Since `dir_load' is a crucial method, when it comes to determine
1558 whether a given path is a _directory_, the code must try its best
1559 to determine the type of `remote_path'. The only reliable way to
1560 achieve this is trough issuing a CWD command. */
1566 if (SUP
.strict
== RFC_AUTODETECT
)
1567 SUP
.strict
= RFC_DARING
;
1569 print_vfs_message (_("%s: done."), me
->name
);
1573 if (SUP
.strict
== RFC_AUTODETECT
)
1575 /* It's our first attempt to get a directory listing from this
1576 server (UNIX style LIST command) */
1577 SUP
.strict
= RFC_STRICT
;
1578 /* I hate goto, but recursive call needs another 8K on stack */
1579 /* return ftpfs_dir_load (me, dir, remote_path); */
1583 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1584 ERRNOR (EACCES
, -1);
1588 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
, char *localname
)
1590 int h
, sock
, n_read
, n_written
;
1592 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1600 struct vfs_s_super
*super
= FH_SUPER
;
1602 h
= open (localname
, O_RDONLY
);
1606 ftpfs_open_data_connection (me
, super
,
1607 fh
->u
.ftp
.append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1608 if (sock
< 0 || fstat (h
, &s
) == -1)
1613 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1616 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1618 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1622 tty_enable_interrupt_key ();
1625 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1)
1629 if (tty_got_interrupt ())
1631 ftpfs_errno
= EINTR
;
1637 ftpfs_errno
= errno
;
1644 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
)
1646 if (n_written
== -1)
1648 if (errno
== EINTR
&& !tty_got_interrupt ())
1652 ftpfs_errno
= errno
;
1656 n_read
-= n_written
;
1658 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1659 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1661 tty_disable_interrupt_key ();
1664 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1668 tty_disable_interrupt_key ();
1671 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1676 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1678 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1682 FH_SOCK
= ftpfs_open_data_connection (me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1686 fh
->linear
= LS_LINEAR_OPEN
;
1687 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1688 fh
->u
.ftp
.append
= 0;
1693 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1696 struct vfs_s_super
*super
= FH_SUPER
;
1698 while ((n
= read (FH_SOCK
, buf
, len
)) < 0)
1700 if ((errno
== EINTR
) && !tty_got_interrupt ())
1706 ftpfs_linear_abort (me
, fh
);
1710 SUP
.ctl_connection_busy
= 0;
1713 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1714 ERRNOR (E_REMOTE
, -1);
1721 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1724 ftpfs_linear_abort (me
, fh
);
1728 ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1734 case VFS_CTL_IS_NOTREADY
:
1739 vfs_die ("You may not do this");
1740 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1743 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1744 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1754 ftpfs_send_command (struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1757 char *p
, *mpath
= g_strdup (filename
);
1758 struct vfs_s_super
*super
;
1760 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1762 rpath
= vfs_s_get_path_mangle (me
, mpath
, &super
, 0);
1768 p
= ftpfs_translate_path (me
, super
, rpath
);
1769 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1771 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1772 if (flags
& OPT_IGNORE_ERROR
)
1780 if (flush_directory_cache
)
1781 vfs_s_invalidate (me
, super
);
1786 /* This routine is called as the last step in load_setup */
1788 ftpfs_init_passwd (void)
1790 ftpfs_anonymous_passwd
= load_anon_passwd ();
1791 if (ftpfs_anonymous_passwd
)
1794 /* If there is no anonymous ftp password specified
1795 * then we'll just use anonymous@
1796 * We don't send any other thing because:
1797 * - We want to remain anonymous
1798 * - We want to stop SPAM
1799 * - We don't want to let ftp sites to discriminate by the user,
1802 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1806 ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1808 char buf
[BUF_SMALL
];
1811 g_snprintf (buf
, sizeof (buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1813 ret
= ftpfs_send_command (me
, path
, buf
, OPT_FLUSH
);
1815 if (mc_config_get_bool (mc_main_config
, CONFIG_APP_SECTION
, "ignore_ftp_chattr_errors", TRUE
))
1824 ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1827 ftpfs_errno
= EPERM
;
1830 /* Everyone knows it is not possible to chown remotely, so why bother them.
1831 If someone's root, then copy/move will always try to chown it... */
1841 ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1843 return ftpfs_send_command (me
, path
, "DELE /%s", OPT_FLUSH
);
1846 /* Return 1 if path is the same directory as the one we are in now */
1848 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1854 if (strcmp (path
, SUP
.cwdir
) == 0)
1860 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1865 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1868 p
= ftpfs_translate_path (me
, super
, remote_path
);
1869 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1879 SUP
.cwdir
= g_strdup (remote_path
);
1880 SUP
.cwd_deferred
= 0;
1886 ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1888 ftpfs_send_command (me
, path1
, "RNFR /%s", OPT_FLUSH
);
1889 return ftpfs_send_command (me
, path2
, "RNTO /%s", OPT_FLUSH
);
1893 ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1895 (void) mode
; /* FIXME: should be used */
1897 return ftpfs_send_command (me
, path
, "MKD /%s", OPT_FLUSH
);
1901 ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1903 return ftpfs_send_command (me
, path
, "RMD /%s", OPT_FLUSH
);
1907 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
, int mode
)
1911 fh
->u
.ftp
.append
= 0;
1912 /* File will be written only, so no need to retrieve it from ftp server */
1913 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
)))
1915 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1922 /* ftpfs_linear_start() called, so data will be written
1923 * to local temporary file and stored to ftp server
1924 * by vfs_s_close later
1926 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
)
1928 if (!fh
->ino
->localname
)
1930 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1931 fh
->ino
->ent
->name
);
1935 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1939 name
= vfs_s_fullpath (me
, fh
->ino
);
1943 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1944 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1949 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1953 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1955 if (fh
->ino
->localname
)
1957 unlink (fh
->ino
->localname
);
1958 g_free (fh
->ino
->localname
);
1959 fh
->ino
->localname
= NULL
;
1964 if (!fh
->ino
->localname
)
1965 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1967 if (!fh
->ino
->localname
)
1968 vfs_die ("retrieve_file failed to fill in localname");
1973 ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1975 if (fh
->handle
!= -1 && !fh
->ino
->localname
)
1979 /* File is stored to destination already, so
1980 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1983 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1985 vfs_s_invalidate (me
, FH_SUPER
);
1991 ftpfs_done (struct vfs_class
*me
)
1993 struct no_proxy_entry
*np
;
1999 np
= no_proxy
->next
;
2000 g_free (no_proxy
->domain
);
2004 g_free (ftpfs_anonymous_passwd
);
2005 g_free (ftpfs_proxy_host
);
2009 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
2011 struct vfs_s_super
*super
= MEDATA
->supers
;
2016 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
2019 super
= super
->next
;
2023 static char buffer
[BUF_MEDIUM
];
2025 static const char *netrcp
;
2027 /* This should match the keywords[] array below */
2042 ftpfs_netrc_next (void)
2046 static const char *const keywords
[] = { "default", "machine",
2047 "login", "password", "passwd", "account", "macdef", NULL
2053 netrcp
= skip_separators (netrcp
);
2054 if (*netrcp
!= '\n')
2063 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++)
2065 if (*netrcp
== '\\')
2072 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
2073 *netrcp
!= ',' && *netrcp
; netrcp
++)
2075 if (*netrcp
== '\\')
2085 while (keywords
[i
- 1])
2087 if (!strcmp (keywords
[i
- 1], buffer
))
2093 return NETRC_UNKNOWN
;
2097 ftpfs_netrc_bad_mode (const char *netrcname
)
2099 static int be_angry
= 1;
2102 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077))
2106 message (D_ERROR
, MSG_ERROR
,
2107 _("~/.netrc file has incorrect mode.\n" "Remove password or correct mode."));
2115 /* Scan .netrc until we find matching "machine" or "default"
2116 * domain is used for additional matching
2117 * No search is done after "default" in compliance with "man netrc"
2118 * Return 0 if found, -1 otherwise */
2120 ftpfs_find_machine (const char *host
, const char *domain
)
2129 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
)
2131 if (keyword
== NETRC_DEFAULT
)
2134 if (keyword
== NETRC_MACDEF
)
2136 /* Scan for an empty line, which concludes "macdef" */
2139 while (*netrcp
&& *netrcp
!= '\n')
2141 if (*netrcp
!= '\n')
2145 while (*netrcp
&& *netrcp
!= '\n');
2149 if (keyword
!= NETRC_MACHINE
)
2152 /* Take machine name */
2153 if (ftpfs_netrc_next () == NETRC_NONE
)
2156 if (g_strcasecmp (host
, buffer
))
2158 /* Try adding our domain to short names in .netrc */
2159 const char *host_domain
= strchr (host
, '.');
2163 /* Compare domain part */
2164 if (g_strcasecmp (host_domain
, domain
))
2167 /* Compare local part */
2168 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
2179 /* Extract login and password from .netrc for the host.
2181 * Returns 0 for success, -1 for error */
2183 ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
2186 char *tmp_pass
= NULL
;
2187 char hostname
[MAXHOSTNAMELEN
];
2190 static struct rupcache
2192 struct rupcache
*next
;
2196 } *rup_cache
= NULL
, *rupp
;
2198 /* Initialize *login and *pass */
2205 /* Look up in the cache first */
2206 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
)
2208 if (!strcmp (host
, rupp
->host
))
2211 *login
= g_strdup (rupp
->login
);
2212 if (pass
&& rupp
->pass
)
2213 *pass
= g_strdup (rupp
->pass
);
2218 /* Load current .netrc */
2219 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
2220 netrcp
= netrc
= load_file (netrcname
);
2227 /* Find our own domain name */
2228 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2231 domain
= strchr (hostname
, '.');
2235 /* Scan for "default" and matching "machine" keywords */
2236 ftpfs_find_machine (host
, domain
);
2238 /* Scan for keywords following "default" and "machine" */
2242 keyword
= ftpfs_netrc_next ();
2247 if (ftpfs_netrc_next () == NETRC_NONE
)
2253 /* We have another name already - should not happen */
2260 /* We have login name now */
2261 *login
= g_strdup (buffer
);
2264 case NETRC_PASSWORD
:
2266 if (ftpfs_netrc_next () == NETRC_NONE
)
2272 /* Ignore unsafe passwords */
2273 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2274 && ftpfs_netrc_bad_mode (netrcname
))
2280 /* Remember password. pass may be NULL, so use tmp_pass */
2281 if (tmp_pass
== NULL
)
2282 tmp_pass
= g_strdup (buffer
);
2286 /* "account" is followed by a token which we ignore */
2287 if (ftpfs_netrc_next () == NETRC_NONE
)
2293 /* Ignore account, but warn user anyways */
2294 ftpfs_netrc_bad_mode (netrcname
);
2298 /* Unexpected keyword or end of file */
2310 rupp
= g_new (struct rupcache
, 1);
2311 rupp
->host
= g_strdup (host
);
2312 rupp
->login
= rupp
->pass
= 0;
2316 rupp
->login
= g_strdup (*login
);
2318 if (tmp_pass
!= NULL
)
2319 rupp
->pass
= g_strdup (tmp_pass
);
2320 rupp
->next
= rup_cache
;
2332 static struct vfs_s_subclass ftpfs_subclass
;
2336 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
2337 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2338 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2339 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2340 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2341 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2342 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2343 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2344 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2345 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2346 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2348 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2349 vfs_ftpfs_ops
.name
= "ftpfs";
2350 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2351 vfs_ftpfs_ops
.prefix
= "ftp:";
2352 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2353 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2354 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2355 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2356 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2357 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2358 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2359 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2360 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2361 vfs_register_class (&vfs_ftpfs_ops
);