Ticket #2361: VFS URI reimplementation
[midnight-commander.git] / src / vfs / ftpfs / ftpfs.c
blobb9b2ed19021c66798313f0a41694ba86557a429a
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_correct_url_parameters (const vfs_path_element_t * velement)
327 vfs_path_element_t *path_element = vfs_path_element_clone (velement);
329 if (path_element->port == 0)
330 path_element->port = FTP_COMMAND_PORT;
332 if (path_element->user == NULL)
334 /* Look up user and password in netrc */
335 if (ftpfs_use_netrc)
336 ftpfs_netrc_lookup (path_element->host, &path_element->user, &path_element->password);
338 if (path_element->user == NULL)
339 path_element->user = g_strdup ("anonymous");
341 /* Look up password in netrc for known user */
342 if (ftpfs_use_netrc && path_element->user != NULL && path_element->password != NULL)
344 char *new_user = NULL;
345 char *new_passwd = NULL;
347 ftpfs_netrc_lookup (path_element->host, &new_user, &new_passwd);
349 /* If user is different, remove password */
350 if (new_user != NULL && strcmp (path_element->user, new_user) != 0)
352 g_free (path_element->password);
353 path_element->password = NULL;
356 g_free (new_user);
357 g_free (new_passwd);
360 return path_element;
363 /* --------------------------------------------------------------------------------------------- */
364 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
366 static int
367 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
369 char answer[BUF_1K];
370 int i;
372 for (;;)
374 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
376 if (string_buf)
377 *string_buf = 0;
378 code = 421;
379 return 4;
381 switch (sscanf (answer, "%d", &code))
383 case 0:
384 if (string_buf)
385 g_strlcpy (string_buf, answer, string_len);
386 code = 500;
387 return 5;
388 case 1:
389 if (answer[3] == '-')
391 while (1)
393 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
395 if (string_buf)
396 *string_buf = 0;
397 code = 421;
398 return 4;
400 if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' '))
401 break;
404 if (string_buf)
405 g_strlcpy (string_buf, answer, string_len);
406 return code / 100;
411 /* --------------------------------------------------------------------------------------------- */
413 static int
414 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
416 int sock;
418 sock = ftpfs_open_socket (me, super);
419 if (sock != -1)
421 char *cwdir = super->path_element->path;
423 close (SUP->sock);
424 SUP->sock = sock;
425 super->path_element->path = NULL;
428 if (ftpfs_login_server (me, super, super->path_element->password) != 0)
430 if (cwdir == NULL)
431 return 1;
432 sock = ftpfs_chdir_internal (me, super, cwdir);
433 g_free (cwdir);
434 return sock == COMPLETE ? 1 : 0;
437 super->path_element->path = cwdir;
440 return 0;
443 /* --------------------------------------------------------------------------------------------- */
445 static int
446 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt,
447 ...)
449 va_list ap;
450 char *cmdstr;
451 int status, cmdlen;
452 static int retry = 0;
453 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
455 va_start (ap, fmt);
456 cmdstr = g_strdup_vprintf (fmt, ap);
457 va_end (ap);
459 cmdlen = strlen (cmdstr);
460 cmdstr = g_realloc (cmdstr, cmdlen + 3);
461 strcpy (cmdstr + cmdlen, "\r\n");
462 cmdlen += 2;
464 if (MEDATA->logfile)
466 if (strncmp (cmdstr, "PASS ", 5) == 0)
468 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
470 else
472 size_t ret;
473 ret = fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
476 fflush (MEDATA->logfile);
479 got_sigpipe = 0;
480 tty_enable_interrupt_key ();
481 status = write (SUP->sock, cmdstr, cmdlen);
483 if (status < 0)
485 code = 421;
487 if (errno == EPIPE)
488 { /* Remote server has closed connection */
489 if (level == 0)
491 level = 1;
492 status = ftpfs_reconnect (me, super);
493 level = 0;
494 if (status && (write (SUP->sock, cmdstr, cmdlen) > 0))
496 goto ok;
500 got_sigpipe = 1;
502 g_free (cmdstr);
503 tty_disable_interrupt_key ();
504 return TRANSIENT;
506 retry = 0;
508 tty_disable_interrupt_key ();
510 if (wait_reply)
512 status = ftpfs_get_reply (me, SUP->sock,
513 (wait_reply & WANT_STRING) ? reply_str : NULL,
514 sizeof (reply_str) - 1);
515 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
517 retry = 1;
518 level = 1;
519 status = ftpfs_reconnect (me, super);
520 level = 0;
521 if (status && (write (SUP->sock, cmdstr, cmdlen) > 0))
523 goto ok;
526 retry = 0;
527 g_free (cmdstr);
528 return status;
530 g_free (cmdstr);
531 return COMPLETE;
534 /* --------------------------------------------------------------------------------------------- */
536 static void
537 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
539 if (SUP->sock != -1)
541 vfs_print_message (_("ftpfs: Disconnecting from %s"), super->path_element->host);
542 ftpfs_command (me, super, NONE, "QUIT");
543 close (SUP->sock);
545 g_free (super->data);
546 super->data = NULL;
549 /* --------------------------------------------------------------------------------------------- */
551 static int
552 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
554 if (binary != SUP->isbinary)
556 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
557 ERRNOR (EIO, -1);
558 SUP->isbinary = binary;
560 return binary;
563 /* --------------------------------------------------------------------------------------------- */
564 /* This routine logs the user in */
566 static int
567 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
569 char *pass;
570 char *op;
571 char *name; /* login user name */
572 int anon = 0;
573 char reply_string[BUF_MEDIUM];
575 SUP->isbinary = TYPE_UNKNOWN;
577 if (super->path_element->password != NULL) /* explicit password */
578 op = g_strdup (super->path_element->password);
579 else if (netrcpass != NULL) /* password from netrc */
580 op = g_strdup (netrcpass);
581 else if (strcmp (super->path_element->user, "anonymous") == 0
582 || strcmp (super->path_element->user, "ftp") == 0)
584 if (ftpfs_anonymous_passwd == NULL) /* default anonymous password */
585 ftpfs_init_passwd ();
586 op = g_strdup (ftpfs_anonymous_passwd);
587 anon = 1;
589 else
590 { /* ask user */
591 char *p;
593 p = g_strdup_printf (_("FTP: Password required for %s"), super->path_element->user);
594 op = vfs_get_password (p);
595 g_free (p);
596 if (op == NULL)
597 ERRNOR (EPERM, 0);
598 super->path_element->password = g_strdup (op);
601 if (!anon || MEDATA->logfile)
602 pass = op;
603 else
605 pass = g_strconcat ("-", op, (char *) NULL);
606 wipe_password (op);
609 /* Proxy server accepts: username@host-we-want-to-connect */
610 if (SUP->proxy)
611 name =
612 g_strconcat (super->path_element->user, "@",
613 super->path_element->host[0] ==
614 '!' ? super->path_element->host + 1 : super->path_element->host,
615 (char *) NULL);
616 else
617 name = g_strdup (super->path_element->user);
619 if (ftpfs_get_reply (me, SUP->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
621 char *reply_up;
623 reply_up = g_ascii_strup (reply_string, -1);
624 SUP->remote_is_amiga = strstr (reply_up, "AMIGA") != 0;
625 g_free (reply_up);
627 if (MEDATA->logfile)
629 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n", SUP->remote_is_amiga);
630 fflush (MEDATA->logfile);
633 vfs_print_message (_("ftpfs: sending login name"));
635 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
637 case CONTINUE:
638 vfs_print_message (_("ftpfs: sending user password"));
639 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
640 if (code == CONTINUE)
642 char *p;
644 p = g_strdup_printf (_("FTP: Account required for user %s"),
645 super->path_element->user);
646 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
647 g_free (p);
648 if (op == NULL)
649 ERRNOR (EPERM, 0);
650 vfs_print_message (_("ftpfs: sending user account"));
651 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
652 g_free (op);
654 if (code != COMPLETE)
655 break;
656 /* fall through */
658 case COMPLETE:
659 vfs_print_message (_("ftpfs: logged in"));
660 wipe_password (pass);
661 g_free (name);
662 return 1;
664 default:
665 SUP->failed_on_login = 1;
666 wipe_password (super->path_element->password);
667 super->path_element->password = NULL;
669 goto login_fail;
673 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
674 super->path_element->user);
676 login_fail:
677 wipe_password (pass);
678 g_free (name);
679 ERRNOR (EPERM, 0);
682 /* --------------------------------------------------------------------------------------------- */
684 static void
685 ftpfs_load_no_proxy_list (void)
687 /* FixMe: shouldn't be hardcoded!!! */
688 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
689 FILE *npf;
690 int c;
691 char *p;
692 static char *mc_file = NULL;
694 mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL);
695 if (exist_file (mc_file))
697 npf = fopen (mc_file, "r");
698 if (npf != NULL)
700 while (fgets (s, sizeof (s), npf) != NULL)
702 p = strchr (s, '\n');
703 if (p == NULL) /* skip bogus entries */
705 while ((c = fgetc (npf)) != EOF && c != '\n')
707 continue;
710 if (p != s)
712 *p = '\0';
713 no_proxy = g_slist_prepend (no_proxy, g_strdup (s));
716 fclose (npf);
719 g_free (mc_file);
722 /* --------------------------------------------------------------------------------------------- */
723 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
725 static int
726 ftpfs_check_proxy (const char *host)
728 GSList *npe;
730 if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0')
731 return 0; /* sanity check */
733 if (*host == '!')
734 return 1;
736 if (!ftpfs_always_use_proxy)
737 return 0;
739 if (strchr (host, '.') == NULL)
740 return 0;
742 ftpfs_load_no_proxy_list ();
743 for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe))
745 const char *domain = (const char *) npe->data;
747 if (domain[0] == '.')
749 size_t ld = strlen (domain);
750 size_t lh = strlen (host);
752 while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1])
754 ld--;
755 lh--;
758 if (ld == 0)
759 return 0;
761 else if (g_ascii_strcasecmp (host, domain) == 0)
762 return 0;
765 return 1;
768 /* --------------------------------------------------------------------------------------------- */
770 static void
771 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
773 vfs_path_element_t *path_element;
775 path_element = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
776 *host = g_strdup (path_element->host);
777 *port = path_element->port;
778 vfs_path_element_free (path_element);
781 /* --------------------------------------------------------------------------------------------- */
783 static int
784 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
786 struct addrinfo hints, *res, *curr_res;
787 int my_socket = 0;
788 char *host = NULL;
789 char port[8];
790 int tmp_port;
791 int e;
793 (void) me;
795 /* Use a proxy host? */
796 host = g_strdup (super->path_element->host);
798 if (host == NULL || *host == '\0')
800 vfs_print_message (_("ftpfs: Invalid host name."));
801 ftpfs_errno = EINVAL;
802 g_free (host);
803 return -1;
806 /* Hosts to connect to that start with a ! should use proxy */
807 tmp_port = super->path_element->port;
809 if (SUP->proxy != NULL)
810 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
812 g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_port);
813 if (port[0] == '\0')
815 g_free (host);
816 ftpfs_errno = errno;
817 return -1;
820 tty_enable_interrupt_key (); /* clear the interrupt flag */
822 memset (&hints, 0, sizeof (struct addrinfo));
823 hints.ai_family = AF_UNSPEC;
824 hints.ai_socktype = SOCK_STREAM;
826 #ifdef AI_ADDRCONFIG
827 /* By default, only look up addresses using address types for
828 * which a local interface is configured (i.e. no IPv6 if no IPv6
829 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
830 hints.ai_flags = AI_ADDRCONFIG;
831 #endif
833 e = getaddrinfo (host, port, &hints, &res);
835 #ifdef AI_ADDRCONFIG
836 if (e == EAI_BADFLAGS)
838 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
839 hints.ai_flags = 0;
840 e = getaddrinfo (host, port, &hints, &res);
842 #endif
844 *port = '\0';
846 if (e != 0)
848 tty_disable_interrupt_key ();
849 vfs_print_message (_("ftpfs: %s"), gai_strerror (e));
850 g_free (host);
851 ftpfs_errno = EINVAL;
852 return -1;
855 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
857 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
859 if (my_socket < 0)
861 if (curr_res->ai_next != NULL)
862 continue;
864 tty_disable_interrupt_key ();
865 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno));
866 g_free (host);
867 freeaddrinfo (res);
868 ftpfs_errno = errno;
869 return -1;
872 vfs_print_message (_("ftpfs: making connection to %s"), host);
873 g_free (host);
874 host = NULL;
876 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
877 break;
879 ftpfs_errno = errno;
880 close (my_socket);
882 if (errno == EINTR && tty_got_interrupt ())
883 vfs_print_message (_("ftpfs: connection interrupted by user"));
884 else if (res->ai_next == NULL)
885 vfs_print_message (_("ftpfs: connection to server failed: %s"),
886 unix_error_string (errno));
887 else
888 continue;
890 freeaddrinfo (res);
891 tty_disable_interrupt_key ();
892 return -1;
895 freeaddrinfo (res);
896 tty_disable_interrupt_key ();
897 return my_socket;
900 /* --------------------------------------------------------------------------------------------- */
902 static int
903 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
905 int retry_seconds = 0;
906 int count_down;
908 /* We do not want to use the passive if we are using proxies */
909 if (SUP->proxy)
910 SUP->use_passive_connection = ftpfs_use_passive_connections_over_proxy;
914 SUP->failed_on_login = 0;
916 SUP->sock = ftpfs_open_socket (me, super);
917 if (SUP->sock == -1)
918 return -1;
920 if (ftpfs_login_server (me, super, NULL) != 0)
922 /* Logged in, no need to retry the connection */
923 break;
925 else
927 if (!SUP->failed_on_login)
928 return -1;
930 /* Close only the socket descriptor */
931 close (SUP->sock);
933 if (ftpfs_retry_seconds != 0)
935 retry_seconds = ftpfs_retry_seconds;
936 tty_enable_interrupt_key ();
937 for (count_down = retry_seconds; count_down; count_down--)
939 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
940 count_down);
941 sleep (1);
942 if (tty_got_interrupt ())
944 /* ftpfs_errno = E; */
945 tty_disable_interrupt_key ();
946 return 0;
949 tty_disable_interrupt_key ();
953 while (retry_seconds != 0);
955 super->path_element->path = ftpfs_get_current_directory (me, super);
956 if (super->path_element->path == NULL)
957 super->path_element->path = g_strdup (PATH_SEP_STR);
959 return 0;
962 /* --------------------------------------------------------------------------------------------- */
964 static int
965 ftpfs_open_archive (struct vfs_s_super *super,
966 const vfs_path_t * vpath, const vfs_path_element_t * vpath_element)
968 (void) vpath;
970 super->data = g_new0 (ftp_super_data_t, 1);
972 super->path_element = ftpfs_correct_url_parameters (vpath_element);
973 SUP->proxy = NULL;
974 if (ftpfs_check_proxy (super->path_element->host))
975 SUP->proxy = ftpfs_proxy_host;
976 SUP->use_passive_connection = ftpfs_use_passive_connections;
977 SUP->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
978 SUP->isbinary = TYPE_UNKNOWN;
979 SUP->remote_is_amiga = 0;
980 super->name = g_strdup ("/");
981 super->root =
982 vfs_s_new_inode (vpath_element->class, super,
983 vfs_s_default_stat (vpath_element->class, S_IFDIR | 0755));
985 return ftpfs_open_archive_int (vpath_element->class, super);
988 /* --------------------------------------------------------------------------------------------- */
990 static int
991 ftpfs_archive_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *super,
992 const vfs_path_t * vpath, void *cookie)
994 vfs_path_element_t *path_element;
995 int result;
997 (void) vpath;
998 (void) cookie;
1000 path_element = ftpfs_correct_url_parameters (vpath_element);
1002 result = ((strcmp (path_element->host, super->path_element->host) == 0)
1003 && (strcmp (path_element->user, super->path_element->user) == 0)
1004 && (path_element->port == super->path_element->port)) ? 1 : 0;
1006 vfs_path_element_free (path_element);
1007 return result;
1010 /* --------------------------------------------------------------------------------------------- */
1011 /* The returned directory should always contain a trailing slash */
1013 static char *
1014 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
1016 char buf[BUF_8K], *bufp, *bufq;
1018 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
1019 ftpfs_get_reply (me, SUP->sock, buf, sizeof (buf)) == COMPLETE)
1021 bufp = NULL;
1022 for (bufq = buf; *bufq; bufq++)
1023 if (*bufq == '"')
1025 if (!bufp)
1027 bufp = bufq + 1;
1029 else
1031 *bufq = 0;
1032 if (*bufp)
1034 if (*(bufq - 1) != '/')
1036 *bufq++ = '/';
1037 *bufq = 0;
1039 if (*bufp == '/')
1040 return g_strdup (bufp);
1041 else
1043 /* If the remote server is an Amiga a leading slash
1044 might be missing. MC needs it because it is used
1045 as separator between hostname and path internally. */
1046 return g_strconcat ("/", bufp, (char *) NULL);
1049 else
1051 ftpfs_errno = EIO;
1052 return NULL;
1057 ftpfs_errno = EIO;
1058 return NULL;
1061 /* --------------------------------------------------------------------------------------------- */
1062 /* Setup Passive PASV FTP connection */
1064 static int
1065 ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
1066 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1068 char *c;
1069 char n[6];
1070 int xa, xb, xc, xd, xe, xf;
1072 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
1073 return 0;
1075 /* Parse remote parameters */
1076 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
1078 if (!*c)
1079 return 0;
1080 if (!isdigit ((unsigned char) *c))
1081 return 0;
1082 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1083 return 0;
1085 n[0] = (unsigned char) xa;
1086 n[1] = (unsigned char) xb;
1087 n[2] = (unsigned char) xc;
1088 n[3] = (unsigned char) xd;
1089 n[4] = (unsigned char) xe;
1090 n[5] = (unsigned char) xf;
1092 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1093 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1095 if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
1096 return 0;
1098 return 1;
1101 /* --------------------------------------------------------------------------------------------- */
1102 /* Setup Passive EPSV FTP connection */
1104 static int
1105 ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
1106 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1108 char *c;
1109 int port;
1111 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") != COMPLETE)
1112 return 0;
1114 /* (|||<port>|) */
1115 c = strchr (reply_str, '|');
1116 if (c == NULL)
1117 return 0;
1118 if (strlen (c) > 3)
1119 c += 3;
1120 else
1121 return 0;
1123 port = atoi (c);
1124 if (port < 0 || port > 65535)
1125 return 0;
1126 port = htons (port);
1128 switch (sa->ss_family)
1130 case AF_INET:
1131 ((struct sockaddr_in *) sa)->sin_port = port;
1132 break;
1133 case AF_INET6:
1134 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1135 break;
1138 return (connect (my_socket, (struct sockaddr *) sa, *salen) < 0) ? 0 : 1;
1141 /* --------------------------------------------------------------------------------------------- */
1142 /* Setup Passive ftp connection, we use it for source routed connections */
1144 static int
1145 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
1146 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1148 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1149 if (sa->ss_family == AF_INET)
1151 if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
1152 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1153 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1154 return 0;
1156 /* It's IPV6, so EPSV is our only hope */
1157 else
1159 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1160 return 0;
1163 return 1;
1166 /* --------------------------------------------------------------------------------------------- */
1167 /* Setup Active PORT or EPRT FTP connection */
1169 static int
1170 ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super,
1171 struct sockaddr_storage data_addr, socklen_t data_addrlen)
1173 unsigned short int port;
1174 char *addr;
1175 unsigned int af;
1177 switch (data_addr.ss_family)
1179 case AF_INET:
1180 af = FTP_INET;
1181 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1182 break;
1183 case AF_INET6:
1184 af = FTP_INET6;
1185 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1186 break;
1187 /* Not implemented */
1188 default:
1189 return 0;
1192 addr = g_try_malloc (NI_MAXHOST);
1193 if (addr == NULL)
1194 ERRNOR (ENOMEM, -1);
1196 if (getnameinfo
1197 ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1198 NI_NUMERICHOST) != 0)
1200 g_free (addr);
1201 ERRNOR (EIO, -1);
1204 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1205 if (af == FTP_INET)
1207 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1208 unsigned char *p = (unsigned char *) &port;
1210 if (ftpfs_command (me, super, WAIT_REPLY,
1211 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1212 p[0], p[1]) == COMPLETE)
1214 g_free (addr);
1215 return 1;
1220 * Converts network MSB first order to host byte order (LSB
1221 * first on i386). If we do it earlier, we will run into an
1222 * endianness issue, because the server actually expects to see
1223 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1225 port = ntohs (port);
1227 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1228 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
1230 g_free (addr);
1231 return 1;
1234 g_free (addr);
1235 return 0;
1238 /* --------------------------------------------------------------------------------------------- */
1239 /* Initialize a socket for FTP DATA connection */
1241 static int
1242 ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super,
1243 struct sockaddr_storage *data_addr, socklen_t * data_addrlen)
1245 int result;
1247 memset (data_addr, 0, sizeof (struct sockaddr_storage));
1248 *data_addrlen = sizeof (struct sockaddr_storage);
1250 if (SUP->use_passive_connection)
1251 result = getpeername (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1252 else
1253 result = getsockname (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1255 if (result == -1)
1256 return -1;
1258 switch (data_addr->ss_family)
1260 case AF_INET:
1261 ((struct sockaddr_in *) data_addr)->sin_port = 0;
1262 break;
1263 case AF_INET6:
1264 ((struct sockaddr_in6 *) data_addr)->sin6_port = 0;
1265 break;
1266 default:
1267 vfs_print_message (_("ftpfs: invalid address family"));
1268 ERRNOR (EINVAL, -1);
1271 result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP);
1273 if (result < 0)
1275 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1276 return -1;
1279 return result;
1282 /* --------------------------------------------------------------------------------------------- */
1283 /* Initialize FTP DATA connection */
1285 static int
1286 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1288 struct sockaddr_storage data_addr;
1289 socklen_t data_addrlen;
1292 * Don't factor socket initialization out of these conditionals,
1293 * because ftpfs_init_data_socket initializes it in different way
1294 * depending on use_passive_connection flag.
1297 /* Try to establish a passive connection first (if requested) */
1298 if (SUP->use_passive_connection)
1300 int data_sock;
1302 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1303 if (data_sock < 0)
1304 return -1;
1306 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1307 return data_sock;
1309 vfs_print_message (_("ftpfs: could not setup passive mode"));
1310 SUP->use_passive_connection = 0;
1312 close (data_sock);
1315 /* If passive setup is diabled or failed, fallback to active connections */
1316 if (!SUP->use_passive_connection)
1318 int data_sock;
1320 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1321 if (data_sock < 0)
1322 return -1;
1324 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) == 0) &&
1325 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) == 0) &&
1326 (listen (data_sock, 1) == 0) &&
1327 (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0))
1328 return data_sock;
1330 close (data_sock);
1333 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1334 SUP->use_passive_connection = SUP->proxy != NULL ? ftpfs_use_passive_connections_over_proxy :
1335 ftpfs_use_passive_connections;
1337 ftpfs_errno = EIO;
1338 return -1;
1341 /* --------------------------------------------------------------------------------------------- */
1343 static int
1344 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1345 const char *remote, int isbinary, int reget)
1347 struct sockaddr_storage from;
1348 int s, j, data;
1349 socklen_t fromlen = sizeof (from);
1351 s = ftpfs_initconn (me, super);
1352 if (s == -1)
1353 return -1;
1355 if (ftpfs_changetype (me, super, isbinary) == -1)
1356 return -1;
1357 if (reget > 0)
1359 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1360 if (j != CONTINUE)
1361 return -1;
1363 if (remote)
1365 char *remote_path = ftpfs_translate_path (me, super, remote);
1366 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1367 /* WarFtpD can't STORE //filename */
1368 (*remote_path == '/') ? remote_path + 1 : remote_path);
1369 g_free (remote_path);
1371 else
1372 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1374 if (j != PRELIM)
1375 ERRNOR (EPERM, -1);
1376 tty_enable_interrupt_key ();
1377 if (SUP->use_passive_connection)
1378 data = s;
1379 else
1381 data = accept (s, (struct sockaddr *) &from, &fromlen);
1382 if (data < 0)
1384 ftpfs_errno = errno;
1385 close (s);
1386 return -1;
1388 close (s);
1390 tty_disable_interrupt_key ();
1391 return data;
1394 /* --------------------------------------------------------------------------------------------- */
1396 static void
1397 ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t * fh)
1399 struct vfs_s_super *super = FH_SUPER;
1400 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1401 fd_set mask;
1402 char buf[BUF_8K];
1403 int dsock = FH_SOCK;
1404 FH_SOCK = -1;
1405 SUP->ctl_connection_busy = 0;
1407 vfs_print_message (_("ftpfs: aborting transfer."));
1408 if (send (SUP->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1410 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1411 if (dsock != -1)
1412 close (dsock);
1413 return;
1416 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1418 vfs_print_message (_("ftpfs: abort failed"));
1419 if (dsock != -1)
1420 close (dsock);
1421 return;
1423 if (dsock != -1)
1425 FD_ZERO (&mask);
1426 FD_SET (dsock, &mask);
1427 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1429 struct timeval start_tim, tim;
1430 gettimeofday (&start_tim, NULL);
1431 /* flush the remaining data */
1432 while (read (dsock, buf, sizeof (buf)) > 0)
1434 gettimeofday (&tim, NULL);
1435 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT)
1437 /* server keeps sending, drop the connection and ftpfs_reconnect */
1438 close (dsock);
1439 ftpfs_reconnect (me, super);
1440 return;
1444 close (dsock);
1446 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) == TRANSIENT) && (code == 426))
1447 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1450 /* --------------------------------------------------------------------------------------------- */
1452 #if 0
1453 static void
1454 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1455 struct vfs_s_inode *dir)
1457 struct linklist *flist;
1458 struct direntry *fe, *fel;
1459 char tmp[MC_MAXPATHLEN];
1460 int depth;
1462 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1463 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1465 /* flist->data->l_stat is alread initialized with 0 */
1466 fel = flist->data;
1467 if (S_ISLNK (fel->s.st_mode) && fel->linkname)
1469 if (fel->linkname[0] == '/')
1471 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1472 continue;
1473 strcpy (tmp, fel->linkname);
1475 else
1477 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1478 continue;
1479 strcpy (tmp, dir->remote_path);
1480 if (tmp[1] != '\0')
1481 strcat (tmp, "/");
1482 strcat (tmp + 1, fel->linkname);
1484 for (depth = 0; depth < 100; depth++)
1485 { /* depth protects against recursive symbolic links */
1486 canonicalize_pathname (tmp);
1487 fe = _get_file_entry (bucket, tmp, 0, 0);
1488 if (fe)
1490 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1492 /* Symlink points to link which isn't resolved, yet. */
1493 if (fe->linkname[0] == '/')
1495 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1496 break;
1497 strcpy (tmp, fe->linkname);
1499 else
1501 /* at this point tmp looks always like this
1502 /directory/filename, i.e. no need to check
1503 strrchr's return value */
1504 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1505 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1506 break;
1507 strcat (tmp, fe->linkname);
1509 continue;
1511 else
1513 fel->l_stat = g_new (struct stat, 1);
1514 if (S_ISLNK (fe->s.st_mode))
1515 *fel->l_stat = *fe->l_stat;
1516 else
1517 *fel->l_stat = fe->s;
1518 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1521 break;
1525 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1528 /* --------------------------------------------------------------------------------------------- */
1530 static void
1531 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1532 struct vfs_s_inode *dir)
1534 char buffer[2048] = "", *filename;
1535 int sock;
1536 FILE *fp;
1537 struct stat s;
1538 struct linklist *flist;
1539 struct direntry *fe;
1540 int switch_method = 0;
1542 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1543 if (strchr (dir->remote_path, ' '))
1545 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1547 vfs_print_message (_("ftpfs: CWD failed."));
1548 return;
1550 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1552 else
1553 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1555 if (sock == -1)
1557 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1558 return;
1561 fp = fdopen (sock, "r");
1562 if (fp == NULL)
1564 close (sock);
1565 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1566 return;
1568 tty_enable_interrupt_key ();
1569 flist = dir->file_list->next;
1570 while (1)
1574 if (flist == dir->file_list)
1575 goto done;
1576 fe = flist->data;
1577 flist = flist->next;
1579 while (!S_ISLNK (fe->s.st_mode));
1580 while (1)
1582 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1583 goto done;
1584 if (MEDATA->logfile)
1586 fputs (buffer, MEDATA->logfile);
1587 fflush (MEDATA->logfile);
1589 vfs_die ("This code should be commented out\n");
1590 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1592 int r = strcmp (fe->name, filename);
1593 g_free (filename);
1594 if (r == 0)
1596 if (S_ISLNK (s.st_mode))
1598 /* This server doesn't understand LIST -lLa */
1599 switch_method = 1;
1600 goto done;
1602 fe->l_stat = g_new (struct stat, 1);
1603 if (fe->l_stat == NULL)
1604 goto done;
1605 *fe->l_stat = s;
1606 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1607 break;
1609 if (r < 0)
1610 break;
1614 done:
1615 while (fgets (buffer, sizeof (buffer), fp) != NULL);
1616 tty_disable_interrupt_key ();
1617 fclose (fp);
1618 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1621 /* --------------------------------------------------------------------------------------------- */
1623 static void
1624 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1626 vfs_print_message (_("Resolving symlink..."));
1628 if (SUP->strict_rfc959_list_cmd)
1629 resolve_symlink_without_ls_options (me, super, dir);
1630 else
1631 resolve_symlink_with_ls_options (me, super, dir);
1633 #endif
1635 /* --------------------------------------------------------------------------------------------- */
1637 static int
1638 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1640 struct vfs_s_entry *ent;
1641 struct vfs_s_super *super = dir->super;
1642 int sock, num_entries = 0;
1643 char lc_buffer[BUF_8K];
1644 int cd_first;
1646 cd_first = ftpfs_first_cd_then_ls || (SUP->strict == RFC_STRICT)
1647 || (strchr (remote_path, ' ') != NULL);
1649 again:
1650 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1651 remote_path,
1652 SUP->strict ==
1653 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1655 if (cd_first)
1657 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1659 ftpfs_errno = ENOENT;
1660 vfs_print_message (_("ftpfs: CWD failed."));
1661 return -1;
1665 gettimeofday (&dir->timestamp, NULL);
1666 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1668 if (SUP->strict == RFC_STRICT)
1669 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1670 else if (cd_first)
1671 /* Dirty hack to avoid autoprepending / to . */
1672 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1673 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1674 else
1676 /* Trailing "/." is necessary if remote_path is a symlink */
1677 char *path = concat_dir_and_file (remote_path, ".");
1678 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1679 g_free (path);
1682 if (sock == -1)
1683 goto fallback;
1685 /* Clear the interrupt flag */
1686 tty_enable_interrupt_key ();
1688 while (1)
1690 int i;
1691 int res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer),
1692 sock);
1693 if (!res)
1694 break;
1696 if (res == EINTR)
1698 me->verrno = ECONNRESET;
1699 close (sock);
1700 tty_disable_interrupt_key ();
1701 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1702 vfs_print_message (_("%s: failure"), me->name);
1703 return -1;
1706 if (MEDATA->logfile)
1708 fputs (lc_buffer, MEDATA->logfile);
1709 fputs ("\n", MEDATA->logfile);
1710 fflush (MEDATA->logfile);
1713 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1714 i = ent->ino->st.st_nlink;
1715 if (!vfs_parse_ls_lga (lc_buffer, &ent->ino->st, &ent->name, &ent->ino->linkname))
1717 vfs_s_free_entry (me, ent);
1718 continue;
1720 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1721 num_entries++;
1722 vfs_s_insert_entry (me, dir, ent);
1725 close (sock);
1726 me->verrno = E_REMOTE;
1727 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1728 goto fallback;
1730 if (num_entries == 0 && cd_first == 0)
1732 /* The LIST command may produce an empty output. In such scenario
1733 it is not clear whether this is caused by `remote_path' being
1734 a non-existent path or for some other reason (listing emtpy
1735 directory without the -a option, non-readable directory, etc.).
1737 Since `dir_load' is a crucial method, when it comes to determine
1738 whether a given path is a _directory_, the code must try its best
1739 to determine the type of `remote_path'. The only reliable way to
1740 achieve this is trough issuing a CWD command. */
1742 cd_first = 1;
1743 goto again;
1746 if (SUP->strict == RFC_AUTODETECT)
1747 SUP->strict = RFC_DARING;
1749 vfs_print_message (_("%s: done."), me->name);
1750 return 0;
1752 fallback:
1753 if (SUP->strict == RFC_AUTODETECT)
1755 /* It's our first attempt to get a directory listing from this
1756 server (UNIX style LIST command) */
1757 SUP->strict = RFC_STRICT;
1758 /* I hate goto, but recursive call needs another 8K on stack */
1759 /* return ftpfs_dir_load (me, dir, remote_path); */
1760 cd_first = 1;
1761 goto again;
1763 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1764 ERRNOR (EACCES, -1);
1767 /* --------------------------------------------------------------------------------------------- */
1769 static int
1770 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname)
1772 int h, sock, n_read, n_written;
1773 off_t n_stored;
1774 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1775 struct linger li;
1776 #else
1777 int flag_one = 1;
1778 #endif
1779 char lc_buffer[BUF_8K];
1780 struct stat s;
1781 char *w_buf;
1782 struct vfs_s_super *super = FH_SUPER;
1783 ftp_fh_data_t *ftp = (ftp_fh_data_t *) fh->data;
1785 h = open (localname, O_RDONLY);
1786 if (h == -1)
1787 ERRNOR (EIO, -1);
1789 sock =
1790 ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1791 if (sock < 0 || fstat (h, &s) == -1)
1793 close (h);
1794 return -1;
1796 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1797 li.l_onoff = 1;
1798 li.l_linger = 120;
1799 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1800 #else
1801 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1802 #endif
1803 n_stored = 0;
1805 tty_enable_interrupt_key ();
1806 while (TRUE)
1808 while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1810 if (errno == EINTR)
1812 if (tty_got_interrupt ())
1814 ftpfs_errno = EINTR;
1815 goto error_return;
1817 else
1818 continue;
1820 ftpfs_errno = errno;
1821 goto error_return;
1823 if (n_read == 0)
1824 break;
1825 n_stored += n_read;
1826 w_buf = lc_buffer;
1827 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1829 if (n_written == -1)
1831 if (errno == EINTR && !tty_got_interrupt ())
1833 continue;
1835 ftpfs_errno = errno;
1836 goto error_return;
1838 w_buf += n_written;
1839 n_read -= n_written;
1841 vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
1842 _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
1844 tty_disable_interrupt_key ();
1845 close (sock);
1846 close (h);
1847 if (ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE)
1848 ERRNOR (EIO, -1);
1849 return 0;
1850 error_return:
1851 tty_disable_interrupt_key ();
1852 close (sock);
1853 close (h);
1854 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1855 return -1;
1858 /* --------------------------------------------------------------------------------------------- */
1860 static int
1861 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset)
1863 char *name;
1865 if (fh->data == NULL)
1866 fh->data = g_new0 (ftp_fh_data_t, 1);
1868 name = vfs_s_fullpath (me, fh->ino);
1869 if (name == NULL)
1870 return 0;
1871 FH_SOCK = ftpfs_open_data_connection (me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1872 g_free (name);
1873 if (FH_SOCK == -1)
1874 ERRNOR (EACCES, 0);
1875 fh->linear = LS_LINEAR_OPEN;
1876 ((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy = 1;
1877 ((ftp_fh_data_t *) fh->data)->append = 0;
1878 return 1;
1881 /* --------------------------------------------------------------------------------------------- */
1883 static int
1884 ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t * fh, void *buf, size_t len)
1886 ssize_t n;
1887 struct vfs_s_super *super = FH_SUPER;
1889 while ((n = read (FH_SOCK, buf, len)) < 0)
1891 if ((errno == EINTR) && !tty_got_interrupt ())
1892 continue;
1893 break;
1896 if (n < 0)
1897 ftpfs_linear_abort (me, fh);
1899 if (n == 0)
1901 SUP->ctl_connection_busy = 0;
1902 close (FH_SOCK);
1903 FH_SOCK = -1;
1904 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1905 ERRNOR (E_REMOTE, -1);
1906 return 0;
1908 ERRNOR (errno, n);
1911 /* --------------------------------------------------------------------------------------------- */
1913 static void
1914 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t * fh)
1916 if (FH_SOCK != -1)
1917 ftpfs_linear_abort (me, fh);
1920 /* --------------------------------------------------------------------------------------------- */
1922 static int
1923 ftpfs_ctl (void *fh, int ctlop, void *arg)
1925 (void) arg;
1927 switch (ctlop)
1929 case VFS_CTL_IS_NOTREADY:
1931 int v;
1933 if (!FH->linear)
1934 vfs_die ("You may not do this");
1935 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1936 return 0;
1938 v = vfs_s_select_on_two (((ftp_fh_data_t *) (FH->data))->sock, 0);
1939 return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
1941 default:
1942 return 0;
1946 /* --------------------------------------------------------------------------------------------- */
1948 static int
1949 ftpfs_send_command (const vfs_path_t * vpath, const char *cmd, int flags)
1951 const char *rpath;
1952 char *p;
1953 struct vfs_s_super *super;
1954 int r;
1955 vfs_path_element_t *path_element;
1957 int flush_directory_cache = (flags & OPT_FLUSH);
1959 path_element = vfs_path_get_by_index (vpath, -1);
1961 rpath = vfs_s_get_path (vpath, &super, 0);
1962 if (rpath == NULL)
1963 return -1;
1965 p = ftpfs_translate_path (path_element->class, super, rpath);
1966 r = ftpfs_command (path_element->class, super, WAIT_REPLY, cmd, p);
1967 g_free (p);
1968 vfs_stamp_create (&vfs_ftpfs_ops, super);
1969 if (flags & OPT_IGNORE_ERROR)
1970 r = COMPLETE;
1971 if (r != COMPLETE)
1973 path_element->class->verrno = EPERM;
1974 return -1;
1976 if (flush_directory_cache)
1977 vfs_s_invalidate (path_element->class, super);
1978 return 0;
1981 /* --------------------------------------------------------------------------------------------- */
1983 static int
1984 ftpfs_chmod (const vfs_path_t * vpath, mode_t mode)
1986 char buf[BUF_SMALL];
1987 int ret;
1989 g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", (int) (mode & 07777));
1991 ret = ftpfs_send_command (vpath, buf, OPT_FLUSH);
1993 return ftpfs_ignore_chattr_errors ? 0 : ret;
1996 /* --------------------------------------------------------------------------------------------- */
1998 static int
1999 ftpfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
2001 #if 0
2002 (void) vpath;
2003 (void) owner;
2004 (void) group;
2006 ftpfs_errno = EPERM;
2007 return -1;
2008 #else
2009 /* Everyone knows it is not possible to chown remotely, so why bother them.
2010 If someone's root, then copy/move will always try to chown it... */
2011 (void) vpath;
2012 (void) owner;
2013 (void) group;
2014 return 0;
2015 #endif
2018 /* --------------------------------------------------------------------------------------------- */
2020 static int
2021 ftpfs_unlink (const vfs_path_t * vpath)
2023 return ftpfs_send_command (vpath, "DELE /%s", OPT_FLUSH);
2026 /* --------------------------------------------------------------------------------------------- */
2028 /* Return 1 if path is the same directory as the one we are in now */
2029 static int
2030 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
2032 (void) me;
2034 if (super->path_element->path == NULL)
2035 return FALSE;
2036 return (strcmp (path, super->path_element->path) == 0);
2039 /* --------------------------------------------------------------------------------------------- */
2041 static int
2042 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
2044 int r;
2045 char *p;
2047 if (!SUP->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
2048 return COMPLETE;
2050 p = ftpfs_translate_path (me, super, remote_path);
2051 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
2052 g_free (p);
2054 if (r != COMPLETE)
2055 ftpfs_errno = EIO;
2056 else
2058 g_free (super->path_element->path);
2059 super->path_element->path = g_strdup (remote_path);
2060 SUP->cwd_deferred = 0;
2062 return r;
2065 /* --------------------------------------------------------------------------------------------- */
2067 static int
2068 ftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
2070 ftpfs_send_command (vpath1, "RNFR /%s", OPT_FLUSH);
2071 return ftpfs_send_command (vpath2, "RNTO /%s", OPT_FLUSH);
2074 /* --------------------------------------------------------------------------------------------- */
2076 static int
2077 ftpfs_mkdir (const vfs_path_t * vpath, mode_t mode)
2079 (void) mode; /* FIXME: should be used */
2081 return ftpfs_send_command (vpath, "MKD /%s", OPT_FLUSH);
2084 /* --------------------------------------------------------------------------------------------- */
2086 static int
2087 ftpfs_rmdir (const vfs_path_t * vpath)
2089 return ftpfs_send_command (vpath, "RMD /%s", OPT_FLUSH);
2092 /* --------------------------------------------------------------------------------------------- */
2094 static int
2095 ftpfs_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
2097 ftp_fh_data_t *ftp;
2099 (void) mode;
2101 fh->data = g_new0 (ftp_fh_data_t, 1);
2102 ftp = (ftp_fh_data_t *) fh->data;
2103 /* File will be written only, so no need to retrieve it from ftp server */
2104 if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
2106 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2107 struct linger li;
2108 #else
2109 int li = 1;
2110 #endif
2111 char *name;
2113 /* ftpfs_linear_start() called, so data will be written
2114 * to local temporary file and stored to ftp server
2115 * by vfs_s_close later
2117 if (((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy)
2119 if (!fh->ino->localname)
2121 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
2122 fh->ino->ent->name);
2123 if (handle == -1)
2124 goto fail;
2125 close (handle);
2126 ftp->append = flags & O_APPEND;
2128 return 0;
2130 name = vfs_s_fullpath (me, fh->ino);
2131 if (name == NULL)
2132 goto fail;
2133 fh->handle =
2134 ftpfs_open_data_connection (me, fh->ino->super,
2135 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
2136 g_free (name);
2138 if (fh->handle < 0)
2139 goto fail;
2140 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2141 li.l_onoff = 1;
2142 li.l_linger = 120;
2143 #endif
2144 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
2146 if (fh->ino->localname)
2148 unlink (fh->ino->localname);
2149 g_free (fh->ino->localname);
2150 fh->ino->localname = NULL;
2152 return 0;
2155 if (!fh->ino->localname && vfs_s_retrieve_file (me, fh->ino) == -1)
2156 goto fail;
2157 if (!fh->ino->localname)
2158 vfs_die ("retrieve_file failed to fill in localname");
2159 return 0;
2161 fail:
2162 g_free (fh->data);
2163 return -1;
2166 /* --------------------------------------------------------------------------------------------- */
2168 static int
2169 ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t * fh)
2171 if (fh->handle != -1 && !fh->ino->localname)
2173 ftp_super_data_t *ftp = (ftp_super_data_t *) fh->ino->super->data;
2175 close (fh->handle);
2176 fh->handle = -1;
2177 /* File is stored to destination already, so
2178 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2180 fh->changed = 0;
2181 if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE)
2182 ERRNOR (EIO, -1);
2183 vfs_s_invalidate (me, FH_SUPER);
2186 g_free (fh->data);
2188 return 0;
2191 /* --------------------------------------------------------------------------------------------- */
2193 static void
2194 ftpfs_done (struct vfs_class *me)
2196 (void) me;
2198 g_slist_foreach (no_proxy, (GFunc) g_free, NULL);
2199 g_slist_free (no_proxy);
2201 g_free (ftpfs_anonymous_passwd);
2202 g_free (ftpfs_proxy_host);
2205 /* --------------------------------------------------------------------------------------------- */
2207 static void
2208 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
2210 GList *iter;
2212 for (iter = MEDATA->supers; iter != NULL; iter = g_list_next (iter))
2214 const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
2215 char *name;
2217 name =
2218 g_strconcat (vfs_ftpfs_ops.prefix, VFS_PATH_URL_DELIMITER, super->path_element->user,
2219 "@", super->path_element->host, "/", super->path_element->path,
2220 (char *) NULL);
2221 func (name);
2222 g_free (name);
2226 /* --------------------------------------------------------------------------------------------- */
2228 static keyword_t
2229 ftpfs_netrc_next (void)
2231 char *p;
2232 keyword_t i;
2233 static const char *const keywords[] = { "default", "machine",
2234 "login", "password", "passwd", "account", "macdef", NULL
2237 while (1)
2239 netrcp = skip_separators (netrcp);
2240 if (*netrcp != '\n')
2241 break;
2242 netrcp++;
2244 if (!*netrcp)
2245 return NETRC_NONE;
2246 p = buffer;
2247 if (*netrcp == '"')
2249 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++)
2251 if (*netrcp == '\\')
2252 netrcp++;
2253 *p++ = *netrcp;
2256 else
2258 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
2259 *netrcp != ',' && *netrcp; netrcp++)
2261 if (*netrcp == '\\')
2262 netrcp++;
2263 *p++ = *netrcp;
2266 *p = 0;
2267 if (!*buffer)
2268 return NETRC_NONE;
2270 for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2271 if (strcmp (keywords[i - 1], buffer) == 0)
2272 return i;
2274 return NETRC_UNKNOWN;
2277 /* --------------------------------------------------------------------------------------------- */
2279 static int
2280 ftpfs_netrc_bad_mode (const char *netrcname)
2282 static int be_angry = 1;
2283 struct stat mystat;
2285 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077))
2287 if (be_angry)
2289 message (D_ERROR, MSG_ERROR,
2290 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2291 be_angry = 0;
2293 return 1;
2295 return 0;
2298 /* --------------------------------------------------------------------------------------------- */
2299 /* Scan .netrc until we find matching "machine" or "default"
2300 * domain is used for additional matching
2301 * No search is done after "default" in compliance with "man netrc"
2302 * Return 0 if found, -1 otherwise */
2304 static int
2305 ftpfs_find_machine (const char *host, const char *domain)
2307 keyword_t keyword;
2309 if (!host)
2310 host = "";
2311 if (!domain)
2312 domain = "";
2314 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2316 if (keyword == NETRC_DEFAULT)
2317 return 0;
2319 if (keyword == NETRC_MACDEF)
2321 /* Scan for an empty line, which concludes "macdef" */
2324 while (*netrcp && *netrcp != '\n')
2325 netrcp++;
2326 if (*netrcp != '\n')
2327 break;
2328 netrcp++;
2330 while (*netrcp && *netrcp != '\n');
2331 continue;
2334 if (keyword != NETRC_MACHINE)
2335 continue;
2337 /* Take machine name */
2338 if (ftpfs_netrc_next () == NETRC_NONE)
2339 break;
2341 if (g_ascii_strcasecmp (host, buffer) != 0)
2343 /* Try adding our domain to short names in .netrc */
2344 const char *host_domain = strchr (host, '.');
2345 if (!host_domain)
2346 continue;
2348 /* Compare domain part */
2349 if (g_ascii_strcasecmp (host_domain, domain) != 0)
2350 continue;
2352 /* Compare local part */
2353 if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2354 continue;
2357 return 0;
2360 /* end of .netrc */
2361 return -1;
2364 /* --------------------------------------------------------------------------------------------- */
2365 /* Extract login and password from .netrc for the host.
2366 * pass may be NULL.
2367 * Returns 0 for success, -1 for error */
2369 static int
2370 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2372 char *netrcname;
2373 char *tmp_pass = NULL;
2374 char hostname[MAXHOSTNAMELEN];
2375 const char *domain;
2376 keyword_t keyword;
2377 static struct rupcache
2379 struct rupcache *next;
2380 char *host;
2381 char *login;
2382 char *pass;
2383 } *rup_cache = NULL, *rupp;
2385 /* Initialize *login and *pass */
2386 g_free (*login);
2387 *login = NULL;
2388 g_free (*pass);
2389 *pass = NULL;
2391 /* Look up in the cache first */
2392 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2394 if (!strcmp (host, rupp->host))
2396 if (rupp->login)
2397 *login = g_strdup (rupp->login);
2398 if (pass && rupp->pass)
2399 *pass = g_strdup (rupp->pass);
2400 return 0;
2404 /* Load current .netrc */
2405 netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2406 if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2408 g_free (netrcname);
2409 return 0;
2412 netrcp = netrc;
2414 /* Find our own domain name */
2415 if (gethostname (hostname, sizeof (hostname)) < 0)
2416 *hostname = '\0';
2418 domain = strchr (hostname, '.');
2419 if (domain == NULL)
2420 domain = "";
2422 /* Scan for "default" and matching "machine" keywords */
2423 ftpfs_find_machine (host, domain);
2425 /* Scan for keywords following "default" and "machine" */
2426 while (1)
2428 int need_break = 0;
2429 keyword = ftpfs_netrc_next ();
2431 switch (keyword)
2433 case NETRC_LOGIN:
2434 if (ftpfs_netrc_next () == NETRC_NONE)
2436 need_break = 1;
2437 break;
2440 /* We have another name already - should not happen */
2441 if (*login)
2443 need_break = 1;
2444 break;
2447 /* We have login name now */
2448 *login = g_strdup (buffer);
2449 break;
2451 case NETRC_PASSWORD:
2452 case NETRC_PASSWD:
2453 if (ftpfs_netrc_next () == NETRC_NONE)
2455 need_break = 1;
2456 break;
2459 /* Ignore unsafe passwords */
2460 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2461 && ftpfs_netrc_bad_mode (netrcname))
2463 need_break = 1;
2464 break;
2467 /* Remember password. pass may be NULL, so use tmp_pass */
2468 if (tmp_pass == NULL)
2469 tmp_pass = g_strdup (buffer);
2470 break;
2472 case NETRC_ACCOUNT:
2473 /* "account" is followed by a token which we ignore */
2474 if (ftpfs_netrc_next () == NETRC_NONE)
2476 need_break = 1;
2477 break;
2480 /* Ignore account, but warn user anyways */
2481 ftpfs_netrc_bad_mode (netrcname);
2482 break;
2484 default:
2485 /* Unexpected keyword or end of file */
2486 need_break = 1;
2487 break;
2490 if (need_break)
2491 break;
2494 g_free (netrc);
2495 g_free (netrcname);
2497 rupp = g_new (struct rupcache, 1);
2498 rupp->host = g_strdup (host);
2499 rupp->login = g_strdup (*login);
2500 rupp->pass = g_strdup (tmp_pass);
2502 rupp->next = rup_cache;
2503 rup_cache = rupp;
2505 *pass = tmp_pass;
2507 return 0;
2510 /* --------------------------------------------------------------------------------------------- */
2511 /*** public functions ****************************************************************************/
2512 /* --------------------------------------------------------------------------------------------- */
2514 /** This routine is called as the last step in load_setup */
2515 void
2516 ftpfs_init_passwd (void)
2518 ftpfs_anonymous_passwd = load_anon_passwd ();
2519 if (ftpfs_anonymous_passwd)
2520 return;
2522 /* If there is no anonymous ftp password specified
2523 * then we'll just use anonymous@
2524 * We don't send any other thing because:
2525 * - We want to remain anonymous
2526 * - We want to stop SPAM
2527 * - We don't want to let ftp sites to discriminate by the user,
2528 * host or country.
2530 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
2533 /* --------------------------------------------------------------------------------------------- */
2535 void
2536 init_ftpfs (void)
2538 static struct vfs_s_subclass ftpfs_subclass;
2540 tcp_init ();
2542 ftpfs_subclass.flags = VFS_S_REMOTE;
2543 ftpfs_subclass.archive_same = ftpfs_archive_same;
2544 ftpfs_subclass.open_archive = ftpfs_open_archive;
2545 ftpfs_subclass.free_archive = ftpfs_free_archive;
2546 ftpfs_subclass.fh_open = ftpfs_fh_open;
2547 ftpfs_subclass.fh_close = ftpfs_fh_close;
2548 ftpfs_subclass.dir_load = ftpfs_dir_load;
2549 ftpfs_subclass.file_store = ftpfs_file_store;
2550 ftpfs_subclass.linear_start = ftpfs_linear_start;
2551 ftpfs_subclass.linear_read = ftpfs_linear_read;
2552 ftpfs_subclass.linear_close = ftpfs_linear_close;
2554 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2555 vfs_ftpfs_ops.name = "ftpfs";
2556 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2557 vfs_ftpfs_ops.prefix = "ftp";
2558 vfs_ftpfs_ops.done = &ftpfs_done;
2559 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2560 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2561 vfs_ftpfs_ops.chown = ftpfs_chown;
2562 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2563 vfs_ftpfs_ops.rename = ftpfs_rename;
2564 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2565 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2566 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2567 vfs_register_class (&vfs_ftpfs_ops);
2570 /* --------------------------------------------------------------------------------------------- */