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
216 #define HSC_PROXY_PORT 9875
219 ftp_split_url(char *path
, char **host
, char **user
, int *port
, char **pass
)
223 p
= vfs_split_url (path
, host
, user
, port
, pass
, FTP_COMMAND_PORT
,
227 /* Look up user and password in netrc */
229 lookup_netrc (*host
, user
, pass
);
231 *user
= g_strdup ("anonymous");
234 /* Look up password in netrc for known user */
235 if (use_netrc
&& *user
&& pass
&& !*pass
) {
238 lookup_netrc (*host
, &new_user
, pass
);
240 /* If user is different, remove password */
241 if (new_user
&& strcmp (*user
, new_user
)) {
253 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
255 get_reply (vfs
*me
, int sock
, char *string_buf
, int string_len
)
261 if (!vfs_s_get_line (me
, sock
, answer
, sizeof (answer
), '\n')){
267 switch (sscanf(answer
, "%d", &code
)){
270 strncpy (string_buf
, answer
, string_len
- 1);
271 *(string_buf
+ string_len
- 1) = 0;
276 if (answer
[3] == '-') {
278 if (!vfs_s_get_line (me
, sock
, answer
, sizeof(answer
), '\n')){
284 if ((sscanf (answer
, "%d", &i
) > 0) &&
285 (code
== i
) && (answer
[3] == ' '))
290 strncpy (string_buf
, answer
, string_len
- 1);
291 *(string_buf
+ string_len
- 1) = 0;
299 reconnect (vfs
*me
, vfs_s_super
*super
)
301 int sock
= ftpfs_open_socket (me
, super
);
303 char *cwdir
= SUP
.cwdir
;
307 if (login_server (me
, super
, SUP
.password
)){
310 sock
= ftpfs_chdir_internal (me
, super
, cwdir
);
312 return sock
== COMPLETE
;
320 command (vfs
*me
, vfs_s_super
*super
, int wait_reply
, char *fmt
, ...)
328 fmt_str
= g_strdup_vprintf (fmt
, ap
);
331 status
= strlen (fmt_str
);
332 str
= g_realloc (fmt_str
, status
+ 3);
333 strcpy (str
+ status
, "\r\n");
336 if (strncmp (str
, "PASS ", 5) == 0){
337 fputs ("PASS <Password not logged>\r\n", logfile
);
339 fwrite (str
, status
+ 2, 1, logfile
);
345 enable_interrupt_key ();
346 status
= write (SUP
.sock
, str
, status
+ 2);
351 if (errno
== EPIPE
){ /* Remote server has closed connection */
352 static int level
= 0; /* login_server() use command() */
355 status
= reconnect (me
, super
);
357 if (status
&& write (SUP
.sock
, str
, status
+ 2) > 0)
363 disable_interrupt_key ();
368 disable_interrupt_key ();
371 return get_reply (me
, sock
, (wait_reply
& WANT_STRING
) ? reply_str
: NULL
, sizeof (reply_str
)-1);
376 free_archive (vfs
*me
, vfs_s_super
*super
)
379 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP
.host
);
380 command(me
, super
, NONE
, "QUIT");
386 g_free (SUP
.password
);
389 /* some defines only used by changetype */
390 /* These two are valid values for the second parameter */
392 #define TYPE_BINARY 1
394 /* This one is only used to initialize bucket->isbinary, don't use it as
395 second parameter to changetype. */
396 #define TYPE_UNKNOWN -1
399 changetype (vfs
*me
, vfs_s_super
*super
, int binary
)
401 if (binary
!= SUP
.isbinary
) {
402 if (command (me
, super
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
404 SUP
.isbinary
= binary
;
409 /* This routine logs the user in */
411 login_server (vfs
*me
, vfs_s_super
*super
, const char *netrcpass
)
413 #if defined(HSC_PROXY)
414 char *proxypass
, *proxyname
;
418 char *name
; /* login user name */
420 char reply_string
[BUF_MEDIUM
];
422 SUP
.isbinary
= TYPE_UNKNOWN
;
424 op
= g_strdup (netrcpass
);
426 if (!strcmp (SUP
.user
, "anonymous") ||
427 !strcmp (SUP
.user
, "ftp")) {
428 if (!ftpfs_anonymous_passwd
)
430 op
= g_strdup (ftpfs_anonymous_passwd
);
436 p
= g_strconcat (_(" FTP: Password required for "), SUP
.user
,
438 op
= vfs_get_password (p
);
442 SUP
.password
= g_strdup (op
);
444 op
= g_strdup (SUP
.password
);
448 if (!anon
|| logfile
)
451 pass
= g_strconcat ("-", op
, NULL
);
455 /* Proxy server accepts: username@host-we-want-to-connect*/
457 #if defined(HSC_PROXY)
460 char *proxyhost
= NULL
;
462 ftp_split_url (ftpfs_proxy_host
, &proxyhost
, &proxyname
, &port
, 0);
464 p
= g_strconcat (_(" Proxy: Password required for "), proxyname
, " ",
466 proxypass
= vfs_get_password (p
);
468 if (proxypass
== NULL
) {
469 wipe_password (pass
);
473 name
= g_strdup (SUP
.user
);
475 name
= g_strconcat (SUP
.user
, "@",
476 SUP
.host
[0] == '!' ? SUP
.host
+1 : SUP
.host
, NULL
);
479 name
= g_strdup (SUP
.user
);
481 if (get_reply (me
, SUP
.sock
, reply_string
, sizeof (reply_string
) - 1) == COMPLETE
) {
482 g_strup (reply_string
);
483 SUP
.remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
485 fprintf (logfile
, "MC -- remote_is_amiga = %d\n", SUP
.remote_is_amiga
);
488 #if defined(HSC_PROXY)
490 print_vfs_message (_("ftpfs: sending proxy login name"));
491 if (command (me
, super
, 1, "USER %s", proxyname
) != CONTINUE
)
494 print_vfs_message (_("ftpfs: sending proxy user password"));
495 if (command (me
, super
, 1, "PASS %s", proxypass
) != COMPLETE
)
498 print_vfs_message (_("ftpfs: proxy authentication succeeded"));
499 if (command (me
, super
, 1, "SITE %s", SUP
.host
+1) != COMPLETE
)
502 print_vfs_message (_("ftpfs: connected to %s"), SUP
.host
+1);
505 SUP
.failed_on_login
= 1;
508 wipe_password (proxypass
);
509 wipe_password (pass
);
515 wipe_password (proxypass
);
519 print_vfs_message (_("ftpfs: sending login name"));
520 code
= command (me
, super
, WAIT_REPLY
, "USER %s", name
);
524 print_vfs_message (_("ftpfs: sending user password"));
525 if (command (me
, super
, WAIT_REPLY
, "PASS %s", pass
) != COMPLETE
)
529 print_vfs_message (_("ftpfs: logged in"));
530 wipe_password (pass
);
535 SUP
.failed_on_login
= 1;
538 wipe_password (SUP
.password
);
544 message_2s (1, MSG_ERROR
, _("ftpfs: Login incorrect for user %s "), SUP
.user
);
546 wipe_password (pass
);
551 #ifdef HAVE_SETSOCKOPT
553 setup_source_route (int socket
, int dest
)
560 memset (buffer
, 0, sizeof (buffer
));
563 *ptr
++ = 4; /* pointer */
566 memcpy (ptr
, (char *) &source_route
, sizeof (int));
569 /* Second hop (ie, final destination) */
570 memcpy (ptr
, (char *) &dest
, sizeof (int));
572 while ((ptr
- buffer
) & 3)
574 if (setsockopt (socket
, IPPROTO_IP
, IP_OPTIONS
,
575 buffer
, ptr
- buffer
) < 0)
576 message_2s (1, MSG_ERROR
, _(" Could not set source routing (%s)"), unix_error_string (errno
));
579 #define setup_source_route(x,y)
582 static struct no_proxy_entry
{
588 load_no_proxy_list (void)
590 /* FixMe: shouldn't be hardcoded!!! */
591 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
592 struct no_proxy_entry
*np
, *current
= 0;
596 static char *mc_file
;
601 mc_file
= concat_dir_and_file (mc_home
, "mc.no_proxy");
602 if (exist_file (mc_file
) &&
603 (npf
= fopen (mc_file
, "r"))) {
604 while (fgets (s
, sizeof(s
), npf
) || !(feof (npf
) || ferror (npf
))) {
605 if (!(p
= strchr (s
, '\n'))) { /* skip bogus entries */
606 while ((c
= fgetc (npf
)) != EOF
&& c
!= '\n')
616 np
= g_new (struct no_proxy_entry
, 1);
617 np
->domain
= g_strdup (s
);
632 ftpfs_check_proxy (const char *host
)
634 struct no_proxy_entry
*npe
;
636 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
637 return 0; /* sanity check */
642 if (!ftpfs_always_use_proxy
)
645 if (!strchr (host
, '.'))
648 load_no_proxy_list ();
649 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
650 char *domain
= npe
->domain
;
652 if (domain
[0] == '.') {
653 int ld
= strlen (domain
);
654 int lh
= strlen (host
);
656 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
664 if (!g_strcasecmp (host
, domain
))
672 ftpfs_get_proxy_host_and_port (char *proxy
, char **host
, int *port
)
676 #if defined(HSC_PROXY)
677 dir
= vfs_split_url (proxy
, host
, &user
, port
, 0, HSC_PROXY_PORT
, URL_ALLOW_ANON
);
679 dir
= vfs_split_url (proxy
, host
, &user
, port
, 0, FTP_COMMAND_PORT
, URL_ALLOW_ANON
);
690 ftpfs_open_socket (vfs
*me
, vfs_s_super
*super
)
692 struct sockaddr_in server_address
;
699 /* Use a proxy host? */
702 if (!host
|| !*host
){
703 print_vfs_message (_("ftpfs: Invalid host name."));
708 /* Hosts to connect to that start with a ! should use proxy */
710 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
714 /* Get host address */
715 memset ((char *) &server_address
, 0, sizeof (server_address
));
716 server_address
.sin_family
= AF_INET
;
717 server_address
.sin_addr
.s_addr
= inet_addr (host
);
718 if (server_address
.sin_addr
.s_addr
!= -1)
719 server_address
.sin_family
= AF_INET
;
721 hp
= gethostbyname (host
);
723 print_vfs_message (_("ftpfs: Invalid host address."));
729 server_address
.sin_family
= hp
->h_addrtype
;
731 /* We copy only 4 bytes, we can not trust hp->h_length, as it comes from the DNS */
732 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
, 4);
735 server_address
.sin_port
= htons (port
);
738 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
744 setup_source_route (my_socket
, server_address
.sin_addr
.s_addr
);
746 print_vfs_message (_("ftpfs: making connection to %s"), host
);
750 enable_interrupt_key (); /* clear the interrupt flag */
752 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
753 sizeof (server_address
)) < 0){
755 if (errno
== EINTR
&& got_interrupt ())
756 print_vfs_message (_("ftpfs: connection interrupted by user"));
758 print_vfs_message (_("ftpfs: connection to server failed: %s"),
759 unix_error_string(errno
));
760 disable_interrupt_key();
764 disable_interrupt_key();
769 open_archive_int (vfs
*me
, vfs_s_super
*super
)
771 int retry_seconds
, count_down
;
773 /* We do not want to use the passive if we are using proxies */
775 SUP
.use_passive_connection
= 0;
779 SUP
.failed_on_login
= 0;
781 SUP
.sock
= ftpfs_open_socket (me
, super
);
785 if (login_server (me
, super
, NULL
)) {
786 /* Logged in, no need to retry the connection */
789 if (SUP
.failed_on_login
){
790 /* Close only the socket descriptor */
795 if (ftpfs_retry_seconds
){
796 retry_seconds
= ftpfs_retry_seconds
;
797 enable_interrupt_key ();
798 for (count_down
= retry_seconds
; count_down
; count_down
--){
799 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
801 if (got_interrupt ()){
803 disable_interrupt_key ();
807 disable_interrupt_key ();
810 } while (retry_seconds
);
812 SUP
.cwdir
= ftpfs_get_current_directory (me
, super
);
814 SUP
.cwdir
= g_strdup (PATH_SEP_STR
);
819 open_archive (vfs
*me
, vfs_s_super
*super
, char *archive_name
, char *op
)
821 char *host
, *user
, *password
;
824 ftp_split_url (strchr (op
, ':') + 1, &host
, &user
, &port
, &password
);
831 if (ftpfs_check_proxy (host
))
832 SUP
.proxy
= ftpfs_proxy_host
;
833 SUP
.password
= password
;
834 SUP
.use_passive_connection
= ftpfs_use_passive_connections
| source_route
;
835 SUP
.use_source_route
= source_route
;
836 SUP
.strict
= ftpfs_use_unix_list_options
? RFC_AUTODETECT
: RFC_STRICT
;
837 SUP
.isbinary
= TYPE_UNKNOWN
;
838 SUP
.remote_is_amiga
= 0;
839 super
->name
= g_strdup("/");
840 super
->root
= vfs_s_new_inode (me
, super
, vfs_s_default_stat(me
, S_IFDIR
| 0755));
842 return open_archive_int (me
, super
);
846 archive_same(vfs
*me
, vfs_s_super
*super
, char *archive_name
, char *op
, void *cookie
)
851 ftp_split_url (strchr(op
, ':') + 1, &host
, &user
, &port
, 0);
853 port
= ((strcmp (host
, SUP
.host
) == 0) &&
854 (strcmp (user
, SUP
.user
) == 0) &&
864 ftpfs_flushdir (void)
866 force_expiration
= 1;
870 dir_uptodate(vfs
*me
, vfs_s_inode
*ino
)
874 if (force_expiration
) {
875 force_expiration
= 0;
878 gettimeofday(&tim
, NULL
);
879 if (tim
.tv_sec
< ino
->u
.ftp
.timestamp
.tv_sec
)
884 /* The returned directory should always contain a trailing slash */
886 ftpfs_get_current_directory (vfs
*me
, vfs_s_super
*super
)
888 char buf
[BUF_8K
], *bufp
, *bufq
;
890 if (command (me
, super
, NONE
, "PWD") == COMPLETE
&&
891 get_reply(me
, SUP
.sock
, buf
, sizeof(buf
)) == COMPLETE
) {
893 for (bufq
= buf
; *bufq
; bufq
++)
900 if (*(bufq
- 1) != '/') {
905 return g_strdup (bufp
);
907 /* If the remote server is an Amiga a leading slash
908 might be missing. MC needs it because it is used
909 as seperator between hostname and path internally. */
910 return g_strconcat( "/", bufp
, 0);
924 /* Setup Passive ftp connection, we use it for source routed connections */
926 setup_passive (vfs
*me
, vfs_s_super
*super
, int my_socket
, struct sockaddr_in
*sa
)
928 int xa
, xb
, xc
, xd
, xe
, xf
;
932 if (command (me
, super
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
935 /* Parse remote parameters */
936 for (c
= reply_str
+ 4; (*c
) && (!isdigit ((unsigned char) *c
)); c
++)
940 if (!isdigit ((unsigned char) *c
))
942 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
944 n
[0] = (unsigned char) xa
;
945 n
[1] = (unsigned char) xb
;
946 n
[2] = (unsigned char) xc
;
947 n
[3] = (unsigned char) xd
;
948 n
[4] = (unsigned char) xe
;
949 n
[5] = (unsigned char) xf
;
951 memcpy (&(sa
->sin_addr
.s_addr
), (void *)n
, 4);
952 memcpy (&(sa
->sin_port
), (void *)&n
[4], 2);
953 setup_source_route (my_socket
, sa
->sin_addr
.s_addr
);
954 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
960 initconn (vfs
*me
, vfs_s_super
*super
)
962 struct sockaddr_in data_addr
;
963 int data
, len
= sizeof(data_addr
);
966 getsockname(SUP
.sock
, (struct sockaddr
*) &data_addr
, &len
);
967 data_addr
.sin_port
= 0;
969 pe
= getprotobyname("tcp");
972 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
976 if (SUP
.use_passive_connection
){
977 if ((SUP
.use_passive_connection
= setup_passive (me
, super
, data
, &data_addr
)))
980 SUP
.use_source_route
= 0;
981 SUP
.use_passive_connection
= 0;
982 print_vfs_message (_("ftpfs: could not setup passive mode"));
985 /* If passive setup fails, fallback to active connections */
986 /* Active FTP connection */
987 if ((bind (data
, (struct sockaddr
*)&data_addr
, len
) == 0) &&
988 (getsockname (data
, (struct sockaddr
*) &data_addr
, &len
) == 0) &&
989 (listen (data
, 1) == 0))
991 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
992 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
994 if (command (me
, super
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
995 a
[2], a
[3], p
[0], p
[1]) == COMPLETE
)
1004 open_data_connection (vfs
*me
, vfs_s_super
*super
, char *cmd
, char *remote
,
1005 int isbinary
, int reget
)
1007 struct sockaddr_in from
;
1008 int s
, j
, data
, fromlen
= sizeof(from
);
1010 if ((s
= initconn (me
, super
)) == -1)
1012 if (changetype (me
, super
, isbinary
) == -1)
1015 j
= command (me
, super
, WAIT_REPLY
, "REST %d", reget
);
1020 char * remote_path
= translate_path (me
, super
, remote
);
1021 j
= command (me
, super
, WAIT_REPLY
, "%s /%s", cmd
,
1022 /* WarFtpD can't STORE //filename */
1023 (*remote_path
== '/') ? remote_path
+ 1 : remote_path
);
1024 g_free (remote_path
);
1026 j
= command (me
, super
, WAIT_REPLY
, "%s", cmd
);
1029 enable_interrupt_key();
1030 if (SUP
.use_passive_connection
)
1033 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
1040 disable_interrupt_key();
1045 linear_abort (vfs
*me
, vfs_s_fh
*fh
)
1047 vfs_s_super
*super
= FH_SUPER
;
1048 static unsigned char const ipbuf
[3] = { IAC
, IP
, IAC
};
1051 int dsock
= FH_SOCK
;
1053 SUP
.control_connection_buzy
= 0;
1055 print_vfs_message(_("ftpfs: aborting transfer."));
1056 if (send(SUP
.sock
, ipbuf
, sizeof(ipbuf
), MSG_OOB
) != sizeof(ipbuf
)) {
1057 print_vfs_message(_("ftpfs: abort error: %s"), unix_error_string(errno
));
1061 if (command(me
, super
, NONE
, "%cABOR", DM
) != COMPLETE
){
1062 print_vfs_message (_("ftpfs: abort failed"));
1067 FD_SET(dsock
, &mask
);
1068 if (select(dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0)
1069 while (read(dsock
, buf
, sizeof(buf
)) > 0);
1071 if ((get_reply(me
, SUP
.sock
, NULL
, 0) == TRANSIENT
) && (code
== 426))
1072 get_reply(me
, SUP
.sock
, NULL
, 0);
1077 resolve_symlink_without_ls_options(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1079 struct linklist
*flist
;
1080 struct direntry
*fe
, *fel
;
1081 char tmp
[MC_MAXPATHLEN
];
1084 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1085 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1086 /* flist->data->l_stat is alread initialized with 0 */
1088 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1089 if (fel
->linkname
[0] == '/') {
1090 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1092 strcpy (tmp
, fel
->linkname
);
1094 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1096 strcpy (tmp
, dir
->remote_path
);
1099 strcat (tmp
+ 1, fel
->linkname
);
1101 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1102 canonicalize_pathname (tmp
);
1103 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1105 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1106 /* Symlink points to link which isn't resolved, yet. */
1107 if (fe
->linkname
[0] == '/') {
1108 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1110 strcpy (tmp
, fe
->linkname
);
1112 /* at this point tmp looks always like this
1113 /directory/filename, i.e. no need to check
1114 strrchr's return value */
1115 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1116 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1118 strcat (tmp
, fe
->linkname
);
1122 fel
->l_stat
= g_new (struct stat
, 1);
1123 if ( S_ISLNK (fe
->s
.st_mode
))
1124 *fel
->l_stat
= *fe
->l_stat
;
1126 *fel
->l_stat
= fe
->s
;
1127 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1134 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1138 resolve_symlink_with_ls_options(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1140 char buffer
[2048] = "", *filename
;
1144 struct linklist
*flist
;
1145 struct direntry
*fe
;
1146 int switch_method
= 0;
1148 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1149 if (strchr (dir
->remote_path
, ' ')) {
1150 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1151 print_vfs_message(_("ftpfs: CWD failed."));
1154 sock
= open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1157 sock
= open_data_connection (bucket
, "LIST -lLa",
1158 dir
->remote_path
, TYPE_ASCII
, 0);
1161 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1165 fp
= fdopen(sock
, "r");
1168 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1171 enable_interrupt_key();
1172 flist
= dir
->file_list
->next
;
1175 if (flist
== dir
->file_list
)
1178 flist
= flist
->next
;
1179 } while (!S_ISLNK(fe
->s
.st_mode
));
1181 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1184 fputs (buffer
, logfile
);
1187 vfs_die("This code should be commented out\n");
1188 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1189 int r
= strcmp(fe
->name
, filename
);
1192 if (S_ISLNK (s
.st_mode
)) {
1193 /* This server doesn't understand LIST -lLa */
1197 fe
->l_stat
= g_new (struct stat
, 1);
1198 if (fe
->l_stat
== NULL
)
1201 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1210 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1211 disable_interrupt_key();
1213 get_reply(me
, SUP
.sock
, NULL
, 0);
1217 resolve_symlink(vfs
*me
, vfs_s_super
*super
, vfs_s_inode
*dir
)
1219 print_vfs_message(_("Resolving symlink..."));
1221 if (SUP
.strict_rfc959_list_cmd
)
1222 resolve_symlink_without_ls_options(me
, super
, dir
);
1224 resolve_symlink_with_ls_options(me
, super
, dir
);
1229 dir_load(vfs
*me
, vfs_s_inode
*dir
, char *remote_path
)
1232 vfs_s_super
*super
= dir
->super
;
1233 int sock
, num_entries
= 0;
1235 int has_symlinks
= 0;
1237 char buffer
[BUF_8K
];
1240 cd_first
= ftpfs_first_cd_then_ls
|| (strchr (remote_path
, ' ') != NULL
)
1241 || (SUP
.strict
== RFC_STRICT
);
1244 print_vfs_message(_("ftpfs: Reading FTP directory %s... %s%s"), remote_path
,
1245 SUP
.strict
== RFC_STRICT
? _("(strict rfc959)") : "",
1246 cd_first
? _("(chdir first)") : "");
1251 p
= translate_path (me
, super
, remote_path
);
1253 if (ftpfs_chdir_internal (me
, super
, p
) != COMPLETE
) {
1256 print_vfs_message(_("ftpfs: CWD failed."));
1262 gettimeofday(&dir
->u
.ftp
.timestamp
, NULL
);
1263 dir
->u
.ftp
.timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1265 if (SUP
.strict
== RFC_STRICT
)
1266 sock
= open_data_connection (me
, super
, "LIST", 0, TYPE_ASCII
, 0);
1268 /* Dirty hack to avoid autoprepending / to . */
1269 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1270 sock
= open_data_connection (me
, super
, "LIST -la", 0, TYPE_ASCII
, 0);
1272 /* Trailing "/." is necessary if remote_path is a symlink */
1273 char *path
= concat_dir_and_file (remote_path
, ".");
1274 sock
= open_data_connection (me
, super
, "LIST -la", path
, TYPE_ASCII
, 0);
1281 /* Clear the interrupt flag */
1282 enable_interrupt_key ();
1286 /* added 20001006 by gisburn
1287 * add dots '.' and '..'. This must be _executed_ before scanning the dir as the
1288 * code below may jump directly into error handling code (without executing
1289 * remaining code). And C doesn't have try {...} finally {}; :-)
1291 vfs_s_inode
*parent
= dir
->ent
->dir
;
1296 ent
= vfs_s_generate_entry(me
, ".", dir
, 0);
1297 ent
->ino
->st
=dir
->st
;
1299 vfs_s_insert_entry(me
, dir
, ent
);
1301 ent
= vfs_s_generate_entry(me
, "..", parent
, 0);
1302 ent
->ino
->st
=parent
->st
;
1304 vfs_s_insert_entry(me
, dir
, ent
);
1310 int res
= vfs_s_get_line_interruptible (me
, buffer
, sizeof (buffer
), sock
);
1315 me
->verrno
= ECONNRESET
;
1317 disable_interrupt_key();
1318 get_reply(me
, SUP
.sock
, NULL
, 0);
1319 print_vfs_message (_("%s: failure"), me
->name
);
1324 fputs (buffer
, logfile
);
1325 fputs ("\n", logfile
);
1329 ent
= vfs_s_generate_entry(me
, NULL
, dir
, 0);
1330 i
= ent
->ino
->st
.st_nlink
;
1331 if (!vfs_parse_ls_lga (buffer
, &ent
->ino
->st
, &ent
->name
, &ent
->ino
->linkname
)) {
1332 vfs_s_free_entry (me
, ent
);
1335 ent
->ino
->st
.st_nlink
= i
; /* Ouch, we need to preserve our counts :-( */
1337 if ((!strcmp(ent
->name
, ".")) || (!strcmp (ent
->name
, ".."))) {
1339 ent
->name
= NULL
; /* Ouch, vfs_s_free_entry "knows" about . and .. being special :-( */
1340 vfs_s_free_entry (me
, ent
);
1344 vfs_s_insert_entry(me
, dir
, ent
);
1347 /* vfs_s_add_dots(me, dir, NULL);
1348 FIXME This really should be here; but we need to provide correct parent.
1349 Disabled for now, please fix it. Pavel
1352 me
->verrno
= E_REMOTE
;
1353 if ((get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
) || !num_entries
)
1356 if (SUP
.strict
== RFC_AUTODETECT
)
1357 SUP
.strict
= RFC_DARING
;
1361 if (resolve_symlinks
)
1362 resolve_symlink(me
, super
, dcache
);
1364 dcache
->symlink_status
= FTPFS_UNRESOLVED_SYMLINKS
;
1367 print_vfs_message (_("%s: done."), me
->name
);
1371 if (SUP
.strict
== RFC_AUTODETECT
) {
1372 /* It's our first attempt to get a directory listing from this
1373 server (UNIX style LIST command) */
1374 SUP
.strict
= RFC_STRICT
;
1375 /* I hate goto, but recursive call needs another 8K on stack */
1376 /* return dir_load (me, dir, remote_path); */
1380 print_vfs_message(_("ftpfs: failed; nowhere to fallback to"));
1385 file_store(vfs
*me
, vfs_s_fh
*fh
, char *name
, char *localname
)
1389 #ifdef HAVE_STRUCT_LINGER
1396 vfs_s_super
*super
= FH_SUPER
;
1398 h
= open(localname
, O_RDONLY
);
1402 sock
= open_data_connection(me
, super
, fh
->u
.ftp
.append
? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1407 #ifdef HAVE_STRUCT_LINGER
1410 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof(li
));
1412 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1416 enable_interrupt_key();
1418 while ((n
= read(h
, buffer
, sizeof(buffer
))) < 0) {
1419 if (errno
== EINTR
) {
1420 if (got_interrupt()) {
1432 while (write(sock
, buffer
, n
) < 0) {
1433 if (errno
== EINTR
) {
1434 if (got_interrupt()) {
1445 print_vfs_message(_("ftpfs: storing file %lu (%lu)"),
1446 (unsigned long) total
, (unsigned long) s
.st_size
);
1448 disable_interrupt_key();
1451 if (get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
)
1455 disable_interrupt_key();
1458 get_reply(me
, SUP
.sock
, NULL
, 0);
1462 //#define FH_SOCK fh->u.ftp.sock
1465 linear_start(vfs
*me
, vfs_s_fh
*fh
, int offset
)
1467 char *name
= vfs_s_fullpath (me
, fh
->ino
);
1471 FH_SOCK
= open_data_connection(me
, FH_SUPER
, "RETR", name
, TYPE_BINARY
, offset
);
1475 fh
->linear
= LS_LINEAR_OPEN
;
1476 FH_SUPER
->u
.ftp
.control_connection_buzy
= 1;
1477 fh
->u
.ftp
.append
= 0;
1482 linear_read (vfs
*me
, vfs_s_fh
*fh
, void *buf
, int len
)
1485 vfs_s_super
*super
= FH_SUPER
;
1487 while ((n
= read (FH_SOCK
, buf
, len
))<0) {
1488 if ((errno
== EINTR
) && !got_interrupt())
1494 linear_abort(me
, fh
);
1497 SUP
.control_connection_buzy
= 0;
1500 if ((get_reply (me
, SUP
.sock
, NULL
, 0) != COMPLETE
))
1501 ERRNOR (E_REMOTE
, -1);
1508 linear_close (vfs
*me
, vfs_s_fh
*fh
)
1511 linear_abort(me
, fh
);
1514 static int ftpfs_ctl (void *fh
, int ctlop
, int arg
)
1517 case MCCTL_IS_NOTREADY
:
1522 vfs_die ("You may not do this");
1523 if (FH
->linear
== LS_LINEAR_CLOSED
)
1526 v
= vfs_s_select_on_two (FH
->u
.ftp
.sock
, 0);
1527 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1536 /* Warning: filename passed to this command is damaged */
1538 send_ftp_command(vfs
*me
, char *filename
, char *cmd
, int flags
)
1543 int flush_directory_cache
= (flags
& OPT_FLUSH
);
1545 if (!(rpath
= vfs_s_get_path_mangle(me
, filename
, &super
, 0)))
1547 p
= translate_path (me
, super
, rpath
);
1548 r
= command (me
, super
, WAIT_REPLY
, cmd
, p
);
1550 vfs_add_noncurrent_stamps (&vfs_ftpfs_ops
, (vfsid
) super
, NULL
);
1551 if (flags
& OPT_IGNORE_ERROR
)
1555 if (flush_directory_cache
)
1556 vfs_s_invalidate(me
, super
);
1560 /* This routine is called as the last step in load_setup */
1562 ftpfs_init_passwd(void)
1564 ftpfs_anonymous_passwd
= load_anon_passwd ();
1565 if (ftpfs_anonymous_passwd
)
1568 /* If there is no anonymous ftp password specified
1569 * then we'll just use anonymous@
1570 * We don't send any other thing because:
1571 * - We want to remain anonymous
1572 * - We want to stop SPAM
1573 * - We don't want to let ftp sites to discriminate by the user,
1576 ftpfs_anonymous_passwd
= g_strdup ("anonymous@");
1579 static int ftpfs_chmod (vfs
*me
, char *path
, int mode
)
1581 char buf
[BUF_SMALL
];
1583 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o /%%s", mode
& 07777);
1584 return send_ftp_command(me
, path
, buf
, OPT_FLUSH
);
1587 static int ftpfs_chown (vfs
*me
, char *path
, int owner
, int group
)
1593 /* Everyone knows it is not possible to chown remotely, so why bother them.
1594 If someone's root, then copy/move will always try to chown it... */
1599 static int ftpfs_unlink (vfs
*me
, char *path
)
1601 return send_ftp_command(me
, path
, "DELE /%s", OPT_FLUSH
);
1604 /* Return 1 if path is the same directory as the one we are in now */
1606 is_same_dir (vfs
*me
, vfs_s_super
*super
, const char *path
)
1610 if (strcmp (path
, SUP
.cwdir
) == 0)
1616 ftpfs_chdir_internal (vfs
*me
, vfs_s_super
*super
, char *remote_path
)
1621 if (!SUP
.cwd_defered
&& is_same_dir (me
, super
, remote_path
))
1624 p
= translate_path (me
, super
, remote_path
);
1625 r
= command (me
, super
, WAIT_REPLY
, "CWD /%s", p
);
1628 if (r
!= COMPLETE
) {
1632 SUP
.cwdir
= g_strdup (remote_path
);
1633 SUP
.cwd_defered
= 0;
1638 static int ftpfs_rename (vfs
*me
, char *path1
, char *path2
)
1640 send_ftp_command(me
, path1
, "RNFR /%s", OPT_FLUSH
);
1641 return send_ftp_command(me
, path2
, "RNTO /%s", OPT_FLUSH
);
1644 static int ftpfs_mkdir (vfs
*me
, char *path
, mode_t mode
)
1646 return send_ftp_command(me
, path
, "MKD /%s", OPT_FLUSH
);
1649 static int ftpfs_rmdir (vfs
*me
, char *path
)
1651 return send_ftp_command(me
, path
, "RMD /%s", OPT_FLUSH
);
1654 static int ftpfs_fh_open (vfs
*me
, vfs_s_fh
*fh
, int flags
, int mode
)
1656 fh
->u
.ftp
.append
= 0;
1657 /* File will be written only, so no need to retrieve it from ftp server */
1658 if (((flags
& O_WRONLY
) == O_WRONLY
) && !(flags
& (O_RDONLY
|O_RDWR
))){
1659 #ifdef HAVE_STRUCT_LINGER
1666 /* linear_start() called, so data will be written
1667 * to local temporary file and stored to ftp server
1668 * by vfs_s_close later
1670 if (FH_SUPER
->u
.ftp
.control_connection_buzy
){
1671 if (!fh
->ino
->localname
){
1672 int handle
= mc_mkstemps (&fh
->ino
->localname
, me
->name
, NULL
);
1676 fh
->u
.ftp
.append
= flags
& O_APPEND
;
1680 name
= vfs_s_fullpath (me
, fh
->ino
);
1683 fh
->handle
= open_data_connection(me
, fh
->ino
->super
,
1684 (flags
& O_APPEND
) ? "APPE" : "STOR", name
, TYPE_BINARY
, 0);
1689 #ifdef HAVE_STRUCT_LINGER
1693 setsockopt(fh
->handle
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof(li
));
1695 if (fh
->ino
->localname
){
1696 unlink (fh
->ino
->localname
);
1697 g_free (fh
->ino
->localname
);
1698 fh
->ino
->localname
= NULL
;
1703 if (!fh
->ino
->localname
)
1704 if (vfs_s_retrieve_file (me
, fh
->ino
)==-1)
1706 if (!fh
->ino
->localname
)
1707 vfs_die( "retrieve_file failed to fill in localname" );
1711 static int ftpfs_fh_close (vfs
*me
, vfs_s_fh
*fh
)
1713 if (fh
->handle
!= -1 && !fh
->ino
->localname
){
1716 /* File is stored to destination already, so
1717 * we prevent MEDATA->file_store() call from vfs_s_close ()
1720 if (get_reply (me
, fh
->ino
->SUP
.sock
, NULL
, 0) != COMPLETE
)
1722 vfs_s_invalidate (me
, FH_SUPER
);
1727 static struct vfs_s_data ftp_data
= {
1733 NULL
, /* init_inode */
1734 NULL
, /* free_inode */
1735 NULL
, /* init_entry */
1737 NULL
, /* archive_check */
1742 ftpfs_fh_open
, /* fh_open */
1743 ftpfs_fh_close
, /* fh_close */
1745 vfs_s_find_entry_linear
,
1756 ftpfs_fill_names (vfs
*me
, void (*func
)(char *))
1758 struct vfs_s_super
* super
= ftp_data
.supers
;
1762 name
= g_strconcat ("/#ftp:", SUP
.user
, "@", SUP
.host
, "/", SUP
.cwdir
, NULL
);
1765 super
= super
->next
;
1769 vfs vfs_ftpfs_ops
= {
1770 NULL
, /* This is place of next pointer */
1773 "ftp:", /* prefix */
1774 &ftp_data
, /* data */
1797 ftpfs_chown
, /* not really implemented but returns success */
1812 vfs_s_nothingisopen
,
1826 void ftpfs_set_debug (const char *file
)
1828 logfile
= fopen (file
, "w+");
1830 ftp_data
.logfile
= logfile
;
1833 static char buffer
[BUF_MEDIUM
];
1834 static char *netrc
, *netrcp
;
1836 /* This should match the keywords[] array below */
1849 static keyword_t
netrc_next (void)
1853 static const char *const keywords
[] = { "default", "machine",
1854 "login", "password", "passwd", "account", "macdef", NULL
1859 netrcp
= skip_separators (netrcp
);
1860 if (*netrcp
!= '\n')
1867 if (*netrcp
== '"') {
1868 for (netrcp
++; *netrcp
!= '"' && *netrcp
; netrcp
++) {
1869 if (*netrcp
== '\\')
1874 for (; *netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1875 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1876 if (*netrcp
== '\\')
1886 while (keywords
[i
- 1]) {
1887 if (!strcmp (keywords
[i
- 1], buffer
))
1893 return NETRC_UNKNOWN
;
1896 static int netrc_has_incorrect_mode (char *netrcname
, char *netrc
)
1898 static int be_angry
= 1;
1901 if (stat (netrcname
, &mystat
) >= 0 && (mystat
.st_mode
& 077)) {
1903 message_1s (1, MSG_ERROR
,
1904 _("~/.netrc file has not correct mode.\n"
1905 "Remove password or correct mode."));
1913 /* Scan .netrc until we find matching "machine" or "default"
1914 * domain is used for additional matching
1915 * No search is done after "default" in compliance with "man netrc"
1916 * Return 0 if found, -1 otherwise */
1917 static int find_machine (const char *host
, const char *domain
)
1921 while ((keyword
= netrc_next ()) != NETRC_NONE
) {
1922 if (keyword
== NETRC_DEFAULT
)
1925 if (keyword
== NETRC_MACDEF
) {
1926 /* Scan for an empty line, which concludes "macdef" */
1928 while (*netrcp
&& *netrcp
!= '\n')
1930 if (*netrcp
!= '\n')
1933 } while (*netrcp
&& *netrcp
!= '\n');
1937 if (keyword
!= NETRC_MACHINE
)
1940 /* Take machine name */
1941 if (netrc_next () == NETRC_NONE
)
1944 if (g_strcasecmp (host
, buffer
)) {
1945 /* Try adding our domain to short names in .netrc */
1946 char *host_domain
= strchr (host
, '.');
1950 /* Compare domain part */
1951 if (g_strcasecmp (host_domain
, domain
))
1954 /* Compare local part */
1955 if (g_strncasecmp (host
, buffer
, host_domain
- host
))
1966 /* Extract login and password from .netrc for the host.
1968 * Returns 0 for success, -1 for error */
1969 static int lookup_netrc (const char *host
, char **login
, char **pass
)
1972 char *tmp_pass
= NULL
;
1973 char hostname
[MAXHOSTNAMELEN
], *domain
;
1975 static struct rupcache
{
1976 struct rupcache
*next
;
1980 } *rup_cache
= NULL
, *rupp
;
1982 /* Initialize *login and *pass */
1989 /* Look up in the cache first */
1990 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
) {
1991 if (!strcmp (host
, rupp
->host
)) {
1993 *login
= g_strdup (rupp
->login
);
1994 if (pass
&& rupp
->pass
)
1995 *pass
= g_strdup (rupp
->pass
);
2000 /* Load current .netrc */
2001 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
2002 netrcp
= netrc
= load_file (netrcname
);
2003 if (netrc
== NULL
) {
2008 /* Find our own domain name */
2009 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2011 if (!(domain
= strchr (hostname
, '.')))
2014 /* Scan for "default" and matching "machine" keywords */
2015 find_machine (host
, domain
);
2017 /* Scan for keywords following "default" and "machine" */
2020 keyword
= netrc_next ();
2024 if (netrc_next () == NETRC_NONE
) {
2029 /* We have another name already - should not happen */
2035 /* We have login name now */
2036 *login
= g_strdup (buffer
);
2039 case NETRC_PASSWORD
:
2041 if (netrc_next () == NETRC_NONE
) {
2046 /* Ignore unsafe passwords */
2047 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp")
2048 && netrc_has_incorrect_mode (netrcname
, netrc
)) {
2053 /* Remember password. pass may be NULL, so use tmp_pass */
2054 if (tmp_pass
== NULL
)
2055 tmp_pass
= g_strdup (buffer
);
2059 /* "account" is followed by a token which we ignore */
2060 if (netrc_next () == NETRC_NONE
) {
2065 /* Ignore account, but warn user anyways */
2066 netrc_has_incorrect_mode (netrcname
, netrc
);
2070 /* Unexpected keyword or end of file */
2082 rupp
= g_new (struct rupcache
, 1);
2083 rupp
->host
= g_strdup (host
);
2084 rupp
->login
= rupp
->pass
= 0;
2086 if (*login
!= NULL
) {
2087 rupp
->login
= g_strdup (*login
);
2089 if (tmp_pass
!= NULL
)
2090 rupp
->pass
= g_strdup (tmp_pass
);
2091 rupp
->next
= rup_cache
;