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 - make it more robust - all the connects etc. should handle EADDRINUSE and
28 ERETRY (have I spelled these names correctly?)
29 - make the user able to flush a connection - all the caches will get empty
30 etc., (tarfs as well), we should give there a user selectable timeout
31 and assign a key sequence.
32 - use hash table instead of linklist to cache ftpfs directory.
37 * NOTE: Usage of tildes is deprecated, consider:
38 * cd /#ftp:pavel@hobit
40 * And now: what do I want to do? Do I want to go to /home/pavel or to
41 * /#ftp:hobit/home/pavel? I think first has better sense...
44 int f = !strcmp( remote_path, "/~" );
45 if (f || !strncmp( remote_path, "/~/", 3 )) {
47 s = mhl_str_dir_plus_file( qhome (*bucket), remote_path +3-f );
56 /* Namespace pollution: horrible */
59 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
60 #include <netdb.h> /* struct hostent */
61 #include <sys/socket.h> /* AF_INET */
62 #include <netinet/in.h> /* struct in_addr */
63 #ifdef HAVE_ARPA_INET_H
64 #include <arpa/inet.h>
67 #include <arpa/telnet.h>
68 #include <sys/param.h>
72 #include <mhl/memory.h>
73 #include <mhl/string.h>
75 #include "../src/global.h"
76 #include "../src/tty.h" /* enable/disable interrupt key */
77 #include "../src/wtools.h" /* message() */
78 #include "../src/main.h" /* print_vfs_message */
79 #include "../src/history.h"
81 #include "xdirentry.h"
84 #include "gc.h" /* vfs_stamp_create */
86 #include "../src/setup.h" /* for load_anon_passwd */
88 #ifndef MAXHOSTNAMELEN
89 # define MAXHOSTNAMELEN 64
92 #define UPLOAD_ZERO_LENGTH_FILE
93 #define SUP super->u.ftp
94 #define FH_SOCK fh->u.ftp.sock
97 #define INADDR_NONE 0xffffffff
100 #define RFC_AUTODETECT 0
104 #ifndef HAVE_SOCKLEN_T
105 typedef int socklen_t
;
108 static int ftpfs_errno
;
111 /* Delay to retry a connection */
112 int ftpfs_retry_seconds
= 30;
114 /* Method to use to connect to ftp sites */
115 int ftpfs_use_passive_connections
= 1;
116 int ftpfs_use_passive_connections_over_proxy
= 0;
118 /* Method used to get directory listings:
119 * 1: try 'LIST -la <path>', if it fails
120 * fall back to CWD <path>; LIST
121 * 0: always use CWD <path>; LIST
123 int ftpfs_use_unix_list_options
= 1;
125 /* First "CWD <path>", then "LIST -la ." */
126 int ftpfs_first_cd_then_ls
= 1;
128 /* Use the ~/.netrc */
131 /* Anonymous setup */
132 char *ftpfs_anonymous_passwd
= NULL
;
133 int ftpfs_directory_timeout
= 900;
136 char *ftpfs_proxy_host
= NULL
;
138 /* wether we have to use proxy by default? */
139 int ftpfs_always_use_proxy
;
141 #ifdef FIXME_LATER_ALIGATOR
142 static struct linklist
*connections_list
;
145 /* ftpfs_command wait_flag: */
147 #define WAIT_REPLY 0x01
148 #define WANT_STRING 0x02
149 static char reply_str
[80];
151 static struct vfs_class vfs_ftpfs_ops
;
153 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
154 Translate a Unix path, i.e. MC's internal path representation (e.g.
155 /somedir/somefile) to a path valid for the remote server. Every path
156 transfered to the remote server has to be mangled by this function
157 right prior to sending it.
158 Currently only Amiga ftp servers are handled in a special manner.
160 When the remote server is an amiga:
161 a) strip leading slash if necesarry
162 b) replace first occurance of ":/" with ":"
163 c) strip trailing "/."
166 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
167 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
);
168 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
169 __attribute__ ((format (__printf__
, 4, 5)));
170 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
171 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
);
172 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
175 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
177 if (!SUP
.remote_is_amiga
)
178 return g_strdup (remote_path
);
182 if (MEDATA
->logfile
) {
183 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
184 fflush (MEDATA
->logfile
);
187 /* strip leading slash(es) */
188 while (*remote_path
== '/')
192 * Don't change "/" into "", e.g. "CWD " would be
195 if (*remote_path
== '\0')
196 return g_strdup (".");
198 ret
= g_strdup (remote_path
);
200 /* replace first occurance of ":/" with ":" */
201 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
202 strcpy (p
+ 1, p
+ 2);
204 /* strip trailing "/." */
205 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
211 /* Extract the hostname and username from the path */
214 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
215 * ftp://sunsite.unc.edu/pub/linux
216 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
217 * ftp://tsx-11.mit.edu:8192/
218 * ftp://joe@foo.edu:11321/private
219 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
224 #define FTP_COMMAND_PORT 21
227 ftpfs_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
231 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
235 /* Look up user and password in netrc */
237 ftpfs_netrc_lookup (*host
, user
, pass
);
239 *user
= g_strdup ("anonymous");
242 /* Look up password in netrc for known user */
243 if (use_netrc
&& *user
&& pass
&& !*pass
) {
246 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
248 /* If user is different, remove password */
249 if (new_user
&& strcmp (*user
, new_user
)) {
260 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
262 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
268 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
274 switch (sscanf(answer
, "%d", &code
)){
277 g_strlcpy (string_buf
, answer
, string_len
);
281 if (answer
[3] == '-') {
283 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
289 if ((sscanf (answer
, "%d", &i
) > 0) &&
290 (code
== i
) && (answer
[3] == ' '))
295 g_strlcpy (string_buf
, answer
, string_len
);
302 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
304 int sock
= ftpfs_open_socket (me
, super
);
306 char *cwdir
= SUP
.cwdir
;
310 if (ftpfs_login_server (me
, super
, SUP
.password
)){
313 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
315 return sock
== COMPLETE
;
323 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
328 static int retry
= 0;
329 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
332 cmdstr
= g_strdup_vprintf (fmt
, ap
);
335 cmdlen
= strlen (cmdstr
);
336 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
337 strcpy (cmdstr
+ cmdlen
, "\r\n");
340 if (MEDATA
->logfile
) {
341 if (strncmp (cmdstr
, "PASS ", 5) == 0) {
342 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
344 fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
346 fflush (MEDATA
->logfile
);
350 enable_interrupt_key ();
351 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
356 if (errno
== EPIPE
) { /* Remote server has closed connection */
359 status
= ftpfs_reconnect (me
, super
);
361 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
369 disable_interrupt_key ();
374 disable_interrupt_key ();
378 status
= ftpfs_get_reply (me
, SUP
.sock
,
379 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
380 sizeof (reply_str
) - 1);
381 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
385 status
= ftpfs_reconnect (me
, super
);
387 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
400 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
403 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
404 ftpfs_command(me
, super
, NONE
, "QUIT");
410 g_free (SUP
.password
);
413 /* some defines only used by ftpfs_changetype */
414 /* These two are valid values for the second parameter */
416 #define TYPE_BINARY 1
418 /* This one is only used to initialize bucket->isbinary, don't use it as
419 second parameter to ftpfs_changetype. */
420 #define TYPE_UNKNOWN -1
423 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
425 if (binary
!= SUP
.isbinary
) {
426 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
428 SUP
.isbinary
= binary
;
433 /* This routine logs the user in */
435 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
436 const char *netrcpass
)
440 char *name
; /* login user name */
442 char reply_string
[BUF_MEDIUM
];
444 SUP
.isbinary
= TYPE_UNKNOWN
;
446 if (SUP
.password
) /* explicit password */
447 op
= g_strdup (SUP
.password
);
448 else if (netrcpass
) /* password from netrc */
449 op
= g_strdup (netrcpass
);
450 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp")) {
451 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
452 ftpfs_init_passwd ();
453 op
= g_strdup (ftpfs_anonymous_passwd
);
455 } else { /* ask user */
458 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
, " ",
460 op
= vfs_get_password (p
);
464 SUP
.password
= g_strdup (op
);
467 if (!anon
|| MEDATA
->logfile
)
470 pass
= g_strconcat ("-", op
, (char *) NULL
);
474 /* Proxy server accepts: username@host-we-want-to-connect */
477 g_strconcat (SUP
.user
, "@",
478 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
,
481 name
= g_strdup (SUP
.user
);
484 (me
, SUP
.sock
, reply_string
,
485 sizeof (reply_string
) - 1) == COMPLETE
) {
486 g_strup (reply_string
);
487 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
488 if (MEDATA
->logfile
) {
489 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n",
490 SUP
.remote_is_amiga
);
491 fflush (MEDATA
->logfile
);
494 print_vfs_message (_("ftpfs: sending login name"));
496 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
)) {
498 print_vfs_message (_("ftpfs: sending user password"));
499 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
500 if (code
== CONTINUE
) {
503 p
= g_strdup_printf (_
504 ("FTP: Account required for user %s"),
506 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
510 print_vfs_message (_("ftpfs: sending user account"));
512 ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
515 if (code
!= COMPLETE
)
520 print_vfs_message (_("ftpfs: logged in"));
521 wipe_password (pass
);
526 SUP
.failed_on_login
= 1;
528 wipe_password (SUP
.password
);
534 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
537 wipe_password (pass
);
542 static struct no_proxy_entry
{
548 ftpfs_load_no_proxy_list (void)
550 /* FixMe: shouldn't be hardcoded!!! */
551 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
552 struct no_proxy_entry
*np
, *current
= 0;
556 static char *mc_file
;
561 mc_file
= mhl_str_dir_plus_file (mc_home
, "mc.no_proxy");
562 if (exist_file (mc_file
) &&
563 (npf
= fopen (mc_file
, "r"))) {
564 while (fgets (s
, sizeof (s
), npf
)) {
565 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
566 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
576 np
= g_new (struct no_proxy_entry
, 1);
577 np
->domain
= g_strdup (s
);
591 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
593 ftpfs_check_proxy (const char *host
)
595 struct no_proxy_entry
*npe
;
597 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
598 return 0; /* sanity check */
603 if (!ftpfs_always_use_proxy
)
606 if (!strchr (host
, '.'))
609 ftpfs_load_no_proxy_list ();
610 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
611 char *domain
= npe
->domain
;
613 if (domain
[0] == '.') {
614 int ld
= strlen (domain
);
615 int lh
= strlen (host
);
617 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
625 if (!g_strcasecmp (host
, domain
))
633 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
638 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
645 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
647 struct sockaddr_in server_address
;
656 /* Use a proxy host? */
659 if (!host
|| !*host
){
660 print_vfs_message (_("ftpfs: Invalid host name."));
661 ftpfs_errno
= EINVAL
;
665 /* Hosts to connect to that start with a ! should use proxy */
667 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
671 enable_interrupt_key(); /* clear the interrupt flag */
673 /* Get host address */
674 memset ((char *) &server_address
, 0, sizeof (server_address
));
675 server_address
.sin_family
= AF_INET
;
676 server_address
.sin_addr
.s_addr
= inet_addr (host
);
677 if (server_address
.sin_addr
.s_addr
== INADDR_NONE
) {
678 hp
= gethostbyname (host
);
680 disable_interrupt_key();
681 print_vfs_message (_("ftpfs: Invalid host address."));
682 ftpfs_errno
= EINVAL
;
687 server_address
.sin_family
= hp
->h_addrtype
;
689 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
690 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
, 4);
693 server_address
.sin_port
= htons (port
);
696 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
697 disable_interrupt_key();
704 print_vfs_message (_("ftpfs: making connection to %s"), host
);
708 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
709 sizeof (server_address
)) < 0){
711 if (errno
== EINTR
&& got_interrupt ())
712 print_vfs_message (_("ftpfs: connection interrupted by user"));
714 print_vfs_message (_("ftpfs: connection to server failed: %s"),
715 unix_error_string(errno
));
716 disable_interrupt_key();
720 disable_interrupt_key();
725 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
727 int retry_seconds
, count_down
;
729 /* We do not want to use the passive if we are using proxies */
731 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
735 SUP
.failed_on_login
= 0;
737 SUP
.sock
= ftpfs_open_socket (me
, super
);
741 if (ftpfs_login_server (me
, super
, NULL
)) {
742 /* Logged in, no need to retry the connection */
745 if (SUP
.failed_on_login
){
746 /* Close only the socket descriptor */
751 if (ftpfs_retry_seconds
){
752 retry_seconds
= ftpfs_retry_seconds
;
753 enable_interrupt_key ();
754 for (count_down
= retry_seconds
; count_down
; count_down
--){
755 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
757 if (got_interrupt ()){
758 /* ftpfs_errno = E; */
759 disable_interrupt_key ();
763 disable_interrupt_key ();
766 } while (retry_seconds
);
768 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
770 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
775 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
776 const char *archive_name
, char *op
)
778 char *host
, *user
, *password
;
783 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
790 if (ftpfs_check_proxy (host
))
791 SUP
.proxy
= ftpfs_proxy_host
;
792 SUP
.password
= password
;
793 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
794 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
795 SUP
.isbinary
= TYPE_UNKNOWN
;
796 SUP
.remote_is_amiga
= 0;
797 super
->name
= g_strdup ("/");
799 vfs_s_new_inode (me
, super
,
800 vfs_s_default_stat (me
, S_IFDIR
| 0755));
802 return ftpfs_open_archive_int (me
, super
);
806 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
807 const char *archive_name
, char *op
, void *cookie
)
816 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
818 port
= ((strcmp (host
, SUP
.host
) == 0)
819 && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
827 /* The returned directory should always contain a trailing slash */
829 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
831 char buf
[BUF_8K
], *bufp
, *bufq
;
833 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
834 ftpfs_get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
836 for (bufq
= buf
; *bufq
; bufq
++)
843 if (*(bufq
- 1) != '/') {
848 return g_strdup (bufp
);
850 /* If the remote server is an Amiga a leading slash
851 might be missing. MC needs it because it is used
852 as separator between hostname and path internally. */
853 return g_strconcat( "/", bufp
, NULL
);
867 /* Setup Passive ftp connection, we use it for source routed connections */
869 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
, int my_socket
, struct sockaddr_in
*sa
)
871 int xa
, xb
, xc
, xd
, xe
, xf
;
875 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
878 /* Parse remote parameters */
879 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++)
883 if (!isdigit ((unsigned char) *c
))
885 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
887 n
[0] = (unsigned char) xa
;
888 n
[1] = (unsigned char) xb
;
889 n
[2] = (unsigned char) xc
;
890 n
[3] = (unsigned char) xd
;
891 n
[4] = (unsigned char) xe
;
892 n
[5] = (unsigned char) xf
;
894 memcpy (&(sa
->sin_addr
.s_addr
), (void *)n
, 4);
895 memcpy (&(sa
->sin_port
), (void *)&n
[4], 2);
896 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
902 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
904 struct sockaddr_in data_addr
;
906 socklen_t len
= sizeof(data_addr
);
909 pe
= getprotobyname ("tcp");
913 if (getsockname (SUP
.sock
, (struct sockaddr
*) &data_addr
, &len
) == -1)
915 data_addr
.sin_port
= 0;
917 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
921 if (SUP
.use_passive_connection
) {
922 if (ftpfs_setup_passive (me
, super
, data
, &data_addr
))
925 SUP
.use_passive_connection
= 0;
926 print_vfs_message (_("ftpfs: could not setup passive mode"));
928 /* data or data_addr may be damaged by ftpfs_setup_passive */
933 /* If passive setup fails, fallback to active connections */
934 /* Active FTP connection */
935 if ((bind (data
, (struct sockaddr
*)&data_addr
, len
) == 0) &&
936 (getsockname (data
, (struct sockaddr
*) &data_addr
, &len
) == 0) &&
937 (listen (data
, 1) == 0))
939 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
940 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
942 if (ftpfs_command (me
, super
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
943 a
[2], a
[3], p
[0], p
[1]) == COMPLETE
)
952 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
953 const char *remote
, int isbinary
, int reget
)
955 struct sockaddr_in from
;
957 socklen_t fromlen
= sizeof(from
);
959 if ((s
= ftpfs_initconn (me
, super
)) == -1)
961 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
964 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
969 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
970 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
971 /* WarFtpD can't STORE //filename */
972 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
973 g_free (remote_path
);
975 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
978 enable_interrupt_key();
979 if (SUP
.use_passive_connection
)
982 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
990 disable_interrupt_key();
994 #define ABORT_TIMEOUT 5
996 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
998 struct vfs_s_super
*super
= FH_SUPER
;
999 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1002 int dsock
= FH_SOCK
;
1004 SUP
.ctl_connection_busy
= 0;
1006 print_vfs_message (_("ftpfs: aborting transfer."));
1007 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
1008 print_vfs_message (_("ftpfs: abort error: %s"),
1009 unix_error_string (errno
));
1015 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1016 print_vfs_message (_("ftpfs: abort failed"));
1023 FD_SET (dsock
, &mask
);
1024 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1025 struct timeval start_tim
, tim
;
1026 gettimeofday (&start_tim
, NULL
);
1027 /* flush the remaining data */
1028 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1029 gettimeofday (&tim
, NULL
);
1030 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1031 /* server keeps sending, drop the connection and ftpfs_reconnect */
1033 ftpfs_reconnect (me
, super
);
1040 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1041 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1046 resolve_symlink_without_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1048 struct linklist
*flist
;
1049 struct direntry
*fe
, *fel
;
1050 char tmp
[MC_MAXPATHLEN
];
1053 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1054 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1055 /* flist->data->l_stat is alread initialized with 0 */
1057 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1058 if (fel
->linkname
[0] == '/') {
1059 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1061 strcpy (tmp
, fel
->linkname
);
1063 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1065 strcpy (tmp
, dir
->remote_path
);
1068 strcat (tmp
+ 1, fel
->linkname
);
1070 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1071 canonicalize_pathname (tmp
);
1072 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1074 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1075 /* Symlink points to link which isn't resolved, yet. */
1076 if (fe
->linkname
[0] == '/') {
1077 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1079 strcpy (tmp
, fe
->linkname
);
1081 /* at this point tmp looks always like this
1082 /directory/filename, i.e. no need to check
1083 strrchr's return value */
1084 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1085 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1087 strcat (tmp
, fe
->linkname
);
1091 fel
->l_stat
= g_new (struct stat
, 1);
1092 if ( S_ISLNK (fe
->s
.st_mode
))
1093 *fel
->l_stat
= *fe
->l_stat
;
1095 *fel
->l_stat
= fe
->s
;
1096 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1103 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1107 resolve_symlink_with_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1109 char buffer
[2048] = "", *filename
;
1113 struct linklist
*flist
;
1114 struct direntry
*fe
;
1115 int switch_method
= 0;
1117 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1118 if (strchr (dir
->remote_path
, ' ')) {
1119 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1120 print_vfs_message(_("ftpfs: CWD failed."));
1123 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1126 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa",
1127 dir
->remote_path
, TYPE_ASCII
, 0);
1130 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1134 fp
= fdopen(sock
, "r");
1137 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1140 enable_interrupt_key();
1141 flist
= dir
->file_list
->next
;
1144 if (flist
== dir
->file_list
)
1147 flist
= flist
->next
;
1148 } while (!S_ISLNK(fe
->s
.st_mode
));
1150 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1152 if (MEDATA
->logfile
){
1153 fputs (buffer
, MEDATA
->logfile
);
1154 fflush (MEDATA
->logfile
);
1156 vfs_die("This code should be commented out\n");
1157 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1158 int r
= strcmp(fe
->name
, filename
);
1161 if (S_ISLNK (s
.st_mode
)) {
1162 /* This server doesn't understand LIST -lLa */
1166 fe
->l_stat
= g_new (struct stat
, 1);
1167 if (fe
->l_stat
== NULL
)
1170 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1179 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1180 disable_interrupt_key();
1182 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1186 resolve_symlink(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1188 print_vfs_message(_("Resolving symlink..."));
1190 if (SUP
.strict_rfc959_list_cmd
)
1191 resolve_symlink_without_ls_options(me
, super
, dir
);
1193 resolve_symlink_with_ls_options(me
, super
, dir
);
1198 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1200 struct vfs_s_entry
*ent
;
1201 struct vfs_s_super
*super
= dir
->super
;
1202 int sock
, num_entries
= 0;
1203 char buffer
[BUF_8K
];
1206 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1207 || (strchr (remote_path
, ' ') != NULL
);
1210 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1213 RFC_STRICT
? _("(strict rfc959)") : "",
1214 cd_first
? _("(chdir first)") : "");
1217 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
) {
1218 ftpfs_errno
= ENOENT
;
1219 print_vfs_message (_("ftpfs: CWD failed."));
1224 gettimeofday (&dir
->timestamp
, NULL
);
1225 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1227 if (SUP
.strict
== RFC_STRICT
)
1228 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1230 /* Dirty hack to avoid autoprepending / to . */
1231 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1233 ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1235 /* Trailing "/." is necessary if remote_path is a symlink */
1236 char *path
= mhl_str_dir_plus_file (remote_path
, ".");
1238 ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
,
1246 /* Clear the interrupt flag */
1247 enable_interrupt_key ();
1252 vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1258 me
->verrno
= ECONNRESET
;
1260 disable_interrupt_key ();
1261 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1262 print_vfs_message (_("%s: failure"), me
->name
);
1266 if (MEDATA
->logfile
) {
1267 fputs (buffer
, MEDATA
->logfile
);
1268 fputs ("\n", MEDATA
->logfile
);
1269 fflush (MEDATA
->logfile
);
1272 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1273 i
= ent
->ino
->st
.st_nlink
;
1274 if (!vfs_parse_ls_lga
1275 (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1276 vfs_s_free_entry (me
, ent
);
1279 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1281 vfs_s_insert_entry (me
, dir
, ent
);
1285 me
->verrno
= E_REMOTE
;
1286 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1289 if (num_entries
== 0 && cd_first
== 0) {
1290 /* The LIST command may produce an empty output. In such scenario
1291 it is not clear whether this is caused by `remote_path' being
1292 a non-existent path or for some other reason (listing emtpy
1293 directory without the -a option, non-readable directory, etc.).
1295 Since `dir_load' is a crucial method, when it comes to determine
1296 whether a given path is a _directory_, the code must try its best
1297 to determine the type of `remote_path'. The only reliable way to
1298 achieve this is trough issuing a CWD command. */
1304 if (SUP
.strict
== RFC_AUTODETECT
)
1305 SUP
.strict
= RFC_DARING
;
1307 print_vfs_message (_("%s: done."), me
->name
);
1311 if (SUP
.strict
== RFC_AUTODETECT
) {
1312 /* It's our first attempt to get a directory listing from this
1313 server (UNIX style LIST command) */
1314 SUP
.strict
= RFC_STRICT
;
1315 /* I hate goto, but recursive call needs another 8K on stack */
1316 /* return ftpfs_dir_load (me, dir, remote_path); */
1320 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1321 ERRNOR (EACCES
, -1);
1325 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
,
1328 int h
, sock
, n_read
, n_written
;
1330 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1338 struct vfs_s_super
*super
= FH_SUPER
;
1340 h
= open (localname
, O_RDONLY
);
1344 ftpfs_open_data_connection (me
, super
,
1345 fh
->u
.ftp
.append
? "APPE" : "STOR", name
,
1347 if (sock
< 0 || fstat (h
, &s
) == -1) {
1351 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1354 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1356 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1360 enable_interrupt_key ();
1362 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1) {
1363 if (errno
== EINTR
) {
1364 if (got_interrupt ()) {
1365 ftpfs_errno
= EINTR
;
1370 ftpfs_errno
= errno
;
1377 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
) {
1378 if (n_written
== -1) {
1379 if (errno
== EINTR
&& !got_interrupt ()) {
1382 ftpfs_errno
= errno
;
1386 n_read
-= n_written
;
1388 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1389 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1391 disable_interrupt_key ();
1394 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1398 disable_interrupt_key ();
1401 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1406 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1408 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1412 FH_SOCK
= ftpfs_open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1416 fh
->linear
= LS_LINEAR_OPEN
;
1417 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1418 fh
->u
.ftp
.append
= 0;
1423 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1426 struct vfs_s_super
*super
= FH_SUPER
;
1428 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1429 if ((errno
== EINTR
) && !got_interrupt())
1435 ftpfs_linear_abort(me
, fh
);
1438 SUP
.ctl_connection_busy
= 0;
1441 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1442 ERRNOR (E_REMOTE
, -1);
1449 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1452 ftpfs_linear_abort(me
, fh
);
1455 static int ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1460 case VFS_CTL_IS_NOTREADY
:
1465 vfs_die ("You may not do this");
1466 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1469 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1470 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1480 ftpfs_send_command(struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1483 char *p
, *mpath
= g_strdup(filename
);
1484 struct vfs_s_super
*super
;
1486 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1488 if (!(rpath
= vfs_s_get_path_mangle(me
, mpath
, &super
, 0))) {
1492 p
= ftpfs_translate_path (me
, super
, rpath
);
1493 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1495 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1496 if (flags
& OPT_IGNORE_ERROR
)
1498 if (r
!= COMPLETE
) {
1503 if (flush_directory_cache
)
1504 vfs_s_invalidate(me
, super
);
1509 /* This routine is called as the last step in load_setup */
1511 ftpfs_init_passwd(void)
1513 ftpfs_anonymous_passwd
= load_anon_passwd ();
1514 if (ftpfs_anonymous_passwd
)
1517 /* If there is no anonymous ftp password specified
1518 * then we'll just use anonymous@
1519 * We don't send any other thing because:
1520 * - We want to remain anonymous
1521 * - We want to stop SPAM
1522 * - We don't want to let ftp sites to discriminate by the user,
1525 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1528 static int ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1530 char buf
[BUF_SMALL
];
1532 snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1533 return ftpfs_send_command(me
, path
, buf
, OPT_FLUSH
);
1536 static int ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1539 ftpfs_errno
= EPERM
;
1542 /* Everyone knows it is not possible to chown remotely, so why bother them.
1543 If someone's root, then copy/move will always try to chown it... */
1552 static int ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1554 return ftpfs_send_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1557 /* Return 1 if path is the same directory as the one we are in now */
1559 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1565 if (strcmp (path
, SUP
.cwdir
) == 0)
1571 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1576 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1579 p
= ftpfs_translate_path (me
, super
, remote_path
);
1580 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1583 if (r
!= COMPLETE
) {
1587 SUP
.cwdir
= g_strdup (remote_path
);
1588 SUP
.cwd_deferred
= 0;
1593 static int ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1595 ftpfs_send_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1596 return ftpfs_send_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1599 static int ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1601 (void) mode
; /* FIXME: should be used */
1603 return ftpfs_send_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1606 static int ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1608 return ftpfs_send_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1612 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
,
1617 fh
->u
.ftp
.append
= 0;
1618 /* File will be written only, so no need to retrieve it from ftp server */
1619 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
))) {
1620 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1627 /* ftpfs_linear_start() called, so data will be written
1628 * to local temporary file and stored to ftp server
1629 * by vfs_s_close later
1631 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
) {
1632 if (!fh
->ino
->localname
) {
1633 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1634 fh
->ino
->ent
->name
);
1638 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1642 name
= vfs_s_fullpath (me
, fh
->ino
);
1646 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1647 (flags
& O_APPEND
) ? "APPE" :
1648 "STOR", name
, TYPE_BINARY
, 0);
1653 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1657 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1659 if (fh
->ino
->localname
) {
1660 unlink (fh
->ino
->localname
);
1661 g_free (fh
->ino
->localname
);
1662 fh
->ino
->localname
= NULL
;
1667 if (!fh
->ino
->localname
)
1668 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1670 if (!fh
->ino
->localname
)
1671 vfs_die ("retrieve_file failed to fill in localname");
1675 static int ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1677 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1680 /* File is stored to destination already, so
1681 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1684 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1686 vfs_s_invalidate (me
, FH_SUPER
);
1692 ftpfs_done (struct vfs_class
*me
)
1694 struct no_proxy_entry
*np
;
1699 np
= no_proxy
->next
;
1700 g_free (no_proxy
->domain
);
1704 g_free (ftpfs_anonymous_passwd
);
1705 g_free (ftpfs_proxy_host
);
1709 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
1711 struct vfs_s_super
*super
= MEDATA
->supers
;
1715 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
1718 super
= super
->next
;
1722 static char buffer
[BUF_MEDIUM
];
1724 static const char *netrcp
;
1726 /* This should match the keywords[] array below */
1739 static keyword_t
ftpfs_netrc_next (void)
1743 static const char *const keywords
[] = { "default", "machine",
1744 "login", "password", "passwd", "account", "macdef", NULL
1749 netrcp
= skip_separators (netrcp
);
1750 if (*netrcp
!= '\n')
1757 if (*netrcp
== '"') {
1758 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1759 if (*netrcp
== '\\')
1764 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1765 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1766 if (*netrcp
== '\\')
1776 while (keywords
[i
- 1]) {
1777 if (!strcmp (keywords
[i
- 1], buffer
))
1783 return NETRC_UNKNOWN
;
1786 static int ftpfs_netrc_bad_mode (const char *netrcname
)
1788 static int be_angry
= 1;
1791 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1793 message (D_ERROR
, MSG_ERROR
,
1794 _("~/.netrc file has incorrect mode.\n"
1795 "Remove password or correct mode."));
1803 /* Scan .netrc until we find matching "machine" or "default"
1804 * domain is used for additional matching
1805 * No search is done after "default" in compliance with "man netrc"
1806 * Return 0 if found, -1 otherwise */
1807 static int ftpfs_find_machine (const char *host
, const char *domain
)
1811 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
) {
1812 if (keyword
== NETRC_DEFAULT
)
1815 if (keyword
== NETRC_MACDEF
) {
1816 /* Scan for an empty line, which concludes "macdef" */
1818 while (*netrcp
&& *netrcp
!= '\n')
1820 if (*netrcp
!= '\n')
1823 } while (*netrcp
&& *netrcp
!= '\n');
1827 if (keyword
!= NETRC_MACHINE
)
1830 /* Take machine name */
1831 if (ftpfs_netrc_next () == NETRC_NONE
)
1834 if (g_strcasecmp (host
, buffer
)) {
1835 /* Try adding our domain to short names in .netrc */
1836 const char *host_domain
= strchr (host
, '.');
1840 /* Compare domain part */
1841 if (g_strcasecmp (host_domain
, domain
))
1844 /* Compare local part */
1845 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1856 /* Extract login and password from .netrc for the host.
1858 * Returns 0 for success, -1 for error */
1859 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
1862 char *tmp_pass
= NULL
;
1863 char hostname
[MAXHOSTNAMELEN
];
1866 static struct rupcache
{
1867 struct rupcache
*next
;
1871 } *rup_cache
= NULL
, *rupp
;
1873 /* Initialize *login and *pass */
1880 /* Look up in the cache first */
1881 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
1882 if (!strcmp (host
, rupp
->host
)) {
1884 *login
= g_strdup (rupp
->login
);
1885 if (pass
&& rupp
->pass
)
1886 *pass
= g_strdup (rupp
->pass
);
1891 /* Load current .netrc */
1892 netrcname
= mhl_str_dir_plus_file (home_dir
, ".netrc");
1893 netrcp
= netrc
= load_file (netrcname
);
1894 if (netrc
== NULL
) {
1899 /* Find our own domain name */
1900 if (gethostname (hostname
, sizeof (hostname
)) < 0)
1902 if (!(domain
= strchr (hostname
, '.')))
1905 /* Scan for "default" and matching "machine" keywords */
1906 ftpfs_find_machine (host
, domain
);
1908 /* Scan for keywords following "default" and "machine" */
1911 keyword
= ftpfs_netrc_next ();
1915 if (ftpfs_netrc_next () == NETRC_NONE
) {
1920 /* We have another name already - should not happen */
1926 /* We have login name now */
1927 *login
= g_strdup (buffer
);
1930 case NETRC_PASSWORD
:
1932 if (ftpfs_netrc_next () == NETRC_NONE
) {
1937 /* Ignore unsafe passwords */
1938 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
1939 && ftpfs_netrc_bad_mode (netrcname
)) {
1944 /* Remember password. pass may be NULL, so use tmp_pass */
1945 if (tmp_pass
== NULL
)
1946 tmp_pass
= g_strdup (buffer
);
1950 /* "account" is followed by a token which we ignore */
1951 if (ftpfs_netrc_next () == NETRC_NONE
) {
1956 /* Ignore account, but warn user anyways */
1957 ftpfs_netrc_bad_mode (netrcname
);
1961 /* Unexpected keyword or end of file */
1973 rupp
= g_new (struct rupcache
, 1);
1974 rupp
->host
= g_strdup (host
);
1975 rupp
->login
= rupp
->pass
= 0;
1977 if (*login
!= NULL
) {
1978 rupp
->login
= g_strdup (*login
);
1980 if (tmp_pass
!= NULL
)
1981 rupp
->pass
= g_strdup (tmp_pass
);
1982 rupp
->next
= rup_cache
;
1994 static struct vfs_s_subclass ftpfs_subclass
;
1996 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
1997 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
1998 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
1999 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2000 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2001 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2002 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2003 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2004 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2005 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2006 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2008 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2009 vfs_ftpfs_ops
.name
= "ftpfs";
2010 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2011 vfs_ftpfs_ops
.prefix
= "ftp:";
2012 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2013 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2014 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2015 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2016 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2017 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2018 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2019 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2020 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2021 vfs_register_class (&vfs_ftpfs_ops
);