1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Ching Hui
6 1995, 1996, 1997 Miguel de Icaza
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public License
12 as published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
26 - make it more robust - all the connects etc. should handle EADDRINUSE and
27 ERETRY (have I spelled these names correctly?)
28 - make the user able to flush a connection - all the caches will get empty
29 etc., (tarfs as well), we should give there a user selectable timeout
30 and assign a key sequence.
31 - use hash table instead of linklist to cache ftpfs directory.
36 * NOTE: Usage of tildes is deprecated, consider:
37 * cd /#ftp:pavel@hobit
39 * And now: what do I want to do? Do I want to go to /home/pavel or to
40 * /#ftp:hobit/home/pavel? I think first has better sense...
43 int f = !strcmp( remote_path, "/~" );
44 if (f || !strncmp( remote_path, "/~/", 3 )) {
46 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
55 /* Namespace pollution: horrible */
58 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
59 #include <netdb.h> /* struct hostent */
60 #include <sys/socket.h> /* AF_INET */
61 #include <netinet/in.h> /* struct in_addr */
62 #ifdef HAVE_ARPA_INET_H
63 #include <arpa/inet.h>
66 #include <arpa/telnet.h>
67 #include <sys/param.h>
73 #include "xdirentry.h"
76 #include "gc.h" /* vfs_stamp_create */
78 #include "../src/setup.h" /* for load_anon_passwd */
80 #ifndef MAXHOSTNAMELEN
81 # define MAXHOSTNAMELEN 64
84 #define UPLOAD_ZERO_LENGTH_FILE
85 #define SUP super->u.ftp
86 #define FH_SOCK fh->u.ftp.sock
89 #define INADDR_NONE 0xffffffff
92 #define RFC_AUTODETECT 0
96 #ifndef HAVE_C_TYPE_SOCKLEN_T
97 typedef int socklen_t
;
100 static int ftpfs_errno
;
103 /* Delay to retry a connection */
104 int ftpfs_retry_seconds
= 30;
106 /* Method to use to connect to ftp sites */
107 int ftpfs_use_passive_connections
= 1;
109 /* Method used to get directory listings:
110 * 1: try 'LIST -la <path>', if it fails
111 * fall back to CWD <path>; LIST
112 * 0: always use CWD <path>; LIST
114 int ftpfs_use_unix_list_options
= 1;
116 /* First "CWD <path>", then "LIST -la ." */
117 int ftpfs_first_cd_then_ls
;
119 /* Use the ~/.netrc */
122 /* Anonymous setup */
123 char *ftpfs_anonymous_passwd
= NULL
;
124 int ftpfs_directory_timeout
= 900;
127 char *ftpfs_proxy_host
= NULL
;
129 /* wether we have to use proxy by default? */
130 int ftpfs_always_use_proxy
;
132 #ifdef FIXME_LATER_ALIGATOR
133 static struct linklist
*connections_list
;
136 /* ftpfs_command wait_flag: */
138 #define WAIT_REPLY 0x01
139 #define WANT_STRING 0x02
140 static char reply_str
[80];
142 static struct vfs_class vfs_ftpfs_ops
;
144 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
145 Translate a Unix path, i.e. MC's internal path representation (e.g.
146 /somedir/somefile) to a path valid for the remote server. Every path
147 transfered to the remote server has to be mangled by this function
148 right prior to sending it.
149 Currently only Amiga ftp servers are handled in a special manner.
151 When the remote server is an amiga:
152 a) strip leading slash if necesarry
153 b) replace first occurance of ":/" with ":"
154 c) strip trailing "/."
157 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
158 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
);
159 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
160 __attribute__ ((format (printf
, 4, 5)));
161 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
162 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
);
163 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
166 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
168 if (!SUP
.remote_is_amiga
)
169 return g_strdup (remote_path
);
173 if (MEDATA
->logfile
) {
174 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
175 fflush (MEDATA
->logfile
);
178 /* strip leading slash(es) */
179 while (*remote_path
== '/')
183 * Don't change "/" into "", e.g. "CWD " would be
186 if (*remote_path
== '\0')
187 return g_strdup (".");
189 ret
= g_strdup (remote_path
);
191 /* replace first occurance of ":/" with ":" */
192 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
193 strcpy (p
+ 1, p
+ 2);
195 /* strip trailing "/." */
196 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
202 /* Extract the hostname and username from the path */
205 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
206 * ftp://sunsite.unc.edu/pub/linux
207 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
208 * ftp://tsx-11.mit.edu:8192/
209 * ftp://joe@foo.edu:11321/private
210 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
215 #define FTP_COMMAND_PORT 21
218 ftpfs_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
222 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
226 /* Look up user and password in netrc */
228 ftpfs_netrc_lookup (*host
, user
, pass
);
230 *user
= g_strdup ("anonymous");
233 /* Look up password in netrc for known user */
234 if (use_netrc
&& *user
&& pass
&& !*pass
) {
237 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
239 /* If user is different, remove password */
240 if (new_user
&& strcmp (*user
, new_user
)) {
251 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
253 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
259 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
265 switch (sscanf(answer
, "%d", &code
)){
268 g_strlcpy (string_buf
, answer
, string_len
);
272 if (answer
[3] == '-') {
274 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
280 if ((sscanf (answer
, "%d", &i
) > 0) &&
281 (code
== i
) && (answer
[3] == ' '))
286 g_strlcpy (string_buf
, answer
, string_len
);
293 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
295 int sock
= ftpfs_open_socket (me
, super
);
297 char *cwdir
= SUP
.cwdir
;
301 if (ftpfs_login_server (me
, super
, SUP
.password
)){
304 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
306 return sock
== COMPLETE
;
314 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
321 cmdstr
= g_strdup_vprintf (fmt
, ap
);
324 cmdlen
= strlen (cmdstr
);
325 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
326 strcpy (cmdstr
+ cmdlen
, "\r\n");
329 if (MEDATA
->logfile
) {
330 if (strncmp (cmdstr
, "PASS ", 5) == 0) {
331 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
333 fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
335 fflush (MEDATA
->logfile
);
339 enable_interrupt_key ();
340 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
345 if (errno
== EPIPE
) { /* Remote server has closed connection */
346 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
349 status
= ftpfs_reconnect (me
, super
);
351 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
359 disable_interrupt_key ();
364 disable_interrupt_key ();
367 return ftpfs_get_reply (me
, SUP
.sock
,
368 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
369 sizeof (reply_str
) - 1);
374 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
377 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
378 ftpfs_command(me
, super
, NONE
, "QUIT");
384 g_free (SUP
.password
);
387 /* some defines only used by ftpfs_changetype */
388 /* These two are valid values for the second parameter */
390 #define TYPE_BINARY 1
392 /* This one is only used to initialize bucket->isbinary, don't use it as
393 second parameter to ftpfs_changetype. */
394 #define TYPE_UNKNOWN -1
397 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
399 if (binary
!= SUP
.isbinary
) {
400 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
402 SUP
.isbinary
= binary
;
407 /* This routine logs the user in */
409 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
410 const char *netrcpass
)
414 char *name
; /* login user name */
416 char reply_string
[BUF_MEDIUM
];
418 SUP
.isbinary
= TYPE_UNKNOWN
;
420 if (SUP
.password
) /* explicit password */
421 op
= g_strdup (SUP
.password
);
422 else if (netrcpass
) /* password from netrc */
423 op
= g_strdup (netrcpass
);
424 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp")) {
425 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
426 ftpfs_init_passwd ();
427 op
= g_strdup (ftpfs_anonymous_passwd
);
429 } else { /* ask user */
432 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
, " ",
434 op
= vfs_get_password (p
);
438 SUP
.password
= g_strdup (op
);
441 if (!anon
|| MEDATA
->logfile
)
444 pass
= g_strconcat ("-", op
, (char *) NULL
);
448 /* Proxy server accepts: username@host-we-want-to-connect */
451 g_strconcat (SUP
.user
, "@",
452 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
,
455 name
= g_strdup (SUP
.user
);
458 (me
, SUP
.sock
, reply_string
,
459 sizeof (reply_string
) - 1) == COMPLETE
) {
460 g_strup (reply_string
);
461 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
462 if (MEDATA
->logfile
) {
463 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n",
464 SUP
.remote_is_amiga
);
465 fflush (MEDATA
->logfile
);
468 print_vfs_message (_("ftpfs: sending login name"));
470 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
)) {
472 print_vfs_message (_("ftpfs: sending user password"));
473 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
474 if (code
== CONTINUE
) {
477 p
= g_strdup_printf (_
478 ("FTP: Account required for user %s"),
480 op
= input_dialog (p
, _("Account:"), "");
484 print_vfs_message (_("ftpfs: sending user account"));
486 ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
489 if (code
!= COMPLETE
)
494 print_vfs_message (_("ftpfs: logged in"));
495 wipe_password (pass
);
500 SUP
.failed_on_login
= 1;
502 wipe_password (SUP
.password
);
508 message (1, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
511 wipe_password (pass
);
516 static struct no_proxy_entry
{
522 ftpfs_load_no_proxy_list (void)
524 /* FixMe: shouldn't be hardcoded!!! */
525 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
526 struct no_proxy_entry
*np
, *current
= 0;
530 static char *mc_file
;
535 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
536 if (exist_file (mc_file
) &&
537 (npf
= fopen (mc_file
, "r"))) {
538 while (fgets (s
, sizeof (s
), npf
)) {
539 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
540 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
550 np
= g_new (struct no_proxy_entry
, 1);
551 np
->domain
= g_strdup (s
);
565 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
567 ftpfs_check_proxy (const char *host
)
569 struct no_proxy_entry
*npe
;
571 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
572 return 0; /* sanity check */
577 if (!ftpfs_always_use_proxy
)
580 if (!strchr (host
, '.'))
583 ftpfs_load_no_proxy_list ();
584 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
585 char *domain
= npe
->domain
;
587 if (domain
[0] == '.') {
588 int ld
= strlen (domain
);
589 int lh
= strlen (host
);
591 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
599 if (!g_strcasecmp (host
, domain
))
607 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
612 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
619 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
621 struct sockaddr_in server_address
;
628 /* Use a proxy host? */
631 if (!host
|| !*host
){
632 print_vfs_message (_("ftpfs: Invalid host name."));
633 ftpfs_errno
= EINVAL
;
637 /* Hosts to connect to that start with a ! should use proxy */
639 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
643 /* Get host address */
644 memset ((char *) &server_address
, 0, sizeof (server_address
));
645 server_address
.sin_family
= AF_INET
;
646 server_address
.sin_addr
.s_addr
= inet_addr (host
);
647 if (server_address
.sin_addr
.s_addr
== INADDR_NONE
) {
648 hp
= gethostbyname (host
);
650 print_vfs_message (_("ftpfs: Invalid host address."));
651 ftpfs_errno
= EINVAL
;
656 server_address
.sin_family
= hp
->h_addrtype
;
658 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
659 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
, 4);
662 server_address
.sin_port
= htons (port
);
665 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
672 print_vfs_message (_("ftpfs: making connection to %s"), host
);
676 enable_interrupt_key (); /* clear the interrupt flag */
678 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
679 sizeof (server_address
)) < 0){
681 if (errno
== EINTR
&& got_interrupt ())
682 print_vfs_message (_("ftpfs: connection interrupted by user"));
684 print_vfs_message (_("ftpfs: connection to server failed: %s"),
685 unix_error_string(errno
));
686 disable_interrupt_key();
690 disable_interrupt_key();
695 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
697 int retry_seconds
, count_down
;
699 /* We do not want to use the passive if we are using proxies */
701 SUP
.use_passive_connection
= 0;
705 SUP
.failed_on_login
= 0;
707 SUP
.sock
= ftpfs_open_socket (me
, super
);
711 if (ftpfs_login_server (me
, super
, NULL
)) {
712 /* Logged in, no need to retry the connection */
715 if (SUP
.failed_on_login
){
716 /* Close only the socket descriptor */
721 if (ftpfs_retry_seconds
){
722 retry_seconds
= ftpfs_retry_seconds
;
723 enable_interrupt_key ();
724 for (count_down
= retry_seconds
; count_down
; count_down
--){
725 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
727 if (got_interrupt ()){
728 /* ftpfs_errno = E; */
729 disable_interrupt_key ();
733 disable_interrupt_key ();
736 } while (retry_seconds
);
738 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
740 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
745 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
746 const char *archive_name
, char *op
)
748 char *host
, *user
, *password
;
751 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
758 if (ftpfs_check_proxy (host
))
759 SUP
.proxy
= ftpfs_proxy_host
;
760 SUP
.password
= password
;
761 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
762 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
763 SUP
.isbinary
= TYPE_UNKNOWN
;
764 SUP
.remote_is_amiga
= 0;
765 super
->name
= g_strdup ("/");
767 vfs_s_new_inode (me
, super
,
768 vfs_s_default_stat (me
, S_IFDIR
| 0755));
770 return ftpfs_open_archive_int (me
, super
);
774 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
775 const char *archive_name
, char *op
, void *cookie
)
780 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
782 port
= ((strcmp (host
, SUP
.host
) == 0)
783 && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
792 ftpfs_dir_uptodate(struct vfs_class
*me
, struct vfs_s_inode
*ino
)
800 gettimeofday(&tim
, NULL
);
801 if (tim
.tv_sec
< ino
->timestamp
.tv_sec
)
806 /* The returned directory should always contain a trailing slash */
808 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
810 char buf
[BUF_8K
], *bufp
, *bufq
;
812 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
813 ftpfs_get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
815 for (bufq
= buf
; *bufq
; bufq
++)
822 if (*(bufq
- 1) != '/') {
827 return g_strdup (bufp
);
829 /* If the remote server is an Amiga a leading slash
830 might be missing. MC needs it because it is used
831 as separator between hostname and path internally. */
832 return g_strconcat( "/", bufp
, 0);
846 /* Setup Passive ftp connection, we use it for source routed connections */
848 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
, int my_socket
, struct sockaddr_in
*sa
)
850 int xa
, xb
, xc
, xd
, xe
, xf
;
854 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
857 /* Parse remote parameters */
858 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++)
862 if (!isdigit ((unsigned char) *c
))
864 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
866 n
[0] = (unsigned char) xa
;
867 n
[1] = (unsigned char) xb
;
868 n
[2] = (unsigned char) xc
;
869 n
[3] = (unsigned char) xd
;
870 n
[4] = (unsigned char) xe
;
871 n
[5] = (unsigned char) xf
;
873 memcpy (&(sa
->sin_addr
.s_addr
), (void *)n
, 4);
874 memcpy (&(sa
->sin_port
), (void *)&n
[4], 2);
875 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
881 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
883 struct sockaddr_in data_addr
;
885 socklen_t len
= sizeof(data_addr
);
888 pe
= getprotobyname ("tcp");
892 if (getsockname (SUP
.sock
, (struct sockaddr
*) &data_addr
, &len
) == -1)
894 data_addr
.sin_port
= 0;
896 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
900 if (SUP
.use_passive_connection
) {
901 if (ftpfs_setup_passive (me
, super
, data
, &data_addr
))
904 SUP
.use_passive_connection
= 0;
905 print_vfs_message (_("ftpfs: could not setup passive mode"));
907 /* data or data_addr may be damaged by ftpfs_setup_passive */
912 /* If passive setup fails, fallback to active connections */
913 /* Active FTP connection */
914 if ((bind (data
, (struct sockaddr
*)&data_addr
, len
) == 0) &&
915 (getsockname (data
, (struct sockaddr
*) &data_addr
, &len
) == 0) &&
916 (listen (data
, 1) == 0))
918 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
919 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
921 if (ftpfs_command (me
, super
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
922 a
[2], a
[3], p
[0], p
[1]) == COMPLETE
)
931 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
932 const char *remote
, int isbinary
, int reget
)
934 struct sockaddr_in from
;
936 socklen_t fromlen
= sizeof(from
);
938 if ((s
= ftpfs_initconn (me
, super
)) == -1)
940 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
943 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
948 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
949 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
950 /* WarFtpD can't STORE //filename */
951 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
952 g_free (remote_path
);
954 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
957 enable_interrupt_key();
958 if (SUP
.use_passive_connection
)
961 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
969 disable_interrupt_key();
973 #define ABORT_TIMEOUT 5
975 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
977 struct vfs_s_super
*super
= FH_SUPER
;
978 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
983 SUP
.ctl_connection_busy
= 0;
985 print_vfs_message (_("ftpfs: aborting transfer."));
986 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
987 print_vfs_message (_("ftpfs: abort error: %s"),
988 unix_error_string (errno
));
994 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
995 print_vfs_message (_("ftpfs: abort failed"));
1002 FD_SET (dsock
, &mask
);
1003 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1004 struct timeval start_tim
, tim
;
1005 gettimeofday (&start_tim
, NULL
);
1006 /* flush the remaining data */
1007 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1008 gettimeofday (&tim
, NULL
);
1009 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1010 /* server keeps sending, drop the connection and ftpfs_reconnect */
1012 ftpfs_reconnect (me
, super
);
1019 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1020 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1025 resolve_symlink_without_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1027 struct linklist
*flist
;
1028 struct direntry
*fe
, *fel
;
1029 char tmp
[MC_MAXPATHLEN
];
1032 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1033 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1034 /* flist->data->l_stat is alread initialized with 0 */
1036 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1037 if (fel
->linkname
[0] == '/') {
1038 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1040 strcpy (tmp
, fel
->linkname
);
1042 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1044 strcpy (tmp
, dir
->remote_path
);
1047 strcat (tmp
+ 1, fel
->linkname
);
1049 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1050 canonicalize_pathname (tmp
);
1051 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1053 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1054 /* Symlink points to link which isn't resolved, yet. */
1055 if (fe
->linkname
[0] == '/') {
1056 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1058 strcpy (tmp
, fe
->linkname
);
1060 /* at this point tmp looks always like this
1061 /directory/filename, i.e. no need to check
1062 strrchr's return value */
1063 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1064 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1066 strcat (tmp
, fe
->linkname
);
1070 fel
->l_stat
= g_new (struct stat
, 1);
1071 if ( S_ISLNK (fe
->s
.st_mode
))
1072 *fel
->l_stat
= *fe
->l_stat
;
1074 *fel
->l_stat
= fe
->s
;
1075 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1082 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1086 resolve_symlink_with_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1088 char buffer
[2048] = "", *filename
;
1092 struct linklist
*flist
;
1093 struct direntry
*fe
;
1094 int switch_method
= 0;
1096 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1097 if (strchr (dir
->remote_path
, ' ')) {
1098 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1099 print_vfs_message(_("ftpfs: CWD failed."));
1102 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1105 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa",
1106 dir
->remote_path
, TYPE_ASCII
, 0);
1109 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1113 fp
= fdopen(sock
, "r");
1116 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1119 enable_interrupt_key();
1120 flist
= dir
->file_list
->next
;
1123 if (flist
== dir
->file_list
)
1126 flist
= flist
->next
;
1127 } while (!S_ISLNK(fe
->s
.st_mode
));
1129 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1131 if (MEDATA
->logfile
){
1132 fputs (buffer
, MEDATA
->logfile
);
1133 fflush (MEDATA
->logfile
);
1135 vfs_die("This code should be commented out\n");
1136 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1137 int r
= strcmp(fe
->name
, filename
);
1140 if (S_ISLNK (s
.st_mode
)) {
1141 /* This server doesn't understand LIST -lLa */
1145 fe
->l_stat
= g_new (struct stat
, 1);
1146 if (fe
->l_stat
== NULL
)
1149 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1158 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1159 disable_interrupt_key();
1161 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1165 resolve_symlink(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1167 print_vfs_message(_("Resolving symlink..."));
1169 if (SUP
.strict_rfc959_list_cmd
)
1170 resolve_symlink_without_ls_options(me
, super
, dir
);
1172 resolve_symlink_with_ls_options(me
, super
, dir
);
1177 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1179 struct vfs_s_entry
*ent
;
1180 struct vfs_s_super
*super
= dir
->super
;
1181 int sock
, num_entries
= 0;
1182 char buffer
[BUF_8K
];
1185 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1186 || (strchr (remote_path
, ' ') != NULL
);
1189 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1192 RFC_STRICT
? _("(strict rfc959)") : "",
1193 cd_first
? _("(chdir first)") : "");
1198 p
= ftpfs_translate_path (me
, super
, remote_path
);
1200 if (ftpfs_chdir_internal (me
, super
, p
) != COMPLETE
) {
1202 ftpfs_errno
= ENOENT
;
1203 print_vfs_message (_("ftpfs: CWD failed."));
1209 gettimeofday (&dir
->timestamp
, NULL
);
1210 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1212 if (SUP
.strict
== RFC_STRICT
)
1213 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1215 /* Dirty hack to avoid autoprepending / to . */
1216 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1218 ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1220 /* Trailing "/." is necessary if remote_path is a symlink */
1221 char *path
= concat_dir_and_file (remote_path
, ".");
1223 ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
,
1231 /* Clear the interrupt flag */
1232 enable_interrupt_key ();
1237 vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1243 me
->verrno
= ECONNRESET
;
1245 disable_interrupt_key ();
1246 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1247 print_vfs_message (_("%s: failure"), me
->name
);
1251 if (MEDATA
->logfile
) {
1252 fputs (buffer
, MEDATA
->logfile
);
1253 fputs ("\n", MEDATA
->logfile
);
1254 fflush (MEDATA
->logfile
);
1257 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1258 i
= ent
->ino
->st
.st_nlink
;
1259 if (!vfs_parse_ls_lga
1260 (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1261 vfs_s_free_entry (me
, ent
);
1264 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1266 vfs_s_insert_entry (me
, dir
, ent
);
1270 me
->verrno
= E_REMOTE
;
1271 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
) || !num_entries
)
1274 if (SUP
.strict
== RFC_AUTODETECT
)
1275 SUP
.strict
= RFC_DARING
;
1277 print_vfs_message (_("%s: done."), me
->name
);
1281 if (SUP
.strict
== RFC_AUTODETECT
) {
1282 /* It's our first attempt to get a directory listing from this
1283 server (UNIX style LIST command) */
1284 SUP
.strict
= RFC_STRICT
;
1285 /* I hate goto, but recursive call needs another 8K on stack */
1286 /* return ftpfs_dir_load (me, dir, remote_path); */
1290 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1291 ERRNOR (-1, EACCES
);
1295 ftpfs_file_store(struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
, char *localname
)
1299 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1306 struct vfs_s_super
*super
= FH_SUPER
;
1308 h
= open(localname
, O_RDONLY
);
1312 sock
= ftpfs_open_data_connection(me
, super
, fh
->u
.ftp
.append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1317 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1320 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof(li
));
1322 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1326 enable_interrupt_key();
1328 while ((n
= read(h
, buffer
, sizeof(buffer
))) < 0) {
1329 if (errno
== EINTR
) {
1330 if (got_interrupt()) {
1331 ftpfs_errno
= EINTR
;
1337 ftpfs_errno
= errno
;
1342 while (write(sock
, buffer
, n
) < 0) {
1343 if (errno
== EINTR
) {
1344 if (got_interrupt()) {
1345 ftpfs_errno
= EINTR
;
1351 ftpfs_errno
= errno
;
1355 print_vfs_message(_("ftpfs: storing file %lu (%lu)"),
1356 (unsigned long) total
, (unsigned long) s
.st_size
);
1358 disable_interrupt_key();
1361 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1365 disable_interrupt_key();
1368 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1373 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1375 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1379 FH_SOCK
= ftpfs_open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1383 fh
->linear
= LS_LINEAR_OPEN
;
1384 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1385 fh
->u
.ftp
.append
= 0;
1390 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1393 struct vfs_s_super
*super
= FH_SUPER
;
1395 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1396 if ((errno
== EINTR
) && !got_interrupt())
1402 ftpfs_linear_abort(me
, fh
);
1405 SUP
.ctl_connection_busy
= 0;
1408 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1409 ERRNOR (E_REMOTE
, -1);
1416 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1419 ftpfs_linear_abort(me
, fh
);
1422 static int ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1425 case VFS_CTL_IS_NOTREADY
:
1430 vfs_die ("You may not do this");
1431 if (FH
->linear
== LS_LINEAR_CLOSED
)
1434 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1435 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1445 ftpfs_send_command(struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1447 char *rpath
, *p
, *mpath
= g_strdup(filename
);
1448 struct vfs_s_super
*super
;
1450 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1452 if (!(rpath
= vfs_s_get_path_mangle(me
, mpath
, &super
, 0))) {
1456 p
= ftpfs_translate_path (me
, super
, rpath
);
1457 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1459 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1460 if (flags
& OPT_IGNORE_ERROR
)
1462 if (r
!= COMPLETE
) {
1467 if (flush_directory_cache
)
1468 vfs_s_invalidate(me
, super
);
1473 /* This routine is called as the last step in load_setup */
1475 ftpfs_init_passwd(void)
1477 ftpfs_anonymous_passwd
= load_anon_passwd ();
1478 if (ftpfs_anonymous_passwd
)
1481 /* If there is no anonymous ftp password specified
1482 * then we'll just use anonymous@
1483 * We don't send any other thing because:
1484 * - We want to remain anonymous
1485 * - We want to stop SPAM
1486 * - We don't want to let ftp sites to discriminate by the user,
1489 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1492 static int ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1494 char buf
[BUF_SMALL
];
1496 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1497 return ftpfs_send_command(me
, path
, buf
, OPT_FLUSH
);
1500 static int ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1503 ftpfs_errno
= EPERM
;
1506 /* Everyone knows it is not possible to chown remotely, so why bother them.
1507 If someone's root, then copy/move will always try to chown it... */
1512 static int ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1514 return ftpfs_send_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1517 /* Return 1 if path is the same directory as the one we are in now */
1519 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1523 if (strcmp (path
, SUP
.cwdir
) == 0)
1529 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1534 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1537 p
= ftpfs_translate_path (me
, super
, remote_path
);
1538 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1541 if (r
!= COMPLETE
) {
1545 SUP
.cwdir
= g_strdup (remote_path
);
1546 SUP
.cwd_deferred
= 0;
1551 static int ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1553 ftpfs_send_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1554 return ftpfs_send_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1557 static int ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1559 return ftpfs_send_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1562 static int ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1564 return ftpfs_send_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1568 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
,
1571 fh
->u
.ftp
.append
= 0;
1572 /* File will be written only, so no need to retrieve it from ftp server */
1573 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
))) {
1574 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1581 /* ftpfs_linear_start() called, so data will be written
1582 * to local temporary file and stored to ftp server
1583 * by vfs_s_close later
1585 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
) {
1586 if (!fh
->ino
->localname
) {
1587 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1588 fh
->ino
->ent
->name
);
1592 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1596 name
= vfs_s_fullpath (me
, fh
->ino
);
1600 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1601 (flags
& O_APPEND
) ? "APPE" :
1602 "STOR", name
, TYPE_BINARY
, 0);
1607 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1611 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1613 if (fh
->ino
->localname
) {
1614 unlink (fh
->ino
->localname
);
1615 g_free (fh
->ino
->localname
);
1616 fh
->ino
->localname
= NULL
;
1621 if (!fh
->ino
->localname
)
1622 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1624 if (!fh
->ino
->localname
)
1625 vfs_die ("retrieve_file failed to fill in localname");
1629 static int ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1631 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1634 /* File is stored to destination already, so
1635 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1638 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1640 vfs_s_invalidate (me
, FH_SUPER
);
1646 ftpfs_done (struct vfs_class
*me
)
1648 struct no_proxy_entry
*np
;
1651 np
= no_proxy
->next
;
1652 g_free (no_proxy
->domain
);
1656 g_free (ftpfs_anonymous_passwd
);
1657 g_free (ftpfs_proxy_host
);
1661 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
1663 struct vfs_s_super
*super
= MEDATA
->supers
;
1667 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
1670 super
= super
->next
;
1674 static char buffer
[BUF_MEDIUM
];
1676 static const char *netrcp
;
1678 /* This should match the keywords[] array below */
1691 static keyword_t
ftpfs_netrc_next (void)
1695 static const char *const keywords
[] = { "default", "machine",
1696 "login", "password", "passwd", "account", "macdef", NULL
1701 netrcp
= skip_separators (netrcp
);
1702 if (*netrcp
!= '\n')
1709 if (*netrcp
== '"') {
1710 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1711 if (*netrcp
== '\\')
1716 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1717 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1718 if (*netrcp
== '\\')
1728 while (keywords
[i
- 1]) {
1729 if (!strcmp (keywords
[i
- 1], buffer
))
1735 return NETRC_UNKNOWN
;
1738 static int ftpfs_netrc_bad_mode (const char *netrcname
, const char *arg_netrc
)
1740 static int be_angry
= 1;
1743 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1745 message (1, MSG_ERROR
,
1746 _("~/.netrc file has incorrect mode.\n"
1747 "Remove password or correct mode."));
1755 /* Scan .netrc until we find matching "machine" or "default"
1756 * domain is used for additional matching
1757 * No search is done after "default" in compliance with "man netrc"
1758 * Return 0 if found, -1 otherwise */
1759 static int ftpfs_find_machine (const char *host
, const char *domain
)
1763 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
) {
1764 if (keyword
== NETRC_DEFAULT
)
1767 if (keyword
== NETRC_MACDEF
) {
1768 /* Scan for an empty line, which concludes "macdef" */
1770 while (*netrcp
&& *netrcp
!= '\n')
1772 if (*netrcp
!= '\n')
1775 } while (*netrcp
&& *netrcp
!= '\n');
1779 if (keyword
!= NETRC_MACHINE
)
1782 /* Take machine name */
1783 if (ftpfs_netrc_next () == NETRC_NONE
)
1786 if (g_strcasecmp (host
, buffer
)) {
1787 /* Try adding our domain to short names in .netrc */
1788 const char *host_domain
= strchr (host
, '.');
1792 /* Compare domain part */
1793 if (g_strcasecmp (host_domain
, domain
))
1796 /* Compare local part */
1797 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1808 /* Extract login and password from .netrc for the host.
1810 * Returns 0 for success, -1 for error */
1811 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
1814 char *tmp_pass
= NULL
;
1815 char hostname
[MAXHOSTNAMELEN
];
1818 static struct rupcache
{
1819 struct rupcache
*next
;
1823 } *rup_cache
= NULL
, *rupp
;
1825 /* Initialize *login and *pass */
1832 /* Look up in the cache first */
1833 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
1834 if (!strcmp (host
, rupp
->host
)) {
1836 *login
= g_strdup (rupp
->login
);
1837 if (pass
&& rupp
->pass
)
1838 *pass
= g_strdup (rupp
->pass
);
1843 /* Load current .netrc */
1844 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
1845 netrcp
= netrc
= load_file (netrcname
);
1846 if (netrc
== NULL
) {
1851 /* Find our own domain name */
1852 if (gethostname (hostname
, sizeof (hostname
)) < 0)
1854 if (!(domain
= strchr (hostname
, '.')))
1857 /* Scan for "default" and matching "machine" keywords */
1858 ftpfs_find_machine (host
, domain
);
1860 /* Scan for keywords following "default" and "machine" */
1863 keyword
= ftpfs_netrc_next ();
1867 if (ftpfs_netrc_next () == NETRC_NONE
) {
1872 /* We have another name already - should not happen */
1878 /* We have login name now */
1879 *login
= g_strdup (buffer
);
1882 case NETRC_PASSWORD
:
1884 if (ftpfs_netrc_next () == NETRC_NONE
) {
1889 /* Ignore unsafe passwords */
1890 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
1891 && ftpfs_netrc_bad_mode (netrcname
, netrc
)) {
1896 /* Remember password. pass may be NULL, so use tmp_pass */
1897 if (tmp_pass
== NULL
)
1898 tmp_pass
= g_strdup (buffer
);
1902 /* "account" is followed by a token which we ignore */
1903 if (ftpfs_netrc_next () == NETRC_NONE
) {
1908 /* Ignore account, but warn user anyways */
1909 ftpfs_netrc_bad_mode (netrcname
, netrc
);
1913 /* Unexpected keyword or end of file */
1925 rupp
= g_new (struct rupcache
, 1);
1926 rupp
->host
= g_strdup (host
);
1927 rupp
->login
= rupp
->pass
= 0;
1929 if (*login
!= NULL
) {
1930 rupp
->login
= g_strdup (*login
);
1932 if (tmp_pass
!= NULL
)
1933 rupp
->pass
= g_strdup (tmp_pass
);
1934 rupp
->next
= rup_cache
;
1946 static struct vfs_s_subclass ftpfs_subclass
;
1948 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
1949 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
1950 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
1951 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
1952 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
1953 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
1954 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
1955 ftpfs_subclass
.dir_uptodate
= ftpfs_dir_uptodate
;
1956 ftpfs_subclass
.file_store
= ftpfs_file_store
;
1957 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
1958 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
1959 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
1961 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
1962 vfs_ftpfs_ops
.name
= "ftpfs";
1963 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
1964 vfs_ftpfs_ops
.prefix
= "ftp:";
1965 vfs_ftpfs_ops
.done
= &ftpfs_done
;
1966 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
1967 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
1968 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
1969 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
1970 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
1971 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
1972 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
1973 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
1974 vfs_register_class (&vfs_ftpfs_ops
);