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:
47 * cd /#ftp:pavel@hobit
49 * And now: what do I want to do? Do I want to go to /home/pavel or to
50 * /#ftp:hobit/home/pavel? I think first has better sense...
53 int f = !strcmp( remote_path, "/~" );
54 if (f || !strncmp( remote_path, "/~/", 3 )) {
56 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
65 /* \todo Fix: Namespace pollution: horrible */
68 #include <stdlib.h> /* atoi() */
69 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
70 #include <netdb.h> /* struct hostent */
71 #include <sys/socket.h> /* AF_INET */
72 #include <netinet/in.h> /* struct in_addr */
73 #ifdef HAVE_ARPA_INET_H
74 #include <arpa/inet.h>
77 #include <arpa/telnet.h>
78 #include <sys/param.h>
82 #include <sys/time.h> /* gettimeofday() */
84 #include "../src/global.h"
86 #include "../src/tty/tty.h" /* enable/disable interrupt key */
88 #include "../src/wtools.h" /* message() */
89 #include "../src/main.h" /* print_vfs_message */
90 #include "../src/history.h"
91 #include "../src/setup.h" /* for load_anon_passwd */
94 #include "xdirentry.h"
97 #include "gc.h" /* vfs_stamp_create */
100 #ifndef MAXHOSTNAMELEN
101 # define MAXHOSTNAMELEN 64
104 #define UPLOAD_ZERO_LENGTH_FILE
105 #define SUP super->u.ftp
106 #define FH_SOCK fh->u.ftp.sock
109 #define INADDR_NONE 0xffffffff
112 #define RFC_AUTODETECT 0
116 #ifndef HAVE_SOCKLEN_T
117 typedef int socklen_t
;
120 static int ftpfs_errno
;
123 /* Delay to retry a connection */
124 int ftpfs_retry_seconds
= 30;
126 /* Method to use to connect to ftp sites */
127 int ftpfs_use_passive_connections
= 1;
128 int ftpfs_use_passive_connections_over_proxy
= 0;
130 /* Method used to get directory listings:
131 * 1: try 'LIST -la <path>', if it fails
132 * fall back to CWD <path>; LIST
133 * 0: always use CWD <path>; LIST
135 int ftpfs_use_unix_list_options
= 1;
137 /* First "CWD <path>", then "LIST -la ." */
138 int ftpfs_first_cd_then_ls
= 1;
140 /* Use the ~/.netrc */
143 /* Anonymous setup */
144 char *ftpfs_anonymous_passwd
= NULL
;
145 int ftpfs_directory_timeout
= 900;
148 char *ftpfs_proxy_host
= NULL
;
150 /* wether we have to use proxy by default? */
151 int ftpfs_always_use_proxy
;
153 #ifdef FIXME_LATER_ALIGATOR
154 static struct linklist
*connections_list
;
157 /* ftpfs_command wait_flag: */
159 #define WAIT_REPLY 0x01
160 #define WANT_STRING 0x02
161 static char reply_str
[80];
163 static struct vfs_class vfs_ftpfs_ops
;
165 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
166 Translate a Unix path, i.e. MC's internal path representation (e.g.
167 /somedir/somefile) to a path valid for the remote server. Every path
168 transfered to the remote server has to be mangled by this function
169 right prior to sending it.
170 Currently only Amiga ftp servers are handled in a special manner.
172 When the remote server is an amiga:
173 a) strip leading slash if necesarry
174 b) replace first occurance of ":/" with ":"
175 c) strip trailing "/."
178 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
179 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
);
180 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
181 __attribute__ ((format (__printf__
, 4, 5)));
182 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
183 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
);
184 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
187 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
189 if (!SUP
.remote_is_amiga
)
190 return g_strdup (remote_path
);
194 if (MEDATA
->logfile
) {
195 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
196 fflush (MEDATA
->logfile
);
199 /* strip leading slash(es) */
200 while (*remote_path
== '/')
204 * Don't change "/" into "", e.g. "CWD " would be
207 if (*remote_path
== '\0')
208 return g_strdup (".");
210 ret
= g_strdup (remote_path
);
212 /* replace first occurance of ":/" with ":" */
213 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
214 strcpy (p
+ 1, p
+ 2);
216 /* strip trailing "/." */
217 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
223 /* Extract the hostname and username from the path */
226 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
227 * ftp://sunsite.unc.edu/pub/linux
228 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
229 * ftp://tsx-11.mit.edu:8192/
230 * ftp://joe@foo.edu:11321/private
231 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
236 #define FTP_COMMAND_PORT 21
239 ftpfs_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
243 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
247 /* Look up user and password in netrc */
249 ftpfs_netrc_lookup (*host
, user
, pass
);
251 *user
= g_strdup ("anonymous");
254 /* Look up password in netrc for known user */
255 if (use_netrc
&& *user
&& pass
&& !*pass
) {
258 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
260 /* If user is different, remove password */
261 if (new_user
&& strcmp (*user
, new_user
)) {
272 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
274 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
280 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
286 switch (sscanf(answer
, "%d", &code
)){
289 g_strlcpy (string_buf
, answer
, string_len
);
293 if (answer
[3] == '-') {
295 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
301 if ((sscanf (answer
, "%d", &i
) > 0) &&
302 (code
== i
) && (answer
[3] == ' '))
307 g_strlcpy (string_buf
, answer
, string_len
);
314 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
316 int sock
= ftpfs_open_socket (me
, super
);
318 char *cwdir
= SUP
.cwdir
;
322 if (ftpfs_login_server (me
, super
, SUP
.password
)){
325 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
327 return sock
== COMPLETE
;
335 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
340 static int retry
= 0;
341 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
344 cmdstr
= g_strdup_vprintf (fmt
, ap
);
347 cmdlen
= strlen (cmdstr
);
348 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
349 strcpy (cmdstr
+ cmdlen
, "\r\n");
352 if (MEDATA
->logfile
) {
353 if (strncmp (cmdstr
, "PASS ", 5) == 0) {
354 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
356 fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
358 fflush (MEDATA
->logfile
);
362 tty_enable_interrupt_key ();
363 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
368 if (errno
== EPIPE
) { /* Remote server has closed connection */
371 status
= ftpfs_reconnect (me
, super
);
373 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
381 tty_disable_interrupt_key ();
386 tty_disable_interrupt_key ();
390 status
= ftpfs_get_reply (me
, SUP
.sock
,
391 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
392 sizeof (reply_str
) - 1);
393 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
397 status
= ftpfs_reconnect (me
, super
);
399 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
412 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
415 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
416 ftpfs_command(me
, super
, NONE
, "QUIT");
422 g_free (SUP
.password
);
425 /* some defines only used by ftpfs_changetype */
426 /* These two are valid values for the second parameter */
428 #define TYPE_BINARY 1
430 /* This one is only used to initialize bucket->isbinary, don't use it as
431 second parameter to ftpfs_changetype. */
432 #define TYPE_UNKNOWN -1
435 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
437 if (binary
!= SUP
.isbinary
) {
438 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
440 SUP
.isbinary
= binary
;
445 /* This routine logs the user in */
447 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
448 const char *netrcpass
)
452 char *name
; /* login user name */
454 char reply_string
[BUF_MEDIUM
];
456 SUP
.isbinary
= TYPE_UNKNOWN
;
458 if (SUP
.password
) /* explicit password */
459 op
= g_strdup (SUP
.password
);
460 else if (netrcpass
) /* password from netrc */
461 op
= g_strdup (netrcpass
);
462 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp")) {
463 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
464 ftpfs_init_passwd ();
465 op
= g_strdup (ftpfs_anonymous_passwd
);
467 } else { /* ask user */
470 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
, " ",
472 op
= vfs_get_password (p
);
476 SUP
.password
= g_strdup (op
);
479 if (!anon
|| MEDATA
->logfile
)
482 pass
= g_strconcat ("-", op
, (char *) NULL
);
486 /* Proxy server accepts: username@host-we-want-to-connect */
489 g_strconcat (SUP
.user
, "@",
490 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
,
493 name
= g_strdup (SUP
.user
);
496 (me
, SUP
.sock
, reply_string
,
497 sizeof (reply_string
) - 1) == COMPLETE
) {
498 g_strup (reply_string
);
499 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
500 if (MEDATA
->logfile
) {
501 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n",
502 SUP
.remote_is_amiga
);
503 fflush (MEDATA
->logfile
);
506 print_vfs_message (_("ftpfs: sending login name"));
508 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
)) {
510 print_vfs_message (_("ftpfs: sending user password"));
511 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
512 if (code
== CONTINUE
) {
515 p
= g_strdup_printf (_
516 ("FTP: Account required for user %s"),
518 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
522 print_vfs_message (_("ftpfs: sending user account"));
524 ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
527 if (code
!= COMPLETE
)
532 print_vfs_message (_("ftpfs: logged in"));
533 wipe_password (pass
);
538 SUP
.failed_on_login
= 1;
540 wipe_password (SUP
.password
);
546 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
549 wipe_password (pass
);
554 static struct no_proxy_entry
{
560 ftpfs_load_no_proxy_list (void)
562 /* FixMe: shouldn't be hardcoded!!! */
563 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
564 struct no_proxy_entry
*np
, *current
= 0;
568 static char *mc_file
;
573 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
574 if (exist_file (mc_file
) &&
575 (npf
= fopen (mc_file
, "r"))) {
576 while (fgets (s
, sizeof (s
), npf
)) {
577 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
578 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
588 np
= g_new (struct no_proxy_entry
, 1);
589 np
->domain
= g_strdup (s
);
603 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
605 ftpfs_check_proxy (const char *host
)
607 struct no_proxy_entry
*npe
;
609 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
610 return 0; /* sanity check */
615 if (!ftpfs_always_use_proxy
)
618 if (!strchr (host
, '.'))
621 ftpfs_load_no_proxy_list ();
622 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
623 char *domain
= npe
->domain
;
625 if (domain
[0] == '.') {
626 int ld
= strlen (domain
);
627 int lh
= strlen (host
);
629 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
637 if (!g_strcasecmp (host
, domain
))
645 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
650 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
657 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
659 struct addrinfo hints
, *res
, *curr_res
;
668 /* Use a proxy host? */
669 host
= g_strdup(SUP
.host
);
671 if (!host
|| !*host
){
672 print_vfs_message (_("ftpfs: Invalid host name."));
673 ftpfs_errno
= EINVAL
;
677 /* Hosts to connect to that start with a ! should use proxy */
681 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
684 port
= g_strdup_printf("%hu", (unsigned short) tmp_port
);
691 tty_enable_interrupt_key(); /* clear the interrupt flag */
693 memset (&hints
, 0, sizeof (struct addrinfo
));
694 hints
.ai_socktype
= SOCK_STREAM
;
695 hints
.ai_flags
= AI_ADDRCONFIG
;
698 e
= getaddrinfo (host
, port
, &hints
, &res
);
703 tty_disable_interrupt_key ();
704 print_vfs_message (_("ftpfs: %s"), gai_strerror (e
));
706 ftpfs_errno
= EINVAL
;
710 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
) {
712 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
716 if (curr_res
->ai_next
!= NULL
)
719 tty_disable_interrupt_key();
720 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno
));
727 print_vfs_message (_("ftpfs: making connection to %s"), host
);
731 if ( connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0 )
737 if (errno
== EINTR
&& tty_got_interrupt ()) {
738 print_vfs_message (_("ftpfs: connection interrupted by user"));
739 } else if (res
->ai_next
== NULL
) {
740 print_vfs_message (_("ftpfs: connection to server failed: %s"),
741 unix_error_string (errno
));
747 tty_disable_interrupt_key ();
752 tty_disable_interrupt_key ();
757 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
759 int retry_seconds
, count_down
;
761 /* We do not want to use the passive if we are using proxies */
763 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
767 SUP
.failed_on_login
= 0;
769 SUP
.sock
= ftpfs_open_socket (me
, super
);
773 if (ftpfs_login_server (me
, super
, NULL
)) {
774 /* Logged in, no need to retry the connection */
777 if (SUP
.failed_on_login
){
778 /* Close only the socket descriptor */
783 if (ftpfs_retry_seconds
){
784 retry_seconds
= ftpfs_retry_seconds
;
785 tty_enable_interrupt_key ();
786 for (count_down
= retry_seconds
; count_down
; count_down
--){
787 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
789 if (tty_got_interrupt ()) {
790 /* ftpfs_errno = E; */
791 tty_disable_interrupt_key ();
795 tty_disable_interrupt_key ();
798 } while (retry_seconds
);
800 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
802 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
807 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
808 const char *archive_name
, char *op
)
810 char *host
, *user
, *password
;
815 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
822 if (ftpfs_check_proxy (host
))
823 SUP
.proxy
= ftpfs_proxy_host
;
824 SUP
.password
= password
;
825 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
826 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
827 SUP
.isbinary
= TYPE_UNKNOWN
;
828 SUP
.remote_is_amiga
= 0;
829 super
->name
= g_strdup ("/");
831 vfs_s_new_inode (me
, super
,
832 vfs_s_default_stat (me
, S_IFDIR
| 0755));
834 return ftpfs_open_archive_int (me
, super
);
838 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
839 const char *archive_name
, char *op
, void *cookie
)
848 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
850 port
= ((strcmp (host
, SUP
.host
) == 0)
851 && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
859 /* The returned directory should always contain a trailing slash */
861 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
863 char buf
[BUF_8K
], *bufp
, *bufq
;
865 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
866 ftpfs_get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
868 for (bufq
= buf
; *bufq
; bufq
++)
875 if (*(bufq
- 1) != '/') {
880 return g_strdup (bufp
);
882 /* If the remote server is an Amiga a leading slash
883 might be missing. MC needs it because it is used
884 as separator between hostname and path internally. */
885 return g_strconcat( "/", bufp
, NULL
);
899 /* Setup Passive ftp connection, we use it for source routed connections */
901 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
902 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
*salen
)
906 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") == COMPLETE
) {
909 c
= strchr (reply_str
, '|');
918 if (port
< 0 || port
> 65535)
922 switch (sa
->ss_family
) {
924 ((struct sockaddr_in
*)sa
)->sin_port
= port
;
927 ((struct sockaddr_in6
*)sa
)->sin6_port
= port
;
930 print_vfs_message (_("ftpfs: invalid address family"));
933 } else if (sa
->ss_family
== AF_INET
) {
934 int xa
, xb
, xc
, xd
, xe
, xf
;
937 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
940 /* Parse remote parameters */
941 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
945 if (!isdigit ((unsigned char) *c
))
947 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
950 n
[0] = (unsigned char) xa
;
951 n
[1] = (unsigned char) xb
;
952 n
[2] = (unsigned char) xc
;
953 n
[3] = (unsigned char) xd
;
954 n
[4] = (unsigned char) xe
;
955 n
[5] = (unsigned char) xf
;
957 memcpy (&(((struct sockaddr_in
*)sa
)->sin_addr
.s_addr
), (void *)n
, 4);
958 memcpy (&(((struct sockaddr_in
*)sa
)->sin_port
), (void *)&n
[4], 2);
962 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
969 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
971 struct sockaddr_storage data_addr
;
972 socklen_t data_addrlen
;
976 memset (&data_addr
, 0, sizeof (struct sockaddr_storage
));
977 data_addrlen
= sizeof (struct sockaddr_storage
);
979 if (getpeername (SUP
.sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == -1)
982 switch (data_addr
.ss_family
) {
984 ((struct sockaddr_in
*)&data_addr
)->sin_port
= 0;
987 ((struct sockaddr_in6
*)&data_addr
)->sin6_port
= 0;
990 print_vfs_message (_("ftpfs: invalid address family"));
994 data_sock
= socket (data_addr
.ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
996 if (SUP
.use_passive_connection
) {
997 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno
));
998 SUP
.use_passive_connection
= 0;
1002 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1006 if (SUP
.use_passive_connection
) {
1008 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1011 SUP
.use_passive_connection
= 0;
1012 print_vfs_message (_("ftpfs: could not setup passive mode"));
1018 /* If passive setup fails, fallback to active connections */
1019 /* Active FTP connection */
1020 if ((bind (data_sock
, (struct sockaddr
*)&data_addr
, data_addrlen
) == 0) &&
1021 (getsockname (data_sock
, (struct sockaddr
*)&data_addr
, &data_addrlen
) == 0) &&
1022 (listen (data_sock
, 1) == 0)) {
1023 unsigned short int port
;
1027 switch (data_addr
.ss_family
) {
1030 port
= ((struct sockaddr_in
*)&data_addr
)->sin_port
;
1034 port
= ((struct sockaddr_in6
*)&data_addr
)->sin6_port
;
1037 print_vfs_message (_("ftpfs: invalid address family"));
1038 ERRNOR (EINVAL
, -1);
1041 port
= ntohs (port
);
1043 addr
= g_malloc (NI_MAXHOST
);
1045 ERRNOR (ENOMEM
, -1);
1047 if (getnameinfo ((struct sockaddr
*)&data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0, NI_NUMERICHOST
) != 0) {
1052 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
) {
1058 if (FTP_INET
== af
) {
1059 unsigned char *a
= (unsigned char *)&((struct sockaddr_in
*)&data_addr
)->sin_addr
;
1060 unsigned char *p
= (unsigned char *)&port
;
1062 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1063 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1064 p
[0], p
[1]) == COMPLETE
)
1074 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1075 const char *remote
, int isbinary
, int reget
)
1077 struct sockaddr_storage from
;
1079 socklen_t fromlen
= sizeof(from
);
1081 if ((s
= ftpfs_initconn (me
, super
)) == -1)
1083 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1086 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1091 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1092 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1093 /* WarFtpD can't STORE //filename */
1094 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1095 g_free (remote_path
);
1097 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1100 tty_enable_interrupt_key ();
1101 if (SUP
.use_passive_connection
)
1104 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
1106 ftpfs_errno
= errno
;
1112 tty_disable_interrupt_key ();
1116 #define ABORT_TIMEOUT 5
1118 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1120 struct vfs_s_super
*super
= FH_SUPER
;
1121 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1124 int dsock
= FH_SOCK
;
1126 SUP
.ctl_connection_busy
= 0;
1128 print_vfs_message (_("ftpfs: aborting transfer."));
1129 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
1130 print_vfs_message (_("ftpfs: abort error: %s"),
1131 unix_error_string (errno
));
1137 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1138 print_vfs_message (_("ftpfs: abort failed"));
1145 FD_SET (dsock
, &mask
);
1146 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1147 struct timeval start_tim
, tim
;
1148 gettimeofday (&start_tim
, NULL
);
1149 /* flush the remaining data */
1150 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1151 gettimeofday (&tim
, NULL
);
1152 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1153 /* server keeps sending, drop the connection and ftpfs_reconnect */
1155 ftpfs_reconnect (me
, super
);
1162 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1163 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1168 resolve_symlink_without_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1170 struct linklist
*flist
;
1171 struct direntry
*fe
, *fel
;
1172 char tmp
[MC_MAXPATHLEN
];
1175 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1176 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1177 /* flist->data->l_stat is alread initialized with 0 */
1179 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1180 if (fel
->linkname
[0] == '/') {
1181 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1183 strcpy (tmp
, fel
->linkname
);
1185 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1187 strcpy (tmp
, dir
->remote_path
);
1190 strcat (tmp
+ 1, fel
->linkname
);
1192 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1193 canonicalize_pathname (tmp
);
1194 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1196 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1197 /* Symlink points to link which isn't resolved, yet. */
1198 if (fe
->linkname
[0] == '/') {
1199 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1201 strcpy (tmp
, fe
->linkname
);
1203 /* at this point tmp looks always like this
1204 /directory/filename, i.e. no need to check
1205 strrchr's return value */
1206 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1207 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1209 strcat (tmp
, fe
->linkname
);
1213 fel
->l_stat
= g_new (struct stat
, 1);
1214 if ( S_ISLNK (fe
->s
.st_mode
))
1215 *fel
->l_stat
= *fe
->l_stat
;
1217 *fel
->l_stat
= fe
->s
;
1218 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1225 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1229 resolve_symlink_with_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1231 char buffer
[2048] = "", *filename
;
1235 struct linklist
*flist
;
1236 struct direntry
*fe
;
1237 int switch_method
= 0;
1239 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1240 if (strchr (dir
->remote_path
, ' ')) {
1241 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1242 print_vfs_message(_("ftpfs: CWD failed."));
1245 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1248 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa",
1249 dir
->remote_path
, TYPE_ASCII
, 0);
1252 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1256 fp
= fdopen(sock
, "r");
1259 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1262 tty_enable_interrupt_key ();
1263 flist
= dir
->file_list
->next
;
1266 if (flist
== dir
->file_list
)
1269 flist
= flist
->next
;
1270 } while (!S_ISLNK(fe
->s
.st_mode
));
1272 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1274 if (MEDATA
->logfile
){
1275 fputs (buffer
, MEDATA
->logfile
);
1276 fflush (MEDATA
->logfile
);
1278 vfs_die("This code should be commented out\n");
1279 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1280 int r
= strcmp(fe
->name
, filename
);
1283 if (S_ISLNK (s
.st_mode
)) {
1284 /* This server doesn't understand LIST -lLa */
1288 fe
->l_stat
= g_new (struct stat
, 1);
1289 if (fe
->l_stat
== NULL
)
1292 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1301 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1302 tty_disable_interrupt_key ();
1304 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1308 resolve_symlink(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1310 print_vfs_message(_("Resolving symlink..."));
1312 if (SUP
.strict_rfc959_list_cmd
)
1313 resolve_symlink_without_ls_options(me
, super
, dir
);
1315 resolve_symlink_with_ls_options(me
, super
, dir
);
1320 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1322 struct vfs_s_entry
*ent
;
1323 struct vfs_s_super
*super
= dir
->super
;
1324 int sock
, num_entries
= 0;
1325 char buffer
[BUF_8K
];
1328 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1329 || (strchr (remote_path
, ' ') != NULL
);
1332 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1335 RFC_STRICT
? _("(strict rfc959)") : "",
1336 cd_first
? _("(chdir first)") : "");
1339 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
) {
1340 ftpfs_errno
= ENOENT
;
1341 print_vfs_message (_("ftpfs: CWD failed."));
1346 gettimeofday (&dir
->timestamp
, NULL
);
1347 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1349 if (SUP
.strict
== RFC_STRICT
)
1350 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1352 /* Dirty hack to avoid autoprepending / to . */
1353 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1355 ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1357 /* Trailing "/." is necessary if remote_path is a symlink */
1358 char *path
= concat_dir_and_file (remote_path
, ".");
1360 ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
,
1368 /* Clear the interrupt flag */
1369 tty_enable_interrupt_key ();
1374 vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1380 me
->verrno
= ECONNRESET
;
1382 tty_disable_interrupt_key ();
1383 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1384 print_vfs_message (_("%s: failure"), me
->name
);
1388 if (MEDATA
->logfile
) {
1389 fputs (buffer
, MEDATA
->logfile
);
1390 fputs ("\n", MEDATA
->logfile
);
1391 fflush (MEDATA
->logfile
);
1394 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1395 i
= ent
->ino
->st
.st_nlink
;
1396 if (!vfs_parse_ls_lga
1397 (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1398 vfs_s_free_entry (me
, ent
);
1401 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1403 vfs_s_insert_entry (me
, dir
, ent
);
1407 me
->verrno
= E_REMOTE
;
1408 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1411 if (num_entries
== 0 && cd_first
== 0) {
1412 /* The LIST command may produce an empty output. In such scenario
1413 it is not clear whether this is caused by `remote_path' being
1414 a non-existent path or for some other reason (listing emtpy
1415 directory without the -a option, non-readable directory, etc.).
1417 Since `dir_load' is a crucial method, when it comes to determine
1418 whether a given path is a _directory_, the code must try its best
1419 to determine the type of `remote_path'. The only reliable way to
1420 achieve this is trough issuing a CWD command. */
1426 if (SUP
.strict
== RFC_AUTODETECT
)
1427 SUP
.strict
= RFC_DARING
;
1429 print_vfs_message (_("%s: done."), me
->name
);
1433 if (SUP
.strict
== RFC_AUTODETECT
) {
1434 /* It's our first attempt to get a directory listing from this
1435 server (UNIX style LIST command) */
1436 SUP
.strict
= RFC_STRICT
;
1437 /* I hate goto, but recursive call needs another 8K on stack */
1438 /* return ftpfs_dir_load (me, dir, remote_path); */
1442 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1443 ERRNOR (EACCES
, -1);
1447 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
,
1450 int h
, sock
, n_read
, n_written
;
1452 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1460 struct vfs_s_super
*super
= FH_SUPER
;
1462 h
= open (localname
, O_RDONLY
);
1466 ftpfs_open_data_connection (me
, super
,
1467 fh
->u
.ftp
.append
? "APPE" : "STOR", name
,
1469 if (sock
< 0 || fstat (h
, &s
) == -1) {
1473 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1476 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1478 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1482 tty_enable_interrupt_key ();
1484 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1) {
1485 if (errno
== EINTR
) {
1486 if (tty_got_interrupt ()) {
1487 ftpfs_errno
= EINTR
;
1492 ftpfs_errno
= errno
;
1499 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
) {
1500 if (n_written
== -1) {
1501 if (errno
== EINTR
&& !tty_got_interrupt ()) {
1504 ftpfs_errno
= errno
;
1508 n_read
-= n_written
;
1510 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1511 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1513 tty_disable_interrupt_key ();
1516 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1520 tty_disable_interrupt_key ();
1523 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1528 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1530 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1534 FH_SOCK
= ftpfs_open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1538 fh
->linear
= LS_LINEAR_OPEN
;
1539 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1540 fh
->u
.ftp
.append
= 0;
1545 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1548 struct vfs_s_super
*super
= FH_SUPER
;
1550 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1551 if ((errno
== EINTR
) && !tty_got_interrupt ())
1557 ftpfs_linear_abort(me
, fh
);
1560 SUP
.ctl_connection_busy
= 0;
1563 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1564 ERRNOR (E_REMOTE
, -1);
1571 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1574 ftpfs_linear_abort(me
, fh
);
1577 static int ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1582 case VFS_CTL_IS_NOTREADY
:
1587 vfs_die ("You may not do this");
1588 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1591 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1592 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1602 ftpfs_send_command(struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1605 char *p
, *mpath
= g_strdup(filename
);
1606 struct vfs_s_super
*super
;
1608 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1610 if (!(rpath
= vfs_s_get_path_mangle(me
, mpath
, &super
, 0))) {
1614 p
= ftpfs_translate_path (me
, super
, rpath
);
1615 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1617 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1618 if (flags
& OPT_IGNORE_ERROR
)
1620 if (r
!= COMPLETE
) {
1625 if (flush_directory_cache
)
1626 vfs_s_invalidate(me
, super
);
1631 /* This routine is called as the last step in load_setup */
1633 ftpfs_init_passwd(void)
1635 ftpfs_anonymous_passwd
= load_anon_passwd ();
1636 if (ftpfs_anonymous_passwd
)
1639 /* If there is no anonymous ftp password specified
1640 * then we'll just use anonymous@
1641 * We don't send any other thing because:
1642 * - We want to remain anonymous
1643 * - We want to stop SPAM
1644 * - We don't want to let ftp sites to discriminate by the user,
1647 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1650 static int ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1652 char buf
[BUF_SMALL
];
1654 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1655 return ftpfs_send_command(me
, path
, buf
, OPT_FLUSH
);
1658 static int ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1661 ftpfs_errno
= EPERM
;
1664 /* Everyone knows it is not possible to chown remotely, so why bother them.
1665 If someone's root, then copy/move will always try to chown it... */
1674 static int ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1676 return ftpfs_send_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1679 /* Return 1 if path is the same directory as the one we are in now */
1681 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1687 if (strcmp (path
, SUP
.cwdir
) == 0)
1693 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1698 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1701 p
= ftpfs_translate_path (me
, super
, remote_path
);
1702 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1705 if (r
!= COMPLETE
) {
1709 SUP
.cwdir
= g_strdup (remote_path
);
1710 SUP
.cwd_deferred
= 0;
1715 static int ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1717 ftpfs_send_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1718 return ftpfs_send_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1721 static int ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1723 (void) mode
; /* FIXME: should be used */
1725 return ftpfs_send_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1728 static int ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1730 return ftpfs_send_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1734 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
,
1739 fh
->u
.ftp
.append
= 0;
1740 /* File will be written only, so no need to retrieve it from ftp server */
1741 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
))) {
1742 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1749 /* ftpfs_linear_start() called, so data will be written
1750 * to local temporary file and stored to ftp server
1751 * by vfs_s_close later
1753 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
) {
1754 if (!fh
->ino
->localname
) {
1755 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1756 fh
->ino
->ent
->name
);
1760 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1764 name
= vfs_s_fullpath (me
, fh
->ino
);
1768 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1769 (flags
& O_APPEND
) ? "APPE" :
1770 "STOR", name
, TYPE_BINARY
, 0);
1775 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1779 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1781 if (fh
->ino
->localname
) {
1782 unlink (fh
->ino
->localname
);
1783 g_free (fh
->ino
->localname
);
1784 fh
->ino
->localname
= NULL
;
1789 if (!fh
->ino
->localname
)
1790 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1792 if (!fh
->ino
->localname
)
1793 vfs_die ("retrieve_file failed to fill in localname");
1797 static int ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1799 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1802 /* File is stored to destination already, so
1803 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1806 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1808 vfs_s_invalidate (me
, FH_SUPER
);
1814 ftpfs_done (struct vfs_class
*me
)
1816 struct no_proxy_entry
*np
;
1821 np
= no_proxy
->next
;
1822 g_free (no_proxy
->domain
);
1826 g_free (ftpfs_anonymous_passwd
);
1827 g_free (ftpfs_proxy_host
);
1831 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
1833 struct vfs_s_super
*super
= MEDATA
->supers
;
1837 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
1840 super
= super
->next
;
1844 static char buffer
[BUF_MEDIUM
];
1846 static const char *netrcp
;
1848 /* This should match the keywords[] array below */
1861 static keyword_t
ftpfs_netrc_next (void)
1865 static const char *const keywords
[] = { "default", "machine",
1866 "login", "password", "passwd", "account", "macdef", NULL
1871 netrcp
= skip_separators (netrcp
);
1872 if (*netrcp
!= '\n')
1879 if (*netrcp
== '"') {
1880 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1881 if (*netrcp
== '\\')
1886 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1887 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1888 if (*netrcp
== '\\')
1898 while (keywords
[i
- 1]) {
1899 if (!strcmp (keywords
[i
- 1], buffer
))
1905 return NETRC_UNKNOWN
;
1908 static int ftpfs_netrc_bad_mode (const char *netrcname
)
1910 static int be_angry
= 1;
1913 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1915 message (D_ERROR
, MSG_ERROR
,
1916 _("~/.netrc file has incorrect mode.\n"
1917 "Remove password or correct mode."));
1925 /* Scan .netrc until we find matching "machine" or "default"
1926 * domain is used for additional matching
1927 * No search is done after "default" in compliance with "man netrc"
1928 * Return 0 if found, -1 otherwise */
1929 static int ftpfs_find_machine (const char *host
, const char *domain
)
1933 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
) {
1934 if (keyword
== NETRC_DEFAULT
)
1937 if (keyword
== NETRC_MACDEF
) {
1938 /* Scan for an empty line, which concludes "macdef" */
1940 while (*netrcp
&& *netrcp
!= '\n')
1942 if (*netrcp
!= '\n')
1945 } while (*netrcp
&& *netrcp
!= '\n');
1949 if (keyword
!= NETRC_MACHINE
)
1952 /* Take machine name */
1953 if (ftpfs_netrc_next () == NETRC_NONE
)
1956 if (g_strcasecmp (host
, buffer
)) {
1957 /* Try adding our domain to short names in .netrc */
1958 const char *host_domain
= strchr (host
, '.');
1962 /* Compare domain part */
1963 if (g_strcasecmp (host_domain
, domain
))
1966 /* Compare local part */
1967 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1978 /* Extract login and password from .netrc for the host.
1980 * Returns 0 for success, -1 for error */
1981 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
1984 char *tmp_pass
= NULL
;
1985 char hostname
[MAXHOSTNAMELEN
];
1988 static struct rupcache
{
1989 struct rupcache
*next
;
1993 } *rup_cache
= NULL
, *rupp
;
1995 /* Initialize *login and *pass */
2002 /* Look up in the cache first */
2003 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
2004 if (!strcmp (host
, rupp
->host
)) {
2006 *login
= g_strdup (rupp
->login
);
2007 if (pass
&& rupp
->pass
)
2008 *pass
= g_strdup (rupp
->pass
);
2013 /* Load current .netrc */
2014 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
2015 netrcp
= netrc
= load_file (netrcname
);
2016 if (netrc
== NULL
) {
2021 /* Find our own domain name */
2022 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2024 if (!(domain
= strchr (hostname
, '.')))
2027 /* Scan for "default" and matching "machine" keywords */
2028 ftpfs_find_machine (host
, domain
);
2030 /* Scan for keywords following "default" and "machine" */
2033 keyword
= ftpfs_netrc_next ();
2037 if (ftpfs_netrc_next () == NETRC_NONE
) {
2042 /* We have another name already - should not happen */
2048 /* We have login name now */
2049 *login
= g_strdup (buffer
);
2052 case NETRC_PASSWORD
:
2054 if (ftpfs_netrc_next () == NETRC_NONE
) {
2059 /* Ignore unsafe passwords */
2060 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2061 && ftpfs_netrc_bad_mode (netrcname
)) {
2066 /* Remember password. pass may be NULL, so use tmp_pass */
2067 if (tmp_pass
== NULL
)
2068 tmp_pass
= g_strdup (buffer
);
2072 /* "account" is followed by a token which we ignore */
2073 if (ftpfs_netrc_next () == NETRC_NONE
) {
2078 /* Ignore account, but warn user anyways */
2079 ftpfs_netrc_bad_mode (netrcname
);
2083 /* Unexpected keyword or end of file */
2095 rupp
= g_new (struct rupcache
, 1);
2096 rupp
->host
= g_strdup (host
);
2097 rupp
->login
= rupp
->pass
= 0;
2099 if (*login
!= NULL
) {
2100 rupp
->login
= g_strdup (*login
);
2102 if (tmp_pass
!= NULL
)
2103 rupp
->pass
= g_strdup (tmp_pass
);
2104 rupp
->next
= rup_cache
;
2116 static struct vfs_s_subclass ftpfs_subclass
;
2118 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
2119 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2120 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2121 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2122 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2123 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2124 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2125 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2126 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2127 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2128 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2130 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2131 vfs_ftpfs_ops
.name
= "ftpfs";
2132 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2133 vfs_ftpfs_ops
.prefix
= "ftp:";
2134 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2135 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2136 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2137 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2138 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2139 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2140 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2141 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2142 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2143 vfs_register_class (&vfs_ftpfs_ops
);