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 * \brief Source: Virtual File System: FTP file system
29 * \author Jakub Jelinek
30 * \author Miguel de Icaza
31 * \author Norbert Warmuth
32 * \author Pavel Machek
33 * \date 1995, 1997, 1998
36 - make it more robust - all the connects etc. should handle EADDRINUSE and
37 ERETRY (have I spelled these names correctly?)
38 - make the user able to flush a connection - all the caches will get empty
39 etc., (tarfs as well), we should give there a user selectable timeout
40 and assign a key sequence.
41 - use hash table instead of linklist to cache ftpfs directory.
46 * NOTE: Usage of tildes is deprecated, consider:
47 * cd /#ftp:pavel@hobit
49 * And now: what do I want to do? Do I want to go to /home/pavel or to
50 * /#ftp:hobit/home/pavel? I think first has better sense...
53 int f = !strcmp( remote_path, "/~" );
54 if (f || !strncmp( remote_path, "/~/", 3 )) {
56 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
65 /* \todo Fix: Namespace pollution: horrible */
68 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
69 #include <netdb.h> /* struct hostent */
70 #include <sys/socket.h> /* AF_INET */
71 #include <netinet/in.h> /* struct in_addr */
72 #ifdef HAVE_ARPA_INET_H
73 #include <arpa/inet.h>
76 #include <arpa/telnet.h>
77 #include <sys/param.h>
81 #include <sys/time.h> /* gettimeofday() */
83 #include "../src/global.h"
84 #include "../src/tty.h" /* enable/disable interrupt key */
85 #include "../src/wtools.h" /* message() */
86 #include "../src/main.h" /* print_vfs_message */
87 #include "../src/history.h"
89 #include "xdirentry.h"
92 #include "gc.h" /* vfs_stamp_create */
94 #include "../src/setup.h" /* for load_anon_passwd */
96 #ifndef MAXHOSTNAMELEN
97 # define MAXHOSTNAMELEN 64
100 #define UPLOAD_ZERO_LENGTH_FILE
101 #define SUP super->u.ftp
102 #define FH_SOCK fh->u.ftp.sock
105 #define INADDR_NONE 0xffffffff
108 #define RFC_AUTODETECT 0
112 #ifndef HAVE_SOCKLEN_T
113 typedef int socklen_t
;
116 static int ftpfs_errno
;
119 /* Delay to retry a connection */
120 int ftpfs_retry_seconds
= 30;
122 /* Method to use to connect to ftp sites */
123 int ftpfs_use_passive_connections
= 1;
124 int ftpfs_use_passive_connections_over_proxy
= 0;
126 /* Method used to get directory listings:
127 * 1: try 'LIST -la <path>', if it fails
128 * fall back to CWD <path>; LIST
129 * 0: always use CWD <path>; LIST
131 int ftpfs_use_unix_list_options
= 1;
133 /* First "CWD <path>", then "LIST -la ." */
134 int ftpfs_first_cd_then_ls
= 1;
136 /* Use the ~/.netrc */
139 /* Anonymous setup */
140 char *ftpfs_anonymous_passwd
= NULL
;
141 int ftpfs_directory_timeout
= 900;
144 char *ftpfs_proxy_host
= NULL
;
146 /* wether we have to use proxy by default? */
147 int ftpfs_always_use_proxy
;
149 #ifdef FIXME_LATER_ALIGATOR
150 static struct linklist
*connections_list
;
153 /* ftpfs_command wait_flag: */
155 #define WAIT_REPLY 0x01
156 #define WANT_STRING 0x02
157 static char reply_str
[80];
159 static struct vfs_class vfs_ftpfs_ops
;
161 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
162 Translate a Unix path, i.e. MC's internal path representation (e.g.
163 /somedir/somefile) to a path valid for the remote server. Every path
164 transfered to the remote server has to be mangled by this function
165 right prior to sending it.
166 Currently only Amiga ftp servers are handled in a special manner.
168 When the remote server is an amiga:
169 a) strip leading slash if necesarry
170 b) replace first occurance of ":/" with ":"
171 c) strip trailing "/."
174 static char *ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
);
175 static int ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
);
176 static int ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
177 __attribute__ ((format (__printf__
, 4, 5)));
178 static int ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
);
179 static int ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *netrcpass
);
180 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
);
183 ftpfs_translate_path (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
185 if (!SUP
.remote_is_amiga
)
186 return g_strdup (remote_path
);
190 if (MEDATA
->logfile
) {
191 fprintf (MEDATA
->logfile
, "MC -- ftpfs_translate_path: %s\n", remote_path
);
192 fflush (MEDATA
->logfile
);
195 /* strip leading slash(es) */
196 while (*remote_path
== '/')
200 * Don't change "/" into "", e.g. "CWD " would be
203 if (*remote_path
== '\0')
204 return g_strdup (".");
206 ret
= g_strdup (remote_path
);
208 /* replace first occurance of ":/" with ":" */
209 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
210 strcpy (p
+ 1, p
+ 2);
212 /* strip trailing "/." */
213 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
219 /* Extract the hostname and username from the path */
222 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
223 * ftp://sunsite.unc.edu/pub/linux
224 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
225 * ftp://tsx-11.mit.edu:8192/
226 * ftp://joe@foo.edu:11321/private
227 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
232 #define FTP_COMMAND_PORT 21
235 ftpfs_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
239 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
243 /* Look up user and password in netrc */
245 ftpfs_netrc_lookup (*host
, user
, pass
);
247 *user
= g_strdup ("anonymous");
250 /* Look up password in netrc for known user */
251 if (use_netrc
&& *user
&& pass
&& !*pass
) {
254 ftpfs_netrc_lookup (*host
, &new_user
, pass
);
256 /* If user is different, remove password */
257 if (new_user
&& strcmp (*user
, new_user
)) {
268 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
270 ftpfs_get_reply (struct vfs_class
*me
, int sock
, char *string_buf
, int string_len
)
276 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
282 switch (sscanf(answer
, "%d", &code
)){
285 g_strlcpy (string_buf
, answer
, string_len
);
289 if (answer
[3] == '-') {
291 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
297 if ((sscanf (answer
, "%d", &i
) > 0) &&
298 (code
== i
) && (answer
[3] == ' '))
303 g_strlcpy (string_buf
, answer
, string_len
);
310 ftpfs_reconnect (struct vfs_class
*me
, struct vfs_s_super
*super
)
312 int sock
= ftpfs_open_socket (me
, super
);
314 char *cwdir
= SUP
.cwdir
;
318 if (ftpfs_login_server (me
, super
, SUP
.password
)){
321 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
323 return sock
== COMPLETE
;
331 ftpfs_command (struct vfs_class
*me
, struct vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
336 static int retry
= 0;
337 static int level
= 0; /* ftpfs_login_server() use ftpfs_command() */
340 cmdstr
= g_strdup_vprintf (fmt
, ap
);
343 cmdlen
= strlen (cmdstr
);
344 cmdstr
= g_realloc (cmdstr
, cmdlen
+ 3);
345 strcpy (cmdstr
+ cmdlen
, "\r\n");
348 if (MEDATA
->logfile
) {
349 if (strncmp (cmdstr
, "PASS ", 5) == 0) {
350 fputs ("PASS <Password not logged>\r\n", MEDATA
->logfile
);
352 fwrite (cmdstr
, cmdlen
, 1, MEDATA
->logfile
);
354 fflush (MEDATA
->logfile
);
358 enable_interrupt_key ();
359 status
= write (SUP
.sock
, cmdstr
, cmdlen
);
364 if (errno
== EPIPE
) { /* Remote server has closed connection */
367 status
= ftpfs_reconnect (me
, super
);
369 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
377 disable_interrupt_key ();
382 disable_interrupt_key ();
386 status
= ftpfs_get_reply (me
, SUP
.sock
,
387 (wait_reply
& WANT_STRING
) ? reply_str
: NULL
,
388 sizeof (reply_str
) - 1);
389 if ((wait_reply
& WANT_STRING
) && !retry
&& !level
&& code
== 421)
393 status
= ftpfs_reconnect (me
, super
);
395 if (status
&& (write (SUP
.sock
, cmdstr
, cmdlen
) > 0)) {
408 ftpfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*super
)
411 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
412 ftpfs_command(me
, super
, NONE
, "QUIT");
418 g_free (SUP
.password
);
421 /* some defines only used by ftpfs_changetype */
422 /* These two are valid values for the second parameter */
424 #define TYPE_BINARY 1
426 /* This one is only used to initialize bucket->isbinary, don't use it as
427 second parameter to ftpfs_changetype. */
428 #define TYPE_UNKNOWN -1
431 ftpfs_changetype (struct vfs_class
*me
, struct vfs_s_super
*super
, int binary
)
433 if (binary
!= SUP
.isbinary
) {
434 if (ftpfs_command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
436 SUP
.isbinary
= binary
;
441 /* This routine logs the user in */
443 ftpfs_login_server (struct vfs_class
*me
, struct vfs_s_super
*super
,
444 const char *netrcpass
)
448 char *name
; /* login user name */
450 char reply_string
[BUF_MEDIUM
];
452 SUP
.isbinary
= TYPE_UNKNOWN
;
454 if (SUP
.password
) /* explicit password */
455 op
= g_strdup (SUP
.password
);
456 else if (netrcpass
) /* password from netrc */
457 op
= g_strdup (netrcpass
);
458 else if (!strcmp (SUP
.user
, "anonymous") || !strcmp (SUP
.user
, "ftp")) {
459 if (!ftpfs_anonymous_passwd
) /* default anonymous password */
460 ftpfs_init_passwd ();
461 op
= g_strdup (ftpfs_anonymous_passwd
);
463 } else { /* ask user */
466 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
, " ",
468 op
= vfs_get_password (p
);
472 SUP
.password
= g_strdup (op
);
475 if (!anon
|| MEDATA
->logfile
)
478 pass
= g_strconcat ("-", op
, (char *) NULL
);
482 /* Proxy server accepts: username@host-we-want-to-connect */
485 g_strconcat (SUP
.user
, "@",
486 SUP
.host
[0] == '!' ? SUP
.host
+ 1 : SUP
.host
,
489 name
= g_strdup (SUP
.user
);
492 (me
, SUP
.sock
, reply_string
,
493 sizeof (reply_string
) - 1) == COMPLETE
) {
494 g_strup (reply_string
);
495 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
496 if (MEDATA
->logfile
) {
497 fprintf (MEDATA
->logfile
, "MC -- remote_is_amiga = %d\n",
498 SUP
.remote_is_amiga
);
499 fflush (MEDATA
->logfile
);
502 print_vfs_message (_("ftpfs: sending login name"));
504 switch (ftpfs_command (me
, super
, WAIT_REPLY
, "USER %s", name
)) {
506 print_vfs_message (_("ftpfs: sending user password"));
507 code
= ftpfs_command (me
, super
, WAIT_REPLY
, "PASS %s", pass
);
508 if (code
== CONTINUE
) {
511 p
= g_strdup_printf (_
512 ("FTP: Account required for user %s"),
514 op
= input_dialog (p
, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT
, "");
518 print_vfs_message (_("ftpfs: sending user account"));
520 ftpfs_command (me
, super
, WAIT_REPLY
, "ACCT %s", op
);
523 if (code
!= COMPLETE
)
528 print_vfs_message (_("ftpfs: logged in"));
529 wipe_password (pass
);
534 SUP
.failed_on_login
= 1;
536 wipe_password (SUP
.password
);
542 message (D_ERROR
, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "),
545 wipe_password (pass
);
550 static struct no_proxy_entry
{
556 ftpfs_load_no_proxy_list (void)
558 /* FixMe: shouldn't be hardcoded!!! */
559 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
560 struct no_proxy_entry
*np
, *current
= 0;
564 static char *mc_file
;
569 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
570 if (exist_file (mc_file
) &&
571 (npf
= fopen (mc_file
, "r"))) {
572 while (fgets (s
, sizeof (s
), npf
)) {
573 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
574 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
584 np
= g_new (struct no_proxy_entry
, 1);
585 np
->domain
= g_strdup (s
);
599 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
601 ftpfs_check_proxy (const char *host
)
603 struct no_proxy_entry
*npe
;
605 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
606 return 0; /* sanity check */
611 if (!ftpfs_always_use_proxy
)
614 if (!strchr (host
, '.'))
617 ftpfs_load_no_proxy_list ();
618 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
619 char *domain
= npe
->domain
;
621 if (domain
[0] == '.') {
622 int ld
= strlen (domain
);
623 int lh
= strlen (host
);
625 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
633 if (!g_strcasecmp (host
, domain
))
641 ftpfs_get_proxy_host_and_port (const char *proxy
, char **host
, int *port
)
646 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
653 ftpfs_open_socket (struct vfs_class
*me
, struct vfs_s_super
*super
)
655 struct sockaddr_in server_address
;
664 /* Use a proxy host? */
667 if (!host
|| !*host
){
668 print_vfs_message (_("ftpfs: Invalid host name."));
669 ftpfs_errno
= EINVAL
;
673 /* Hosts to connect to that start with a ! should use proxy */
675 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
679 enable_interrupt_key(); /* clear the interrupt flag */
681 /* Get host address */
682 memset ((char *) &server_address
, 0, sizeof (server_address
));
683 server_address
.sin_family
= AF_INET
;
684 server_address
.sin_addr
.s_addr
= inet_addr (host
);
685 if (server_address
.sin_addr
.s_addr
== INADDR_NONE
) {
686 hp
= gethostbyname (host
);
688 disable_interrupt_key();
689 print_vfs_message (_("ftpfs: Invalid host address."));
690 ftpfs_errno
= EINVAL
;
695 server_address
.sin_family
= hp
->h_addrtype
;
697 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
698 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
, 4);
701 server_address
.sin_port
= htons (port
);
704 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
705 disable_interrupt_key();
712 print_vfs_message (_("ftpfs: making connection to %s"), host
);
716 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
717 sizeof (server_address
)) < 0){
719 if (errno
== EINTR
&& got_interrupt ())
720 print_vfs_message (_("ftpfs: connection interrupted by user"));
722 print_vfs_message (_("ftpfs: connection to server failed: %s"),
723 unix_error_string(errno
));
724 disable_interrupt_key();
728 disable_interrupt_key();
733 ftpfs_open_archive_int (struct vfs_class
*me
, struct vfs_s_super
*super
)
735 int retry_seconds
, count_down
;
737 /* We do not want to use the passive if we are using proxies */
739 SUP
.use_passive_connection
= ftpfs_use_passive_connections_over_proxy
;
743 SUP
.failed_on_login
= 0;
745 SUP
.sock
= ftpfs_open_socket (me
, super
);
749 if (ftpfs_login_server (me
, super
, NULL
)) {
750 /* Logged in, no need to retry the connection */
753 if (SUP
.failed_on_login
){
754 /* Close only the socket descriptor */
759 if (ftpfs_retry_seconds
){
760 retry_seconds
= ftpfs_retry_seconds
;
761 enable_interrupt_key ();
762 for (count_down
= retry_seconds
; count_down
; count_down
--){
763 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
765 if (got_interrupt ()){
766 /* ftpfs_errno = E; */
767 disable_interrupt_key ();
771 disable_interrupt_key ();
774 } while (retry_seconds
);
776 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
778 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
783 ftpfs_open_archive (struct vfs_class
*me
, struct vfs_s_super
*super
,
784 const char *archive_name
, char *op
)
786 char *host
, *user
, *password
;
791 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
798 if (ftpfs_check_proxy (host
))
799 SUP
.proxy
= ftpfs_proxy_host
;
800 SUP
.password
= password
;
801 SUP
.use_passive_connection
= ftpfs_use_passive_connections
;
802 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
803 SUP
.isbinary
= TYPE_UNKNOWN
;
804 SUP
.remote_is_amiga
= 0;
805 super
->name
= g_strdup ("/");
807 vfs_s_new_inode (me
, super
,
808 vfs_s_default_stat (me
, S_IFDIR
| 0755));
810 return ftpfs_open_archive_int (me
, super
);
814 ftpfs_archive_same (struct vfs_class
*me
, struct vfs_s_super
*super
,
815 const char *archive_name
, char *op
, void *cookie
)
824 ftpfs_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, 0);
826 port
= ((strcmp (host
, SUP
.host
) == 0)
827 && (strcmp (user
, SUP
.user
) == 0) && (port
== SUP
.port
));
835 /* The returned directory should always contain a trailing slash */
837 ftpfs_get_current_directory (struct vfs_class
*me
, struct vfs_s_super
*super
)
839 char buf
[BUF_8K
], *bufp
, *bufq
;
841 if (ftpfs_command (me
, super
, NONE
, "PWD") == COMPLETE
&&
842 ftpfs_get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
844 for (bufq
= buf
; *bufq
; bufq
++)
851 if (*(bufq
- 1) != '/') {
856 return g_strdup (bufp
);
858 /* If the remote server is an Amiga a leading slash
859 might be missing. MC needs it because it is used
860 as separator between hostname and path internally. */
861 return g_strconcat( "/", bufp
, NULL
);
875 /* Setup Passive ftp connection, we use it for source routed connections */
877 ftpfs_setup_passive (struct vfs_class
*me
, struct vfs_s_super
*super
, int my_socket
, struct sockaddr_in
*sa
)
879 int xa
, xb
, xc
, xd
, xe
, xf
;
883 if (ftpfs_command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
886 /* Parse remote parameters */
887 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++)
891 if (!isdigit ((unsigned char) *c
))
893 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
895 n
[0] = (unsigned char) xa
;
896 n
[1] = (unsigned char) xb
;
897 n
[2] = (unsigned char) xc
;
898 n
[3] = (unsigned char) xd
;
899 n
[4] = (unsigned char) xe
;
900 n
[5] = (unsigned char) xf
;
902 memcpy (&(sa
->sin_addr
.s_addr
), (void *)n
, 4);
903 memcpy (&(sa
->sin_port
), (void *)&n
[4], 2);
904 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
910 ftpfs_initconn (struct vfs_class
*me
, struct vfs_s_super
*super
)
912 struct sockaddr_in data_addr
;
914 socklen_t len
= sizeof(data_addr
);
917 pe
= getprotobyname ("tcp");
921 if (getsockname (SUP
.sock
, (struct sockaddr
*) &data_addr
, &len
) == -1)
923 data_addr
.sin_port
= 0;
925 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
929 if (SUP
.use_passive_connection
) {
930 if (ftpfs_setup_passive (me
, super
, data
, &data_addr
))
933 SUP
.use_passive_connection
= 0;
934 print_vfs_message (_("ftpfs: could not setup passive mode"));
936 /* data or data_addr may be damaged by ftpfs_setup_passive */
941 /* If passive setup fails, fallback to active connections */
942 /* Active FTP connection */
943 if ((bind (data
, (struct sockaddr
*)&data_addr
, len
) == 0) &&
944 (getsockname (data
, (struct sockaddr
*) &data_addr
, &len
) == 0) &&
945 (listen (data
, 1) == 0))
947 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
948 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
950 if (ftpfs_command (me
, super
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
951 a
[2], a
[3], p
[0], p
[1]) == COMPLETE
)
960 ftpfs_open_data_connection (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *cmd
,
961 const char *remote
, int isbinary
, int reget
)
963 struct sockaddr_in from
;
965 socklen_t fromlen
= sizeof(from
);
967 if ((s
= ftpfs_initconn (me
, super
)) == -1)
969 if (ftpfs_changetype (me
, super
, isbinary
) == -1)
972 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
977 char *remote_path
= ftpfs_translate_path (me
, super
, remote
);
978 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
979 /* WarFtpD can't STORE //filename */
980 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
981 g_free (remote_path
);
983 j
= ftpfs_command (me
, super
, WAIT_REPLY
, "%s", cmd
);
986 enable_interrupt_key();
987 if (SUP
.use_passive_connection
)
990 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
998 disable_interrupt_key();
1002 #define ABORT_TIMEOUT 5
1004 ftpfs_linear_abort (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1006 struct vfs_s_super
*super
= FH_SUPER
;
1007 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1010 int dsock
= FH_SOCK
;
1012 SUP
.ctl_connection_busy
= 0;
1014 print_vfs_message (_("ftpfs: aborting transfer."));
1015 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
1016 print_vfs_message (_("ftpfs: abort error: %s"),
1017 unix_error_string (errno
));
1023 if (ftpfs_command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1024 print_vfs_message (_("ftpfs: abort failed"));
1031 FD_SET (dsock
, &mask
);
1032 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1033 struct timeval start_tim
, tim
;
1034 gettimeofday (&start_tim
, NULL
);
1035 /* flush the remaining data */
1036 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1037 gettimeofday (&tim
, NULL
);
1038 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1039 /* server keeps sending, drop the connection and ftpfs_reconnect */
1041 ftpfs_reconnect (me
, super
);
1048 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1049 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1054 resolve_symlink_without_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1056 struct linklist
*flist
;
1057 struct direntry
*fe
, *fel
;
1058 char tmp
[MC_MAXPATHLEN
];
1061 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1062 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1063 /* flist->data->l_stat is alread initialized with 0 */
1065 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1066 if (fel
->linkname
[0] == '/') {
1067 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1069 strcpy (tmp
, fel
->linkname
);
1071 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1073 strcpy (tmp
, dir
->remote_path
);
1076 strcat (tmp
+ 1, fel
->linkname
);
1078 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1079 canonicalize_pathname (tmp
);
1080 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1082 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1083 /* Symlink points to link which isn't resolved, yet. */
1084 if (fe
->linkname
[0] == '/') {
1085 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1087 strcpy (tmp
, fe
->linkname
);
1089 /* at this point tmp looks always like this
1090 /directory/filename, i.e. no need to check
1091 strrchr's return value */
1092 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1093 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1095 strcat (tmp
, fe
->linkname
);
1099 fel
->l_stat
= g_new (struct stat
, 1);
1100 if ( S_ISLNK (fe
->s
.st_mode
))
1101 *fel
->l_stat
= *fe
->l_stat
;
1103 *fel
->l_stat
= fe
->s
;
1104 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1111 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1115 resolve_symlink_with_ls_options(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1117 char buffer
[2048] = "", *filename
;
1121 struct linklist
*flist
;
1122 struct direntry
*fe
;
1123 int switch_method
= 0;
1125 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1126 if (strchr (dir
->remote_path
, ' ')) {
1127 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1128 print_vfs_message(_("ftpfs: CWD failed."));
1131 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1134 sock
= ftpfs_open_data_connection (bucket
, "LIST -lLa",
1135 dir
->remote_path
, TYPE_ASCII
, 0);
1138 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1142 fp
= fdopen(sock
, "r");
1145 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1148 enable_interrupt_key();
1149 flist
= dir
->file_list
->next
;
1152 if (flist
== dir
->file_list
)
1155 flist
= flist
->next
;
1156 } while (!S_ISLNK(fe
->s
.st_mode
));
1158 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1160 if (MEDATA
->logfile
){
1161 fputs (buffer
, MEDATA
->logfile
);
1162 fflush (MEDATA
->logfile
);
1164 vfs_die("This code should be commented out\n");
1165 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1166 int r
= strcmp(fe
->name
, filename
);
1169 if (S_ISLNK (s
.st_mode
)) {
1170 /* This server doesn't understand LIST -lLa */
1174 fe
->l_stat
= g_new (struct stat
, 1);
1175 if (fe
->l_stat
== NULL
)
1178 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1187 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1188 disable_interrupt_key();
1190 ftpfs_get_reply(me
, SUP
.sock
, NULL
, 0);
1194 resolve_symlink(struct vfs_class
*me
, struct vfs_s_super
*super
, struct vfs_s_inode
*dir
)
1196 print_vfs_message(_("Resolving symlink..."));
1198 if (SUP
.strict_rfc959_list_cmd
)
1199 resolve_symlink_without_ls_options(me
, super
, dir
);
1201 resolve_symlink_with_ls_options(me
, super
, dir
);
1206 ftpfs_dir_load (struct vfs_class
*me
, struct vfs_s_inode
*dir
, char *remote_path
)
1208 struct vfs_s_entry
*ent
;
1209 struct vfs_s_super
*super
= dir
->super
;
1210 int sock
, num_entries
= 0;
1211 char buffer
[BUF_8K
];
1214 cd_first
= ftpfs_first_cd_then_ls
|| (SUP
.strict
== RFC_STRICT
)
1215 || (strchr (remote_path
, ' ') != NULL
);
1218 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1221 RFC_STRICT
? _("(strict rfc959)") : "",
1222 cd_first
? _("(chdir first)") : "");
1225 if (ftpfs_chdir_internal (me
, super
, remote_path
) != COMPLETE
) {
1226 ftpfs_errno
= ENOENT
;
1227 print_vfs_message (_("ftpfs: CWD failed."));
1232 gettimeofday (&dir
->timestamp
, NULL
);
1233 dir
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1235 if (SUP
.strict
== RFC_STRICT
)
1236 sock
= ftpfs_open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1238 /* Dirty hack to avoid autoprepending / to . */
1239 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1241 ftpfs_open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1243 /* Trailing "/." is necessary if remote_path is a symlink */
1244 char *path
= concat_dir_and_file (remote_path
, ".");
1246 ftpfs_open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
,
1254 /* Clear the interrupt flag */
1255 enable_interrupt_key ();
1260 vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
),
1266 me
->verrno
= ECONNRESET
;
1268 disable_interrupt_key ();
1269 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1270 print_vfs_message (_("%s: failure"), me
->name
);
1274 if (MEDATA
->logfile
) {
1275 fputs (buffer
, MEDATA
->logfile
);
1276 fputs ("\n", MEDATA
->logfile
);
1277 fflush (MEDATA
->logfile
);
1280 ent
= vfs_s_generate_entry (me
, NULL
, dir
, 0);
1281 i
= ent
->ino
->st
.st_nlink
;
1282 if (!vfs_parse_ls_lga
1283 (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1284 vfs_s_free_entry (me
, ent
);
1287 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1289 vfs_s_insert_entry (me
, dir
, ent
);
1293 me
->verrno
= E_REMOTE
;
1294 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1297 if (num_entries
== 0 && cd_first
== 0) {
1298 /* The LIST command may produce an empty output. In such scenario
1299 it is not clear whether this is caused by `remote_path' being
1300 a non-existent path or for some other reason (listing emtpy
1301 directory without the -a option, non-readable directory, etc.).
1303 Since `dir_load' is a crucial method, when it comes to determine
1304 whether a given path is a _directory_, the code must try its best
1305 to determine the type of `remote_path'. The only reliable way to
1306 achieve this is trough issuing a CWD command. */
1312 if (SUP
.strict
== RFC_AUTODETECT
)
1313 SUP
.strict
= RFC_DARING
;
1315 print_vfs_message (_("%s: done."), me
->name
);
1319 if (SUP
.strict
== RFC_AUTODETECT
) {
1320 /* It's our first attempt to get a directory listing from this
1321 server (UNIX style LIST command) */
1322 SUP
.strict
= RFC_STRICT
;
1323 /* I hate goto, but recursive call needs another 8K on stack */
1324 /* return ftpfs_dir_load (me, dir, remote_path); */
1328 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1329 ERRNOR (EACCES
, -1);
1333 ftpfs_file_store (struct vfs_class
*me
, struct vfs_s_fh
*fh
, char *name
,
1336 int h
, sock
, n_read
, n_written
;
1338 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1346 struct vfs_s_super
*super
= FH_SUPER
;
1348 h
= open (localname
, O_RDONLY
);
1352 ftpfs_open_data_connection (me
, super
,
1353 fh
->u
.ftp
.append
? "APPE" : "STOR", name
,
1355 if (sock
< 0 || fstat (h
, &s
) == -1) {
1359 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1362 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof (li
));
1364 setsockopt (sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1368 enable_interrupt_key ();
1370 while ((n_read
= read (h
, buffer
, sizeof (buffer
))) == -1) {
1371 if (errno
== EINTR
) {
1372 if (got_interrupt ()) {
1373 ftpfs_errno
= EINTR
;
1378 ftpfs_errno
= errno
;
1385 while ((n_written
= write (sock
, w_buf
, n_read
)) != n_read
) {
1386 if (n_written
== -1) {
1387 if (errno
== EINTR
&& !got_interrupt ()) {
1390 ftpfs_errno
= errno
;
1394 n_read
-= n_written
;
1396 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1397 (unsigned long) n_stored
, (unsigned long) s
.st_size
);
1399 disable_interrupt_key ();
1402 if (ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1406 disable_interrupt_key ();
1409 ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0);
1414 ftpfs_linear_start (struct vfs_class
*me
, struct vfs_s_fh
*fh
, off_t offset
)
1416 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1420 FH_SOCK
= ftpfs_open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1424 fh
->linear
= LS_LINEAR_OPEN
;
1425 FH_SUPER
->u
.ftp
.ctl_connection_busy
= 1;
1426 fh
->u
.ftp
.append
= 0;
1431 ftpfs_linear_read (struct vfs_class
*me
, struct vfs_s_fh
*fh
, void *buf
, int len
)
1434 struct vfs_s_super
*super
= FH_SUPER
;
1436 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1437 if ((errno
== EINTR
) && !got_interrupt())
1443 ftpfs_linear_abort(me
, fh
);
1446 SUP
.ctl_connection_busy
= 0;
1449 if ((ftpfs_get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1450 ERRNOR (E_REMOTE
, -1);
1457 ftpfs_linear_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1460 ftpfs_linear_abort(me
, fh
);
1463 static int ftpfs_ctl (void *fh
, int ctlop
, void *arg
)
1468 case VFS_CTL_IS_NOTREADY
:
1473 vfs_die ("You may not do this");
1474 if (FH
->linear
== LS_LINEAR_CLOSED
|| FH
->linear
== LS_LINEAR_PREOPEN
)
1477 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1478 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1488 ftpfs_send_command(struct vfs_class
*me
, const char *filename
, const char *cmd
, int flags
)
1491 char *p
, *mpath
= g_strdup(filename
);
1492 struct vfs_s_super
*super
;
1494 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1496 if (!(rpath
= vfs_s_get_path_mangle(me
, mpath
, &super
, 0))) {
1500 p
= ftpfs_translate_path (me
, super
, rpath
);
1501 r
= ftpfs_command (me
, super
, WAIT_REPLY
, cmd
, p
);
1503 vfs_stamp_create (&vfs_ftpfs_ops
, super
);
1504 if (flags
& OPT_IGNORE_ERROR
)
1506 if (r
!= COMPLETE
) {
1511 if (flush_directory_cache
)
1512 vfs_s_invalidate(me
, super
);
1517 /* This routine is called as the last step in load_setup */
1519 ftpfs_init_passwd(void)
1521 ftpfs_anonymous_passwd
= load_anon_passwd ();
1522 if (ftpfs_anonymous_passwd
)
1525 /* If there is no anonymous ftp password specified
1526 * then we'll just use anonymous@
1527 * We don't send any other thing because:
1528 * - We want to remain anonymous
1529 * - We want to stop SPAM
1530 * - We don't want to let ftp sites to discriminate by the user,
1533 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1536 static int ftpfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1538 char buf
[BUF_SMALL
];
1540 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1541 return ftpfs_send_command(me
, path
, buf
, OPT_FLUSH
);
1544 static int ftpfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1547 ftpfs_errno
= EPERM
;
1550 /* Everyone knows it is not possible to chown remotely, so why bother them.
1551 If someone's root, then copy/move will always try to chown it... */
1560 static int ftpfs_unlink (struct vfs_class
*me
, const char *path
)
1562 return ftpfs_send_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1565 /* Return 1 if path is the same directory as the one we are in now */
1567 ftpfs_is_same_dir (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *path
)
1573 if (strcmp (path
, SUP
.cwdir
) == 0)
1579 ftpfs_chdir_internal (struct vfs_class
*me
, struct vfs_s_super
*super
, const char *remote_path
)
1584 if (!SUP
.cwd_deferred
&& ftpfs_is_same_dir (me
, super
, remote_path
))
1587 p
= ftpfs_translate_path (me
, super
, remote_path
);
1588 r
= ftpfs_command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1591 if (r
!= COMPLETE
) {
1595 SUP
.cwdir
= g_strdup (remote_path
);
1596 SUP
.cwd_deferred
= 0;
1601 static int ftpfs_rename (struct vfs_class
*me
, const char *path1
, const char *path2
)
1603 ftpfs_send_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1604 return ftpfs_send_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1607 static int ftpfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1609 (void) mode
; /* FIXME: should be used */
1611 return ftpfs_send_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1614 static int ftpfs_rmdir (struct vfs_class
*me
, const char *path
)
1616 return ftpfs_send_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1620 ftpfs_fh_open (struct vfs_class
*me
, struct vfs_s_fh
*fh
, int flags
,
1625 fh
->u
.ftp
.append
= 0;
1626 /* File will be written only, so no need to retrieve it from ftp server */
1627 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
| O_RDWR
))) {
1628 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1635 /* ftpfs_linear_start() called, so data will be written
1636 * to local temporary file and stored to ftp server
1637 * by vfs_s_close later
1639 if (FH_SUPER
->u
.ftp
.ctl_connection_busy
) {
1640 if (!fh
->ino
->localname
) {
1641 int handle
= vfs_mkstemps (&fh
->ino
->localname
, me
->name
,
1642 fh
->ino
->ent
->name
);
1646 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1650 name
= vfs_s_fullpath (me
, fh
->ino
);
1654 ftpfs_open_data_connection (me
, fh
->ino
->super
,
1655 (flags
& O_APPEND
) ? "APPE" :
1656 "STOR", name
, TYPE_BINARY
, 0);
1661 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1665 setsockopt (fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof (li
));
1667 if (fh
->ino
->localname
) {
1668 unlink (fh
->ino
->localname
);
1669 g_free (fh
->ino
->localname
);
1670 fh
->ino
->localname
= NULL
;
1675 if (!fh
->ino
->localname
)
1676 if (vfs_s_retrieve_file (me
, fh
->ino
) == -1)
1678 if (!fh
->ino
->localname
)
1679 vfs_die ("retrieve_file failed to fill in localname");
1683 static int ftpfs_fh_close (struct vfs_class
*me
, struct vfs_s_fh
*fh
)
1685 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1688 /* File is stored to destination already, so
1689 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1692 if (ftpfs_get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1694 vfs_s_invalidate (me
, FH_SUPER
);
1700 ftpfs_done (struct vfs_class
*me
)
1702 struct no_proxy_entry
*np
;
1707 np
= no_proxy
->next
;
1708 g_free (no_proxy
->domain
);
1712 g_free (ftpfs_anonymous_passwd
);
1713 g_free (ftpfs_proxy_host
);
1717 ftpfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
1719 struct vfs_s_super
*super
= MEDATA
->supers
;
1723 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, (char *) NULL
);
1726 super
= super
->next
;
1730 static char buffer
[BUF_MEDIUM
];
1732 static const char *netrcp
;
1734 /* This should match the keywords[] array below */
1747 static keyword_t
ftpfs_netrc_next (void)
1751 static const char *const keywords
[] = { "default", "machine",
1752 "login", "password", "passwd", "account", "macdef", NULL
1757 netrcp
= skip_separators (netrcp
);
1758 if (*netrcp
!= '\n')
1765 if (*netrcp
== '"') {
1766 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1767 if (*netrcp
== '\\')
1772 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1773 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1774 if (*netrcp
== '\\')
1784 while (keywords
[i
- 1]) {
1785 if (!strcmp (keywords
[i
- 1], buffer
))
1791 return NETRC_UNKNOWN
;
1794 static int ftpfs_netrc_bad_mode (const char *netrcname
)
1796 static int be_angry
= 1;
1799 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1801 message (D_ERROR
, MSG_ERROR
,
1802 _("~/.netrc file has incorrect mode.\n"
1803 "Remove password or correct mode."));
1811 /* Scan .netrc until we find matching "machine" or "default"
1812 * domain is used for additional matching
1813 * No search is done after "default" in compliance with "man netrc"
1814 * Return 0 if found, -1 otherwise */
1815 static int ftpfs_find_machine (const char *host
, const char *domain
)
1819 while ((keyword
= ftpfs_netrc_next ()) != NETRC_NONE
) {
1820 if (keyword
== NETRC_DEFAULT
)
1823 if (keyword
== NETRC_MACDEF
) {
1824 /* Scan for an empty line, which concludes "macdef" */
1826 while (*netrcp
&& *netrcp
!= '\n')
1828 if (*netrcp
!= '\n')
1831 } while (*netrcp
&& *netrcp
!= '\n');
1835 if (keyword
!= NETRC_MACHINE
)
1838 /* Take machine name */
1839 if (ftpfs_netrc_next () == NETRC_NONE
)
1842 if (g_strcasecmp (host
, buffer
)) {
1843 /* Try adding our domain to short names in .netrc */
1844 const char *host_domain
= strchr (host
, '.');
1848 /* Compare domain part */
1849 if (g_strcasecmp (host_domain
, domain
))
1852 /* Compare local part */
1853 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1864 /* Extract login and password from .netrc for the host.
1866 * Returns 0 for success, -1 for error */
1867 static int ftpfs_netrc_lookup (const char *host
, char **login
, char **pass
)
1870 char *tmp_pass
= NULL
;
1871 char hostname
[MAXHOSTNAMELEN
];
1874 static struct rupcache
{
1875 struct rupcache
*next
;
1879 } *rup_cache
= NULL
, *rupp
;
1881 /* Initialize *login and *pass */
1888 /* Look up in the cache first */
1889 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
1890 if (!strcmp (host
, rupp
->host
)) {
1892 *login
= g_strdup (rupp
->login
);
1893 if (pass
&& rupp
->pass
)
1894 *pass
= g_strdup (rupp
->pass
);
1899 /* Load current .netrc */
1900 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
1901 netrcp
= netrc
= load_file (netrcname
);
1902 if (netrc
== NULL
) {
1907 /* Find our own domain name */
1908 if (gethostname (hostname
, sizeof (hostname
)) < 0)
1910 if (!(domain
= strchr (hostname
, '.')))
1913 /* Scan for "default" and matching "machine" keywords */
1914 ftpfs_find_machine (host
, domain
);
1916 /* Scan for keywords following "default" and "machine" */
1919 keyword
= ftpfs_netrc_next ();
1923 if (ftpfs_netrc_next () == NETRC_NONE
) {
1928 /* We have another name already - should not happen */
1934 /* We have login name now */
1935 *login
= g_strdup (buffer
);
1938 case NETRC_PASSWORD
:
1940 if (ftpfs_netrc_next () == NETRC_NONE
) {
1945 /* Ignore unsafe passwords */
1946 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
1947 && ftpfs_netrc_bad_mode (netrcname
)) {
1952 /* Remember password. pass may be NULL, so use tmp_pass */
1953 if (tmp_pass
== NULL
)
1954 tmp_pass
= g_strdup (buffer
);
1958 /* "account" is followed by a token which we ignore */
1959 if (ftpfs_netrc_next () == NETRC_NONE
) {
1964 /* Ignore account, but warn user anyways */
1965 ftpfs_netrc_bad_mode (netrcname
);
1969 /* Unexpected keyword or end of file */
1981 rupp
= g_new (struct rupcache
, 1);
1982 rupp
->host
= g_strdup (host
);
1983 rupp
->login
= rupp
->pass
= 0;
1985 if (*login
!= NULL
) {
1986 rupp
->login
= g_strdup (*login
);
1988 if (tmp_pass
!= NULL
)
1989 rupp
->pass
= g_strdup (tmp_pass
);
1990 rupp
->next
= rup_cache
;
2002 static struct vfs_s_subclass ftpfs_subclass
;
2004 ftpfs_subclass
.flags
= VFS_S_REMOTE
;
2005 ftpfs_subclass
.archive_same
= ftpfs_archive_same
;
2006 ftpfs_subclass
.open_archive
= ftpfs_open_archive
;
2007 ftpfs_subclass
.free_archive
= ftpfs_free_archive
;
2008 ftpfs_subclass
.fh_open
= ftpfs_fh_open
;
2009 ftpfs_subclass
.fh_close
= ftpfs_fh_close
;
2010 ftpfs_subclass
.dir_load
= ftpfs_dir_load
;
2011 ftpfs_subclass
.file_store
= ftpfs_file_store
;
2012 ftpfs_subclass
.linear_start
= ftpfs_linear_start
;
2013 ftpfs_subclass
.linear_read
= ftpfs_linear_read
;
2014 ftpfs_subclass
.linear_close
= ftpfs_linear_close
;
2016 vfs_s_init_class (&vfs_ftpfs_ops
, &ftpfs_subclass
);
2017 vfs_ftpfs_ops
.name
= "ftpfs";
2018 vfs_ftpfs_ops
.flags
= VFSF_NOLINKS
;
2019 vfs_ftpfs_ops
.prefix
= "ftp:";
2020 vfs_ftpfs_ops
.done
= &ftpfs_done
;
2021 vfs_ftpfs_ops
.fill_names
= ftpfs_fill_names
;
2022 vfs_ftpfs_ops
.chmod
= ftpfs_chmod
;
2023 vfs_ftpfs_ops
.chown
= ftpfs_chown
;
2024 vfs_ftpfs_ops
.unlink
= ftpfs_unlink
;
2025 vfs_ftpfs_ops
.rename
= ftpfs_rename
;
2026 vfs_ftpfs_ops
.mkdir
= ftpfs_mkdir
;
2027 vfs_ftpfs_ops
.rmdir
= ftpfs_rmdir
;
2028 vfs_ftpfs_ops
.ctl
= ftpfs_ctl
;
2029 vfs_register_class (&vfs_ftpfs_ops
);