1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Ching Hui
6 1995, 1996, 1997 Miguel de Icaza
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public License
12 as published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
26 - make it more robust - all the connects etc. should handle EADDRINUSE and
27 ERETRY (have I spelled these names correctly?)
28 - make the user able to flush a connection - all the caches will get empty
29 etc., (tarfs as well), we should give there a user selectable timeout
30 and assign a key sequence.
31 - use hash table instead of linklist to cache ftpfs directory.
34 /* Namespace pollution: horrible */
42 #include <sys/types.h>
46 #include <ctype.h> /* For isdigit */
48 # include <sys/timeb.h> /* alex: for struct timeb definition */
49 #endif /* SCO_FLAVOR */
51 #include <sys/types.h>
52 #if defined(HAVE_UNISTD_H)
56 # include <sys/select.h>
59 #include "../src/setup.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>
71 # include <sys/time.h> /* alex: this redefines struct timeval */
72 #endif /* SCO_FLAVOR */
73 #include <sys/param.h>
83 #include "../src/dialog.h"
84 #include "container.h"
86 #ifndef MAXHOSTNAMELEN
87 # define MAXHOSTNAMELEN 64
90 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
91 #define UPLOAD_ZERO_LENGTH_FILE
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
= 0;
119 int ftpfs_directory_timeout
;
122 char *ftpfs_proxy_host
= 0;
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 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 static struct direntry
*_get_file_entry (struct connection
*bucket
,
145 char *file_name
, int op
, int flags
);
147 static char *ftpfs_get_current_directory (struct connection
*bucket
);
148 static int ftpfs_chdir_internal (struct connection
*bucket
,
150 static void free_bucket (void *data
);
151 static void connection_destructor (void *data
);
152 static void flush_all_directory (struct connection
*bucket
);
153 static int get_line (int sock
, char *buf
, int buf_len
,
155 static char *get_path (struct connection
**bucket
,
158 /* char *translate_path (struct ftpfs_connection *bucket, char *remote_path)
159 Translate a Unix path, i.e. MC's internal path representation (e.g.
160 /somedir/somefile) to a path valid for the remote server. Every path
161 transfered to the remote server has to be mangled by this function
162 right prior to sending it.
163 Currently only Amiga ftp servers are handled in a special manner.
165 When the remote server is an amiga:
166 a) strip leading slash if necesarry
167 b) replace first occurance of ":/" with ":"
168 c) strip trailing "/."
172 translate_path (struct connection
*bucket
, char *remote_path
)
176 if (!bucket
->remote_is_amiga
)
177 return g_strdup (remote_path
);
182 fprintf (logfile
, "MC -- translate_path: %s\n", remote_path
);
187 * Don't change "/" into "", e.g. "CWD " would be
190 if (*remote_path
== '/' && remote_path
[1] == '\0')
191 return g_strdup (".");
193 /* strip leading slash */
194 if (*remote_path
== '/')
195 ret
= g_strdup (remote_path
+ 1);
197 ret
= g_strdup (remote_path
);
199 /* replace first occurance of ":/" with ":" */
200 if ((p
= strchr (ret
, ':')) && *(p
+ 1) == '/')
201 strcpy (p
+ 1, p
+ 2);
203 /* strip trailing "/." */
204 if ((p
= strrchr (ret
, '/')) && *(p
+ 1) == '.' && *(p
+ 2) == '\0')
210 /* Extract the hostname and username from the path */
213 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
214 * ftp://sunsite.unc.edu/pub/linux
215 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
216 * ftp://tsx-11.mit.edu:8192/
217 * ftp://joe@foo.edu:11321/private
218 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
224 my_get_host_and_username (char *path
, char **host
, char **user
, int *port
, char **pass
)
226 return vfs_split_url (path
, host
, user
, port
, pass
, 21, URL_DEFAULTANON
);
229 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
231 get_reply (int sock
, char *string_buf
, int string_len
)
237 if (!get_line (sock
, answer
, sizeof (answer
), '\n')){
243 switch (sscanf(answer
, "%d", &code
)){
246 strncpy (string_buf
, answer
, string_len
- 1);
247 *(string_buf
+ string_len
- 1) = 0;
252 if (answer
[3] == '-') {
254 if (!get_line (sock
, answer
, sizeof(answer
), '\n')){
260 if ((sscanf (answer
, "%d", &i
) > 0) &&
261 (code
== i
) && (answer
[3] == ' '))
266 strncpy (string_buf
, answer
, string_len
- 1);
267 *(string_buf
+ string_len
- 1) = 0;
275 command (struct connection
*bucket
, int wait_reply
, char *fmt
, ...)
280 int sock
= qsock (bucket
);
284 fmt_str
= g_strdup_vprintf (fmt
, ap
);
287 str
= g_strconcat (fmt_str
, "\r\n", NULL
);
291 if (strncmp (str
, "PASS ", 5) == 0){
292 char *tmp
= "PASS <Password not logged>\r\n";
294 fwrite (tmp
, strlen (tmp
), 1, logfile
);
296 fwrite (str
, strlen (str
), 1, logfile
);
302 enable_interrupt_key ();
303 status
= write (sock
, str
, strlen (str
));
312 disable_interrupt_key ();
315 disable_interrupt_key ();
318 return get_reply (sock
, (wait_reply
& WANT_STRING
) ? reply_str
: NULL
, sizeof (reply_str
)-1);
323 connection_close (void *data
)
325 struct connection
*bucket
= data
;
327 if (qsock (bucket
) != -1){
328 print_vfs_message (_("ftpfs: Disconnecting from %s"), qhost (bucket
));
329 command(bucket
, NONE
, "QUIT");
330 close(qsock(bucket
));
334 /* some defines only used by changetype */
335 /* These two are valid values for the second parameter */
337 #define TYPE_BINARY 1
339 /* This one is only used to initialize bucket->isbinary, don't use it as
340 second parameter to changetype. */
341 #define TYPE_UNKNOWN -1
344 changetype (struct connection
*bucket
, int binary
)
346 if (binary
!= bucket
->isbinary
) {
347 if (command (bucket
, WAIT_REPLY
, "TYPE %c", binary
? 'I' : 'A') != COMPLETE
)
349 bucket
->isbinary
= binary
;
354 /* This routine logs the user in */
356 login_server (struct connection
*bucket
, char *netrcpass
)
358 #if defined(HSC_PROXY)
359 char *proxypass
, *proxyname
;
363 char *name
; /* login user name */
365 char reply_string
[255];
367 bucket
->isbinary
= TYPE_UNKNOWN
;
369 op
= g_strdup (netrcpass
);
371 if (!strcmp (quser (bucket
), "anonymous") ||
372 !strcmp (quser (bucket
), "ftp")) {
373 op
= g_strdup (ftpfs_anonymous_passwd
);
378 if (!bucket
->password
){
379 p
= g_strconcat (_(" FTP: Password required for "), quser (bucket
),
381 op
= vfs_get_password (p
);
385 bucket
->password
= g_strdup (op
);
387 op
= g_strdup (bucket
->password
);
391 if (!anon
|| logfile
)
392 pass
= g_strdup (op
);
394 pass
= g_strconcat ("-", op
, NULL
);
398 /* Proxy server accepts: username@host-we-want-to-connect*/
399 if (qproxy (bucket
)){
400 #if defined(HSC_PROXY)
403 p
= my_get_host_and_username (ftpfs_proxy_host
, &host
, &proxyname
,
410 wipe_password (proxypass
);
411 p
= g_strconcat (_(" Proxy: Password required for "), proxyname
, " ",
413 proxypass
= vfs_get_password (p
);
415 if (proxypass
== NULL
) {
416 wipe_password (pass
);
420 name
= g_strdup (quser (bucket
));
422 name
= g_strconcat (quser (bucket
), "@",
423 qhost (bucket
)[0] == '!' ? qhost (bucket
)+1 : qhost (bucket
), NULL
);
426 name
= g_strdup (quser (bucket
));
428 if (get_reply (qsock(bucket
), reply_string
, sizeof (reply_string
) - 1) == COMPLETE
) {
429 g_strup (reply_string
);
430 bucket
->remote_is_amiga
= strstr (reply_string
, "AMIGA") != 0;
432 fprintf (logfile
, "MC -- remote_is_amiga = %d\n", bucket
->remote_is_amiga
);
435 #if defined(HSC_PROXY)
436 if (qproxy (bucket
)){
437 print_vfs_message (_("ftpfs: sending proxy login name"));
438 if (command (bucket
, 1, "USER %s", proxyname
) != CONTINUE
)
441 print_vfs_message (_("ftpfs: sending proxy user password"));
442 if (command (bucket
, 1, "PASS %s", proxypass
) != COMPLETE
)
445 print_vfs_message (_("ftpfs: proxy authentication succeeded"));
446 if (command (bucket
, 1, "SITE %s", qhost (bucket
)+1) != COMPLETE
)
449 print_vfs_message (_("ftpfs: connected to %s"), qhost (bucket
)+1);
452 bucket
->failed_on_login
= 1;
455 wipe_password (proxypass
);
456 wipe_password (pass
);
462 wipe_password (proxypass
);
466 print_vfs_message (_("ftpfs: sending login name"));
467 code
= command (bucket
, WAIT_REPLY
, "USER %s", name
);
471 print_vfs_message (_("ftpfs: sending user password"));
472 if (command (bucket
, WAIT_REPLY
, "PASS %s", pass
) != COMPLETE
)
476 print_vfs_message (_("ftpfs: logged in"));
477 wipe_password (pass
);
482 bucket
->failed_on_login
= 1;
484 if (bucket
->password
)
485 wipe_password (bucket
->password
);
486 bucket
->password
= 0;
491 print_vfs_message (_("ftpfs: Login incorrect for user %s "), quser (bucket
));
493 wipe_password (pass
);
498 #ifdef HAVE_SETSOCKOPT
500 setup_source_route (int socket
, int dest
)
507 bzero (buffer
, sizeof (buffer
));
510 *ptr
++ = 4; /* pointer */
513 bcopy ((char *) &source_route
, ptr
, sizeof (int));
516 /* Second hop (ie, final destination) */
517 bcopy ((char *) &dest
, ptr
, 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 ()
537 /* FixMe: shouldn't be hardcoded!!! */
538 char s
[BUF_LARGE
]; /* provide for BUF_LARGE characters */
539 struct no_proxy_entry
*np
, *current
= 0;
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
);
580 ftpfs_check_proxy (char *host
)
582 struct no_proxy_entry
*npe
;
584 if (!ftpfs_proxy_host
|| !*ftpfs_proxy_host
|| !host
|| !*host
)
585 return 0; /* sanity check */
590 if (!ftpfs_always_use_proxy
)
593 if (!strchr (host
, '.'))
596 load_no_proxy_list ();
597 for (npe
= no_proxy
; npe
; npe
=npe
->next
) {
598 char *domain
= npe
->domain
;
600 if (domain
[0] == '.') {
601 int ld
= strlen (domain
);
602 int lh
= strlen (host
);
604 while (ld
&& lh
&& host
[lh
- 1] == domain
[ld
- 1]) {
612 if (!g_strcasecmp (host
, domain
))
620 ftpfs_get_proxy_host_and_port (char *proxy
, char **host
, int *port
)
622 char *user
, *pass
, *dir
;
624 #if defined(HSC_PROXY)
629 dir
= vfs_split_url (proxy
, host
, &user
, port
, &pass
, PORT
, URL_DEFAULTANON
);
633 wipe_password (pass
);
639 ftpfs_open_socket (struct connection
*bucket
)
641 struct sockaddr_in server_address
;
648 /* Use a proxy host? */
649 host
= qhost (bucket
);
651 if (!host
|| !*host
){
652 print_vfs_message (_("ftpfs: Invalid host name."));
657 /* Hosts to connect to that start with a ! should use proxy */
658 if (qproxy (bucket
)){
659 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host
, &host
, &port
);
663 /* Get host address */
664 bzero ((char *) &server_address
, sizeof (server_address
));
665 server_address
.sin_family
= AF_INET
;
666 server_address
.sin_addr
.s_addr
= inet_addr (host
);
667 if (server_address
.sin_addr
.s_addr
!= -1)
668 server_address
.sin_family
= AF_INET
;
670 hp
= gethostbyname (host
);
672 print_vfs_message (_("ftpfs: Invalid host address."));
678 server_address
.sin_family
= hp
->h_addrtype
;
680 /* We copy only 4 bytes, we can not trust hp->h_length, as it comes from the DNS */
681 bcopy ((char *) hp
->h_addr
, (char *) &server_address
.sin_addr
, 4);
684 #define THE_PORT qproxy(bucket) ? port : qport (bucket)
686 server_address
.sin_port
= htons (THE_PORT
);
689 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
695 setup_source_route (my_socket
, server_address
.sin_addr
.s_addr
);
697 print_vfs_message (_("ftpfs: making connection to %s"), host
);
701 enable_interrupt_key (); /* clear the interrupt flag */
703 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
704 sizeof (server_address
)) < 0){
706 if (errno
== EINTR
&& got_interrupt ())
707 print_vfs_message (_("ftpfs: connection interrupted by user"));
709 print_vfs_message (_("ftpfs: connection to server failed: %s"),
710 unix_error_string(errno
));
711 disable_interrupt_key();
715 disable_interrupt_key();
719 static struct connection
*
720 open_command_connection (char *host
, char *user
, int port
, char *netrcpass
)
722 struct connection
*bucket
;
723 int retry_seconds
, count_down
;
725 bucket
= g_new (struct connection
, 1);
728 ERRNOR (ENOMEM
, NULL
);
731 extern void *watch_free_pointer
;
733 if (!watch_free_pointer
)
734 watch_free_pointer
= host
;
737 qhost (bucket
) = g_strdup (host
);
738 quser (bucket
) = g_strdup (user
);
739 qcdir (bucket
) = NULL
;
740 qport (bucket
) = port
;
742 qhome (bucket
) = NULL
;
746 bucket
->__inode_counter
= 0;
748 bucket
->use_proxy
= ftpfs_check_proxy (host
);
749 bucket
->password
= 0;
750 bucket
->use_passive_connection
= ftpfs_use_passive_connections
| source_route
;
751 bucket
->use_source_route
= source_route
;
752 bucket
->strict_rfc959_list_cmd
= !ftpfs_use_unix_list_options
;
753 bucket
->isbinary
= TYPE_UNKNOWN
;
754 bucket
->remote_is_amiga
= 0;
756 /* We do not want to use the passive if we are using proxies */
757 if (bucket
->use_proxy
)
758 bucket
->use_passive_connection
= 0;
760 if ((qdcache (bucket
) = linklist_init ()) == NULL
) {
762 g_free (qhost (bucket
));
763 g_free (quser (bucket
));
770 bucket
->failed_on_login
= 0;
772 qsock (bucket
) = ftpfs_open_socket (bucket
);
773 if (qsock (bucket
) == -1) {
774 free_bucket (bucket
);
778 if (login_server (bucket
, netrcpass
)) {
779 /* Logged in, no need to retry the connection */
782 if (bucket
->failed_on_login
){
783 /* Close only the socket descriptor */
784 close (qsock (bucket
));
786 connection_destructor (bucket
);
789 if (ftpfs_retry_seconds
){
790 retry_seconds
= ftpfs_retry_seconds
;
791 enable_interrupt_key ();
792 for (count_down
= retry_seconds
; count_down
; count_down
--){
793 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down
);
795 if (got_interrupt ()){
797 disable_interrupt_key ();
798 free_bucket (bucket
);
802 disable_interrupt_key ();
805 } while (retry_seconds
);
807 qhome (bucket
) = ftpfs_get_current_directory (bucket
);
809 qhome (bucket
) = g_strdup (PATH_SEP_STR
);
810 qupdir (bucket
) = g_strdup (PATH_SEP_STR
); /* FIXME: I changed behavior to ignore last_current_dir */
815 is_connection_closed (struct connection
*bucket
)
826 FD_SET (qsock (bucket
), &rset
);
828 if (select (qsock (bucket
) + 1, &rset
, NULL
, NULL
, &t
) < 0)
833 if (FD_ISSET (qsock(bucket
), &rset
)) {
834 n
= read (qsock(bucket
), &read_ahead
, sizeof (read_ahead
));
842 /* This routine keeps track of open connections */
843 /* Returns a connected socket to host */
844 static struct connection
*
845 open_link (char *host
, char *user
, int port
, char *netrcpass
)
848 struct connection
*bucket
;
849 struct linklist
*lptr
;
851 for (lptr
= connections_list
->next
;
852 lptr
!= connections_list
; lptr
= lptr
->next
) {
854 if ((strcmp (host
, qhost (bucket
)) == 0) &&
855 (strcmp (user
, quser (bucket
)) == 0) &&
856 (port
== qport (bucket
))) {
858 /* check the connection is closed or not, just hack */
859 if (is_connection_closed (bucket
)) {
860 flush_all_directory (bucket
);
861 sock
= ftpfs_open_socket (bucket
);
863 close (qsock (bucket
));
864 qsock (bucket
) = sock
;
865 if (login_server (bucket
, netrcpass
))
869 /* connection refused */
870 lptr
->prev
->next
= lptr
->next
;
871 lptr
->next
->prev
= lptr
->prev
;
872 connection_destructor (bucket
);
878 bucket
= open_command_connection (host
, user
, port
, netrcpass
);
881 if (!linklist_insert (connections_list
, bucket
)) {
883 connection_destructor (bucket
);
889 /* The returned directory should always contain a trailing slash */
891 ftpfs_get_current_directory (struct connection
*bucket
)
893 char buf
[4096], *bufp
, *bufq
;
895 if (command (bucket
, NONE
, "PWD") == COMPLETE
&&
896 get_reply(qsock(bucket
), buf
, sizeof(buf
)) == COMPLETE
) {
898 for (bufq
= buf
; *bufq
; bufq
++)
905 if (*(bufq
- 1) != '/') {
910 return g_strdup (bufp
);
912 /* If the remote server is an Amiga a leading slash
913 might be missing. MC needs it because it is used
914 as seperator between hostname and path internally. */
915 return g_strconcat( "/", bufp
, 0);
929 /* Setup Passive ftp connection, we use it for source routed connections */
931 setup_passive (int my_socket
, struct connection
*bucket
, struct sockaddr_in
*sa
)
933 int xa
, xb
, xc
, xd
, xe
, xf
;
937 if (command (bucket
, WAIT_REPLY
| WANT_STRING
, "PASV") != COMPLETE
)
940 /* Parse remote parameters */
941 for (c
= reply_str
+ 4; (*c
) && (!isdigit (*c
)); c
++)
947 if (sscanf (c
, "%d,%d,%d,%d,%d,%d", &xa
, &xb
, &xc
, &xd
, &xe
, &xf
) != 6)
949 n
[0] = (unsigned char) xa
;
950 n
[1] = (unsigned char) xb
;
951 n
[2] = (unsigned char) xc
;
952 n
[3] = (unsigned char) xd
;
953 n
[4] = (unsigned char) xe
;
954 n
[5] = (unsigned char) xf
;
956 bcopy ((void *)n
, &(sa
->sin_addr
.s_addr
), 4);
957 bcopy ((void *)&n
[4], &(sa
->sin_port
), 2);
958 setup_source_route (my_socket
, sa
->sin_addr
.s_addr
);
959 if (connect (my_socket
, (struct sockaddr
*) sa
, sizeof (struct sockaddr_in
)) < 0)
965 initconn (struct connection
*bucket
)
967 struct sockaddr_in data_addr
;
968 int data
, len
= sizeof(data_addr
);
971 getsockname(qsock(bucket
), (struct sockaddr
*) &data_addr
, &len
);
972 data_addr
.sin_port
= 0;
974 pe
= getprotobyname("tcp");
977 data
= socket (AF_INET
, SOCK_STREAM
, pe
->p_proto
);
981 #ifdef ORIGINAL_CONNECT_CODE
982 if (bucket
->use_source_route
){
985 if ((c
= setup_passive (data
, bucket
, &data_addr
)))
987 print_vfs_message(_("ftpfs: could not setup passive mode for source routing"));
988 bucket
->use_source_route
= 0;
991 if (bucket
->use_passive_connection
){
992 if ((bucket
->use_passive_connection
= setup_passive (data
, bucket
, &data_addr
)))
995 bucket
->use_source_route
= 0;
996 bucket
->use_passive_connection
= 0;
997 print_vfs_message (_("ftpfs: could not setup passive mode"));
1000 /* If passive setup fails, fallback to active connections */
1001 /* Active FTP connection */
1002 if (bind (data
, (struct sockaddr
*)&data_addr
, len
) < 0)
1004 getsockname(data
, (struct sockaddr
*) &data_addr
, &len
);
1005 if (listen (data
, 1) < 0)
1008 unsigned char *a
= (unsigned char *)&data_addr
.sin_addr
;
1009 unsigned char *p
= (unsigned char *)&data_addr
.sin_port
;
1011 if (command (bucket
, WAIT_REPLY
, "PORT %d,%d,%d,%d,%d,%d", a
[0], a
[1],
1012 a
[2], a
[3], p
[0], p
[1]) != COMPLETE
)
1023 open_data_connection (struct connection
*bucket
, char *cmd
, char *remote
,
1024 int isbinary
, int reget
)
1026 struct sockaddr_in from
;
1027 int s
, j
, data
, fromlen
= sizeof(from
);
1029 if ((s
= initconn (bucket
)) == -1)
1031 if (changetype (bucket
, isbinary
) == -1)
1034 j
= command (bucket
, WAIT_REPLY
, "REST %d", reget
);
1039 char *path
= translate_path (bucket
, remote
);
1041 j
= command (bucket
, WAIT_REPLY
, "%s %s", cmd
, path
);
1044 j
= command (bucket
, WAIT_REPLY
, "%s", cmd
);
1047 enable_interrupt_key();
1048 if (bucket
->use_passive_connection
)
1051 data
= accept (s
, (struct sockaddr
*)&from
, &fromlen
);
1059 disable_interrupt_key();
1064 my_abort (struct connection
*bucket
, int dsock
)
1066 static unsigned char ipbuf
[3] = { IAC
, IP
, IAC
};
1070 print_vfs_message(_("ftpfs: aborting transfer."));
1071 if (send(qsock(bucket
), ipbuf
, sizeof(ipbuf
), MSG_OOB
) != sizeof(ipbuf
)) {
1072 print_vfs_message(_("ftpfs: abort error: %s"), unix_error_string(errno
));
1076 if (command(bucket
, NONE
, "%cABOR", DM
) != COMPLETE
){
1077 print_vfs_message (_("ftpfs: abort failed"));
1082 FD_SET(dsock
, &mask
);
1083 if (select(dsock
+ 1, &mask
, NULL
, NULL
, NULL
) > 0)
1084 while (read(dsock
, buf
, sizeof(buf
)) > 0);
1086 if ((get_reply(qsock(bucket
), NULL
, 0) == TRANSIENT
) && (code
== 426))
1087 get_reply(qsock(bucket
), NULL
, 0);
1091 resolve_symlink_without_ls_options(struct connection
*bucket
, struct dir
*dir
)
1093 struct linklist
*flist
;
1094 struct direntry
*fe
, *fel
;
1095 char tmp
[MC_MAXPATHLEN
];
1098 dir
->symlink_status
= FTPFS_RESOLVING_SYMLINKS
;
1099 for (flist
= dir
->file_list
->next
; flist
!= dir
->file_list
; flist
= flist
->next
) {
1100 /* flist->data->l_stat is alread initialized with 0 */
1102 if (S_ISLNK(fel
->s
.st_mode
) && fel
->linkname
) {
1103 if (fel
->linkname
[0] == '/') {
1104 if (strlen (fel
->linkname
) >= MC_MAXPATHLEN
)
1106 strcpy (tmp
, fel
->linkname
);
1108 if ((strlen (dir
->remote_path
) + strlen (fel
->linkname
)) >= MC_MAXPATHLEN
)
1110 strcpy (tmp
, dir
->remote_path
);
1113 strcat (tmp
+ 1, fel
->linkname
);
1115 for ( depth
= 0; depth
< 100; depth
++) { /* depth protects against recursive symbolic links */
1116 canonicalize_pathname (tmp
);
1117 fe
= _get_file_entry(bucket
, tmp
, 0, 0);
1119 if (S_ISLNK (fe
->s
.st_mode
) && fe
->l_stat
== 0) {
1120 /* Symlink points to link which isn't resolved, yet. */
1121 if (fe
->linkname
[0] == '/') {
1122 if (strlen (fe
->linkname
) >= MC_MAXPATHLEN
)
1124 strcpy (tmp
, fe
->linkname
);
1126 /* at this point tmp looks always like this
1127 /directory/filename, i.e. no need to check
1128 strrchr's return value */
1129 *(strrchr (tmp
, '/') + 1) = '\0'; /* dirname */
1130 if ((strlen (tmp
) + strlen (fe
->linkname
)) >= MC_MAXPATHLEN
)
1132 strcat (tmp
, fe
->linkname
);
1136 fel
->l_stat
= g_new (struct stat
, 1);
1137 if ( S_ISLNK (fe
->s
.st_mode
))
1138 *fel
->l_stat
= *fe
->l_stat
;
1140 *fel
->l_stat
= fe
->s
;
1141 (*fel
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1148 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1152 resolve_symlink_with_ls_options(struct connection
*bucket
, struct dir
*dir
)
1154 char buffer
[2048] = "", *filename
;
1158 struct linklist
*flist
;
1159 struct direntry
*fe
;
1160 int switch_method
= 0;
1162 dir
->symlink_status
= FTPFS_RESOLVED_SYMLINKS
;
1163 if (strchr (dir
->remote_path
, ' ')) {
1164 if (ftpfs_chdir_internal (bucket
, dir
->remote_path
) != COMPLETE
) {
1165 print_vfs_message(_("ftpfs: CWD failed."));
1168 sock
= open_data_connection (bucket
, "LIST -lLa", ".", TYPE_ASCII
, 0);
1171 sock
= open_data_connection (bucket
, "LIST -lLa",
1172 dir
->remote_path
, TYPE_ASCII
, 0);
1175 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1179 fp
= fdopen(sock
, "r");
1182 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1185 enable_interrupt_key();
1186 flist
= dir
->file_list
->next
;
1189 if (flist
== dir
->file_list
)
1192 flist
= flist
->next
;
1193 } while (!S_ISLNK(fe
->s
.st_mode
));
1195 if (fgets (buffer
, sizeof (buffer
), fp
) == NULL
)
1198 fputs (buffer
, logfile
);
1201 if (vfs_parse_ls_lga (buffer
, &s
, &filename
, NULL
)) {
1202 int r
= strcmp(fe
->name
, filename
);
1205 if (S_ISLNK (s
.st_mode
)) {
1206 /* This server doesn't understand LIST -lLa */
1210 fe
->l_stat
= g_new (struct stat
, 1);
1211 if (fe
->l_stat
== NULL
)
1214 (*fe
->l_stat
).st_ino
= bucket
->__inode_counter
++;
1223 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
);
1224 disable_interrupt_key();
1226 get_reply(qsock(bucket
), NULL
, 0);
1227 if (switch_method
) {
1228 bucket
->strict_rfc959_list_cmd
= 1;
1229 resolve_symlink_without_ls_options(bucket
, dir
);
1234 resolve_symlink(struct connection
*bucket
, struct dir
*dir
)
1236 print_vfs_message(_("Resolving symlink..."));
1238 if (bucket
->strict_rfc959_list_cmd
)
1239 resolve_symlink_without_ls_options(bucket
, dir
);
1241 resolve_symlink_with_ls_options(bucket
, dir
);
1246 #define X_myname "/#ftp:"
1247 #define vfs_X_ops vfs_ftpfs_ops
1248 #define X_fill_names ftpfs_fill_names
1249 #define X_hint_reread ftpfs_hint_reread
1250 #define X_flushdir ftpfs_flushdir
1251 #define X_done ftpfs_done
1252 #include "shared_ftp_fish.c"
1255 get_path (struct connection
**bucket
, char *path
)
1257 return s_get_path (bucket
, path
, "/#ftp:");
1260 /* Inserts an entry for "." (and "..") into the linked list. Ignore any
1261 errors because "." isn't important (as fas as you don't try to save a
1262 file in the root dir of the ftp server).
1263 Actually the dot is needed when stating the root directory, e.g.
1264 mc_stat ("/ftp#localhost", &buf). Down the call tree _get_file_entry
1265 gets called with filename = "/" which will be transformed into "."
1266 before searching for a fileentry. Whithout "." in the linked list
1267 this search fails. -- Norbert. */
1269 insert_dots (struct linklist
*file_list
, struct connection
*bucket
)
1271 struct direntry
*fe
;
1273 char buffer
[][58] = {
1274 "drwxrwxrwx 1 0 0 1024 Jan 1 1970 .",
1275 "drwxrwxrwx 1 0 0 1024 Jan 1 1970 .."
1278 for (i
= 0; i
< 2; i
++ ) {
1279 fe
= g_new (struct direntry
, 1);
1282 if (vfs_parse_ls_lga (buffer
[i
], &fe
->s
, &fe
->name
, &fe
->linkname
)) {
1283 fe
->freshly_created
= 0;
1285 fe
->local_filename
= fe
->remote_filename
= NULL
;
1287 fe
->bucket
= bucket
;
1288 (fe
->s
).st_ino
= bucket
->__inode_counter
++;
1290 if (!linklist_insert(file_list
, fe
))
1298 retrieve_dir(struct connection
*bucket
, char *remote_path
, int resolve_symlinks
)
1303 int sock
, has_symlinks
;
1304 struct linklist
*file_list
, *p
;
1305 struct direntry
*fe
;
1310 int has_spaces
= (strchr (remote_path
, ' ') != NULL
);
1312 canonicalize_pathname (remote_path
);
1313 for (p
= qdcache(bucket
)->next
;p
!= qdcache(bucket
);
1316 if (strcmp(dcache
->remote_path
, remote_path
) == 0) {
1319 gettimeofday(&tim
, NULL
);
1320 if (((tim
.tv_sec
< dcache
->timestamp
.tv_sec
) && !force_expiration
)
1321 || dcache
->symlink_status
== FTPFS_RESOLVING_SYMLINKS
) {
1322 if (resolve_symlinks
&& dcache
->symlink_status
== FTPFS_UNRESOLVED_SYMLINKS
)
1323 resolve_symlink(bucket
, dcache
);
1326 force_expiration
= 0;
1327 p
->next
->prev
= p
->prev
;
1328 p
->prev
->next
= p
->next
;
1329 dir_destructor(dcache
);
1337 if (bucket
->strict_rfc959_list_cmd
)
1338 print_vfs_message(_("ftpfs: Reading FTP directory %s... (don't use UNIX ls options)"), remote_path
);
1340 print_vfs_message(_("ftpfs: Reading FTP directory %s..."), remote_path
);
1341 if (has_spaces
|| bucket
->strict_rfc959_list_cmd
|| ftpfs_first_cd_then_ls
) {
1342 if (ftpfs_chdir_internal (bucket
, remote_path
) != COMPLETE
) {
1344 print_vfs_message(_("ftpfs: CWD failed."));
1349 file_list
= linklist_init();
1350 if (file_list
== NULL
)
1351 ERRNOR (ENOMEM
, NULL
);
1352 dcache
= g_new (struct dir
, 1);
1353 if (dcache
== NULL
) {
1355 linklist_destroy(file_list
, NULL
);
1356 print_vfs_message(_("ftpfs: FAIL"));
1359 gettimeofday(&dcache
->timestamp
, NULL
);
1360 dcache
->timestamp
.tv_sec
+= ftpfs_directory_timeout
;
1361 dcache
->file_list
= file_list
;
1362 dcache
->remote_path
= g_strdup(remote_path
);
1364 dcache
->symlink_status
= FTPFS_NO_SYMLINKS
;
1366 if (bucket
->strict_rfc959_list_cmd
== 1)
1367 sock
= open_data_connection (bucket
, "LIST", 0, TYPE_ASCII
, 0);
1368 else if (has_spaces
|| ftpfs_first_cd_then_ls
)
1369 sock
= open_data_connection (bucket
, "LIST -la", ".", TYPE_ASCII
, 0);
1371 /* Trailing "/." is necessary if remote_path is a symlink
1372 but don't generate "//." */
1373 char *path
= g_strconcat (remote_path
,
1374 remote_path
[1] == '\0' ? "" : PATH_SEP_STR
,
1377 sock
= open_data_connection (bucket
, "LIST -la", path
, TYPE_ASCII
, 0);
1385 #define close_this_sock(x,y) fclose (x)
1386 fp
= fdopen(sock
, "r");
1393 #define close_this_sock(x,y) close (y)
1396 /* Clear the interrupt flag */
1397 enable_interrupt_key ();
1401 while (fgets (buffer
, sizeof (buffer
), fp
) != NULL
) {
1402 if (got_intr
= got_interrupt ())
1405 while ((got_intr
= get_line_interruptible (buffer
, sizeof (buffer
), sock
)) != EINTR
){
1407 int eof
= got_intr
== 0;
1410 fputs (buffer
, logfile
);
1411 fputs ("\n", logfile
);
1414 if (buffer
[0] == 0 && eof
)
1416 fe
= g_new (struct direntry
, 1);
1417 fe
->freshly_created
= 0;
1418 fe
->local_filename
= NULL
;
1423 if (vfs_parse_ls_lga (buffer
, &fe
->s
, &fe
->name
, &fe
->linkname
)) {
1424 if (strcmp (fe
->name
, ".") == 0)
1427 fe
->local_filename
= fe
->remote_filename
= NULL
;
1429 fe
->bucket
= bucket
;
1430 (fe
->s
).st_ino
= bucket
->__inode_counter
++;
1431 if (S_ISLNK(fe
->s
.st_mode
))
1434 if (!linklist_insert(file_list
, fe
)) {
1446 disable_interrupt_key();
1447 print_vfs_message(_("ftpfs: reading FTP directory interrupt by user"));
1449 my_abort(bucket
, fileno(fp
));
1451 my_abort(bucket
, sock
);
1453 close_this_sock(fp
, sock
);
1457 close_this_sock(fp
, sock
);
1458 disable_interrupt_key();
1459 if ((get_reply (qsock (bucket
), NULL
, 0) != COMPLETE
) ||
1460 (file_list
->next
== file_list
))
1465 insert_dots (file_list
, bucket
);
1467 if (!linklist_insert(qdcache(bucket
), dcache
)) {
1472 if (resolve_symlinks
)
1473 resolve_symlink(bucket
, dcache
);
1475 dcache
->symlink_status
= FTPFS_UNRESOLVED_SYMLINKS
;
1477 print_vfs_message(_("ftpfs: got listing"));
1480 disable_interrupt_key();
1481 close_this_sock(fp
, sock
);
1485 get_reply(qsock(bucket
), NULL
, 0);
1487 g_free(dcache
->remote_path
);
1489 linklist_destroy(file_list
, direntry_destructor
);
1490 print_vfs_message(_("ftpfs: failed"));
1494 if (bucket
->__inode_counter
== 0 && (!bucket
->strict_rfc959_list_cmd
)) {
1495 /* It's our first attempt to get a directory listing from this
1496 server (UNIX style LIST command) */
1497 bucket
->strict_rfc959_list_cmd
= 1;
1498 g_free(dcache
->remote_path
);
1500 linklist_destroy(file_list
, direntry_destructor
);
1501 return retrieve_dir (bucket
, remote_path
, resolve_symlinks
);
1504 /* Ok, maybe the directory exists but the remote server doesn't
1506 Check whether the directory exists:
1508 if (has_spaces
|| bucket
->strict_rfc959_list_cmd
||
1509 ftpfs_first_cd_then_ls
) /* CWD has been already performed, i.e.
1510 we know that remote_path exists */
1513 if (bucket
->remote_is_amiga
) {
1514 /* The Amiga ftp server needs extra processing because it
1515 always gets relative pathes instead of absolute pathes
1517 char *p
= ftpfs_get_current_directory (bucket
);
1519 if (ftpfs_chdir_internal (bucket
, remote_path
) == COMPLETE
) {
1520 ftpfs_chdir_internal (bucket
, p
);
1525 if (ftpfs_chdir_internal (bucket
, remote_path
) == COMPLETE
)
1531 g_free(dcache
->remote_path
);
1533 linklist_destroy(file_list
, direntry_destructor
);
1534 print_vfs_message(_("ftpfs: failed; nowhere to fallback to"));
1539 store_file(struct direntry
*fe
)
1541 int local_handle
, sock
, n
, total
;
1542 #ifdef HAVE_STRUCT_LINGER
1550 local_handle
= open(fe
->local_filename
, O_RDONLY
);
1551 unlink (fe
->local_filename
);
1552 if (local_handle
== -1)
1554 fstat(local_handle
, &s
);
1555 sock
= open_data_connection(fe
->bucket
, "STOR", fe
->remote_filename
, TYPE_BINARY
, 0);
1557 close(local_handle
);
1560 #ifdef HAVE_STRUCT_LINGER
1563 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, (char *) &li
, sizeof(li
));
1565 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, &flag_one
, sizeof (flag_one
));
1569 enable_interrupt_key();
1571 while ((n
= read(local_handle
, buffer
, sizeof(buffer
))) < 0) {
1572 if (errno
== EINTR
) {
1573 if (got_interrupt()) {
1585 while (write(sock
, buffer
, n
) < 0) {
1586 if (errno
== EINTR
) {
1587 if (got_interrupt()) {
1598 print_vfs_message(_("ftpfs: storing file %d (%d)"),
1601 disable_interrupt_key();
1603 close(local_handle
);
1604 if (get_reply (qsock (fe
->bucket
), NULL
, 0) != COMPLETE
)
1608 disable_interrupt_key();
1610 close(local_handle
);
1611 get_reply(qsock(fe
->bucket
), NULL
, 0);
1616 linear_start(struct direntry
*fe
, int offset
)
1618 fe
->local_stat
.st_mtime
= 0;
1619 fe
->data_sock
= open_data_connection(fe
->bucket
, "RETR", fe
->remote_filename
, TYPE_BINARY
, offset
);
1620 if (fe
->data_sock
== -1)
1622 fe
->linear_state
= LS_LINEAR_OPEN
;
1627 linear_abort (struct direntry
*fe
)
1629 my_abort(fe
->bucket
, fe
->data_sock
);
1634 linear_read (struct direntry
*fe
, void *buf
, int len
)
1637 while ((n
= read (fe
->data_sock
, buf
, len
))<0) {
1638 if ((errno
== EINTR
) && !got_interrupt())
1647 if ((get_reply (qsock (fe
->bucket
), NULL
, 0) != COMPLETE
)) {
1651 close (fe
->data_sock
);
1658 linear_close (struct direntry
*fe
)
1660 if (fe
->data_sock
!= -1)
1664 int ftpfs_ctl (void *data
, int ctlop
, int arg
)
1666 struct filp
*fp
= data
;
1668 case MCCTL_IS_NOTREADY
:
1672 if (!fp
->fe
->linear_state
)
1673 vfs_die ("You may not do this");
1674 if (fp
->fe
->linear_state
== LS_LINEAR_CLOSED
)
1677 v
= select_on_two (fp
->fe
->data_sock
, 0);
1678 if (((v
< 0) && (errno
== EINTR
)) || v
== 0)
1688 send_ftp_command(char *filename
, char *cmd
, int flags
)
1690 char *remote_path
, *p
;
1691 struct connection
*bucket
;
1693 int flush_directory_cache
= (flags
& OPT_FLUSH
) && (normal_flush
> 0);
1695 if (!(remote_path
= get_path(&bucket
, filename
)))
1697 p
= translate_path (bucket
, remote_path
);
1698 r
= command (bucket
, WAIT_REPLY
, cmd
, p
);
1700 g_free(remote_path
);
1701 vfs_add_noncurrent_stamps (&vfs_ftpfs_ops
, (vfsid
) bucket
, NULL
);
1702 if (flags
& OPT_IGNORE_ERROR
)
1706 if (flush_directory_cache
)
1707 flush_all_directory(bucket
);
1711 /* This routine is called as the last step in load_setup */
1713 ftpfs_init_passwd(void)
1715 struct passwd
*passwd_info
;
1716 char *p
, hostname
[MAXHOSTNAMELEN
];
1719 ftpfs_anonymous_passwd
= load_anon_passwd ();
1720 if (ftpfs_anonymous_passwd
)
1723 if ((passwd_info
= getpwuid (geteuid ())) == NULL
)
1726 p
= passwd_info
->pw_name
;
1727 gethostname(hostname
, sizeof(hostname
));
1728 hp
= gethostbyname(hostname
);
1730 ftpfs_anonymous_passwd
= g_strconcat (p
, "@", hp
->h_name
, NULL
);
1732 ftpfs_anonymous_passwd
= g_strconcat (p
, "@", hostname
, NULL
);
1737 ftpfs_init (vfs
*me
)
1739 connections_list
= linklist_init();
1740 ftpfs_directory_timeout
= FTPFS_DIRECTORY_TIMEOUT
;
1744 static int ftpfs_chmod (vfs
*me
, char *path
, int mode
)
1746 char buf
[BUF_SMALL
];
1748 g_snprintf(buf
, sizeof(buf
), "SITE CHMOD %4.4o %%s", mode
& 07777);
1749 return send_ftp_command(path
, buf
, OPT_IGNORE_ERROR
| OPT_FLUSH
);
1752 static int ftpfs_chown (vfs
*me
, char *path
, int owner
, int group
)
1758 /* Everyone knows it is not possible to chown remotely, so why bother them.
1759 If someone's root, then copy/move will always try to chown it... */
1764 static int ftpfs_unlink (vfs
*me
, char *path
)
1766 return send_ftp_command(path
, "DELE %s", OPT_FLUSH
);
1769 /* Return true if path is the same directoy as the one we are on now */
1771 is_same_dir (char *path
, struct connection
*bucket
)
1773 if (!qcdir (bucket
))
1775 if (strcmp (path
, qcdir (bucket
)) == 0)
1781 ftpfs_chdir_internal (struct connection
*bucket
,char *remote_path
)
1786 if (!bucket
->cwd_defered
&& is_same_dir (remote_path
, bucket
))
1789 p
= translate_path (bucket
, remote_path
);
1790 r
= command (bucket
, WAIT_REPLY
, "CWD %s", p
);
1793 if (r
!= COMPLETE
) {
1797 g_free(qcdir(bucket
));
1798 qcdir(bucket
) = g_strdup (remote_path
);
1799 bucket
->cwd_defered
= 0;
1804 static int ftpfs_rename (vfs
*me
, char *path1
, char *path2
)
1806 send_ftp_command(path1
, "RNFR %s", OPT_FLUSH
);
1807 return send_ftp_command(path2
, "RNTO %s", OPT_FLUSH
);
1810 static int ftpfs_mkdir (vfs
*me
, char *path
, mode_t mode
)
1812 return send_ftp_command(path
, "MKD %s", OPT_FLUSH
);
1815 static int ftpfs_rmdir (vfs
*me
, char *path
)
1817 return send_ftp_command(path
, "RMD %s", OPT_FLUSH
);
1820 void ftpfs_set_debug (const char *file
)
1822 logfile
= fopen (file
, "w+");
1825 static void my_forget (char *file
)
1828 char *host
, *user
, *pass
, *rp
;
1831 #ifndef BROKEN_PATHS
1832 if (strncmp (file
, "/#ftp:", 6))
1833 return; /* Normal: consider cd /bla/#ftp */
1835 if (!(file
= strstr (file
, "/#ftp:")))
1840 if (!(rp
= my_get_host_and_username (file
, &host
, &user
, &port
, &pass
))) {
1844 wipe_password (pass
);
1848 /* we do not care about the path actually */
1851 for (l
= connections_list
->next
; l
!= connections_list
; l
= l
->next
){
1852 struct connection
*bucket
= l
->data
;
1854 if ((strcmp (host
, qhost (bucket
)) == 0) &&
1855 (strcmp (user
, quser (bucket
)) == 0) &&
1856 (port
== qport (bucket
))){
1858 /* close socket: the child owns it now */
1859 close (bucket
->sock
);
1862 /* reopen the connection */
1863 bucket
->sock
= ftpfs_open_socket (bucket
);
1864 if (bucket
->sock
!= -1)
1865 login_server (bucket
, pass
);
1872 wipe_password (pass
);
1875 vfs vfs_ftpfs_ops
= {
1876 NULL
, /* This is place of next pointer */
1877 "File Tranfer Protocol (ftp)",
1879 "ftp:", /* prefix */
1903 ftpfs_chown
, /* not really implemented but returns success */
1933 static char buffer
[BUF_MEDIUM
];
1934 static char *netrc
, *netrcp
;
1936 static int netrc_next (void)
1940 static char *keywords
[] = { "default", "machine",
1941 "login", "password", "passwd",
1942 "account", "macdef" };
1945 netrcp
= skip_separators (netrcp
);
1946 if (*netrcp
!= '\n')
1953 if (*netrcp
== '"') {
1954 for (;*netrcp
!= '"' && *netrcp
; netrcp
++) {
1955 if (*netrcp
== '\\')
1960 for (;*netrcp
!= '\n' && *netrcp
!= '\t' && *netrcp
!= ' ' &&
1961 *netrcp
!= ',' && *netrcp
; netrcp
++) {
1962 if (*netrcp
== '\\')
1970 for (i
= 0; i
< sizeof (keywords
) / sizeof (keywords
[0]); i
++)
1971 if (!strcmp (keywords
[i
], buffer
))
1976 int lookup_netrc (char *host
, char **login
, char **pass
)
1978 char *netrcname
, *tmp
;
1979 char hostname
[MAXHOSTNAMELEN
], *domain
;
1982 static int be_angry
= 1;
1983 static struct rupcache
{
1984 struct rupcache
*next
;
1988 } *rup_cache
= NULL
, *rupp
;
1990 for (rupp
= rup_cache
; rupp
!= NULL
; rupp
= rupp
->next
)
1991 if (!strcmp (host
, rupp
->host
)) {
1992 if (rupp
->login
!= NULL
)
1993 *login
= g_strdup (rupp
->login
);
1994 if (rupp
->pass
!= NULL
)
1995 *pass
= g_strdup (rupp
->pass
);
1998 netrcname
= concat_dir_and_file (home_dir
, ".netrc");
1999 netrcp
= netrc
= load_file (netrcname
);
2000 if (netrc
== NULL
) {
2004 if (gethostname (hostname
, sizeof (hostname
)) < 0)
2006 if (!(domain
= strchr (hostname
, '.')))
2009 while ((keyword
= netrc_next ())) {
2011 if (netrc_next () != 8)
2013 if (g_strcasecmp (host
, buffer
) &&
2014 ((tmp
= strchr (host
, '.')) == NULL
||
2015 g_strcasecmp (tmp
, domain
) ||
2016 g_strncasecmp (host
, buffer
, tmp
- host
) ||
2017 buffer
[tmp
- host
]))
2019 } else if (keyword
!= 1)
2021 while ((keyword
= netrc_next ()) > 2) {
2026 *login
= g_strdup (buffer
);
2027 else if (strcmp (*login
, buffer
))
2032 if (strcmp (*login
, "anonymous") && strcmp (*login
, "ftp") &&
2033 stat (netrcname
, &mystat
) >= 0 &&
2034 (mystat
.st_mode
& 077)) {
2036 message_1s (1, MSG_ERROR
, _("~/.netrc file has not correct mode.\n"
2037 "Remove password or correct mode."));
2044 if (netrc_next () && *pass
== NULL
)
2045 *pass
= g_strdup (buffer
);
2048 if (stat (netrcname
, &mystat
) >= 0 &&
2049 (mystat
.st_mode
& 077)) {
2051 message_1s (1, MSG_ERROR
, _("~/.netrc file has not correct mode.\n"
2052 "Remove password or correct mode."));
2063 while (*netrcp
!= '\n' && *netrcp
);
2064 if (*netrcp
!= '\n')
2067 if (*netrcp
== '\n' || !*netrcp
)
2080 rupp
= g_new (struct rupcache
, 1);
2081 rupp
->host
= g_strdup (host
);
2082 rupp
->login
= rupp
->pass
= 0;
2085 rupp
->login
= g_strdup (*login
);
2087 rupp
->pass
= g_strdup (*pass
);
2088 rupp
->next
= rup_cache
;
2096 #endif /* USE_NETRC */