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
89 #define INADDR_NONE 0xffffffff
96 /* Delay to retry a connection */
97 int ftpfs_retry_seconds
= 30;
99 /* Method to use to connect to ftp sites */
100 int ftpfs_use_passive_connections
= 1;
102 /* Method used to get directory listings:
103 * 1: try 'LIST -la <path>', if it fails
104 * fall back to CWD <path>; LIST
105 * 0: always use CWD <path>; LIST
107 int ftpfs_use_unix_list_options
= 1;
109 /* First "CWD <path>", then "LIST -la ." */
110 int ftpfs_first_cd_then_ls
;
112 /* Use the ~/.netrc */
115 /* Anonymous setup */
116 char *ftpfs_anonymous_passwd
= NULL
;
117 int ftpfs_directory_timeout
= 900;
120 char *ftpfs_proxy_host
= NULL
;
122 /* wether we have to use proxy by default? */
123 int ftpfs_always_use_proxy
;
125 /* source routing host */
126 extern int source_route
;
128 /* Where we store the transactions */
129 static FILE *logfile
= NULL
;
131 /* If true, the directory cache is forced to reload */
132 static int force_expiration
= 0;
134 #ifdef FIXME_LATER_ALIGATOR
135 static struct linklist
*connections_list
;
138 /* command wait_flag: */
140 #define WAIT_REPLY 0x01
141 #define WANT_STRING 0x02
142 static char reply_str
[80];
144 /* char *translate_path (struct ftpfs_connection *bucket, char *remote_path)
145 Translate a Unix path, i.e. MC's internal path representation (e.g.
146 /somedir/somefile) to a path valid for the remote server. Every path
147 transfered to the remote server has to be mangled by this function
148 right prior to sending it.
149 Currently only Amiga ftp servers are handled in a special manner.
151 When the remote server is an amiga:
152 a) strip leading slash if necesarry
153 b) replace first occurance of ":/" with ":"
154 c) strip trailing "/."
157 static char *ftpfs_get_current_directory (vfs
*me
, vfs_s_super
*super
);
158 static int ftpfs_chdir_internal (vfs
*me
, vfs_s_super
*super
, char *remote_path
);
159 static int command (vfs
*me
, vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
160 __attribute__ ((format (printf
, 4, 5)));
161 static int ftpfs_open_socket (vfs
*me
, vfs_s_super
*super
);
162 static int login_server (vfs
*me
, vfs_s_super
*super
, const char *netrcpass
);
163 static int lookup_netrc (const char *host
, char **login
, char **pass
);
166 translate_path (vfs
*me
, vfs_s_super
*super
, const char *remote_path
)
168 if (!SUP
.remote_is_amiga
)
169 return g_strdup (remote_path
);
174 fprintf (logfile
, "MC -- translate_path: %s\n", remote_path
);
178 /* strip leading slash(es) */
179 while (*remote_path
== '/')
183 * Don't change "/" into "", e.g. "CWD " would be
186 if (*remote_path
== '\0')
187 return g_strdup (".");
189 ret
= g_strdup (remote_path
);
191 /* replace first occurance of ":/" with ":" */
192 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
193 strcpy (p
+ 1, p
+ 2);
195 /* strip trailing "/." */
196 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
202 /* Extract the hostname and username from the path */
205 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
206 * ftp://sunsite.unc.edu/pub/linux
207 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
208 * ftp://tsx-11.mit.edu:8192/
209 * ftp://joe@foo.edu:11321/private
210 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
215 #define FTP_COMMAND_PORT 21
218 ftp_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
222 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
226 /* Look up user and password in netrc */
228 lookup_netrc (*host
, user
, pass
);
230 *user
= g_strdup ("anonymous");
233 /* Look up password in netrc for known user */
234 if (use_netrc
&& *user
&& pass
&& !*pass
) {
237 lookup_netrc (*host
, &new_user
, pass
);
239 /* If user is different, remove password */
240 if (new_user
&& strcmp (*user
, new_user
)) {
252 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
254 get_reply (vfs
*me
, int sock
, char *string_buf
, int string_len
)
260 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
266 switch (sscanf(answer
, "%d", &code
)){
269 strncpy (string_buf
, answer
, string_len
- 1);
270 *(string_buf
+ string_len
- 1) = 0;
275 if (answer
[3] == '-') {
277 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
283 if ((sscanf (answer
, "%d", &i
) > 0) &&
284 (code
== i
) && (answer
[3] == ' '))
289 strncpy (string_buf
, answer
, string_len
- 1);
290 *(string_buf
+ string_len
- 1) = 0;
298 reconnect (vfs
*me
, vfs_s_super
*super
)
300 int sock
= ftpfs_open_socket (me
, super
);
302 char *cwdir
= SUP
.cwdir
;
306 if (login_server (me
, super
, SUP
.password
)){
309 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
311 return sock
== COMPLETE
;
319 command (vfs
*me
, vfs_s_super
*super
, int wait_reply
, const char *fmt
, ...)
327 fmt_str
= g_strdup_vprintf (fmt
, ap
);
330 status
= strlen (fmt_str
);
331 str
= g_realloc (fmt_str
, status
+ 3);
332 strcpy (str
+ status
, "\r\n");
335 if (strncmp (str
, "PASS ", 5) == 0){
336 fputs ("PASS <Password not logged>\r\n", logfile
);
338 fwrite (str
, status
+ 2, 1, logfile
);
344 enable_interrupt_key ();
345 status
= write (SUP
.sock
, str
, status
+ 2);
350 if (errno
== EPIPE
){ /* Remote server has closed connection */
351 static int level
= 0; /* login_server() use command() */
354 status
= reconnect (me
, super
);
356 if (status
&& write (SUP
.sock
, str
, status
+ 2) > 0)
362 disable_interrupt_key ();
367 disable_interrupt_key ();
370 return get_reply (me
, sock
, (wait_reply
& WANT_STRING
) ? reply_str
: NULL
, sizeof (reply_str
)-1);
375 free_archive (vfs
*me
, vfs_s_super
*super
)
378 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
379 command(me
, super
, NONE
, "QUIT");
385 g_free (SUP
.password
);
388 /* some defines only used by changetype */
389 /* These two are valid values for the second parameter */
391 #define TYPE_BINARY 1
393 /* This one is only used to initialize bucket->isbinary, don't use it as
394 second parameter to changetype. */
395 #define TYPE_UNKNOWN -1
398 changetype (vfs
*me
, vfs_s_super
*super
, int binary
)
400 if (binary
!= SUP
.isbinary
) {
401 if (command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
403 SUP
.isbinary
= binary
;
408 /* This routine logs the user in */
410 login_server (vfs
*me
, vfs_s_super
*super
, const char *netrcpass
)
414 char *name
; /* login user name */
416 char reply_string
[BUF_MEDIUM
];
418 SUP
.isbinary
= TYPE_UNKNOWN
;
420 op
= g_strdup (netrcpass
);
422 if (!strcmp (SUP
.user
, "anonymous") ||
423 !strcmp (SUP
.user
, "ftp")) {
424 if (!ftpfs_anonymous_passwd
)
426 op
= g_strdup (ftpfs_anonymous_passwd
);
432 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
,
434 op
= vfs_get_password (p
);
438 SUP
.password
= g_strdup (op
);
440 op
= g_strdup (SUP
.password
);
444 if (!anon
|| logfile
)
447 pass
= g_strconcat ("-", op
, NULL
);
451 /* Proxy server accepts: username@host-we-want-to-connect*/
453 name
= g_strconcat (SUP
.user
, "@",
454 SUP
.host
[0] == '!' ? SUP
.host
+1 : SUP
.host
, NULL
);
456 name
= g_strdup (SUP
.user
);
458 if (get_reply (me
, SUP
.sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
) {
459 g_strup (reply_string
);
460 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
462 fprintf (logfile
, "MC -- remote_is_amiga = %d\n", SUP
.remote_is_amiga
);
466 print_vfs_message (_("ftpfs: sending login name"));
467 code
= command (me
, super
, WAIT_REPLY
, "USER %s", name
);
471 print_vfs_message (_("ftpfs: sending user password"));
472 if (command (me
, super
, WAIT_REPLY
, "PASS %s", pass
) != COMPLETE
)
476 print_vfs_message (_("ftpfs: logged in"));
477 wipe_password (pass
);
482 SUP
.failed_on_login
= 1;
485 wipe_password (SUP
.password
);
491 message_2s (1, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "), SUP
.user
);
493 wipe_password (pass
);
498 #ifdef HAVE_SETSOCKOPT
500 setup_source_route (int socket
, int dest
)
507 memset (buffer
, 0, sizeof (buffer
));
510 *ptr
++ = 4; /* pointer */
513 memcpy (ptr
, (char *) &source_route
, sizeof (int));
516 /* Second hop (ie, final destination) */
517 memcpy (ptr
, (char *) &dest
, sizeof (int));
519 while ((ptr
- buffer
) & 3)
521 if (setsockopt (socket
, IPPROTO_IP
, IP_OPTIONS
,
522 buffer
, ptr
- buffer
) < 0)
523 message_2s (1, MSG_ERROR
, _(" Cannot set source routing (%s)"), unix_error_string (errno
));
526 #define setup_source_route(x,y)
529 static struct no_proxy_entry
{
535 load_no_proxy_list (void)
537 /* FixMe: shouldn't be hardcoded!!! */
538 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
539 struct no_proxy_entry
*np
, *current
= 0;
543 static char *mc_file
;
548 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
549 if (exist_file (mc_file
) &&
550 (npf
= fopen (mc_file
, "r"))) {
551 while (fgets (s
, sizeof(s
), npf
) || !(feof (npf
) || ferror (npf
))) {
552 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
553 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
563 np
= g_new (struct no_proxy_entry
, 1);
564 np
->domain
= g_strdup (s
);
578 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
580 ftpfs_check_proxy (const char *host
)
582 struct no_proxy_entry
*npe
;
584 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
585 return 0; /* sanity check */
590 if (!ftpfs_always_use_proxy
)
593 if (!strchr (host
, '.'))
596 load_no_proxy_list ();
597 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
598 char *domain
= npe
->domain
;
600 if (domain
[0] == '.') {
601 int ld
= strlen (domain
);
602 int lh
= strlen (host
);
604 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
612 if (!g_strcasecmp (host
, domain
))
620 ftpfs_get_proxy_host_and_port (char *proxy
, char **host
, int *port
)
625 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
632 ftpfs_open_socket (vfs
*me
, vfs_s_super
*super
)
634 struct sockaddr_in server_address
;
641 /* Use a proxy host? */
644 if (!host
|| !*host
){
645 print_vfs_message (_("ftpfs: Invalid host name."));
650 /* Hosts to connect to that start with a ! should use proxy */
652 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
656 /* Get host address */
657 memset ((char *) &server_address
, 0, sizeof (server_address
));
658 server_address
.sin_family
= AF_INET
;
659 server_address
.sin_addr
.s_addr
= inet_addr (host
);
660 if (server_address
.sin_addr
.s_addr
== INADDR_NONE
) {
661 hp
= gethostbyname (host
);
663 print_vfs_message (_("ftpfs: Invalid host address."));
669 server_address
.sin_family
= hp
->h_addrtype
;
671 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
672 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
, 4);
675 server_address
.sin_port
= htons (port
);
678 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
684 setup_source_route (my_socket
, server_address
.sin_addr
.s_addr
);
686 print_vfs_message (_("ftpfs: making connection to %s"), host
);
690 enable_interrupt_key (); /* clear the interrupt flag */
692 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
693 sizeof (server_address
)) < 0){
695 if (errno
== EINTR
&& got_interrupt ())
696 print_vfs_message (_("ftpfs: connection interrupted by user"));
698 print_vfs_message (_("ftpfs: connection to server failed: %s"),
699 unix_error_string(errno
));
700 disable_interrupt_key();
704 disable_interrupt_key();
709 open_archive_int (vfs
*me
, vfs_s_super
*super
)
711 int retry_seconds
, count_down
;
713 /* We do not want to use the passive if we are using proxies */
715 SUP
.use_passive_connection
= 0;
719 SUP
.failed_on_login
= 0;
721 SUP
.sock
= ftpfs_open_socket (me
, super
);
725 if (login_server (me
, super
, NULL
)) {
726 /* Logged in, no need to retry the connection */
729 if (SUP
.failed_on_login
){
730 /* Close only the socket descriptor */
735 if (ftpfs_retry_seconds
){
736 retry_seconds
= ftpfs_retry_seconds
;
737 enable_interrupt_key ();
738 for (count_down
= retry_seconds
; count_down
; count_down
--){
739 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
741 if (got_interrupt ()){
743 disable_interrupt_key ();
747 disable_interrupt_key ();
750 } while (retry_seconds
);
752 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
754 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
759 open_archive (vfs
*me
, vfs_s_super
*super
, char *archive_name
, char *op
)
761 char *host
, *user
, *password
;
764 ftp_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
771 if (ftpfs_check_proxy (host
))
772 SUP
.proxy
= ftpfs_proxy_host
;
773 SUP
.password
= password
;
774 SUP
.use_passive_connection
= ftpfs_use_passive_connections
| source_route
;
775 SUP
.use_source_route
= source_route
;
776 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
777 SUP
.isbinary
= TYPE_UNKNOWN
;
778 SUP
.remote_is_amiga
= 0;
779 super
->name
= g_strdup("/");
780 super
->root
= vfs_s_new_inode (me
, super
, vfs_s_default_stat(me
, S_IFDIR
| 0755));
782 return open_archive_int (me
, super
);
786 archive_same(vfs
*me
, vfs_s_super
*super
, char *archive_name
, char *op
, void *cookie
)
791 ftp_split_url (strchr(op
, ':') + 1, &host
, &user
, &port
, 0);
793 port
= ((strcmp (host
, SUP
.host
) == 0) &&
794 (strcmp (user
, SUP
.user
) == 0) &&
804 ftpfs_flushdir (void)
806 force_expiration
= 1;
810 dir_uptodate(vfs
*me
, vfs_s_inode
*ino
)
814 if (force_expiration
) {
815 force_expiration
= 0;
818 gettimeofday(&tim
, NULL
);
819 if (tim
.tv_sec
< ino
->u
.ftp
.timestamp
.tv_sec
)
824 /* The returned directory should always contain a trailing slash */
826 ftpfs_get_current_directory (vfs
*me
, vfs_s_super
*super
)
828 char buf
[BUF_8K
], *bufp
, *bufq
;
830 if (command (me
, super
, NONE
, "PWD") == COMPLETE
&&
831 get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
833 for (bufq
= buf
; *bufq
; bufq
++)
840 if (*(bufq
- 1) != '/') {
845 return g_strdup (bufp
);
847 /* If the remote server is an Amiga a leading slash
848 might be missing. MC needs it because it is used
849 as seperator between hostname and path internally. */
850 return g_strconcat( "/", bufp
, 0);
864 /* Setup Passive ftp connection, we use it for source routed connections */
866 setup_passive (vfs
*me
, vfs_s_super
*super
, int my_socket
, struct sockaddr_in
*sa
)
868 int xa
, xb
, xc
, xd
, xe
, xf
;
872 if (command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
875 /* Parse remote parameters */
876 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++)
880 if (!isdigit ((unsigned char) *c
))
882 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
884 n
[0] = (unsigned char) xa
;
885 n
[1] = (unsigned char) xb
;
886 n
[2] = (unsigned char) xc
;
887 n
[3] = (unsigned char) xd
;
888 n
[4] = (unsigned char) xe
;
889 n
[5] = (unsigned char) xf
;
891 memcpy (&(sa
->sin_addr
.s_addr
), (void *)n
, 4);
892 memcpy (&(sa
->sin_port
), (void *)&n
[4], 2);
893 setup_source_route (my_socket
, sa
->sin_addr
.s_addr
);
894 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
900 initconn (vfs
*me
, vfs_s_super
*super
)
902 struct sockaddr_in data_addr
;
903 int data
, len
= sizeof(data_addr
);
906 getsockname(SUP
.sock
, (struct sockaddr
*) &data_addr
, &len
);
907 data_addr
.sin_port
= 0;
909 pe
= getprotobyname("tcp");
912 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
916 if (SUP
.use_passive_connection
){
917 if ((SUP
.use_passive_connection
= setup_passive (me
, super
, data
, &data_addr
)))
920 SUP
.use_source_route
= 0;
921 SUP
.use_passive_connection
= 0;
922 print_vfs_message (_("ftpfs: could not setup passive mode"));
925 /* If passive setup fails, fallback to active connections */
926 /* Active FTP connection */
927 if ((bind (data
, (struct sockaddr
*)&data_addr
, len
) == 0) &&
928 (getsockname (data
, (struct sockaddr
*) &data_addr
, &len
) == 0) &&
929 (listen (data
, 1) == 0))
931 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
932 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
934 if (command (me
, super
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
935 a
[2], a
[3], p
[0], p
[1]) == COMPLETE
)
944 open_data_connection (vfs
*me
, vfs_s_super
*super
, char *cmd
, char *remote
,
945 int isbinary
, int reget
)
947 struct sockaddr_in from
;
948 int s
, j
, data
, fromlen
= sizeof(from
);
950 if ((s
= initconn (me
, super
)) == -1)
952 if (changetype (me
, super
, isbinary
) == -1)
955 j
= command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
960 char * remote_path
= translate_path (me
, super
, remote
);
961 j
= command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
962 /* WarFtpD can't STORE //filename */
963 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
964 g_free (remote_path
);
966 j
= command (me
, super
, WAIT_REPLY
, "%s", cmd
);
969 enable_interrupt_key();
970 if (SUP
.use_passive_connection
)
973 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
980 disable_interrupt_key();
984 #define ABORT_TIMEOUT 5
986 linear_abort (vfs
*me
, vfs_s_fh
*fh
)
988 vfs_s_super
*super
= FH_SUPER
;
989 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
994 SUP
.control_connection_buzy
= 0;
996 print_vfs_message (_("ftpfs: aborting transfer."));
997 if (send (SUP
.sock
, ipbuf
, sizeof (ipbuf
), MSG_OOB
) != sizeof (ipbuf
)) {
998 print_vfs_message (_("ftpfs: abort error: %s"),
999 unix_error_string (errno
));
1005 if (command (me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
) {
1006 print_vfs_message (_("ftpfs: abort failed"));
1013 FD_SET (dsock
, &mask
);
1014 if (select (dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0) {
1015 struct timeval start_tim
, tim
;
1016 gettimeofday (&start_tim
, NULL
);
1017 /* flush the remaining data */
1018 while (read (dsock
, buf
, sizeof (buf
)) > 0) {
1019 gettimeofday (&tim
, NULL
);
1020 if (tim
.tv_sec
> start_tim
.tv_sec
+ ABORT_TIMEOUT
) {
1021 /* server keeps sending, drop the connection and reconnect */
1022 reconnect (me
, super
);
1029 if ((get_reply (me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1030 get_reply (me
, SUP
.sock
, NULL
, 0);
1035 resolve_symlink_without_ls_options(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1037 struct linklist
*flist
;
1038 struct direntry
*fe
, *fel
;
1039 char tmp
[MC_MAXPATHLEN
];
1042 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1043 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1044 /* flist->data->l_stat is alread initialized with 0 */
1046 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1047 if (fel
->linkname
[0] == '/') {
1048 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1050 strcpy (tmp
, fel
->linkname
);
1052 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1054 strcpy (tmp
, dir
->remote_path
);
1057 strcat (tmp
+ 1, fel
->linkname
);
1059 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1060 canonicalize_pathname (tmp
);
1061 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1063 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1064 /* Symlink points to link which isn't resolved, yet. */
1065 if (fe
->linkname
[0] == '/') {
1066 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1068 strcpy (tmp
, fe
->linkname
);
1070 /* at this point tmp looks always like this
1071 /directory/filename, i.e. no need to check
1072 strrchr's return value */
1073 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1074 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1076 strcat (tmp
, fe
->linkname
);
1080 fel
->l_stat
= g_new (struct stat
, 1);
1081 if ( S_ISLNK (fe
->s
.st_mode
))
1082 *fel
->l_stat
= *fe
->l_stat
;
1084 *fel
->l_stat
= fe
->s
;
1085 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1092 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1096 resolve_symlink_with_ls_options(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1098 char buffer
[2048] = "", *filename
;
1102 struct linklist
*flist
;
1103 struct direntry
*fe
;
1104 int switch_method
= 0;
1106 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1107 if (strchr (dir
->remote_path
, ' ')) {
1108 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1109 print_vfs_message(_("ftpfs: CWD failed."));
1112 sock
= open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1115 sock
= open_data_connection (bucket
, "LIST -lLa",
1116 dir
->remote_path
, TYPE_ASCII
, 0);
1119 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1123 fp
= fdopen(sock
, "r");
1126 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1129 enable_interrupt_key();
1130 flist
= dir
->file_list
->next
;
1133 if (flist
== dir
->file_list
)
1136 flist
= flist
->next
;
1137 } while (!S_ISLNK(fe
->s
.st_mode
));
1139 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1142 fputs (buffer
, logfile
);
1145 vfs_die("This code should be commented out\n");
1146 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1147 int r
= strcmp(fe
->name
, filename
);
1150 if (S_ISLNK (s
.st_mode
)) {
1151 /* This server doesn't understand LIST -lLa */
1155 fe
->l_stat
= g_new (struct stat
, 1);
1156 if (fe
->l_stat
== NULL
)
1159 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1168 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1169 disable_interrupt_key();
1171 get_reply(me
, SUP
.sock
, NULL
, 0);
1175 resolve_symlink(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1177 print_vfs_message(_("Resolving symlink..."));
1179 if (SUP
.strict_rfc959_list_cmd
)
1180 resolve_symlink_without_ls_options(me
, super
, dir
);
1182 resolve_symlink_with_ls_options(me
, super
, dir
);
1187 dir_load(vfs
*me
, vfs_s_inode
*dir
, char *remote_path
)
1190 vfs_s_super
*super
= dir
->super
;
1191 int sock
, num_entries
= 0;
1193 int has_symlinks
= 0;
1195 char buffer
[BUF_8K
];
1198 cd_first
= ftpfs_first_cd_then_ls
|| (strchr (remote_path
, ' ') != NULL
)
1199 || (SUP
.strict
== RFC_STRICT
);
1202 print_vfs_message(_("ftpfs: Reading FTP directory %s... %s%s"), remote_path
,
1203 SUP
.strict
== RFC_STRICT
? _("(strict rfc959)") : "",
1204 cd_first
? _("(chdir first)") : "");
1209 p
= translate_path (me
, super
, remote_path
);
1211 if (ftpfs_chdir_internal (me
, super
, p
) != COMPLETE
) {
1214 print_vfs_message(_("ftpfs: CWD failed."));
1220 gettimeofday(&dir
->u
.ftp
.timestamp
, NULL
);
1221 dir
->u
.ftp
.timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1223 if (SUP
.strict
== RFC_STRICT
)
1224 sock
= open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1226 /* Dirty hack to avoid autoprepending / to . */
1227 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1228 sock
= open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1230 /* Trailing "/." is necessary if remote_path is a symlink */
1231 char *path
= concat_dir_and_file (remote_path
, ".");
1232 sock
= open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1239 /* Clear the interrupt flag */
1240 enable_interrupt_key ();
1244 /* added 20001006 by gisburn
1245 * add dots '.' and '..'. This must be _executed_ before scanning the dir as the
1246 * code below may jump directly into error handling code (without executing
1247 * remaining code). And C doesn't have try {...} finally {}; :-)
1249 vfs_s_inode
*parent
= dir
->ent
->dir
;
1254 ent
= vfs_s_generate_entry(me
, ".", dir
, 0);
1255 ent
->ino
->st
=dir
->st
;
1257 vfs_s_insert_entry(me
, dir
, ent
);
1259 ent
= vfs_s_generate_entry(me
, "..", parent
, 0);
1260 ent
->ino
->st
=parent
->st
;
1262 vfs_s_insert_entry(me
, dir
, ent
);
1268 int res
= vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
), sock
);
1273 me
->verrno
= ECONNRESET
;
1275 disable_interrupt_key();
1276 get_reply(me
, SUP
.sock
, NULL
, 0);
1277 print_vfs_message (_("%s: failure"), me
->name
);
1282 fputs (buffer
, logfile
);
1283 fputs ("\n", logfile
);
1287 ent
= vfs_s_generate_entry(me
, NULL
, dir
, 0);
1288 i
= ent
->ino
->st
.st_nlink
;
1289 if (!vfs_parse_ls_lga (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1290 vfs_s_free_entry (me
, ent
);
1293 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1295 if ((!strcmp(ent
->name
, ".")) || (!strcmp (ent
->name
, ".."))) {
1297 ent
->name
= NULL
; /* Ouch, vfs_s_free_entry "knows" about . and .. being special :-( */
1298 vfs_s_free_entry (me
, ent
);
1302 vfs_s_insert_entry(me
, dir
, ent
);
1305 /* vfs_s_add_dots(me, dir, NULL);
1306 FIXME This really should be here; but we need to provide correct parent.
1307 Disabled for now, please fix it. Pavel
1310 me
->verrno
= E_REMOTE
;
1311 if ((get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
) || !num_entries
)
1314 if (SUP
.strict
== RFC_AUTODETECT
)
1315 SUP
.strict
= RFC_DARING
;
1319 if (resolve_symlinks
)
1320 resolve_symlink(me
, super
, dcache
);
1322 dcache
->symlink_status
= FTPFS_UNRESOLVED_SYMLINKS
;
1325 print_vfs_message (_("%s: done."), me
->name
);
1329 if (SUP
.strict
== RFC_AUTODETECT
) {
1330 /* It's our first attempt to get a directory listing from this
1331 server (UNIX style LIST command) */
1332 SUP
.strict
= RFC_STRICT
;
1333 /* I hate goto, but recursive call needs another 8K on stack */
1334 /* return dir_load (me, dir, remote_path); */
1338 print_vfs_message(_("ftpfs: failed; nowhere to fallback to"));
1343 file_store(vfs
*me
, vfs_s_fh
*fh
, char *name
, char *localname
)
1347 #ifdef HAVE_STRUCT_LINGER
1354 vfs_s_super
*super
= FH_SUPER
;
1356 h
= open(localname
, O_RDONLY
);
1360 sock
= open_data_connection(me
, super
, fh
->u
.ftp
.append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1365 #ifdef HAVE_STRUCT_LINGER
1368 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof(li
));
1370 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1374 enable_interrupt_key();
1376 while ((n
= read(h
, buffer
, sizeof(buffer
))) < 0) {
1377 if (errno
== EINTR
) {
1378 if (got_interrupt()) {
1390 while (write(sock
, buffer
, n
) < 0) {
1391 if (errno
== EINTR
) {
1392 if (got_interrupt()) {
1403 print_vfs_message(_("ftpfs: storing file %lu (%lu)"),
1404 (unsigned long) total
, (unsigned long) s
.st_size
);
1406 disable_interrupt_key();
1409 if (get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1413 disable_interrupt_key();
1416 get_reply(me
, SUP
.sock
, NULL
, 0);
1421 linear_start(vfs
*me
, vfs_s_fh
*fh
, int offset
)
1423 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1427 FH_SOCK
= open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1431 fh
->linear
= LS_LINEAR_OPEN
;
1432 FH_SUPER
->u
.ftp
.control_connection_buzy
= 1;
1433 fh
->u
.ftp
.append
= 0;
1438 linear_read (vfs
*me
, vfs_s_fh
*fh
, void *buf
, int len
)
1441 vfs_s_super
*super
= FH_SUPER
;
1443 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1444 if ((errno
== EINTR
) && !got_interrupt())
1450 linear_abort(me
, fh
);
1453 SUP
.control_connection_buzy
= 0;
1456 if ((get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1457 ERRNOR (E_REMOTE
, -1);
1464 linear_close (vfs
*me
, vfs_s_fh
*fh
)
1467 linear_abort(me
, fh
);
1470 static int ftpfs_ctl (void *fh
, int ctlop
, int arg
)
1473 case MCCTL_IS_NOTREADY
:
1478 vfs_die ("You may not do this");
1479 if (FH
->linear
== LS_LINEAR_CLOSED
)
1482 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1483 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1492 /* Warning: filename passed to this command is damaged */
1494 send_ftp_command(vfs
*me
, char *filename
, char *cmd
, int flags
)
1499 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1501 if (!(rpath
= vfs_s_get_path_mangle(me
, filename
, &super
, 0)))
1503 p
= translate_path (me
, super
, rpath
);
1504 r
= command (me
, super
, WAIT_REPLY
, cmd
, p
);
1506 vfs_add_noncurrent_stamps (&vfs_ftpfs_ops
, (vfsid
) super
, NULL
);
1507 if (flags
& OPT_IGNORE_ERROR
)
1511 if (flush_directory_cache
)
1512 vfs_s_invalidate(me
, super
);
1516 /* This routine is called as the last step in load_setup */
1518 ftpfs_init_passwd(void)
1520 ftpfs_anonymous_passwd
= load_anon_passwd ();
1521 if (ftpfs_anonymous_passwd
)
1524 /* If there is no anonymous ftp password specified
1525 * then we'll just use anonymous@
1526 * We don't send any other thing because:
1527 * - We want to remain anonymous
1528 * - We want to stop SPAM
1529 * - We don't want to let ftp sites to discriminate by the user,
1532 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1535 static int ftpfs_chmod (vfs
*me
, char *path
, int mode
)
1537 char buf
[BUF_SMALL
];
1539 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1540 return send_ftp_command(me
, path
, buf
, OPT_FLUSH
);
1543 static int ftpfs_chown (vfs
*me
, char *path
, int owner
, int group
)
1549 /* Everyone knows it is not possible to chown remotely, so why bother them.
1550 If someone's root, then copy/move will always try to chown it... */
1555 static int ftpfs_unlink (vfs
*me
, char *path
)
1557 return send_ftp_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1560 /* Return 1 if path is the same directory as the one we are in now */
1562 is_same_dir (vfs
*me
, vfs_s_super
*super
, const char *path
)
1566 if (strcmp (path
, SUP
.cwdir
) == 0)
1572 ftpfs_chdir_internal (vfs
*me
, vfs_s_super
*super
, char *remote_path
)
1577 if (!SUP
.cwd_defered
&& is_same_dir (me
, super
, remote_path
))
1580 p
= translate_path (me
, super
, remote_path
);
1581 r
= command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1584 if (r
!= COMPLETE
) {
1588 SUP
.cwdir
= g_strdup (remote_path
);
1589 SUP
.cwd_defered
= 0;
1594 static int ftpfs_rename (vfs
*me
, char *path1
, char *path2
)
1596 send_ftp_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1597 return send_ftp_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1600 static int ftpfs_mkdir (vfs
*me
, char *path
, mode_t mode
)
1602 return send_ftp_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1605 static int ftpfs_rmdir (vfs
*me
, char *path
)
1607 return send_ftp_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1610 static int ftpfs_fh_open (vfs
*me
, vfs_s_fh
*fh
, int flags
, int mode
)
1612 fh
->u
.ftp
.append
= 0;
1613 /* File will be written only, so no need to retrieve it from ftp server */
1614 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
|O_RDWR
))){
1615 #ifdef HAVE_STRUCT_LINGER
1622 /* linear_start() called, so data will be written
1623 * to local temporary file and stored to ftp server
1624 * by vfs_s_close later
1626 if (FH_SUPER
->u
.ftp
.control_connection_buzy
){
1627 if (!fh
->ino
->localname
){
1628 int handle
= mc_mkstemps (&fh
->ino
->localname
, me
->name
, NULL
);
1632 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1636 name
= vfs_s_fullpath (me
, fh
->ino
);
1639 fh
->handle
= open_data_connection(me
, fh
->ino
->super
,
1640 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1645 #ifdef HAVE_STRUCT_LINGER
1649 setsockopt(fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof(li
));
1651 if (fh
->ino
->localname
){
1652 unlink (fh
->ino
->localname
);
1653 g_free (fh
->ino
->localname
);
1654 fh
->ino
->localname
= NULL
;
1659 if (!fh
->ino
->localname
)
1660 if (vfs_s_retrieve_file (me
, fh
->ino
)==-1)
1662 if (!fh
->ino
->localname
)
1663 vfs_die( "retrieve_file failed to fill in localname" );
1667 static int ftpfs_fh_close (vfs
*me
, vfs_s_fh
*fh
)
1669 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1672 /* File is stored to destination already, so
1673 * we prevent MEDATA->file_store() call from vfs_s_close ()
1676 if (get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1678 vfs_s_invalidate (me
, FH_SUPER
);
1683 static struct vfs_s_data ftp_data
= {
1689 NULL
, /* init_inode */
1690 NULL
, /* free_inode */
1691 NULL
, /* init_entry */
1693 NULL
, /* archive_check */
1698 ftpfs_fh_open
, /* fh_open */
1699 ftpfs_fh_close
, /* fh_close */
1701 vfs_s_find_entry_linear
,
1712 ftpfs_fill_names (vfs
*me
, void (*func
)(char *))
1714 struct vfs_s_super
* super
= ftp_data
.supers
;
1718 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, NULL
);
1721 super
= super
->next
;
1725 vfs vfs_ftpfs_ops
= {
1726 NULL
, /* This is place of next pointer */
1729 "ftp:", /* prefix */
1730 &ftp_data
, /* data */
1753 ftpfs_chown
, /* not really implemented but returns success */
1768 vfs_s_nothingisopen
,
1782 void ftpfs_set_debug (const char *file
)
1784 logfile
= fopen (file
, "w+");
1786 ftp_data
.logfile
= logfile
;
1789 static char buffer
[BUF_MEDIUM
];
1790 static char *netrc
, *netrcp
;
1792 /* This should match the keywords[] array below */
1805 static keyword_t
netrc_next (void)
1809 static const char *const keywords
[] = { "default", "machine",
1810 "login", "password", "passwd", "account", "macdef", NULL
1815 netrcp
= skip_separators (netrcp
);
1816 if (*netrcp
!= '\n')
1823 if (*netrcp
== '"') {
1824 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1825 if (*netrcp
== '\\')
1830 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1831 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1832 if (*netrcp
== '\\')
1842 while (keywords
[i
- 1]) {
1843 if (!strcmp (keywords
[i
- 1], buffer
))
1849 return NETRC_UNKNOWN
;
1852 static int netrc_has_incorrect_mode (char *netrcname
, char *netrc
)
1854 static int be_angry
= 1;
1857 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1859 message_1s (1, MSG_ERROR
,
1860 _("~/.netrc file has not correct mode.\n"
1861 "Remove password or correct mode."));
1869 /* Scan .netrc until we find matching "machine" or "default"
1870 * domain is used for additional matching
1871 * No search is done after "default" in compliance with "man netrc"
1872 * Return 0 if found, -1 otherwise */
1873 static int find_machine (const char *host
, const char *domain
)
1877 while ((keyword
= netrc_next ()) != NETRC_NONE
) {
1878 if (keyword
== NETRC_DEFAULT
)
1881 if (keyword
== NETRC_MACDEF
) {
1882 /* Scan for an empty line, which concludes "macdef" */
1884 while (*netrcp
&& *netrcp
!= '\n')
1886 if (*netrcp
!= '\n')
1889 } while (*netrcp
&& *netrcp
!= '\n');
1893 if (keyword
!= NETRC_MACHINE
)
1896 /* Take machine name */
1897 if (netrc_next () == NETRC_NONE
)
1900 if (g_strcasecmp (host
, buffer
)) {
1901 /* Try adding our domain to short names in .netrc */
1902 char *host_domain
= strchr (host
, '.');
1906 /* Compare domain part */
1907 if (g_strcasecmp (host_domain
, domain
))
1910 /* Compare local part */
1911 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1922 /* Extract login and password from .netrc for the host.
1924 * Returns 0 for success, -1 for error */
1925 static int lookup_netrc (const char *host
, char **login
, char **pass
)
1928 char *tmp_pass
= NULL
;
1929 char hostname
[MAXHOSTNAMELEN
], *domain
;
1931 static struct rupcache
{
1932 struct rupcache
*next
;
1936 } *rup_cache
= NULL
, *rupp
;
1938 /* Initialize *login and *pass */
1945 /* Look up in the cache first */
1946 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
1947 if (!strcmp (host
, rupp
->host
)) {
1949 *login
= g_strdup (rupp
->login
);
1950 if (pass
&& rupp
->pass
)
1951 *pass
= g_strdup (rupp
->pass
);
1956 /* Load current .netrc */
1957 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
1958 netrcp
= netrc
= load_file (netrcname
);
1959 if (netrc
== NULL
) {
1964 /* Find our own domain name */
1965 if (gethostname (hostname
, sizeof (hostname
)) < 0)
1967 if (!(domain
= strchr (hostname
, '.')))
1970 /* Scan for "default" and matching "machine" keywords */
1971 find_machine (host
, domain
);
1973 /* Scan for keywords following "default" and "machine" */
1976 keyword
= netrc_next ();
1980 if (netrc_next () == NETRC_NONE
) {
1985 /* We have another name already - should not happen */
1991 /* We have login name now */
1992 *login
= g_strdup (buffer
);
1995 case NETRC_PASSWORD
:
1997 if (netrc_next () == NETRC_NONE
) {
2002 /* Ignore unsafe passwords */
2003 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2004 && netrc_has_incorrect_mode (netrcname
, netrc
)) {
2009 /* Remember password. pass may be NULL, so use tmp_pass */
2010 if (tmp_pass
== NULL
)
2011 tmp_pass
= g_strdup (buffer
);
2015 /* "account" is followed by a token which we ignore */
2016 if (netrc_next () == NETRC_NONE
) {
2021 /* Ignore account, but warn user anyways */
2022 netrc_has_incorrect_mode (netrcname
, netrc
);
2026 /* Unexpected keyword or end of file */
2038 rupp
= g_new (struct rupcache
, 1);
2039 rupp
->host
= g_strdup (host
);
2040 rupp
->login
= rupp
->pass
= 0;
2042 if (*login
!= NULL
) {
2043 rupp
->login
= g_strdup (*login
);
2045 if (tmp_pass
!= NULL
)
2046 rupp
->pass
= g_strdup (tmp_pass
);
2047 rupp
->next
= rup_cache
;