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 = concat_dir_and_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 "../src/global.h"
73 #include "../src/tty.h" /* enable/disable interrupt key */
74 #include "../src/wtools.h" /* message() */
75 #include "../src/main.h" /* print_vfs_message */
77 #include "xdirentry.h"
80 #include "gc.h" /* vfs_stamp_create */
82 #include "../src/setup.h" /* for load_anon_passwd */
84 #ifndef MAXHOSTNAMELEN
85 # define MAXHOSTNAMELEN 64
88 #define UPLOAD_ZERO_LENGTH_FILE
89 #define SUP super->u.ftp
90 #define FH_SOCK fh->u.ftp.sock
93 #define INADDR_NONE 0xffffffff
96 #define RFC_AUTODETECT 0
100 #ifndef HAVE_SOCKLEN_T
101 typedef int socklen_t
;
104 static int ftpfs_errno
;
107 /* Delay to retry a connection */
108 int ftpfs_retry_seconds
= 30;
110 /* Method to use to connect to ftp sites */
111 int ftpfs_use_passive_connections
= 1;
112 int ftpfs_use_passive_connections_over_proxy
= 0;
114 /* Method used to get directory listings:
115 * 1: try 'LIST -la <path>', if it fails
116 * fall back to CWD <path>; LIST
117 * 0: always use CWD <path>; LIST
119 int ftpfs_use_unix_list_options
= 1;
121 /* First "CWD <path>", then "LIST -la ." */
122 int ftpfs_first_cd_then_ls
= 1;
124 /* Use the ~/.netrc */
127 /* Anonymous setup */
128 char *ftpfs_anonymous_passwd
= NULL
;
129 int ftpfs_directory_timeout
= 900;
132 char *ftpfs_proxy_host
= NULL
;
134 /* wether we have to use proxy by default? */
135 int ftpfs_always_use_proxy
;
137 #ifdef FIXME_LATER_ALIGATOR
138 static struct linklist
*connections_list
;
141 /* ftpfs_command wait_flag: */
143 #define WAIT_REPLY 0x01
144 #define WANT_STRING 0x02
145 static char reply_str
[80];
147 static struct vfs_class vfs_ftpfs_ops
;
149 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
150 Translate a Unix path, i.e. MC's internal path representation (e.g.
151 /somedir/somefile) to a path valid for the remote server. Every path
152 transfered to the remote server has to be mangled by this function
153 right prior to sending it.
154 Currently only Amiga ftp servers are handled in a special manner.
156 When the remote server is an amiga:
157 a) strip leading slash if necesarry
158 b) replace first occurance of ":/" with ":"
159 c) strip trailing "/."
162 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
163 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
);
164 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
165 __attribute__ ((format (__printf__
, 4, 5)));
166 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
167 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
);
168 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
171 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
173 if (!SUP
.remote_is_amiga
)
174 return g_strdup (remote_path
);
178 if (MEDATA
->logfile
) {
179 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
180 fflush (MEDATA
->logfile
);
183 /* strip leading slash(es) */
184 while (*remote_path
== '/')
188 * Don't change "/" into "", e.g. "CWD " would be
191 if (*remote_path
== '\0')
192 return g_strdup (".");
194 ret
= g_strdup (remote_path
);
196 /* replace first occurance of ":/" with ":" */
197 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
198 strcpy (p
+ 1, p
+ 2);
200 /* strip trailing "/." */
201 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
207 /* Extract the hostname and username from the path */
210 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
211 * ftp://sunsite.unc.edu/pub/linux
212 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
213 * ftp://tsx-11.mit.edu:8192/
214 * ftp://joe@foo.edu:11321/private
215 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
220 #define FTP_COMMAND_PORT 21
223 ftpfs_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
227 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
231 /* Look up user and password in netrc */
233 ftpfs_netrc_lookup (*host
, user
, pass
);
235 *user
= g_strdup ("anonymous");
238 /* Look up password in netrc for known user */
239 if (use_netrc
&& *user
&& pass
&& !*pass
) {
242 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
244 /* If user is different, remove password */
245 if (new_user
&& strcmp (*user
, new_user
)) {
256 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
258 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
264 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
270 switch (sscanf(answer
, "%d", &code
)){
273 g_strlcpy (string_buf
, answer
, string_len
);
277 if (answer
[3] == '-') {
279 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
285 if ((sscanf (answer
, "%d", &i
) > 0) &&
286 (code
== i
) && (answer
[3] == ' '))
291 g_strlcpy (string_buf
, answer
, string_len
);
298 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
300 int sock
= ftpfs_open_socket (me
, super
);
302 char *cwdir
= SUP
.cwdir
;
306 if (ftpfs_login_server (me
, super
, SUP
.password
)){
309 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
311 return sock
== COMPLETE
;
319 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
324 static int retry
= 0;
325 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
328 cmdstr
= g_strdup_vprintf (fmt
, ap
);
331 cmdlen
= strlen (cmdstr
);
332 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
333 strcpy (cmdstr
+ cmdlen
, "\r\n");
336 if (MEDATA
->logfile
) {
337 if (strncmp (cmdstr
, "PASS ", 5) == 0) {
338 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
340 fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
342 fflush (MEDATA
->logfile
);
346 enable_interrupt_key ();
347 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
352 if (errno
== EPIPE
) { /* Remote server has closed connection */
355 status
= ftpfs_reconnect (me
, super
);
357 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
365 disable_interrupt_key ();
370 disable_interrupt_key ();
374 status
= ftpfs_get_reply (me
, SUP
.sock
,
375 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
376 sizeof (reply_str
) - 1);
377 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
381 status
= ftpfs_reconnect (me
, super
);
383 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
396 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
399 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
400 ftpfs_command(me
, super
, NONE
, "QUIT");
406 g_free (SUP
.password
);
409 /* some defines only used by ftpfs_changetype */
410 /* These two are valid values for the second parameter */
412 #define TYPE_BINARY 1
414 /* This one is only used to initialize bucket->isbinary, don't use it as
415 second parameter to ftpfs_changetype. */
416 #define TYPE_UNKNOWN -1
419 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
421 if (binary
!= SUP
.isbinary
) {
422 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
424 SUP
.isbinary
= binary
;
429 /* This routine logs the user in */
431 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
432 const char *netrcpass
)
436 char *name
; /* login user name */
438 char reply_string
[BUF_MEDIUM
];
440 SUP
.isbinary
= TYPE_UNKNOWN
;
442 if (SUP
.password
) /* explicit password */
443 op
= g_strdup (SUP
.password
);
444 else if (netrcpass
) /* password from netrc */
445 op
= g_strdup (netrcpass
);
446 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp")) {
447 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
448 ftpfs_init_passwd ();
449 op
= g_strdup (ftpfs_anonymous_passwd
);
451 } else { /* ask user */
454 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
, " ",
456 op
= vfs_get_password (p
);
460 SUP
.password
= g_strdup (op
);
463 if (!anon
|| MEDATA
->logfile
)
466 pass
= g_strconcat ("-", op
, (char *) NULL
);
470 /* Proxy server accepts: username@host-we-want-to-connect */
473 g_strconcat (SUP
.user
, "@",
474 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
,
477 name
= g_strdup (SUP
.user
);
480 (me
, SUP
.sock
, reply_string
,
481 sizeof (reply_string
) - 1) == COMPLETE
) {
482 g_strup (reply_string
);
483 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
484 if (MEDATA
->logfile
) {
485 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n",
486 SUP
.remote_is_amiga
);
487 fflush (MEDATA
->logfile
);
490 print_vfs_message (_("ftpfs: sending login name"));
492 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
)) {
494 print_vfs_message (_("ftpfs: sending user password"));
495 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
496 if (code
== CONTINUE
) {
499 p
= g_strdup_printf (_
500 ("FTP: Account required for user %s"),
502 op
= input_dialog (p
, _("Account:"), "");
506 print_vfs_message (_("ftpfs: sending user account"));
508 ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
511 if (code
!= COMPLETE
)
516 print_vfs_message (_("ftpfs: logged in"));
517 wipe_password (pass
);
522 SUP
.failed_on_login
= 1;
524 wipe_password (SUP
.password
);
530 message (1, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
533 wipe_password (pass
);
538 static struct no_proxy_entry
{
544 ftpfs_load_no_proxy_list (void)
546 /* FixMe: shouldn't be hardcoded!!! */
547 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
548 struct no_proxy_entry
*np
, *current
= 0;
552 static char *mc_file
;
557 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
558 if (exist_file (mc_file
) &&
559 (npf
= fopen (mc_file
, "r"))) {
560 while (fgets (s
, sizeof (s
), npf
)) {
561 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
562 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
572 np
= g_new (struct no_proxy_entry
, 1);
573 np
->domain
= g_strdup (s
);
587 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
589 ftpfs_check_proxy (const char *host
)
591 struct no_proxy_entry
*npe
;
593 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
594 return 0; /* sanity check */
599 if (!ftpfs_always_use_proxy
)
602 if (!strchr (host
, '.'))
605 ftpfs_load_no_proxy_list ();
606 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
607 char *domain
= npe
->domain
;
609 if (domain
[0] == '.') {
610 int ld
= strlen (domain
);
611 int lh
= strlen (host
);
613 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
621 if (!g_strcasecmp (host
, domain
))
629 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
634 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
641 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
643 struct sockaddr_in server_address
;
652 /* Use a proxy host? */
655 if (!host
|| !*host
){
656 print_vfs_message (_("ftpfs: Invalid host name."));
657 ftpfs_errno
= EINVAL
;
661 /* Hosts to connect to that start with a ! should use proxy */
663 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
667 enable_interrupt_key(); /* clear the interrupt flag */
669 /* Get host address */
670 memset ((char *) &server_address
, 0, sizeof (server_address
));
671 server_address
.sin_family
= AF_INET
;
672 server_address
.sin_addr
.s_addr
= inet_addr (host
);
673 if (server_address
.sin_addr
.s_addr
== INADDR_NONE
) {
674 hp
= gethostbyname (host
);
676 disable_interrupt_key();
677 print_vfs_message (_("ftpfs: Invalid host address."));
678 ftpfs_errno
= EINVAL
;
683 server_address
.sin_family
= hp
->h_addrtype
;
685 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
686 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
, 4);
689 server_address
.sin_port
= htons (port
);
692 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
693 disable_interrupt_key();
700 print_vfs_message (_("ftpfs: making connection to %s"), host
);
704 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
705 sizeof (server_address
)) < 0){
707 if (errno
== EINTR
&& got_interrupt ())
708 print_vfs_message (_("ftpfs: connection interrupted by user"));
710 print_vfs_message (_("ftpfs: connection to server failed: %s"),
711 unix_error_string(errno
));
712 disable_interrupt_key();
716 disable_interrupt_key();
721 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
723 int retry_seconds
, count_down
;
725 /* We do not want to use the passive if we are using proxies */
727 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
731 SUP
.failed_on_login
= 0;
733 SUP
.sock
= ftpfs_open_socket (me
, super
);
737 if (ftpfs_login_server (me
, super
, NULL
)) {
738 /* Logged in, no need to retry the connection */
741 if (SUP
.failed_on_login
){
742 /* Close only the socket descriptor */
747 if (ftpfs_retry_seconds
){
748 retry_seconds
= ftpfs_retry_seconds
;
749 enable_interrupt_key ();
750 for (count_down
= retry_seconds
; count_down
; count_down
--){
751 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
753 if (got_interrupt ()){
754 /* ftpfs_errno = E; */
755 disable_interrupt_key ();
759 disable_interrupt_key ();
762 } while (retry_seconds
);
764 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
766 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
771 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
772 const char *archive_name
, char *op
)
774 char *host
, *user
, *password
;
779 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
786 if (ftpfs_check_proxy (host
))
787 SUP
.proxy
= ftpfs_proxy_host
;
788 SUP
.password
= password
;
789 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
790 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
791 SUP
.isbinary
= TYPE_UNKNOWN
;
792 SUP
.remote_is_amiga
= 0;
793 super
->name
= g_strdup ("/");
795 vfs_s_new_inode (me
, super
,
796 vfs_s_default_stat (me
, S_IFDIR
| 0755));
798 return ftpfs_open_archive_int (me
, super
);
802 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
803 const char *archive_name
, char *op
, void *cookie
)
812 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
814 port
= ((strcmp (host
, SUP
.host
) == 0)
815 && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
823 /* The returned directory should always contain a trailing slash */
825 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
827 char buf
[BUF_8K
], *bufp
, *bufq
;
829 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
830 ftpfs_get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
832 for (bufq
= buf
; *bufq
; bufq
++)
839 if (*(bufq
- 1) != '/') {
844 return g_strdup (bufp
);
846 /* If the remote server is an Amiga a leading slash
847 might be missing. MC needs it because it is used
848 as separator between hostname and path internally. */
849 return g_strconcat( "/", bufp
, NULL
);
863 /* Setup Passive ftp connection, we use it for source routed connections */
865 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
, int my_socket
, struct sockaddr_in
*sa
)
867 int xa
, xb
, xc
, xd
, xe
, xf
;
871 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
874 /* Parse remote parameters */
875 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++)
879 if (!isdigit ((unsigned char) *c
))
881 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
883 n
[0] = (unsigned char) xa
;
884 n
[1] = (unsigned char) xb
;
885 n
[2] = (unsigned char) xc
;
886 n
[3] = (unsigned char) xd
;
887 n
[4] = (unsigned char) xe
;
888 n
[5] = (unsigned char) xf
;
890 memcpy (&(sa
->sin_addr
.s_addr
), (void *)n
, 4);
891 memcpy (&(sa
->sin_port
), (void *)&n
[4], 2);
892 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
898 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
900 struct sockaddr_in data_addr
;
902 socklen_t len
= sizeof(data_addr
);
905 pe
= getprotobyname ("tcp");
909 if (getsockname (SUP
.sock
, (struct sockaddr
*) &data_addr
, &len
) == -1)
911 data_addr
.sin_port
= 0;
913 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
917 if (SUP
.use_passive_connection
) {
918 if (ftpfs_setup_passive (me
, super
, data
, &data_addr
))
921 SUP
.use_passive_connection
= 0;
922 print_vfs_message (_("ftpfs: could not setup passive mode"));
924 /* data or data_addr may be damaged by ftpfs_setup_passive */
929 /* If passive setup fails, fallback to active connections */
930 /* Active FTP connection */
931 if ((bind (data
, (struct sockaddr
*)&data_addr
, len
) == 0) &&
932 (getsockname (data
, (struct sockaddr
*) &data_addr
, &len
) == 0) &&
933 (listen (data
, 1) == 0))
935 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
936 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
938 if (ftpfs_command (me
, super
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
939 a
[2], a
[3], p
[0], p
[1]) == COMPLETE
)
948 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
949 const char *remote
, int isbinary
, int reget
)
951 struct sockaddr_in from
;
953 socklen_t fromlen
= sizeof(from
);
955 if ((s
= ftpfs_initconn (me
, super
)) == -1)
957 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
960 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
965 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
966 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
967 /* WarFtpD can't STORE //filename */
968 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
969 g_free (remote_path
);
971 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
974 enable_interrupt_key();
975 if (SUP
.use_passive_connection
)
978 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
986 disable_interrupt_key();
990 #define ABORT_TIMEOUT 5
992 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
994 struct vfs_s_super
*super
= FH_SUPER
;
995 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1000 SUP
.ctl_connection_busy
= 0;
1002 print_vfs_message (_("ftpfs: aborting transfer."));
1003 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
1004 print_vfs_message (_("ftpfs: abort error: %s"),
1005 unix_error_string (errno
));
1011 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1012 print_vfs_message (_("ftpfs: abort failed"));
1019 FD_SET (dsock
, &mask
);
1020 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1021 struct timeval start_tim
, tim
;
1022 gettimeofday (&start_tim
, NULL
);
1023 /* flush the remaining data */
1024 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1025 gettimeofday (&tim
, NULL
);
1026 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1027 /* server keeps sending, drop the connection and ftpfs_reconnect */
1029 ftpfs_reconnect (me
, super
);
1036 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1037 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1042 resolve_symlink_without_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1044 struct linklist
*flist
;
1045 struct direntry
*fe
, *fel
;
1046 char tmp
[MC_MAXPATHLEN
];
1049 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1050 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1051 /* flist->data->l_stat is alread initialized with 0 */
1053 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1054 if (fel
->linkname
[0] == '/') {
1055 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1057 strcpy (tmp
, fel
->linkname
);
1059 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1061 strcpy (tmp
, dir
->remote_path
);
1064 strcat (tmp
+ 1, fel
->linkname
);
1066 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1067 canonicalize_pathname (tmp
);
1068 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1070 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1071 /* Symlink points to link which isn't resolved, yet. */
1072 if (fe
->linkname
[0] == '/') {
1073 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1075 strcpy (tmp
, fe
->linkname
);
1077 /* at this point tmp looks always like this
1078 /directory/filename, i.e. no need to check
1079 strrchr's return value */
1080 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1081 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1083 strcat (tmp
, fe
->linkname
);
1087 fel
->l_stat
= g_new (struct stat
, 1);
1088 if ( S_ISLNK (fe
->s
.st_mode
))
1089 *fel
->l_stat
= *fe
->l_stat
;
1091 *fel
->l_stat
= fe
->s
;
1092 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1099 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1103 resolve_symlink_with_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1105 char buffer
[2048] = "", *filename
;
1109 struct linklist
*flist
;
1110 struct direntry
*fe
;
1111 int switch_method
= 0;
1113 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1114 if (strchr (dir
->remote_path
, ' ')) {
1115 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1116 print_vfs_message(_("ftpfs: CWD failed."));
1119 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1122 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa",
1123 dir
->remote_path
, TYPE_ASCII
, 0);
1126 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1130 fp
= fdopen(sock
, "r");
1133 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1136 enable_interrupt_key();
1137 flist
= dir
->file_list
->next
;
1140 if (flist
== dir
->file_list
)
1143 flist
= flist
->next
;
1144 } while (!S_ISLNK(fe
->s
.st_mode
));
1146 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1148 if (MEDATA
->logfile
){
1149 fputs (buffer
, MEDATA
->logfile
);
1150 fflush (MEDATA
->logfile
);
1152 vfs_die("This code should be commented out\n");
1153 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1154 int r
= strcmp(fe
->name
, filename
);
1157 if (S_ISLNK (s
.st_mode
)) {
1158 /* This server doesn't understand LIST -lLa */
1162 fe
->l_stat
= g_new (struct stat
, 1);
1163 if (fe
->l_stat
== NULL
)
1166 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1175 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1176 disable_interrupt_key();
1178 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1182 resolve_symlink(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1184 print_vfs_message(_("Resolving symlink..."));
1186 if (SUP
.strict_rfc959_list_cmd
)
1187 resolve_symlink_without_ls_options(me
, super
, dir
);
1189 resolve_symlink_with_ls_options(me
, super
, dir
);
1194 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1196 struct vfs_s_entry
*ent
;
1197 struct vfs_s_super
*super
= dir
->super
;
1198 int sock
, num_entries
= 0;
1199 char buffer
[BUF_8K
];
1202 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1203 || (strchr (remote_path
, ' ') != NULL
);
1206 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1209 RFC_STRICT
? _("(strict rfc959)") : "",
1210 cd_first
? _("(chdir first)") : "");
1213 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
) {
1214 ftpfs_errno
= ENOENT
;
1215 print_vfs_message (_("ftpfs: CWD failed."));
1220 gettimeofday (&dir
->timestamp
, NULL
);
1221 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1223 if (SUP
.strict
== RFC_STRICT
)
1224 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1226 /* Dirty hack to avoid autoprepending / to . */
1227 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1229 ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1231 /* Trailing "/." is necessary if remote_path is a symlink */
1232 char *path
= concat_dir_and_file (remote_path
, ".");
1234 ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
,
1242 /* Clear the interrupt flag */
1243 enable_interrupt_key ();
1248 vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1254 me
->verrno
= ECONNRESET
;
1256 disable_interrupt_key ();
1257 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1258 print_vfs_message (_("%s: failure"), me
->name
);
1262 if (MEDATA
->logfile
) {
1263 fputs (buffer
, MEDATA
->logfile
);
1264 fputs ("\n", MEDATA
->logfile
);
1265 fflush (MEDATA
->logfile
);
1268 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1269 i
= ent
->ino
->st
.st_nlink
;
1270 if (!vfs_parse_ls_lga
1271 (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1272 vfs_s_free_entry (me
, ent
);
1275 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1277 vfs_s_insert_entry (me
, dir
, ent
);
1281 me
->verrno
= E_REMOTE
;
1282 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1285 if (num_entries
== 0 && cd_first
== 0) {
1286 /* The LIST command may produce an empty output. In such scenario
1287 it is not clear whether this is caused by `remote_path' being
1288 a non-existent path or for some other reason (listing emtpy
1289 directory without the -a option, non-readable directory, etc.).
1291 Since `dir_load' is a crucial method, when it comes to determine
1292 whether a given path is a _directory_, the code must try its best
1293 to determine the type of `remote_path'. The only reliable way to
1294 achieve this is trough issuing a CWD command. */
1300 if (SUP
.strict
== RFC_AUTODETECT
)
1301 SUP
.strict
= RFC_DARING
;
1303 print_vfs_message (_("%s: done."), me
->name
);
1307 if (SUP
.strict
== RFC_AUTODETECT
) {
1308 /* It's our first attempt to get a directory listing from this
1309 server (UNIX style LIST command) */
1310 SUP
.strict
= RFC_STRICT
;
1311 /* I hate goto, but recursive call needs another 8K on stack */
1312 /* return ftpfs_dir_load (me, dir, remote_path); */
1316 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1317 ERRNOR (EACCES
, -1);
1321 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
,
1324 int h
, sock
, n_read
, n_written
;
1326 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1334 struct vfs_s_super
*super
= FH_SUPER
;
1336 h
= open (localname
, O_RDONLY
);
1340 ftpfs_open_data_connection (me
, super
,
1341 fh
->u
.ftp
.append
? "APPE" : "STOR", name
,
1343 if (sock
< 0 || fstat (h
, &s
) == -1) {
1347 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1350 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1352 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1356 enable_interrupt_key ();
1358 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1) {
1359 if (errno
== EINTR
) {
1360 if (got_interrupt ()) {
1361 ftpfs_errno
= EINTR
;
1366 ftpfs_errno
= errno
;
1373 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
) {
1374 if (n_written
== -1) {
1375 if (errno
== EINTR
&& !got_interrupt ()) {
1378 ftpfs_errno
= errno
;
1382 n_read
-= n_written
;
1384 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1385 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1387 disable_interrupt_key ();
1390 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1394 disable_interrupt_key ();
1397 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1402 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1404 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1408 FH_SOCK
= ftpfs_open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1412 fh
->linear
= LS_LINEAR_OPEN
;
1413 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1414 fh
->u
.ftp
.append
= 0;
1419 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1422 struct vfs_s_super
*super
= FH_SUPER
;
1424 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1425 if ((errno
== EINTR
) && !got_interrupt())
1431 ftpfs_linear_abort(me
, fh
);
1434 SUP
.ctl_connection_busy
= 0;
1437 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1438 ERRNOR (E_REMOTE
, -1);
1445 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1448 ftpfs_linear_abort(me
, fh
);
1451 static int ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1456 case VFS_CTL_IS_NOTREADY
:
1461 vfs_die ("You may not do this");
1462 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1465 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1466 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1476 ftpfs_send_command(struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1479 char *p
, *mpath
= g_strdup(filename
);
1480 struct vfs_s_super
*super
;
1482 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1484 if (!(rpath
= vfs_s_get_path_mangle(me
, mpath
, &super
, 0))) {
1488 p
= ftpfs_translate_path (me
, super
, rpath
);
1489 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1491 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1492 if (flags
& OPT_IGNORE_ERROR
)
1494 if (r
!= COMPLETE
) {
1499 if (flush_directory_cache
)
1500 vfs_s_invalidate(me
, super
);
1505 /* This routine is called as the last step in load_setup */
1507 ftpfs_init_passwd(void)
1509 ftpfs_anonymous_passwd
= load_anon_passwd ();
1510 if (ftpfs_anonymous_passwd
)
1513 /* If there is no anonymous ftp password specified
1514 * then we'll just use anonymous@
1515 * We don't send any other thing because:
1516 * - We want to remain anonymous
1517 * - We want to stop SPAM
1518 * - We don't want to let ftp sites to discriminate by the user,
1521 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1524 static int ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1526 char buf
[BUF_SMALL
];
1528 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1529 return ftpfs_send_command(me
, path
, buf
, OPT_FLUSH
);
1532 static int ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1535 ftpfs_errno
= EPERM
;
1538 /* Everyone knows it is not possible to chown remotely, so why bother them.
1539 If someone's root, then copy/move will always try to chown it... */
1548 static int ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1550 return ftpfs_send_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1553 /* Return 1 if path is the same directory as the one we are in now */
1555 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1561 if (strcmp (path
, SUP
.cwdir
) == 0)
1567 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1572 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1575 p
= ftpfs_translate_path (me
, super
, remote_path
);
1576 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1579 if (r
!= COMPLETE
) {
1583 SUP
.cwdir
= g_strdup (remote_path
);
1584 SUP
.cwd_deferred
= 0;
1589 static int ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1591 ftpfs_send_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1592 return ftpfs_send_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1595 static int ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1597 (void) mode
; /* FIXME: should be used */
1599 return ftpfs_send_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1602 static int ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1604 return ftpfs_send_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1608 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
,
1613 fh
->u
.ftp
.append
= 0;
1614 /* File will be written only, so no need to retrieve it from ftp server */
1615 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
))) {
1616 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1623 /* ftpfs_linear_start() called, so data will be written
1624 * to local temporary file and stored to ftp server
1625 * by vfs_s_close later
1627 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
) {
1628 if (!fh
->ino
->localname
) {
1629 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1630 fh
->ino
->ent
->name
);
1634 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1638 name
= vfs_s_fullpath (me
, fh
->ino
);
1642 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1643 (flags
& O_APPEND
) ? "APPE" :
1644 "STOR", name
, TYPE_BINARY
, 0);
1649 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1653 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1655 if (fh
->ino
->localname
) {
1656 unlink (fh
->ino
->localname
);
1657 g_free (fh
->ino
->localname
);
1658 fh
->ino
->localname
= NULL
;
1663 if (!fh
->ino
->localname
)
1664 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1666 if (!fh
->ino
->localname
)
1667 vfs_die ("retrieve_file failed to fill in localname");
1671 static int ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1673 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1676 /* File is stored to destination already, so
1677 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1680 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1682 vfs_s_invalidate (me
, FH_SUPER
);
1688 ftpfs_done (struct vfs_class
*me
)
1690 struct no_proxy_entry
*np
;
1695 np
= no_proxy
->next
;
1696 g_free (no_proxy
->domain
);
1700 g_free (ftpfs_anonymous_passwd
);
1701 g_free (ftpfs_proxy_host
);
1705 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
1707 struct vfs_s_super
*super
= MEDATA
->supers
;
1711 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
1714 super
= super
->next
;
1718 static char buffer
[BUF_MEDIUM
];
1720 static const char *netrcp
;
1722 /* This should match the keywords[] array below */
1735 static keyword_t
ftpfs_netrc_next (void)
1739 static const char *const keywords
[] = { "default", "machine",
1740 "login", "password", "passwd", "account", "macdef", NULL
1745 netrcp
= skip_separators (netrcp
);
1746 if (*netrcp
!= '\n')
1753 if (*netrcp
== '"') {
1754 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1755 if (*netrcp
== '\\')
1760 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1761 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1762 if (*netrcp
== '\\')
1772 while (keywords
[i
- 1]) {
1773 if (!strcmp (keywords
[i
- 1], buffer
))
1779 return NETRC_UNKNOWN
;
1782 static int ftpfs_netrc_bad_mode (const char *netrcname
)
1784 static int be_angry
= 1;
1787 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1789 message (1, MSG_ERROR
,
1790 _("~/.netrc file has incorrect mode.\n"
1791 "Remove password or correct mode."));
1799 /* Scan .netrc until we find matching "machine" or "default"
1800 * domain is used for additional matching
1801 * No search is done after "default" in compliance with "man netrc"
1802 * Return 0 if found, -1 otherwise */
1803 static int ftpfs_find_machine (const char *host
, const char *domain
)
1807 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
) {
1808 if (keyword
== NETRC_DEFAULT
)
1811 if (keyword
== NETRC_MACDEF
) {
1812 /* Scan for an empty line, which concludes "macdef" */
1814 while (*netrcp
&& *netrcp
!= '\n')
1816 if (*netrcp
!= '\n')
1819 } while (*netrcp
&& *netrcp
!= '\n');
1823 if (keyword
!= NETRC_MACHINE
)
1826 /* Take machine name */
1827 if (ftpfs_netrc_next () == NETRC_NONE
)
1830 if (g_strcasecmp (host
, buffer
)) {
1831 /* Try adding our domain to short names in .netrc */
1832 const char *host_domain
= strchr (host
, '.');
1836 /* Compare domain part */
1837 if (g_strcasecmp (host_domain
, domain
))
1840 /* Compare local part */
1841 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1852 /* Extract login and password from .netrc for the host.
1854 * Returns 0 for success, -1 for error */
1855 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
1858 char *tmp_pass
= NULL
;
1859 char hostname
[MAXHOSTNAMELEN
];
1862 static struct rupcache
{
1863 struct rupcache
*next
;
1867 } *rup_cache
= NULL
, *rupp
;
1869 /* Initialize *login and *pass */
1876 /* Look up in the cache first */
1877 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
1878 if (!strcmp (host
, rupp
->host
)) {
1880 *login
= g_strdup (rupp
->login
);
1881 if (pass
&& rupp
->pass
)
1882 *pass
= g_strdup (rupp
->pass
);
1887 /* Load current .netrc */
1888 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
1889 netrcp
= netrc
= load_file (netrcname
);
1890 if (netrc
== NULL
) {
1895 /* Find our own domain name */
1896 if (gethostname (hostname
, sizeof (hostname
)) < 0)
1898 if (!(domain
= strchr (hostname
, '.')))
1901 /* Scan for "default" and matching "machine" keywords */
1902 ftpfs_find_machine (host
, domain
);
1904 /* Scan for keywords following "default" and "machine" */
1907 keyword
= ftpfs_netrc_next ();
1911 if (ftpfs_netrc_next () == NETRC_NONE
) {
1916 /* We have another name already - should not happen */
1922 /* We have login name now */
1923 *login
= g_strdup (buffer
);
1926 case NETRC_PASSWORD
:
1928 if (ftpfs_netrc_next () == NETRC_NONE
) {
1933 /* Ignore unsafe passwords */
1934 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
1935 && ftpfs_netrc_bad_mode (netrcname
)) {
1940 /* Remember password. pass may be NULL, so use tmp_pass */
1941 if (tmp_pass
== NULL
)
1942 tmp_pass
= g_strdup (buffer
);
1946 /* "account" is followed by a token which we ignore */
1947 if (ftpfs_netrc_next () == NETRC_NONE
) {
1952 /* Ignore account, but warn user anyways */
1953 ftpfs_netrc_bad_mode (netrcname
);
1957 /* Unexpected keyword or end of file */
1969 rupp
= g_new (struct rupcache
, 1);
1970 rupp
->host
= g_strdup (host
);
1971 rupp
->login
= rupp
->pass
= 0;
1973 if (*login
!= NULL
) {
1974 rupp
->login
= g_strdup (*login
);
1976 if (tmp_pass
!= NULL
)
1977 rupp
->pass
= g_strdup (tmp_pass
);
1978 rupp
->next
= rup_cache
;
1990 static struct vfs_s_subclass ftpfs_subclass
;
1992 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
1993 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
1994 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
1995 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
1996 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
1997 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
1998 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
1999 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2000 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2001 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2002 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2004 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2005 vfs_ftpfs_ops
.name
= "ftpfs";
2006 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2007 vfs_ftpfs_ops
.prefix
= "ftp:";
2008 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2009 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2010 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2011 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2012 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2013 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2014 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2015 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2016 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2017 vfs_register_class (&vfs_ftpfs_ops
);