1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1995 Ching Hui
7 1995, 1996, 1997 Miguel de Icaza
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Library General Public License
13 as published by the Free Software Foundation; either version 2 of
14 the License, or (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU Library General Public License for more details.
21 You should have received a copy of the GNU Library General Public
22 License along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
27 - make it more robust - all the connects etc. should handle EADDRINUSE and
28 ERETRY (have I spelled these names correctly?)
29 - make the user able to flush a connection - all the caches will get empty
30 etc., (tarfs as well), we should give there a user selectable timeout
31 and assign a key sequence.
32 - use hash table instead of linklist to cache ftpfs directory.
37 * NOTE: Usage of tildes is deprecated, consider:
38 * cd /#ftp:pavel@hobit
40 * And now: what do I want to do? Do I want to go to /home/pavel or to
41 * /#ftp:hobit/home/pavel? I think first has better sense...
44 int f = !strcmp( remote_path, "/~" );
45 if (f || !strncmp( remote_path, "/~/", 3 )) {
47 s = mhl_str_dir_plus_file( qhome (*bucket), remote_path +3-f );
56 /* Namespace pollution: horrible */
59 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
60 #include <netdb.h> /* struct hostent */
61 #include <sys/socket.h> /* AF_INET */
62 #include <netinet/in.h> /* struct in_addr */
63 #ifdef HAVE_ARPA_INET_H
64 #include <arpa/inet.h>
67 #include <arpa/telnet.h>
68 #include <sys/param.h>
72 #include <mhl/string.h>
74 #include "../src/global.h"
75 #include "../src/tty.h" /* enable/disable interrupt key */
76 #include "../src/wtools.h" /* message() */
77 #include "../src/main.h" /* print_vfs_message */
78 #include "../src/history.h"
80 #include "xdirentry.h"
83 #include "gc.h" /* vfs_stamp_create */
85 #include "../src/setup.h" /* for load_anon_passwd */
87 #ifndef MAXHOSTNAMELEN
88 # define MAXHOSTNAMELEN 64
91 #define UPLOAD_ZERO_LENGTH_FILE
92 #define SUP super->u.ftp
93 #define FH_SOCK fh->u.ftp.sock
96 #define INADDR_NONE 0xffffffff
99 #define RFC_AUTODETECT 0
103 #ifndef HAVE_SOCKLEN_T
104 typedef int socklen_t
;
107 static int ftpfs_errno
;
110 /* Delay to retry a connection */
111 int ftpfs_retry_seconds
= 30;
113 /* Method to use to connect to ftp sites */
114 int ftpfs_use_passive_connections
= 1;
115 int ftpfs_use_passive_connections_over_proxy
= 0;
117 /* Method used to get directory listings:
118 * 1: try 'LIST -la <path>', if it fails
119 * fall back to CWD <path>; LIST
120 * 0: always use CWD <path>; LIST
122 int ftpfs_use_unix_list_options
= 1;
124 /* First "CWD <path>", then "LIST -la ." */
125 int ftpfs_first_cd_then_ls
= 1;
127 /* Use the ~/.netrc */
130 /* Anonymous setup */
131 char *ftpfs_anonymous_passwd
= NULL
;
132 int ftpfs_directory_timeout
= 900;
135 char *ftpfs_proxy_host
= NULL
;
137 /* wether we have to use proxy by default? */
138 int ftpfs_always_use_proxy
;
140 #ifdef FIXME_LATER_ALIGATOR
141 static struct linklist
*connections_list
;
144 /* ftpfs_command wait_flag: */
146 #define WAIT_REPLY 0x01
147 #define WANT_STRING 0x02
148 static char reply_str
[80];
150 static struct vfs_class vfs_ftpfs_ops
;
152 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
153 Translate a Unix path, i.e. MC's internal path representation (e.g.
154 /somedir/somefile) to a path valid for the remote server. Every path
155 transfered to the remote server has to be mangled by this function
156 right prior to sending it.
157 Currently only Amiga ftp servers are handled in a special manner.
159 When the remote server is an amiga:
160 a) strip leading slash if necesarry
161 b) replace first occurance of ":/" with ":"
162 c) strip trailing "/."
165 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
166 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
);
167 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
168 __attribute__ ((format (__printf__
, 4, 5)));
169 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
170 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
);
171 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
174 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
176 if (!SUP
.remote_is_amiga
)
177 return g_strdup (remote_path
);
181 if (MEDATA
->logfile
) {
182 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
183 fflush (MEDATA
->logfile
);
186 /* strip leading slash(es) */
187 while (*remote_path
== '/')
191 * Don't change "/" into "", e.g. "CWD " would be
194 if (*remote_path
== '\0')
195 return g_strdup (".");
197 ret
= g_strdup (remote_path
);
199 /* replace first occurance of ":/" with ":" */
200 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
201 strcpy (p
+ 1, p
+ 2);
203 /* strip trailing "/." */
204 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
210 /* Extract the hostname and username from the path */
213 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
214 * ftp://sunsite.unc.edu/pub/linux
215 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
216 * ftp://tsx-11.mit.edu:8192/
217 * ftp://joe@foo.edu:11321/private
218 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
223 #define FTP_COMMAND_PORT 21
226 ftpfs_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
230 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
234 /* Look up user and password in netrc */
236 ftpfs_netrc_lookup (*host
, user
, pass
);
238 *user
= g_strdup ("anonymous");
241 /* Look up password in netrc for known user */
242 if (use_netrc
&& *user
&& pass
&& !*pass
) {
245 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
247 /* If user is different, remove password */
248 if (new_user
&& strcmp (*user
, new_user
)) {
259 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
261 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
267 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
273 switch (sscanf(answer
, "%d", &code
)){
276 g_strlcpy (string_buf
, answer
, string_len
);
280 if (answer
[3] == '-') {
282 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
288 if ((sscanf (answer
, "%d", &i
) > 0) &&
289 (code
== i
) && (answer
[3] == ' '))
294 g_strlcpy (string_buf
, answer
, string_len
);
301 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
303 int sock
= ftpfs_open_socket (me
, super
);
305 char *cwdir
= SUP
.cwdir
;
309 if (ftpfs_login_server (me
, super
, SUP
.password
)){
312 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
314 return sock
== COMPLETE
;
322 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
327 static int retry
= 0;
328 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
331 cmdstr
= g_strdup_vprintf (fmt
, ap
);
334 cmdlen
= strlen (cmdstr
);
335 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
336 strcpy (cmdstr
+ cmdlen
, "\r\n");
339 if (MEDATA
->logfile
) {
340 if (strncmp (cmdstr
, "PASS ", 5) == 0) {
341 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
343 fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
345 fflush (MEDATA
->logfile
);
349 enable_interrupt_key ();
350 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
355 if (errno
== EPIPE
) { /* Remote server has closed connection */
358 status
= ftpfs_reconnect (me
, super
);
360 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
368 disable_interrupt_key ();
373 disable_interrupt_key ();
377 status
= ftpfs_get_reply (me
, SUP
.sock
,
378 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
379 sizeof (reply_str
) - 1);
380 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
384 status
= ftpfs_reconnect (me
, super
);
386 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
399 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
402 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
403 ftpfs_command(me
, super
, NONE
, "QUIT");
409 g_free (SUP
.password
);
412 /* some defines only used by ftpfs_changetype */
413 /* These two are valid values for the second parameter */
415 #define TYPE_BINARY 1
417 /* This one is only used to initialize bucket->isbinary, don't use it as
418 second parameter to ftpfs_changetype. */
419 #define TYPE_UNKNOWN -1
422 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
424 if (binary
!= SUP
.isbinary
) {
425 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
427 SUP
.isbinary
= binary
;
432 /* This routine logs the user in */
434 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
435 const char *netrcpass
)
439 char *name
; /* login user name */
441 char reply_string
[BUF_MEDIUM
];
443 SUP
.isbinary
= TYPE_UNKNOWN
;
445 if (SUP
.password
) /* explicit password */
446 op
= g_strdup (SUP
.password
);
447 else if (netrcpass
) /* password from netrc */
448 op
= g_strdup (netrcpass
);
449 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp")) {
450 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
451 ftpfs_init_passwd ();
452 op
= g_strdup (ftpfs_anonymous_passwd
);
454 } else { /* ask user */
457 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
, " ",
459 op
= vfs_get_password (p
);
463 SUP
.password
= g_strdup (op
);
466 if (!anon
|| MEDATA
->logfile
)
469 pass
= g_strconcat ("-", op
, (char *) NULL
);
473 /* Proxy server accepts: username@host-we-want-to-connect */
476 g_strconcat (SUP
.user
, "@",
477 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
,
480 name
= g_strdup (SUP
.user
);
483 (me
, SUP
.sock
, reply_string
,
484 sizeof (reply_string
) - 1) == COMPLETE
) {
485 g_strup (reply_string
);
486 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
487 if (MEDATA
->logfile
) {
488 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n",
489 SUP
.remote_is_amiga
);
490 fflush (MEDATA
->logfile
);
493 print_vfs_message (_("ftpfs: sending login name"));
495 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
)) {
497 print_vfs_message (_("ftpfs: sending user password"));
498 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
499 if (code
== CONTINUE
) {
502 p
= g_strdup_printf (_
503 ("FTP: Account required for user %s"),
505 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
509 print_vfs_message (_("ftpfs: sending user account"));
511 ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
514 if (code
!= COMPLETE
)
519 print_vfs_message (_("ftpfs: logged in"));
520 wipe_password (pass
);
525 SUP
.failed_on_login
= 1;
527 wipe_password (SUP
.password
);
533 message (1, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
536 wipe_password (pass
);
541 static struct no_proxy_entry
{
547 ftpfs_load_no_proxy_list (void)
549 /* FixMe: shouldn't be hardcoded!!! */
550 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
551 struct no_proxy_entry
*np
, *current
= 0;
555 static char *mc_file
;
560 mc_file
= mhl_str_dir_plus_file (mc_home
, "mc.no_proxy");
561 if (exist_file (mc_file
) &&
562 (npf
= fopen (mc_file
, "r"))) {
563 while (fgets (s
, sizeof (s
), npf
)) {
564 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
565 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
575 np
= g_new (struct no_proxy_entry
, 1);
576 np
->domain
= g_strdup (s
);
590 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
592 ftpfs_check_proxy (const char *host
)
594 struct no_proxy_entry
*npe
;
596 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
597 return 0; /* sanity check */
602 if (!ftpfs_always_use_proxy
)
605 if (!strchr (host
, '.'))
608 ftpfs_load_no_proxy_list ();
609 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
610 char *domain
= npe
->domain
;
612 if (domain
[0] == '.') {
613 int ld
= strlen (domain
);
614 int lh
= strlen (host
);
616 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
624 if (!g_strcasecmp (host
, domain
))
632 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
637 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
644 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
646 struct sockaddr_in server_address
;
655 /* Use a proxy host? */
658 if (!host
|| !*host
){
659 print_vfs_message (_("ftpfs: Invalid host name."));
660 ftpfs_errno
= EINVAL
;
664 /* Hosts to connect to that start with a ! should use proxy */
666 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
670 enable_interrupt_key(); /* clear the interrupt flag */
672 /* Get host address */
673 memset ((char *) &server_address
, 0, sizeof (server_address
));
674 server_address
.sin_family
= AF_INET
;
675 server_address
.sin_addr
.s_addr
= inet_addr (host
);
676 if (server_address
.sin_addr
.s_addr
== INADDR_NONE
) {
677 hp
= gethostbyname (host
);
679 disable_interrupt_key();
680 print_vfs_message (_("ftpfs: Invalid host address."));
681 ftpfs_errno
= EINVAL
;
686 server_address
.sin_family
= hp
->h_addrtype
;
688 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
689 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
, 4);
692 server_address
.sin_port
= htons (port
);
695 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
696 disable_interrupt_key();
703 print_vfs_message (_("ftpfs: making connection to %s"), host
);
707 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
708 sizeof (server_address
)) < 0){
710 if (errno
== EINTR
&& got_interrupt ())
711 print_vfs_message (_("ftpfs: connection interrupted by user"));
713 print_vfs_message (_("ftpfs: connection to server failed: %s"),
714 unix_error_string(errno
));
715 disable_interrupt_key();
719 disable_interrupt_key();
724 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
726 int retry_seconds
, count_down
;
728 /* We do not want to use the passive if we are using proxies */
730 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
734 SUP
.failed_on_login
= 0;
736 SUP
.sock
= ftpfs_open_socket (me
, super
);
740 if (ftpfs_login_server (me
, super
, NULL
)) {
741 /* Logged in, no need to retry the connection */
744 if (SUP
.failed_on_login
){
745 /* Close only the socket descriptor */
750 if (ftpfs_retry_seconds
){
751 retry_seconds
= ftpfs_retry_seconds
;
752 enable_interrupt_key ();
753 for (count_down
= retry_seconds
; count_down
; count_down
--){
754 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
756 if (got_interrupt ()){
757 /* ftpfs_errno = E; */
758 disable_interrupt_key ();
762 disable_interrupt_key ();
765 } while (retry_seconds
);
767 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
769 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
774 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
775 const char *archive_name
, char *op
)
777 char *host
, *user
, *password
;
782 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
789 if (ftpfs_check_proxy (host
))
790 SUP
.proxy
= ftpfs_proxy_host
;
791 SUP
.password
= password
;
792 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
793 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
794 SUP
.isbinary
= TYPE_UNKNOWN
;
795 SUP
.remote_is_amiga
= 0;
796 super
->name
= g_strdup ("/");
798 vfs_s_new_inode (me
, super
,
799 vfs_s_default_stat (me
, S_IFDIR
| 0755));
801 return ftpfs_open_archive_int (me
, super
);
805 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
806 const char *archive_name
, char *op
, void *cookie
)
815 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
817 port
= ((strcmp (host
, SUP
.host
) == 0)
818 && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
826 /* The returned directory should always contain a trailing slash */
828 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
830 char buf
[BUF_8K
], *bufp
, *bufq
;
832 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
833 ftpfs_get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
835 for (bufq
= buf
; *bufq
; bufq
++)
842 if (*(bufq
- 1) != '/') {
847 return g_strdup (bufp
);
849 /* If the remote server is an Amiga a leading slash
850 might be missing. MC needs it because it is used
851 as separator between hostname and path internally. */
852 return g_strconcat( "/", bufp
, NULL
);
866 /* Setup Passive ftp connection, we use it for source routed connections */
868 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
, int my_socket
, struct sockaddr_in
*sa
)
870 int xa
, xb
, xc
, xd
, xe
, xf
;
874 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
877 /* Parse remote parameters */
878 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++)
882 if (!isdigit ((unsigned char) *c
))
884 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
886 n
[0] = (unsigned char) xa
;
887 n
[1] = (unsigned char) xb
;
888 n
[2] = (unsigned char) xc
;
889 n
[3] = (unsigned char) xd
;
890 n
[4] = (unsigned char) xe
;
891 n
[5] = (unsigned char) xf
;
893 memcpy (&(sa
->sin_addr
.s_addr
), (void *)n
, 4);
894 memcpy (&(sa
->sin_port
), (void *)&n
[4], 2);
895 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
901 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
903 struct sockaddr_in data_addr
;
905 socklen_t len
= sizeof(data_addr
);
908 pe
= getprotobyname ("tcp");
912 if (getsockname (SUP
.sock
, (struct sockaddr
*) &data_addr
, &len
) == -1)
914 data_addr
.sin_port
= 0;
916 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
920 if (SUP
.use_passive_connection
) {
921 if (ftpfs_setup_passive (me
, super
, data
, &data_addr
))
924 SUP
.use_passive_connection
= 0;
925 print_vfs_message (_("ftpfs: could not setup passive mode"));
927 /* data or data_addr may be damaged by ftpfs_setup_passive */
932 /* If passive setup fails, fallback to active connections */
933 /* Active FTP connection */
934 if ((bind (data
, (struct sockaddr
*)&data_addr
, len
) == 0) &&
935 (getsockname (data
, (struct sockaddr
*) &data_addr
, &len
) == 0) &&
936 (listen (data
, 1) == 0))
938 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
939 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
941 if (ftpfs_command (me
, super
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
942 a
[2], a
[3], p
[0], p
[1]) == COMPLETE
)
951 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
952 const char *remote
, int isbinary
, int reget
)
954 struct sockaddr_in from
;
956 socklen_t fromlen
= sizeof(from
);
958 if ((s
= ftpfs_initconn (me
, super
)) == -1)
960 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
963 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
968 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
969 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
970 /* WarFtpD can't STORE //filename */
971 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
972 g_free (remote_path
);
974 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
977 enable_interrupt_key();
978 if (SUP
.use_passive_connection
)
981 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
989 disable_interrupt_key();
993 #define ABORT_TIMEOUT 5
995 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
997 struct vfs_s_super
*super
= FH_SUPER
;
998 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1001 int dsock
= FH_SOCK
;
1003 SUP
.ctl_connection_busy
= 0;
1005 print_vfs_message (_("ftpfs: aborting transfer."));
1006 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
1007 print_vfs_message (_("ftpfs: abort error: %s"),
1008 unix_error_string (errno
));
1014 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1015 print_vfs_message (_("ftpfs: abort failed"));
1022 FD_SET (dsock
, &mask
);
1023 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1024 struct timeval start_tim
, tim
;
1025 gettimeofday (&start_tim
, NULL
);
1026 /* flush the remaining data */
1027 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1028 gettimeofday (&tim
, NULL
);
1029 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1030 /* server keeps sending, drop the connection and ftpfs_reconnect */
1032 ftpfs_reconnect (me
, super
);
1039 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1040 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1045 resolve_symlink_without_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1047 struct linklist
*flist
;
1048 struct direntry
*fe
, *fel
;
1049 char tmp
[MC_MAXPATHLEN
];
1052 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1053 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1054 /* flist->data->l_stat is alread initialized with 0 */
1056 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1057 if (fel
->linkname
[0] == '/') {
1058 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1060 strcpy (tmp
, fel
->linkname
);
1062 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1064 strcpy (tmp
, dir
->remote_path
);
1067 strcat (tmp
+ 1, fel
->linkname
);
1069 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1070 canonicalize_pathname (tmp
);
1071 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1073 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1074 /* Symlink points to link which isn't resolved, yet. */
1075 if (fe
->linkname
[0] == '/') {
1076 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1078 strcpy (tmp
, fe
->linkname
);
1080 /* at this point tmp looks always like this
1081 /directory/filename, i.e. no need to check
1082 strrchr's return value */
1083 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1084 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1086 strcat (tmp
, fe
->linkname
);
1090 fel
->l_stat
= g_new (struct stat
, 1);
1091 if ( S_ISLNK (fe
->s
.st_mode
))
1092 *fel
->l_stat
= *fe
->l_stat
;
1094 *fel
->l_stat
= fe
->s
;
1095 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1102 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1106 resolve_symlink_with_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1108 char buffer
[2048] = "", *filename
;
1112 struct linklist
*flist
;
1113 struct direntry
*fe
;
1114 int switch_method
= 0;
1116 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1117 if (strchr (dir
->remote_path
, ' ')) {
1118 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1119 print_vfs_message(_("ftpfs: CWD failed."));
1122 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1125 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa",
1126 dir
->remote_path
, TYPE_ASCII
, 0);
1129 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1133 fp
= fdopen(sock
, "r");
1136 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1139 enable_interrupt_key();
1140 flist
= dir
->file_list
->next
;
1143 if (flist
== dir
->file_list
)
1146 flist
= flist
->next
;
1147 } while (!S_ISLNK(fe
->s
.st_mode
));
1149 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1151 if (MEDATA
->logfile
){
1152 fputs (buffer
, MEDATA
->logfile
);
1153 fflush (MEDATA
->logfile
);
1155 vfs_die("This code should be commented out\n");
1156 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1157 int r
= strcmp(fe
->name
, filename
);
1160 if (S_ISLNK (s
.st_mode
)) {
1161 /* This server doesn't understand LIST -lLa */
1165 fe
->l_stat
= g_new (struct stat
, 1);
1166 if (fe
->l_stat
== NULL
)
1169 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1178 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1179 disable_interrupt_key();
1181 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1185 resolve_symlink(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1187 print_vfs_message(_("Resolving symlink..."));
1189 if (SUP
.strict_rfc959_list_cmd
)
1190 resolve_symlink_without_ls_options(me
, super
, dir
);
1192 resolve_symlink_with_ls_options(me
, super
, dir
);
1197 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1199 struct vfs_s_entry
*ent
;
1200 struct vfs_s_super
*super
= dir
->super
;
1201 int sock
, num_entries
= 0;
1202 char buffer
[BUF_8K
];
1205 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1206 || (strchr (remote_path
, ' ') != NULL
);
1209 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1212 RFC_STRICT
? _("(strict rfc959)") : "",
1213 cd_first
? _("(chdir first)") : "");
1216 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
) {
1217 ftpfs_errno
= ENOENT
;
1218 print_vfs_message (_("ftpfs: CWD failed."));
1223 gettimeofday (&dir
->timestamp
, NULL
);
1224 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1226 if (SUP
.strict
== RFC_STRICT
)
1227 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1229 /* Dirty hack to avoid autoprepending / to . */
1230 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1232 ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1234 /* Trailing "/." is necessary if remote_path is a symlink */
1235 char *path
= mhl_str_dir_plus_file (remote_path
, ".");
1237 ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
,
1245 /* Clear the interrupt flag */
1246 enable_interrupt_key ();
1251 vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1257 me
->verrno
= ECONNRESET
;
1259 disable_interrupt_key ();
1260 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1261 print_vfs_message (_("%s: failure"), me
->name
);
1265 if (MEDATA
->logfile
) {
1266 fputs (buffer
, MEDATA
->logfile
);
1267 fputs ("\n", MEDATA
->logfile
);
1268 fflush (MEDATA
->logfile
);
1271 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1272 i
= ent
->ino
->st
.st_nlink
;
1273 if (!vfs_parse_ls_lga
1274 (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1275 vfs_s_free_entry (me
, ent
);
1278 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1280 vfs_s_insert_entry (me
, dir
, ent
);
1284 me
->verrno
= E_REMOTE
;
1285 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1288 if (num_entries
== 0 && cd_first
== 0) {
1289 /* The LIST command may produce an empty output. In such scenario
1290 it is not clear whether this is caused by `remote_path' being
1291 a non-existent path or for some other reason (listing emtpy
1292 directory without the -a option, non-readable directory, etc.).
1294 Since `dir_load' is a crucial method, when it comes to determine
1295 whether a given path is a _directory_, the code must try its best
1296 to determine the type of `remote_path'. The only reliable way to
1297 achieve this is trough issuing a CWD command. */
1303 if (SUP
.strict
== RFC_AUTODETECT
)
1304 SUP
.strict
= RFC_DARING
;
1306 print_vfs_message (_("%s: done."), me
->name
);
1310 if (SUP
.strict
== RFC_AUTODETECT
) {
1311 /* It's our first attempt to get a directory listing from this
1312 server (UNIX style LIST command) */
1313 SUP
.strict
= RFC_STRICT
;
1314 /* I hate goto, but recursive call needs another 8K on stack */
1315 /* return ftpfs_dir_load (me, dir, remote_path); */
1319 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1320 ERRNOR (EACCES
, -1);
1324 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
,
1327 int h
, sock
, n_read
, n_written
;
1329 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1337 struct vfs_s_super
*super
= FH_SUPER
;
1339 h
= open (localname
, O_RDONLY
);
1343 ftpfs_open_data_connection (me
, super
,
1344 fh
->u
.ftp
.append
? "APPE" : "STOR", name
,
1346 if (sock
< 0 || fstat (h
, &s
) == -1) {
1350 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1353 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1355 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1359 enable_interrupt_key ();
1361 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1) {
1362 if (errno
== EINTR
) {
1363 if (got_interrupt ()) {
1364 ftpfs_errno
= EINTR
;
1369 ftpfs_errno
= errno
;
1376 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
) {
1377 if (n_written
== -1) {
1378 if (errno
== EINTR
&& !got_interrupt ()) {
1381 ftpfs_errno
= errno
;
1385 n_read
-= n_written
;
1387 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1388 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1390 disable_interrupt_key ();
1393 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1397 disable_interrupt_key ();
1400 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1405 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1407 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1411 FH_SOCK
= ftpfs_open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1415 fh
->linear
= LS_LINEAR_OPEN
;
1416 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1417 fh
->u
.ftp
.append
= 0;
1422 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1425 struct vfs_s_super
*super
= FH_SUPER
;
1427 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1428 if ((errno
== EINTR
) && !got_interrupt())
1434 ftpfs_linear_abort(me
, fh
);
1437 SUP
.ctl_connection_busy
= 0;
1440 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1441 ERRNOR (E_REMOTE
, -1);
1448 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1451 ftpfs_linear_abort(me
, fh
);
1454 static int ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1459 case VFS_CTL_IS_NOTREADY
:
1464 vfs_die ("You may not do this");
1465 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1468 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1469 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1479 ftpfs_send_command(struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1482 char *p
, *mpath
= g_strdup(filename
);
1483 struct vfs_s_super
*super
;
1485 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1487 if (!(rpath
= vfs_s_get_path_mangle(me
, mpath
, &super
, 0))) {
1491 p
= ftpfs_translate_path (me
, super
, rpath
);
1492 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1494 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1495 if (flags
& OPT_IGNORE_ERROR
)
1497 if (r
!= COMPLETE
) {
1502 if (flush_directory_cache
)
1503 vfs_s_invalidate(me
, super
);
1508 /* This routine is called as the last step in load_setup */
1510 ftpfs_init_passwd(void)
1512 ftpfs_anonymous_passwd
= load_anon_passwd ();
1513 if (ftpfs_anonymous_passwd
)
1516 /* If there is no anonymous ftp password specified
1517 * then we'll just use anonymous@
1518 * We don't send any other thing because:
1519 * - We want to remain anonymous
1520 * - We want to stop SPAM
1521 * - We don't want to let ftp sites to discriminate by the user,
1524 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1527 static int ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1529 char buf
[BUF_SMALL
];
1531 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1532 return ftpfs_send_command(me
, path
, buf
, OPT_FLUSH
);
1535 static int ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1538 ftpfs_errno
= EPERM
;
1541 /* Everyone knows it is not possible to chown remotely, so why bother them.
1542 If someone's root, then copy/move will always try to chown it... */
1551 static int ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1553 return ftpfs_send_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1556 /* Return 1 if path is the same directory as the one we are in now */
1558 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1564 if (strcmp (path
, SUP
.cwdir
) == 0)
1570 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1575 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1578 p
= ftpfs_translate_path (me
, super
, remote_path
);
1579 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1582 if (r
!= COMPLETE
) {
1586 SUP
.cwdir
= g_strdup (remote_path
);
1587 SUP
.cwd_deferred
= 0;
1592 static int ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1594 ftpfs_send_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1595 return ftpfs_send_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1598 static int ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1600 (void) mode
; /* FIXME: should be used */
1602 return ftpfs_send_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1605 static int ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1607 return ftpfs_send_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1611 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
,
1616 fh
->u
.ftp
.append
= 0;
1617 /* File will be written only, so no need to retrieve it from ftp server */
1618 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
))) {
1619 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1626 /* ftpfs_linear_start() called, so data will be written
1627 * to local temporary file and stored to ftp server
1628 * by vfs_s_close later
1630 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
) {
1631 if (!fh
->ino
->localname
) {
1632 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1633 fh
->ino
->ent
->name
);
1637 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1641 name
= vfs_s_fullpath (me
, fh
->ino
);
1645 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1646 (flags
& O_APPEND
) ? "APPE" :
1647 "STOR", name
, TYPE_BINARY
, 0);
1652 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1656 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1658 if (fh
->ino
->localname
) {
1659 unlink (fh
->ino
->localname
);
1660 g_free (fh
->ino
->localname
);
1661 fh
->ino
->localname
= NULL
;
1666 if (!fh
->ino
->localname
)
1667 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1669 if (!fh
->ino
->localname
)
1670 vfs_die ("retrieve_file failed to fill in localname");
1674 static int ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1676 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1679 /* File is stored to destination already, so
1680 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1683 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1685 vfs_s_invalidate (me
, FH_SUPER
);
1691 ftpfs_done (struct vfs_class
*me
)
1693 struct no_proxy_entry
*np
;
1698 np
= no_proxy
->next
;
1699 g_free (no_proxy
->domain
);
1703 g_free (ftpfs_anonymous_passwd
);
1704 g_free (ftpfs_proxy_host
);
1708 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
1710 struct vfs_s_super
*super
= MEDATA
->supers
;
1714 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
1717 super
= super
->next
;
1721 static char buffer
[BUF_MEDIUM
];
1723 static const char *netrcp
;
1725 /* This should match the keywords[] array below */
1738 static keyword_t
ftpfs_netrc_next (void)
1742 static const char *const keywords
[] = { "default", "machine",
1743 "login", "password", "passwd", "account", "macdef", NULL
1748 netrcp
= skip_separators (netrcp
);
1749 if (*netrcp
!= '\n')
1756 if (*netrcp
== '"') {
1757 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1758 if (*netrcp
== '\\')
1763 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1764 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1765 if (*netrcp
== '\\')
1775 while (keywords
[i
- 1]) {
1776 if (!strcmp (keywords
[i
- 1], buffer
))
1782 return NETRC_UNKNOWN
;
1785 static int ftpfs_netrc_bad_mode (const char *netrcname
)
1787 static int be_angry
= 1;
1790 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1792 message (1, MSG_ERROR
,
1793 _("~/.netrc file has incorrect mode.\n"
1794 "Remove password or correct mode."));
1802 /* Scan .netrc until we find matching "machine" or "default"
1803 * domain is used for additional matching
1804 * No search is done after "default" in compliance with "man netrc"
1805 * Return 0 if found, -1 otherwise */
1806 static int ftpfs_find_machine (const char *host
, const char *domain
)
1810 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
) {
1811 if (keyword
== NETRC_DEFAULT
)
1814 if (keyword
== NETRC_MACDEF
) {
1815 /* Scan for an empty line, which concludes "macdef" */
1817 while (*netrcp
&& *netrcp
!= '\n')
1819 if (*netrcp
!= '\n')
1822 } while (*netrcp
&& *netrcp
!= '\n');
1826 if (keyword
!= NETRC_MACHINE
)
1829 /* Take machine name */
1830 if (ftpfs_netrc_next () == NETRC_NONE
)
1833 if (g_strcasecmp (host
, buffer
)) {
1834 /* Try adding our domain to short names in .netrc */
1835 const char *host_domain
= strchr (host
, '.');
1839 /* Compare domain part */
1840 if (g_strcasecmp (host_domain
, domain
))
1843 /* Compare local part */
1844 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1855 /* Extract login and password from .netrc for the host.
1857 * Returns 0 for success, -1 for error */
1858 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
1861 char *tmp_pass
= NULL
;
1862 char hostname
[MAXHOSTNAMELEN
];
1865 static struct rupcache
{
1866 struct rupcache
*next
;
1870 } *rup_cache
= NULL
, *rupp
;
1872 /* Initialize *login and *pass */
1879 /* Look up in the cache first */
1880 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
1881 if (!strcmp (host
, rupp
->host
)) {
1883 *login
= g_strdup (rupp
->login
);
1884 if (pass
&& rupp
->pass
)
1885 *pass
= g_strdup (rupp
->pass
);
1890 /* Load current .netrc */
1891 netrcname
= mhl_str_dir_plus_file (home_dir
, ".netrc");
1892 netrcp
= netrc
= load_file (netrcname
);
1893 if (netrc
== NULL
) {
1898 /* Find our own domain name */
1899 if (gethostname (hostname
, sizeof (hostname
)) < 0)
1901 if (!(domain
= strchr (hostname
, '.')))
1904 /* Scan for "default" and matching "machine" keywords */
1905 ftpfs_find_machine (host
, domain
);
1907 /* Scan for keywords following "default" and "machine" */
1910 keyword
= ftpfs_netrc_next ();
1914 if (ftpfs_netrc_next () == NETRC_NONE
) {
1919 /* We have another name already - should not happen */
1925 /* We have login name now */
1926 *login
= g_strdup (buffer
);
1929 case NETRC_PASSWORD
:
1931 if (ftpfs_netrc_next () == NETRC_NONE
) {
1936 /* Ignore unsafe passwords */
1937 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
1938 && ftpfs_netrc_bad_mode (netrcname
)) {
1943 /* Remember password. pass may be NULL, so use tmp_pass */
1944 if (tmp_pass
== NULL
)
1945 tmp_pass
= g_strdup (buffer
);
1949 /* "account" is followed by a token which we ignore */
1950 if (ftpfs_netrc_next () == NETRC_NONE
) {
1955 /* Ignore account, but warn user anyways */
1956 ftpfs_netrc_bad_mode (netrcname
);
1960 /* Unexpected keyword or end of file */
1972 rupp
= g_new (struct rupcache
, 1);
1973 rupp
->host
= g_strdup (host
);
1974 rupp
->login
= rupp
->pass
= 0;
1976 if (*login
!= NULL
) {
1977 rupp
->login
= g_strdup (*login
);
1979 if (tmp_pass
!= NULL
)
1980 rupp
->pass
= g_strdup (tmp_pass
);
1981 rupp
->next
= rup_cache
;
1993 static struct vfs_s_subclass ftpfs_subclass
;
1995 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
1996 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
1997 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
1998 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
1999 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2000 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2001 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2002 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2003 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2004 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2005 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2007 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2008 vfs_ftpfs_ops
.name
= "ftpfs";
2009 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2010 vfs_ftpfs_ops
.prefix
= "ftp:";
2011 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2012 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2013 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2014 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2015 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2016 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2017 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2018 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2019 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2020 vfs_register_class (&vfs_ftpfs_ops
);