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
);
576 ftpfs_check_proxy (const char *host
)
578 struct no_proxy_entry
*npe
;
580 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
581 return 0; /* sanity check */
586 if (!ftpfs_always_use_proxy
)
589 if (!strchr (host
, '.'))
592 load_no_proxy_list ();
593 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
594 char *domain
= npe
->domain
;
596 if (domain
[0] == '.') {
597 int ld
= strlen (domain
);
598 int lh
= strlen (host
);
600 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
608 if (!g_strcasecmp (host
, domain
))
616 ftpfs_get_proxy_host_and_port (char *proxy
, char **host
, int *port
)
621 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
628 ftpfs_open_socket (vfs
*me
, vfs_s_super
*super
)
630 struct sockaddr_in server_address
;
637 /* Use a proxy host? */
640 if (!host
|| !*host
){
641 print_vfs_message (_("ftpfs: Invalid host name."));
646 /* Hosts to connect to that start with a ! should use proxy */
648 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
652 /* Get host address */
653 memset ((char *) &server_address
, 0, sizeof (server_address
));
654 server_address
.sin_family
= AF_INET
;
655 server_address
.sin_addr
.s_addr
= inet_addr (host
);
656 if (server_address
.sin_addr
.s_addr
!= -1)
657 server_address
.sin_family
= AF_INET
;
659 hp
= gethostbyname (host
);
661 print_vfs_message (_("ftpfs: Invalid host address."));
667 server_address
.sin_family
= hp
->h_addrtype
;
669 /* We copy only 4 bytes, we can not trust hp->h_length, as it comes from the DNS */
670 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
, 4);
673 server_address
.sin_port
= htons (port
);
676 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
682 setup_source_route (my_socket
, server_address
.sin_addr
.s_addr
);
684 print_vfs_message (_("ftpfs: making connection to %s"), host
);
688 enable_interrupt_key (); /* clear the interrupt flag */
690 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
691 sizeof (server_address
)) < 0){
693 if (errno
== EINTR
&& got_interrupt ())
694 print_vfs_message (_("ftpfs: connection interrupted by user"));
696 print_vfs_message (_("ftpfs: connection to server failed: %s"),
697 unix_error_string(errno
));
698 disable_interrupt_key();
702 disable_interrupt_key();
707 open_archive_int (vfs
*me
, vfs_s_super
*super
)
709 int retry_seconds
, count_down
;
711 /* We do not want to use the passive if we are using proxies */
713 SUP
.use_passive_connection
= 0;
717 SUP
.failed_on_login
= 0;
719 SUP
.sock
= ftpfs_open_socket (me
, super
);
723 if (login_server (me
, super
, NULL
)) {
724 /* Logged in, no need to retry the connection */
727 if (SUP
.failed_on_login
){
728 /* Close only the socket descriptor */
733 if (ftpfs_retry_seconds
){
734 retry_seconds
= ftpfs_retry_seconds
;
735 enable_interrupt_key ();
736 for (count_down
= retry_seconds
; count_down
; count_down
--){
737 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
739 if (got_interrupt ()){
741 disable_interrupt_key ();
745 disable_interrupt_key ();
748 } while (retry_seconds
);
750 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
752 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
757 open_archive (vfs
*me
, vfs_s_super
*super
, char *archive_name
, char *op
)
759 char *host
, *user
, *password
;
762 ftp_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
769 if (ftpfs_check_proxy (host
))
770 SUP
.proxy
= ftpfs_proxy_host
;
771 SUP
.password
= password
;
772 SUP
.use_passive_connection
= ftpfs_use_passive_connections
| source_route
;
773 SUP
.use_source_route
= source_route
;
774 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
775 SUP
.isbinary
= TYPE_UNKNOWN
;
776 SUP
.remote_is_amiga
= 0;
777 super
->name
= g_strdup("/");
778 super
->root
= vfs_s_new_inode (me
, super
, vfs_s_default_stat(me
, S_IFDIR
| 0755));
780 return open_archive_int (me
, super
);
784 archive_same(vfs
*me
, vfs_s_super
*super
, char *archive_name
, char *op
, void *cookie
)
789 ftp_split_url (strchr(op
, ':') + 1, &host
, &user
, &port
, 0);
791 port
= ((strcmp (host
, SUP
.host
) == 0) &&
792 (strcmp (user
, SUP
.user
) == 0) &&
802 ftpfs_flushdir (void)
804 force_expiration
= 1;
808 dir_uptodate(vfs
*me
, vfs_s_inode
*ino
)
812 if (force_expiration
) {
813 force_expiration
= 0;
816 gettimeofday(&tim
, NULL
);
817 if (tim
.tv_sec
< ino
->u
.ftp
.timestamp
.tv_sec
)
822 /* The returned directory should always contain a trailing slash */
824 ftpfs_get_current_directory (vfs
*me
, vfs_s_super
*super
)
826 char buf
[BUF_8K
], *bufp
, *bufq
;
828 if (command (me
, super
, NONE
, "PWD") == COMPLETE
&&
829 get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
831 for (bufq
= buf
; *bufq
; bufq
++)
838 if (*(bufq
- 1) != '/') {
843 return g_strdup (bufp
);
845 /* If the remote server is an Amiga a leading slash
846 might be missing. MC needs it because it is used
847 as seperator between hostname and path internally. */
848 return g_strconcat( "/", bufp
, 0);
862 /* Setup Passive ftp connection, we use it for source routed connections */
864 setup_passive (vfs
*me
, vfs_s_super
*super
, int my_socket
, struct sockaddr_in
*sa
)
866 int xa
, xb
, xc
, xd
, xe
, xf
;
870 if (command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
873 /* Parse remote parameters */
874 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++)
878 if (!isdigit ((unsigned char) *c
))
880 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
882 n
[0] = (unsigned char) xa
;
883 n
[1] = (unsigned char) xb
;
884 n
[2] = (unsigned char) xc
;
885 n
[3] = (unsigned char) xd
;
886 n
[4] = (unsigned char) xe
;
887 n
[5] = (unsigned char) xf
;
889 memcpy (&(sa
->sin_addr
.s_addr
), (void *)n
, 4);
890 memcpy (&(sa
->sin_port
), (void *)&n
[4], 2);
891 setup_source_route (my_socket
, sa
->sin_addr
.s_addr
);
892 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
898 initconn (vfs
*me
, vfs_s_super
*super
)
900 struct sockaddr_in data_addr
;
901 int data
, len
= sizeof(data_addr
);
904 getsockname(SUP
.sock
, (struct sockaddr
*) &data_addr
, &len
);
905 data_addr
.sin_port
= 0;
907 pe
= getprotobyname("tcp");
910 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
914 if (SUP
.use_passive_connection
){
915 if ((SUP
.use_passive_connection
= setup_passive (me
, super
, data
, &data_addr
)))
918 SUP
.use_source_route
= 0;
919 SUP
.use_passive_connection
= 0;
920 print_vfs_message (_("ftpfs: could not setup passive mode"));
923 /* If passive setup fails, fallback to active connections */
924 /* Active FTP connection */
925 if ((bind (data
, (struct sockaddr
*)&data_addr
, len
) == 0) &&
926 (getsockname (data
, (struct sockaddr
*) &data_addr
, &len
) == 0) &&
927 (listen (data
, 1) == 0))
929 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
930 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
932 if (command (me
, super
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
933 a
[2], a
[3], p
[0], p
[1]) == COMPLETE
)
942 open_data_connection (vfs
*me
, vfs_s_super
*super
, char *cmd
, char *remote
,
943 int isbinary
, int reget
)
945 struct sockaddr_in from
;
946 int s
, j
, data
, fromlen
= sizeof(from
);
948 if ((s
= initconn (me
, super
)) == -1)
950 if (changetype (me
, super
, isbinary
) == -1)
953 j
= command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
958 char * remote_path
= translate_path (me
, super
, remote
);
959 j
= command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
960 /* WarFtpD can't STORE //filename */
961 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
962 g_free (remote_path
);
964 j
= command (me
, super
, WAIT_REPLY
, "%s", cmd
);
967 enable_interrupt_key();
968 if (SUP
.use_passive_connection
)
971 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
978 disable_interrupt_key();
982 #define ABORT_TIMEOUT 5
984 linear_abort (vfs
*me
, vfs_s_fh
*fh
)
986 vfs_s_super
*super
= FH_SUPER
;
987 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
992 SUP
.control_connection_buzy
= 0;
994 print_vfs_message (_("ftpfs: aborting transfer."));
995 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
996 print_vfs_message (_("ftpfs: abort error: %s"),
997 unix_error_string (errno
));
1001 if (command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1002 print_vfs_message (_("ftpfs: abort failed"));
1007 FD_SET (dsock
, &mask
);
1008 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1009 struct timeval start_tim
, tim
;
1010 gettimeofday (&start_tim
, NULL
);
1011 /* flush the remaining data */
1012 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1013 gettimeofday (&tim
, NULL
);
1014 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1015 /* server keeps sending, drop the connection and reconnect */
1016 reconnect (me
, super
);
1022 if ((get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1023 get_reply (me
, SUP
.sock
, NULL
, 0);
1028 resolve_symlink_without_ls_options(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1030 struct linklist
*flist
;
1031 struct direntry
*fe
, *fel
;
1032 char tmp
[MC_MAXPATHLEN
];
1035 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1036 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1037 /* flist->data->l_stat is alread initialized with 0 */
1039 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1040 if (fel
->linkname
[0] == '/') {
1041 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1043 strcpy (tmp
, fel
->linkname
);
1045 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1047 strcpy (tmp
, dir
->remote_path
);
1050 strcat (tmp
+ 1, fel
->linkname
);
1052 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1053 canonicalize_pathname (tmp
);
1054 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1056 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1057 /* Symlink points to link which isn't resolved, yet. */
1058 if (fe
->linkname
[0] == '/') {
1059 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1061 strcpy (tmp
, fe
->linkname
);
1063 /* at this point tmp looks always like this
1064 /directory/filename, i.e. no need to check
1065 strrchr's return value */
1066 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1067 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1069 strcat (tmp
, fe
->linkname
);
1073 fel
->l_stat
= g_new (struct stat
, 1);
1074 if ( S_ISLNK (fe
->s
.st_mode
))
1075 *fel
->l_stat
= *fe
->l_stat
;
1077 *fel
->l_stat
= fe
->s
;
1078 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1085 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1089 resolve_symlink_with_ls_options(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1091 char buffer
[2048] = "", *filename
;
1095 struct linklist
*flist
;
1096 struct direntry
*fe
;
1097 int switch_method
= 0;
1099 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1100 if (strchr (dir
->remote_path
, ' ')) {
1101 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1102 print_vfs_message(_("ftpfs: CWD failed."));
1105 sock
= open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1108 sock
= open_data_connection (bucket
, "LIST -lLa",
1109 dir
->remote_path
, TYPE_ASCII
, 0);
1112 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1116 fp
= fdopen(sock
, "r");
1119 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1122 enable_interrupt_key();
1123 flist
= dir
->file_list
->next
;
1126 if (flist
== dir
->file_list
)
1129 flist
= flist
->next
;
1130 } while (!S_ISLNK(fe
->s
.st_mode
));
1132 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1135 fputs (buffer
, logfile
);
1138 vfs_die("This code should be commented out\n");
1139 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1140 int r
= strcmp(fe
->name
, filename
);
1143 if (S_ISLNK (s
.st_mode
)) {
1144 /* This server doesn't understand LIST -lLa */
1148 fe
->l_stat
= g_new (struct stat
, 1);
1149 if (fe
->l_stat
== NULL
)
1152 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1161 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1162 disable_interrupt_key();
1164 get_reply(me
, SUP
.sock
, NULL
, 0);
1168 resolve_symlink(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1170 print_vfs_message(_("Resolving symlink..."));
1172 if (SUP
.strict_rfc959_list_cmd
)
1173 resolve_symlink_without_ls_options(me
, super
, dir
);
1175 resolve_symlink_with_ls_options(me
, super
, dir
);
1180 dir_load(vfs
*me
, vfs_s_inode
*dir
, char *remote_path
)
1183 vfs_s_super
*super
= dir
->super
;
1184 int sock
, num_entries
= 0;
1186 int has_symlinks
= 0;
1188 char buffer
[BUF_8K
];
1191 cd_first
= ftpfs_first_cd_then_ls
|| (strchr (remote_path
, ' ') != NULL
)
1192 || (SUP
.strict
== RFC_STRICT
);
1195 print_vfs_message(_("ftpfs: Reading FTP directory %s... %s%s"), remote_path
,
1196 SUP
.strict
== RFC_STRICT
? _("(strict rfc959)") : "",
1197 cd_first
? _("(chdir first)") : "");
1202 p
= translate_path (me
, super
, remote_path
);
1204 if (ftpfs_chdir_internal (me
, super
, p
) != COMPLETE
) {
1207 print_vfs_message(_("ftpfs: CWD failed."));
1213 gettimeofday(&dir
->u
.ftp
.timestamp
, NULL
);
1214 dir
->u
.ftp
.timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1216 if (SUP
.strict
== RFC_STRICT
)
1217 sock
= open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1219 /* Dirty hack to avoid autoprepending / to . */
1220 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1221 sock
= open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1223 /* Trailing "/." is necessary if remote_path is a symlink */
1224 char *path
= concat_dir_and_file (remote_path
, ".");
1225 sock
= open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1232 /* Clear the interrupt flag */
1233 enable_interrupt_key ();
1237 /* added 20001006 by gisburn
1238 * add dots '.' and '..'. This must be _executed_ before scanning the dir as the
1239 * code below may jump directly into error handling code (without executing
1240 * remaining code). And C doesn't have try {...} finally {}; :-)
1242 vfs_s_inode
*parent
= dir
->ent
->dir
;
1247 ent
= vfs_s_generate_entry(me
, ".", dir
, 0);
1248 ent
->ino
->st
=dir
->st
;
1250 vfs_s_insert_entry(me
, dir
, ent
);
1252 ent
= vfs_s_generate_entry(me
, "..", parent
, 0);
1253 ent
->ino
->st
=parent
->st
;
1255 vfs_s_insert_entry(me
, dir
, ent
);
1261 int res
= vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
), sock
);
1266 me
->verrno
= ECONNRESET
;
1268 disable_interrupt_key();
1269 get_reply(me
, SUP
.sock
, NULL
, 0);
1270 print_vfs_message (_("%s: failure"), me
->name
);
1275 fputs (buffer
, logfile
);
1276 fputs ("\n", logfile
);
1280 ent
= vfs_s_generate_entry(me
, NULL
, dir
, 0);
1281 i
= ent
->ino
->st
.st_nlink
;
1282 if (!vfs_parse_ls_lga (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1283 vfs_s_free_entry (me
, ent
);
1286 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1288 if ((!strcmp(ent
->name
, ".")) || (!strcmp (ent
->name
, ".."))) {
1290 ent
->name
= NULL
; /* Ouch, vfs_s_free_entry "knows" about . and .. being special :-( */
1291 vfs_s_free_entry (me
, ent
);
1295 vfs_s_insert_entry(me
, dir
, ent
);
1298 /* vfs_s_add_dots(me, dir, NULL);
1299 FIXME This really should be here; but we need to provide correct parent.
1300 Disabled for now, please fix it. Pavel
1303 me
->verrno
= E_REMOTE
;
1304 if ((get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
) || !num_entries
)
1307 if (SUP
.strict
== RFC_AUTODETECT
)
1308 SUP
.strict
= RFC_DARING
;
1312 if (resolve_symlinks
)
1313 resolve_symlink(me
, super
, dcache
);
1315 dcache
->symlink_status
= FTPFS_UNRESOLVED_SYMLINKS
;
1318 print_vfs_message (_("%s: done."), me
->name
);
1322 if (SUP
.strict
== RFC_AUTODETECT
) {
1323 /* It's our first attempt to get a directory listing from this
1324 server (UNIX style LIST command) */
1325 SUP
.strict
= RFC_STRICT
;
1326 /* I hate goto, but recursive call needs another 8K on stack */
1327 /* return dir_load (me, dir, remote_path); */
1331 print_vfs_message(_("ftpfs: failed; nowhere to fallback to"));
1336 file_store(vfs
*me
, vfs_s_fh
*fh
, char *name
, char *localname
)
1340 #ifdef HAVE_STRUCT_LINGER
1347 vfs_s_super
*super
= FH_SUPER
;
1349 h
= open(localname
, O_RDONLY
);
1353 sock
= open_data_connection(me
, super
, fh
->u
.ftp
.append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1358 #ifdef HAVE_STRUCT_LINGER
1361 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof(li
));
1363 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1367 enable_interrupt_key();
1369 while ((n
= read(h
, buffer
, sizeof(buffer
))) < 0) {
1370 if (errno
== EINTR
) {
1371 if (got_interrupt()) {
1383 while (write(sock
, buffer
, n
) < 0) {
1384 if (errno
== EINTR
) {
1385 if (got_interrupt()) {
1396 print_vfs_message(_("ftpfs: storing file %lu (%lu)"),
1397 (unsigned long) total
, (unsigned long) s
.st_size
);
1399 disable_interrupt_key();
1402 if (get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1406 disable_interrupt_key();
1409 get_reply(me
, SUP
.sock
, NULL
, 0);
1413 //#define FH_SOCK fh->u.ftp.sock
1416 linear_start(vfs
*me
, vfs_s_fh
*fh
, int offset
)
1418 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1422 FH_SOCK
= open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1426 fh
->linear
= LS_LINEAR_OPEN
;
1427 FH_SUPER
->u
.ftp
.control_connection_buzy
= 1;
1428 fh
->u
.ftp
.append
= 0;
1433 linear_read (vfs
*me
, vfs_s_fh
*fh
, void *buf
, int len
)
1436 vfs_s_super
*super
= FH_SUPER
;
1438 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1439 if ((errno
== EINTR
) && !got_interrupt())
1445 linear_abort(me
, fh
);
1448 SUP
.control_connection_buzy
= 0;
1451 if ((get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1452 ERRNOR (E_REMOTE
, -1);
1459 linear_close (vfs
*me
, vfs_s_fh
*fh
)
1462 linear_abort(me
, fh
);
1465 static int ftpfs_ctl (void *fh
, int ctlop
, int arg
)
1468 case MCCTL_IS_NOTREADY
:
1473 vfs_die ("You may not do this");
1474 if (FH
->linear
== LS_LINEAR_CLOSED
)
1477 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1478 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1487 /* Warning: filename passed to this command is damaged */
1489 send_ftp_command(vfs
*me
, char *filename
, char *cmd
, int flags
)
1494 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1496 if (!(rpath
= vfs_s_get_path_mangle(me
, filename
, &super
, 0)))
1498 p
= translate_path (me
, super
, rpath
);
1499 r
= command (me
, super
, WAIT_REPLY
, cmd
, p
);
1501 vfs_add_noncurrent_stamps (&vfs_ftpfs_ops
, (vfsid
) super
, NULL
);
1502 if (flags
& OPT_IGNORE_ERROR
)
1506 if (flush_directory_cache
)
1507 vfs_s_invalidate(me
, super
);
1511 /* This routine is called as the last step in load_setup */
1513 ftpfs_init_passwd(void)
1515 ftpfs_anonymous_passwd
= load_anon_passwd ();
1516 if (ftpfs_anonymous_passwd
)
1519 /* If there is no anonymous ftp password specified
1520 * then we'll just use anonymous@
1521 * We don't send any other thing because:
1522 * - We want to remain anonymous
1523 * - We want to stop SPAM
1524 * - We don't want to let ftp sites to discriminate by the user,
1527 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1530 static int ftpfs_chmod (vfs
*me
, char *path
, int mode
)
1532 char buf
[BUF_SMALL
];
1534 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1535 return send_ftp_command(me
, path
, buf
, OPT_FLUSH
);
1538 static int ftpfs_chown (vfs
*me
, char *path
, int owner
, int group
)
1544 /* Everyone knows it is not possible to chown remotely, so why bother them.
1545 If someone's root, then copy/move will always try to chown it... */
1550 static int ftpfs_unlink (vfs
*me
, char *path
)
1552 return send_ftp_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1555 /* Return 1 if path is the same directory as the one we are in now */
1557 is_same_dir (vfs
*me
, vfs_s_super
*super
, const char *path
)
1561 if (strcmp (path
, SUP
.cwdir
) == 0)
1567 ftpfs_chdir_internal (vfs
*me
, vfs_s_super
*super
, char *remote_path
)
1572 if (!SUP
.cwd_defered
&& is_same_dir (me
, super
, remote_path
))
1575 p
= translate_path (me
, super
, remote_path
);
1576 r
= command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1579 if (r
!= COMPLETE
) {
1583 SUP
.cwdir
= g_strdup (remote_path
);
1584 SUP
.cwd_defered
= 0;
1589 static int ftpfs_rename (vfs
*me
, char *path1
, char *path2
)
1591 send_ftp_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1592 return send_ftp_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1595 static int ftpfs_mkdir (vfs
*me
, char *path
, mode_t mode
)
1597 return send_ftp_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1600 static int ftpfs_rmdir (vfs
*me
, char *path
)
1602 return send_ftp_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1605 static int ftpfs_fh_open (vfs
*me
, vfs_s_fh
*fh
, int flags
, int mode
)
1607 fh
->u
.ftp
.append
= 0;
1608 /* File will be written only, so no need to retrieve it from ftp server */
1609 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
|O_RDWR
))){
1610 #ifdef HAVE_STRUCT_LINGER
1617 /* linear_start() called, so data will be written
1618 * to local temporary file and stored to ftp server
1619 * by vfs_s_close later
1621 if (FH_SUPER
->u
.ftp
.control_connection_buzy
){
1622 if (!fh
->ino
->localname
){
1623 int handle
= mc_mkstemps (&fh
->ino
->localname
, me
->name
, NULL
);
1627 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1631 name
= vfs_s_fullpath (me
, fh
->ino
);
1634 fh
->handle
= open_data_connection(me
, fh
->ino
->super
,
1635 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1640 #ifdef HAVE_STRUCT_LINGER
1644 setsockopt(fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof(li
));
1646 if (fh
->ino
->localname
){
1647 unlink (fh
->ino
->localname
);
1648 g_free (fh
->ino
->localname
);
1649 fh
->ino
->localname
= NULL
;
1654 if (!fh
->ino
->localname
)
1655 if (vfs_s_retrieve_file (me
, fh
->ino
)==-1)
1657 if (!fh
->ino
->localname
)
1658 vfs_die( "retrieve_file failed to fill in localname" );
1662 static int ftpfs_fh_close (vfs
*me
, vfs_s_fh
*fh
)
1664 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1667 /* File is stored to destination already, so
1668 * we prevent MEDATA->file_store() call from vfs_s_close ()
1671 if (get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1673 vfs_s_invalidate (me
, FH_SUPER
);
1678 static struct vfs_s_data ftp_data
= {
1684 NULL
, /* init_inode */
1685 NULL
, /* free_inode */
1686 NULL
, /* init_entry */
1688 NULL
, /* archive_check */
1693 ftpfs_fh_open
, /* fh_open */
1694 ftpfs_fh_close
, /* fh_close */
1696 vfs_s_find_entry_linear
,
1707 ftpfs_fill_names (vfs
*me
, void (*func
)(char *))
1709 struct vfs_s_super
* super
= ftp_data
.supers
;
1713 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, NULL
);
1716 super
= super
->next
;
1720 vfs vfs_ftpfs_ops
= {
1721 NULL
, /* This is place of next pointer */
1724 "ftp:", /* prefix */
1725 &ftp_data
, /* data */
1748 ftpfs_chown
, /* not really implemented but returns success */
1763 vfs_s_nothingisopen
,
1777 void ftpfs_set_debug (const char *file
)
1779 logfile
= fopen (file
, "w+");
1781 ftp_data
.logfile
= logfile
;
1784 static char buffer
[BUF_MEDIUM
];
1785 static char *netrc
, *netrcp
;
1787 /* This should match the keywords[] array below */
1800 static keyword_t
netrc_next (void)
1804 static const char *const keywords
[] = { "default", "machine",
1805 "login", "password", "passwd", "account", "macdef", NULL
1810 netrcp
= skip_separators (netrcp
);
1811 if (*netrcp
!= '\n')
1818 if (*netrcp
== '"') {
1819 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1820 if (*netrcp
== '\\')
1825 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1826 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1827 if (*netrcp
== '\\')
1837 while (keywords
[i
- 1]) {
1838 if (!strcmp (keywords
[i
- 1], buffer
))
1844 return NETRC_UNKNOWN
;
1847 static int netrc_has_incorrect_mode (char *netrcname
, char *netrc
)
1849 static int be_angry
= 1;
1852 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1854 message_1s (1, MSG_ERROR
,
1855 _("~/.netrc file has not correct mode.\n"
1856 "Remove password or correct mode."));
1864 /* Scan .netrc until we find matching "machine" or "default"
1865 * domain is used for additional matching
1866 * No search is done after "default" in compliance with "man netrc"
1867 * Return 0 if found, -1 otherwise */
1868 static int find_machine (const char *host
, const char *domain
)
1872 while ((keyword
= netrc_next ()) != NETRC_NONE
) {
1873 if (keyword
== NETRC_DEFAULT
)
1876 if (keyword
== NETRC_MACDEF
) {
1877 /* Scan for an empty line, which concludes "macdef" */
1879 while (*netrcp
&& *netrcp
!= '\n')
1881 if (*netrcp
!= '\n')
1884 } while (*netrcp
&& *netrcp
!= '\n');
1888 if (keyword
!= NETRC_MACHINE
)
1891 /* Take machine name */
1892 if (netrc_next () == NETRC_NONE
)
1895 if (g_strcasecmp (host
, buffer
)) {
1896 /* Try adding our domain to short names in .netrc */
1897 char *host_domain
= strchr (host
, '.');
1901 /* Compare domain part */
1902 if (g_strcasecmp (host_domain
, domain
))
1905 /* Compare local part */
1906 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1917 /* Extract login and password from .netrc for the host.
1919 * Returns 0 for success, -1 for error */
1920 static int lookup_netrc (const char *host
, char **login
, char **pass
)
1923 char *tmp_pass
= NULL
;
1924 char hostname
[MAXHOSTNAMELEN
], *domain
;
1926 static struct rupcache
{
1927 struct rupcache
*next
;
1931 } *rup_cache
= NULL
, *rupp
;
1933 /* Initialize *login and *pass */
1940 /* Look up in the cache first */
1941 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
1942 if (!strcmp (host
, rupp
->host
)) {
1944 *login
= g_strdup (rupp
->login
);
1945 if (pass
&& rupp
->pass
)
1946 *pass
= g_strdup (rupp
->pass
);
1951 /* Load current .netrc */
1952 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
1953 netrcp
= netrc
= load_file (netrcname
);
1954 if (netrc
== NULL
) {
1959 /* Find our own domain name */
1960 if (gethostname (hostname
, sizeof (hostname
)) < 0)
1962 if (!(domain
= strchr (hostname
, '.')))
1965 /* Scan for "default" and matching "machine" keywords */
1966 find_machine (host
, domain
);
1968 /* Scan for keywords following "default" and "machine" */
1971 keyword
= netrc_next ();
1975 if (netrc_next () == NETRC_NONE
) {
1980 /* We have another name already - should not happen */
1986 /* We have login name now */
1987 *login
= g_strdup (buffer
);
1990 case NETRC_PASSWORD
:
1992 if (netrc_next () == NETRC_NONE
) {
1997 /* Ignore unsafe passwords */
1998 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
1999 && netrc_has_incorrect_mode (netrcname
, netrc
)) {
2004 /* Remember password. pass may be NULL, so use tmp_pass */
2005 if (tmp_pass
== NULL
)
2006 tmp_pass
= g_strdup (buffer
);
2010 /* "account" is followed by a token which we ignore */
2011 if (netrc_next () == NETRC_NONE
) {
2016 /* Ignore account, but warn user anyways */
2017 netrc_has_incorrect_mode (netrcname
, netrc
);
2021 /* Unexpected keyword or end of file */
2033 rupp
= g_new (struct rupcache
, 1);
2034 rupp
->host
= g_strdup (host
);
2035 rupp
->login
= rupp
->pass
= 0;
2037 if (*login
!= NULL
) {
2038 rupp
->login
= g_strdup (*login
);
2040 if (tmp_pass
!= NULL
)
2041 rupp
->pass
= g_strdup (tmp_pass
);
2042 rupp
->next
= rup_cache
;