Following prototypes of functions was changed in VFS-module API:
[midnight-commander.git] / src / vfs / ftpfs / ftpfs.c
blob8e199a5bee552d4746cac62470951c1772f322ad
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_url_t *
325 ftpfs_split_url (const char *path)
327 vfs_url_t *p;
329 p = vfs_url_split (path, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
331 if (p->user != NULL)
333 /* Look up user and password in netrc */
334 if (ftpfs_use_netrc)
335 ftpfs_netrc_lookup (p->host, &p->user, &p->password);
337 if (p->user == NULL)
338 p->user = g_strdup ("anonymous");
340 /* Look up password in netrc for known user */
341 if (ftpfs_use_netrc && p->user != NULL && p->password != NULL)
343 char *new_user = NULL;
345 ftpfs_netrc_lookup (p->host, &new_user, &p->password);
347 /* If user is different, remove password */
348 if (new_user != NULL && strcmp (p->user, new_user) != 0)
350 g_free (p->password);
351 p->password = NULL;
354 g_free (new_user);
357 return p;
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->url->path;
420 close (SUP->sock);
421 SUP->sock = sock;
422 super->url->path = NULL;
425 if (ftpfs_login_server (me, super, super->url->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->url->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->url->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->url->password != NULL) /* explicit password */
575 op = g_strdup (super->url->password);
576 else if (netrcpass != NULL) /* password from netrc */
577 op = g_strdup (netrcpass);
578 else if (strcmp (super->url->user, "anonymous") == 0 || strcmp (super->url->user, "ftp") == 0)
580 if (ftpfs_anonymous_passwd == NULL) /* default anonymous password */
581 ftpfs_init_passwd ();
582 op = g_strdup (ftpfs_anonymous_passwd);
583 anon = 1;
585 else
586 { /* ask user */
587 char *p;
589 p = g_strdup_printf (_("FTP: Password required for %s"), super->url->user);
590 op = vfs_get_password (p);
591 g_free (p);
592 if (op == NULL)
593 ERRNOR (EPERM, 0);
594 super->url->password = g_strdup (op);
597 if (!anon || MEDATA->logfile)
598 pass = op;
599 else
601 pass = g_strconcat ("-", op, (char *) NULL);
602 wipe_password (op);
605 /* Proxy server accepts: username@host-we-want-to-connect */
606 if (SUP->proxy)
607 name =
608 g_strconcat (super->url->user, "@",
609 super->url->host[0] == '!' ? super->url->host + 1 : super->url->host,
610 (char *) NULL);
611 else
612 name = g_strdup (super->url->user);
614 if (ftpfs_get_reply (me, SUP->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
616 char *reply_up;
618 reply_up = g_ascii_strup (reply_string, -1);
619 SUP->remote_is_amiga = strstr (reply_up, "AMIGA") != 0;
620 g_free (reply_up);
622 if (MEDATA->logfile)
624 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n", SUP->remote_is_amiga);
625 fflush (MEDATA->logfile);
628 vfs_print_message (_("ftpfs: sending login name"));
630 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
632 case CONTINUE:
633 vfs_print_message (_("ftpfs: sending user password"));
634 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
635 if (code == CONTINUE)
637 char *p;
639 p = g_strdup_printf (_("FTP: Account required for user %s"), super->url->user);
640 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
641 g_free (p);
642 if (op == NULL)
643 ERRNOR (EPERM, 0);
644 vfs_print_message (_("ftpfs: sending user account"));
645 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
646 g_free (op);
648 if (code != COMPLETE)
649 break;
650 /* fall through */
652 case COMPLETE:
653 vfs_print_message (_("ftpfs: logged in"));
654 wipe_password (pass);
655 g_free (name);
656 return 1;
658 default:
659 SUP->failed_on_login = 1;
660 wipe_password (super->url->password);
661 super->url->password = NULL;
663 goto login_fail;
667 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "), super->url->user);
669 login_fail:
670 wipe_password (pass);
671 g_free (name);
672 ERRNOR (EPERM, 0);
675 /* --------------------------------------------------------------------------------------------- */
677 static void
678 ftpfs_load_no_proxy_list (void)
680 /* FixMe: shouldn't be hardcoded!!! */
681 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
682 FILE *npf;
683 int c;
684 char *p;
685 static char *mc_file = NULL;
687 mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL);
688 if (exist_file (mc_file))
690 npf = fopen (mc_file, "r");
691 if (npf != NULL)
693 while (fgets (s, sizeof (s), npf) != NULL)
695 p = strchr (s, '\n');
696 if (p == NULL) /* skip bogus entries */
698 while ((c = fgetc (npf)) != EOF && c != '\n')
700 continue;
703 if (p != s)
705 *p = '\0';
706 no_proxy = g_slist_prepend (no_proxy, g_strdup (s));
709 fclose (npf);
712 g_free (mc_file);
715 /* --------------------------------------------------------------------------------------------- */
716 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
718 static int
719 ftpfs_check_proxy (const char *host)
721 GSList *npe;
723 if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0')
724 return 0; /* sanity check */
726 if (*host == '!')
727 return 1;
729 if (!ftpfs_always_use_proxy)
730 return 0;
732 if (strchr (host, '.') == NULL)
733 return 0;
735 ftpfs_load_no_proxy_list ();
736 for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe))
738 const char *domain = (const char *) npe->data;
740 if (domain[0] == '.')
742 size_t ld = strlen (domain);
743 size_t lh = strlen (host);
745 while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1])
747 ld--;
748 lh--;
751 if (ld == 0)
752 return 0;
754 else if (g_ascii_strcasecmp (host, domain) == 0)
755 return 0;
758 return 1;
761 /* --------------------------------------------------------------------------------------------- */
763 static void
764 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
766 vfs_url_t *url;
768 url = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
769 *host = g_strdup (url->host);
770 *port = url->port;
771 vfs_url_free (url);
774 /* --------------------------------------------------------------------------------------------- */
776 static int
777 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
779 struct addrinfo hints, *res, *curr_res;
780 int my_socket = 0;
781 char *host = NULL;
782 char port[8];
783 int tmp_port;
784 int e;
786 (void) me;
788 /* Use a proxy host? */
789 host = g_strdup (super->url->host);
791 if (host == NULL || *host == '\0')
793 vfs_print_message (_("ftpfs: Invalid host name."));
794 ftpfs_errno = EINVAL;
795 g_free (host);
796 return -1;
799 /* Hosts to connect to that start with a ! should use proxy */
800 tmp_port = super->url->port;
802 if (SUP->proxy != NULL)
803 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
805 g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_port);
806 if (port[0] == '\0')
808 g_free (host);
809 ftpfs_errno = errno;
810 return -1;
813 tty_enable_interrupt_key (); /* clear the interrupt flag */
815 memset (&hints, 0, sizeof (struct addrinfo));
816 hints.ai_family = AF_UNSPEC;
817 hints.ai_socktype = SOCK_STREAM;
819 #ifdef AI_ADDRCONFIG
820 /* By default, only look up addresses using address types for
821 * which a local interface is configured (i.e. no IPv6 if no IPv6
822 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
823 hints.ai_flags = AI_ADDRCONFIG;
824 #endif
826 e = getaddrinfo (host, port, &hints, &res);
828 #ifdef AI_ADDRCONFIG
829 if (e == EAI_BADFLAGS)
831 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
832 hints.ai_flags = 0;
833 e = getaddrinfo (host, port, &hints, &res);
835 #endif
837 *port = '\0';
839 if (e != 0)
841 tty_disable_interrupt_key ();
842 vfs_print_message (_("ftpfs: %s"), gai_strerror (e));
843 g_free (host);
844 ftpfs_errno = EINVAL;
845 return -1;
848 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
850 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
852 if (my_socket < 0)
854 if (curr_res->ai_next != NULL)
855 continue;
857 tty_disable_interrupt_key ();
858 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno));
859 g_free (host);
860 freeaddrinfo (res);
861 ftpfs_errno = errno;
862 return -1;
865 vfs_print_message (_("ftpfs: making connection to %s"), host);
866 g_free (host);
867 host = NULL;
869 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
870 break;
872 ftpfs_errno = errno;
873 close (my_socket);
875 if (errno == EINTR && tty_got_interrupt ())
876 vfs_print_message (_("ftpfs: connection interrupted by user"));
877 else if (res->ai_next == NULL)
878 vfs_print_message (_("ftpfs: connection to server failed: %s"),
879 unix_error_string (errno));
880 else
881 continue;
883 freeaddrinfo (res);
884 tty_disable_interrupt_key ();
885 return -1;
888 freeaddrinfo (res);
889 tty_disable_interrupt_key ();
890 return my_socket;
893 /* --------------------------------------------------------------------------------------------- */
895 static int
896 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
898 int retry_seconds = 0;
899 int count_down;
901 /* We do not want to use the passive if we are using proxies */
902 if (SUP->proxy)
903 SUP->use_passive_connection = ftpfs_use_passive_connections_over_proxy;
907 SUP->failed_on_login = 0;
909 SUP->sock = ftpfs_open_socket (me, super);
910 if (SUP->sock == -1)
911 return -1;
913 if (ftpfs_login_server (me, super, NULL) != 0)
915 /* Logged in, no need to retry the connection */
916 break;
918 else
920 if (!SUP->failed_on_login)
921 return -1;
923 /* Close only the socket descriptor */
924 close (SUP->sock);
926 if (ftpfs_retry_seconds != 0)
928 retry_seconds = ftpfs_retry_seconds;
929 tty_enable_interrupt_key ();
930 for (count_down = retry_seconds; count_down; count_down--)
932 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
933 count_down);
934 sleep (1);
935 if (tty_got_interrupt ())
937 /* ftpfs_errno = E; */
938 tty_disable_interrupt_key ();
939 return 0;
942 tty_disable_interrupt_key ();
946 while (retry_seconds != 0);
948 super->url->path = ftpfs_get_current_directory (me, super);
949 if (super->url->path == NULL)
950 super->url->path = g_strdup (PATH_SEP_STR);
952 return 0;
955 /* --------------------------------------------------------------------------------------------- */
957 static int
958 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
959 const char *archive_name, char *op)
961 (void) archive_name;
963 super->data = g_new0 (ftp_super_data_t, 1);
965 super->url = ftpfs_split_url (strchr (op, ':') + 1);
966 SUP->proxy = NULL;
967 if (ftpfs_check_proxy (super->url->host))
968 SUP->proxy = ftpfs_proxy_host;
969 SUP->use_passive_connection = ftpfs_use_passive_connections;
970 SUP->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
971 SUP->isbinary = TYPE_UNKNOWN;
972 SUP->remote_is_amiga = 0;
973 super->name = g_strdup ("/");
974 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755));
976 return ftpfs_open_archive_int (me, super);
979 /* --------------------------------------------------------------------------------------------- */
981 static int
982 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
983 const char *archive_name, char *op, void *cookie)
985 vfs_url_t *url;
986 int result;
988 (void) me;
989 (void) archive_name;
990 (void) cookie;
992 url = ftpfs_split_url (strchr (op, ':') + 1);
994 result = ((strcmp (url->host, super->url->host) == 0)
995 && (strcmp (url->user, super->url->user) == 0)
996 && (url->port == super->url->port)) ? 1 : 0;
998 vfs_url_free (url);
999 return result;
1002 /* --------------------------------------------------------------------------------------------- */
1003 /* The returned directory should always contain a trailing slash */
1005 static char *
1006 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
1008 char buf[BUF_8K], *bufp, *bufq;
1010 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
1011 ftpfs_get_reply (me, SUP->sock, buf, sizeof (buf)) == COMPLETE)
1013 bufp = NULL;
1014 for (bufq = buf; *bufq; bufq++)
1015 if (*bufq == '"')
1017 if (!bufp)
1019 bufp = bufq + 1;
1021 else
1023 *bufq = 0;
1024 if (*bufp)
1026 if (*(bufq - 1) != '/')
1028 *bufq++ = '/';
1029 *bufq = 0;
1031 if (*bufp == '/')
1032 return g_strdup (bufp);
1033 else
1035 /* If the remote server is an Amiga a leading slash
1036 might be missing. MC needs it because it is used
1037 as separator between hostname and path internally. */
1038 return g_strconcat ("/", bufp, (char *) NULL);
1041 else
1043 ftpfs_errno = EIO;
1044 return NULL;
1049 ftpfs_errno = EIO;
1050 return NULL;
1053 /* --------------------------------------------------------------------------------------------- */
1054 /* Setup Passive PASV FTP connection */
1056 static int
1057 ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
1058 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1060 char *c;
1061 char n[6];
1062 int xa, xb, xc, xd, xe, xf;
1064 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
1065 return 0;
1067 /* Parse remote parameters */
1068 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
1070 if (!*c)
1071 return 0;
1072 if (!isdigit ((unsigned char) *c))
1073 return 0;
1074 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1075 return 0;
1077 n[0] = (unsigned char) xa;
1078 n[1] = (unsigned char) xb;
1079 n[2] = (unsigned char) xc;
1080 n[3] = (unsigned char) xd;
1081 n[4] = (unsigned char) xe;
1082 n[5] = (unsigned char) xf;
1084 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1085 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1087 if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
1088 return 0;
1090 return 1;
1093 /* --------------------------------------------------------------------------------------------- */
1094 /* Setup Passive EPSV FTP connection */
1096 static int
1097 ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
1098 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1100 char *c;
1101 int port;
1103 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") != COMPLETE)
1104 return 0;
1106 /* (|||<port>|) */
1107 c = strchr (reply_str, '|');
1108 if (c == NULL)
1109 return 0;
1110 if (strlen (c) > 3)
1111 c += 3;
1112 else
1113 return 0;
1115 port = atoi (c);
1116 if (port < 0 || port > 65535)
1117 return 0;
1118 port = htons (port);
1120 switch (sa->ss_family)
1122 case AF_INET:
1123 ((struct sockaddr_in *) sa)->sin_port = port;
1124 break;
1125 case AF_INET6:
1126 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1127 break;
1130 return (connect (my_socket, (struct sockaddr *) sa, *salen) < 0) ? 0 : 1;
1133 /* --------------------------------------------------------------------------------------------- */
1134 /* Setup Passive ftp connection, we use it for source routed connections */
1136 static int
1137 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
1138 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1140 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1141 if (sa->ss_family == AF_INET)
1143 if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
1144 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1145 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1146 return 0;
1148 /* It's IPV6, so EPSV is our only hope */
1149 else
1151 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1152 return 0;
1155 return 1;
1158 /* --------------------------------------------------------------------------------------------- */
1159 /* Setup Active PORT or EPRT FTP connection */
1161 static int
1162 ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super,
1163 struct sockaddr_storage data_addr, socklen_t data_addrlen)
1165 unsigned short int port;
1166 char *addr;
1167 unsigned int af;
1169 switch (data_addr.ss_family)
1171 case AF_INET:
1172 af = FTP_INET;
1173 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1174 break;
1175 case AF_INET6:
1176 af = FTP_INET6;
1177 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1178 break;
1179 /* Not implemented */
1180 default:
1181 return 0;
1184 addr = g_try_malloc (NI_MAXHOST);
1185 if (addr == NULL)
1186 ERRNOR (ENOMEM, -1);
1188 if (getnameinfo
1189 ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1190 NI_NUMERICHOST) != 0)
1192 g_free (addr);
1193 ERRNOR (EIO, -1);
1196 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1197 if (af == FTP_INET)
1199 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1200 unsigned char *p = (unsigned char *) &port;
1202 if (ftpfs_command (me, super, WAIT_REPLY,
1203 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1204 p[0], p[1]) == COMPLETE)
1206 g_free (addr);
1207 return 1;
1212 * Converts network MSB first order to host byte order (LSB
1213 * first on i386). If we do it earlier, we will run into an
1214 * endianness issue, because the server actually expects to see
1215 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1217 port = ntohs (port);
1219 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1220 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
1222 g_free (addr);
1223 return 1;
1226 g_free (addr);
1227 return 0;
1230 /* --------------------------------------------------------------------------------------------- */
1231 /* Initialize a socket for FTP DATA connection */
1233 static int
1234 ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super,
1235 struct sockaddr_storage *data_addr, socklen_t * data_addrlen)
1237 int result;
1239 memset (data_addr, 0, sizeof (struct sockaddr_storage));
1240 *data_addrlen = sizeof (struct sockaddr_storage);
1242 if (SUP->use_passive_connection)
1243 result = getpeername (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1244 else
1245 result = getsockname (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1247 if (result == -1)
1248 return -1;
1250 switch (data_addr->ss_family)
1252 case AF_INET:
1253 ((struct sockaddr_in *) data_addr)->sin_port = 0;
1254 break;
1255 case AF_INET6:
1256 ((struct sockaddr_in6 *) data_addr)->sin6_port = 0;
1257 break;
1258 default:
1259 vfs_print_message (_("ftpfs: invalid address family"));
1260 ERRNOR (EINVAL, -1);
1263 result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP);
1265 if (result < 0)
1267 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1268 return -1;
1271 return result;
1274 /* --------------------------------------------------------------------------------------------- */
1275 /* Initialize FTP DATA connection */
1277 static int
1278 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1280 struct sockaddr_storage data_addr;
1281 socklen_t data_addrlen;
1284 * Don't factor socket initialization out of these conditionals,
1285 * because ftpfs_init_data_socket initializes it in different way
1286 * depending on use_passive_connection flag.
1289 /* Try to establish a passive connection first (if requested) */
1290 if (SUP->use_passive_connection)
1292 int data_sock;
1294 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1295 if (data_sock < 0)
1296 return -1;
1298 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1299 return data_sock;
1301 vfs_print_message (_("ftpfs: could not setup passive mode"));
1302 SUP->use_passive_connection = 0;
1304 close (data_sock);
1307 /* If passive setup is diabled or failed, fallback to active connections */
1308 if (!SUP->use_passive_connection)
1310 int data_sock;
1312 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1313 if (data_sock < 0)
1314 return -1;
1316 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) == 0) &&
1317 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) == 0) &&
1318 (listen (data_sock, 1) == 0) &&
1319 (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0))
1320 return data_sock;
1322 close (data_sock);
1325 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1326 SUP->use_passive_connection = SUP->proxy != NULL ? ftpfs_use_passive_connections_over_proxy :
1327 ftpfs_use_passive_connections;
1329 ftpfs_errno = EIO;
1330 return -1;
1333 /* --------------------------------------------------------------------------------------------- */
1335 static int
1336 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1337 const char *remote, int isbinary, int reget)
1339 struct sockaddr_storage from;
1340 int s, j, data;
1341 socklen_t fromlen = sizeof (from);
1343 s = ftpfs_initconn (me, super);
1344 if (s == -1)
1345 return -1;
1347 if (ftpfs_changetype (me, super, isbinary) == -1)
1348 return -1;
1349 if (reget > 0)
1351 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1352 if (j != CONTINUE)
1353 return -1;
1355 if (remote)
1357 char *remote_path = ftpfs_translate_path (me, super, remote);
1358 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1359 /* WarFtpD can't STORE //filename */
1360 (*remote_path == '/') ? remote_path + 1 : remote_path);
1361 g_free (remote_path);
1363 else
1364 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1366 if (j != PRELIM)
1367 ERRNOR (EPERM, -1);
1368 tty_enable_interrupt_key ();
1369 if (SUP->use_passive_connection)
1370 data = s;
1371 else
1373 data = accept (s, (struct sockaddr *) &from, &fromlen);
1374 if (data < 0)
1376 ftpfs_errno = errno;
1377 close (s);
1378 return -1;
1380 close (s);
1382 tty_disable_interrupt_key ();
1383 return data;
1386 /* --------------------------------------------------------------------------------------------- */
1388 static void
1389 ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t * fh)
1391 struct vfs_s_super *super = FH_SUPER;
1392 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1393 fd_set mask;
1394 char buf[BUF_8K];
1395 int dsock = FH_SOCK;
1396 FH_SOCK = -1;
1397 SUP->ctl_connection_busy = 0;
1399 vfs_print_message (_("ftpfs: aborting transfer."));
1400 if (send (SUP->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1402 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1403 if (dsock != -1)
1404 close (dsock);
1405 return;
1408 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1410 vfs_print_message (_("ftpfs: abort failed"));
1411 if (dsock != -1)
1412 close (dsock);
1413 return;
1415 if (dsock != -1)
1417 FD_ZERO (&mask);
1418 FD_SET (dsock, &mask);
1419 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1421 struct timeval start_tim, tim;
1422 gettimeofday (&start_tim, NULL);
1423 /* flush the remaining data */
1424 while (read (dsock, buf, sizeof (buf)) > 0)
1426 gettimeofday (&tim, NULL);
1427 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT)
1429 /* server keeps sending, drop the connection and ftpfs_reconnect */
1430 close (dsock);
1431 ftpfs_reconnect (me, super);
1432 return;
1436 close (dsock);
1438 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) == TRANSIENT) && (code == 426))
1439 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1442 /* --------------------------------------------------------------------------------------------- */
1444 #if 0
1445 static void
1446 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1447 struct vfs_s_inode *dir)
1449 struct linklist *flist;
1450 struct direntry *fe, *fel;
1451 char tmp[MC_MAXPATHLEN];
1452 int depth;
1454 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1455 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1457 /* flist->data->l_stat is alread initialized with 0 */
1458 fel = flist->data;
1459 if (S_ISLNK (fel->s.st_mode) && fel->linkname)
1461 if (fel->linkname[0] == '/')
1463 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1464 continue;
1465 strcpy (tmp, fel->linkname);
1467 else
1469 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1470 continue;
1471 strcpy (tmp, dir->remote_path);
1472 if (tmp[1] != '\0')
1473 strcat (tmp, "/");
1474 strcat (tmp + 1, fel->linkname);
1476 for (depth = 0; depth < 100; depth++)
1477 { /* depth protects against recursive symbolic links */
1478 canonicalize_pathname (tmp);
1479 fe = _get_file_entry (bucket, tmp, 0, 0);
1480 if (fe)
1482 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1484 /* Symlink points to link which isn't resolved, yet. */
1485 if (fe->linkname[0] == '/')
1487 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1488 break;
1489 strcpy (tmp, fe->linkname);
1491 else
1493 /* at this point tmp looks always like this
1494 /directory/filename, i.e. no need to check
1495 strrchr's return value */
1496 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1497 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1498 break;
1499 strcat (tmp, fe->linkname);
1501 continue;
1503 else
1505 fel->l_stat = g_new (struct stat, 1);
1506 if (S_ISLNK (fe->s.st_mode))
1507 *fel->l_stat = *fe->l_stat;
1508 else
1509 *fel->l_stat = fe->s;
1510 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1513 break;
1517 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1520 /* --------------------------------------------------------------------------------------------- */
1522 static void
1523 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1524 struct vfs_s_inode *dir)
1526 char buffer[2048] = "", *filename;
1527 int sock;
1528 FILE *fp;
1529 struct stat s;
1530 struct linklist *flist;
1531 struct direntry *fe;
1532 int switch_method = 0;
1534 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1535 if (strchr (dir->remote_path, ' '))
1537 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1539 vfs_print_message (_("ftpfs: CWD failed."));
1540 return;
1542 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1544 else
1545 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1547 if (sock == -1)
1549 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1550 return;
1553 fp = fdopen (sock, "r");
1554 if (fp == NULL)
1556 close (sock);
1557 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1558 return;
1560 tty_enable_interrupt_key ();
1561 flist = dir->file_list->next;
1562 while (1)
1566 if (flist == dir->file_list)
1567 goto done;
1568 fe = flist->data;
1569 flist = flist->next;
1571 while (!S_ISLNK (fe->s.st_mode));
1572 while (1)
1574 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1575 goto done;
1576 if (MEDATA->logfile)
1578 fputs (buffer, MEDATA->logfile);
1579 fflush (MEDATA->logfile);
1581 vfs_die ("This code should be commented out\n");
1582 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1584 int r = strcmp (fe->name, filename);
1585 g_free (filename);
1586 if (r == 0)
1588 if (S_ISLNK (s.st_mode))
1590 /* This server doesn't understand LIST -lLa */
1591 switch_method = 1;
1592 goto done;
1594 fe->l_stat = g_new (struct stat, 1);
1595 if (fe->l_stat == NULL)
1596 goto done;
1597 *fe->l_stat = s;
1598 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1599 break;
1601 if (r < 0)
1602 break;
1606 done:
1607 while (fgets (buffer, sizeof (buffer), fp) != NULL);
1608 tty_disable_interrupt_key ();
1609 fclose (fp);
1610 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1613 /* --------------------------------------------------------------------------------------------- */
1615 static void
1616 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1618 vfs_print_message (_("Resolving symlink..."));
1620 if (SUP->strict_rfc959_list_cmd)
1621 resolve_symlink_without_ls_options (me, super, dir);
1622 else
1623 resolve_symlink_with_ls_options (me, super, dir);
1625 #endif
1627 /* --------------------------------------------------------------------------------------------- */
1629 static int
1630 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1632 struct vfs_s_entry *ent;
1633 struct vfs_s_super *super = dir->super;
1634 int sock, num_entries = 0;
1635 char lc_buffer[BUF_8K];
1636 int cd_first;
1638 cd_first = ftpfs_first_cd_then_ls || (SUP->strict == RFC_STRICT)
1639 || (strchr (remote_path, ' ') != NULL);
1641 again:
1642 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1643 remote_path,
1644 SUP->strict ==
1645 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1647 if (cd_first)
1649 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1651 ftpfs_errno = ENOENT;
1652 vfs_print_message (_("ftpfs: CWD failed."));
1653 return -1;
1657 gettimeofday (&dir->timestamp, NULL);
1658 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1660 if (SUP->strict == RFC_STRICT)
1661 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1662 else if (cd_first)
1663 /* Dirty hack to avoid autoprepending / to . */
1664 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1665 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1666 else
1668 /* Trailing "/." is necessary if remote_path is a symlink */
1669 char *path = concat_dir_and_file (remote_path, ".");
1670 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1671 g_free (path);
1674 if (sock == -1)
1675 goto fallback;
1677 /* Clear the interrupt flag */
1678 tty_enable_interrupt_key ();
1680 while (1)
1682 int i;
1683 int res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer),
1684 sock);
1685 if (!res)
1686 break;
1688 if (res == EINTR)
1690 me->verrno = ECONNRESET;
1691 close (sock);
1692 tty_disable_interrupt_key ();
1693 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1694 vfs_print_message (_("%s: failure"), me->name);
1695 return -1;
1698 if (MEDATA->logfile)
1700 fputs (lc_buffer, MEDATA->logfile);
1701 fputs ("\n", MEDATA->logfile);
1702 fflush (MEDATA->logfile);
1705 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1706 i = ent->ino->st.st_nlink;
1707 if (!vfs_parse_ls_lga (lc_buffer, &ent->ino->st, &ent->name, &ent->ino->linkname))
1709 vfs_s_free_entry (me, ent);
1710 continue;
1712 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1713 num_entries++;
1714 vfs_s_insert_entry (me, dir, ent);
1717 close (sock);
1718 me->verrno = E_REMOTE;
1719 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1720 goto fallback;
1722 if (num_entries == 0 && cd_first == 0)
1724 /* The LIST command may produce an empty output. In such scenario
1725 it is not clear whether this is caused by `remote_path' being
1726 a non-existent path or for some other reason (listing emtpy
1727 directory without the -a option, non-readable directory, etc.).
1729 Since `dir_load' is a crucial method, when it comes to determine
1730 whether a given path is a _directory_, the code must try its best
1731 to determine the type of `remote_path'. The only reliable way to
1732 achieve this is trough issuing a CWD command. */
1734 cd_first = 1;
1735 goto again;
1738 if (SUP->strict == RFC_AUTODETECT)
1739 SUP->strict = RFC_DARING;
1741 vfs_print_message (_("%s: done."), me->name);
1742 return 0;
1744 fallback:
1745 if (SUP->strict == RFC_AUTODETECT)
1747 /* It's our first attempt to get a directory listing from this
1748 server (UNIX style LIST command) */
1749 SUP->strict = RFC_STRICT;
1750 /* I hate goto, but recursive call needs another 8K on stack */
1751 /* return ftpfs_dir_load (me, dir, remote_path); */
1752 cd_first = 1;
1753 goto again;
1755 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1756 ERRNOR (EACCES, -1);
1759 /* --------------------------------------------------------------------------------------------- */
1761 static int
1762 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname)
1764 int h, sock, n_read, n_written;
1765 off_t n_stored;
1766 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1767 struct linger li;
1768 #else
1769 int flag_one = 1;
1770 #endif
1771 char lc_buffer[BUF_8K];
1772 struct stat s;
1773 char *w_buf;
1774 struct vfs_s_super *super = FH_SUPER;
1775 ftp_fh_data_t *ftp = (ftp_fh_data_t *) fh->data;
1777 h = open (localname, O_RDONLY);
1778 if (h == -1)
1779 ERRNOR (EIO, -1);
1781 sock =
1782 ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1783 if (sock < 0 || fstat (h, &s) == -1)
1785 close (h);
1786 return -1;
1788 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1789 li.l_onoff = 1;
1790 li.l_linger = 120;
1791 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1792 #else
1793 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1794 #endif
1795 n_stored = 0;
1797 tty_enable_interrupt_key ();
1798 while (TRUE)
1800 while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1802 if (errno == EINTR)
1804 if (tty_got_interrupt ())
1806 ftpfs_errno = EINTR;
1807 goto error_return;
1809 else
1810 continue;
1812 ftpfs_errno = errno;
1813 goto error_return;
1815 if (n_read == 0)
1816 break;
1817 n_stored += n_read;
1818 w_buf = lc_buffer;
1819 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1821 if (n_written == -1)
1823 if (errno == EINTR && !tty_got_interrupt ())
1825 continue;
1827 ftpfs_errno = errno;
1828 goto error_return;
1830 w_buf += n_written;
1831 n_read -= n_written;
1833 vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
1834 _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
1836 tty_disable_interrupt_key ();
1837 close (sock);
1838 close (h);
1839 if (ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE)
1840 ERRNOR (EIO, -1);
1841 return 0;
1842 error_return:
1843 tty_disable_interrupt_key ();
1844 close (sock);
1845 close (h);
1846 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1847 return -1;
1850 /* --------------------------------------------------------------------------------------------- */
1852 static int
1853 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset)
1855 char *name;
1857 if (fh->data == NULL)
1858 fh->data = g_new0 (ftp_fh_data_t, 1);
1860 name = vfs_s_fullpath (me, fh->ino);
1861 if (name == NULL)
1862 return 0;
1863 FH_SOCK = ftpfs_open_data_connection (me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1864 g_free (name);
1865 if (FH_SOCK == -1)
1866 ERRNOR (EACCES, 0);
1867 fh->linear = LS_LINEAR_OPEN;
1868 ((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy = 1;
1869 ((ftp_fh_data_t *) fh->data)->append = 0;
1870 return 1;
1873 /* --------------------------------------------------------------------------------------------- */
1875 static int
1876 ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t * fh, void *buf, size_t len)
1878 ssize_t n;
1879 struct vfs_s_super *super = FH_SUPER;
1881 while ((n = read (FH_SOCK, buf, len)) < 0)
1883 if ((errno == EINTR) && !tty_got_interrupt ())
1884 continue;
1885 break;
1888 if (n < 0)
1889 ftpfs_linear_abort (me, fh);
1891 if (n == 0)
1893 SUP->ctl_connection_busy = 0;
1894 close (FH_SOCK);
1895 FH_SOCK = -1;
1896 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1897 ERRNOR (E_REMOTE, -1);
1898 return 0;
1900 ERRNOR (errno, n);
1903 /* --------------------------------------------------------------------------------------------- */
1905 static void
1906 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t * fh)
1908 if (FH_SOCK != -1)
1909 ftpfs_linear_abort (me, fh);
1912 /* --------------------------------------------------------------------------------------------- */
1914 static int
1915 ftpfs_ctl (void *fh, int ctlop, void *arg)
1917 (void) arg;
1919 switch (ctlop)
1921 case VFS_CTL_IS_NOTREADY:
1923 int v;
1925 if (!FH->linear)
1926 vfs_die ("You may not do this");
1927 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1928 return 0;
1930 v = vfs_s_select_on_two (((ftp_fh_data_t *) (FH->data))->sock, 0);
1931 return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
1933 default:
1934 return 0;
1938 /* --------------------------------------------------------------------------------------------- */
1940 static int
1941 ftpfs_send_command (struct vfs_class *me, const char *filename, const char *cmd, int flags)
1943 const char *rpath;
1944 char *p, *mpath;
1945 struct vfs_s_super *super;
1946 int r;
1947 int flush_directory_cache = (flags & OPT_FLUSH);
1949 mpath = g_strdup (filename);
1950 rpath = vfs_s_get_path_mangle (me, mpath, &super, 0);
1951 if (rpath == NULL)
1953 g_free (mpath);
1954 return -1;
1956 p = ftpfs_translate_path (me, super, rpath);
1957 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1958 g_free (p);
1959 vfs_stamp_create (&vfs_ftpfs_ops, super);
1960 if (flags & OPT_IGNORE_ERROR)
1961 r = COMPLETE;
1962 if (r != COMPLETE)
1964 me->verrno = EPERM;
1965 g_free (mpath);
1966 return -1;
1968 if (flush_directory_cache)
1969 vfs_s_invalidate (me, super);
1970 g_free (mpath);
1971 return 0;
1974 /* --------------------------------------------------------------------------------------------- */
1976 static int
1977 ftpfs_chmod (const vfs_path_t * vpath, int mode)
1979 char buf[BUF_SMALL];
1980 int ret;
1981 vfs_path_element_t *path_element;
1983 path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
1985 g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1987 ret = ftpfs_send_command (path_element->class, vpath->unparsed, 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 vfs_path_element_t *path_element;
2021 path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
2022 return ftpfs_send_command (path_element->class, vpath->unparsed, "DELE /%s", OPT_FLUSH);
2025 /* --------------------------------------------------------------------------------------------- */
2027 /* Return 1 if path is the same directory as the one we are in now */
2028 static int
2029 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
2031 (void) me;
2033 if (super->url->path == NULL)
2034 return FALSE;
2035 return (strcmp (path, super->url->path) == 0);
2038 /* --------------------------------------------------------------------------------------------- */
2040 static int
2041 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
2043 int r;
2044 char *p;
2046 if (!SUP->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
2047 return COMPLETE;
2049 p = ftpfs_translate_path (me, super, remote_path);
2050 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
2051 g_free (p);
2053 if (r != COMPLETE)
2054 ftpfs_errno = EIO;
2055 else
2057 g_free (super->url->path);
2058 super->url->path = g_strdup (remote_path);
2059 SUP->cwd_deferred = 0;
2061 return r;
2064 /* --------------------------------------------------------------------------------------------- */
2066 static int
2067 ftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
2069 vfs_path_element_t *path_element;
2071 path_element = vfs_path_get_by_index (vpath1, vfs_path_length (vpath1) - 1);
2072 ftpfs_send_command (path_element->class, vpath1->unparsed, "RNFR /%s", OPT_FLUSH);
2073 return ftpfs_send_command (path_element->class, vpath2->unparsed, "RNTO /%s", OPT_FLUSH);
2076 /* --------------------------------------------------------------------------------------------- */
2078 static int
2079 ftpfs_mkdir (const vfs_path_t * vpath, mode_t mode)
2081 vfs_path_element_t *path_element;
2082 (void) mode; /* FIXME: should be used */
2084 path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
2085 return ftpfs_send_command (path_element->class, vpath->unparsed, "MKD /%s", OPT_FLUSH);
2088 /* --------------------------------------------------------------------------------------------- */
2090 static int
2091 ftpfs_rmdir (const vfs_path_t * vpath)
2093 vfs_path_element_t *path_element;
2095 path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
2096 return ftpfs_send_command (path_element->class, vpath->unparsed, "RMD /%s", OPT_FLUSH);
2099 /* --------------------------------------------------------------------------------------------- */
2101 static int
2102 ftpfs_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
2104 ftp_fh_data_t *ftp;
2106 (void) mode;
2108 fh->data = g_new0 (ftp_fh_data_t, 1);
2109 ftp = (ftp_fh_data_t *) fh->data;
2110 /* File will be written only, so no need to retrieve it from ftp server */
2111 if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
2113 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2114 struct linger li;
2115 #else
2116 int li = 1;
2117 #endif
2118 char *name;
2120 /* ftpfs_linear_start() called, so data will be written
2121 * to local temporary file and stored to ftp server
2122 * by vfs_s_close later
2124 if (((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy)
2126 if (!fh->ino->localname)
2128 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
2129 fh->ino->ent->name);
2130 if (handle == -1)
2131 goto fail;
2132 close (handle);
2133 ftp->append = flags & O_APPEND;
2135 return 0;
2137 name = vfs_s_fullpath (me, fh->ino);
2138 if (name == NULL)
2139 goto fail;
2140 fh->handle =
2141 ftpfs_open_data_connection (me, fh->ino->super,
2142 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
2143 g_free (name);
2145 if (fh->handle < 0)
2146 goto fail;
2147 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2148 li.l_onoff = 1;
2149 li.l_linger = 120;
2150 #endif
2151 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
2153 if (fh->ino->localname)
2155 unlink (fh->ino->localname);
2156 g_free (fh->ino->localname);
2157 fh->ino->localname = NULL;
2159 return 0;
2162 if (!fh->ino->localname && vfs_s_retrieve_file (me, fh->ino) == -1)
2163 goto fail;
2164 if (!fh->ino->localname)
2165 vfs_die ("retrieve_file failed to fill in localname");
2166 return 0;
2168 fail:
2169 g_free (fh->data);
2170 return -1;
2173 /* --------------------------------------------------------------------------------------------- */
2175 static int
2176 ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t * fh)
2178 if (fh->handle != -1 && !fh->ino->localname)
2180 ftp_super_data_t *ftp = (ftp_super_data_t *) fh->ino->super->data;
2182 close (fh->handle);
2183 fh->handle = -1;
2184 /* File is stored to destination already, so
2185 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2187 fh->changed = 0;
2188 if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE)
2189 ERRNOR (EIO, -1);
2190 vfs_s_invalidate (me, FH_SUPER);
2193 g_free (fh->data);
2195 return 0;
2198 /* --------------------------------------------------------------------------------------------- */
2200 static void
2201 ftpfs_done (struct vfs_class *me)
2203 (void) me;
2205 g_slist_foreach (no_proxy, (GFunc) g_free, NULL);
2206 g_slist_free (no_proxy);
2208 g_free (ftpfs_anonymous_passwd);
2209 g_free (ftpfs_proxy_host);
2212 /* --------------------------------------------------------------------------------------------- */
2214 static void
2215 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
2217 GList *iter;
2219 for (iter = MEDATA->supers; iter != NULL; iter = g_list_next (iter))
2221 const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
2222 char *name;
2224 name = g_strconcat ("/#ftp:", super->url->user, "@", super->url->host, "/",
2225 super->url->path, (char *) NULL);
2226 func (name);
2227 g_free (name);
2231 /* --------------------------------------------------------------------------------------------- */
2233 static keyword_t
2234 ftpfs_netrc_next (void)
2236 char *p;
2237 keyword_t i;
2238 static const char *const keywords[] = { "default", "machine",
2239 "login", "password", "passwd", "account", "macdef", NULL
2242 while (1)
2244 netrcp = skip_separators (netrcp);
2245 if (*netrcp != '\n')
2246 break;
2247 netrcp++;
2249 if (!*netrcp)
2250 return NETRC_NONE;
2251 p = buffer;
2252 if (*netrcp == '"')
2254 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++)
2256 if (*netrcp == '\\')
2257 netrcp++;
2258 *p++ = *netrcp;
2261 else
2263 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
2264 *netrcp != ',' && *netrcp; netrcp++)
2266 if (*netrcp == '\\')
2267 netrcp++;
2268 *p++ = *netrcp;
2271 *p = 0;
2272 if (!*buffer)
2273 return NETRC_NONE;
2275 for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2276 if (strcmp (keywords[i - 1], buffer) == 0)
2277 return i;
2279 return NETRC_UNKNOWN;
2282 /* --------------------------------------------------------------------------------------------- */
2284 static int
2285 ftpfs_netrc_bad_mode (const char *netrcname)
2287 static int be_angry = 1;
2288 struct stat mystat;
2290 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077))
2292 if (be_angry)
2294 message (D_ERROR, MSG_ERROR,
2295 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2296 be_angry = 0;
2298 return 1;
2300 return 0;
2303 /* --------------------------------------------------------------------------------------------- */
2304 /* Scan .netrc until we find matching "machine" or "default"
2305 * domain is used for additional matching
2306 * No search is done after "default" in compliance with "man netrc"
2307 * Return 0 if found, -1 otherwise */
2309 static int
2310 ftpfs_find_machine (const char *host, const char *domain)
2312 keyword_t keyword;
2314 if (!host)
2315 host = "";
2316 if (!domain)
2317 domain = "";
2319 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2321 if (keyword == NETRC_DEFAULT)
2322 return 0;
2324 if (keyword == NETRC_MACDEF)
2326 /* Scan for an empty line, which concludes "macdef" */
2329 while (*netrcp && *netrcp != '\n')
2330 netrcp++;
2331 if (*netrcp != '\n')
2332 break;
2333 netrcp++;
2335 while (*netrcp && *netrcp != '\n');
2336 continue;
2339 if (keyword != NETRC_MACHINE)
2340 continue;
2342 /* Take machine name */
2343 if (ftpfs_netrc_next () == NETRC_NONE)
2344 break;
2346 if (g_ascii_strcasecmp (host, buffer) != 0)
2348 /* Try adding our domain to short names in .netrc */
2349 const char *host_domain = strchr (host, '.');
2350 if (!host_domain)
2351 continue;
2353 /* Compare domain part */
2354 if (g_ascii_strcasecmp (host_domain, domain) != 0)
2355 continue;
2357 /* Compare local part */
2358 if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2359 continue;
2362 return 0;
2365 /* end of .netrc */
2366 return -1;
2369 /* --------------------------------------------------------------------------------------------- */
2370 /* Extract login and password from .netrc for the host.
2371 * pass may be NULL.
2372 * Returns 0 for success, -1 for error */
2374 static int
2375 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2377 char *netrcname;
2378 char *tmp_pass = NULL;
2379 char hostname[MAXHOSTNAMELEN];
2380 const char *domain;
2381 keyword_t keyword;
2382 static struct rupcache
2384 struct rupcache *next;
2385 char *host;
2386 char *login;
2387 char *pass;
2388 } *rup_cache = NULL, *rupp;
2390 /* Initialize *login and *pass */
2391 g_free (*login);
2392 *login = NULL;
2393 g_free (*pass);
2394 *pass = NULL;
2396 /* Look up in the cache first */
2397 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2399 if (!strcmp (host, rupp->host))
2401 if (rupp->login)
2402 *login = g_strdup (rupp->login);
2403 if (pass && rupp->pass)
2404 *pass = g_strdup (rupp->pass);
2405 return 0;
2409 /* Load current .netrc */
2410 netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2411 if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2413 g_free (netrcname);
2414 return 0;
2417 netrcp = netrc;
2419 /* Find our own domain name */
2420 if (gethostname (hostname, sizeof (hostname)) < 0)
2421 *hostname = '\0';
2423 domain = strchr (hostname, '.');
2424 if (domain == NULL)
2425 domain = "";
2427 /* Scan for "default" and matching "machine" keywords */
2428 ftpfs_find_machine (host, domain);
2430 /* Scan for keywords following "default" and "machine" */
2431 while (1)
2433 int need_break = 0;
2434 keyword = ftpfs_netrc_next ();
2436 switch (keyword)
2438 case NETRC_LOGIN:
2439 if (ftpfs_netrc_next () == NETRC_NONE)
2441 need_break = 1;
2442 break;
2445 /* We have another name already - should not happen */
2446 if (*login)
2448 need_break = 1;
2449 break;
2452 /* We have login name now */
2453 *login = g_strdup (buffer);
2454 break;
2456 case NETRC_PASSWORD:
2457 case NETRC_PASSWD:
2458 if (ftpfs_netrc_next () == NETRC_NONE)
2460 need_break = 1;
2461 break;
2464 /* Ignore unsafe passwords */
2465 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2466 && ftpfs_netrc_bad_mode (netrcname))
2468 need_break = 1;
2469 break;
2472 /* Remember password. pass may be NULL, so use tmp_pass */
2473 if (tmp_pass == NULL)
2474 tmp_pass = g_strdup (buffer);
2475 break;
2477 case NETRC_ACCOUNT:
2478 /* "account" is followed by a token which we ignore */
2479 if (ftpfs_netrc_next () == NETRC_NONE)
2481 need_break = 1;
2482 break;
2485 /* Ignore account, but warn user anyways */
2486 ftpfs_netrc_bad_mode (netrcname);
2487 break;
2489 default:
2490 /* Unexpected keyword or end of file */
2491 need_break = 1;
2492 break;
2495 if (need_break)
2496 break;
2499 g_free (netrc);
2500 g_free (netrcname);
2502 rupp = g_new (struct rupcache, 1);
2503 rupp->host = g_strdup (host);
2504 rupp->login = g_strdup (*login);
2505 rupp->pass = g_strdup (tmp_pass);
2507 rupp->next = rup_cache;
2508 rup_cache = rupp;
2510 *pass = tmp_pass;
2512 return 0;
2515 /* --------------------------------------------------------------------------------------------- */
2516 /*** public functions ****************************************************************************/
2517 /* --------------------------------------------------------------------------------------------- */
2519 /** This routine is called as the last step in load_setup */
2520 void
2521 ftpfs_init_passwd (void)
2523 ftpfs_anonymous_passwd = load_anon_passwd ();
2524 if (ftpfs_anonymous_passwd)
2525 return;
2527 /* If there is no anonymous ftp password specified
2528 * then we'll just use anonymous@
2529 * We don't send any other thing because:
2530 * - We want to remain anonymous
2531 * - We want to stop SPAM
2532 * - We don't want to let ftp sites to discriminate by the user,
2533 * host or country.
2535 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
2538 /* --------------------------------------------------------------------------------------------- */
2540 void
2541 init_ftpfs (void)
2543 static struct vfs_s_subclass ftpfs_subclass;
2545 tcp_init ();
2547 ftpfs_subclass.flags = VFS_S_REMOTE;
2548 ftpfs_subclass.archive_same = ftpfs_archive_same;
2549 ftpfs_subclass.open_archive = ftpfs_open_archive;
2550 ftpfs_subclass.free_archive = ftpfs_free_archive;
2551 ftpfs_subclass.fh_open = ftpfs_fh_open;
2552 ftpfs_subclass.fh_close = ftpfs_fh_close;
2553 ftpfs_subclass.dir_load = ftpfs_dir_load;
2554 ftpfs_subclass.file_store = ftpfs_file_store;
2555 ftpfs_subclass.linear_start = ftpfs_linear_start;
2556 ftpfs_subclass.linear_read = ftpfs_linear_read;
2557 ftpfs_subclass.linear_close = ftpfs_linear_close;
2559 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2560 vfs_ftpfs_ops.name = "ftpfs";
2561 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2562 vfs_ftpfs_ops.prefix = "ftp:";
2563 vfs_ftpfs_ops.done = &ftpfs_done;
2564 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2565 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2566 vfs_ftpfs_ops.chown = ftpfs_chown;
2567 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2568 vfs_ftpfs_ops.rename = ftpfs_rename;
2569 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2570 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2571 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2572 vfs_register_class (&vfs_ftpfs_ops);
2575 /* --------------------------------------------------------------------------------------------- */