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 #include <arpa/inet.h>
69 #include <arpa/telnet.h>
70 #include <sys/param.h>
78 #include "xdirentry.h"
81 #include "../src/dialog.h"
82 #include "../src/setup.h" /* for load_anon_passwd */
83 #include "container.h"
85 #ifndef MAXHOSTNAMELEN
86 # define MAXHOSTNAMELEN 64
89 #define UPLOAD_ZERO_LENGTH_FILE
90 #define SUP super->u.ftp
91 #define FH_SOCK fh->u.ftp.sock
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 extern char *home_dir
;
117 /* Anonymous setup */
118 char *ftpfs_anonymous_passwd
= NULL
;
119 int ftpfs_directory_timeout
= 900;
122 char *ftpfs_proxy_host
= NULL
;
124 /* wether we have to use proxy by default? */
125 int ftpfs_always_use_proxy
;
127 /* source routing host */
128 extern int source_route
;
130 /* Where we store the transactions */
131 static FILE *logfile
= NULL
;
133 /* If true, the directory cache is forced to reload */
134 static int force_expiration
= 0;
136 #ifdef FIXME_LATER_ALIGATOR
137 static struct linklist
*connections_list
;
140 /* command wait_flag: */
142 #define WAIT_REPLY 0x01
143 #define WANT_STRING 0x02
144 static char reply_str
[80];
146 /* char *translate_path (struct ftpfs_connection *bucket, char *remote_path)
147 Translate a Unix path, i.e. MC's internal path representation (e.g.
148 /somedir/somefile) to a path valid for the remote server. Every path
149 transfered to the remote server has to be mangled by this function
150 right prior to sending it.
151 Currently only Amiga ftp servers are handled in a special manner.
153 When the remote server is an amiga:
154 a) strip leading slash if necesarry
155 b) replace first occurance of ":/" with ":"
156 c) strip trailing "/."
159 static char *ftpfs_get_current_directory (vfs
*me
, vfs_s_super
*super
);
160 static int ftpfs_chdir_internal (vfs
*me
, vfs_s_super
*super
, char *remote_path
);
161 static int command (vfs
*me
, vfs_s_super
*super
, int wait_reply
, char *fmt
, ...)
162 __attribute__ ((format (printf
, 4, 5)));
163 static int ftpfs_open_socket (vfs
*me
, vfs_s_super
*super
);
164 static int login_server (vfs
*me
, vfs_s_super
*super
, const char *netrcpass
);
165 static int lookup_netrc (const char *host
, char **login
, char **pass
);
168 translate_path (vfs
*me
, vfs_s_super
*super
, const char *remote_path
)
170 if (!SUP
.remote_is_amiga
)
171 return g_strdup (remote_path
);
176 fprintf (logfile
, "MC -- translate_path: %s\n", remote_path
);
180 /* strip leading slash(es) */
181 while (*remote_path
== '/')
185 * Don't change "/" into "", e.g. "CWD " would be
188 if (*remote_path
== '\0')
189 return g_strdup (".");
191 ret
= g_strdup (remote_path
);
193 /* replace first occurance of ":/" with ":" */
194 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
195 strcpy (p
+ 1, p
+ 2);
197 /* strip trailing "/." */
198 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
204 /* Extract the hostname and username from the path */
207 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
208 * ftp://sunsite.unc.edu/pub/linux
209 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
210 * ftp://tsx-11.mit.edu:8192/
211 * ftp://joe@foo.edu:11321/private
212 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
217 #define FTP_COMMAND_PORT 21
218 #define HSC_PROXY_PORT 9875
221 ftp_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
225 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
229 /* Look up user and password in netrc */
231 lookup_netrc (*host
, user
, pass
);
233 *user
= g_strdup ("anonymous");
236 /* Look up password in netrc for known user */
237 if (use_netrc
&& *user
&& pass
&& !*pass
) {
240 lookup_netrc (*host
, &new_user
, pass
);
242 /* If user is different, remove password */
243 if (new_user
&& strcmp (*user
, new_user
)) {
255 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
257 get_reply (vfs
*me
, int sock
, char *string_buf
, int string_len
)
263 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
269 switch (sscanf(answer
, "%d", &code
)){
272 strncpy (string_buf
, answer
, string_len
- 1);
273 *(string_buf
+ string_len
- 1) = 0;
278 if (answer
[3] == '-') {
280 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
286 if ((sscanf (answer
, "%d", &i
) > 0) &&
287 (code
== i
) && (answer
[3] == ' '))
292 strncpy (string_buf
, answer
, string_len
- 1);
293 *(string_buf
+ string_len
- 1) = 0;
301 reconnect (vfs
*me
, vfs_s_super
*super
)
303 int sock
= ftpfs_open_socket (me
, super
);
305 char *cwdir
= SUP
.cwdir
;
309 if (login_server (me
, super
, SUP
.password
)){
312 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
314 return sock
== COMPLETE
;
322 command (vfs
*me
, vfs_s_super
*super
, int wait_reply
, char *fmt
, ...)
330 fmt_str
= g_strdup_vprintf (fmt
, ap
);
333 status
= strlen (fmt_str
);
334 str
= g_realloc (fmt_str
, status
+ 3);
335 strcpy (str
+ status
, "\r\n");
338 if (strncmp (str
, "PASS ", 5) == 0){
339 fputs ("PASS <Password not logged>\r\n", logfile
);
341 fwrite (str
, status
+ 2, 1, logfile
);
347 enable_interrupt_key ();
348 status
= write (SUP
.sock
, str
, status
+ 2);
353 if (errno
== EPIPE
){ /* Remote server has closed connection */
354 static int level
= 0; /* login_server() use command() */
357 status
= reconnect (me
, super
);
359 if (status
&& write (SUP
.sock
, str
, status
+ 2) > 0)
365 disable_interrupt_key ();
370 disable_interrupt_key ();
373 return get_reply (me
, sock
, (wait_reply
& WANT_STRING
) ? reply_str
: NULL
, sizeof (reply_str
)-1);
378 free_archive (vfs
*me
, vfs_s_super
*super
)
381 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
382 command(me
, super
, NONE
, "QUIT");
388 g_free (SUP
.password
);
391 /* some defines only used by changetype */
392 /* These two are valid values for the second parameter */
394 #define TYPE_BINARY 1
396 /* This one is only used to initialize bucket->isbinary, don't use it as
397 second parameter to changetype. */
398 #define TYPE_UNKNOWN -1
401 changetype (vfs
*me
, vfs_s_super
*super
, int binary
)
403 if (binary
!= SUP
.isbinary
) {
404 if (command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
406 SUP
.isbinary
= binary
;
411 /* This routine logs the user in */
413 login_server (vfs
*me
, vfs_s_super
*super
, const char *netrcpass
)
415 #if defined(HSC_PROXY)
416 char *proxypass
, *proxyname
;
420 char *name
; /* login user name */
422 char reply_string
[BUF_MEDIUM
];
424 SUP
.isbinary
= TYPE_UNKNOWN
;
426 op
= g_strdup (netrcpass
);
428 if (!strcmp (SUP
.user
, "anonymous") ||
429 !strcmp (SUP
.user
, "ftp")) {
430 if (!ftpfs_anonymous_passwd
)
432 op
= g_strdup (ftpfs_anonymous_passwd
);
438 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
,
440 op
= vfs_get_password (p
);
444 SUP
.password
= g_strdup (op
);
446 op
= g_strdup (SUP
.password
);
450 if (!anon
|| logfile
)
453 pass
= g_strconcat ("-", op
, NULL
);
457 /* Proxy server accepts: username@host-we-want-to-connect*/
459 #if defined(HSC_PROXY)
462 char *proxyhost
= NULL
;
464 ftp_split_url (ftpfs_proxy_host
, &proxyhost
, &proxyname
, &port
, 0);
466 p
= g_strconcat (_(" Proxy: Password required for "), proxyname
, " ",
468 proxypass
= vfs_get_password (p
);
470 if (proxypass
== NULL
) {
471 wipe_password (pass
);
475 name
= g_strdup (SUP
.user
);
477 name
= g_strconcat (SUP
.user
, "@",
478 SUP
.host
[0] == '!' ? SUP
.host
+1 : SUP
.host
, NULL
);
481 name
= g_strdup (SUP
.user
);
483 if (get_reply (me
, SUP
.sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
) {
484 g_strup (reply_string
);
485 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
487 fprintf (logfile
, "MC -- remote_is_amiga = %d\n", SUP
.remote_is_amiga
);
490 #if defined(HSC_PROXY)
492 print_vfs_message (_("ftpfs: sending proxy login name"));
493 if (command (me
, super
, 1, "USER %s", proxyname
) != CONTINUE
)
496 print_vfs_message (_("ftpfs: sending proxy user password"));
497 if (command (me
, super
, 1, "PASS %s", proxypass
) != COMPLETE
)
500 print_vfs_message (_("ftpfs: proxy authentication succeeded"));
501 if (command (me
, super
, 1, "SITE %s", SUP
.host
+1) != COMPLETE
)
504 print_vfs_message (_("ftpfs: connected to %s"), SUP
.host
+1);
507 SUP
.failed_on_login
= 1;
510 wipe_password (proxypass
);
511 wipe_password (pass
);
517 wipe_password (proxypass
);
521 print_vfs_message (_("ftpfs: sending login name"));
522 code
= command (me
, super
, WAIT_REPLY
, "USER %s", name
);
526 print_vfs_message (_("ftpfs: sending user password"));
527 if (command (me
, super
, WAIT_REPLY
, "PASS %s", pass
) != COMPLETE
)
531 print_vfs_message (_("ftpfs: logged in"));
532 wipe_password (pass
);
537 SUP
.failed_on_login
= 1;
540 wipe_password (SUP
.password
);
546 message_2s (1, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "), SUP
.user
);
548 wipe_password (pass
);
553 #ifdef HAVE_SETSOCKOPT
555 setup_source_route (int socket
, int dest
)
562 memset (buffer
, 0, sizeof (buffer
));
565 *ptr
++ = 4; /* pointer */
568 memcpy (ptr
, (char *) &source_route
, sizeof (int));
571 /* Second hop (ie, final destination) */
572 memcpy (ptr
, (char *) &dest
, sizeof (int));
574 while ((ptr
- buffer
) & 3)
576 if (setsockopt (socket
, IPPROTO_IP
, IP_OPTIONS
,
577 buffer
, ptr
- buffer
) < 0)
578 message_2s (1, MSG_ERROR
, _(" Could not set source routing (%s)"), unix_error_string (errno
));
581 #define setup_source_route(x,y)
584 static struct no_proxy_entry
{
590 load_no_proxy_list (void)
592 /* FixMe: shouldn't be hardcoded!!! */
593 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
594 struct no_proxy_entry
*np
, *current
= 0;
598 static char *mc_file
;
603 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
604 if (exist_file (mc_file
) &&
605 (npf
= fopen (mc_file
, "r"))) {
606 while (fgets (s
, sizeof(s
), npf
) || !(feof (npf
) || ferror (npf
))) {
607 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
608 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
618 np
= g_new (struct no_proxy_entry
, 1);
619 np
->domain
= g_strdup (s
);
634 ftpfs_check_proxy (const char *host
)
636 struct no_proxy_entry
*npe
;
638 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
639 return 0; /* sanity check */
644 if (!ftpfs_always_use_proxy
)
647 if (!strchr (host
, '.'))
650 load_no_proxy_list ();
651 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
652 char *domain
= npe
->domain
;
654 if (domain
[0] == '.') {
655 int ld
= strlen (domain
);
656 int lh
= strlen (host
);
658 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
666 if (!g_strcasecmp (host
, domain
))
674 ftpfs_get_proxy_host_and_port (char *proxy
, char **host
, int *port
)
678 #if defined(HSC_PROXY)
679 dir
= vfs_split_url (proxy
, host
, &user
, port
, 0, HSC_PROXY_PORT
, URL_ALLOW_ANON
);
681 dir
= vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
, URL_ALLOW_ANON
);
692 ftpfs_open_socket (vfs
*me
, vfs_s_super
*super
)
694 struct sockaddr_in server_address
;
701 /* Use a proxy host? */
704 if (!host
|| !*host
){
705 print_vfs_message (_("ftpfs: Invalid host name."));
710 /* Hosts to connect to that start with a ! should use proxy */
712 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
716 /* Get host address */
717 memset ((char *) &server_address
, 0, sizeof (server_address
));
718 server_address
.sin_family
= AF_INET
;
719 server_address
.sin_addr
.s_addr
= inet_addr (host
);
720 if (server_address
.sin_addr
.s_addr
!= -1)
721 server_address
.sin_family
= AF_INET
;
723 hp
= gethostbyname (host
);
725 print_vfs_message (_("ftpfs: Invalid host address."));
731 server_address
.sin_family
= hp
->h_addrtype
;
733 /* We copy only 4 bytes, we can not trust hp->h_length, as it comes from the DNS */
734 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
, 4);
737 server_address
.sin_port
= htons (port
);
740 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
746 setup_source_route (my_socket
, server_address
.sin_addr
.s_addr
);
748 print_vfs_message (_("ftpfs: making connection to %s"), host
);
752 enable_interrupt_key (); /* clear the interrupt flag */
754 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
755 sizeof (server_address
)) < 0){
757 if (errno
== EINTR
&& got_interrupt ())
758 print_vfs_message (_("ftpfs: connection interrupted by user"));
760 print_vfs_message (_("ftpfs: connection to server failed: %s"),
761 unix_error_string(errno
));
762 disable_interrupt_key();
766 disable_interrupt_key();
771 open_archive_int (vfs
*me
, vfs_s_super
*super
)
773 int retry_seconds
, count_down
;
775 /* We do not want to use the passive if we are using proxies */
777 SUP
.use_passive_connection
= 0;
781 SUP
.failed_on_login
= 0;
783 SUP
.sock
= ftpfs_open_socket (me
, super
);
787 if (login_server (me
, super
, NULL
)) {
788 /* Logged in, no need to retry the connection */
791 if (SUP
.failed_on_login
){
792 /* Close only the socket descriptor */
797 if (ftpfs_retry_seconds
){
798 retry_seconds
= ftpfs_retry_seconds
;
799 enable_interrupt_key ();
800 for (count_down
= retry_seconds
; count_down
; count_down
--){
801 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
803 if (got_interrupt ()){
805 disable_interrupt_key ();
809 disable_interrupt_key ();
812 } while (retry_seconds
);
814 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
816 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
821 open_archive (vfs
*me
, vfs_s_super
*super
, char *archive_name
, char *op
)
823 char *host
, *user
, *password
;
826 ftp_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
833 if (ftpfs_check_proxy (host
))
834 SUP
.proxy
= ftpfs_proxy_host
;
835 SUP
.password
= password
;
836 SUP
.use_passive_connection
= ftpfs_use_passive_connections
| source_route
;
837 SUP
.use_source_route
= source_route
;
838 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
839 SUP
.isbinary
= TYPE_UNKNOWN
;
840 SUP
.remote_is_amiga
= 0;
841 super
->name
= g_strdup("/");
842 super
->root
= vfs_s_new_inode (me
, super
, vfs_s_default_stat(me
, S_IFDIR
| 0755));
844 return open_archive_int (me
, super
);
848 archive_same(vfs
*me
, vfs_s_super
*super
, char *archive_name
, char *op
, void *cookie
)
853 ftp_split_url (strchr(op
, ':') + 1, &host
, &user
, &port
, 0);
855 port
= ((strcmp (host
, SUP
.host
) == 0) &&
856 (strcmp (user
, SUP
.user
) == 0) &&
866 ftpfs_flushdir (void)
868 force_expiration
= 1;
872 dir_uptodate(vfs
*me
, vfs_s_inode
*ino
)
876 if (force_expiration
) {
877 force_expiration
= 0;
880 gettimeofday(&tim
, NULL
);
881 if (tim
.tv_sec
< ino
->u
.ftp
.timestamp
.tv_sec
)
886 /* The returned directory should always contain a trailing slash */
888 ftpfs_get_current_directory (vfs
*me
, vfs_s_super
*super
)
890 char buf
[BUF_8K
], *bufp
, *bufq
;
892 if (command (me
, super
, NONE
, "PWD") == COMPLETE
&&
893 get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
895 for (bufq
= buf
; *bufq
; bufq
++)
902 if (*(bufq
- 1) != '/') {
907 return g_strdup (bufp
);
909 /* If the remote server is an Amiga a leading slash
910 might be missing. MC needs it because it is used
911 as seperator between hostname and path internally. */
912 return g_strconcat( "/", bufp
, 0);
926 /* Setup Passive ftp connection, we use it for source routed connections */
928 setup_passive (vfs
*me
, vfs_s_super
*super
, int my_socket
, struct sockaddr_in
*sa
)
930 int xa
, xb
, xc
, xd
, xe
, xf
;
934 if (command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
937 /* Parse remote parameters */
938 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++)
942 if (!isdigit ((unsigned char) *c
))
944 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
946 n
[0] = (unsigned char) xa
;
947 n
[1] = (unsigned char) xb
;
948 n
[2] = (unsigned char) xc
;
949 n
[3] = (unsigned char) xd
;
950 n
[4] = (unsigned char) xe
;
951 n
[5] = (unsigned char) xf
;
953 memcpy (&(sa
->sin_addr
.s_addr
), (void *)n
, 4);
954 memcpy (&(sa
->sin_port
), (void *)&n
[4], 2);
955 setup_source_route (my_socket
, sa
->sin_addr
.s_addr
);
956 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
962 initconn (vfs
*me
, vfs_s_super
*super
)
964 struct sockaddr_in data_addr
;
965 int data
, len
= sizeof(data_addr
);
968 getsockname(SUP
.sock
, (struct sockaddr
*) &data_addr
, &len
);
969 data_addr
.sin_port
= 0;
971 pe
= getprotobyname("tcp");
974 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
978 if (SUP
.use_passive_connection
){
979 if ((SUP
.use_passive_connection
= setup_passive (me
, super
, data
, &data_addr
)))
982 SUP
.use_source_route
= 0;
983 SUP
.use_passive_connection
= 0;
984 print_vfs_message (_("ftpfs: could not setup passive mode"));
987 /* If passive setup fails, fallback to active connections */
988 /* Active FTP connection */
989 if ((bind (data
, (struct sockaddr
*)&data_addr
, len
) == 0) &&
990 (getsockname (data
, (struct sockaddr
*) &data_addr
, &len
) == 0) &&
991 (listen (data
, 1) == 0))
993 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
994 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
996 if (command (me
, super
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
997 a
[2], a
[3], p
[0], p
[1]) == COMPLETE
)
1006 open_data_connection (vfs
*me
, vfs_s_super
*super
, char *cmd
, char *remote
,
1007 int isbinary
, int reget
)
1009 struct sockaddr_in from
;
1010 int s
, j
, data
, fromlen
= sizeof(from
);
1012 if ((s
= initconn (me
, super
)) == -1)
1014 if (changetype (me
, super
, isbinary
) == -1)
1017 j
= command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1022 char * remote_path
= translate_path (me
, super
, remote
);
1023 j
= command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1024 /* WarFtpD can't STORE //filename */
1025 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1026 g_free (remote_path
);
1028 j
= command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1031 enable_interrupt_key();
1032 if (SUP
.use_passive_connection
)
1035 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
1042 disable_interrupt_key();
1047 linear_abort (vfs
*me
, vfs_s_fh
*fh
)
1049 vfs_s_super
*super
= FH_SUPER
;
1050 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1053 int dsock
= FH_SOCK
;
1055 SUP
.control_connection_buzy
= 0;
1057 print_vfs_message(_("ftpfs: aborting transfer."));
1058 if (send(SUP
.sock
, ipbuf
, sizeof(ipbuf
), MSG_OOB
) != sizeof(ipbuf
)) {
1059 print_vfs_message(_("ftpfs: abort error: %s"), unix_error_string(errno
));
1063 if (command(me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
){
1064 print_vfs_message (_("ftpfs: abort failed"));
1069 FD_SET(dsock
, &mask
);
1070 if (select(dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0)
1071 while (read(dsock
, buf
, sizeof(buf
)) > 0);
1073 if ((get_reply(me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1074 get_reply(me
, SUP
.sock
, NULL
, 0);
1079 resolve_symlink_without_ls_options(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1081 struct linklist
*flist
;
1082 struct direntry
*fe
, *fel
;
1083 char tmp
[MC_MAXPATHLEN
];
1086 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1087 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1088 /* flist->data->l_stat is alread initialized with 0 */
1090 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1091 if (fel
->linkname
[0] == '/') {
1092 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1094 strcpy (tmp
, fel
->linkname
);
1096 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1098 strcpy (tmp
, dir
->remote_path
);
1101 strcat (tmp
+ 1, fel
->linkname
);
1103 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1104 canonicalize_pathname (tmp
);
1105 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1107 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1108 /* Symlink points to link which isn't resolved, yet. */
1109 if (fe
->linkname
[0] == '/') {
1110 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1112 strcpy (tmp
, fe
->linkname
);
1114 /* at this point tmp looks always like this
1115 /directory/filename, i.e. no need to check
1116 strrchr's return value */
1117 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1118 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1120 strcat (tmp
, fe
->linkname
);
1124 fel
->l_stat
= g_new (struct stat
, 1);
1125 if ( S_ISLNK (fe
->s
.st_mode
))
1126 *fel
->l_stat
= *fe
->l_stat
;
1128 *fel
->l_stat
= fe
->s
;
1129 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1136 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1140 resolve_symlink_with_ls_options(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1142 char buffer
[2048] = "", *filename
;
1146 struct linklist
*flist
;
1147 struct direntry
*fe
;
1148 int switch_method
= 0;
1150 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1151 if (strchr (dir
->remote_path
, ' ')) {
1152 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1153 print_vfs_message(_("ftpfs: CWD failed."));
1156 sock
= open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1159 sock
= open_data_connection (bucket
, "LIST -lLa",
1160 dir
->remote_path
, TYPE_ASCII
, 0);
1163 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1167 fp
= fdopen(sock
, "r");
1170 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1173 enable_interrupt_key();
1174 flist
= dir
->file_list
->next
;
1177 if (flist
== dir
->file_list
)
1180 flist
= flist
->next
;
1181 } while (!S_ISLNK(fe
->s
.st_mode
));
1183 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1186 fputs (buffer
, logfile
);
1189 vfs_die("This code should be commented out\n");
1190 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1191 int r
= strcmp(fe
->name
, filename
);
1194 if (S_ISLNK (s
.st_mode
)) {
1195 /* This server doesn't understand LIST -lLa */
1199 fe
->l_stat
= g_new (struct stat
, 1);
1200 if (fe
->l_stat
== NULL
)
1203 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1212 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1213 disable_interrupt_key();
1215 get_reply(me
, SUP
.sock
, NULL
, 0);
1219 resolve_symlink(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1221 print_vfs_message(_("Resolving symlink..."));
1223 if (SUP
.strict_rfc959_list_cmd
)
1224 resolve_symlink_without_ls_options(me
, super
, dir
);
1226 resolve_symlink_with_ls_options(me
, super
, dir
);
1231 dir_load(vfs
*me
, vfs_s_inode
*dir
, char *remote_path
)
1234 vfs_s_super
*super
= dir
->super
;
1235 int sock
, num_entries
= 0;
1237 int has_symlinks
= 0;
1239 char buffer
[BUF_8K
];
1242 cd_first
= ftpfs_first_cd_then_ls
|| (strchr (remote_path
, ' ') != NULL
)
1243 || (SUP
.strict
== RFC_STRICT
);
1246 print_vfs_message(_("ftpfs: Reading FTP directory %s... %s%s"), remote_path
,
1247 SUP
.strict
== RFC_STRICT
? _("(strict rfc959)") : "",
1248 cd_first
? _("(chdir first)") : "");
1253 p
= translate_path (me
, super
, remote_path
);
1255 if (ftpfs_chdir_internal (me
, super
, p
) != COMPLETE
) {
1258 print_vfs_message(_("ftpfs: CWD failed."));
1264 gettimeofday(&dir
->u
.ftp
.timestamp
, NULL
);
1265 dir
->u
.ftp
.timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1267 if (SUP
.strict
== RFC_STRICT
)
1268 sock
= open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1270 /* Dirty hack to avoid autoprepending / to . */
1271 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1272 sock
= open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1274 /* Trailing "/." is necessary if remote_path is a symlink */
1275 char *path
= concat_dir_and_file (remote_path
, ".");
1276 sock
= open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1283 /* Clear the interrupt flag */
1284 enable_interrupt_key ();
1288 /* added 20001006 by gisburn
1289 * add dots '.' and '..'. This must be _executed_ before scanning the dir as the
1290 * code below may jump directly into error handling code (without executing
1291 * remaining code). And C doesn't have try {...} finally {}; :-)
1293 vfs_s_inode
*parent
= dir
->ent
->dir
;
1298 ent
= vfs_s_generate_entry(me
, ".", dir
, 0);
1299 ent
->ino
->st
=dir
->st
;
1301 vfs_s_insert_entry(me
, dir
, ent
);
1303 ent
= vfs_s_generate_entry(me
, "..", parent
, 0);
1304 ent
->ino
->st
=parent
->st
;
1306 vfs_s_insert_entry(me
, dir
, ent
);
1312 int res
= vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
), sock
);
1317 me
->verrno
= ECONNRESET
;
1319 disable_interrupt_key();
1320 get_reply(me
, SUP
.sock
, NULL
, 0);
1321 print_vfs_message (_("%s: failure"), me
->name
);
1326 fputs (buffer
, logfile
);
1327 fputs ("\n", logfile
);
1331 ent
= vfs_s_generate_entry(me
, NULL
, dir
, 0);
1332 i
= ent
->ino
->st
.st_nlink
;
1333 if (!vfs_parse_ls_lga (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1334 vfs_s_free_entry (me
, ent
);
1337 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1339 if ((!strcmp(ent
->name
, ".")) || (!strcmp (ent
->name
, ".."))) {
1341 ent
->name
= NULL
; /* Ouch, vfs_s_free_entry "knows" about . and .. being special :-( */
1342 vfs_s_free_entry (me
, ent
);
1346 vfs_s_insert_entry(me
, dir
, ent
);
1349 /* vfs_s_add_dots(me, dir, NULL);
1350 FIXME This really should be here; but we need to provide correct parent.
1351 Disabled for now, please fix it. Pavel
1354 me
->verrno
= E_REMOTE
;
1355 if ((get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
) || !num_entries
)
1358 if (SUP
.strict
== RFC_AUTODETECT
)
1359 SUP
.strict
= RFC_DARING
;
1363 if (resolve_symlinks
)
1364 resolve_symlink(me
, super
, dcache
);
1366 dcache
->symlink_status
= FTPFS_UNRESOLVED_SYMLINKS
;
1369 print_vfs_message (_("%s: done."), me
->name
);
1373 if (SUP
.strict
== RFC_AUTODETECT
) {
1374 /* It's our first attempt to get a directory listing from this
1375 server (UNIX style LIST command) */
1376 SUP
.strict
= RFC_STRICT
;
1377 /* I hate goto, but recursive call needs another 8K on stack */
1378 /* return dir_load (me, dir, remote_path); */
1382 print_vfs_message(_("ftpfs: failed; nowhere to fallback to"));
1387 file_store(vfs
*me
, vfs_s_fh
*fh
, char *name
, char *localname
)
1391 #ifdef HAVE_STRUCT_LINGER
1398 vfs_s_super
*super
= FH_SUPER
;
1400 h
= open(localname
, O_RDONLY
);
1404 sock
= open_data_connection(me
, super
, fh
->u
.ftp
.append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1409 #ifdef HAVE_STRUCT_LINGER
1412 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof(li
));
1414 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1418 enable_interrupt_key();
1420 while ((n
= read(h
, buffer
, sizeof(buffer
))) < 0) {
1421 if (errno
== EINTR
) {
1422 if (got_interrupt()) {
1434 while (write(sock
, buffer
, n
) < 0) {
1435 if (errno
== EINTR
) {
1436 if (got_interrupt()) {
1447 print_vfs_message(_("ftpfs: storing file %lu (%lu)"),
1448 (unsigned long) total
, (unsigned long) s
.st_size
);
1450 disable_interrupt_key();
1453 if (get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1457 disable_interrupt_key();
1460 get_reply(me
, SUP
.sock
, NULL
, 0);
1464 //#define FH_SOCK fh->u.ftp.sock
1467 linear_start(vfs
*me
, vfs_s_fh
*fh
, int offset
)
1469 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1473 FH_SOCK
= open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1477 fh
->linear
= LS_LINEAR_OPEN
;
1478 FH_SUPER
->u
.ftp
.control_connection_buzy
= 1;
1479 fh
->u
.ftp
.append
= 0;
1484 linear_read (vfs
*me
, vfs_s_fh
*fh
, void *buf
, int len
)
1487 vfs_s_super
*super
= FH_SUPER
;
1489 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1490 if ((errno
== EINTR
) && !got_interrupt())
1496 linear_abort(me
, fh
);
1499 SUP
.control_connection_buzy
= 0;
1502 if ((get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1503 ERRNOR (E_REMOTE
, -1);
1510 linear_close (vfs
*me
, vfs_s_fh
*fh
)
1513 linear_abort(me
, fh
);
1516 static int ftpfs_ctl (void *fh
, int ctlop
, int arg
)
1519 case MCCTL_IS_NOTREADY
:
1524 vfs_die ("You may not do this");
1525 if (FH
->linear
== LS_LINEAR_CLOSED
)
1528 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1529 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1538 /* Warning: filename passed to this command is damaged */
1540 send_ftp_command(vfs
*me
, char *filename
, char *cmd
, int flags
)
1545 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1547 if (!(rpath
= vfs_s_get_path_mangle(me
, filename
, &super
, 0)))
1549 p
= translate_path (me
, super
, rpath
);
1550 r
= command (me
, super
, WAIT_REPLY
, cmd
, p
);
1552 vfs_add_noncurrent_stamps (&vfs_ftpfs_ops
, (vfsid
) super
, NULL
);
1553 if (flags
& OPT_IGNORE_ERROR
)
1557 if (flush_directory_cache
)
1558 vfs_s_invalidate(me
, super
);
1562 /* This routine is called as the last step in load_setup */
1564 ftpfs_init_passwd(void)
1566 ftpfs_anonymous_passwd
= load_anon_passwd ();
1567 if (ftpfs_anonymous_passwd
)
1570 /* If there is no anonymous ftp password specified
1571 * then we'll just use anonymous@
1572 * We don't send any other thing because:
1573 * - We want to remain anonymous
1574 * - We want to stop SPAM
1575 * - We don't want to let ftp sites to discriminate by the user,
1578 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1581 static int ftpfs_chmod (vfs
*me
, char *path
, int mode
)
1583 char buf
[BUF_SMALL
];
1585 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1586 return send_ftp_command(me
, path
, buf
, OPT_FLUSH
);
1589 static int ftpfs_chown (vfs
*me
, char *path
, int owner
, int group
)
1595 /* Everyone knows it is not possible to chown remotely, so why bother them.
1596 If someone's root, then copy/move will always try to chown it... */
1601 static int ftpfs_unlink (vfs
*me
, char *path
)
1603 return send_ftp_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1606 /* Return 1 if path is the same directory as the one we are in now */
1608 is_same_dir (vfs
*me
, vfs_s_super
*super
, const char *path
)
1612 if (strcmp (path
, SUP
.cwdir
) == 0)
1618 ftpfs_chdir_internal (vfs
*me
, vfs_s_super
*super
, char *remote_path
)
1623 if (!SUP
.cwd_defered
&& is_same_dir (me
, super
, remote_path
))
1626 p
= translate_path (me
, super
, remote_path
);
1627 r
= command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1630 if (r
!= COMPLETE
) {
1634 SUP
.cwdir
= g_strdup (remote_path
);
1635 SUP
.cwd_defered
= 0;
1640 static int ftpfs_rename (vfs
*me
, char *path1
, char *path2
)
1642 send_ftp_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1643 return send_ftp_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1646 static int ftpfs_mkdir (vfs
*me
, char *path
, mode_t mode
)
1648 return send_ftp_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1651 static int ftpfs_rmdir (vfs
*me
, char *path
)
1653 return send_ftp_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1656 static int ftpfs_fh_open (vfs
*me
, vfs_s_fh
*fh
, int flags
, int mode
)
1658 fh
->u
.ftp
.append
= 0;
1659 /* File will be written only, so no need to retrieve it from ftp server */
1660 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
|O_RDWR
))){
1661 #ifdef HAVE_STRUCT_LINGER
1668 /* linear_start() called, so data will be written
1669 * to local temporary file and stored to ftp server
1670 * by vfs_s_close later
1672 if (FH_SUPER
->u
.ftp
.control_connection_buzy
){
1673 if (!fh
->ino
->localname
){
1674 int handle
= mc_mkstemps (&fh
->ino
->localname
, me
->name
, NULL
);
1678 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1682 name
= vfs_s_fullpath (me
, fh
->ino
);
1685 fh
->handle
= open_data_connection(me
, fh
->ino
->super
,
1686 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1691 #ifdef HAVE_STRUCT_LINGER
1695 setsockopt(fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof(li
));
1697 if (fh
->ino
->localname
){
1698 unlink (fh
->ino
->localname
);
1699 g_free (fh
->ino
->localname
);
1700 fh
->ino
->localname
= NULL
;
1705 if (!fh
->ino
->localname
)
1706 if (vfs_s_retrieve_file (me
, fh
->ino
)==-1)
1708 if (!fh
->ino
->localname
)
1709 vfs_die( "retrieve_file failed to fill in localname" );
1713 static int ftpfs_fh_close (vfs
*me
, vfs_s_fh
*fh
)
1715 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1718 /* File is stored to destination already, so
1719 * we prevent MEDATA->file_store() call from vfs_s_close ()
1722 if (get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1724 vfs_s_invalidate (me
, FH_SUPER
);
1729 static struct vfs_s_data ftp_data
= {
1735 NULL
, /* init_inode */
1736 NULL
, /* free_inode */
1737 NULL
, /* init_entry */
1739 NULL
, /* archive_check */
1744 ftpfs_fh_open
, /* fh_open */
1745 ftpfs_fh_close
, /* fh_close */
1747 vfs_s_find_entry_linear
,
1758 ftpfs_fill_names (vfs
*me
, void (*func
)(char *))
1760 struct vfs_s_super
* super
= ftp_data
.supers
;
1764 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, NULL
);
1767 super
= super
->next
;
1771 vfs vfs_ftpfs_ops
= {
1772 NULL
, /* This is place of next pointer */
1775 "ftp:", /* prefix */
1776 &ftp_data
, /* data */
1799 ftpfs_chown
, /* not really implemented but returns success */
1814 vfs_s_nothingisopen
,
1828 void ftpfs_set_debug (const char *file
)
1830 logfile
= fopen (file
, "w+");
1832 ftp_data
.logfile
= logfile
;
1835 static char buffer
[BUF_MEDIUM
];
1836 static char *netrc
, *netrcp
;
1838 /* This should match the keywords[] array below */
1851 static keyword_t
netrc_next (void)
1855 static const char *const keywords
[] = { "default", "machine",
1856 "login", "password", "passwd", "account", "macdef", NULL
1861 netrcp
= skip_separators (netrcp
);
1862 if (*netrcp
!= '\n')
1869 if (*netrcp
== '"') {
1870 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1871 if (*netrcp
== '\\')
1876 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1877 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1878 if (*netrcp
== '\\')
1888 while (keywords
[i
- 1]) {
1889 if (!strcmp (keywords
[i
- 1], buffer
))
1895 return NETRC_UNKNOWN
;
1898 static int netrc_has_incorrect_mode (char *netrcname
, char *netrc
)
1900 static int be_angry
= 1;
1903 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1905 message_1s (1, MSG_ERROR
,
1906 _("~/.netrc file has not correct mode.\n"
1907 "Remove password or correct mode."));
1915 /* Scan .netrc until we find matching "machine" or "default"
1916 * domain is used for additional matching
1917 * No search is done after "default" in compliance with "man netrc"
1918 * Return 0 if found, -1 otherwise */
1919 static int find_machine (const char *host
, const char *domain
)
1923 while ((keyword
= netrc_next ()) != NETRC_NONE
) {
1924 if (keyword
== NETRC_DEFAULT
)
1927 if (keyword
== NETRC_MACDEF
) {
1928 /* Scan for an empty line, which concludes "macdef" */
1930 while (*netrcp
&& *netrcp
!= '\n')
1932 if (*netrcp
!= '\n')
1935 } while (*netrcp
&& *netrcp
!= '\n');
1939 if (keyword
!= NETRC_MACHINE
)
1942 /* Take machine name */
1943 if (netrc_next () == NETRC_NONE
)
1946 if (g_strcasecmp (host
, buffer
)) {
1947 /* Try adding our domain to short names in .netrc */
1948 char *host_domain
= strchr (host
, '.');
1952 /* Compare domain part */
1953 if (g_strcasecmp (host_domain
, domain
))
1956 /* Compare local part */
1957 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1968 /* Extract login and password from .netrc for the host.
1970 * Returns 0 for success, -1 for error */
1971 static int lookup_netrc (const char *host
, char **login
, char **pass
)
1974 char *tmp_pass
= NULL
;
1975 char hostname
[MAXHOSTNAMELEN
], *domain
;
1977 static struct rupcache
{
1978 struct rupcache
*next
;
1982 } *rup_cache
= NULL
, *rupp
;
1984 /* Initialize *login and *pass */
1991 /* Look up in the cache first */
1992 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
1993 if (!strcmp (host
, rupp
->host
)) {
1995 *login
= g_strdup (rupp
->login
);
1996 if (pass
&& rupp
->pass
)
1997 *pass
= g_strdup (rupp
->pass
);
2002 /* Load current .netrc */
2003 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
2004 netrcp
= netrc
= load_file (netrcname
);
2005 if (netrc
== NULL
) {
2010 /* Find our own domain name */
2011 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2013 if (!(domain
= strchr (hostname
, '.')))
2016 /* Scan for "default" and matching "machine" keywords */
2017 find_machine (host
, domain
);
2019 /* Scan for keywords following "default" and "machine" */
2022 keyword
= netrc_next ();
2026 if (netrc_next () == NETRC_NONE
) {
2031 /* We have another name already - should not happen */
2037 /* We have login name now */
2038 *login
= g_strdup (buffer
);
2041 case NETRC_PASSWORD
:
2043 if (netrc_next () == NETRC_NONE
) {
2048 /* Ignore unsafe passwords */
2049 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2050 && netrc_has_incorrect_mode (netrcname
, netrc
)) {
2055 /* Remember password. pass may be NULL, so use tmp_pass */
2056 if (tmp_pass
== NULL
)
2057 tmp_pass
= g_strdup (buffer
);
2061 /* "account" is followed by a token which we ignore */
2062 if (netrc_next () == NETRC_NONE
) {
2067 /* Ignore account, but warn user anyways */
2068 netrc_has_incorrect_mode (netrcname
, netrc
);
2072 /* Unexpected keyword or end of file */
2084 rupp
= g_new (struct rupcache
, 1);
2085 rupp
->host
= g_strdup (host
);
2086 rupp
->login
= rupp
->pass
= 0;
2088 if (*login
!= NULL
) {
2089 rupp
->login
= g_strdup (*login
);
2091 if (tmp_pass
!= NULL
)
2092 rupp
->pass
= g_strdup (tmp_pass
);
2093 rupp
->next
= rup_cache
;