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 */
76 #include "../src/history.h"
78 #include "xdirentry.h"
81 #include "gc.h" /* vfs_stamp_create */
83 #include "../src/setup.h" /* for load_anon_passwd */
85 #ifndef MAXHOSTNAMELEN
86 # define MAXHOSTNAMELEN 64
89 #define UPLOAD_ZERO_LENGTH_FILE
90 #define SUP super->u.ftp
91 #define FH_SOCK fh->u.ftp.sock
94 #define INADDR_NONE 0xffffffff
97 #define RFC_AUTODETECT 0
101 #ifndef HAVE_SOCKLEN_T
102 typedef int socklen_t
;
105 static int ftpfs_errno
;
108 /* Delay to retry a connection */
109 int ftpfs_retry_seconds
= 30;
111 /* Method to use to connect to ftp sites */
112 int ftpfs_use_passive_connections
= 1;
113 int ftpfs_use_passive_connections_over_proxy
= 0;
115 /* Method used to get directory listings:
116 * 1: try 'LIST -la <path>', if it fails
117 * fall back to CWD <path>; LIST
118 * 0: always use CWD <path>; LIST
120 int ftpfs_use_unix_list_options
= 1;
122 /* First "CWD <path>", then "LIST -la ." */
123 int ftpfs_first_cd_then_ls
= 1;
125 /* Use the ~/.netrc */
128 /* Anonymous setup */
129 char *ftpfs_anonymous_passwd
= NULL
;
130 int ftpfs_directory_timeout
= 900;
133 char *ftpfs_proxy_host
= NULL
;
135 /* wether we have to use proxy by default? */
136 int ftpfs_always_use_proxy
;
138 #ifdef FIXME_LATER_ALIGATOR
139 static struct linklist
*connections_list
;
142 /* ftpfs_command wait_flag: */
144 #define WAIT_REPLY 0x01
145 #define WANT_STRING 0x02
146 static char reply_str
[80];
148 static struct vfs_class vfs_ftpfs_ops
;
150 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
151 Translate a Unix path, i.e. MC's internal path representation (e.g.
152 /somedir/somefile) to a path valid for the remote server. Every path
153 transfered to the remote server has to be mangled by this function
154 right prior to sending it.
155 Currently only Amiga ftp servers are handled in a special manner.
157 When the remote server is an amiga:
158 a) strip leading slash if necesarry
159 b) replace first occurance of ":/" with ":"
160 c) strip trailing "/."
163 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
164 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
);
165 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
166 __attribute__ ((format (__printf__
, 4, 5)));
167 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
168 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
);
169 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
172 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
174 if (!SUP
.remote_is_amiga
)
175 return g_strdup (remote_path
);
179 if (MEDATA
->logfile
) {
180 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
181 fflush (MEDATA
->logfile
);
184 /* strip leading slash(es) */
185 while (*remote_path
== '/')
189 * Don't change "/" into "", e.g. "CWD " would be
192 if (*remote_path
== '\0')
193 return g_strdup (".");
195 ret
= g_strdup (remote_path
);
197 /* replace first occurance of ":/" with ":" */
198 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
199 strcpy (p
+ 1, p
+ 2);
201 /* strip trailing "/." */
202 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
208 /* Extract the hostname and username from the path */
211 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
212 * ftp://sunsite.unc.edu/pub/linux
213 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
214 * ftp://tsx-11.mit.edu:8192/
215 * ftp://joe@foo.edu:11321/private
216 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
221 #define FTP_COMMAND_PORT 21
224 ftpfs_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
228 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
232 /* Look up user and password in netrc */
234 ftpfs_netrc_lookup (*host
, user
, pass
);
236 *user
= g_strdup ("anonymous");
239 /* Look up password in netrc for known user */
240 if (use_netrc
&& *user
&& pass
&& !*pass
) {
243 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
245 /* If user is different, remove password */
246 if (new_user
&& strcmp (*user
, new_user
)) {
257 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
259 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
265 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
271 switch (sscanf(answer
, "%d", &code
)){
274 g_strlcpy (string_buf
, answer
, string_len
);
278 if (answer
[3] == '-') {
280 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
286 if ((sscanf (answer
, "%d", &i
) > 0) &&
287 (code
== i
) && (answer
[3] == ' '))
292 g_strlcpy (string_buf
, answer
, string_len
);
299 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
301 int sock
= ftpfs_open_socket (me
, super
);
303 char *cwdir
= SUP
.cwdir
;
307 if (ftpfs_login_server (me
, super
, SUP
.password
)){
310 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
312 return sock
== COMPLETE
;
320 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
325 static int retry
= 0;
326 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
329 cmdstr
= g_strdup_vprintf (fmt
, ap
);
332 cmdlen
= strlen (cmdstr
);
333 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
334 strcpy (cmdstr
+ cmdlen
, "\r\n");
337 if (MEDATA
->logfile
) {
338 if (strncmp (cmdstr
, "PASS ", 5) == 0) {
339 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
341 fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
343 fflush (MEDATA
->logfile
);
347 enable_interrupt_key ();
348 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
353 if (errno
== EPIPE
) { /* Remote server has closed connection */
356 status
= ftpfs_reconnect (me
, super
);
358 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
366 disable_interrupt_key ();
371 disable_interrupt_key ();
375 status
= ftpfs_get_reply (me
, SUP
.sock
,
376 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
377 sizeof (reply_str
) - 1);
378 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
382 status
= ftpfs_reconnect (me
, super
);
384 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
397 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
400 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
401 ftpfs_command(me
, super
, NONE
, "QUIT");
407 g_free (SUP
.password
);
410 /* some defines only used by ftpfs_changetype */
411 /* These two are valid values for the second parameter */
413 #define TYPE_BINARY 1
415 /* This one is only used to initialize bucket->isbinary, don't use it as
416 second parameter to ftpfs_changetype. */
417 #define TYPE_UNKNOWN -1
420 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
422 if (binary
!= SUP
.isbinary
) {
423 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
425 SUP
.isbinary
= binary
;
430 /* This routine logs the user in */
432 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
433 const char *netrcpass
)
437 char *name
; /* login user name */
439 char reply_string
[BUF_MEDIUM
];
441 SUP
.isbinary
= TYPE_UNKNOWN
;
443 if (SUP
.password
) /* explicit password */
444 op
= g_strdup (SUP
.password
);
445 else if (netrcpass
) /* password from netrc */
446 op
= g_strdup (netrcpass
);
447 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp")) {
448 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
449 ftpfs_init_passwd ();
450 op
= g_strdup (ftpfs_anonymous_passwd
);
452 } else { /* ask user */
455 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
, " ",
457 op
= vfs_get_password (p
);
461 SUP
.password
= g_strdup (op
);
464 if (!anon
|| MEDATA
->logfile
)
467 pass
= g_strconcat ("-", op
, (char *) NULL
);
471 /* Proxy server accepts: username@host-we-want-to-connect */
474 g_strconcat (SUP
.user
, "@",
475 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
,
478 name
= g_strdup (SUP
.user
);
481 (me
, SUP
.sock
, reply_string
,
482 sizeof (reply_string
) - 1) == COMPLETE
) {
483 g_strup (reply_string
);
484 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
485 if (MEDATA
->logfile
) {
486 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n",
487 SUP
.remote_is_amiga
);
488 fflush (MEDATA
->logfile
);
491 print_vfs_message (_("ftpfs: sending login name"));
493 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
)) {
495 print_vfs_message (_("ftpfs: sending user password"));
496 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
497 if (code
== CONTINUE
) {
500 p
= g_strdup_printf (_
501 ("FTP: Account required for user %s"),
503 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
507 print_vfs_message (_("ftpfs: sending user account"));
509 ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
512 if (code
!= COMPLETE
)
517 print_vfs_message (_("ftpfs: logged in"));
518 wipe_password (pass
);
523 SUP
.failed_on_login
= 1;
525 wipe_password (SUP
.password
);
531 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
534 wipe_password (pass
);
539 static struct no_proxy_entry
{
545 ftpfs_load_no_proxy_list (void)
547 /* FixMe: shouldn't be hardcoded!!! */
548 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
549 struct no_proxy_entry
*np
, *current
= 0;
553 static char *mc_file
;
558 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
559 if (exist_file (mc_file
) &&
560 (npf
= fopen (mc_file
, "r"))) {
561 while (fgets (s
, sizeof (s
), npf
)) {
562 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
563 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
573 np
= g_new (struct no_proxy_entry
, 1);
574 np
->domain
= g_strdup (s
);
588 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
590 ftpfs_check_proxy (const char *host
)
592 struct no_proxy_entry
*npe
;
594 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
595 return 0; /* sanity check */
600 if (!ftpfs_always_use_proxy
)
603 if (!strchr (host
, '.'))
606 ftpfs_load_no_proxy_list ();
607 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
608 char *domain
= npe
->domain
;
610 if (domain
[0] == '.') {
611 int ld
= strlen (domain
);
612 int lh
= strlen (host
);
614 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
622 if (!g_strcasecmp (host
, domain
))
630 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
635 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
642 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
644 struct sockaddr_in server_address
;
653 /* Use a proxy host? */
656 if (!host
|| !*host
){
657 print_vfs_message (_("ftpfs: Invalid host name."));
658 ftpfs_errno
= EINVAL
;
662 /* Hosts to connect to that start with a ! should use proxy */
664 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
668 enable_interrupt_key(); /* clear the interrupt flag */
670 /* Get host address */
671 memset ((char *) &server_address
, 0, sizeof (server_address
));
672 server_address
.sin_family
= AF_INET
;
673 server_address
.sin_addr
.s_addr
= inet_addr (host
);
674 if (server_address
.sin_addr
.s_addr
== INADDR_NONE
) {
675 hp
= gethostbyname (host
);
677 disable_interrupt_key();
678 print_vfs_message (_("ftpfs: Invalid host address."));
679 ftpfs_errno
= EINVAL
;
684 server_address
.sin_family
= hp
->h_addrtype
;
686 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
687 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
, 4);
690 server_address
.sin_port
= htons (port
);
693 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
694 disable_interrupt_key();
701 print_vfs_message (_("ftpfs: making connection to %s"), host
);
705 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
706 sizeof (server_address
)) < 0){
708 if (errno
== EINTR
&& got_interrupt ())
709 print_vfs_message (_("ftpfs: connection interrupted by user"));
711 print_vfs_message (_("ftpfs: connection to server failed: %s"),
712 unix_error_string(errno
));
713 disable_interrupt_key();
717 disable_interrupt_key();
722 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
724 int retry_seconds
, count_down
;
726 /* We do not want to use the passive if we are using proxies */
728 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
732 SUP
.failed_on_login
= 0;
734 SUP
.sock
= ftpfs_open_socket (me
, super
);
738 if (ftpfs_login_server (me
, super
, NULL
)) {
739 /* Logged in, no need to retry the connection */
742 if (SUP
.failed_on_login
){
743 /* Close only the socket descriptor */
748 if (ftpfs_retry_seconds
){
749 retry_seconds
= ftpfs_retry_seconds
;
750 enable_interrupt_key ();
751 for (count_down
= retry_seconds
; count_down
; count_down
--){
752 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
754 if (got_interrupt ()){
755 /* ftpfs_errno = E; */
756 disable_interrupt_key ();
760 disable_interrupt_key ();
763 } while (retry_seconds
);
765 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
767 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
772 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
773 const char *archive_name
, char *op
)
775 char *host
, *user
, *password
;
780 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
787 if (ftpfs_check_proxy (host
))
788 SUP
.proxy
= ftpfs_proxy_host
;
789 SUP
.password
= password
;
790 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
791 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
792 SUP
.isbinary
= TYPE_UNKNOWN
;
793 SUP
.remote_is_amiga
= 0;
794 super
->name
= g_strdup ("/");
796 vfs_s_new_inode (me
, super
,
797 vfs_s_default_stat (me
, S_IFDIR
| 0755));
799 return ftpfs_open_archive_int (me
, super
);
803 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
804 const char *archive_name
, char *op
, void *cookie
)
813 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
815 port
= ((strcmp (host
, SUP
.host
) == 0)
816 && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
824 /* The returned directory should always contain a trailing slash */
826 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
828 char buf
[BUF_8K
], *bufp
, *bufq
;
830 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
831 ftpfs_get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
833 for (bufq
= buf
; *bufq
; bufq
++)
840 if (*(bufq
- 1) != '/') {
845 return g_strdup (bufp
);
847 /* If the remote server is an Amiga a leading slash
848 might be missing. MC needs it because it is used
849 as separator between hostname and path internally. */
850 return g_strconcat( "/", bufp
, NULL
);
864 /* Setup Passive ftp connection, we use it for source routed connections */
866 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
, int my_socket
, struct sockaddr_in
*sa
)
868 int xa
, xb
, xc
, xd
, xe
, xf
;
872 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
875 /* Parse remote parameters */
876 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++)
880 if (!isdigit ((unsigned char) *c
))
882 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
884 n
[0] = (unsigned char) xa
;
885 n
[1] = (unsigned char) xb
;
886 n
[2] = (unsigned char) xc
;
887 n
[3] = (unsigned char) xd
;
888 n
[4] = (unsigned char) xe
;
889 n
[5] = (unsigned char) xf
;
891 memcpy (&(sa
->sin_addr
.s_addr
), (void *)n
, 4);
892 memcpy (&(sa
->sin_port
), (void *)&n
[4], 2);
893 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
899 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
901 struct sockaddr_in data_addr
;
903 socklen_t len
= sizeof(data_addr
);
906 pe
= getprotobyname ("tcp");
910 if (getsockname (SUP
.sock
, (struct sockaddr
*) &data_addr
, &len
) == -1)
912 data_addr
.sin_port
= 0;
914 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
918 if (SUP
.use_passive_connection
) {
919 if (ftpfs_setup_passive (me
, super
, data
, &data_addr
))
922 SUP
.use_passive_connection
= 0;
923 print_vfs_message (_("ftpfs: could not setup passive mode"));
925 /* data or data_addr may be damaged by ftpfs_setup_passive */
930 /* If passive setup fails, fallback to active connections */
931 /* Active FTP connection */
932 if ((bind (data
, (struct sockaddr
*)&data_addr
, len
) == 0) &&
933 (getsockname (data
, (struct sockaddr
*) &data_addr
, &len
) == 0) &&
934 (listen (data
, 1) == 0))
936 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
937 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
939 if (ftpfs_command (me
, super
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
940 a
[2], a
[3], p
[0], p
[1]) == COMPLETE
)
949 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
950 const char *remote
, int isbinary
, int reget
)
952 struct sockaddr_in from
;
954 socklen_t fromlen
= sizeof(from
);
956 if ((s
= ftpfs_initconn (me
, super
)) == -1)
958 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
961 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
966 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
967 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
968 /* WarFtpD can't STORE //filename */
969 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
970 g_free (remote_path
);
972 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
975 enable_interrupt_key();
976 if (SUP
.use_passive_connection
)
979 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
987 disable_interrupt_key();
991 #define ABORT_TIMEOUT 5
993 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
995 struct vfs_s_super
*super
= FH_SUPER
;
996 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1001 SUP
.ctl_connection_busy
= 0;
1003 print_vfs_message (_("ftpfs: aborting transfer."));
1004 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
1005 print_vfs_message (_("ftpfs: abort error: %s"),
1006 unix_error_string (errno
));
1012 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1013 print_vfs_message (_("ftpfs: abort failed"));
1020 FD_SET (dsock
, &mask
);
1021 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1022 struct timeval start_tim
, tim
;
1023 gettimeofday (&start_tim
, NULL
);
1024 /* flush the remaining data */
1025 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1026 gettimeofday (&tim
, NULL
);
1027 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1028 /* server keeps sending, drop the connection and ftpfs_reconnect */
1030 ftpfs_reconnect (me
, super
);
1037 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1038 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1043 resolve_symlink_without_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1045 struct linklist
*flist
;
1046 struct direntry
*fe
, *fel
;
1047 char tmp
[MC_MAXPATHLEN
];
1050 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1051 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1052 /* flist->data->l_stat is alread initialized with 0 */
1054 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1055 if (fel
->linkname
[0] == '/') {
1056 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1058 strcpy (tmp
, fel
->linkname
);
1060 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1062 strcpy (tmp
, dir
->remote_path
);
1065 strcat (tmp
+ 1, fel
->linkname
);
1067 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1068 canonicalize_pathname (tmp
);
1069 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1071 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1072 /* Symlink points to link which isn't resolved, yet. */
1073 if (fe
->linkname
[0] == '/') {
1074 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1076 strcpy (tmp
, fe
->linkname
);
1078 /* at this point tmp looks always like this
1079 /directory/filename, i.e. no need to check
1080 strrchr's return value */
1081 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1082 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1084 strcat (tmp
, fe
->linkname
);
1088 fel
->l_stat
= g_new (struct stat
, 1);
1089 if ( S_ISLNK (fe
->s
.st_mode
))
1090 *fel
->l_stat
= *fe
->l_stat
;
1092 *fel
->l_stat
= fe
->s
;
1093 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1100 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1104 resolve_symlink_with_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1106 char buffer
[2048] = "", *filename
;
1110 struct linklist
*flist
;
1111 struct direntry
*fe
;
1112 int switch_method
= 0;
1114 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1115 if (strchr (dir
->remote_path
, ' ')) {
1116 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1117 print_vfs_message(_("ftpfs: CWD failed."));
1120 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1123 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa",
1124 dir
->remote_path
, TYPE_ASCII
, 0);
1127 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1131 fp
= fdopen(sock
, "r");
1134 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1137 enable_interrupt_key();
1138 flist
= dir
->file_list
->next
;
1141 if (flist
== dir
->file_list
)
1144 flist
= flist
->next
;
1145 } while (!S_ISLNK(fe
->s
.st_mode
));
1147 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1149 if (MEDATA
->logfile
){
1150 fputs (buffer
, MEDATA
->logfile
);
1151 fflush (MEDATA
->logfile
);
1153 vfs_die("This code should be commented out\n");
1154 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1155 int r
= strcmp(fe
->name
, filename
);
1158 if (S_ISLNK (s
.st_mode
)) {
1159 /* This server doesn't understand LIST -lLa */
1163 fe
->l_stat
= g_new (struct stat
, 1);
1164 if (fe
->l_stat
== NULL
)
1167 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1176 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1177 disable_interrupt_key();
1179 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1183 resolve_symlink(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1185 print_vfs_message(_("Resolving symlink..."));
1187 if (SUP
.strict_rfc959_list_cmd
)
1188 resolve_symlink_without_ls_options(me
, super
, dir
);
1190 resolve_symlink_with_ls_options(me
, super
, dir
);
1195 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1197 struct vfs_s_entry
*ent
;
1198 struct vfs_s_super
*super
= dir
->super
;
1199 int sock
, num_entries
= 0;
1200 char buffer
[BUF_8K
];
1203 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1204 || (strchr (remote_path
, ' ') != NULL
);
1207 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1210 RFC_STRICT
? _("(strict rfc959)") : "",
1211 cd_first
? _("(chdir first)") : "");
1214 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
) {
1215 ftpfs_errno
= ENOENT
;
1216 print_vfs_message (_("ftpfs: CWD failed."));
1221 gettimeofday (&dir
->timestamp
, NULL
);
1222 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1224 if (SUP
.strict
== RFC_STRICT
)
1225 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1227 /* Dirty hack to avoid autoprepending / to . */
1228 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1230 ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1232 /* Trailing "/." is necessary if remote_path is a symlink */
1233 char *path
= concat_dir_and_file (remote_path
, ".");
1235 ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
,
1243 /* Clear the interrupt flag */
1244 enable_interrupt_key ();
1249 vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1255 me
->verrno
= ECONNRESET
;
1257 disable_interrupt_key ();
1258 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1259 print_vfs_message (_("%s: failure"), me
->name
);
1263 if (MEDATA
->logfile
) {
1264 fputs (buffer
, MEDATA
->logfile
);
1265 fputs ("\n", MEDATA
->logfile
);
1266 fflush (MEDATA
->logfile
);
1269 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1270 i
= ent
->ino
->st
.st_nlink
;
1271 if (!vfs_parse_ls_lga
1272 (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1273 vfs_s_free_entry (me
, ent
);
1276 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1278 vfs_s_insert_entry (me
, dir
, ent
);
1282 me
->verrno
= E_REMOTE
;
1283 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1286 if (num_entries
== 0 && cd_first
== 0) {
1287 /* The LIST command may produce an empty output. In such scenario
1288 it is not clear whether this is caused by `remote_path' being
1289 a non-existent path or for some other reason (listing emtpy
1290 directory without the -a option, non-readable directory, etc.).
1292 Since `dir_load' is a crucial method, when it comes to determine
1293 whether a given path is a _directory_, the code must try its best
1294 to determine the type of `remote_path'. The only reliable way to
1295 achieve this is trough issuing a CWD command. */
1301 if (SUP
.strict
== RFC_AUTODETECT
)
1302 SUP
.strict
= RFC_DARING
;
1304 print_vfs_message (_("%s: done."), me
->name
);
1308 if (SUP
.strict
== RFC_AUTODETECT
) {
1309 /* It's our first attempt to get a directory listing from this
1310 server (UNIX style LIST command) */
1311 SUP
.strict
= RFC_STRICT
;
1312 /* I hate goto, but recursive call needs another 8K on stack */
1313 /* return ftpfs_dir_load (me, dir, remote_path); */
1317 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1318 ERRNOR (EACCES
, -1);
1322 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
,
1325 int h
, sock
, n_read
, n_written
;
1327 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1335 struct vfs_s_super
*super
= FH_SUPER
;
1337 h
= open (localname
, O_RDONLY
);
1341 ftpfs_open_data_connection (me
, super
,
1342 fh
->u
.ftp
.append
? "APPE" : "STOR", name
,
1344 if (sock
< 0 || fstat (h
, &s
) == -1) {
1348 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1351 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1353 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1357 enable_interrupt_key ();
1359 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1) {
1360 if (errno
== EINTR
) {
1361 if (got_interrupt ()) {
1362 ftpfs_errno
= EINTR
;
1367 ftpfs_errno
= errno
;
1374 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
) {
1375 if (n_written
== -1) {
1376 if (errno
== EINTR
&& !got_interrupt ()) {
1379 ftpfs_errno
= errno
;
1383 n_read
-= n_written
;
1385 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1386 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1388 disable_interrupt_key ();
1391 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1395 disable_interrupt_key ();
1398 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1403 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1405 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1409 FH_SOCK
= ftpfs_open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1413 fh
->linear
= LS_LINEAR_OPEN
;
1414 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1415 fh
->u
.ftp
.append
= 0;
1420 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1423 struct vfs_s_super
*super
= FH_SUPER
;
1425 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1426 if ((errno
== EINTR
) && !got_interrupt())
1432 ftpfs_linear_abort(me
, fh
);
1435 SUP
.ctl_connection_busy
= 0;
1438 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1439 ERRNOR (E_REMOTE
, -1);
1446 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1449 ftpfs_linear_abort(me
, fh
);
1452 static int ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1457 case VFS_CTL_IS_NOTREADY
:
1462 vfs_die ("You may not do this");
1463 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1466 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1467 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1477 ftpfs_send_command(struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1480 char *p
, *mpath
= g_strdup(filename
);
1481 struct vfs_s_super
*super
;
1483 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1485 if (!(rpath
= vfs_s_get_path_mangle(me
, mpath
, &super
, 0))) {
1489 p
= ftpfs_translate_path (me
, super
, rpath
);
1490 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1492 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1493 if (flags
& OPT_IGNORE_ERROR
)
1495 if (r
!= COMPLETE
) {
1500 if (flush_directory_cache
)
1501 vfs_s_invalidate(me
, super
);
1506 /* This routine is called as the last step in load_setup */
1508 ftpfs_init_passwd(void)
1510 ftpfs_anonymous_passwd
= load_anon_passwd ();
1511 if (ftpfs_anonymous_passwd
)
1514 /* If there is no anonymous ftp password specified
1515 * then we'll just use anonymous@
1516 * We don't send any other thing because:
1517 * - We want to remain anonymous
1518 * - We want to stop SPAM
1519 * - We don't want to let ftp sites to discriminate by the user,
1522 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1525 static int ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1527 char buf
[BUF_SMALL
];
1529 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1530 return ftpfs_send_command(me
, path
, buf
, OPT_FLUSH
);
1533 static int ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1536 ftpfs_errno
= EPERM
;
1539 /* Everyone knows it is not possible to chown remotely, so why bother them.
1540 If someone's root, then copy/move will always try to chown it... */
1549 static int ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1551 return ftpfs_send_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1554 /* Return 1 if path is the same directory as the one we are in now */
1556 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1562 if (strcmp (path
, SUP
.cwdir
) == 0)
1568 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1573 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1576 p
= ftpfs_translate_path (me
, super
, remote_path
);
1577 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1580 if (r
!= COMPLETE
) {
1584 SUP
.cwdir
= g_strdup (remote_path
);
1585 SUP
.cwd_deferred
= 0;
1590 static int ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1592 ftpfs_send_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1593 return ftpfs_send_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1596 static int ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1598 (void) mode
; /* FIXME: should be used */
1600 return ftpfs_send_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1603 static int ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1605 return ftpfs_send_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1609 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
,
1614 fh
->u
.ftp
.append
= 0;
1615 /* File will be written only, so no need to retrieve it from ftp server */
1616 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
))) {
1617 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1624 /* ftpfs_linear_start() called, so data will be written
1625 * to local temporary file and stored to ftp server
1626 * by vfs_s_close later
1628 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
) {
1629 if (!fh
->ino
->localname
) {
1630 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1631 fh
->ino
->ent
->name
);
1635 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1639 name
= vfs_s_fullpath (me
, fh
->ino
);
1643 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1644 (flags
& O_APPEND
) ? "APPE" :
1645 "STOR", name
, TYPE_BINARY
, 0);
1650 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1654 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1656 if (fh
->ino
->localname
) {
1657 unlink (fh
->ino
->localname
);
1658 g_free (fh
->ino
->localname
);
1659 fh
->ino
->localname
= NULL
;
1664 if (!fh
->ino
->localname
)
1665 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1667 if (!fh
->ino
->localname
)
1668 vfs_die ("retrieve_file failed to fill in localname");
1672 static int ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1674 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1677 /* File is stored to destination already, so
1678 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1681 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1683 vfs_s_invalidate (me
, FH_SUPER
);
1689 ftpfs_done (struct vfs_class
*me
)
1691 struct no_proxy_entry
*np
;
1696 np
= no_proxy
->next
;
1697 g_free (no_proxy
->domain
);
1701 g_free (ftpfs_anonymous_passwd
);
1702 g_free (ftpfs_proxy_host
);
1706 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
1708 struct vfs_s_super
*super
= MEDATA
->supers
;
1712 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
1715 super
= super
->next
;
1719 static char buffer
[BUF_MEDIUM
];
1721 static const char *netrcp
;
1723 /* This should match the keywords[] array below */
1736 static keyword_t
ftpfs_netrc_next (void)
1740 static const char *const keywords
[] = { "default", "machine",
1741 "login", "password", "passwd", "account", "macdef", NULL
1746 netrcp
= skip_separators (netrcp
);
1747 if (*netrcp
!= '\n')
1754 if (*netrcp
== '"') {
1755 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1756 if (*netrcp
== '\\')
1761 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1762 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1763 if (*netrcp
== '\\')
1773 while (keywords
[i
- 1]) {
1774 if (!strcmp (keywords
[i
- 1], buffer
))
1780 return NETRC_UNKNOWN
;
1783 static int ftpfs_netrc_bad_mode (const char *netrcname
)
1785 static int be_angry
= 1;
1788 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1790 message (D_ERROR
, MSG_ERROR
,
1791 _("~/.netrc file has incorrect mode.\n"
1792 "Remove password or correct mode."));
1800 /* Scan .netrc until we find matching "machine" or "default"
1801 * domain is used for additional matching
1802 * No search is done after "default" in compliance with "man netrc"
1803 * Return 0 if found, -1 otherwise */
1804 static int ftpfs_find_machine (const char *host
, const char *domain
)
1808 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
) {
1809 if (keyword
== NETRC_DEFAULT
)
1812 if (keyword
== NETRC_MACDEF
) {
1813 /* Scan for an empty line, which concludes "macdef" */
1815 while (*netrcp
&& *netrcp
!= '\n')
1817 if (*netrcp
!= '\n')
1820 } while (*netrcp
&& *netrcp
!= '\n');
1824 if (keyword
!= NETRC_MACHINE
)
1827 /* Take machine name */
1828 if (ftpfs_netrc_next () == NETRC_NONE
)
1831 if (g_strcasecmp (host
, buffer
)) {
1832 /* Try adding our domain to short names in .netrc */
1833 const char *host_domain
= strchr (host
, '.');
1837 /* Compare domain part */
1838 if (g_strcasecmp (host_domain
, domain
))
1841 /* Compare local part */
1842 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1853 /* Extract login and password from .netrc for the host.
1855 * Returns 0 for success, -1 for error */
1856 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
1859 char *tmp_pass
= NULL
;
1860 char hostname
[MAXHOSTNAMELEN
];
1863 static struct rupcache
{
1864 struct rupcache
*next
;
1868 } *rup_cache
= NULL
, *rupp
;
1870 /* Initialize *login and *pass */
1877 /* Look up in the cache first */
1878 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
1879 if (!strcmp (host
, rupp
->host
)) {
1881 *login
= g_strdup (rupp
->login
);
1882 if (pass
&& rupp
->pass
)
1883 *pass
= g_strdup (rupp
->pass
);
1888 /* Load current .netrc */
1889 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
1890 netrcp
= netrc
= load_file (netrcname
);
1891 if (netrc
== NULL
) {
1896 /* Find our own domain name */
1897 if (gethostname (hostname
, sizeof (hostname
)) < 0)
1899 if (!(domain
= strchr (hostname
, '.')))
1902 /* Scan for "default" and matching "machine" keywords */
1903 ftpfs_find_machine (host
, domain
);
1905 /* Scan for keywords following "default" and "machine" */
1908 keyword
= ftpfs_netrc_next ();
1912 if (ftpfs_netrc_next () == NETRC_NONE
) {
1917 /* We have another name already - should not happen */
1923 /* We have login name now */
1924 *login
= g_strdup (buffer
);
1927 case NETRC_PASSWORD
:
1929 if (ftpfs_netrc_next () == NETRC_NONE
) {
1934 /* Ignore unsafe passwords */
1935 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
1936 && ftpfs_netrc_bad_mode (netrcname
)) {
1941 /* Remember password. pass may be NULL, so use tmp_pass */
1942 if (tmp_pass
== NULL
)
1943 tmp_pass
= g_strdup (buffer
);
1947 /* "account" is followed by a token which we ignore */
1948 if (ftpfs_netrc_next () == NETRC_NONE
) {
1953 /* Ignore account, but warn user anyways */
1954 ftpfs_netrc_bad_mode (netrcname
);
1958 /* Unexpected keyword or end of file */
1970 rupp
= g_new (struct rupcache
, 1);
1971 rupp
->host
= g_strdup (host
);
1972 rupp
->login
= rupp
->pass
= 0;
1974 if (*login
!= NULL
) {
1975 rupp
->login
= g_strdup (*login
);
1977 if (tmp_pass
!= NULL
)
1978 rupp
->pass
= g_strdup (tmp_pass
);
1979 rupp
->next
= rup_cache
;
1991 static struct vfs_s_subclass ftpfs_subclass
;
1993 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
1994 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
1995 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
1996 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
1997 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
1998 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
1999 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2000 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2001 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2002 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2003 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2005 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2006 vfs_ftpfs_ops
.name
= "ftpfs";
2007 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2008 vfs_ftpfs_ops
.prefix
= "ftp:";
2009 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2010 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2011 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2012 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2013 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2014 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2015 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2016 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2017 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2018 vfs_register_class (&vfs_ftpfs_ops
);