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
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Library General Public License
14 as published by the Free Software Foundation; either version 2 of
15 the License, or (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU Library General Public License for more details.
22 You should have received a copy of the GNU Library General Public
23 License along with this program; if not, write to the Free Software
24 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
28 - make it more robust - all the connects etc. should handle EADDRINUSE and
29 ERETRY (have I spelled these names correctly?)
30 - make the user able to flush a connection - all the caches will get empty
31 etc., (tarfs as well), we should give there a user selectable timeout
32 and assign a key sequence.
33 - use hash table instead of linklist to cache ftpfs directory.
38 * NOTE: Usage of tildes is deprecated, consider:
39 * cd /#ftp:pavel@hobit
41 * And now: what do I want to do? Do I want to go to /home/pavel or to
42 * /#ftp:hobit/home/pavel? I think first has better sense...
45 int f = !strcmp( remote_path, "/~" );
46 if (f || !strncmp( remote_path, "/~/", 3 )) {
48 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
57 /* Namespace pollution: horrible */
60 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
61 #include <netdb.h> /* struct hostent */
62 #include <sys/socket.h> /* AF_INET */
63 #include <netinet/in.h> /* struct in_addr */
64 #ifdef HAVE_SETSOCKOPT
65 # include <netinet/ip.h> /* IP options */
67 #ifdef HAVE_ARPA_INET_H
68 #include <arpa/inet.h>
71 #include <arpa/telnet.h>
72 #include <sys/param.h>
76 #include "xdirentry.h"
79 #include "../src/dialog.h"
80 #include "../src/setup.h" /* for load_anon_passwd */
81 #include "container.h"
83 #ifndef MAXHOSTNAMELEN
84 # define MAXHOSTNAMELEN 64
87 #define UPLOAD_ZERO_LENGTH_FILE
88 #define SUP super->u.ftp
89 #define FH_SOCK fh->u.ftp.sock
94 /* Delay to retry a connection */
95 int ftpfs_retry_seconds
= 30;
97 /* Method to use to connect to ftp sites */
98 int ftpfs_use_passive_connections
= 1;
100 /* Method used to get directory listings:
101 * 1: try 'LIST -la <path>', if it fails
102 * fall back to CWD <path>; LIST
103 * 0: always use CWD <path>; LIST
105 int ftpfs_use_unix_list_options
= 1;
107 /* First "CWD <path>", then "LIST -la ." */
108 int ftpfs_first_cd_then_ls
;
110 /* Use the ~/.netrc */
113 extern char *home_dir
;
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
, 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
, 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
, _(" Could not 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
);
579 ftpfs_check_proxy (const char *host
)
581 struct no_proxy_entry
*npe
;
583 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
584 return 0; /* sanity check */
589 if (!ftpfs_always_use_proxy
)
592 if (!strchr (host
, '.'))
595 load_no_proxy_list ();
596 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
597 char *domain
= npe
->domain
;
599 if (domain
[0] == '.') {
600 int ld
= strlen (domain
);
601 int lh
= strlen (host
);
603 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
611 if (!g_strcasecmp (host
, domain
))
619 ftpfs_get_proxy_host_and_port (char *proxy
, char **host
, int *port
)
624 vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
,
631 ftpfs_open_socket (vfs
*me
, vfs_s_super
*super
)
633 struct sockaddr_in server_address
;
640 /* Use a proxy host? */
643 if (!host
|| !*host
){
644 print_vfs_message (_("ftpfs: Invalid host name."));
649 /* Hosts to connect to that start with a ! should use proxy */
651 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
655 /* Get host address */
656 memset ((char *) &server_address
, 0, sizeof (server_address
));
657 server_address
.sin_family
= AF_INET
;
658 server_address
.sin_addr
.s_addr
= inet_addr (host
);
659 if (server_address
.sin_addr
.s_addr
!= -1)
660 server_address
.sin_family
= AF_INET
;
662 hp
= gethostbyname (host
);
664 print_vfs_message (_("ftpfs: Invalid host address."));
670 server_address
.sin_family
= hp
->h_addrtype
;
672 /* We copy only 4 bytes, we can not trust hp->h_length, as it comes from the DNS */
673 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
, 4);
676 server_address
.sin_port
= htons (port
);
679 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
685 setup_source_route (my_socket
, server_address
.sin_addr
.s_addr
);
687 print_vfs_message (_("ftpfs: making connection to %s"), host
);
691 enable_interrupt_key (); /* clear the interrupt flag */
693 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
694 sizeof (server_address
)) < 0){
696 if (errno
== EINTR
&& got_interrupt ())
697 print_vfs_message (_("ftpfs: connection interrupted by user"));
699 print_vfs_message (_("ftpfs: connection to server failed: %s"),
700 unix_error_string(errno
));
701 disable_interrupt_key();
705 disable_interrupt_key();
710 open_archive_int (vfs
*me
, vfs_s_super
*super
)
712 int retry_seconds
, count_down
;
714 /* We do not want to use the passive if we are using proxies */
716 SUP
.use_passive_connection
= 0;
720 SUP
.failed_on_login
= 0;
722 SUP
.sock
= ftpfs_open_socket (me
, super
);
726 if (login_server (me
, super
, NULL
)) {
727 /* Logged in, no need to retry the connection */
730 if (SUP
.failed_on_login
){
731 /* Close only the socket descriptor */
736 if (ftpfs_retry_seconds
){
737 retry_seconds
= ftpfs_retry_seconds
;
738 enable_interrupt_key ();
739 for (count_down
= retry_seconds
; count_down
; count_down
--){
740 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
742 if (got_interrupt ()){
744 disable_interrupt_key ();
748 disable_interrupt_key ();
751 } while (retry_seconds
);
753 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
755 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
760 open_archive (vfs
*me
, vfs_s_super
*super
, char *archive_name
, char *op
)
762 char *host
, *user
, *password
;
765 ftp_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
772 if (ftpfs_check_proxy (host
))
773 SUP
.proxy
= ftpfs_proxy_host
;
774 SUP
.password
= password
;
775 SUP
.use_passive_connection
= ftpfs_use_passive_connections
| source_route
;
776 SUP
.use_source_route
= source_route
;
777 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
778 SUP
.isbinary
= TYPE_UNKNOWN
;
779 SUP
.remote_is_amiga
= 0;
780 super
->name
= g_strdup("/");
781 super
->root
= vfs_s_new_inode (me
, super
, vfs_s_default_stat(me
, S_IFDIR
| 0755));
783 return open_archive_int (me
, super
);
787 archive_same(vfs
*me
, vfs_s_super
*super
, char *archive_name
, char *op
, void *cookie
)
792 ftp_split_url (strchr(op
, ':') + 1, &host
, &user
, &port
, 0);
794 port
= ((strcmp (host
, SUP
.host
) == 0) &&
795 (strcmp (user
, SUP
.user
) == 0) &&
805 ftpfs_flushdir (void)
807 force_expiration
= 1;
811 dir_uptodate(vfs
*me
, vfs_s_inode
*ino
)
815 if (force_expiration
) {
816 force_expiration
= 0;
819 gettimeofday(&tim
, NULL
);
820 if (tim
.tv_sec
< ino
->u
.ftp
.timestamp
.tv_sec
)
825 /* The returned directory should always contain a trailing slash */
827 ftpfs_get_current_directory (vfs
*me
, vfs_s_super
*super
)
829 char buf
[BUF_8K
], *bufp
, *bufq
;
831 if (command (me
, super
, NONE
, "PWD") == COMPLETE
&&
832 get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
834 for (bufq
= buf
; *bufq
; bufq
++)
841 if (*(bufq
- 1) != '/') {
846 return g_strdup (bufp
);
848 /* If the remote server is an Amiga a leading slash
849 might be missing. MC needs it because it is used
850 as seperator between hostname and path internally. */
851 return g_strconcat( "/", bufp
, 0);
865 /* Setup Passive ftp connection, we use it for source routed connections */
867 setup_passive (vfs
*me
, vfs_s_super
*super
, int my_socket
, struct sockaddr_in
*sa
)
869 int xa
, xb
, xc
, xd
, xe
, xf
;
873 if (command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
876 /* Parse remote parameters */
877 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++)
881 if (!isdigit ((unsigned char) *c
))
883 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
885 n
[0] = (unsigned char) xa
;
886 n
[1] = (unsigned char) xb
;
887 n
[2] = (unsigned char) xc
;
888 n
[3] = (unsigned char) xd
;
889 n
[4] = (unsigned char) xe
;
890 n
[5] = (unsigned char) xf
;
892 memcpy (&(sa
->sin_addr
.s_addr
), (void *)n
, 4);
893 memcpy (&(sa
->sin_port
), (void *)&n
[4], 2);
894 setup_source_route (my_socket
, sa
->sin_addr
.s_addr
);
895 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
901 initconn (vfs
*me
, vfs_s_super
*super
)
903 struct sockaddr_in data_addr
;
904 int data
, len
= sizeof(data_addr
);
907 getsockname(SUP
.sock
, (struct sockaddr
*) &data_addr
, &len
);
908 data_addr
.sin_port
= 0;
910 pe
= getprotobyname("tcp");
913 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
917 if (SUP
.use_passive_connection
){
918 if ((SUP
.use_passive_connection
= setup_passive (me
, super
, data
, &data_addr
)))
921 SUP
.use_source_route
= 0;
922 SUP
.use_passive_connection
= 0;
923 print_vfs_message (_("ftpfs: could not setup passive mode"));
926 /* If passive setup fails, fallback to active connections */
927 /* Active FTP connection */
928 if ((bind (data
, (struct sockaddr
*)&data_addr
, len
) == 0) &&
929 (getsockname (data
, (struct sockaddr
*) &data_addr
, &len
) == 0) &&
930 (listen (data
, 1) == 0))
932 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
933 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
935 if (command (me
, super
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
936 a
[2], a
[3], p
[0], p
[1]) == COMPLETE
)
945 open_data_connection (vfs
*me
, vfs_s_super
*super
, char *cmd
, char *remote
,
946 int isbinary
, int reget
)
948 struct sockaddr_in from
;
949 int s
, j
, data
, fromlen
= sizeof(from
);
951 if ((s
= initconn (me
, super
)) == -1)
953 if (changetype (me
, super
, isbinary
) == -1)
956 j
= command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
961 char * remote_path
= translate_path (me
, super
, remote
);
962 j
= command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
963 /* WarFtpD can't STORE //filename */
964 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
965 g_free (remote_path
);
967 j
= command (me
, super
, WAIT_REPLY
, "%s", cmd
);
970 enable_interrupt_key();
971 if (SUP
.use_passive_connection
)
974 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
981 disable_interrupt_key();
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"), 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 while (read(dsock
, buf
, sizeof(buf
)) > 0);
1012 if ((get_reply(me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1013 get_reply(me
, SUP
.sock
, NULL
, 0);
1018 resolve_symlink_without_ls_options(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1020 struct linklist
*flist
;
1021 struct direntry
*fe
, *fel
;
1022 char tmp
[MC_MAXPATHLEN
];
1025 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1026 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1027 /* flist->data->l_stat is alread initialized with 0 */
1029 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1030 if (fel
->linkname
[0] == '/') {
1031 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1033 strcpy (tmp
, fel
->linkname
);
1035 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1037 strcpy (tmp
, dir
->remote_path
);
1040 strcat (tmp
+ 1, fel
->linkname
);
1042 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1043 canonicalize_pathname (tmp
);
1044 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1046 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1047 /* Symlink points to link which isn't resolved, yet. */
1048 if (fe
->linkname
[0] == '/') {
1049 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1051 strcpy (tmp
, fe
->linkname
);
1053 /* at this point tmp looks always like this
1054 /directory/filename, i.e. no need to check
1055 strrchr's return value */
1056 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1057 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1059 strcat (tmp
, fe
->linkname
);
1063 fel
->l_stat
= g_new (struct stat
, 1);
1064 if ( S_ISLNK (fe
->s
.st_mode
))
1065 *fel
->l_stat
= *fe
->l_stat
;
1067 *fel
->l_stat
= fe
->s
;
1068 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1075 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1079 resolve_symlink_with_ls_options(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1081 char buffer
[2048] = "", *filename
;
1085 struct linklist
*flist
;
1086 struct direntry
*fe
;
1087 int switch_method
= 0;
1089 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1090 if (strchr (dir
->remote_path
, ' ')) {
1091 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1092 print_vfs_message(_("ftpfs: CWD failed."));
1095 sock
= open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1098 sock
= open_data_connection (bucket
, "LIST -lLa",
1099 dir
->remote_path
, TYPE_ASCII
, 0);
1102 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1106 fp
= fdopen(sock
, "r");
1109 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1112 enable_interrupt_key();
1113 flist
= dir
->file_list
->next
;
1116 if (flist
== dir
->file_list
)
1119 flist
= flist
->next
;
1120 } while (!S_ISLNK(fe
->s
.st_mode
));
1122 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1125 fputs (buffer
, logfile
);
1128 vfs_die("This code should be commented out\n");
1129 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1130 int r
= strcmp(fe
->name
, filename
);
1133 if (S_ISLNK (s
.st_mode
)) {
1134 /* This server doesn't understand LIST -lLa */
1138 fe
->l_stat
= g_new (struct stat
, 1);
1139 if (fe
->l_stat
== NULL
)
1142 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1151 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1152 disable_interrupt_key();
1154 get_reply(me
, SUP
.sock
, NULL
, 0);
1158 resolve_symlink(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1160 print_vfs_message(_("Resolving symlink..."));
1162 if (SUP
.strict_rfc959_list_cmd
)
1163 resolve_symlink_without_ls_options(me
, super
, dir
);
1165 resolve_symlink_with_ls_options(me
, super
, dir
);
1170 dir_load(vfs
*me
, vfs_s_inode
*dir
, char *remote_path
)
1173 vfs_s_super
*super
= dir
->super
;
1174 int sock
, num_entries
= 0;
1176 int has_symlinks
= 0;
1178 char buffer
[BUF_8K
];
1181 cd_first
= ftpfs_first_cd_then_ls
|| (strchr (remote_path
, ' ') != NULL
)
1182 || (SUP
.strict
== RFC_STRICT
);
1185 print_vfs_message(_("ftpfs: Reading FTP directory %s... %s%s"), remote_path
,
1186 SUP
.strict
== RFC_STRICT
? _("(strict rfc959)") : "",
1187 cd_first
? _("(chdir first)") : "");
1192 p
= translate_path (me
, super
, remote_path
);
1194 if (ftpfs_chdir_internal (me
, super
, p
) != COMPLETE
) {
1197 print_vfs_message(_("ftpfs: CWD failed."));
1203 gettimeofday(&dir
->u
.ftp
.timestamp
, NULL
);
1204 dir
->u
.ftp
.timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1206 if (SUP
.strict
== RFC_STRICT
)
1207 sock
= open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1209 /* Dirty hack to avoid autoprepending / to . */
1210 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1211 sock
= open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1213 /* Trailing "/." is necessary if remote_path is a symlink */
1214 char *path
= concat_dir_and_file (remote_path
, ".");
1215 sock
= open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1222 /* Clear the interrupt flag */
1223 enable_interrupt_key ();
1227 /* added 20001006 by gisburn
1228 * add dots '.' and '..'. This must be _executed_ before scanning the dir as the
1229 * code below may jump directly into error handling code (without executing
1230 * remaining code). And C doesn't have try {...} finally {}; :-)
1232 vfs_s_inode
*parent
= dir
->ent
->dir
;
1237 ent
= vfs_s_generate_entry(me
, ".", dir
, 0);
1238 ent
->ino
->st
=dir
->st
;
1240 vfs_s_insert_entry(me
, dir
, ent
);
1242 ent
= vfs_s_generate_entry(me
, "..", parent
, 0);
1243 ent
->ino
->st
=parent
->st
;
1245 vfs_s_insert_entry(me
, dir
, ent
);
1251 int res
= vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
), sock
);
1256 me
->verrno
= ECONNRESET
;
1258 disable_interrupt_key();
1259 get_reply(me
, SUP
.sock
, NULL
, 0);
1260 print_vfs_message (_("%s: failure"), me
->name
);
1265 fputs (buffer
, logfile
);
1266 fputs ("\n", logfile
);
1270 ent
= vfs_s_generate_entry(me
, NULL
, dir
, 0);
1271 i
= ent
->ino
->st
.st_nlink
;
1272 if (!vfs_parse_ls_lga (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1273 vfs_s_free_entry (me
, ent
);
1276 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1278 if ((!strcmp(ent
->name
, ".")) || (!strcmp (ent
->name
, ".."))) {
1280 ent
->name
= NULL
; /* Ouch, vfs_s_free_entry "knows" about . and .. being special :-( */
1281 vfs_s_free_entry (me
, ent
);
1285 vfs_s_insert_entry(me
, dir
, ent
);
1288 /* vfs_s_add_dots(me, dir, NULL);
1289 FIXME This really should be here; but we need to provide correct parent.
1290 Disabled for now, please fix it. Pavel
1293 me
->verrno
= E_REMOTE
;
1294 if ((get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
) || !num_entries
)
1297 if (SUP
.strict
== RFC_AUTODETECT
)
1298 SUP
.strict
= RFC_DARING
;
1302 if (resolve_symlinks
)
1303 resolve_symlink(me
, super
, dcache
);
1305 dcache
->symlink_status
= FTPFS_UNRESOLVED_SYMLINKS
;
1308 print_vfs_message (_("%s: done."), me
->name
);
1312 if (SUP
.strict
== RFC_AUTODETECT
) {
1313 /* It's our first attempt to get a directory listing from this
1314 server (UNIX style LIST command) */
1315 SUP
.strict
= RFC_STRICT
;
1316 /* I hate goto, but recursive call needs another 8K on stack */
1317 /* return dir_load (me, dir, remote_path); */
1321 print_vfs_message(_("ftpfs: failed; nowhere to fallback to"));
1326 file_store(vfs
*me
, vfs_s_fh
*fh
, char *name
, char *localname
)
1330 #ifdef HAVE_STRUCT_LINGER
1337 vfs_s_super
*super
= FH_SUPER
;
1339 h
= open(localname
, O_RDONLY
);
1343 sock
= open_data_connection(me
, super
, fh
->u
.ftp
.append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1348 #ifdef HAVE_STRUCT_LINGER
1351 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof(li
));
1353 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1357 enable_interrupt_key();
1359 while ((n
= read(h
, buffer
, sizeof(buffer
))) < 0) {
1360 if (errno
== EINTR
) {
1361 if (got_interrupt()) {
1373 while (write(sock
, buffer
, n
) < 0) {
1374 if (errno
== EINTR
) {
1375 if (got_interrupt()) {
1386 print_vfs_message(_("ftpfs: storing file %lu (%lu)"),
1387 (unsigned long) total
, (unsigned long) s
.st_size
);
1389 disable_interrupt_key();
1392 if (get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1396 disable_interrupt_key();
1399 get_reply(me
, SUP
.sock
, NULL
, 0);
1403 //#define FH_SOCK fh->u.ftp.sock
1406 linear_start(vfs
*me
, vfs_s_fh
*fh
, int offset
)
1408 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1412 FH_SOCK
= open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1416 fh
->linear
= LS_LINEAR_OPEN
;
1417 FH_SUPER
->u
.ftp
.control_connection_buzy
= 1;
1418 fh
->u
.ftp
.append
= 0;
1423 linear_read (vfs
*me
, vfs_s_fh
*fh
, void *buf
, int len
)
1426 vfs_s_super
*super
= FH_SUPER
;
1428 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1429 if ((errno
== EINTR
) && !got_interrupt())
1435 linear_abort(me
, fh
);
1438 SUP
.control_connection_buzy
= 0;
1441 if ((get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1442 ERRNOR (E_REMOTE
, -1);
1449 linear_close (vfs
*me
, vfs_s_fh
*fh
)
1452 linear_abort(me
, fh
);
1455 static int ftpfs_ctl (void *fh
, int ctlop
, int arg
)
1458 case MCCTL_IS_NOTREADY
:
1463 vfs_die ("You may not do this");
1464 if (FH
->linear
== LS_LINEAR_CLOSED
)
1467 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1468 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1477 /* Warning: filename passed to this command is damaged */
1479 send_ftp_command(vfs
*me
, char *filename
, char *cmd
, int flags
)
1484 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1486 if (!(rpath
= vfs_s_get_path_mangle(me
, filename
, &super
, 0)))
1488 p
= translate_path (me
, super
, rpath
);
1489 r
= command (me
, super
, WAIT_REPLY
, cmd
, p
);
1491 vfs_add_noncurrent_stamps (&vfs_ftpfs_ops
, (vfsid
) super
, NULL
);
1492 if (flags
& OPT_IGNORE_ERROR
)
1496 if (flush_directory_cache
)
1497 vfs_s_invalidate(me
, super
);
1501 /* This routine is called as the last step in load_setup */
1503 ftpfs_init_passwd(void)
1505 ftpfs_anonymous_passwd
= load_anon_passwd ();
1506 if (ftpfs_anonymous_passwd
)
1509 /* If there is no anonymous ftp password specified
1510 * then we'll just use anonymous@
1511 * We don't send any other thing because:
1512 * - We want to remain anonymous
1513 * - We want to stop SPAM
1514 * - We don't want to let ftp sites to discriminate by the user,
1517 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1520 static int ftpfs_chmod (vfs
*me
, char *path
, int mode
)
1522 char buf
[BUF_SMALL
];
1524 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1525 return send_ftp_command(me
, path
, buf
, OPT_FLUSH
);
1528 static int ftpfs_chown (vfs
*me
, char *path
, int owner
, int group
)
1534 /* Everyone knows it is not possible to chown remotely, so why bother them.
1535 If someone's root, then copy/move will always try to chown it... */
1540 static int ftpfs_unlink (vfs
*me
, char *path
)
1542 return send_ftp_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1545 /* Return 1 if path is the same directory as the one we are in now */
1547 is_same_dir (vfs
*me
, vfs_s_super
*super
, const char *path
)
1551 if (strcmp (path
, SUP
.cwdir
) == 0)
1557 ftpfs_chdir_internal (vfs
*me
, vfs_s_super
*super
, char *remote_path
)
1562 if (!SUP
.cwd_defered
&& is_same_dir (me
, super
, remote_path
))
1565 p
= translate_path (me
, super
, remote_path
);
1566 r
= command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1569 if (r
!= COMPLETE
) {
1573 SUP
.cwdir
= g_strdup (remote_path
);
1574 SUP
.cwd_defered
= 0;
1579 static int ftpfs_rename (vfs
*me
, char *path1
, char *path2
)
1581 send_ftp_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1582 return send_ftp_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1585 static int ftpfs_mkdir (vfs
*me
, char *path
, mode_t mode
)
1587 return send_ftp_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1590 static int ftpfs_rmdir (vfs
*me
, char *path
)
1592 return send_ftp_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1595 static int ftpfs_fh_open (vfs
*me
, vfs_s_fh
*fh
, int flags
, int mode
)
1597 fh
->u
.ftp
.append
= 0;
1598 /* File will be written only, so no need to retrieve it from ftp server */
1599 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
|O_RDWR
))){
1600 #ifdef HAVE_STRUCT_LINGER
1607 /* linear_start() called, so data will be written
1608 * to local temporary file and stored to ftp server
1609 * by vfs_s_close later
1611 if (FH_SUPER
->u
.ftp
.control_connection_buzy
){
1612 if (!fh
->ino
->localname
){
1613 int handle
= mc_mkstemps (&fh
->ino
->localname
, me
->name
, NULL
);
1617 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1621 name
= vfs_s_fullpath (me
, fh
->ino
);
1624 fh
->handle
= open_data_connection(me
, fh
->ino
->super
,
1625 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1630 #ifdef HAVE_STRUCT_LINGER
1634 setsockopt(fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof(li
));
1636 if (fh
->ino
->localname
){
1637 unlink (fh
->ino
->localname
);
1638 g_free (fh
->ino
->localname
);
1639 fh
->ino
->localname
= NULL
;
1644 if (!fh
->ino
->localname
)
1645 if (vfs_s_retrieve_file (me
, fh
->ino
)==-1)
1647 if (!fh
->ino
->localname
)
1648 vfs_die( "retrieve_file failed to fill in localname" );
1652 static int ftpfs_fh_close (vfs
*me
, vfs_s_fh
*fh
)
1654 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1657 /* File is stored to destination already, so
1658 * we prevent MEDATA->file_store() call from vfs_s_close ()
1661 if (get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1663 vfs_s_invalidate (me
, FH_SUPER
);
1668 static struct vfs_s_data ftp_data
= {
1674 NULL
, /* init_inode */
1675 NULL
, /* free_inode */
1676 NULL
, /* init_entry */
1678 NULL
, /* archive_check */
1683 ftpfs_fh_open
, /* fh_open */
1684 ftpfs_fh_close
, /* fh_close */
1686 vfs_s_find_entry_linear
,
1697 ftpfs_fill_names (vfs
*me
, void (*func
)(char *))
1699 struct vfs_s_super
* super
= ftp_data
.supers
;
1703 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, NULL
);
1706 super
= super
->next
;
1710 vfs vfs_ftpfs_ops
= {
1711 NULL
, /* This is place of next pointer */
1714 "ftp:", /* prefix */
1715 &ftp_data
, /* data */
1738 ftpfs_chown
, /* not really implemented but returns success */
1753 vfs_s_nothingisopen
,
1767 void ftpfs_set_debug (const char *file
)
1769 logfile
= fopen (file
, "w+");
1771 ftp_data
.logfile
= logfile
;
1774 static char buffer
[BUF_MEDIUM
];
1775 static char *netrc
, *netrcp
;
1777 /* This should match the keywords[] array below */
1790 static keyword_t
netrc_next (void)
1794 static const char *const keywords
[] = { "default", "machine",
1795 "login", "password", "passwd", "account", "macdef", NULL
1800 netrcp
= skip_separators (netrcp
);
1801 if (*netrcp
!= '\n')
1808 if (*netrcp
== '"') {
1809 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1810 if (*netrcp
== '\\')
1815 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1816 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1817 if (*netrcp
== '\\')
1827 while (keywords
[i
- 1]) {
1828 if (!strcmp (keywords
[i
- 1], buffer
))
1834 return NETRC_UNKNOWN
;
1837 static int netrc_has_incorrect_mode (char *netrcname
, char *netrc
)
1839 static int be_angry
= 1;
1842 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1844 message_1s (1, MSG_ERROR
,
1845 _("~/.netrc file has not correct mode.\n"
1846 "Remove password or correct mode."));
1854 /* Scan .netrc until we find matching "machine" or "default"
1855 * domain is used for additional matching
1856 * No search is done after "default" in compliance with "man netrc"
1857 * Return 0 if found, -1 otherwise */
1858 static int find_machine (const char *host
, const char *domain
)
1862 while ((keyword
= netrc_next ()) != NETRC_NONE
) {
1863 if (keyword
== NETRC_DEFAULT
)
1866 if (keyword
== NETRC_MACDEF
) {
1867 /* Scan for an empty line, which concludes "macdef" */
1869 while (*netrcp
&& *netrcp
!= '\n')
1871 if (*netrcp
!= '\n')
1874 } while (*netrcp
&& *netrcp
!= '\n');
1878 if (keyword
!= NETRC_MACHINE
)
1881 /* Take machine name */
1882 if (netrc_next () == NETRC_NONE
)
1885 if (g_strcasecmp (host
, buffer
)) {
1886 /* Try adding our domain to short names in .netrc */
1887 char *host_domain
= strchr (host
, '.');
1891 /* Compare domain part */
1892 if (g_strcasecmp (host_domain
, domain
))
1895 /* Compare local part */
1896 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1907 /* Extract login and password from .netrc for the host.
1909 * Returns 0 for success, -1 for error */
1910 static int lookup_netrc (const char *host
, char **login
, char **pass
)
1913 char *tmp_pass
= NULL
;
1914 char hostname
[MAXHOSTNAMELEN
], *domain
;
1916 static struct rupcache
{
1917 struct rupcache
*next
;
1921 } *rup_cache
= NULL
, *rupp
;
1923 /* Initialize *login and *pass */
1930 /* Look up in the cache first */
1931 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
1932 if (!strcmp (host
, rupp
->host
)) {
1934 *login
= g_strdup (rupp
->login
);
1935 if (pass
&& rupp
->pass
)
1936 *pass
= g_strdup (rupp
->pass
);
1941 /* Load current .netrc */
1942 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
1943 netrcp
= netrc
= load_file (netrcname
);
1944 if (netrc
== NULL
) {
1949 /* Find our own domain name */
1950 if (gethostname (hostname
, sizeof (hostname
)) < 0)
1952 if (!(domain
= strchr (hostname
, '.')))
1955 /* Scan for "default" and matching "machine" keywords */
1956 find_machine (host
, domain
);
1958 /* Scan for keywords following "default" and "machine" */
1961 keyword
= netrc_next ();
1965 if (netrc_next () == NETRC_NONE
) {
1970 /* We have another name already - should not happen */
1976 /* We have login name now */
1977 *login
= g_strdup (buffer
);
1980 case NETRC_PASSWORD
:
1982 if (netrc_next () == NETRC_NONE
) {
1987 /* Ignore unsafe passwords */
1988 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
1989 && netrc_has_incorrect_mode (netrcname
, netrc
)) {
1994 /* Remember password. pass may be NULL, so use tmp_pass */
1995 if (tmp_pass
== NULL
)
1996 tmp_pass
= g_strdup (buffer
);
2000 /* "account" is followed by a token which we ignore */
2001 if (netrc_next () == NETRC_NONE
) {
2006 /* Ignore account, but warn user anyways */
2007 netrc_has_incorrect_mode (netrcname
, netrc
);
2011 /* Unexpected keyword or end of file */
2023 rupp
= g_new (struct rupcache
, 1);
2024 rupp
->host
= g_strdup (host
);
2025 rupp
->login
= rupp
->pass
= 0;
2027 if (*login
!= NULL
) {
2028 rupp
->login
= g_strdup (*login
);
2030 if (tmp_pass
!= NULL
)
2031 rupp
->pass
= g_strdup (tmp_pass
);
2032 rupp
->next
= rup_cache
;