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 <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
69 #include <netdb.h> /* struct hostent */
70 #include <sys/socket.h> /* AF_INET */
71 #include <netinet/in.h> /* struct in_addr */
72 #ifdef HAVE_ARPA_INET_H
73 #include <arpa/inet.h>
76 #include <arpa/telnet.h>
77 #include <sys/param.h>
81 #include <sys/time.h> /* gettimeofday() */
83 #include "../src/global.h"
85 #include "../src/tty/tty.h" /* enable/disable interrupt key */
87 #include "../src/wtools.h" /* message() */
88 #include "../src/main.h" /* print_vfs_message */
89 #include "../src/history.h"
90 #include "../src/setup.h" /* for load_anon_passwd */
93 #include "xdirentry.h"
96 #include "gc.h" /* vfs_stamp_create */
99 #ifndef MAXHOSTNAMELEN
100 # define MAXHOSTNAMELEN 64
103 #define UPLOAD_ZERO_LENGTH_FILE
104 #define SUP super->u.ftp
105 #define FH_SOCK fh->u.ftp.sock
108 #define INADDR_NONE 0xffffffff
111 #define RFC_AUTODETECT 0
115 #ifndef HAVE_SOCKLEN_T
116 typedef int socklen_t
;
119 static int ftpfs_errno
;
122 /* Delay to retry a connection */
123 int ftpfs_retry_seconds
= 30;
125 /* Method to use to connect to ftp sites */
126 int ftpfs_use_passive_connections
= 1;
127 int ftpfs_use_passive_connections_over_proxy
= 0;
129 /* Method used to get directory listings:
130 * 1: try 'LIST -la <path>', if it fails
131 * fall back to CWD <path>; LIST
132 * 0: always use CWD <path>; LIST
134 int ftpfs_use_unix_list_options
= 1;
136 /* First "CWD <path>", then "LIST -la ." */
137 int ftpfs_first_cd_then_ls
= 1;
139 /* Use the ~/.netrc */
142 /* Anonymous setup */
143 char *ftpfs_anonymous_passwd
= NULL
;
144 int ftpfs_directory_timeout
= 900;
147 char *ftpfs_proxy_host
= NULL
;
149 /* wether we have to use proxy by default? */
150 int ftpfs_always_use_proxy
;
152 #ifdef FIXME_LATER_ALIGATOR
153 static struct linklist
*connections_list
;
156 /* ftpfs_command wait_flag: */
158 #define WAIT_REPLY 0x01
159 #define WANT_STRING 0x02
160 static char reply_str
[80];
162 static struct vfs_class vfs_ftpfs_ops
;
164 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
165 Translate a Unix path, i.e. MC's internal path representation (e.g.
166 /somedir/somefile) to a path valid for the remote server. Every path
167 transfered to the remote server has to be mangled by this function
168 right prior to sending it.
169 Currently only Amiga ftp servers are handled in a special manner.
171 When the remote server is an amiga:
172 a) strip leading slash if necesarry
173 b) replace first occurance of ":/" with ":"
174 c) strip trailing "/."
177 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
178 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
);
179 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
180 __attribute__ ((format (__printf__
, 4, 5)));
181 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
182 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
);
183 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
186 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
188 if (!SUP
.remote_is_amiga
)
189 return g_strdup (remote_path
);
193 if (MEDATA
->logfile
) {
194 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
195 fflush (MEDATA
->logfile
);
198 /* strip leading slash(es) */
199 while (*remote_path
== '/')
203 * Don't change "/" into "", e.g. "CWD " would be
206 if (*remote_path
== '\0')
207 return g_strdup (".");
209 ret
= g_strdup (remote_path
);
211 /* replace first occurance of ":/" with ":" */
212 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
213 strcpy (p
+ 1, p
+ 2);
215 /* strip trailing "/." */
216 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
222 /* Extract the hostname and username from the path */
225 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
226 * ftp://sunsite.unc.edu/pub/linux
227 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
228 * ftp://tsx-11.mit.edu:8192/
229 * ftp://joe@foo.edu:11321/private
230 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
235 #define FTP_COMMAND_PORT 21
238 ftpfs_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
242 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
246 /* Look up user and password in netrc */
248 ftpfs_netrc_lookup (*host
, user
, pass
);
250 *user
= g_strdup ("anonymous");
253 /* Look up password in netrc for known user */
254 if (use_netrc
&& *user
&& pass
&& !*pass
) {
257 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
259 /* If user is different, remove password */
260 if (new_user
&& strcmp (*user
, new_user
)) {
271 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
273 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
279 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
285 switch (sscanf(answer
, "%d", &code
)){
288 g_strlcpy (string_buf
, answer
, string_len
);
292 if (answer
[3] == '-') {
294 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
300 if ((sscanf (answer
, "%d", &i
) > 0) &&
301 (code
== i
) && (answer
[3] == ' '))
306 g_strlcpy (string_buf
, answer
, string_len
);
313 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
315 int sock
= ftpfs_open_socket (me
, super
);
317 char *cwdir
= SUP
.cwdir
;
321 if (ftpfs_login_server (me
, super
, SUP
.password
)){
324 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
326 return sock
== COMPLETE
;
334 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
339 static int retry
= 0;
340 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
343 cmdstr
= g_strdup_vprintf (fmt
, ap
);
346 cmdlen
= strlen (cmdstr
);
347 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
348 strcpy (cmdstr
+ cmdlen
, "\r\n");
351 if (MEDATA
->logfile
) {
352 if (strncmp (cmdstr
, "PASS ", 5) == 0) {
353 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
355 fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
357 fflush (MEDATA
->logfile
);
361 tty_enable_interrupt_key ();
362 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
367 if (errno
== EPIPE
) { /* Remote server has closed connection */
370 status
= ftpfs_reconnect (me
, super
);
372 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
380 tty_disable_interrupt_key ();
385 tty_disable_interrupt_key ();
389 status
= ftpfs_get_reply (me
, SUP
.sock
,
390 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
391 sizeof (reply_str
) - 1);
392 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
396 status
= ftpfs_reconnect (me
, super
);
398 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
411 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
414 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
415 ftpfs_command(me
, super
, NONE
, "QUIT");
421 g_free (SUP
.password
);
424 /* some defines only used by ftpfs_changetype */
425 /* These two are valid values for the second parameter */
427 #define TYPE_BINARY 1
429 /* This one is only used to initialize bucket->isbinary, don't use it as
430 second parameter to ftpfs_changetype. */
431 #define TYPE_UNKNOWN -1
434 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
436 if (binary
!= SUP
.isbinary
) {
437 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
439 SUP
.isbinary
= binary
;
444 /* This routine logs the user in */
446 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
447 const char *netrcpass
)
451 char *name
; /* login user name */
453 char reply_string
[BUF_MEDIUM
];
455 SUP
.isbinary
= TYPE_UNKNOWN
;
457 if (SUP
.password
) /* explicit password */
458 op
= g_strdup (SUP
.password
);
459 else if (netrcpass
) /* password from netrc */
460 op
= g_strdup (netrcpass
);
461 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp")) {
462 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
463 ftpfs_init_passwd ();
464 op
= g_strdup (ftpfs_anonymous_passwd
);
466 } else { /* ask user */
469 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
, " ",
471 op
= vfs_get_password (p
);
475 SUP
.password
= g_strdup (op
);
478 if (!anon
|| MEDATA
->logfile
)
481 pass
= g_strconcat ("-", op
, (char *) NULL
);
485 /* Proxy server accepts: username@host-we-want-to-connect */
488 g_strconcat (SUP
.user
, "@",
489 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
,
492 name
= g_strdup (SUP
.user
);
495 (me
, SUP
.sock
, reply_string
,
496 sizeof (reply_string
) - 1) == COMPLETE
) {
497 g_strup (reply_string
);
498 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
499 if (MEDATA
->logfile
) {
500 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n",
501 SUP
.remote_is_amiga
);
502 fflush (MEDATA
->logfile
);
505 print_vfs_message (_("ftpfs: sending login name"));
507 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
)) {
509 print_vfs_message (_("ftpfs: sending user password"));
510 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
511 if (code
== CONTINUE
) {
514 p
= g_strdup_printf (_
515 ("FTP: Account required for user %s"),
517 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
521 print_vfs_message (_("ftpfs: sending user account"));
523 ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
526 if (code
!= COMPLETE
)
531 print_vfs_message (_("ftpfs: logged in"));
532 wipe_password (pass
);
537 SUP
.failed_on_login
= 1;
539 wipe_password (SUP
.password
);
545 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
548 wipe_password (pass
);
553 static struct no_proxy_entry
{
559 ftpfs_load_no_proxy_list (void)
561 /* FixMe: shouldn't be hardcoded!!! */
562 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
563 struct no_proxy_entry
*np
, *current
= 0;
567 static char *mc_file
;
572 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
573 if (exist_file (mc_file
) &&
574 (npf
= fopen (mc_file
, "r"))) {
575 while (fgets (s
, sizeof (s
), npf
)) {
576 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
577 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
587 np
= g_new (struct no_proxy_entry
, 1);
588 np
->domain
= g_strdup (s
);
602 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
604 ftpfs_check_proxy (const char *host
)
606 struct no_proxy_entry
*npe
;
608 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
609 return 0; /* sanity check */
614 if (!ftpfs_always_use_proxy
)
617 if (!strchr (host
, '.'))
620 ftpfs_load_no_proxy_list ();
621 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
622 char *domain
= npe
->domain
;
624 if (domain
[0] == '.') {
625 int ld
= strlen (domain
);
626 int lh
= strlen (host
);
628 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
636 if (!g_strcasecmp (host
, domain
))
644 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
649 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
656 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
658 struct addrinfo hints
, *res
, *curr_res
;
667 /* Use a proxy host? */
668 host
= g_strdup(SUP
.host
);
670 if (!host
|| !*host
){
671 print_vfs_message (_("ftpfs: Invalid host name."));
672 ftpfs_errno
= EINVAL
;
676 /* Hosts to connect to that start with a ! should use proxy */
680 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
683 port
= g_strdup_printf("%hu", (unsigned short) tmp_port
);
690 tty_enable_interrupt_key(); /* clear the interrupt flag */
692 memset (&hints
, 0, sizeof (struct addrinfo
));
693 hints
.ai_socktype
= SOCK_STREAM
;
694 hints
.ai_flags
= AI_ADDRCONFIG
;
697 e
= getaddrinfo (host
, port
, &hints
, &res
);
702 tty_disable_interrupt_key ();
703 print_vfs_message (_("ftpfs: %s"), gai_strerror (e
));
705 ftpfs_errno
= EINVAL
;
709 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
) {
711 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
715 if (curr_res
->ai_next
!= NULL
)
718 tty_disable_interrupt_key();
719 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno
));
726 print_vfs_message (_("ftpfs: making connection to %s"), host
);
730 if ( connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0 )
736 if (errno
== EINTR
&& tty_got_interrupt ()) {
737 print_vfs_message (_("ftpfs: connection interrupted by user"));
738 } else if (res
->ai_next
== NULL
) {
739 print_vfs_message (_("ftpfs: connection to server failed: %s"),
740 unix_error_string (errno
));
746 tty_disable_interrupt_key ();
751 tty_disable_interrupt_key ();
756 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
758 int retry_seconds
, count_down
;
760 /* We do not want to use the passive if we are using proxies */
762 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
766 SUP
.failed_on_login
= 0;
768 SUP
.sock
= ftpfs_open_socket (me
, super
);
772 if (ftpfs_login_server (me
, super
, NULL
)) {
773 /* Logged in, no need to retry the connection */
776 if (SUP
.failed_on_login
){
777 /* Close only the socket descriptor */
782 if (ftpfs_retry_seconds
){
783 retry_seconds
= ftpfs_retry_seconds
;
784 tty_enable_interrupt_key ();
785 for (count_down
= retry_seconds
; count_down
; count_down
--){
786 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
788 if (tty_got_interrupt ()) {
789 /* ftpfs_errno = E; */
790 tty_disable_interrupt_key ();
794 tty_disable_interrupt_key ();
797 } while (retry_seconds
);
799 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
801 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
806 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
807 const char *archive_name
, char *op
)
809 char *host
, *user
, *password
;
814 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
821 if (ftpfs_check_proxy (host
))
822 SUP
.proxy
= ftpfs_proxy_host
;
823 SUP
.password
= password
;
824 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
825 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
826 SUP
.isbinary
= TYPE_UNKNOWN
;
827 SUP
.remote_is_amiga
= 0;
828 super
->name
= g_strdup ("/");
830 vfs_s_new_inode (me
, super
,
831 vfs_s_default_stat (me
, S_IFDIR
| 0755));
833 return ftpfs_open_archive_int (me
, super
);
837 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
838 const char *archive_name
, char *op
, void *cookie
)
847 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
849 port
= ((strcmp (host
, SUP
.host
) == 0)
850 && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
858 /* The returned directory should always contain a trailing slash */
860 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
862 char buf
[BUF_8K
], *bufp
, *bufq
;
864 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
865 ftpfs_get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
867 for (bufq
= buf
; *bufq
; bufq
++)
874 if (*(bufq
- 1) != '/') {
879 return g_strdup (bufp
);
881 /* If the remote server is an Amiga a leading slash
882 might be missing. MC needs it because it is used
883 as separator between hostname and path internally. */
884 return g_strconcat( "/", bufp
, NULL
);
898 /* Setup Passive ftp connection, we use it for source routed connections */
900 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
901 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
*salen
)
905 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") == COMPLETE
) {
908 c
= strchr (reply_str
, '|');
917 if (port
< 0 || port
> 65535)
921 switch (sa
->ss_family
) {
923 ((struct sockaddr_in
*)sa
)->sin_port
= port
;
926 ((struct sockaddr_in6
*)sa
)->sin6_port
= port
;
929 print_vfs_message (_("ftpfs: invalid address family"));
932 } else if (sa
->ss_family
== AF_INET
) {
933 int xa
, xb
, xc
, xd
, xe
, xf
;
936 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
939 /* Parse remote parameters */
940 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
944 if (!isdigit ((unsigned char) *c
))
946 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
949 n
[0] = (unsigned char) xa
;
950 n
[1] = (unsigned char) xb
;
951 n
[2] = (unsigned char) xc
;
952 n
[3] = (unsigned char) xd
;
953 n
[4] = (unsigned char) xe
;
954 n
[5] = (unsigned char) xf
;
956 memcpy (&(((struct sockaddr_in
*)sa
)->sin_addr
.s_addr
), (void *)n
, 4);
957 memcpy (&(((struct sockaddr_in
*)sa
)->sin_port
), (void *)&n
[4], 2);
961 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
968 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
970 struct sockaddr_storage data_addr
;
971 socklen_t data_addrlen
;
975 memset (&data_addr
, 0, sizeof (struct sockaddr_storage
));
976 data_addrlen
= sizeof (struct sockaddr_storage
);
978 if (getpeername (SUP
.sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == -1)
981 switch (data_addr
.ss_family
) {
983 ((struct sockaddr_in
*)&data_addr
)->sin_port
= 0;
986 ((struct sockaddr_in6
*)&data_addr
)->sin6_port
= 0;
989 print_vfs_message (_("ftpfs: invalid address family"));
993 data_sock
= socket (data_addr
.ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
995 if (SUP
.use_passive_connection
) {
996 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno
));
997 SUP
.use_passive_connection
= 0;
1001 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1005 if (SUP
.use_passive_connection
) {
1007 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1010 SUP
.use_passive_connection
= 0;
1011 print_vfs_message (_("ftpfs: could not setup passive mode"));
1017 /* If passive setup fails, fallback to active connections */
1018 /* Active FTP connection */
1019 if ((bind (data_sock
, (struct sockaddr
*)&data_addr
, data_addrlen
) == 0) &&
1020 (getsockname (data_sock
, (struct sockaddr
*)&data_addr
, &data_addrlen
) == 0) &&
1021 (listen (data_sock
, 1) == 0)) {
1022 unsigned short int port
;
1026 switch (data_addr
.ss_family
) {
1029 port
= ((struct sockaddr_in
*)&data_addr
)->sin_port
;
1033 port
= ((struct sockaddr_in6
*)&data_addr
)->sin6_port
;
1036 print_vfs_message (_("ftpfs: invalid address family"));
1037 ERRNOR (EINVAL
, -1);
1040 port
= ntohs (port
);
1042 addr
= malloc (NI_MAXHOST
);
1044 ERRNOR (ENOMEM
, -1);
1046 if (getnameinfo ((struct sockaddr
*)&data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0, NI_NUMERICHOST
) != 0) {
1051 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
) {
1057 if (FTP_INET
== af
) {
1058 unsigned char *a
= (unsigned char *)&((struct sockaddr_in
*)&data_addr
)->sin_addr
;
1059 unsigned char *p
= (unsigned char *)&port
;
1061 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1062 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1063 p
[0], p
[1]) == COMPLETE
)
1073 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1074 const char *remote
, int isbinary
, int reget
)
1076 struct sockaddr_storage from
;
1078 socklen_t fromlen
= sizeof(from
);
1080 if ((s
= ftpfs_initconn (me
, super
)) == -1)
1082 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1085 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1090 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1091 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1092 /* WarFtpD can't STORE //filename */
1093 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1094 g_free (remote_path
);
1096 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1099 tty_enable_interrupt_key ();
1100 if (SUP
.use_passive_connection
)
1103 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
1105 ftpfs_errno
= errno
;
1111 tty_disable_interrupt_key ();
1115 #define ABORT_TIMEOUT 5
1117 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1119 struct vfs_s_super
*super
= FH_SUPER
;
1120 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1123 int dsock
= FH_SOCK
;
1125 SUP
.ctl_connection_busy
= 0;
1127 print_vfs_message (_("ftpfs: aborting transfer."));
1128 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
1129 print_vfs_message (_("ftpfs: abort error: %s"),
1130 unix_error_string (errno
));
1136 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1137 print_vfs_message (_("ftpfs: abort failed"));
1144 FD_SET (dsock
, &mask
);
1145 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1146 struct timeval start_tim
, tim
;
1147 gettimeofday (&start_tim
, NULL
);
1148 /* flush the remaining data */
1149 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1150 gettimeofday (&tim
, NULL
);
1151 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1152 /* server keeps sending, drop the connection and ftpfs_reconnect */
1154 ftpfs_reconnect (me
, super
);
1161 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1162 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1167 resolve_symlink_without_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1169 struct linklist
*flist
;
1170 struct direntry
*fe
, *fel
;
1171 char tmp
[MC_MAXPATHLEN
];
1174 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1175 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1176 /* flist->data->l_stat is alread initialized with 0 */
1178 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1179 if (fel
->linkname
[0] == '/') {
1180 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1182 strcpy (tmp
, fel
->linkname
);
1184 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1186 strcpy (tmp
, dir
->remote_path
);
1189 strcat (tmp
+ 1, fel
->linkname
);
1191 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1192 canonicalize_pathname (tmp
);
1193 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1195 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1196 /* Symlink points to link which isn't resolved, yet. */
1197 if (fe
->linkname
[0] == '/') {
1198 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1200 strcpy (tmp
, fe
->linkname
);
1202 /* at this point tmp looks always like this
1203 /directory/filename, i.e. no need to check
1204 strrchr's return value */
1205 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1206 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1208 strcat (tmp
, fe
->linkname
);
1212 fel
->l_stat
= g_new (struct stat
, 1);
1213 if ( S_ISLNK (fe
->s
.st_mode
))
1214 *fel
->l_stat
= *fe
->l_stat
;
1216 *fel
->l_stat
= fe
->s
;
1217 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1224 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1228 resolve_symlink_with_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1230 char buffer
[2048] = "", *filename
;
1234 struct linklist
*flist
;
1235 struct direntry
*fe
;
1236 int switch_method
= 0;
1238 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1239 if (strchr (dir
->remote_path
, ' ')) {
1240 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1241 print_vfs_message(_("ftpfs: CWD failed."));
1244 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1247 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa",
1248 dir
->remote_path
, TYPE_ASCII
, 0);
1251 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1255 fp
= fdopen(sock
, "r");
1258 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1261 tty_enable_interrupt_key ();
1262 flist
= dir
->file_list
->next
;
1265 if (flist
== dir
->file_list
)
1268 flist
= flist
->next
;
1269 } while (!S_ISLNK(fe
->s
.st_mode
));
1271 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1273 if (MEDATA
->logfile
){
1274 fputs (buffer
, MEDATA
->logfile
);
1275 fflush (MEDATA
->logfile
);
1277 vfs_die("This code should be commented out\n");
1278 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1279 int r
= strcmp(fe
->name
, filename
);
1282 if (S_ISLNK (s
.st_mode
)) {
1283 /* This server doesn't understand LIST -lLa */
1287 fe
->l_stat
= g_new (struct stat
, 1);
1288 if (fe
->l_stat
== NULL
)
1291 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1300 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1301 tty_disable_interrupt_key ();
1303 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1307 resolve_symlink(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1309 print_vfs_message(_("Resolving symlink..."));
1311 if (SUP
.strict_rfc959_list_cmd
)
1312 resolve_symlink_without_ls_options(me
, super
, dir
);
1314 resolve_symlink_with_ls_options(me
, super
, dir
);
1319 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1321 struct vfs_s_entry
*ent
;
1322 struct vfs_s_super
*super
= dir
->super
;
1323 int sock
, num_entries
= 0;
1324 char buffer
[BUF_8K
];
1327 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1328 || (strchr (remote_path
, ' ') != NULL
);
1331 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1334 RFC_STRICT
? _("(strict rfc959)") : "",
1335 cd_first
? _("(chdir first)") : "");
1338 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
) {
1339 ftpfs_errno
= ENOENT
;
1340 print_vfs_message (_("ftpfs: CWD failed."));
1345 gettimeofday (&dir
->timestamp
, NULL
);
1346 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1348 if (SUP
.strict
== RFC_STRICT
)
1349 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1351 /* Dirty hack to avoid autoprepending / to . */
1352 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1354 ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1356 /* Trailing "/." is necessary if remote_path is a symlink */
1357 char *path
= concat_dir_and_file (remote_path
, ".");
1359 ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
,
1367 /* Clear the interrupt flag */
1368 tty_enable_interrupt_key ();
1373 vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1379 me
->verrno
= ECONNRESET
;
1381 tty_disable_interrupt_key ();
1382 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1383 print_vfs_message (_("%s: failure"), me
->name
);
1387 if (MEDATA
->logfile
) {
1388 fputs (buffer
, MEDATA
->logfile
);
1389 fputs ("\n", MEDATA
->logfile
);
1390 fflush (MEDATA
->logfile
);
1393 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1394 i
= ent
->ino
->st
.st_nlink
;
1395 if (!vfs_parse_ls_lga
1396 (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1397 vfs_s_free_entry (me
, ent
);
1400 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1402 vfs_s_insert_entry (me
, dir
, ent
);
1406 me
->verrno
= E_REMOTE
;
1407 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1410 if (num_entries
== 0 && cd_first
== 0) {
1411 /* The LIST command may produce an empty output. In such scenario
1412 it is not clear whether this is caused by `remote_path' being
1413 a non-existent path or for some other reason (listing emtpy
1414 directory without the -a option, non-readable directory, etc.).
1416 Since `dir_load' is a crucial method, when it comes to determine
1417 whether a given path is a _directory_, the code must try its best
1418 to determine the type of `remote_path'. The only reliable way to
1419 achieve this is trough issuing a CWD command. */
1425 if (SUP
.strict
== RFC_AUTODETECT
)
1426 SUP
.strict
= RFC_DARING
;
1428 print_vfs_message (_("%s: done."), me
->name
);
1432 if (SUP
.strict
== RFC_AUTODETECT
) {
1433 /* It's our first attempt to get a directory listing from this
1434 server (UNIX style LIST command) */
1435 SUP
.strict
= RFC_STRICT
;
1436 /* I hate goto, but recursive call needs another 8K on stack */
1437 /* return ftpfs_dir_load (me, dir, remote_path); */
1441 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1442 ERRNOR (EACCES
, -1);
1446 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
,
1449 int h
, sock
, n_read
, n_written
;
1451 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1459 struct vfs_s_super
*super
= FH_SUPER
;
1461 h
= open (localname
, O_RDONLY
);
1465 ftpfs_open_data_connection (me
, super
,
1466 fh
->u
.ftp
.append
? "APPE" : "STOR", name
,
1468 if (sock
< 0 || fstat (h
, &s
) == -1) {
1472 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1475 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1477 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1481 tty_enable_interrupt_key ();
1483 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1) {
1484 if (errno
== EINTR
) {
1485 if (tty_got_interrupt ()) {
1486 ftpfs_errno
= EINTR
;
1491 ftpfs_errno
= errno
;
1498 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
) {
1499 if (n_written
== -1) {
1500 if (errno
== EINTR
&& !tty_got_interrupt ()) {
1503 ftpfs_errno
= errno
;
1507 n_read
-= n_written
;
1509 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1510 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1512 tty_disable_interrupt_key ();
1515 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1519 tty_disable_interrupt_key ();
1522 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1527 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1529 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1533 FH_SOCK
= ftpfs_open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1537 fh
->linear
= LS_LINEAR_OPEN
;
1538 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1539 fh
->u
.ftp
.append
= 0;
1544 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1547 struct vfs_s_super
*super
= FH_SUPER
;
1549 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1550 if ((errno
== EINTR
) && !tty_got_interrupt ())
1556 ftpfs_linear_abort(me
, fh
);
1559 SUP
.ctl_connection_busy
= 0;
1562 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1563 ERRNOR (E_REMOTE
, -1);
1570 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1573 ftpfs_linear_abort(me
, fh
);
1576 static int ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1581 case VFS_CTL_IS_NOTREADY
:
1586 vfs_die ("You may not do this");
1587 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1590 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1591 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1601 ftpfs_send_command(struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1604 char *p
, *mpath
= g_strdup(filename
);
1605 struct vfs_s_super
*super
;
1607 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1609 if (!(rpath
= vfs_s_get_path_mangle(me
, mpath
, &super
, 0))) {
1613 p
= ftpfs_translate_path (me
, super
, rpath
);
1614 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1616 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1617 if (flags
& OPT_IGNORE_ERROR
)
1619 if (r
!= COMPLETE
) {
1624 if (flush_directory_cache
)
1625 vfs_s_invalidate(me
, super
);
1630 /* This routine is called as the last step in load_setup */
1632 ftpfs_init_passwd(void)
1634 ftpfs_anonymous_passwd
= load_anon_passwd ();
1635 if (ftpfs_anonymous_passwd
)
1638 /* If there is no anonymous ftp password specified
1639 * then we'll just use anonymous@
1640 * We don't send any other thing because:
1641 * - We want to remain anonymous
1642 * - We want to stop SPAM
1643 * - We don't want to let ftp sites to discriminate by the user,
1646 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1649 static int ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1651 char buf
[BUF_SMALL
];
1653 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1654 return ftpfs_send_command(me
, path
, buf
, OPT_FLUSH
);
1657 static int ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1660 ftpfs_errno
= EPERM
;
1663 /* Everyone knows it is not possible to chown remotely, so why bother them.
1664 If someone's root, then copy/move will always try to chown it... */
1673 static int ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1675 return ftpfs_send_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1678 /* Return 1 if path is the same directory as the one we are in now */
1680 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1686 if (strcmp (path
, SUP
.cwdir
) == 0)
1692 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1697 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1700 p
= ftpfs_translate_path (me
, super
, remote_path
);
1701 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1704 if (r
!= COMPLETE
) {
1708 SUP
.cwdir
= g_strdup (remote_path
);
1709 SUP
.cwd_deferred
= 0;
1714 static int ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1716 ftpfs_send_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1717 return ftpfs_send_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1720 static int ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1722 (void) mode
; /* FIXME: should be used */
1724 return ftpfs_send_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1727 static int ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1729 return ftpfs_send_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1733 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
,
1738 fh
->u
.ftp
.append
= 0;
1739 /* File will be written only, so no need to retrieve it from ftp server */
1740 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
))) {
1741 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1748 /* ftpfs_linear_start() called, so data will be written
1749 * to local temporary file and stored to ftp server
1750 * by vfs_s_close later
1752 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
) {
1753 if (!fh
->ino
->localname
) {
1754 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1755 fh
->ino
->ent
->name
);
1759 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1763 name
= vfs_s_fullpath (me
, fh
->ino
);
1767 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1768 (flags
& O_APPEND
) ? "APPE" :
1769 "STOR", name
, TYPE_BINARY
, 0);
1774 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1778 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1780 if (fh
->ino
->localname
) {
1781 unlink (fh
->ino
->localname
);
1782 g_free (fh
->ino
->localname
);
1783 fh
->ino
->localname
= NULL
;
1788 if (!fh
->ino
->localname
)
1789 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1791 if (!fh
->ino
->localname
)
1792 vfs_die ("retrieve_file failed to fill in localname");
1796 static int ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1798 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1801 /* File is stored to destination already, so
1802 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1805 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1807 vfs_s_invalidate (me
, FH_SUPER
);
1813 ftpfs_done (struct vfs_class
*me
)
1815 struct no_proxy_entry
*np
;
1820 np
= no_proxy
->next
;
1821 g_free (no_proxy
->domain
);
1825 g_free (ftpfs_anonymous_passwd
);
1826 g_free (ftpfs_proxy_host
);
1830 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
1832 struct vfs_s_super
*super
= MEDATA
->supers
;
1836 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
1839 super
= super
->next
;
1843 static char buffer
[BUF_MEDIUM
];
1845 static const char *netrcp
;
1847 /* This should match the keywords[] array below */
1860 static keyword_t
ftpfs_netrc_next (void)
1864 static const char *const keywords
[] = { "default", "machine",
1865 "login", "password", "passwd", "account", "macdef", NULL
1870 netrcp
= skip_separators (netrcp
);
1871 if (*netrcp
!= '\n')
1878 if (*netrcp
== '"') {
1879 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1880 if (*netrcp
== '\\')
1885 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1886 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1887 if (*netrcp
== '\\')
1897 while (keywords
[i
- 1]) {
1898 if (!strcmp (keywords
[i
- 1], buffer
))
1904 return NETRC_UNKNOWN
;
1907 static int ftpfs_netrc_bad_mode (const char *netrcname
)
1909 static int be_angry
= 1;
1912 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1914 message (D_ERROR
, MSG_ERROR
,
1915 _("~/.netrc file has incorrect mode.\n"
1916 "Remove password or correct mode."));
1924 /* Scan .netrc until we find matching "machine" or "default"
1925 * domain is used for additional matching
1926 * No search is done after "default" in compliance with "man netrc"
1927 * Return 0 if found, -1 otherwise */
1928 static int ftpfs_find_machine (const char *host
, const char *domain
)
1932 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
) {
1933 if (keyword
== NETRC_DEFAULT
)
1936 if (keyword
== NETRC_MACDEF
) {
1937 /* Scan for an empty line, which concludes "macdef" */
1939 while (*netrcp
&& *netrcp
!= '\n')
1941 if (*netrcp
!= '\n')
1944 } while (*netrcp
&& *netrcp
!= '\n');
1948 if (keyword
!= NETRC_MACHINE
)
1951 /* Take machine name */
1952 if (ftpfs_netrc_next () == NETRC_NONE
)
1955 if (g_strcasecmp (host
, buffer
)) {
1956 /* Try adding our domain to short names in .netrc */
1957 const char *host_domain
= strchr (host
, '.');
1961 /* Compare domain part */
1962 if (g_strcasecmp (host_domain
, domain
))
1965 /* Compare local part */
1966 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1977 /* Extract login and password from .netrc for the host.
1979 * Returns 0 for success, -1 for error */
1980 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
1983 char *tmp_pass
= NULL
;
1984 char hostname
[MAXHOSTNAMELEN
];
1987 static struct rupcache
{
1988 struct rupcache
*next
;
1992 } *rup_cache
= NULL
, *rupp
;
1994 /* Initialize *login and *pass */
2001 /* Look up in the cache first */
2002 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
2003 if (!strcmp (host
, rupp
->host
)) {
2005 *login
= g_strdup (rupp
->login
);
2006 if (pass
&& rupp
->pass
)
2007 *pass
= g_strdup (rupp
->pass
);
2012 /* Load current .netrc */
2013 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
2014 netrcp
= netrc
= load_file (netrcname
);
2015 if (netrc
== NULL
) {
2020 /* Find our own domain name */
2021 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2023 if (!(domain
= strchr (hostname
, '.')))
2026 /* Scan for "default" and matching "machine" keywords */
2027 ftpfs_find_machine (host
, domain
);
2029 /* Scan for keywords following "default" and "machine" */
2032 keyword
= ftpfs_netrc_next ();
2036 if (ftpfs_netrc_next () == NETRC_NONE
) {
2041 /* We have another name already - should not happen */
2047 /* We have login name now */
2048 *login
= g_strdup (buffer
);
2051 case NETRC_PASSWORD
:
2053 if (ftpfs_netrc_next () == NETRC_NONE
) {
2058 /* Ignore unsafe passwords */
2059 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2060 && ftpfs_netrc_bad_mode (netrcname
)) {
2065 /* Remember password. pass may be NULL, so use tmp_pass */
2066 if (tmp_pass
== NULL
)
2067 tmp_pass
= g_strdup (buffer
);
2071 /* "account" is followed by a token which we ignore */
2072 if (ftpfs_netrc_next () == NETRC_NONE
) {
2077 /* Ignore account, but warn user anyways */
2078 ftpfs_netrc_bad_mode (netrcname
);
2082 /* Unexpected keyword or end of file */
2094 rupp
= g_new (struct rupcache
, 1);
2095 rupp
->host
= g_strdup (host
);
2096 rupp
->login
= rupp
->pass
= 0;
2098 if (*login
!= NULL
) {
2099 rupp
->login
= g_strdup (*login
);
2101 if (tmp_pass
!= NULL
)
2102 rupp
->pass
= g_strdup (tmp_pass
);
2103 rupp
->next
= rup_cache
;
2115 static struct vfs_s_subclass ftpfs_subclass
;
2117 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
2118 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2119 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2120 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2121 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2122 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2123 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2124 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2125 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2126 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2127 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2129 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2130 vfs_ftpfs_ops
.name
= "ftpfs";
2131 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2132 vfs_ftpfs_ops
.prefix
= "ftp:";
2133 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2134 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2135 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2136 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2137 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2138 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2139 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2140 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2141 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2142 vfs_register_class (&vfs_ftpfs_ops
);