Remove vfs_url_t structure (replace with vfs_path_element_t)
[midnight-commander.git] / src / vfs / ftpfs / ftpfs.c
blob77daf5aab8a1b8a97f0960b48bbcf65ba1e0840d
1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
5 Written by:
6 1995 Ching Hui
7 1995 Jakub Jelinek
8 1995, 1996, 1997 Miguel de Icaza
9 1997 Norbert Warmuth
10 1998 Pavel Machek
11 2010 Yury V. Zaytsev
12 2010 Slava Zanko
13 2010 Andrew Borodin
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU Library General Public License
17 as published by the Free Software Foundation; either version 2 of
18 the License, or (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU Library General Public License for more details.
25 You should have received a copy of the GNU Library General Public
26 License along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
29 /**
30 * \file
31 * \brief Source: Virtual File System: FTP file system
32 * \author Ching Hui
33 * \author Jakub Jelinek
34 * \author Miguel de Icaza
35 * \author Norbert Warmuth
36 * \author Pavel Machek
37 * \date 1995, 1997, 1998
39 * \todo
40 - make it more robust - all the connects etc. should handle EADDRINUSE and
41 ERETRY (have I spelled these names correctly?)
42 - make the user able to flush a connection - all the caches will get empty
43 etc., (tarfs as well), we should give there a user selectable timeout
44 and assign a key sequence.
45 - use hash table instead of linklist to cache ftpfs directory.
47 What to do with this?
50 * NOTE: Usage of tildes is deprecated, consider:
51 * \verbatim
52 cd /#ftp:pavel@hobit
53 cd ~
54 \endverbatim
55 * And now: what do I want to do? Do I want to go to /home/pavel or to
56 * /#ftp:hobit/home/pavel? I think first has better sense...
58 \verbatim
60 int f = !strcmp( remote_path, "/~" );
61 if (f || !strncmp( remote_path, "/~/", 3 )) {
62 char *s;
63 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
64 g_free (remote_path);
65 remote_path = s;
68 \endverbatim
71 /* \todo Fix: Namespace pollution: horrible */
73 #include <config.h>
74 #include <stdio.h> /* sscanf() */
75 #include <stdlib.h> /* atoi() */
76 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
77 #include <netdb.h> /* struct hostent */
78 #include <sys/socket.h> /* AF_INET */
79 #include <netinet/in.h> /* struct in_addr */
80 #ifdef HAVE_ARPA_INET_H
81 #include <arpa/inet.h>
82 #endif
83 #include <arpa/ftp.h>
84 #include <arpa/telnet.h>
85 #include <sys/param.h>
86 #include <errno.h>
87 #include <ctype.h>
88 #include <fcntl.h>
89 #include <sys/time.h> /* gettimeofday() */
90 #include <inttypes.h> /* uintmax_t */
92 #include "lib/global.h"
93 #include "lib/util.h"
94 #include "lib/mcconfig.h"
96 #include "lib/tty/tty.h" /* enable/disable interrupt key */
97 #include "lib/widget.h" /* message() */
99 #include "src/history.h"
100 #include "src/setup.h" /* for load_anon_passwd */
102 #include "lib/vfs/vfs.h"
103 #include "lib/vfs/utilvfs.h"
104 #include "lib/vfs/netutil.h"
105 #include "lib/vfs/xdirentry.h"
106 #include "lib/vfs/gc.h" /* vfs_stamp_create */
108 #include "ftpfs.h"
110 /*** global variables ****************************************************************************/
112 /* Delay to retry a connection */
113 int ftpfs_retry_seconds = 30;
115 /* Method to use to connect to ftp sites */
116 int ftpfs_use_passive_connections = 1;
117 int ftpfs_use_passive_connections_over_proxy = 0;
119 /* Method used to get directory listings:
120 * 1: try 'LIST -la <path>', if it fails
121 * fall back to CWD <path>; LIST
122 * 0: always use CWD <path>; LIST
124 int ftpfs_use_unix_list_options = 1;
126 /* First "CWD <path>", then "LIST -la ." */
127 int ftpfs_first_cd_then_ls = 1;
129 /* Use the ~/.netrc */
130 int ftpfs_use_netrc = 1;
132 /* Anonymous setup */
133 char *ftpfs_anonymous_passwd = NULL;
134 int ftpfs_directory_timeout = 900;
136 /* Proxy host */
137 char *ftpfs_proxy_host = NULL;
139 /* wether we have to use proxy by default? */
140 int ftpfs_always_use_proxy = 0;
142 int ftpfs_ignore_chattr_errors = 1;
144 /*** file scope macro definitions ****************************************************************/
146 #ifndef MAXHOSTNAMELEN
147 #define MAXHOSTNAMELEN 64
148 #endif
150 #define UPLOAD_ZERO_LENGTH_FILE
151 #define SUP ((ftp_super_data_t *) super->data)
152 #define FH_SOCK ((ftp_fh_data_t *) fh->data)->sock
154 #ifndef INADDR_NONE
155 #define INADDR_NONE 0xffffffff
156 #endif
158 #define RFC_AUTODETECT 0
159 #define RFC_DARING 1
160 #define RFC_STRICT 2
162 /* ftpfs_command wait_flag: */
163 #define NONE 0x00
164 #define WAIT_REPLY 0x01
165 #define WANT_STRING 0x02
167 #define FTP_COMMAND_PORT 21
169 /* some defines only used by ftpfs_changetype */
170 /* These two are valid values for the second parameter */
171 #define TYPE_ASCII 0
172 #define TYPE_BINARY 1
174 /* This one is only used to initialize bucket->isbinary, don't use it as
175 second parameter to ftpfs_changetype. */
176 #define TYPE_UNKNOWN -1
178 #define ABORT_TIMEOUT 5
179 /*** file scope type declarations ****************************************************************/
181 #ifndef HAVE_SOCKLEN_T
182 typedef int socklen_t;
183 #endif
185 /* This should match the keywords[] array below */
186 typedef enum
188 NETRC_NONE = 0,
189 NETRC_DEFAULT,
190 NETRC_MACHINE,
191 NETRC_LOGIN,
192 NETRC_PASSWORD,
193 NETRC_PASSWD,
194 NETRC_ACCOUNT,
195 NETRC_MACDEF,
196 NETRC_UNKNOWN
197 } keyword_t;
199 typedef struct
201 int sock;
203 char *proxy; /* proxy server, NULL if no proxy */
204 int failed_on_login; /* used to pass the failure reason to upper levels */
205 int use_passive_connection;
206 int remote_is_amiga; /* No leading slash allowed for AmiTCP (Amiga) */
207 int isbinary;
208 int cwd_deferred; /* current_directory was changed but CWD command hasn't
209 been sent yet */
210 int strict; /* ftp server doesn't understand
211 * "LIST -la <path>"; use "CWD <path>"/
212 * "LIST" instead
214 int ctl_connection_busy;
215 } ftp_super_data_t;
217 typedef struct
219 int sock;
220 int append;
221 } ftp_fh_data_t;
223 /*** file scope variables ************************************************************************/
225 static int ftpfs_errno;
226 static int code;
228 #ifdef FIXME_LATER_ALIGATOR
229 static struct linklist *connections_list;
230 #endif
232 static char reply_str[80];
234 static struct vfs_class vfs_ftpfs_ops;
236 static GSList *no_proxy;
238 static char buffer[BUF_MEDIUM];
239 static char *netrc;
240 static const char *netrcp;
242 /*** file scope functions ************************************************************************/
243 /* --------------------------------------------------------------------------------------------- */
245 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
246 Translate a Unix path, i.e. MC's internal path representation (e.g.
247 /somedir/somefile) to a path valid for the remote server. Every path
248 transfered to the remote server has to be mangled by this function
249 right prior to sending it.
250 Currently only Amiga ftp servers are handled in a special manner.
252 When the remote server is an amiga:
253 a) strip leading slash if necesarry
254 b) replace first occurance of ":/" with ":"
255 c) strip trailing "/."
258 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
259 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super,
260 const char *remote_path);
261 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply,
262 const char *fmt, ...) __attribute__ ((format (__printf__, 4, 5)));
263 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
264 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
265 const char *netrcpass);
266 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
268 /* --------------------------------------------------------------------------------------------- */
270 static char *
271 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
273 if (!SUP->remote_is_amiga)
274 return g_strdup (remote_path);
275 else
277 char *ret, *p;
279 if (MEDATA->logfile)
281 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
282 fflush (MEDATA->logfile);
285 /* strip leading slash(es) */
286 while (*remote_path == '/')
287 remote_path++;
290 * Don't change "/" into "", e.g. "CWD " would be
291 * invalid.
293 if (*remote_path == '\0')
294 return g_strdup (".");
296 ret = g_strdup (remote_path);
298 /* replace first occurance of ":/" with ":" */
299 p = strchr (ret, ':');
300 if ((p != NULL) && (*(p + 1) == '/'))
301 memmove (p + 1, p + 2, strlen (p + 2) + 1);
303 /* strip trailing "/." */
304 p = strrchr (ret, '/');
305 if ((p != NULL) && (*(p + 1) == '.') && (*(p + 2) == '\0'))
306 *p = '\0';
308 return ret;
312 /* --------------------------------------------------------------------------------------------- */
313 /** Extract the hostname and username from the path */
315 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
316 * ftp://sunsite.unc.edu/pub/linux
317 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
318 * ftp://tsx-11.mit.edu:8192/
319 * ftp://joe@foo.edu:11321/private
320 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
321 * is supplied.
324 static vfs_path_element_t *
325 ftpfs_split_url (const char *path)
327 vfs_path_element_t *path_element;
329 path_element = vfs_url_split (path, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
331 if (path_element->user != NULL)
333 /* Look up user and password in netrc */
334 if (ftpfs_use_netrc)
335 ftpfs_netrc_lookup (path_element->host, &path_element->user, &path_element->password);
337 if (path_element->user == NULL)
338 path_element->user = g_strdup ("anonymous");
340 /* Look up password in netrc for known user */
341 if (ftpfs_use_netrc && path_element->user != NULL && path_element->password != NULL)
343 char *new_user = NULL;
345 ftpfs_netrc_lookup (path_element->host, &new_user, &path_element->password);
347 /* If user is different, remove password */
348 if (new_user != NULL && strcmp (path_element->user, new_user) != 0)
350 g_free (path_element->password);
351 path_element->password = NULL;
354 g_free (new_user);
357 return path_element;
360 /* --------------------------------------------------------------------------------------------- */
361 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
363 static int
364 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
366 char answer[BUF_1K];
367 int i;
369 for (;;)
371 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
373 if (string_buf)
374 *string_buf = 0;
375 code = 421;
376 return 4;
378 switch (sscanf (answer, "%d", &code))
380 case 0:
381 if (string_buf)
382 g_strlcpy (string_buf, answer, string_len);
383 code = 500;
384 return 5;
385 case 1:
386 if (answer[3] == '-')
388 while (1)
390 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
392 if (string_buf)
393 *string_buf = 0;
394 code = 421;
395 return 4;
397 if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' '))
398 break;
401 if (string_buf)
402 g_strlcpy (string_buf, answer, string_len);
403 return code / 100;
408 /* --------------------------------------------------------------------------------------------- */
410 static int
411 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
413 int sock;
415 sock = ftpfs_open_socket (me, super);
416 if (sock != -1)
418 char *cwdir = super->path_element->path;
420 close (SUP->sock);
421 SUP->sock = sock;
422 super->path_element->path = NULL;
425 if (ftpfs_login_server (me, super, super->path_element->password) != 0)
427 if (cwdir == NULL)
428 return 1;
429 sock = ftpfs_chdir_internal (me, super, cwdir);
430 g_free (cwdir);
431 return sock == COMPLETE ? 1 : 0;
434 super->path_element->path = cwdir;
437 return 0;
440 /* --------------------------------------------------------------------------------------------- */
442 static int
443 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt,
444 ...)
446 va_list ap;
447 char *cmdstr;
448 int status, cmdlen;
449 static int retry = 0;
450 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
452 va_start (ap, fmt);
453 cmdstr = g_strdup_vprintf (fmt, ap);
454 va_end (ap);
456 cmdlen = strlen (cmdstr);
457 cmdstr = g_realloc (cmdstr, cmdlen + 3);
458 strcpy (cmdstr + cmdlen, "\r\n");
459 cmdlen += 2;
461 if (MEDATA->logfile)
463 if (strncmp (cmdstr, "PASS ", 5) == 0)
465 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
467 else
469 size_t ret;
470 ret = fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
473 fflush (MEDATA->logfile);
476 got_sigpipe = 0;
477 tty_enable_interrupt_key ();
478 status = write (SUP->sock, cmdstr, cmdlen);
480 if (status < 0)
482 code = 421;
484 if (errno == EPIPE)
485 { /* Remote server has closed connection */
486 if (level == 0)
488 level = 1;
489 status = ftpfs_reconnect (me, super);
490 level = 0;
491 if (status && (write (SUP->sock, cmdstr, cmdlen) > 0))
493 goto ok;
497 got_sigpipe = 1;
499 g_free (cmdstr);
500 tty_disable_interrupt_key ();
501 return TRANSIENT;
503 retry = 0;
505 tty_disable_interrupt_key ();
507 if (wait_reply)
509 status = ftpfs_get_reply (me, SUP->sock,
510 (wait_reply & WANT_STRING) ? reply_str : NULL,
511 sizeof (reply_str) - 1);
512 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
514 retry = 1;
515 level = 1;
516 status = ftpfs_reconnect (me, super);
517 level = 0;
518 if (status && (write (SUP->sock, cmdstr, cmdlen) > 0))
520 goto ok;
523 retry = 0;
524 g_free (cmdstr);
525 return status;
527 g_free (cmdstr);
528 return COMPLETE;
531 /* --------------------------------------------------------------------------------------------- */
533 static void
534 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
536 if (SUP->sock != -1)
538 vfs_print_message (_("ftpfs: Disconnecting from %s"), super->path_element->host);
539 ftpfs_command (me, super, NONE, "QUIT");
540 close (SUP->sock);
542 g_free (super->data);
543 super->data = NULL;
546 /* --------------------------------------------------------------------------------------------- */
548 static int
549 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
551 if (binary != SUP->isbinary)
553 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
554 ERRNOR (EIO, -1);
555 SUP->isbinary = binary;
557 return binary;
560 /* --------------------------------------------------------------------------------------------- */
561 /* This routine logs the user in */
563 static int
564 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
566 char *pass;
567 char *op;
568 char *name; /* login user name */
569 int anon = 0;
570 char reply_string[BUF_MEDIUM];
572 SUP->isbinary = TYPE_UNKNOWN;
574 if (super->path_element->password != NULL) /* explicit password */
575 op = g_strdup (super->path_element->password);
576 else if (netrcpass != NULL) /* password from netrc */
577 op = g_strdup (netrcpass);
578 else if (strcmp (super->path_element->user, "anonymous") == 0
579 || strcmp (super->path_element->user, "ftp") == 0)
581 if (ftpfs_anonymous_passwd == NULL) /* default anonymous password */
582 ftpfs_init_passwd ();
583 op = g_strdup (ftpfs_anonymous_passwd);
584 anon = 1;
586 else
587 { /* ask user */
588 char *p;
590 p = g_strdup_printf (_("FTP: Password required for %s"), super->path_element->user);
591 op = vfs_get_password (p);
592 g_free (p);
593 if (op == NULL)
594 ERRNOR (EPERM, 0);
595 super->path_element->password = g_strdup (op);
598 if (!anon || MEDATA->logfile)
599 pass = op;
600 else
602 pass = g_strconcat ("-", op, (char *) NULL);
603 wipe_password (op);
606 /* Proxy server accepts: username@host-we-want-to-connect */
607 if (SUP->proxy)
608 name =
609 g_strconcat (super->path_element->user, "@",
610 super->path_element->host[0] ==
611 '!' ? super->path_element->host + 1 : super->path_element->host,
612 (char *) NULL);
613 else
614 name = g_strdup (super->path_element->user);
616 if (ftpfs_get_reply (me, SUP->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
618 char *reply_up;
620 reply_up = g_ascii_strup (reply_string, -1);
621 SUP->remote_is_amiga = strstr (reply_up, "AMIGA") != 0;
622 g_free (reply_up);
624 if (MEDATA->logfile)
626 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n", SUP->remote_is_amiga);
627 fflush (MEDATA->logfile);
630 vfs_print_message (_("ftpfs: sending login name"));
632 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
634 case CONTINUE:
635 vfs_print_message (_("ftpfs: sending user password"));
636 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
637 if (code == CONTINUE)
639 char *p;
641 p = g_strdup_printf (_("FTP: Account required for user %s"),
642 super->path_element->user);
643 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
644 g_free (p);
645 if (op == NULL)
646 ERRNOR (EPERM, 0);
647 vfs_print_message (_("ftpfs: sending user account"));
648 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
649 g_free (op);
651 if (code != COMPLETE)
652 break;
653 /* fall through */
655 case COMPLETE:
656 vfs_print_message (_("ftpfs: logged in"));
657 wipe_password (pass);
658 g_free (name);
659 return 1;
661 default:
662 SUP->failed_on_login = 1;
663 wipe_password (super->path_element->password);
664 super->path_element->password = NULL;
666 goto login_fail;
670 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
671 super->path_element->user);
673 login_fail:
674 wipe_password (pass);
675 g_free (name);
676 ERRNOR (EPERM, 0);
679 /* --------------------------------------------------------------------------------------------- */
681 static void
682 ftpfs_load_no_proxy_list (void)
684 /* FixMe: shouldn't be hardcoded!!! */
685 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
686 FILE *npf;
687 int c;
688 char *p;
689 static char *mc_file = NULL;
691 mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL);
692 if (exist_file (mc_file))
694 npf = fopen (mc_file, "r");
695 if (npf != NULL)
697 while (fgets (s, sizeof (s), npf) != NULL)
699 p = strchr (s, '\n');
700 if (p == NULL) /* skip bogus entries */
702 while ((c = fgetc (npf)) != EOF && c != '\n')
704 continue;
707 if (p != s)
709 *p = '\0';
710 no_proxy = g_slist_prepend (no_proxy, g_strdup (s));
713 fclose (npf);
716 g_free (mc_file);
719 /* --------------------------------------------------------------------------------------------- */
720 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
722 static int
723 ftpfs_check_proxy (const char *host)
725 GSList *npe;
727 if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0')
728 return 0; /* sanity check */
730 if (*host == '!')
731 return 1;
733 if (!ftpfs_always_use_proxy)
734 return 0;
736 if (strchr (host, '.') == NULL)
737 return 0;
739 ftpfs_load_no_proxy_list ();
740 for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe))
742 const char *domain = (const char *) npe->data;
744 if (domain[0] == '.')
746 size_t ld = strlen (domain);
747 size_t lh = strlen (host);
749 while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1])
751 ld--;
752 lh--;
755 if (ld == 0)
756 return 0;
758 else if (g_ascii_strcasecmp (host, domain) == 0)
759 return 0;
762 return 1;
765 /* --------------------------------------------------------------------------------------------- */
767 static void
768 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
770 vfs_path_element_t *path_element;
772 path_element = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
773 *host = g_strdup (path_element->host);
774 *port = path_element->port;
775 vfs_path_element_free (path_element);
778 /* --------------------------------------------------------------------------------------------- */
780 static int
781 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
783 struct addrinfo hints, *res, *curr_res;
784 int my_socket = 0;
785 char *host = NULL;
786 char port[8];
787 int tmp_port;
788 int e;
790 (void) me;
792 /* Use a proxy host? */
793 host = g_strdup (super->path_element->host);
795 if (host == NULL || *host == '\0')
797 vfs_print_message (_("ftpfs: Invalid host name."));
798 ftpfs_errno = EINVAL;
799 g_free (host);
800 return -1;
803 /* Hosts to connect to that start with a ! should use proxy */
804 tmp_port = super->path_element->port;
806 if (SUP->proxy != NULL)
807 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
809 g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_port);
810 if (port[0] == '\0')
812 g_free (host);
813 ftpfs_errno = errno;
814 return -1;
817 tty_enable_interrupt_key (); /* clear the interrupt flag */
819 memset (&hints, 0, sizeof (struct addrinfo));
820 hints.ai_family = AF_UNSPEC;
821 hints.ai_socktype = SOCK_STREAM;
823 #ifdef AI_ADDRCONFIG
824 /* By default, only look up addresses using address types for
825 * which a local interface is configured (i.e. no IPv6 if no IPv6
826 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
827 hints.ai_flags = AI_ADDRCONFIG;
828 #endif
830 e = getaddrinfo (host, port, &hints, &res);
832 #ifdef AI_ADDRCONFIG
833 if (e == EAI_BADFLAGS)
835 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
836 hints.ai_flags = 0;
837 e = getaddrinfo (host, port, &hints, &res);
839 #endif
841 *port = '\0';
843 if (e != 0)
845 tty_disable_interrupt_key ();
846 vfs_print_message (_("ftpfs: %s"), gai_strerror (e));
847 g_free (host);
848 ftpfs_errno = EINVAL;
849 return -1;
852 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
854 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
856 if (my_socket < 0)
858 if (curr_res->ai_next != NULL)
859 continue;
861 tty_disable_interrupt_key ();
862 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno));
863 g_free (host);
864 freeaddrinfo (res);
865 ftpfs_errno = errno;
866 return -1;
869 vfs_print_message (_("ftpfs: making connection to %s"), host);
870 g_free (host);
871 host = NULL;
873 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
874 break;
876 ftpfs_errno = errno;
877 close (my_socket);
879 if (errno == EINTR && tty_got_interrupt ())
880 vfs_print_message (_("ftpfs: connection interrupted by user"));
881 else if (res->ai_next == NULL)
882 vfs_print_message (_("ftpfs: connection to server failed: %s"),
883 unix_error_string (errno));
884 else
885 continue;
887 freeaddrinfo (res);
888 tty_disable_interrupt_key ();
889 return -1;
892 freeaddrinfo (res);
893 tty_disable_interrupt_key ();
894 return my_socket;
897 /* --------------------------------------------------------------------------------------------- */
899 static int
900 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
902 int retry_seconds = 0;
903 int count_down;
905 /* We do not want to use the passive if we are using proxies */
906 if (SUP->proxy)
907 SUP->use_passive_connection = ftpfs_use_passive_connections_over_proxy;
911 SUP->failed_on_login = 0;
913 SUP->sock = ftpfs_open_socket (me, super);
914 if (SUP->sock == -1)
915 return -1;
917 if (ftpfs_login_server (me, super, NULL) != 0)
919 /* Logged in, no need to retry the connection */
920 break;
922 else
924 if (!SUP->failed_on_login)
925 return -1;
927 /* Close only the socket descriptor */
928 close (SUP->sock);
930 if (ftpfs_retry_seconds != 0)
932 retry_seconds = ftpfs_retry_seconds;
933 tty_enable_interrupt_key ();
934 for (count_down = retry_seconds; count_down; count_down--)
936 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
937 count_down);
938 sleep (1);
939 if (tty_got_interrupt ())
941 /* ftpfs_errno = E; */
942 tty_disable_interrupt_key ();
943 return 0;
946 tty_disable_interrupt_key ();
950 while (retry_seconds != 0);
952 super->path_element->path = ftpfs_get_current_directory (me, super);
953 if (super->path_element->path == NULL)
954 super->path_element->path = g_strdup (PATH_SEP_STR);
956 return 0;
959 /* --------------------------------------------------------------------------------------------- */
961 static int
962 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
963 const char *archive_name, char *op)
965 (void) archive_name;
967 super->data = g_new0 (ftp_super_data_t, 1);
969 super->path_element = ftpfs_split_url (strchr (op, ':') + 1);
970 SUP->proxy = NULL;
971 if (ftpfs_check_proxy (super->path_element->host))
972 SUP->proxy = ftpfs_proxy_host;
973 SUP->use_passive_connection = ftpfs_use_passive_connections;
974 SUP->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
975 SUP->isbinary = TYPE_UNKNOWN;
976 SUP->remote_is_amiga = 0;
977 super->name = g_strdup ("/");
978 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755));
980 return ftpfs_open_archive_int (me, super);
983 /* --------------------------------------------------------------------------------------------- */
985 static int
986 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
987 const char *archive_name, char *op, void *cookie)
989 vfs_path_element_t *path_element;
990 int result;
992 (void) me;
993 (void) archive_name;
994 (void) cookie;
996 path_element = ftpfs_split_url (strchr (op, ':') + 1);
998 result = ((strcmp (path_element->host, super->path_element->host) == 0)
999 && (strcmp (path_element->user, super->path_element->user) == 0)
1000 && (path_element->port == super->path_element->port)) ? 1 : 0;
1002 vfs_path_element_free (path_element);
1003 return result;
1006 /* --------------------------------------------------------------------------------------------- */
1007 /* The returned directory should always contain a trailing slash */
1009 static char *
1010 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
1012 char buf[BUF_8K], *bufp, *bufq;
1014 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
1015 ftpfs_get_reply (me, SUP->sock, buf, sizeof (buf)) == COMPLETE)
1017 bufp = NULL;
1018 for (bufq = buf; *bufq; bufq++)
1019 if (*bufq == '"')
1021 if (!bufp)
1023 bufp = bufq + 1;
1025 else
1027 *bufq = 0;
1028 if (*bufp)
1030 if (*(bufq - 1) != '/')
1032 *bufq++ = '/';
1033 *bufq = 0;
1035 if (*bufp == '/')
1036 return g_strdup (bufp);
1037 else
1039 /* If the remote server is an Amiga a leading slash
1040 might be missing. MC needs it because it is used
1041 as separator between hostname and path internally. */
1042 return g_strconcat ("/", bufp, (char *) NULL);
1045 else
1047 ftpfs_errno = EIO;
1048 return NULL;
1053 ftpfs_errno = EIO;
1054 return NULL;
1057 /* --------------------------------------------------------------------------------------------- */
1058 /* Setup Passive PASV FTP connection */
1060 static int
1061 ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
1062 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1064 char *c;
1065 char n[6];
1066 int xa, xb, xc, xd, xe, xf;
1068 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
1069 return 0;
1071 /* Parse remote parameters */
1072 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
1074 if (!*c)
1075 return 0;
1076 if (!isdigit ((unsigned char) *c))
1077 return 0;
1078 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1079 return 0;
1081 n[0] = (unsigned char) xa;
1082 n[1] = (unsigned char) xb;
1083 n[2] = (unsigned char) xc;
1084 n[3] = (unsigned char) xd;
1085 n[4] = (unsigned char) xe;
1086 n[5] = (unsigned char) xf;
1088 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1089 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1091 if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
1092 return 0;
1094 return 1;
1097 /* --------------------------------------------------------------------------------------------- */
1098 /* Setup Passive EPSV FTP connection */
1100 static int
1101 ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
1102 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1104 char *c;
1105 int port;
1107 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") != COMPLETE)
1108 return 0;
1110 /* (|||<port>|) */
1111 c = strchr (reply_str, '|');
1112 if (c == NULL)
1113 return 0;
1114 if (strlen (c) > 3)
1115 c += 3;
1116 else
1117 return 0;
1119 port = atoi (c);
1120 if (port < 0 || port > 65535)
1121 return 0;
1122 port = htons (port);
1124 switch (sa->ss_family)
1126 case AF_INET:
1127 ((struct sockaddr_in *) sa)->sin_port = port;
1128 break;
1129 case AF_INET6:
1130 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1131 break;
1134 return (connect (my_socket, (struct sockaddr *) sa, *salen) < 0) ? 0 : 1;
1137 /* --------------------------------------------------------------------------------------------- */
1138 /* Setup Passive ftp connection, we use it for source routed connections */
1140 static int
1141 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
1142 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1144 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1145 if (sa->ss_family == AF_INET)
1147 if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
1148 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1149 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1150 return 0;
1152 /* It's IPV6, so EPSV is our only hope */
1153 else
1155 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1156 return 0;
1159 return 1;
1162 /* --------------------------------------------------------------------------------------------- */
1163 /* Setup Active PORT or EPRT FTP connection */
1165 static int
1166 ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super,
1167 struct sockaddr_storage data_addr, socklen_t data_addrlen)
1169 unsigned short int port;
1170 char *addr;
1171 unsigned int af;
1173 switch (data_addr.ss_family)
1175 case AF_INET:
1176 af = FTP_INET;
1177 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1178 break;
1179 case AF_INET6:
1180 af = FTP_INET6;
1181 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1182 break;
1183 /* Not implemented */
1184 default:
1185 return 0;
1188 addr = g_try_malloc (NI_MAXHOST);
1189 if (addr == NULL)
1190 ERRNOR (ENOMEM, -1);
1192 if (getnameinfo
1193 ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1194 NI_NUMERICHOST) != 0)
1196 g_free (addr);
1197 ERRNOR (EIO, -1);
1200 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1201 if (af == FTP_INET)
1203 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1204 unsigned char *p = (unsigned char *) &port;
1206 if (ftpfs_command (me, super, WAIT_REPLY,
1207 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1208 p[0], p[1]) == COMPLETE)
1210 g_free (addr);
1211 return 1;
1216 * Converts network MSB first order to host byte order (LSB
1217 * first on i386). If we do it earlier, we will run into an
1218 * endianness issue, because the server actually expects to see
1219 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1221 port = ntohs (port);
1223 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1224 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
1226 g_free (addr);
1227 return 1;
1230 g_free (addr);
1231 return 0;
1234 /* --------------------------------------------------------------------------------------------- */
1235 /* Initialize a socket for FTP DATA connection */
1237 static int
1238 ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super,
1239 struct sockaddr_storage *data_addr, socklen_t * data_addrlen)
1241 int result;
1243 memset (data_addr, 0, sizeof (struct sockaddr_storage));
1244 *data_addrlen = sizeof (struct sockaddr_storage);
1246 if (SUP->use_passive_connection)
1247 result = getpeername (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1248 else
1249 result = getsockname (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1251 if (result == -1)
1252 return -1;
1254 switch (data_addr->ss_family)
1256 case AF_INET:
1257 ((struct sockaddr_in *) data_addr)->sin_port = 0;
1258 break;
1259 case AF_INET6:
1260 ((struct sockaddr_in6 *) data_addr)->sin6_port = 0;
1261 break;
1262 default:
1263 vfs_print_message (_("ftpfs: invalid address family"));
1264 ERRNOR (EINVAL, -1);
1267 result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP);
1269 if (result < 0)
1271 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1272 return -1;
1275 return result;
1278 /* --------------------------------------------------------------------------------------------- */
1279 /* Initialize FTP DATA connection */
1281 static int
1282 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1284 struct sockaddr_storage data_addr;
1285 socklen_t data_addrlen;
1288 * Don't factor socket initialization out of these conditionals,
1289 * because ftpfs_init_data_socket initializes it in different way
1290 * depending on use_passive_connection flag.
1293 /* Try to establish a passive connection first (if requested) */
1294 if (SUP->use_passive_connection)
1296 int data_sock;
1298 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1299 if (data_sock < 0)
1300 return -1;
1302 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1303 return data_sock;
1305 vfs_print_message (_("ftpfs: could not setup passive mode"));
1306 SUP->use_passive_connection = 0;
1308 close (data_sock);
1311 /* If passive setup is diabled or failed, fallback to active connections */
1312 if (!SUP->use_passive_connection)
1314 int data_sock;
1316 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1317 if (data_sock < 0)
1318 return -1;
1320 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) == 0) &&
1321 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) == 0) &&
1322 (listen (data_sock, 1) == 0) &&
1323 (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0))
1324 return data_sock;
1326 close (data_sock);
1329 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1330 SUP->use_passive_connection = SUP->proxy != NULL ? ftpfs_use_passive_connections_over_proxy :
1331 ftpfs_use_passive_connections;
1333 ftpfs_errno = EIO;
1334 return -1;
1337 /* --------------------------------------------------------------------------------------------- */
1339 static int
1340 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1341 const char *remote, int isbinary, int reget)
1343 struct sockaddr_storage from;
1344 int s, j, data;
1345 socklen_t fromlen = sizeof (from);
1347 s = ftpfs_initconn (me, super);
1348 if (s == -1)
1349 return -1;
1351 if (ftpfs_changetype (me, super, isbinary) == -1)
1352 return -1;
1353 if (reget > 0)
1355 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1356 if (j != CONTINUE)
1357 return -1;
1359 if (remote)
1361 char *remote_path = ftpfs_translate_path (me, super, remote);
1362 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1363 /* WarFtpD can't STORE //filename */
1364 (*remote_path == '/') ? remote_path + 1 : remote_path);
1365 g_free (remote_path);
1367 else
1368 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1370 if (j != PRELIM)
1371 ERRNOR (EPERM, -1);
1372 tty_enable_interrupt_key ();
1373 if (SUP->use_passive_connection)
1374 data = s;
1375 else
1377 data = accept (s, (struct sockaddr *) &from, &fromlen);
1378 if (data < 0)
1380 ftpfs_errno = errno;
1381 close (s);
1382 return -1;
1384 close (s);
1386 tty_disable_interrupt_key ();
1387 return data;
1390 /* --------------------------------------------------------------------------------------------- */
1392 static void
1393 ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t * fh)
1395 struct vfs_s_super *super = FH_SUPER;
1396 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1397 fd_set mask;
1398 char buf[BUF_8K];
1399 int dsock = FH_SOCK;
1400 FH_SOCK = -1;
1401 SUP->ctl_connection_busy = 0;
1403 vfs_print_message (_("ftpfs: aborting transfer."));
1404 if (send (SUP->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1406 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1407 if (dsock != -1)
1408 close (dsock);
1409 return;
1412 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1414 vfs_print_message (_("ftpfs: abort failed"));
1415 if (dsock != -1)
1416 close (dsock);
1417 return;
1419 if (dsock != -1)
1421 FD_ZERO (&mask);
1422 FD_SET (dsock, &mask);
1423 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1425 struct timeval start_tim, tim;
1426 gettimeofday (&start_tim, NULL);
1427 /* flush the remaining data */
1428 while (read (dsock, buf, sizeof (buf)) > 0)
1430 gettimeofday (&tim, NULL);
1431 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT)
1433 /* server keeps sending, drop the connection and ftpfs_reconnect */
1434 close (dsock);
1435 ftpfs_reconnect (me, super);
1436 return;
1440 close (dsock);
1442 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) == TRANSIENT) && (code == 426))
1443 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1446 /* --------------------------------------------------------------------------------------------- */
1448 #if 0
1449 static void
1450 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1451 struct vfs_s_inode *dir)
1453 struct linklist *flist;
1454 struct direntry *fe, *fel;
1455 char tmp[MC_MAXPATHLEN];
1456 int depth;
1458 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1459 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1461 /* flist->data->l_stat is alread initialized with 0 */
1462 fel = flist->data;
1463 if (S_ISLNK (fel->s.st_mode) && fel->linkname)
1465 if (fel->linkname[0] == '/')
1467 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1468 continue;
1469 strcpy (tmp, fel->linkname);
1471 else
1473 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1474 continue;
1475 strcpy (tmp, dir->remote_path);
1476 if (tmp[1] != '\0')
1477 strcat (tmp, "/");
1478 strcat (tmp + 1, fel->linkname);
1480 for (depth = 0; depth < 100; depth++)
1481 { /* depth protects against recursive symbolic links */
1482 canonicalize_pathname (tmp);
1483 fe = _get_file_entry (bucket, tmp, 0, 0);
1484 if (fe)
1486 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1488 /* Symlink points to link which isn't resolved, yet. */
1489 if (fe->linkname[0] == '/')
1491 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1492 break;
1493 strcpy (tmp, fe->linkname);
1495 else
1497 /* at this point tmp looks always like this
1498 /directory/filename, i.e. no need to check
1499 strrchr's return value */
1500 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1501 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1502 break;
1503 strcat (tmp, fe->linkname);
1505 continue;
1507 else
1509 fel->l_stat = g_new (struct stat, 1);
1510 if (S_ISLNK (fe->s.st_mode))
1511 *fel->l_stat = *fe->l_stat;
1512 else
1513 *fel->l_stat = fe->s;
1514 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1517 break;
1521 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1524 /* --------------------------------------------------------------------------------------------- */
1526 static void
1527 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1528 struct vfs_s_inode *dir)
1530 char buffer[2048] = "", *filename;
1531 int sock;
1532 FILE *fp;
1533 struct stat s;
1534 struct linklist *flist;
1535 struct direntry *fe;
1536 int switch_method = 0;
1538 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1539 if (strchr (dir->remote_path, ' '))
1541 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1543 vfs_print_message (_("ftpfs: CWD failed."));
1544 return;
1546 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1548 else
1549 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1551 if (sock == -1)
1553 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1554 return;
1557 fp = fdopen (sock, "r");
1558 if (fp == NULL)
1560 close (sock);
1561 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1562 return;
1564 tty_enable_interrupt_key ();
1565 flist = dir->file_list->next;
1566 while (1)
1570 if (flist == dir->file_list)
1571 goto done;
1572 fe = flist->data;
1573 flist = flist->next;
1575 while (!S_ISLNK (fe->s.st_mode));
1576 while (1)
1578 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1579 goto done;
1580 if (MEDATA->logfile)
1582 fputs (buffer, MEDATA->logfile);
1583 fflush (MEDATA->logfile);
1585 vfs_die ("This code should be commented out\n");
1586 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1588 int r = strcmp (fe->name, filename);
1589 g_free (filename);
1590 if (r == 0)
1592 if (S_ISLNK (s.st_mode))
1594 /* This server doesn't understand LIST -lLa */
1595 switch_method = 1;
1596 goto done;
1598 fe->l_stat = g_new (struct stat, 1);
1599 if (fe->l_stat == NULL)
1600 goto done;
1601 *fe->l_stat = s;
1602 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1603 break;
1605 if (r < 0)
1606 break;
1610 done:
1611 while (fgets (buffer, sizeof (buffer), fp) != NULL);
1612 tty_disable_interrupt_key ();
1613 fclose (fp);
1614 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1617 /* --------------------------------------------------------------------------------------------- */
1619 static void
1620 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1622 vfs_print_message (_("Resolving symlink..."));
1624 if (SUP->strict_rfc959_list_cmd)
1625 resolve_symlink_without_ls_options (me, super, dir);
1626 else
1627 resolve_symlink_with_ls_options (me, super, dir);
1629 #endif
1631 /* --------------------------------------------------------------------------------------------- */
1633 static int
1634 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1636 struct vfs_s_entry *ent;
1637 struct vfs_s_super *super = dir->super;
1638 int sock, num_entries = 0;
1639 char lc_buffer[BUF_8K];
1640 int cd_first;
1642 cd_first = ftpfs_first_cd_then_ls || (SUP->strict == RFC_STRICT)
1643 || (strchr (remote_path, ' ') != NULL);
1645 again:
1646 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1647 remote_path,
1648 SUP->strict ==
1649 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1651 if (cd_first)
1653 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1655 ftpfs_errno = ENOENT;
1656 vfs_print_message (_("ftpfs: CWD failed."));
1657 return -1;
1661 gettimeofday (&dir->timestamp, NULL);
1662 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1664 if (SUP->strict == RFC_STRICT)
1665 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1666 else if (cd_first)
1667 /* Dirty hack to avoid autoprepending / to . */
1668 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1669 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1670 else
1672 /* Trailing "/." is necessary if remote_path is a symlink */
1673 char *path = concat_dir_and_file (remote_path, ".");
1674 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1675 g_free (path);
1678 if (sock == -1)
1679 goto fallback;
1681 /* Clear the interrupt flag */
1682 tty_enable_interrupt_key ();
1684 while (1)
1686 int i;
1687 int res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer),
1688 sock);
1689 if (!res)
1690 break;
1692 if (res == EINTR)
1694 me->verrno = ECONNRESET;
1695 close (sock);
1696 tty_disable_interrupt_key ();
1697 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1698 vfs_print_message (_("%s: failure"), me->name);
1699 return -1;
1702 if (MEDATA->logfile)
1704 fputs (lc_buffer, MEDATA->logfile);
1705 fputs ("\n", MEDATA->logfile);
1706 fflush (MEDATA->logfile);
1709 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1710 i = ent->ino->st.st_nlink;
1711 if (!vfs_parse_ls_lga (lc_buffer, &ent->ino->st, &ent->name, &ent->ino->linkname))
1713 vfs_s_free_entry (me, ent);
1714 continue;
1716 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1717 num_entries++;
1718 vfs_s_insert_entry (me, dir, ent);
1721 close (sock);
1722 me->verrno = E_REMOTE;
1723 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1724 goto fallback;
1726 if (num_entries == 0 && cd_first == 0)
1728 /* The LIST command may produce an empty output. In such scenario
1729 it is not clear whether this is caused by `remote_path' being
1730 a non-existent path or for some other reason (listing emtpy
1731 directory without the -a option, non-readable directory, etc.).
1733 Since `dir_load' is a crucial method, when it comes to determine
1734 whether a given path is a _directory_, the code must try its best
1735 to determine the type of `remote_path'. The only reliable way to
1736 achieve this is trough issuing a CWD command. */
1738 cd_first = 1;
1739 goto again;
1742 if (SUP->strict == RFC_AUTODETECT)
1743 SUP->strict = RFC_DARING;
1745 vfs_print_message (_("%s: done."), me->name);
1746 return 0;
1748 fallback:
1749 if (SUP->strict == RFC_AUTODETECT)
1751 /* It's our first attempt to get a directory listing from this
1752 server (UNIX style LIST command) */
1753 SUP->strict = RFC_STRICT;
1754 /* I hate goto, but recursive call needs another 8K on stack */
1755 /* return ftpfs_dir_load (me, dir, remote_path); */
1756 cd_first = 1;
1757 goto again;
1759 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1760 ERRNOR (EACCES, -1);
1763 /* --------------------------------------------------------------------------------------------- */
1765 static int
1766 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname)
1768 int h, sock, n_read, n_written;
1769 off_t n_stored;
1770 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1771 struct linger li;
1772 #else
1773 int flag_one = 1;
1774 #endif
1775 char lc_buffer[BUF_8K];
1776 struct stat s;
1777 char *w_buf;
1778 struct vfs_s_super *super = FH_SUPER;
1779 ftp_fh_data_t *ftp = (ftp_fh_data_t *) fh->data;
1781 h = open (localname, O_RDONLY);
1782 if (h == -1)
1783 ERRNOR (EIO, -1);
1785 sock =
1786 ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1787 if (sock < 0 || fstat (h, &s) == -1)
1789 close (h);
1790 return -1;
1792 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1793 li.l_onoff = 1;
1794 li.l_linger = 120;
1795 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1796 #else
1797 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1798 #endif
1799 n_stored = 0;
1801 tty_enable_interrupt_key ();
1802 while (TRUE)
1804 while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1806 if (errno == EINTR)
1808 if (tty_got_interrupt ())
1810 ftpfs_errno = EINTR;
1811 goto error_return;
1813 else
1814 continue;
1816 ftpfs_errno = errno;
1817 goto error_return;
1819 if (n_read == 0)
1820 break;
1821 n_stored += n_read;
1822 w_buf = lc_buffer;
1823 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1825 if (n_written == -1)
1827 if (errno == EINTR && !tty_got_interrupt ())
1829 continue;
1831 ftpfs_errno = errno;
1832 goto error_return;
1834 w_buf += n_written;
1835 n_read -= n_written;
1837 vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
1838 _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
1840 tty_disable_interrupt_key ();
1841 close (sock);
1842 close (h);
1843 if (ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE)
1844 ERRNOR (EIO, -1);
1845 return 0;
1846 error_return:
1847 tty_disable_interrupt_key ();
1848 close (sock);
1849 close (h);
1850 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1851 return -1;
1854 /* --------------------------------------------------------------------------------------------- */
1856 static int
1857 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset)
1859 char *name;
1861 if (fh->data == NULL)
1862 fh->data = g_new0 (ftp_fh_data_t, 1);
1864 name = vfs_s_fullpath (me, fh->ino);
1865 if (name == NULL)
1866 return 0;
1867 FH_SOCK = ftpfs_open_data_connection (me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1868 g_free (name);
1869 if (FH_SOCK == -1)
1870 ERRNOR (EACCES, 0);
1871 fh->linear = LS_LINEAR_OPEN;
1872 ((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy = 1;
1873 ((ftp_fh_data_t *) fh->data)->append = 0;
1874 return 1;
1877 /* --------------------------------------------------------------------------------------------- */
1879 static int
1880 ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t * fh, void *buf, size_t len)
1882 ssize_t n;
1883 struct vfs_s_super *super = FH_SUPER;
1885 while ((n = read (FH_SOCK, buf, len)) < 0)
1887 if ((errno == EINTR) && !tty_got_interrupt ())
1888 continue;
1889 break;
1892 if (n < 0)
1893 ftpfs_linear_abort (me, fh);
1895 if (n == 0)
1897 SUP->ctl_connection_busy = 0;
1898 close (FH_SOCK);
1899 FH_SOCK = -1;
1900 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1901 ERRNOR (E_REMOTE, -1);
1902 return 0;
1904 ERRNOR (errno, n);
1907 /* --------------------------------------------------------------------------------------------- */
1909 static void
1910 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t * fh)
1912 if (FH_SOCK != -1)
1913 ftpfs_linear_abort (me, fh);
1916 /* --------------------------------------------------------------------------------------------- */
1918 static int
1919 ftpfs_ctl (void *fh, int ctlop, void *arg)
1921 (void) arg;
1923 switch (ctlop)
1925 case VFS_CTL_IS_NOTREADY:
1927 int v;
1929 if (!FH->linear)
1930 vfs_die ("You may not do this");
1931 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1932 return 0;
1934 v = vfs_s_select_on_two (((ftp_fh_data_t *) (FH->data))->sock, 0);
1935 return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
1937 default:
1938 return 0;
1942 /* --------------------------------------------------------------------------------------------- */
1944 static int
1945 ftpfs_send_command (const vfs_path_t * vpath, const char *cmd, int flags)
1947 const char *rpath;
1948 char *p;
1949 struct vfs_s_super *super;
1950 int r;
1951 vfs_path_element_t *path_element;
1953 int flush_directory_cache = (flags & OPT_FLUSH);
1955 path_element = vfs_path_get_by_index (vpath, -1);
1957 rpath = vfs_s_get_path_mangle (vpath, &super, 0);
1958 if (rpath == NULL)
1959 return -1;
1961 p = ftpfs_translate_path (path_element->class, super, rpath);
1962 r = ftpfs_command (path_element->class, super, WAIT_REPLY, cmd, p);
1963 g_free (p);
1964 vfs_stamp_create (&vfs_ftpfs_ops, super);
1965 if (flags & OPT_IGNORE_ERROR)
1966 r = COMPLETE;
1967 if (r != COMPLETE)
1969 path_element->class->verrno = EPERM;
1970 return -1;
1972 if (flush_directory_cache)
1973 vfs_s_invalidate (path_element->class, super);
1974 return 0;
1977 /* --------------------------------------------------------------------------------------------- */
1979 static int
1980 ftpfs_chmod (const vfs_path_t * vpath, int mode)
1982 char buf[BUF_SMALL];
1983 int ret;
1985 g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1987 ret = ftpfs_send_command (vpath, buf, OPT_FLUSH);
1989 return ftpfs_ignore_chattr_errors ? 0 : ret;
1992 /* --------------------------------------------------------------------------------------------- */
1994 static int
1995 ftpfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
1997 #if 0
1998 (void) vpath;
1999 (void) owner;
2000 (void) group;
2002 ftpfs_errno = EPERM;
2003 return -1;
2004 #else
2005 /* Everyone knows it is not possible to chown remotely, so why bother them.
2006 If someone's root, then copy/move will always try to chown it... */
2007 (void) vpath;
2008 (void) owner;
2009 (void) group;
2010 return 0;
2011 #endif
2014 /* --------------------------------------------------------------------------------------------- */
2016 static int
2017 ftpfs_unlink (const vfs_path_t * vpath)
2019 return ftpfs_send_command (vpath, "DELE /%s", OPT_FLUSH);
2022 /* --------------------------------------------------------------------------------------------- */
2024 /* Return 1 if path is the same directory as the one we are in now */
2025 static int
2026 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
2028 (void) me;
2030 if (super->path_element->path == NULL)
2031 return FALSE;
2032 return (strcmp (path, super->path_element->path) == 0);
2035 /* --------------------------------------------------------------------------------------------- */
2037 static int
2038 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
2040 int r;
2041 char *p;
2043 if (!SUP->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
2044 return COMPLETE;
2046 p = ftpfs_translate_path (me, super, remote_path);
2047 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
2048 g_free (p);
2050 if (r != COMPLETE)
2051 ftpfs_errno = EIO;
2052 else
2054 g_free (super->path_element->path);
2055 super->path_element->path = g_strdup (remote_path);
2056 SUP->cwd_deferred = 0;
2058 return r;
2061 /* --------------------------------------------------------------------------------------------- */
2063 static int
2064 ftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
2066 ftpfs_send_command (vpath1, "RNFR /%s", OPT_FLUSH);
2067 return ftpfs_send_command (vpath2, "RNTO /%s", OPT_FLUSH);
2070 /* --------------------------------------------------------------------------------------------- */
2072 static int
2073 ftpfs_mkdir (const vfs_path_t * vpath, mode_t mode)
2075 (void) mode; /* FIXME: should be used */
2077 return ftpfs_send_command (vpath, "MKD /%s", OPT_FLUSH);
2080 /* --------------------------------------------------------------------------------------------- */
2082 static int
2083 ftpfs_rmdir (const vfs_path_t * vpath)
2085 return ftpfs_send_command (vpath, "RMD /%s", OPT_FLUSH);
2088 /* --------------------------------------------------------------------------------------------- */
2090 static int
2091 ftpfs_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
2093 ftp_fh_data_t *ftp;
2095 (void) mode;
2097 fh->data = g_new0 (ftp_fh_data_t, 1);
2098 ftp = (ftp_fh_data_t *) fh->data;
2099 /* File will be written only, so no need to retrieve it from ftp server */
2100 if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
2102 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2103 struct linger li;
2104 #else
2105 int li = 1;
2106 #endif
2107 char *name;
2109 /* ftpfs_linear_start() called, so data will be written
2110 * to local temporary file and stored to ftp server
2111 * by vfs_s_close later
2113 if (((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy)
2115 if (!fh->ino->localname)
2117 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
2118 fh->ino->ent->name);
2119 if (handle == -1)
2120 goto fail;
2121 close (handle);
2122 ftp->append = flags & O_APPEND;
2124 return 0;
2126 name = vfs_s_fullpath (me, fh->ino);
2127 if (name == NULL)
2128 goto fail;
2129 fh->handle =
2130 ftpfs_open_data_connection (me, fh->ino->super,
2131 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
2132 g_free (name);
2134 if (fh->handle < 0)
2135 goto fail;
2136 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2137 li.l_onoff = 1;
2138 li.l_linger = 120;
2139 #endif
2140 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
2142 if (fh->ino->localname)
2144 unlink (fh->ino->localname);
2145 g_free (fh->ino->localname);
2146 fh->ino->localname = NULL;
2148 return 0;
2151 if (!fh->ino->localname && vfs_s_retrieve_file (me, fh->ino) == -1)
2152 goto fail;
2153 if (!fh->ino->localname)
2154 vfs_die ("retrieve_file failed to fill in localname");
2155 return 0;
2157 fail:
2158 g_free (fh->data);
2159 return -1;
2162 /* --------------------------------------------------------------------------------------------- */
2164 static int
2165 ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t * fh)
2167 if (fh->handle != -1 && !fh->ino->localname)
2169 ftp_super_data_t *ftp = (ftp_super_data_t *) fh->ino->super->data;
2171 close (fh->handle);
2172 fh->handle = -1;
2173 /* File is stored to destination already, so
2174 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2176 fh->changed = 0;
2177 if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE)
2178 ERRNOR (EIO, -1);
2179 vfs_s_invalidate (me, FH_SUPER);
2182 g_free (fh->data);
2184 return 0;
2187 /* --------------------------------------------------------------------------------------------- */
2189 static void
2190 ftpfs_done (struct vfs_class *me)
2192 (void) me;
2194 g_slist_foreach (no_proxy, (GFunc) g_free, NULL);
2195 g_slist_free (no_proxy);
2197 g_free (ftpfs_anonymous_passwd);
2198 g_free (ftpfs_proxy_host);
2201 /* --------------------------------------------------------------------------------------------- */
2203 static void
2204 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
2206 GList *iter;
2208 for (iter = MEDATA->supers; iter != NULL; iter = g_list_next (iter))
2210 const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
2211 char *name;
2213 name =
2214 g_strconcat ("/#ftp:", super->path_element->user, "@", super->path_element->host, "/",
2215 super->path_element->path, (char *) NULL);
2216 func (name);
2217 g_free (name);
2221 /* --------------------------------------------------------------------------------------------- */
2223 static keyword_t
2224 ftpfs_netrc_next (void)
2226 char *p;
2227 keyword_t i;
2228 static const char *const keywords[] = { "default", "machine",
2229 "login", "password", "passwd", "account", "macdef", NULL
2232 while (1)
2234 netrcp = skip_separators (netrcp);
2235 if (*netrcp != '\n')
2236 break;
2237 netrcp++;
2239 if (!*netrcp)
2240 return NETRC_NONE;
2241 p = buffer;
2242 if (*netrcp == '"')
2244 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++)
2246 if (*netrcp == '\\')
2247 netrcp++;
2248 *p++ = *netrcp;
2251 else
2253 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
2254 *netrcp != ',' && *netrcp; netrcp++)
2256 if (*netrcp == '\\')
2257 netrcp++;
2258 *p++ = *netrcp;
2261 *p = 0;
2262 if (!*buffer)
2263 return NETRC_NONE;
2265 for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2266 if (strcmp (keywords[i - 1], buffer) == 0)
2267 return i;
2269 return NETRC_UNKNOWN;
2272 /* --------------------------------------------------------------------------------------------- */
2274 static int
2275 ftpfs_netrc_bad_mode (const char *netrcname)
2277 static int be_angry = 1;
2278 struct stat mystat;
2280 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077))
2282 if (be_angry)
2284 message (D_ERROR, MSG_ERROR,
2285 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2286 be_angry = 0;
2288 return 1;
2290 return 0;
2293 /* --------------------------------------------------------------------------------------------- */
2294 /* Scan .netrc until we find matching "machine" or "default"
2295 * domain is used for additional matching
2296 * No search is done after "default" in compliance with "man netrc"
2297 * Return 0 if found, -1 otherwise */
2299 static int
2300 ftpfs_find_machine (const char *host, const char *domain)
2302 keyword_t keyword;
2304 if (!host)
2305 host = "";
2306 if (!domain)
2307 domain = "";
2309 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2311 if (keyword == NETRC_DEFAULT)
2312 return 0;
2314 if (keyword == NETRC_MACDEF)
2316 /* Scan for an empty line, which concludes "macdef" */
2319 while (*netrcp && *netrcp != '\n')
2320 netrcp++;
2321 if (*netrcp != '\n')
2322 break;
2323 netrcp++;
2325 while (*netrcp && *netrcp != '\n');
2326 continue;
2329 if (keyword != NETRC_MACHINE)
2330 continue;
2332 /* Take machine name */
2333 if (ftpfs_netrc_next () == NETRC_NONE)
2334 break;
2336 if (g_ascii_strcasecmp (host, buffer) != 0)
2338 /* Try adding our domain to short names in .netrc */
2339 const char *host_domain = strchr (host, '.');
2340 if (!host_domain)
2341 continue;
2343 /* Compare domain part */
2344 if (g_ascii_strcasecmp (host_domain, domain) != 0)
2345 continue;
2347 /* Compare local part */
2348 if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2349 continue;
2352 return 0;
2355 /* end of .netrc */
2356 return -1;
2359 /* --------------------------------------------------------------------------------------------- */
2360 /* Extract login and password from .netrc for the host.
2361 * pass may be NULL.
2362 * Returns 0 for success, -1 for error */
2364 static int
2365 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2367 char *netrcname;
2368 char *tmp_pass = NULL;
2369 char hostname[MAXHOSTNAMELEN];
2370 const char *domain;
2371 keyword_t keyword;
2372 static struct rupcache
2374 struct rupcache *next;
2375 char *host;
2376 char *login;
2377 char *pass;
2378 } *rup_cache = NULL, *rupp;
2380 /* Initialize *login and *pass */
2381 g_free (*login);
2382 *login = NULL;
2383 g_free (*pass);
2384 *pass = NULL;
2386 /* Look up in the cache first */
2387 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2389 if (!strcmp (host, rupp->host))
2391 if (rupp->login)
2392 *login = g_strdup (rupp->login);
2393 if (pass && rupp->pass)
2394 *pass = g_strdup (rupp->pass);
2395 return 0;
2399 /* Load current .netrc */
2400 netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2401 if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2403 g_free (netrcname);
2404 return 0;
2407 netrcp = netrc;
2409 /* Find our own domain name */
2410 if (gethostname (hostname, sizeof (hostname)) < 0)
2411 *hostname = '\0';
2413 domain = strchr (hostname, '.');
2414 if (domain == NULL)
2415 domain = "";
2417 /* Scan for "default" and matching "machine" keywords */
2418 ftpfs_find_machine (host, domain);
2420 /* Scan for keywords following "default" and "machine" */
2421 while (1)
2423 int need_break = 0;
2424 keyword = ftpfs_netrc_next ();
2426 switch (keyword)
2428 case NETRC_LOGIN:
2429 if (ftpfs_netrc_next () == NETRC_NONE)
2431 need_break = 1;
2432 break;
2435 /* We have another name already - should not happen */
2436 if (*login)
2438 need_break = 1;
2439 break;
2442 /* We have login name now */
2443 *login = g_strdup (buffer);
2444 break;
2446 case NETRC_PASSWORD:
2447 case NETRC_PASSWD:
2448 if (ftpfs_netrc_next () == NETRC_NONE)
2450 need_break = 1;
2451 break;
2454 /* Ignore unsafe passwords */
2455 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2456 && ftpfs_netrc_bad_mode (netrcname))
2458 need_break = 1;
2459 break;
2462 /* Remember password. pass may be NULL, so use tmp_pass */
2463 if (tmp_pass == NULL)
2464 tmp_pass = g_strdup (buffer);
2465 break;
2467 case NETRC_ACCOUNT:
2468 /* "account" is followed by a token which we ignore */
2469 if (ftpfs_netrc_next () == NETRC_NONE)
2471 need_break = 1;
2472 break;
2475 /* Ignore account, but warn user anyways */
2476 ftpfs_netrc_bad_mode (netrcname);
2477 break;
2479 default:
2480 /* Unexpected keyword or end of file */
2481 need_break = 1;
2482 break;
2485 if (need_break)
2486 break;
2489 g_free (netrc);
2490 g_free (netrcname);
2492 rupp = g_new (struct rupcache, 1);
2493 rupp->host = g_strdup (host);
2494 rupp->login = g_strdup (*login);
2495 rupp->pass = g_strdup (tmp_pass);
2497 rupp->next = rup_cache;
2498 rup_cache = rupp;
2500 *pass = tmp_pass;
2502 return 0;
2505 /* --------------------------------------------------------------------------------------------- */
2506 /*** public functions ****************************************************************************/
2507 /* --------------------------------------------------------------------------------------------- */
2509 /** This routine is called as the last step in load_setup */
2510 void
2511 ftpfs_init_passwd (void)
2513 ftpfs_anonymous_passwd = load_anon_passwd ();
2514 if (ftpfs_anonymous_passwd)
2515 return;
2517 /* If there is no anonymous ftp password specified
2518 * then we'll just use anonymous@
2519 * We don't send any other thing because:
2520 * - We want to remain anonymous
2521 * - We want to stop SPAM
2522 * - We don't want to let ftp sites to discriminate by the user,
2523 * host or country.
2525 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
2528 /* --------------------------------------------------------------------------------------------- */
2530 void
2531 init_ftpfs (void)
2533 static struct vfs_s_subclass ftpfs_subclass;
2535 tcp_init ();
2537 ftpfs_subclass.flags = VFS_S_REMOTE;
2538 ftpfs_subclass.archive_same = ftpfs_archive_same;
2539 ftpfs_subclass.open_archive = ftpfs_open_archive;
2540 ftpfs_subclass.free_archive = ftpfs_free_archive;
2541 ftpfs_subclass.fh_open = ftpfs_fh_open;
2542 ftpfs_subclass.fh_close = ftpfs_fh_close;
2543 ftpfs_subclass.dir_load = ftpfs_dir_load;
2544 ftpfs_subclass.file_store = ftpfs_file_store;
2545 ftpfs_subclass.linear_start = ftpfs_linear_start;
2546 ftpfs_subclass.linear_read = ftpfs_linear_read;
2547 ftpfs_subclass.linear_close = ftpfs_linear_close;
2549 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2550 vfs_ftpfs_ops.name = "ftpfs";
2551 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2552 vfs_ftpfs_ops.prefix = "ftp:";
2553 vfs_ftpfs_ops.done = &ftpfs_done;
2554 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2555 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2556 vfs_ftpfs_ops.chown = ftpfs_chown;
2557 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2558 vfs_ftpfs_ops.rename = ftpfs_rename;
2559 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2560 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2561 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2562 vfs_register_class (&vfs_ftpfs_ops);
2565 /* --------------------------------------------------------------------------------------------- */