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 /* for uclibc < 0.9.29 */
113 #ifndef AI_ADDRCONFIG
114 #define AI_ADDRCONFIG 0x0020
117 #define RFC_AUTODETECT 0
121 #ifndef HAVE_SOCKLEN_T
122 typedef int socklen_t
;
125 static int ftpfs_errno
;
128 /* Delay to retry a connection */
129 int ftpfs_retry_seconds
= 30;
131 /* Method to use to connect to ftp sites */
132 int ftpfs_use_passive_connections
= 1;
133 int ftpfs_use_passive_connections_over_proxy
= 0;
135 /* Method used to get directory listings:
136 * 1: try 'LIST -la <path>', if it fails
137 * fall back to CWD <path>; LIST
138 * 0: always use CWD <path>; LIST
140 int ftpfs_use_unix_list_options
= 1;
142 /* First "CWD <path>", then "LIST -la ." */
143 int ftpfs_first_cd_then_ls
= 1;
145 /* Use the ~/.netrc */
148 /* Anonymous setup */
149 char *ftpfs_anonymous_passwd
= NULL
;
150 int ftpfs_directory_timeout
= 900;
153 char *ftpfs_proxy_host
= NULL
;
155 /* wether we have to use proxy by default? */
156 int ftpfs_always_use_proxy
;
158 #ifdef FIXME_LATER_ALIGATOR
159 static struct linklist
*connections_list
;
162 /* ftpfs_command wait_flag: */
164 #define WAIT_REPLY 0x01
165 #define WANT_STRING 0x02
166 static char reply_str
[80];
168 static struct vfs_class vfs_ftpfs_ops
;
170 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
171 Translate a Unix path, i.e. MC's internal path representation (e.g.
172 /somedir/somefile) to a path valid for the remote server. Every path
173 transfered to the remote server has to be mangled by this function
174 right prior to sending it.
175 Currently only Amiga ftp servers are handled in a special manner.
177 When the remote server is an amiga:
178 a) strip leading slash if necesarry
179 b) replace first occurance of ":/" with ":"
180 c) strip trailing "/."
183 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
184 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
);
185 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
186 __attribute__ ((format (__printf__
, 4, 5)));
187 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
188 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
);
189 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
192 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
194 if (!SUP
.remote_is_amiga
)
195 return g_strdup (remote_path
);
199 if (MEDATA
->logfile
) {
200 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
201 fflush (MEDATA
->logfile
);
204 /* strip leading slash(es) */
205 while (*remote_path
== '/')
209 * Don't change "/" into "", e.g. "CWD " would be
212 if (*remote_path
== '\0')
213 return g_strdup (".");
215 ret
= g_strdup (remote_path
);
217 /* replace first occurance of ":/" with ":" */
218 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
219 strcpy (p
+ 1, p
+ 2);
221 /* strip trailing "/." */
222 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
228 /* Extract the hostname and username from the path */
231 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
232 * ftp://sunsite.unc.edu/pub/linux
233 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
234 * ftp://tsx-11.mit.edu:8192/
235 * ftp://joe@foo.edu:11321/private
236 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
241 #define FTP_COMMAND_PORT 21
244 ftpfs_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
248 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
252 /* Look up user and password in netrc */
254 ftpfs_netrc_lookup (*host
, user
, pass
);
256 *user
= g_strdup ("anonymous");
259 /* Look up password in netrc for known user */
260 if (use_netrc
&& *user
&& pass
&& !*pass
) {
263 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
265 /* If user is different, remove password */
266 if (new_user
&& strcmp (*user
, new_user
)) {
277 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
279 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
285 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
291 switch (sscanf(answer
, "%d", &code
)){
294 g_strlcpy (string_buf
, answer
, string_len
);
298 if (answer
[3] == '-') {
300 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
306 if ((sscanf (answer
, "%d", &i
) > 0) &&
307 (code
== i
) && (answer
[3] == ' '))
312 g_strlcpy (string_buf
, answer
, string_len
);
319 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
321 int sock
= ftpfs_open_socket (me
, super
);
323 char *cwdir
= SUP
.cwdir
;
327 if (ftpfs_login_server (me
, super
, SUP
.password
)){
330 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
332 return sock
== COMPLETE
;
340 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
345 static int retry
= 0;
346 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
349 cmdstr
= g_strdup_vprintf (fmt
, ap
);
352 cmdlen
= strlen (cmdstr
);
353 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
354 strcpy (cmdstr
+ cmdlen
, "\r\n");
357 if (MEDATA
->logfile
) {
358 if (strncmp (cmdstr
, "PASS ", 5) == 0) {
359 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
361 fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
363 fflush (MEDATA
->logfile
);
367 tty_enable_interrupt_key ();
368 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
373 if (errno
== EPIPE
) { /* Remote server has closed connection */
376 status
= ftpfs_reconnect (me
, super
);
378 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
386 tty_disable_interrupt_key ();
391 tty_disable_interrupt_key ();
395 status
= ftpfs_get_reply (me
, SUP
.sock
,
396 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
397 sizeof (reply_str
) - 1);
398 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
402 status
= ftpfs_reconnect (me
, super
);
404 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
417 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
420 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
421 ftpfs_command(me
, super
, NONE
, "QUIT");
427 g_free (SUP
.password
);
430 /* some defines only used by ftpfs_changetype */
431 /* These two are valid values for the second parameter */
433 #define TYPE_BINARY 1
435 /* This one is only used to initialize bucket->isbinary, don't use it as
436 second parameter to ftpfs_changetype. */
437 #define TYPE_UNKNOWN -1
440 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
442 if (binary
!= SUP
.isbinary
) {
443 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
445 SUP
.isbinary
= binary
;
450 /* This routine logs the user in */
452 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
453 const char *netrcpass
)
457 char *name
; /* login user name */
459 char reply_string
[BUF_MEDIUM
];
461 SUP
.isbinary
= TYPE_UNKNOWN
;
463 if (SUP
.password
) /* explicit password */
464 op
= g_strdup (SUP
.password
);
465 else if (netrcpass
) /* password from netrc */
466 op
= g_strdup (netrcpass
);
467 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp")) {
468 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
469 ftpfs_init_passwd ();
470 op
= g_strdup (ftpfs_anonymous_passwd
);
472 } else { /* ask user */
475 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
, " ",
477 op
= vfs_get_password (p
);
481 SUP
.password
= g_strdup (op
);
484 if (!anon
|| MEDATA
->logfile
)
487 pass
= g_strconcat ("-", op
, (char *) NULL
);
491 /* Proxy server accepts: username@host-we-want-to-connect */
494 g_strconcat (SUP
.user
, "@",
495 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
,
498 name
= g_strdup (SUP
.user
);
501 (me
, SUP
.sock
, reply_string
,
502 sizeof (reply_string
) - 1) == COMPLETE
) {
503 g_strup (reply_string
);
504 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
505 if (MEDATA
->logfile
) {
506 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n",
507 SUP
.remote_is_amiga
);
508 fflush (MEDATA
->logfile
);
511 print_vfs_message (_("ftpfs: sending login name"));
513 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
)) {
515 print_vfs_message (_("ftpfs: sending user password"));
516 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
517 if (code
== CONTINUE
) {
520 p
= g_strdup_printf (_
521 ("FTP: Account required for user %s"),
523 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
527 print_vfs_message (_("ftpfs: sending user account"));
529 ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
532 if (code
!= COMPLETE
)
537 print_vfs_message (_("ftpfs: logged in"));
538 wipe_password (pass
);
543 SUP
.failed_on_login
= 1;
545 wipe_password (SUP
.password
);
551 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
554 wipe_password (pass
);
559 static struct no_proxy_entry
{
565 ftpfs_load_no_proxy_list (void)
567 /* FixMe: shouldn't be hardcoded!!! */
568 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
569 struct no_proxy_entry
*np
, *current
= 0;
573 static char *mc_file
;
578 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
579 if (exist_file (mc_file
) &&
580 (npf
= fopen (mc_file
, "r"))) {
581 while (fgets (s
, sizeof (s
), npf
)) {
582 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
583 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
593 np
= g_new (struct no_proxy_entry
, 1);
594 np
->domain
= g_strdup (s
);
608 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
610 ftpfs_check_proxy (const char *host
)
612 struct no_proxy_entry
*npe
;
614 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
615 return 0; /* sanity check */
620 if (!ftpfs_always_use_proxy
)
623 if (!strchr (host
, '.'))
626 ftpfs_load_no_proxy_list ();
627 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
628 char *domain
= npe
->domain
;
630 if (domain
[0] == '.') {
631 int ld
= strlen (domain
);
632 int lh
= strlen (host
);
634 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
642 if (!g_strcasecmp (host
, domain
))
650 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
655 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
662 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
664 struct addrinfo hints
, *res
, *curr_res
;
673 /* Use a proxy host? */
674 host
= g_strdup(SUP
.host
);
676 if (!host
|| !*host
){
677 print_vfs_message (_("ftpfs: Invalid host name."));
678 ftpfs_errno
= EINVAL
;
682 /* Hosts to connect to that start with a ! should use proxy */
686 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &tmp_port
);
689 port
= g_strdup_printf("%hu", (unsigned short) tmp_port
);
696 tty_enable_interrupt_key(); /* clear the interrupt flag */
698 memset (&hints
, 0, sizeof (struct addrinfo
));
699 hints
.ai_socktype
= SOCK_STREAM
;
700 hints
.ai_flags
= AI_ADDRCONFIG
;
703 e
= getaddrinfo (host
, port
, &hints
, &res
);
708 tty_disable_interrupt_key ();
709 print_vfs_message (_("ftpfs: %s"), gai_strerror (e
));
711 ftpfs_errno
= EINVAL
;
715 for (curr_res
= res
; curr_res
!= NULL
; curr_res
= curr_res
->ai_next
) {
717 my_socket
= socket (curr_res
->ai_family
, curr_res
->ai_socktype
, curr_res
->ai_protocol
);
721 if (curr_res
->ai_next
!= NULL
)
724 tty_disable_interrupt_key();
725 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno
));
732 print_vfs_message (_("ftpfs: making connection to %s"), host
);
736 if ( connect (my_socket
, curr_res
->ai_addr
, curr_res
->ai_addrlen
) >= 0 )
742 if (errno
== EINTR
&& tty_got_interrupt ()) {
743 print_vfs_message (_("ftpfs: connection interrupted by user"));
744 } else if (res
->ai_next
== NULL
) {
745 print_vfs_message (_("ftpfs: connection to server failed: %s"),
746 unix_error_string (errno
));
752 tty_disable_interrupt_key ();
757 tty_disable_interrupt_key ();
762 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
764 int retry_seconds
, count_down
;
766 /* We do not want to use the passive if we are using proxies */
768 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
772 SUP
.failed_on_login
= 0;
774 SUP
.sock
= ftpfs_open_socket (me
, super
);
778 if (ftpfs_login_server (me
, super
, NULL
)) {
779 /* Logged in, no need to retry the connection */
782 if (SUP
.failed_on_login
){
783 /* Close only the socket descriptor */
788 if (ftpfs_retry_seconds
){
789 retry_seconds
= ftpfs_retry_seconds
;
790 tty_enable_interrupt_key ();
791 for (count_down
= retry_seconds
; count_down
; count_down
--){
792 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
794 if (tty_got_interrupt ()) {
795 /* ftpfs_errno = E; */
796 tty_disable_interrupt_key ();
800 tty_disable_interrupt_key ();
803 } while (retry_seconds
);
805 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
807 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
812 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
813 const char *archive_name
, char *op
)
815 char *host
, *user
, *password
;
820 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
827 if (ftpfs_check_proxy (host
))
828 SUP
.proxy
= ftpfs_proxy_host
;
829 SUP
.password
= password
;
830 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
831 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
832 SUP
.isbinary
= TYPE_UNKNOWN
;
833 SUP
.remote_is_amiga
= 0;
834 super
->name
= g_strdup ("/");
836 vfs_s_new_inode (me
, super
,
837 vfs_s_default_stat (me
, S_IFDIR
| 0755));
839 return ftpfs_open_archive_int (me
, super
);
843 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
844 const char *archive_name
, char *op
, void *cookie
)
853 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
855 port
= ((strcmp (host
, SUP
.host
) == 0)
856 && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
864 /* The returned directory should always contain a trailing slash */
866 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
868 char buf
[BUF_8K
], *bufp
, *bufq
;
870 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
871 ftpfs_get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
873 for (bufq
= buf
; *bufq
; bufq
++)
880 if (*(bufq
- 1) != '/') {
885 return g_strdup (bufp
);
887 /* If the remote server is an Amiga a leading slash
888 might be missing. MC needs it because it is used
889 as separator between hostname and path internally. */
890 return g_strconcat( "/", bufp
, NULL
);
904 /* Setup Passive ftp connection, we use it for source routed connections */
906 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
,
907 int my_socket
, struct sockaddr_storage
*sa
, socklen_t
*salen
)
911 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "EPSV") == COMPLETE
) {
914 c
= strchr (reply_str
, '|');
923 if (port
< 0 || port
> 65535)
927 switch (sa
->ss_family
) {
929 ((struct sockaddr_in
*)sa
)->sin_port
= port
;
932 ((struct sockaddr_in6
*)sa
)->sin6_port
= port
;
935 print_vfs_message (_("ftpfs: invalid address family"));
938 } else if (sa
->ss_family
== AF_INET
) {
939 int xa
, xb
, xc
, xd
, xe
, xf
;
942 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
945 /* Parse remote parameters */
946 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++);
950 if (!isdigit ((unsigned char) *c
))
952 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
955 n
[0] = (unsigned char) xa
;
956 n
[1] = (unsigned char) xb
;
957 n
[2] = (unsigned char) xc
;
958 n
[3] = (unsigned char) xd
;
959 n
[4] = (unsigned char) xe
;
960 n
[5] = (unsigned char) xf
;
962 memcpy (&(((struct sockaddr_in
*)sa
)->sin_addr
.s_addr
), (void *)n
, 4);
963 memcpy (&(((struct sockaddr_in
*)sa
)->sin_port
), (void *)&n
[4], 2);
967 if (connect (my_socket
, (struct sockaddr
*) sa
, *salen
) < 0)
974 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
976 struct sockaddr_storage data_addr
;
977 socklen_t data_addrlen
;
981 memset (&data_addr
, 0, sizeof (struct sockaddr_storage
));
982 data_addrlen
= sizeof (struct sockaddr_storage
);
984 if (getpeername (SUP
.sock
, (struct sockaddr
*) &data_addr
, &data_addrlen
) == -1)
987 switch (data_addr
.ss_family
) {
989 ((struct sockaddr_in
*)&data_addr
)->sin_port
= 0;
992 ((struct sockaddr_in6
*)&data_addr
)->sin6_port
= 0;
995 print_vfs_message (_("ftpfs: invalid address family"));
999 data_sock
= socket (data_addr
.ss_family
, SOCK_STREAM
, IPPROTO_TCP
);
1000 if (data_sock
< 0) {
1001 if (SUP
.use_passive_connection
) {
1002 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno
));
1003 SUP
.use_passive_connection
= 0;
1007 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno
));
1011 if (SUP
.use_passive_connection
) {
1013 if (ftpfs_setup_passive (me
, super
, data_sock
, &data_addr
, &data_addrlen
))
1016 SUP
.use_passive_connection
= 0;
1017 print_vfs_message (_("ftpfs: could not setup passive mode"));
1023 /* If passive setup fails, fallback to active connections */
1024 /* Active FTP connection */
1025 if ((bind (data_sock
, (struct sockaddr
*)&data_addr
, data_addrlen
) == 0) &&
1026 (getsockname (data_sock
, (struct sockaddr
*)&data_addr
, &data_addrlen
) == 0) &&
1027 (listen (data_sock
, 1) == 0)) {
1028 unsigned short int port
;
1032 switch (data_addr
.ss_family
) {
1035 port
= ((struct sockaddr_in
*)&data_addr
)->sin_port
;
1039 port
= ((struct sockaddr_in6
*)&data_addr
)->sin6_port
;
1042 print_vfs_message (_("ftpfs: invalid address family"));
1043 ERRNOR (EINVAL
, -1);
1046 port
= ntohs (port
);
1048 addr
= g_malloc (NI_MAXHOST
);
1050 ERRNOR (ENOMEM
, -1);
1052 if (getnameinfo ((struct sockaddr
*)&data_addr
, data_addrlen
, addr
, NI_MAXHOST
, NULL
, 0, NI_NUMERICHOST
) != 0) {
1057 if (ftpfs_command (me
, super
, WAIT_REPLY
, "EPRT |%u|%s|%hu|", af
, addr
, port
) == COMPLETE
) {
1063 if (FTP_INET
== af
) {
1064 unsigned char *a
= (unsigned char *)&((struct sockaddr_in
*)&data_addr
)->sin_addr
;
1065 unsigned char *p
= (unsigned char *)&port
;
1067 if (ftpfs_command (me
, super
, WAIT_REPLY
,
1068 "PORT %u,%u,%u,%u,%u,%u", a
[0], a
[1], a
[2], a
[3],
1069 p
[0], p
[1]) == COMPLETE
)
1079 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
1080 const char *remote
, int isbinary
, int reget
)
1082 struct sockaddr_storage from
;
1084 socklen_t fromlen
= sizeof(from
);
1086 if ((s
= ftpfs_initconn (me
, super
)) == -1)
1088 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
1091 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1096 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
1097 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1098 /* WarFtpD can't STORE //filename */
1099 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1100 g_free (remote_path
);
1102 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1105 tty_enable_interrupt_key ();
1106 if (SUP
.use_passive_connection
)
1109 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
1111 ftpfs_errno
= errno
;
1117 tty_disable_interrupt_key ();
1121 #define ABORT_TIMEOUT 5
1123 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1125 struct vfs_s_super
*super
= FH_SUPER
;
1126 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1129 int dsock
= FH_SOCK
;
1131 SUP
.ctl_connection_busy
= 0;
1133 print_vfs_message (_("ftpfs: aborting transfer."));
1134 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
1135 print_vfs_message (_("ftpfs: abort error: %s"),
1136 unix_error_string (errno
));
1142 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1143 print_vfs_message (_("ftpfs: abort failed"));
1150 FD_SET (dsock
, &mask
);
1151 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1152 struct timeval start_tim
, tim
;
1153 gettimeofday (&start_tim
, NULL
);
1154 /* flush the remaining data */
1155 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1156 gettimeofday (&tim
, NULL
);
1157 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1158 /* server keeps sending, drop the connection and ftpfs_reconnect */
1160 ftpfs_reconnect (me
, super
);
1167 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1168 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1173 resolve_symlink_without_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1175 struct linklist
*flist
;
1176 struct direntry
*fe
, *fel
;
1177 char tmp
[MC_MAXPATHLEN
];
1180 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1181 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1182 /* flist->data->l_stat is alread initialized with 0 */
1184 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1185 if (fel
->linkname
[0] == '/') {
1186 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1188 strcpy (tmp
, fel
->linkname
);
1190 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1192 strcpy (tmp
, dir
->remote_path
);
1195 strcat (tmp
+ 1, fel
->linkname
);
1197 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1198 canonicalize_pathname (tmp
);
1199 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1201 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1202 /* Symlink points to link which isn't resolved, yet. */
1203 if (fe
->linkname
[0] == '/') {
1204 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1206 strcpy (tmp
, fe
->linkname
);
1208 /* at this point tmp looks always like this
1209 /directory/filename, i.e. no need to check
1210 strrchr's return value */
1211 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1212 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1214 strcat (tmp
, fe
->linkname
);
1218 fel
->l_stat
= g_new (struct stat
, 1);
1219 if ( S_ISLNK (fe
->s
.st_mode
))
1220 *fel
->l_stat
= *fe
->l_stat
;
1222 *fel
->l_stat
= fe
->s
;
1223 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1230 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1234 resolve_symlink_with_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1236 char buffer
[2048] = "", *filename
;
1240 struct linklist
*flist
;
1241 struct direntry
*fe
;
1242 int switch_method
= 0;
1244 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1245 if (strchr (dir
->remote_path
, ' ')) {
1246 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1247 print_vfs_message(_("ftpfs: CWD failed."));
1250 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1253 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa",
1254 dir
->remote_path
, TYPE_ASCII
, 0);
1257 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1261 fp
= fdopen(sock
, "r");
1264 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1267 tty_enable_interrupt_key ();
1268 flist
= dir
->file_list
->next
;
1271 if (flist
== dir
->file_list
)
1274 flist
= flist
->next
;
1275 } while (!S_ISLNK(fe
->s
.st_mode
));
1277 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1279 if (MEDATA
->logfile
){
1280 fputs (buffer
, MEDATA
->logfile
);
1281 fflush (MEDATA
->logfile
);
1283 vfs_die("This code should be commented out\n");
1284 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1285 int r
= strcmp(fe
->name
, filename
);
1288 if (S_ISLNK (s
.st_mode
)) {
1289 /* This server doesn't understand LIST -lLa */
1293 fe
->l_stat
= g_new (struct stat
, 1);
1294 if (fe
->l_stat
== NULL
)
1297 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1306 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1307 tty_disable_interrupt_key ();
1309 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1313 resolve_symlink(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1315 print_vfs_message(_("Resolving symlink..."));
1317 if (SUP
.strict_rfc959_list_cmd
)
1318 resolve_symlink_without_ls_options(me
, super
, dir
);
1320 resolve_symlink_with_ls_options(me
, super
, dir
);
1325 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1327 struct vfs_s_entry
*ent
;
1328 struct vfs_s_super
*super
= dir
->super
;
1329 int sock
, num_entries
= 0;
1330 char buffer
[BUF_8K
];
1333 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1334 || (strchr (remote_path
, ' ') != NULL
);
1337 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1340 RFC_STRICT
? _("(strict rfc959)") : "",
1341 cd_first
? _("(chdir first)") : "");
1344 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
) {
1345 ftpfs_errno
= ENOENT
;
1346 print_vfs_message (_("ftpfs: CWD failed."));
1351 gettimeofday (&dir
->timestamp
, NULL
);
1352 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1354 if (SUP
.strict
== RFC_STRICT
)
1355 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1357 /* Dirty hack to avoid autoprepending / to . */
1358 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1360 ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1362 /* Trailing "/." is necessary if remote_path is a symlink */
1363 char *path
= concat_dir_and_file (remote_path
, ".");
1365 ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
,
1373 /* Clear the interrupt flag */
1374 tty_enable_interrupt_key ();
1379 vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1385 me
->verrno
= ECONNRESET
;
1387 tty_disable_interrupt_key ();
1388 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1389 print_vfs_message (_("%s: failure"), me
->name
);
1393 if (MEDATA
->logfile
) {
1394 fputs (buffer
, MEDATA
->logfile
);
1395 fputs ("\n", MEDATA
->logfile
);
1396 fflush (MEDATA
->logfile
);
1399 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1400 i
= ent
->ino
->st
.st_nlink
;
1401 if (!vfs_parse_ls_lga
1402 (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1403 vfs_s_free_entry (me
, ent
);
1406 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1408 vfs_s_insert_entry (me
, dir
, ent
);
1412 me
->verrno
= E_REMOTE
;
1413 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1416 if (num_entries
== 0 && cd_first
== 0) {
1417 /* The LIST command may produce an empty output. In such scenario
1418 it is not clear whether this is caused by `remote_path' being
1419 a non-existent path or for some other reason (listing emtpy
1420 directory without the -a option, non-readable directory, etc.).
1422 Since `dir_load' is a crucial method, when it comes to determine
1423 whether a given path is a _directory_, the code must try its best
1424 to determine the type of `remote_path'. The only reliable way to
1425 achieve this is trough issuing a CWD command. */
1431 if (SUP
.strict
== RFC_AUTODETECT
)
1432 SUP
.strict
= RFC_DARING
;
1434 print_vfs_message (_("%s: done."), me
->name
);
1438 if (SUP
.strict
== RFC_AUTODETECT
) {
1439 /* It's our first attempt to get a directory listing from this
1440 server (UNIX style LIST command) */
1441 SUP
.strict
= RFC_STRICT
;
1442 /* I hate goto, but recursive call needs another 8K on stack */
1443 /* return ftpfs_dir_load (me, dir, remote_path); */
1447 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1448 ERRNOR (EACCES
, -1);
1452 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
,
1455 int h
, sock
, n_read
, n_written
;
1457 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1465 struct vfs_s_super
*super
= FH_SUPER
;
1467 h
= open (localname
, O_RDONLY
);
1471 ftpfs_open_data_connection (me
, super
,
1472 fh
->u
.ftp
.append
? "APPE" : "STOR", name
,
1474 if (sock
< 0 || fstat (h
, &s
) == -1) {
1478 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1481 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1483 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1487 tty_enable_interrupt_key ();
1489 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1) {
1490 if (errno
== EINTR
) {
1491 if (tty_got_interrupt ()) {
1492 ftpfs_errno
= EINTR
;
1497 ftpfs_errno
= errno
;
1504 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
) {
1505 if (n_written
== -1) {
1506 if (errno
== EINTR
&& !tty_got_interrupt ()) {
1509 ftpfs_errno
= errno
;
1513 n_read
-= n_written
;
1515 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1516 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1518 tty_disable_interrupt_key ();
1521 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1525 tty_disable_interrupt_key ();
1528 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1533 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1535 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1539 FH_SOCK
= ftpfs_open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1543 fh
->linear
= LS_LINEAR_OPEN
;
1544 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1545 fh
->u
.ftp
.append
= 0;
1550 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1553 struct vfs_s_super
*super
= FH_SUPER
;
1555 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1556 if ((errno
== EINTR
) && !tty_got_interrupt ())
1562 ftpfs_linear_abort(me
, fh
);
1565 SUP
.ctl_connection_busy
= 0;
1568 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1569 ERRNOR (E_REMOTE
, -1);
1576 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1579 ftpfs_linear_abort(me
, fh
);
1582 static int ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1587 case VFS_CTL_IS_NOTREADY
:
1592 vfs_die ("You may not do this");
1593 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1596 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1597 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1607 ftpfs_send_command(struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1610 char *p
, *mpath
= g_strdup(filename
);
1611 struct vfs_s_super
*super
;
1613 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1615 if (!(rpath
= vfs_s_get_path_mangle(me
, mpath
, &super
, 0))) {
1619 p
= ftpfs_translate_path (me
, super
, rpath
);
1620 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1622 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1623 if (flags
& OPT_IGNORE_ERROR
)
1625 if (r
!= COMPLETE
) {
1630 if (flush_directory_cache
)
1631 vfs_s_invalidate(me
, super
);
1636 /* This routine is called as the last step in load_setup */
1638 ftpfs_init_passwd(void)
1640 ftpfs_anonymous_passwd
= load_anon_passwd ();
1641 if (ftpfs_anonymous_passwd
)
1644 /* If there is no anonymous ftp password specified
1645 * then we'll just use anonymous@
1646 * We don't send any other thing because:
1647 * - We want to remain anonymous
1648 * - We want to stop SPAM
1649 * - We don't want to let ftp sites to discriminate by the user,
1652 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1655 static int ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1657 char buf
[BUF_SMALL
];
1659 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1660 return ftpfs_send_command(me
, path
, buf
, OPT_FLUSH
);
1663 static int ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1666 ftpfs_errno
= EPERM
;
1669 /* Everyone knows it is not possible to chown remotely, so why bother them.
1670 If someone's root, then copy/move will always try to chown it... */
1679 static int ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1681 return ftpfs_send_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1684 /* Return 1 if path is the same directory as the one we are in now */
1686 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1692 if (strcmp (path
, SUP
.cwdir
) == 0)
1698 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1703 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1706 p
= ftpfs_translate_path (me
, super
, remote_path
);
1707 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1710 if (r
!= COMPLETE
) {
1714 SUP
.cwdir
= g_strdup (remote_path
);
1715 SUP
.cwd_deferred
= 0;
1720 static int ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1722 ftpfs_send_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1723 return ftpfs_send_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1726 static int ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1728 (void) mode
; /* FIXME: should be used */
1730 return ftpfs_send_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1733 static int ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1735 return ftpfs_send_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1739 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
,
1744 fh
->u
.ftp
.append
= 0;
1745 /* File will be written only, so no need to retrieve it from ftp server */
1746 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
))) {
1747 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1754 /* ftpfs_linear_start() called, so data will be written
1755 * to local temporary file and stored to ftp server
1756 * by vfs_s_close later
1758 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
) {
1759 if (!fh
->ino
->localname
) {
1760 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1761 fh
->ino
->ent
->name
);
1765 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1769 name
= vfs_s_fullpath (me
, fh
->ino
);
1773 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1774 (flags
& O_APPEND
) ? "APPE" :
1775 "STOR", name
, TYPE_BINARY
, 0);
1780 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1784 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1786 if (fh
->ino
->localname
) {
1787 unlink (fh
->ino
->localname
);
1788 g_free (fh
->ino
->localname
);
1789 fh
->ino
->localname
= NULL
;
1794 if (!fh
->ino
->localname
)
1795 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1797 if (!fh
->ino
->localname
)
1798 vfs_die ("retrieve_file failed to fill in localname");
1802 static int ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1804 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1807 /* File is stored to destination already, so
1808 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1811 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1813 vfs_s_invalidate (me
, FH_SUPER
);
1819 ftpfs_done (struct vfs_class
*me
)
1821 struct no_proxy_entry
*np
;
1826 np
= no_proxy
->next
;
1827 g_free (no_proxy
->domain
);
1831 g_free (ftpfs_anonymous_passwd
);
1832 g_free (ftpfs_proxy_host
);
1836 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
1838 struct vfs_s_super
*super
= MEDATA
->supers
;
1842 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
1845 super
= super
->next
;
1849 static char buffer
[BUF_MEDIUM
];
1851 static const char *netrcp
;
1853 /* This should match the keywords[] array below */
1866 static keyword_t
ftpfs_netrc_next (void)
1870 static const char *const keywords
[] = { "default", "machine",
1871 "login", "password", "passwd", "account", "macdef", NULL
1876 netrcp
= skip_separators (netrcp
);
1877 if (*netrcp
!= '\n')
1884 if (*netrcp
== '"') {
1885 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1886 if (*netrcp
== '\\')
1891 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1892 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1893 if (*netrcp
== '\\')
1903 while (keywords
[i
- 1]) {
1904 if (!strcmp (keywords
[i
- 1], buffer
))
1910 return NETRC_UNKNOWN
;
1913 static int ftpfs_netrc_bad_mode (const char *netrcname
)
1915 static int be_angry
= 1;
1918 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1920 message (D_ERROR
, MSG_ERROR
,
1921 _("~/.netrc file has incorrect mode.\n"
1922 "Remove password or correct mode."));
1930 /* Scan .netrc until we find matching "machine" or "default"
1931 * domain is used for additional matching
1932 * No search is done after "default" in compliance with "man netrc"
1933 * Return 0 if found, -1 otherwise */
1934 static int ftpfs_find_machine (const char *host
, const char *domain
)
1938 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
) {
1939 if (keyword
== NETRC_DEFAULT
)
1942 if (keyword
== NETRC_MACDEF
) {
1943 /* Scan for an empty line, which concludes "macdef" */
1945 while (*netrcp
&& *netrcp
!= '\n')
1947 if (*netrcp
!= '\n')
1950 } while (*netrcp
&& *netrcp
!= '\n');
1954 if (keyword
!= NETRC_MACHINE
)
1957 /* Take machine name */
1958 if (ftpfs_netrc_next () == NETRC_NONE
)
1961 if (g_strcasecmp (host
, buffer
)) {
1962 /* Try adding our domain to short names in .netrc */
1963 const char *host_domain
= strchr (host
, '.');
1967 /* Compare domain part */
1968 if (g_strcasecmp (host_domain
, domain
))
1971 /* Compare local part */
1972 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1983 /* Extract login and password from .netrc for the host.
1985 * Returns 0 for success, -1 for error */
1986 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
1989 char *tmp_pass
= NULL
;
1990 char hostname
[MAXHOSTNAMELEN
];
1993 static struct rupcache
{
1994 struct rupcache
*next
;
1998 } *rup_cache
= NULL
, *rupp
;
2000 /* Initialize *login and *pass */
2007 /* Look up in the cache first */
2008 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
2009 if (!strcmp (host
, rupp
->host
)) {
2011 *login
= g_strdup (rupp
->login
);
2012 if (pass
&& rupp
->pass
)
2013 *pass
= g_strdup (rupp
->pass
);
2018 /* Load current .netrc */
2019 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
2020 netrcp
= netrc
= load_file (netrcname
);
2021 if (netrc
== NULL
) {
2026 /* Find our own domain name */
2027 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2029 if (!(domain
= strchr (hostname
, '.')))
2032 /* Scan for "default" and matching "machine" keywords */
2033 ftpfs_find_machine (host
, domain
);
2035 /* Scan for keywords following "default" and "machine" */
2038 keyword
= ftpfs_netrc_next ();
2042 if (ftpfs_netrc_next () == NETRC_NONE
) {
2047 /* We have another name already - should not happen */
2053 /* We have login name now */
2054 *login
= g_strdup (buffer
);
2057 case NETRC_PASSWORD
:
2059 if (ftpfs_netrc_next () == NETRC_NONE
) {
2064 /* Ignore unsafe passwords */
2065 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2066 && ftpfs_netrc_bad_mode (netrcname
)) {
2071 /* Remember password. pass may be NULL, so use tmp_pass */
2072 if (tmp_pass
== NULL
)
2073 tmp_pass
= g_strdup (buffer
);
2077 /* "account" is followed by a token which we ignore */
2078 if (ftpfs_netrc_next () == NETRC_NONE
) {
2083 /* Ignore account, but warn user anyways */
2084 ftpfs_netrc_bad_mode (netrcname
);
2088 /* Unexpected keyword or end of file */
2100 rupp
= g_new (struct rupcache
, 1);
2101 rupp
->host
= g_strdup (host
);
2102 rupp
->login
= rupp
->pass
= 0;
2104 if (*login
!= NULL
) {
2105 rupp
->login
= g_strdup (*login
);
2107 if (tmp_pass
!= NULL
)
2108 rupp
->pass
= g_strdup (tmp_pass
);
2109 rupp
->next
= rup_cache
;
2121 static struct vfs_s_subclass ftpfs_subclass
;
2123 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
2124 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2125 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2126 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2127 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2128 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2129 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2130 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2131 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2132 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2133 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2135 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2136 vfs_ftpfs_ops
.name
= "ftpfs";
2137 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2138 vfs_ftpfs_ops
.prefix
= "ftp:";
2139 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2140 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2141 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2142 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2143 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2144 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2145 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2146 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2147 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2148 vfs_register_class (&vfs_ftpfs_ops
);