1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Ching Hui
6 1995, 1996, 1997 Miguel de Icaza
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public License
12 as published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
26 - make it more robust - all the connects etc. should handle EADDRINUSE and
27 ERETRY (have I spelled these names correctly?)
28 - make the user able to flush a connection - all the caches will get empty
29 etc., (tarfs as well), we should give there a user selectable timeout
30 and assign a key sequence.
31 - use hash table instead of linklist to cache ftpfs directory.
36 * NOTE: Usage of tildes is deprecated, consider:
37 * cd /#ftp:pavel@hobit
39 * And now: what do I want to do? Do I want to go to /home/pavel or to
40 * /#ftp:hobit/home/pavel? I think first has better sense...
43 int f = !strcmp( remote_path, "/~" );
44 if (f || !strncmp( remote_path, "/~/", 3 )) {
46 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
55 /* Namespace pollution: horrible */
58 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
59 #include <netdb.h> /* struct hostent */
60 #include <sys/socket.h> /* AF_INET */
61 #include <netinet/in.h> /* struct in_addr */
62 #ifdef HAVE_SETSOCKOPT
63 # include <netinet/ip.h> /* IP options */
65 #ifdef HAVE_ARPA_INET_H
66 #include <arpa/inet.h>
69 #include <arpa/telnet.h>
70 #include <sys/param.h>
74 #include "xdirentry.h"
77 #include "../src/dialog.h"
78 #include "../src/setup.h" /* for load_anon_passwd */
80 #ifndef MAXHOSTNAMELEN
81 # define MAXHOSTNAMELEN 64
84 #define UPLOAD_ZERO_LENGTH_FILE
85 #define SUP super->u.ftp
86 #define FH_SOCK fh->u.ftp.sock
91 /* Delay to retry a connection */
92 int ftpfs_retry_seconds
= 30;
94 /* Method to use to connect to ftp sites */
95 int ftpfs_use_passive_connections
= 1;
97 /* Method used to get directory listings:
98 * 1: try 'LIST -la <path>', if it fails
99 * fall back to CWD <path>; LIST
100 * 0: always use CWD <path>; LIST
102 int ftpfs_use_unix_list_options
= 1;
104 /* First "CWD <path>", then "LIST -la ." */
105 int ftpfs_first_cd_then_ls
;
107 /* Use the ~/.netrc */
110 extern char *home_dir
;
112 /* Anonymous setup */
113 char *ftpfs_anonymous_passwd
= NULL
;
114 int ftpfs_directory_timeout
= 900;
117 char *ftpfs_proxy_host
= NULL
;
119 /* wether we have to use proxy by default? */
120 int ftpfs_always_use_proxy
;
122 /* source routing host */
123 extern int source_route
;
125 /* Where we store the transactions */
126 static FILE *logfile
= NULL
;
128 /* If true, the directory cache is forced to reload */
129 static int force_expiration
= 0;
131 #ifdef FIXME_LATER_ALIGATOR
132 static struct linklist
*connections_list
;
135 /* command wait_flag: */
137 #define WAIT_REPLY 0x01
138 #define WANT_STRING 0x02
139 static char reply_str
[80];
141 /* char *translate_path (struct ftpfs_connection *bucket, char *remote_path)
142 Translate a Unix path, i.e. MC's internal path representation (e.g.
143 /somedir/somefile) to a path valid for the remote server. Every path
144 transfered to the remote server has to be mangled by this function
145 right prior to sending it.
146 Currently only Amiga ftp servers are handled in a special manner.
148 When the remote server is an amiga:
149 a) strip leading slash if necesarry
150 b) replace first occurance of ":/" with ":"
151 c) strip trailing "/."
154 static char *ftpfs_get_current_directory (vfs
*me
, vfs_s_super
*super
);
155 static int ftpfs_chdir_internal (vfs
*me
, vfs_s_super
*super
, char *remote_path
);
156 static int command (vfs
*me
, vfs_s_super
*super
, int wait_reply
, char *fmt
, ...)
157 __attribute__ ((format (printf
, 4, 5)));
158 static int ftpfs_open_socket (vfs
*me
, vfs_s_super
*super
);
159 static int login_server (vfs
*me
, vfs_s_super
*super
, const char *netrcpass
);
160 static int lookup_netrc (const char *host
, char **login
, char **pass
);
163 translate_path (vfs
*me
, vfs_s_super
*super
, const char *remote_path
)
165 if (!SUP
.remote_is_amiga
)
166 return g_strdup (remote_path
);
171 fprintf (logfile
, "MC -- translate_path: %s\n", remote_path
);
175 /* strip leading slash(es) */
176 while (*remote_path
== '/')
180 * Don't change "/" into "", e.g. "CWD " would be
183 if (*remote_path
== '\0')
184 return g_strdup (".");
186 ret
= g_strdup (remote_path
);
188 /* replace first occurance of ":/" with ":" */
189 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
190 strcpy (p
+ 1, p
+ 2);
192 /* strip trailing "/." */
193 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
199 /* Extract the hostname and username from the path */
202 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
203 * ftp://sunsite.unc.edu/pub/linux
204 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
205 * ftp://tsx-11.mit.edu:8192/
206 * ftp://joe@foo.edu:11321/private
207 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
212 #define FTP_COMMAND_PORT 21
215 ftp_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
219 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
223 /* Look up user and password in netrc */
225 lookup_netrc (*host
, user
, pass
);
227 *user
= g_strdup ("anonymous");
230 /* Look up password in netrc for known user */
231 if (use_netrc
&& *user
&& pass
&& !*pass
) {
234 lookup_netrc (*host
, &new_user
, pass
);
236 /* If user is different, remove password */
237 if (new_user
&& strcmp (*user
, new_user
)) {
249 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
251 get_reply (vfs
*me
, int sock
, char *string_buf
, int string_len
)
257 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
263 switch (sscanf(answer
, "%d", &code
)){
266 strncpy (string_buf
, answer
, string_len
- 1);
267 *(string_buf
+ string_len
- 1) = 0;
272 if (answer
[3] == '-') {
274 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
280 if ((sscanf (answer
, "%d", &i
) > 0) &&
281 (code
== i
) && (answer
[3] == ' '))
286 strncpy (string_buf
, answer
, string_len
- 1);
287 *(string_buf
+ string_len
- 1) = 0;
295 reconnect (vfs
*me
, vfs_s_super
*super
)
297 int sock
= ftpfs_open_socket (me
, super
);
299 char *cwdir
= SUP
.cwdir
;
303 if (login_server (me
, super
, SUP
.password
)){
306 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
308 return sock
== COMPLETE
;
316 command (vfs
*me
, vfs_s_super
*super
, int wait_reply
, char *fmt
, ...)
324 fmt_str
= g_strdup_vprintf (fmt
, ap
);
327 status
= strlen (fmt_str
);
328 str
= g_realloc (fmt_str
, status
+ 3);
329 strcpy (str
+ status
, "\r\n");
332 if (strncmp (str
, "PASS ", 5) == 0){
333 fputs ("PASS <Password not logged>\r\n", logfile
);
335 fwrite (str
, status
+ 2, 1, logfile
);
341 enable_interrupt_key ();
342 status
= write (SUP
.sock
, str
, status
+ 2);
347 if (errno
== EPIPE
){ /* Remote server has closed connection */
348 static int level
= 0; /* login_server() use command() */
351 status
= reconnect (me
, super
);
353 if (status
&& write (SUP
.sock
, str
, status
+ 2) > 0)
359 disable_interrupt_key ();
364 disable_interrupt_key ();
367 return get_reply (me
, sock
, (wait_reply
& WANT_STRING
) ? reply_str
: NULL
, sizeof (reply_str
)-1);
372 free_archive (vfs
*me
, vfs_s_super
*super
)
375 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
376 command(me
, super
, NONE
, "QUIT");
382 g_free (SUP
.password
);
385 /* some defines only used by changetype */
386 /* These two are valid values for the second parameter */
388 #define TYPE_BINARY 1
390 /* This one is only used to initialize bucket->isbinary, don't use it as
391 second parameter to changetype. */
392 #define TYPE_UNKNOWN -1
395 changetype (vfs
*me
, vfs_s_super
*super
, int binary
)
397 if (binary
!= SUP
.isbinary
) {
398 if (command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
400 SUP
.isbinary
= binary
;
405 /* This routine logs the user in */
407 login_server (vfs
*me
, vfs_s_super
*super
, const char *netrcpass
)
411 char *name
; /* login user name */
413 char reply_string
[BUF_MEDIUM
];
415 SUP
.isbinary
= TYPE_UNKNOWN
;
417 op
= g_strdup (netrcpass
);
419 if (!strcmp (SUP
.user
, "anonymous") ||
420 !strcmp (SUP
.user
, "ftp")) {
421 if (!ftpfs_anonymous_passwd
)
423 op
= g_strdup (ftpfs_anonymous_passwd
);
429 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
,
431 op
= vfs_get_password (p
);
435 SUP
.password
= g_strdup (op
);
437 op
= g_strdup (SUP
.password
);
441 if (!anon
|| logfile
)
444 pass
= g_strconcat ("-", op
, NULL
);
448 /* Proxy server accepts: username@host-we-want-to-connect*/
450 name
= g_strconcat (SUP
.user
, "@",
451 SUP
.host
[0] == '!' ? SUP
.host
+1 : SUP
.host
, NULL
);
453 name
= g_strdup (SUP
.user
);
455 if (get_reply (me
, SUP
.sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
) {
456 g_strup (reply_string
);
457 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
459 fprintf (logfile
, "MC -- remote_is_amiga = %d\n", SUP
.remote_is_amiga
);
463 print_vfs_message (_("ftpfs: sending login name"));
464 code
= command (me
, super
, WAIT_REPLY
, "USER %s", name
);
468 print_vfs_message (_("ftpfs: sending user password"));
469 if (command (me
, super
, WAIT_REPLY
, "PASS %s", pass
) != COMPLETE
)
473 print_vfs_message (_("ftpfs: logged in"));
474 wipe_password (pass
);
479 SUP
.failed_on_login
= 1;
482 wipe_password (SUP
.password
);
488 message_2s (1, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "), SUP
.user
);
490 wipe_password (pass
);
495 #ifdef HAVE_SETSOCKOPT
497 setup_source_route (int socket
, int dest
)
504 memset (buffer
, 0, sizeof (buffer
));
507 *ptr
++ = 4; /* pointer */
510 memcpy (ptr
, (char *) &source_route
, sizeof (int));
513 /* Second hop (ie, final destination) */
514 memcpy (ptr
, (char *) &dest
, sizeof (int));
516 while ((ptr
- buffer
) & 3)
518 if (setsockopt (socket
, IPPROTO_IP
, IP_OPTIONS
,
519 buffer
, ptr
- buffer
) < 0)
520 message_2s (1, MSG_ERROR
, _(" Could not set source routing (%s)"), unix_error_string (errno
));
523 #define setup_source_route(x,y)
526 static struct no_proxy_entry
{
532 load_no_proxy_list (void)
534 /* FixMe: shouldn't be hardcoded!!! */
535 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
536 struct no_proxy_entry
*np
, *current
= 0;
540 static char *mc_file
;
545 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
546 if (exist_file (mc_file
) &&
547 (npf
= fopen (mc_file
, "r"))) {
548 while (fgets (s
, sizeof(s
), npf
) || !(feof (npf
) || ferror (npf
))) {
549 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
550 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
560 np
= g_new (struct no_proxy_entry
, 1);
561 np
->domain
= g_strdup (s
);
575 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
577 ftpfs_check_proxy (const char *host
)
579 struct no_proxy_entry
*npe
;
581 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
582 return 0; /* sanity check */
587 if (!ftpfs_always_use_proxy
)
590 if (!strchr (host
, '.'))
593 load_no_proxy_list ();
594 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
595 char *domain
= npe
->domain
;
597 if (domain
[0] == '.') {
598 int ld
= strlen (domain
);
599 int lh
= strlen (host
);
601 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
609 if (!g_strcasecmp (host
, domain
))
617 ftpfs_get_proxy_host_and_port (char *proxy
, char **host
, int *port
)
622 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
629 ftpfs_open_socket (vfs
*me
, vfs_s_super
*super
)
631 struct sockaddr_in server_address
;
638 /* Use a proxy host? */
641 if (!host
|| !*host
){
642 print_vfs_message (_("ftpfs: Invalid host name."));
647 /* Hosts to connect to that start with a ! should use proxy */
649 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
653 /* Get host address */
654 memset ((char *) &server_address
, 0, sizeof (server_address
));
655 server_address
.sin_family
= AF_INET
;
656 server_address
.sin_addr
.s_addr
= inet_addr (host
);
657 if (server_address
.sin_addr
.s_addr
!= -1)
658 server_address
.sin_family
= AF_INET
;
660 hp
= gethostbyname (host
);
662 print_vfs_message (_("ftpfs: Invalid host address."));
668 server_address
.sin_family
= hp
->h_addrtype
;
670 /* We copy only 4 bytes, we can not trust hp->h_length, as it comes from the DNS */
671 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
, 4);
674 server_address
.sin_port
= htons (port
);
677 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
683 setup_source_route (my_socket
, server_address
.sin_addr
.s_addr
);
685 print_vfs_message (_("ftpfs: making connection to %s"), host
);
689 enable_interrupt_key (); /* clear the interrupt flag */
691 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
692 sizeof (server_address
)) < 0){
694 if (errno
== EINTR
&& got_interrupt ())
695 print_vfs_message (_("ftpfs: connection interrupted by user"));
697 print_vfs_message (_("ftpfs: connection to server failed: %s"),
698 unix_error_string(errno
));
699 disable_interrupt_key();
703 disable_interrupt_key();
708 open_archive_int (vfs
*me
, vfs_s_super
*super
)
710 int retry_seconds
, count_down
;
712 /* We do not want to use the passive if we are using proxies */
714 SUP
.use_passive_connection
= 0;
718 SUP
.failed_on_login
= 0;
720 SUP
.sock
= ftpfs_open_socket (me
, super
);
724 if (login_server (me
, super
, NULL
)) {
725 /* Logged in, no need to retry the connection */
728 if (SUP
.failed_on_login
){
729 /* Close only the socket descriptor */
734 if (ftpfs_retry_seconds
){
735 retry_seconds
= ftpfs_retry_seconds
;
736 enable_interrupt_key ();
737 for (count_down
= retry_seconds
; count_down
; count_down
--){
738 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
740 if (got_interrupt ()){
742 disable_interrupt_key ();
746 disable_interrupt_key ();
749 } while (retry_seconds
);
751 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
753 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
758 open_archive (vfs
*me
, vfs_s_super
*super
, char *archive_name
, char *op
)
760 char *host
, *user
, *password
;
763 ftp_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
770 if (ftpfs_check_proxy (host
))
771 SUP
.proxy
= ftpfs_proxy_host
;
772 SUP
.password
= password
;
773 SUP
.use_passive_connection
= ftpfs_use_passive_connections
| source_route
;
774 SUP
.use_source_route
= source_route
;
775 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
776 SUP
.isbinary
= TYPE_UNKNOWN
;
777 SUP
.remote_is_amiga
= 0;
778 super
->name
= g_strdup("/");
779 super
->root
= vfs_s_new_inode (me
, super
, vfs_s_default_stat(me
, S_IFDIR
| 0755));
781 return open_archive_int (me
, super
);
785 archive_same(vfs
*me
, vfs_s_super
*super
, char *archive_name
, char *op
, void *cookie
)
790 ftp_split_url (strchr(op
, ':') + 1, &host
, &user
, &port
, 0);
792 port
= ((strcmp (host
, SUP
.host
) == 0) &&
793 (strcmp (user
, SUP
.user
) == 0) &&
803 ftpfs_flushdir (void)
805 force_expiration
= 1;
809 dir_uptodate(vfs
*me
, vfs_s_inode
*ino
)
813 if (force_expiration
) {
814 force_expiration
= 0;
817 gettimeofday(&tim
, NULL
);
818 if (tim
.tv_sec
< ino
->u
.ftp
.timestamp
.tv_sec
)
823 /* The returned directory should always contain a trailing slash */
825 ftpfs_get_current_directory (vfs
*me
, vfs_s_super
*super
)
827 char buf
[BUF_8K
], *bufp
, *bufq
;
829 if (command (me
, super
, NONE
, "PWD") == COMPLETE
&&
830 get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
832 for (bufq
= buf
; *bufq
; bufq
++)
839 if (*(bufq
- 1) != '/') {
844 return g_strdup (bufp
);
846 /* If the remote server is an Amiga a leading slash
847 might be missing. MC needs it because it is used
848 as seperator between hostname and path internally. */
849 return g_strconcat( "/", bufp
, 0);
863 /* Setup Passive ftp connection, we use it for source routed connections */
865 setup_passive (vfs
*me
, vfs_s_super
*super
, int my_socket
, struct sockaddr_in
*sa
)
867 int xa
, xb
, xc
, xd
, xe
, xf
;
871 if (command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
874 /* Parse remote parameters */
875 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++)
879 if (!isdigit ((unsigned char) *c
))
881 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
883 n
[0] = (unsigned char) xa
;
884 n
[1] = (unsigned char) xb
;
885 n
[2] = (unsigned char) xc
;
886 n
[3] = (unsigned char) xd
;
887 n
[4] = (unsigned char) xe
;
888 n
[5] = (unsigned char) xf
;
890 memcpy (&(sa
->sin_addr
.s_addr
), (void *)n
, 4);
891 memcpy (&(sa
->sin_port
), (void *)&n
[4], 2);
892 setup_source_route (my_socket
, sa
->sin_addr
.s_addr
);
893 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
899 initconn (vfs
*me
, vfs_s_super
*super
)
901 struct sockaddr_in data_addr
;
902 int data
, len
= sizeof(data_addr
);
905 getsockname(SUP
.sock
, (struct sockaddr
*) &data_addr
, &len
);
906 data_addr
.sin_port
= 0;
908 pe
= getprotobyname("tcp");
911 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
915 if (SUP
.use_passive_connection
){
916 if ((SUP
.use_passive_connection
= setup_passive (me
, super
, data
, &data_addr
)))
919 SUP
.use_source_route
= 0;
920 SUP
.use_passive_connection
= 0;
921 print_vfs_message (_("ftpfs: could not setup passive mode"));
924 /* If passive setup fails, fallback to active connections */
925 /* Active FTP connection */
926 if ((bind (data
, (struct sockaddr
*)&data_addr
, len
) == 0) &&
927 (getsockname (data
, (struct sockaddr
*) &data_addr
, &len
) == 0) &&
928 (listen (data
, 1) == 0))
930 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
931 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
933 if (command (me
, super
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
934 a
[2], a
[3], p
[0], p
[1]) == COMPLETE
)
943 open_data_connection (vfs
*me
, vfs_s_super
*super
, char *cmd
, char *remote
,
944 int isbinary
, int reget
)
946 struct sockaddr_in from
;
947 int s
, j
, data
, fromlen
= sizeof(from
);
949 if ((s
= initconn (me
, super
)) == -1)
951 if (changetype (me
, super
, isbinary
) == -1)
954 j
= command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
959 char * remote_path
= translate_path (me
, super
, remote
);
960 j
= command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
961 /* WarFtpD can't STORE //filename */
962 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
963 g_free (remote_path
);
965 j
= command (me
, super
, WAIT_REPLY
, "%s", cmd
);
968 enable_interrupt_key();
969 if (SUP
.use_passive_connection
)
972 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
979 disable_interrupt_key();
983 #define ABORT_TIMEOUT 5
985 linear_abort (vfs
*me
, vfs_s_fh
*fh
)
987 vfs_s_super
*super
= FH_SUPER
;
988 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
993 SUP
.control_connection_buzy
= 0;
995 print_vfs_message (_("ftpfs: aborting transfer."));
996 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
997 print_vfs_message (_("ftpfs: abort error: %s"),
998 unix_error_string (errno
));
1002 if (command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1003 print_vfs_message (_("ftpfs: abort failed"));
1008 FD_SET (dsock
, &mask
);
1009 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1010 struct timeval start_tim
, tim
;
1011 gettimeofday (&start_tim
, NULL
);
1012 /* flush the remaining data */
1013 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1014 gettimeofday (&tim
, NULL
);
1015 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1016 /* server keeps sending, drop the connection and reconnect */
1017 reconnect (me
, super
);
1027 if ((get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1028 get_reply (me
, SUP
.sock
, NULL
, 0);
1033 resolve_symlink_without_ls_options(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1035 struct linklist
*flist
;
1036 struct direntry
*fe
, *fel
;
1037 char tmp
[MC_MAXPATHLEN
];
1040 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1041 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1042 /* flist->data->l_stat is alread initialized with 0 */
1044 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1045 if (fel
->linkname
[0] == '/') {
1046 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1048 strcpy (tmp
, fel
->linkname
);
1050 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1052 strcpy (tmp
, dir
->remote_path
);
1055 strcat (tmp
+ 1, fel
->linkname
);
1057 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1058 canonicalize_pathname (tmp
);
1059 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1061 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1062 /* Symlink points to link which isn't resolved, yet. */
1063 if (fe
->linkname
[0] == '/') {
1064 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1066 strcpy (tmp
, fe
->linkname
);
1068 /* at this point tmp looks always like this
1069 /directory/filename, i.e. no need to check
1070 strrchr's return value */
1071 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1072 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1074 strcat (tmp
, fe
->linkname
);
1078 fel
->l_stat
= g_new (struct stat
, 1);
1079 if ( S_ISLNK (fe
->s
.st_mode
))
1080 *fel
->l_stat
= *fe
->l_stat
;
1082 *fel
->l_stat
= fe
->s
;
1083 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1090 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1094 resolve_symlink_with_ls_options(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1096 char buffer
[2048] = "", *filename
;
1100 struct linklist
*flist
;
1101 struct direntry
*fe
;
1102 int switch_method
= 0;
1104 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1105 if (strchr (dir
->remote_path
, ' ')) {
1106 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1107 print_vfs_message(_("ftpfs: CWD failed."));
1110 sock
= open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1113 sock
= open_data_connection (bucket
, "LIST -lLa",
1114 dir
->remote_path
, TYPE_ASCII
, 0);
1117 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1121 fp
= fdopen(sock
, "r");
1124 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1127 enable_interrupt_key();
1128 flist
= dir
->file_list
->next
;
1131 if (flist
== dir
->file_list
)
1134 flist
= flist
->next
;
1135 } while (!S_ISLNK(fe
->s
.st_mode
));
1137 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1140 fputs (buffer
, logfile
);
1143 vfs_die("This code should be commented out\n");
1144 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1145 int r
= strcmp(fe
->name
, filename
);
1148 if (S_ISLNK (s
.st_mode
)) {
1149 /* This server doesn't understand LIST -lLa */
1153 fe
->l_stat
= g_new (struct stat
, 1);
1154 if (fe
->l_stat
== NULL
)
1157 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1166 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1167 disable_interrupt_key();
1169 get_reply(me
, SUP
.sock
, NULL
, 0);
1173 resolve_symlink(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1175 print_vfs_message(_("Resolving symlink..."));
1177 if (SUP
.strict_rfc959_list_cmd
)
1178 resolve_symlink_without_ls_options(me
, super
, dir
);
1180 resolve_symlink_with_ls_options(me
, super
, dir
);
1185 dir_load(vfs
*me
, vfs_s_inode
*dir
, char *remote_path
)
1188 vfs_s_super
*super
= dir
->super
;
1189 int sock
, num_entries
= 0;
1191 int has_symlinks
= 0;
1193 char buffer
[BUF_8K
];
1196 cd_first
= ftpfs_first_cd_then_ls
|| (strchr (remote_path
, ' ') != NULL
)
1197 || (SUP
.strict
== RFC_STRICT
);
1200 print_vfs_message(_("ftpfs: Reading FTP directory %s... %s%s"), remote_path
,
1201 SUP
.strict
== RFC_STRICT
? _("(strict rfc959)") : "",
1202 cd_first
? _("(chdir first)") : "");
1207 p
= translate_path (me
, super
, remote_path
);
1209 if (ftpfs_chdir_internal (me
, super
, p
) != COMPLETE
) {
1212 print_vfs_message(_("ftpfs: CWD failed."));
1218 gettimeofday(&dir
->u
.ftp
.timestamp
, NULL
);
1219 dir
->u
.ftp
.timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1221 if (SUP
.strict
== RFC_STRICT
)
1222 sock
= open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1224 /* Dirty hack to avoid autoprepending / to . */
1225 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1226 sock
= open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1228 /* Trailing "/." is necessary if remote_path is a symlink */
1229 char *path
= concat_dir_and_file (remote_path
, ".");
1230 sock
= open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1237 /* Clear the interrupt flag */
1238 enable_interrupt_key ();
1242 /* added 20001006 by gisburn
1243 * add dots '.' and '..'. This must be _executed_ before scanning the dir as the
1244 * code below may jump directly into error handling code (without executing
1245 * remaining code). And C doesn't have try {...} finally {}; :-)
1247 vfs_s_inode
*parent
= dir
->ent
->dir
;
1252 ent
= vfs_s_generate_entry(me
, ".", dir
, 0);
1253 ent
->ino
->st
=dir
->st
;
1255 vfs_s_insert_entry(me
, dir
, ent
);
1257 ent
= vfs_s_generate_entry(me
, "..", parent
, 0);
1258 ent
->ino
->st
=parent
->st
;
1260 vfs_s_insert_entry(me
, dir
, ent
);
1266 int res
= vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
), sock
);
1271 me
->verrno
= ECONNRESET
;
1273 disable_interrupt_key();
1274 get_reply(me
, SUP
.sock
, NULL
, 0);
1275 print_vfs_message (_("%s: failure"), me
->name
);
1280 fputs (buffer
, logfile
);
1281 fputs ("\n", logfile
);
1285 ent
= vfs_s_generate_entry(me
, NULL
, dir
, 0);
1286 i
= ent
->ino
->st
.st_nlink
;
1287 if (!vfs_parse_ls_lga (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1288 vfs_s_free_entry (me
, ent
);
1291 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1293 if ((!strcmp(ent
->name
, ".")) || (!strcmp (ent
->name
, ".."))) {
1295 ent
->name
= NULL
; /* Ouch, vfs_s_free_entry "knows" about . and .. being special :-( */
1296 vfs_s_free_entry (me
, ent
);
1300 vfs_s_insert_entry(me
, dir
, ent
);
1303 /* vfs_s_add_dots(me, dir, NULL);
1304 FIXME This really should be here; but we need to provide correct parent.
1305 Disabled for now, please fix it. Pavel
1308 me
->verrno
= E_REMOTE
;
1309 if ((get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
) || !num_entries
)
1312 if (SUP
.strict
== RFC_AUTODETECT
)
1313 SUP
.strict
= RFC_DARING
;
1317 if (resolve_symlinks
)
1318 resolve_symlink(me
, super
, dcache
);
1320 dcache
->symlink_status
= FTPFS_UNRESOLVED_SYMLINKS
;
1323 print_vfs_message (_("%s: done."), me
->name
);
1327 if (SUP
.strict
== RFC_AUTODETECT
) {
1328 /* It's our first attempt to get a directory listing from this
1329 server (UNIX style LIST command) */
1330 SUP
.strict
= RFC_STRICT
;
1331 /* I hate goto, but recursive call needs another 8K on stack */
1332 /* return dir_load (me, dir, remote_path); */
1336 print_vfs_message(_("ftpfs: failed; nowhere to fallback to"));
1341 file_store(vfs
*me
, vfs_s_fh
*fh
, char *name
, char *localname
)
1345 #ifdef HAVE_STRUCT_LINGER
1352 vfs_s_super
*super
= FH_SUPER
;
1354 h
= open(localname
, O_RDONLY
);
1358 sock
= open_data_connection(me
, super
, fh
->u
.ftp
.append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1363 #ifdef HAVE_STRUCT_LINGER
1366 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof(li
));
1368 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1372 enable_interrupt_key();
1374 while ((n
= read(h
, buffer
, sizeof(buffer
))) < 0) {
1375 if (errno
== EINTR
) {
1376 if (got_interrupt()) {
1388 while (write(sock
, buffer
, n
) < 0) {
1389 if (errno
== EINTR
) {
1390 if (got_interrupt()) {
1401 print_vfs_message(_("ftpfs: storing file %lu (%lu)"),
1402 (unsigned long) total
, (unsigned long) s
.st_size
);
1404 disable_interrupt_key();
1407 if (get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1411 disable_interrupt_key();
1414 get_reply(me
, SUP
.sock
, NULL
, 0);
1419 linear_start(vfs
*me
, vfs_s_fh
*fh
, int offset
)
1421 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1425 FH_SOCK
= open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1429 fh
->linear
= LS_LINEAR_OPEN
;
1430 FH_SUPER
->u
.ftp
.control_connection_buzy
= 1;
1431 fh
->u
.ftp
.append
= 0;
1436 linear_read (vfs
*me
, vfs_s_fh
*fh
, void *buf
, int len
)
1439 vfs_s_super
*super
= FH_SUPER
;
1441 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1442 if ((errno
== EINTR
) && !got_interrupt())
1448 linear_abort(me
, fh
);
1451 SUP
.control_connection_buzy
= 0;
1454 if ((get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1455 ERRNOR (E_REMOTE
, -1);
1462 linear_close (vfs
*me
, vfs_s_fh
*fh
)
1465 linear_abort(me
, fh
);
1468 static int ftpfs_ctl (void *fh
, int ctlop
, int arg
)
1471 case MCCTL_IS_NOTREADY
:
1476 vfs_die ("You may not do this");
1477 if (FH
->linear
== LS_LINEAR_CLOSED
)
1480 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1481 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1490 /* Warning: filename passed to this command is damaged */
1492 send_ftp_command(vfs
*me
, char *filename
, char *cmd
, int flags
)
1497 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1499 if (!(rpath
= vfs_s_get_path_mangle(me
, filename
, &super
, 0)))
1501 p
= translate_path (me
, super
, rpath
);
1502 r
= command (me
, super
, WAIT_REPLY
, cmd
, p
);
1504 vfs_add_noncurrent_stamps (&vfs_ftpfs_ops
, (vfsid
) super
, NULL
);
1505 if (flags
& OPT_IGNORE_ERROR
)
1509 if (flush_directory_cache
)
1510 vfs_s_invalidate(me
, super
);
1514 /* This routine is called as the last step in load_setup */
1516 ftpfs_init_passwd(void)
1518 ftpfs_anonymous_passwd
= load_anon_passwd ();
1519 if (ftpfs_anonymous_passwd
)
1522 /* If there is no anonymous ftp password specified
1523 * then we'll just use anonymous@
1524 * We don't send any other thing because:
1525 * - We want to remain anonymous
1526 * - We want to stop SPAM
1527 * - We don't want to let ftp sites to discriminate by the user,
1530 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1533 static int ftpfs_chmod (vfs
*me
, char *path
, int mode
)
1535 char buf
[BUF_SMALL
];
1537 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1538 return send_ftp_command(me
, path
, buf
, OPT_FLUSH
);
1541 static int ftpfs_chown (vfs
*me
, char *path
, int owner
, int group
)
1547 /* Everyone knows it is not possible to chown remotely, so why bother them.
1548 If someone's root, then copy/move will always try to chown it... */
1553 static int ftpfs_unlink (vfs
*me
, char *path
)
1555 return send_ftp_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1558 /* Return 1 if path is the same directory as the one we are in now */
1560 is_same_dir (vfs
*me
, vfs_s_super
*super
, const char *path
)
1564 if (strcmp (path
, SUP
.cwdir
) == 0)
1570 ftpfs_chdir_internal (vfs
*me
, vfs_s_super
*super
, char *remote_path
)
1575 if (!SUP
.cwd_defered
&& is_same_dir (me
, super
, remote_path
))
1578 p
= translate_path (me
, super
, remote_path
);
1579 r
= command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1582 if (r
!= COMPLETE
) {
1586 SUP
.cwdir
= g_strdup (remote_path
);
1587 SUP
.cwd_defered
= 0;
1592 static int ftpfs_rename (vfs
*me
, char *path1
, char *path2
)
1594 send_ftp_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1595 return send_ftp_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1598 static int ftpfs_mkdir (vfs
*me
, char *path
, mode_t mode
)
1600 return send_ftp_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1603 static int ftpfs_rmdir (vfs
*me
, char *path
)
1605 return send_ftp_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1608 static int ftpfs_fh_open (vfs
*me
, vfs_s_fh
*fh
, int flags
, int mode
)
1610 fh
->u
.ftp
.append
= 0;
1611 /* File will be written only, so no need to retrieve it from ftp server */
1612 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
|O_RDWR
))){
1613 #ifdef HAVE_STRUCT_LINGER
1620 /* linear_start() called, so data will be written
1621 * to local temporary file and stored to ftp server
1622 * by vfs_s_close later
1624 if (FH_SUPER
->u
.ftp
.control_connection_buzy
){
1625 if (!fh
->ino
->localname
){
1626 int handle
= mc_mkstemps (&fh
->ino
->localname
, me
->name
, NULL
);
1630 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1634 name
= vfs_s_fullpath (me
, fh
->ino
);
1637 fh
->handle
= open_data_connection(me
, fh
->ino
->super
,
1638 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1643 #ifdef HAVE_STRUCT_LINGER
1647 setsockopt(fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof(li
));
1649 if (fh
->ino
->localname
){
1650 unlink (fh
->ino
->localname
);
1651 g_free (fh
->ino
->localname
);
1652 fh
->ino
->localname
= NULL
;
1657 if (!fh
->ino
->localname
)
1658 if (vfs_s_retrieve_file (me
, fh
->ino
)==-1)
1660 if (!fh
->ino
->localname
)
1661 vfs_die( "retrieve_file failed to fill in localname" );
1665 static int ftpfs_fh_close (vfs
*me
, vfs_s_fh
*fh
)
1667 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1670 /* File is stored to destination already, so
1671 * we prevent MEDATA->file_store() call from vfs_s_close ()
1674 if (get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1676 vfs_s_invalidate (me
, FH_SUPER
);
1681 static struct vfs_s_data ftp_data
= {
1687 NULL
, /* init_inode */
1688 NULL
, /* free_inode */
1689 NULL
, /* init_entry */
1691 NULL
, /* archive_check */
1696 ftpfs_fh_open
, /* fh_open */
1697 ftpfs_fh_close
, /* fh_close */
1699 vfs_s_find_entry_linear
,
1710 ftpfs_fill_names (vfs
*me
, void (*func
)(char *))
1712 struct vfs_s_super
* super
= ftp_data
.supers
;
1716 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, NULL
);
1719 super
= super
->next
;
1723 vfs vfs_ftpfs_ops
= {
1724 NULL
, /* This is place of next pointer */
1727 "ftp:", /* prefix */
1728 &ftp_data
, /* data */
1751 ftpfs_chown
, /* not really implemented but returns success */
1766 vfs_s_nothingisopen
,
1780 void ftpfs_set_debug (const char *file
)
1782 logfile
= fopen (file
, "w+");
1784 ftp_data
.logfile
= logfile
;
1787 static char buffer
[BUF_MEDIUM
];
1788 static char *netrc
, *netrcp
;
1790 /* This should match the keywords[] array below */
1803 static keyword_t
netrc_next (void)
1807 static const char *const keywords
[] = { "default", "machine",
1808 "login", "password", "passwd", "account", "macdef", NULL
1813 netrcp
= skip_separators (netrcp
);
1814 if (*netrcp
!= '\n')
1821 if (*netrcp
== '"') {
1822 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1823 if (*netrcp
== '\\')
1828 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1829 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1830 if (*netrcp
== '\\')
1840 while (keywords
[i
- 1]) {
1841 if (!strcmp (keywords
[i
- 1], buffer
))
1847 return NETRC_UNKNOWN
;
1850 static int netrc_has_incorrect_mode (char *netrcname
, char *netrc
)
1852 static int be_angry
= 1;
1855 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1857 message_1s (1, MSG_ERROR
,
1858 _("~/.netrc file has not correct mode.\n"
1859 "Remove password or correct mode."));
1867 /* Scan .netrc until we find matching "machine" or "default"
1868 * domain is used for additional matching
1869 * No search is done after "default" in compliance with "man netrc"
1870 * Return 0 if found, -1 otherwise */
1871 static int find_machine (const char *host
, const char *domain
)
1875 while ((keyword
= netrc_next ()) != NETRC_NONE
) {
1876 if (keyword
== NETRC_DEFAULT
)
1879 if (keyword
== NETRC_MACDEF
) {
1880 /* Scan for an empty line, which concludes "macdef" */
1882 while (*netrcp
&& *netrcp
!= '\n')
1884 if (*netrcp
!= '\n')
1887 } while (*netrcp
&& *netrcp
!= '\n');
1891 if (keyword
!= NETRC_MACHINE
)
1894 /* Take machine name */
1895 if (netrc_next () == NETRC_NONE
)
1898 if (g_strcasecmp (host
, buffer
)) {
1899 /* Try adding our domain to short names in .netrc */
1900 char *host_domain
= strchr (host
, '.');
1904 /* Compare domain part */
1905 if (g_strcasecmp (host_domain
, domain
))
1908 /* Compare local part */
1909 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1920 /* Extract login and password from .netrc for the host.
1922 * Returns 0 for success, -1 for error */
1923 static int lookup_netrc (const char *host
, char **login
, char **pass
)
1926 char *tmp_pass
= NULL
;
1927 char hostname
[MAXHOSTNAMELEN
], *domain
;
1929 static struct rupcache
{
1930 struct rupcache
*next
;
1934 } *rup_cache
= NULL
, *rupp
;
1936 /* Initialize *login and *pass */
1943 /* Look up in the cache first */
1944 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
1945 if (!strcmp (host
, rupp
->host
)) {
1947 *login
= g_strdup (rupp
->login
);
1948 if (pass
&& rupp
->pass
)
1949 *pass
= g_strdup (rupp
->pass
);
1954 /* Load current .netrc */
1955 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
1956 netrcp
= netrc
= load_file (netrcname
);
1957 if (netrc
== NULL
) {
1962 /* Find our own domain name */
1963 if (gethostname (hostname
, sizeof (hostname
)) < 0)
1965 if (!(domain
= strchr (hostname
, '.')))
1968 /* Scan for "default" and matching "machine" keywords */
1969 find_machine (host
, domain
);
1971 /* Scan for keywords following "default" and "machine" */
1974 keyword
= netrc_next ();
1978 if (netrc_next () == NETRC_NONE
) {
1983 /* We have another name already - should not happen */
1989 /* We have login name now */
1990 *login
= g_strdup (buffer
);
1993 case NETRC_PASSWORD
:
1995 if (netrc_next () == NETRC_NONE
) {
2000 /* Ignore unsafe passwords */
2001 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2002 && netrc_has_incorrect_mode (netrcname
, netrc
)) {
2007 /* Remember password. pass may be NULL, so use tmp_pass */
2008 if (tmp_pass
== NULL
)
2009 tmp_pass
= g_strdup (buffer
);
2013 /* "account" is followed by a token which we ignore */
2014 if (netrc_next () == NETRC_NONE
) {
2019 /* Ignore account, but warn user anyways */
2020 netrc_has_incorrect_mode (netrcname
, netrc
);
2024 /* Unexpected keyword or end of file */
2036 rupp
= g_new (struct rupcache
, 1);
2037 rupp
->host
= g_strdup (host
);
2038 rupp
->login
= rupp
->pass
= 0;
2040 if (*login
!= NULL
) {
2041 rupp
->login
= g_strdup (*login
);
2043 if (tmp_pass
!= NULL
)
2044 rupp
->pass
= g_strdup (tmp_pass
);
2045 rupp
->next
= rup_cache
;