Ticket #2779: Active VFS directories list contain incorrect current path
[midnight-commander.git] / src / vfs / ftpfs / ftpfs.c
blob384a5387efa0441e5ab375458344026b48a16f0e
1 /*
2 Virtual File System: FTP file system.
4 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2006, 2007, 2008, 2009, 2010, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Ching Hui, 1995
10 Jakub Jelinek, 1995
11 Miguel de Icaza, 1995, 1996, 1997
12 Norbert Warmuth, 1997
13 Pavel Machek, 1998
14 Yury V. Zaytsev, 2010
15 Slava Zanko <slavazanko@gmail.com>, 2010
16 Andrew Borodin <aborodin@vmail.ru>, 2010
18 This file is part of the Midnight Commander.
20 The Midnight Commander is free software: you can redistribute it
21 and/or modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation, either version 3 of the License,
23 or (at your option) any later version.
25 The Midnight Commander is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <http://www.gnu.org/licenses/>.
34 /**
35 * \file
36 * \brief Source: Virtual File System: FTP file system
37 * \author Ching Hui
38 * \author Jakub Jelinek
39 * \author Miguel de Icaza
40 * \author Norbert Warmuth
41 * \author Pavel Machek
42 * \date 1995, 1997, 1998
44 * \todo
45 - make it more robust - all the connects etc. should handle EADDRINUSE and
46 ERETRY (have I spelled these names correctly?)
47 - make the user able to flush a connection - all the caches will get empty
48 etc., (tarfs as well), we should give there a user selectable timeout
49 and assign a key sequence.
50 - use hash table instead of linklist to cache ftpfs directory.
52 What to do with this?
55 * NOTE: Usage of tildes is deprecated, consider:
56 * \verbatim
57 cd /#ftp:pavel@hobit
58 cd ~
59 \endverbatim
60 * And now: what do I want to do? Do I want to go to /home/pavel or to
61 * /#ftp:hobit/home/pavel? I think first has better sense...
63 \verbatim
65 int f = !strcmp( remote_path, "/~" );
66 if (f || !strncmp( remote_path, "/~/", 3 )) {
67 char *s;
68 s = mc_build_filename ( qhome (*bucket), remote_path +3-f, NULL );
69 g_free (remote_path);
70 remote_path = s;
73 \endverbatim
76 /* \todo Fix: Namespace pollution: horrible */
78 #include <config.h>
79 #include <stdio.h> /* sscanf() */
80 #include <stdlib.h> /* atoi() */
81 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
82 #include <netdb.h> /* struct hostent */
83 #include <sys/socket.h> /* AF_INET */
84 #include <netinet/in.h> /* struct in_addr */
85 #ifdef HAVE_ARPA_INET_H
86 #include <arpa/inet.h>
87 #endif
88 #include <arpa/ftp.h>
89 #include <arpa/telnet.h>
90 #ifdef HAVE_SYS_PARAM_H
91 #include <sys/param.h>
92 #endif
93 #include <errno.h>
94 #include <ctype.h>
95 #include <fcntl.h>
96 #include <sys/time.h> /* gettimeofday() */
97 #include <inttypes.h> /* uintmax_t */
99 #include "lib/global.h"
100 #include "lib/util.h"
101 #include "lib/mcconfig.h"
103 #include "lib/tty/tty.h" /* enable/disable interrupt key */
104 #include "lib/widget.h" /* message() */
106 #include "src/history.h"
107 #include "src/setup.h" /* for load_anon_passwd */
109 #include "lib/vfs/vfs.h"
110 #include "lib/vfs/utilvfs.h"
111 #include "lib/vfs/netutil.h"
112 #include "lib/vfs/xdirentry.h"
113 #include "lib/vfs/gc.h" /* vfs_stamp_create */
115 #include "ftpfs.h"
117 /*** global variables ****************************************************************************/
119 /* Delay to retry a connection */
120 int ftpfs_retry_seconds = 30;
122 /* Method to use to connect to ftp sites */
123 int ftpfs_use_passive_connections = 1;
124 int ftpfs_use_passive_connections_over_proxy = 0;
126 /* Method used to get directory listings:
127 * 1: try 'LIST -la <path>', if it fails
128 * fall back to CWD <path>; LIST
129 * 0: always use CWD <path>; LIST
131 int ftpfs_use_unix_list_options = 1;
133 /* First "CWD <path>", then "LIST -la ." */
134 int ftpfs_first_cd_then_ls = 1;
136 /* Use the ~/.netrc */
137 int ftpfs_use_netrc = 1;
139 /* Anonymous setup */
140 char *ftpfs_anonymous_passwd = NULL;
141 int ftpfs_directory_timeout = 900;
143 /* Proxy host */
144 char *ftpfs_proxy_host = NULL;
146 /* wether we have to use proxy by default? */
147 int ftpfs_always_use_proxy = 0;
149 int ftpfs_ignore_chattr_errors = 1;
151 /*** file scope macro definitions ****************************************************************/
153 #ifndef MAXHOSTNAMELEN
154 #define MAXHOSTNAMELEN 64
155 #endif
157 #define UPLOAD_ZERO_LENGTH_FILE
158 #define SUP ((ftp_super_data_t *) super->data)
159 #define FH_SOCK ((ftp_fh_data_t *) fh->data)->sock
161 #ifndef INADDR_NONE
162 #define INADDR_NONE 0xffffffff
163 #endif
165 #define RFC_AUTODETECT 0
166 #define RFC_DARING 1
167 #define RFC_STRICT 2
169 /* ftpfs_command wait_flag: */
170 #define NONE 0x00
171 #define WAIT_REPLY 0x01
172 #define WANT_STRING 0x02
174 #define FTP_COMMAND_PORT 21
176 /* some defines only used by ftpfs_changetype */
177 /* These two are valid values for the second parameter */
178 #define TYPE_ASCII 0
179 #define TYPE_BINARY 1
181 /* This one is only used to initialize bucket->isbinary, don't use it as
182 second parameter to ftpfs_changetype. */
183 #define TYPE_UNKNOWN -1
185 #define ABORT_TIMEOUT 5
186 /*** file scope type declarations ****************************************************************/
188 #ifndef HAVE_SOCKLEN_T
189 typedef int socklen_t;
190 #endif
192 /* This should match the keywords[] array below */
193 typedef enum
195 NETRC_NONE = 0,
196 NETRC_DEFAULT,
197 NETRC_MACHINE,
198 NETRC_LOGIN,
199 NETRC_PASSWORD,
200 NETRC_PASSWD,
201 NETRC_ACCOUNT,
202 NETRC_MACDEF,
203 NETRC_UNKNOWN
204 } keyword_t;
206 typedef struct
208 int sock;
210 char *proxy; /* proxy server, NULL if no proxy */
211 int failed_on_login; /* used to pass the failure reason to upper levels */
212 int use_passive_connection;
213 int remote_is_amiga; /* No leading slash allowed for AmiTCP (Amiga) */
214 int isbinary;
215 int cwd_deferred; /* current_directory was changed but CWD command hasn't
216 been sent yet */
217 int strict; /* ftp server doesn't understand
218 * "LIST -la <path>"; use "CWD <path>"/
219 * "LIST" instead
221 int ctl_connection_busy;
222 } ftp_super_data_t;
224 typedef struct
226 int sock;
227 int append;
228 } ftp_fh_data_t;
230 /*** file scope variables ************************************************************************/
232 static int ftpfs_errno;
233 static int code;
235 #ifdef FIXME_LATER_ALIGATOR
236 static struct linklist *connections_list;
237 #endif
239 static char reply_str[80];
241 static struct vfs_class vfs_ftpfs_ops;
243 static GSList *no_proxy;
245 static char buffer[BUF_MEDIUM];
246 static char *netrc;
247 static const char *netrcp;
249 /*** file scope functions ************************************************************************/
250 /* --------------------------------------------------------------------------------------------- */
252 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
253 Translate a Unix path, i.e. MC's internal path representation (e.g.
254 /somedir/somefile) to a path valid for the remote server. Every path
255 transfered to the remote server has to be mangled by this function
256 right prior to sending it.
257 Currently only Amiga ftp servers are handled in a special manner.
259 When the remote server is an amiga:
260 a) strip leading slash if necesarry
261 b) replace first occurance of ":/" with ":"
262 c) strip trailing "/."
265 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super,
266 const char *remote_path);
267 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply,
268 const char *fmt, ...) __attribute__ ((format (__printf__, 4, 5)));
269 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
270 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
271 const char *netrcpass);
272 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
274 /* --------------------------------------------------------------------------------------------- */
276 static char *
277 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
279 if (!SUP->remote_is_amiga)
280 return g_strdup (remote_path);
281 else
283 char *ret, *p;
285 if (MEDATA->logfile)
287 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
288 fflush (MEDATA->logfile);
291 /* strip leading slash(es) */
292 while (*remote_path == '/')
293 remote_path++;
296 * Don't change "/" into "", e.g. "CWD " would be
297 * invalid.
299 if (*remote_path == '\0')
300 return g_strdup (".");
302 ret = g_strdup (remote_path);
304 /* replace first occurance of ":/" with ":" */
305 p = strchr (ret, ':');
306 if ((p != NULL) && (*(p + 1) == '/'))
307 memmove (p + 1, p + 2, strlen (p + 2) + 1);
309 /* strip trailing "/." */
310 p = strrchr (ret, '/');
311 if ((p != NULL) && (*(p + 1) == '.') && (*(p + 2) == '\0'))
312 *p = '\0';
314 return ret;
318 /* --------------------------------------------------------------------------------------------- */
319 /** Extract the hostname and username from the path */
321 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
322 * ftp://sunsite.unc.edu/pub/linux
323 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
324 * ftp://tsx-11.mit.edu:8192/
325 * ftp://joe@foo.edu:11321/private
326 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
327 * is supplied.
330 static vfs_path_element_t *
331 ftpfs_correct_url_parameters (const vfs_path_element_t * velement)
333 vfs_path_element_t *path_element = vfs_path_element_clone (velement);
335 if (path_element->port == 0)
336 path_element->port = FTP_COMMAND_PORT;
338 if (path_element->user == NULL)
340 /* Look up user and password in netrc */
341 if (ftpfs_use_netrc)
342 ftpfs_netrc_lookup (path_element->host, &path_element->user, &path_element->password);
344 if (path_element->user == NULL)
345 path_element->user = g_strdup ("anonymous");
347 /* Look up password in netrc for known user */
348 if (ftpfs_use_netrc && path_element->user != NULL && path_element->password != NULL)
350 char *new_user = NULL;
351 char *new_passwd = NULL;
353 ftpfs_netrc_lookup (path_element->host, &new_user, &new_passwd);
355 /* If user is different, remove password */
356 if (new_user != NULL && strcmp (path_element->user, new_user) != 0)
358 g_free (path_element->password);
359 path_element->password = NULL;
362 g_free (new_user);
363 g_free (new_passwd);
366 return path_element;
369 /* --------------------------------------------------------------------------------------------- */
370 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
372 static int
373 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
375 char answer[BUF_1K];
376 int i;
378 for (;;)
380 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
382 if (string_buf)
383 *string_buf = 0;
384 code = 421;
385 return 4;
387 switch (sscanf (answer, "%d", &code))
389 case 0:
390 if (string_buf)
391 g_strlcpy (string_buf, answer, string_len);
392 code = 500;
393 return 5;
394 case 1:
395 if (answer[3] == '-')
397 while (1)
399 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
401 if (string_buf)
402 *string_buf = 0;
403 code = 421;
404 return 4;
406 if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' '))
407 break;
410 if (string_buf)
411 g_strlcpy (string_buf, answer, string_len);
412 return code / 100;
417 /* --------------------------------------------------------------------------------------------- */
419 static int
420 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
422 int sock;
424 sock = ftpfs_open_socket (me, super);
425 if (sock != -1)
427 char *cwdir = super->path_element->path;
429 close (SUP->sock);
430 SUP->sock = sock;
431 super->path_element->path = NULL;
434 if (ftpfs_login_server (me, super, super->path_element->password) != 0)
436 if (cwdir == NULL)
437 return 1;
438 sock = ftpfs_chdir_internal (me, super, cwdir);
439 g_free (cwdir);
440 return sock == COMPLETE ? 1 : 0;
443 super->path_element->path = cwdir;
446 return 0;
449 /* --------------------------------------------------------------------------------------------- */
451 static int
452 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt,
453 ...)
455 va_list ap;
456 char *cmdstr;
457 int status, cmdlen;
458 static int retry = 0;
459 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
461 va_start (ap, fmt);
462 cmdstr = g_strdup_vprintf (fmt, ap);
463 va_end (ap);
465 cmdlen = strlen (cmdstr);
466 cmdstr = g_realloc (cmdstr, cmdlen + 3);
467 strcpy (cmdstr + cmdlen, "\r\n");
468 cmdlen += 2;
470 if (MEDATA->logfile)
472 if (strncmp (cmdstr, "PASS ", 5) == 0)
474 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
476 else
478 size_t ret;
479 ret = fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
482 fflush (MEDATA->logfile);
485 got_sigpipe = 0;
486 tty_enable_interrupt_key ();
487 status = write (SUP->sock, cmdstr, cmdlen);
489 if (status < 0)
491 code = 421;
493 if (errno == EPIPE)
494 { /* Remote server has closed connection */
495 if (level == 0)
497 level = 1;
498 status = ftpfs_reconnect (me, super);
499 level = 0;
500 if (status && (write (SUP->sock, cmdstr, cmdlen) > 0))
502 goto ok;
506 got_sigpipe = 1;
508 g_free (cmdstr);
509 tty_disable_interrupt_key ();
510 return TRANSIENT;
512 retry = 0;
514 tty_disable_interrupt_key ();
516 if (wait_reply)
518 status = ftpfs_get_reply (me, SUP->sock,
519 (wait_reply & WANT_STRING) ? reply_str : NULL,
520 sizeof (reply_str) - 1);
521 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
523 retry = 1;
524 level = 1;
525 status = ftpfs_reconnect (me, super);
526 level = 0;
527 if (status && (write (SUP->sock, cmdstr, cmdlen) > 0))
529 goto ok;
532 retry = 0;
533 g_free (cmdstr);
534 return status;
536 g_free (cmdstr);
537 return COMPLETE;
540 /* --------------------------------------------------------------------------------------------- */
542 static void
543 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
545 if (SUP->sock != -1)
547 vfs_print_message (_("ftpfs: Disconnecting from %s"), super->path_element->host);
548 ftpfs_command (me, super, NONE, "QUIT");
549 close (SUP->sock);
551 g_free (super->data);
552 super->data = NULL;
555 /* --------------------------------------------------------------------------------------------- */
557 static int
558 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
560 if (binary != SUP->isbinary)
562 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
563 ERRNOR (EIO, -1);
564 SUP->isbinary = binary;
566 return binary;
569 /* --------------------------------------------------------------------------------------------- */
570 /* This routine logs the user in */
572 static int
573 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
575 char *pass;
576 char *op;
577 char *name; /* login user name */
578 int anon = 0;
579 char reply_string[BUF_MEDIUM];
581 SUP->isbinary = TYPE_UNKNOWN;
583 if (super->path_element->password != NULL) /* explicit password */
584 op = g_strdup (super->path_element->password);
585 else if (netrcpass != NULL) /* password from netrc */
586 op = g_strdup (netrcpass);
587 else if (strcmp (super->path_element->user, "anonymous") == 0
588 || strcmp (super->path_element->user, "ftp") == 0)
590 if (ftpfs_anonymous_passwd == NULL) /* default anonymous password */
591 ftpfs_init_passwd ();
592 op = g_strdup (ftpfs_anonymous_passwd);
593 anon = 1;
595 else
596 { /* ask user */
597 char *p;
599 p = g_strdup_printf (_("FTP: Password required for %s"), super->path_element->user);
600 op = vfs_get_password (p);
601 g_free (p);
602 if (op == NULL)
603 ERRNOR (EPERM, 0);
604 super->path_element->password = g_strdup (op);
607 if (!anon || MEDATA->logfile)
608 pass = op;
609 else
611 pass = g_strconcat ("-", op, (char *) NULL);
612 wipe_password (op);
615 /* Proxy server accepts: username@host-we-want-to-connect */
616 if (SUP->proxy)
617 name =
618 g_strconcat (super->path_element->user, "@",
619 super->path_element->host[0] ==
620 '!' ? super->path_element->host + 1 : super->path_element->host,
621 (char *) NULL);
622 else
623 name = g_strdup (super->path_element->user);
625 if (ftpfs_get_reply (me, SUP->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
627 char *reply_up;
629 reply_up = g_ascii_strup (reply_string, -1);
630 SUP->remote_is_amiga = strstr (reply_up, "AMIGA") != 0;
631 if (strstr (reply_up, " SPFTP/1.0.0000 SERVER ")) /* handles `LIST -la` in a weird way */
632 SUP->strict = RFC_STRICT;
633 g_free (reply_up);
635 if (MEDATA->logfile)
637 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n", SUP->remote_is_amiga);
638 fflush (MEDATA->logfile);
641 vfs_print_message (_("ftpfs: sending login name"));
643 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
645 case CONTINUE:
646 vfs_print_message (_("ftpfs: sending user password"));
647 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
648 if (code == CONTINUE)
650 char *p;
652 p = g_strdup_printf (_("FTP: Account required for user %s"),
653 super->path_element->user);
654 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
655 g_free (p);
656 if (op == NULL)
657 ERRNOR (EPERM, 0);
658 vfs_print_message (_("ftpfs: sending user account"));
659 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
660 g_free (op);
662 if (code != COMPLETE)
663 break;
664 /* fall through */
666 case COMPLETE:
667 vfs_print_message (_("ftpfs: logged in"));
668 wipe_password (pass);
669 g_free (name);
670 return 1;
672 default:
673 SUP->failed_on_login = 1;
674 wipe_password (super->path_element->password);
675 super->path_element->password = NULL;
677 goto login_fail;
681 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
682 super->path_element->user);
684 login_fail:
685 wipe_password (pass);
686 g_free (name);
687 ERRNOR (EPERM, 0);
690 /* --------------------------------------------------------------------------------------------- */
692 static void
693 ftpfs_load_no_proxy_list (void)
695 /* FixMe: shouldn't be hardcoded!!! */
696 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
697 FILE *npf;
698 int c;
699 char *p;
700 static char *mc_file = NULL;
702 mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL);
703 if (exist_file (mc_file))
705 npf = fopen (mc_file, "r");
706 if (npf != NULL)
708 while (fgets (s, sizeof (s), npf) != NULL)
710 p = strchr (s, '\n');
711 if (p == NULL) /* skip bogus entries */
713 while ((c = fgetc (npf)) != EOF && c != '\n')
715 continue;
718 if (p != s)
720 *p = '\0';
721 no_proxy = g_slist_prepend (no_proxy, g_strdup (s));
724 fclose (npf);
727 g_free (mc_file);
730 /* --------------------------------------------------------------------------------------------- */
731 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
733 static int
734 ftpfs_check_proxy (const char *host)
736 GSList *npe;
738 if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0')
739 return 0; /* sanity check */
741 if (*host == '!')
742 return 1;
744 if (!ftpfs_always_use_proxy)
745 return 0;
747 if (strchr (host, '.') == NULL)
748 return 0;
750 ftpfs_load_no_proxy_list ();
751 for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe))
753 const char *domain = (const char *) npe->data;
755 if (domain[0] == '.')
757 size_t ld = strlen (domain);
758 size_t lh = strlen (host);
760 while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1])
762 ld--;
763 lh--;
766 if (ld == 0)
767 return 0;
769 else if (g_ascii_strcasecmp (host, domain) == 0)
770 return 0;
773 return 1;
776 /* --------------------------------------------------------------------------------------------- */
778 static void
779 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
781 vfs_path_element_t *path_element;
783 path_element = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
784 *host = g_strdup (path_element->host);
785 *port = path_element->port;
786 vfs_path_element_free (path_element);
789 /* --------------------------------------------------------------------------------------------- */
791 static int
792 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
794 struct addrinfo hints, *res, *curr_res;
795 int my_socket = 0;
796 char *host = NULL;
797 char port[8];
798 int tmp_port;
799 int e;
801 (void) me;
803 /* Use a proxy host? */
804 host = g_strdup (super->path_element->host);
806 if (host == NULL || *host == '\0')
808 vfs_print_message (_("ftpfs: Invalid host name."));
809 ftpfs_errno = EINVAL;
810 g_free (host);
811 return -1;
814 /* Hosts to connect to that start with a ! should use proxy */
815 tmp_port = super->path_element->port;
817 if (SUP->proxy != NULL)
818 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
820 g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_port);
821 if (port[0] == '\0')
823 g_free (host);
824 ftpfs_errno = errno;
825 return -1;
828 tty_enable_interrupt_key (); /* clear the interrupt flag */
830 memset (&hints, 0, sizeof (struct addrinfo));
831 hints.ai_family = AF_UNSPEC;
832 hints.ai_socktype = SOCK_STREAM;
834 #ifdef AI_ADDRCONFIG
835 /* By default, only look up addresses using address types for
836 * which a local interface is configured (i.e. no IPv6 if no IPv6
837 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
838 hints.ai_flags = AI_ADDRCONFIG;
839 #endif
841 e = getaddrinfo (host, port, &hints, &res);
843 #ifdef AI_ADDRCONFIG
844 if (e == EAI_BADFLAGS)
846 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
847 hints.ai_flags = 0;
848 e = getaddrinfo (host, port, &hints, &res);
850 #endif
852 *port = '\0';
854 if (e != 0)
856 tty_disable_interrupt_key ();
857 vfs_print_message (_("ftpfs: %s"), gai_strerror (e));
858 g_free (host);
859 ftpfs_errno = EINVAL;
860 return -1;
863 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
865 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
867 if (my_socket < 0)
869 if (curr_res->ai_next != NULL)
870 continue;
872 tty_disable_interrupt_key ();
873 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno));
874 g_free (host);
875 freeaddrinfo (res);
876 ftpfs_errno = errno;
877 return -1;
880 vfs_print_message (_("ftpfs: making connection to %s"), host);
881 g_free (host);
882 host = NULL;
884 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
885 break;
887 ftpfs_errno = errno;
888 close (my_socket);
890 if (errno == EINTR && tty_got_interrupt ())
891 vfs_print_message (_("ftpfs: connection interrupted by user"));
892 else if (res->ai_next == NULL)
893 vfs_print_message (_("ftpfs: connection to server failed: %s"),
894 unix_error_string (errno));
895 else
896 continue;
898 freeaddrinfo (res);
899 tty_disable_interrupt_key ();
900 return -1;
903 freeaddrinfo (res);
904 tty_disable_interrupt_key ();
905 return my_socket;
908 /* --------------------------------------------------------------------------------------------- */
910 static int
911 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
913 int retry_seconds = 0;
914 int count_down;
916 /* We do not want to use the passive if we are using proxies */
917 if (SUP->proxy)
918 SUP->use_passive_connection = ftpfs_use_passive_connections_over_proxy;
922 SUP->failed_on_login = 0;
924 SUP->sock = ftpfs_open_socket (me, super);
925 if (SUP->sock == -1)
926 return -1;
928 if (ftpfs_login_server (me, super, NULL) != 0)
930 /* Logged in, no need to retry the connection */
931 break;
933 else
935 if (!SUP->failed_on_login)
936 return -1;
938 /* Close only the socket descriptor */
939 close (SUP->sock);
941 if (ftpfs_retry_seconds != 0)
943 retry_seconds = ftpfs_retry_seconds;
944 tty_enable_interrupt_key ();
945 for (count_down = retry_seconds; count_down; count_down--)
947 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
948 count_down);
949 sleep (1);
950 if (tty_got_interrupt ())
952 /* ftpfs_errno = E; */
953 tty_disable_interrupt_key ();
954 return 0;
957 tty_disable_interrupt_key ();
961 while (retry_seconds != 0);
963 return 0;
966 /* --------------------------------------------------------------------------------------------- */
968 static int
969 ftpfs_open_archive (struct vfs_s_super *super,
970 const vfs_path_t * vpath, const vfs_path_element_t * vpath_element)
972 (void) vpath;
974 super->data = g_new0 (ftp_super_data_t, 1);
976 super->path_element = ftpfs_correct_url_parameters (vpath_element);
977 SUP->proxy = NULL;
978 if (ftpfs_check_proxy (super->path_element->host))
979 SUP->proxy = ftpfs_proxy_host;
980 SUP->use_passive_connection = ftpfs_use_passive_connections;
981 SUP->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
982 SUP->isbinary = TYPE_UNKNOWN;
983 SUP->remote_is_amiga = 0;
984 super->name = g_strdup ("/");
985 super->root =
986 vfs_s_new_inode (vpath_element->class, super,
987 vfs_s_default_stat (vpath_element->class, S_IFDIR | 0755));
989 return ftpfs_open_archive_int (vpath_element->class, super);
992 /* --------------------------------------------------------------------------------------------- */
994 static int
995 ftpfs_archive_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *super,
996 const vfs_path_t * vpath, void *cookie)
998 vfs_path_element_t *path_element;
999 int result;
1001 (void) vpath;
1002 (void) cookie;
1004 path_element = ftpfs_correct_url_parameters (vpath_element);
1006 result = ((strcmp (path_element->host, super->path_element->host) == 0)
1007 && (strcmp (path_element->user, super->path_element->user) == 0)
1008 && (path_element->port == super->path_element->port)) ? 1 : 0;
1010 vfs_path_element_free (path_element);
1011 return result;
1014 /* --------------------------------------------------------------------------------------------- */
1015 /* Setup Passive PASV FTP connection */
1017 static int
1018 ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
1019 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1021 char *c;
1022 char n[6];
1023 int xa, xb, xc, xd, xe, xf;
1025 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
1026 return 0;
1028 /* Parse remote parameters */
1029 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
1031 if (!*c)
1032 return 0;
1033 if (!isdigit ((unsigned char) *c))
1034 return 0;
1035 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1036 return 0;
1038 n[0] = (unsigned char) xa;
1039 n[1] = (unsigned char) xb;
1040 n[2] = (unsigned char) xc;
1041 n[3] = (unsigned char) xd;
1042 n[4] = (unsigned char) xe;
1043 n[5] = (unsigned char) xf;
1045 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1046 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1048 if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
1049 return 0;
1051 return 1;
1054 /* --------------------------------------------------------------------------------------------- */
1055 /* Setup Passive EPSV FTP connection */
1057 static int
1058 ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
1059 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1061 char *c;
1062 int port;
1064 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") != COMPLETE)
1065 return 0;
1067 /* (|||<port>|) */
1068 c = strchr (reply_str, '|');
1069 if (c == NULL)
1070 return 0;
1071 if (strlen (c) > 3)
1072 c += 3;
1073 else
1074 return 0;
1076 port = atoi (c);
1077 if (port < 0 || port > 65535)
1078 return 0;
1079 port = htons (port);
1081 switch (sa->ss_family)
1083 case AF_INET:
1084 ((struct sockaddr_in *) sa)->sin_port = port;
1085 break;
1086 case AF_INET6:
1087 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1088 break;
1091 return (connect (my_socket, (struct sockaddr *) sa, *salen) < 0) ? 0 : 1;
1094 /* --------------------------------------------------------------------------------------------- */
1095 /* Setup Passive ftp connection, we use it for source routed connections */
1097 static int
1098 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
1099 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1101 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1102 if (sa->ss_family == AF_INET)
1104 if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
1105 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1106 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1107 return 0;
1109 /* It's IPV6, so EPSV is our only hope */
1110 else
1112 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1113 return 0;
1116 return 1;
1119 /* --------------------------------------------------------------------------------------------- */
1120 /* Setup Active PORT or EPRT FTP connection */
1122 static int
1123 ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super,
1124 struct sockaddr_storage data_addr, socklen_t data_addrlen)
1126 unsigned short int port;
1127 char *addr;
1128 unsigned int af;
1130 switch (data_addr.ss_family)
1132 case AF_INET:
1133 af = FTP_INET;
1134 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1135 break;
1136 case AF_INET6:
1137 af = FTP_INET6;
1138 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1139 break;
1140 /* Not implemented */
1141 default:
1142 return 0;
1145 addr = g_try_malloc (NI_MAXHOST);
1146 if (addr == NULL)
1147 ERRNOR (ENOMEM, -1);
1149 if (getnameinfo
1150 ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1151 NI_NUMERICHOST) != 0)
1153 g_free (addr);
1154 ERRNOR (EIO, -1);
1157 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1158 if (af == FTP_INET)
1160 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1161 unsigned char *p = (unsigned char *) &port;
1163 if (ftpfs_command (me, super, WAIT_REPLY,
1164 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1165 p[0], p[1]) == COMPLETE)
1167 g_free (addr);
1168 return 1;
1173 * Converts network MSB first order to host byte order (LSB
1174 * first on i386). If we do it earlier, we will run into an
1175 * endianness issue, because the server actually expects to see
1176 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1178 port = ntohs (port);
1180 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1181 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
1183 g_free (addr);
1184 return 1;
1187 g_free (addr);
1188 return 0;
1191 /* --------------------------------------------------------------------------------------------- */
1192 /* Initialize a socket for FTP DATA connection */
1194 static int
1195 ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super,
1196 struct sockaddr_storage *data_addr, socklen_t * data_addrlen)
1198 int result;
1200 memset (data_addr, 0, sizeof (struct sockaddr_storage));
1201 *data_addrlen = sizeof (struct sockaddr_storage);
1203 if (SUP->use_passive_connection)
1204 result = getpeername (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1205 else
1206 result = getsockname (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1208 if (result == -1)
1209 return -1;
1211 switch (data_addr->ss_family)
1213 case AF_INET:
1214 ((struct sockaddr_in *) data_addr)->sin_port = 0;
1215 break;
1216 case AF_INET6:
1217 ((struct sockaddr_in6 *) data_addr)->sin6_port = 0;
1218 break;
1219 default:
1220 vfs_print_message (_("ftpfs: invalid address family"));
1221 ERRNOR (EINVAL, -1);
1224 result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP);
1226 if (result < 0)
1228 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1229 return -1;
1232 return result;
1235 /* --------------------------------------------------------------------------------------------- */
1236 /* Initialize FTP DATA connection */
1238 static int
1239 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1241 struct sockaddr_storage data_addr;
1242 socklen_t data_addrlen;
1245 * Don't factor socket initialization out of these conditionals,
1246 * because ftpfs_init_data_socket initializes it in different way
1247 * depending on use_passive_connection flag.
1250 /* Try to establish a passive connection first (if requested) */
1251 if (SUP->use_passive_connection)
1253 int data_sock;
1255 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1256 if (data_sock < 0)
1257 return -1;
1259 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1260 return data_sock;
1262 vfs_print_message (_("ftpfs: could not setup passive mode"));
1263 SUP->use_passive_connection = 0;
1265 close (data_sock);
1268 /* If passive setup is diabled or failed, fallback to active connections */
1269 if (!SUP->use_passive_connection)
1271 int data_sock;
1273 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1274 if (data_sock < 0)
1275 return -1;
1277 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) == 0) &&
1278 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) == 0) &&
1279 (listen (data_sock, 1) == 0) &&
1280 (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0))
1281 return data_sock;
1283 close (data_sock);
1286 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1287 SUP->use_passive_connection = SUP->proxy != NULL ? ftpfs_use_passive_connections_over_proxy :
1288 ftpfs_use_passive_connections;
1290 ftpfs_errno = EIO;
1291 return -1;
1294 /* --------------------------------------------------------------------------------------------- */
1296 static int
1297 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1298 const char *remote, int isbinary, int reget)
1300 struct sockaddr_storage from;
1301 int s, j, data;
1302 socklen_t fromlen = sizeof (from);
1304 s = ftpfs_initconn (me, super);
1305 if (s == -1)
1306 return -1;
1308 if (ftpfs_changetype (me, super, isbinary) == -1)
1309 return -1;
1310 if (reget > 0)
1312 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1313 if (j != CONTINUE)
1314 return -1;
1316 if (remote)
1318 char *remote_path = ftpfs_translate_path (me, super, remote);
1319 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1320 /* WarFtpD can't STORE //filename */
1321 (*remote_path == '/') ? remote_path + 1 : remote_path);
1322 g_free (remote_path);
1324 else
1325 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1327 if (j != PRELIM)
1328 ERRNOR (EPERM, -1);
1329 tty_enable_interrupt_key ();
1330 if (SUP->use_passive_connection)
1331 data = s;
1332 else
1334 data = accept (s, (struct sockaddr *) &from, &fromlen);
1335 if (data < 0)
1337 ftpfs_errno = errno;
1338 close (s);
1339 return -1;
1341 close (s);
1343 tty_disable_interrupt_key ();
1344 return data;
1347 /* --------------------------------------------------------------------------------------------- */
1349 static void
1350 ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t * fh)
1352 struct vfs_s_super *super = FH_SUPER;
1353 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1354 fd_set mask;
1355 char buf[BUF_8K];
1356 int dsock = FH_SOCK;
1357 FH_SOCK = -1;
1358 SUP->ctl_connection_busy = 0;
1360 vfs_print_message (_("ftpfs: aborting transfer."));
1361 if (send (SUP->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1363 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1364 if (dsock != -1)
1365 close (dsock);
1366 return;
1369 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1371 vfs_print_message (_("ftpfs: abort failed"));
1372 if (dsock != -1)
1373 close (dsock);
1374 return;
1376 if (dsock != -1)
1378 FD_ZERO (&mask);
1379 FD_SET (dsock, &mask);
1380 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1382 struct timeval start_tim, tim;
1383 gettimeofday (&start_tim, NULL);
1384 /* flush the remaining data */
1385 while (read (dsock, buf, sizeof (buf)) > 0)
1387 gettimeofday (&tim, NULL);
1388 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT)
1390 /* server keeps sending, drop the connection and ftpfs_reconnect */
1391 close (dsock);
1392 ftpfs_reconnect (me, super);
1393 return;
1397 close (dsock);
1399 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) == TRANSIENT) && (code == 426))
1400 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1403 /* --------------------------------------------------------------------------------------------- */
1405 #if 0
1406 static void
1407 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1408 struct vfs_s_inode *dir)
1410 struct linklist *flist;
1411 struct direntry *fe, *fel;
1412 char tmp[MC_MAXPATHLEN];
1413 int depth;
1415 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1416 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1418 /* flist->data->l_stat is alread initialized with 0 */
1419 fel = flist->data;
1420 if (S_ISLNK (fel->s.st_mode) && fel->linkname)
1422 if (fel->linkname[0] == '/')
1424 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1425 continue;
1426 strcpy (tmp, fel->linkname);
1428 else
1430 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1431 continue;
1432 strcpy (tmp, dir->remote_path);
1433 if (tmp[1] != '\0')
1434 strcat (tmp, "/");
1435 strcat (tmp + 1, fel->linkname);
1437 for (depth = 0; depth < 100; depth++)
1438 { /* depth protects against recursive symbolic links */
1439 canonicalize_pathname (tmp);
1440 fe = _get_file_entry (bucket, tmp, 0, 0);
1441 if (fe)
1443 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1445 /* Symlink points to link which isn't resolved, yet. */
1446 if (fe->linkname[0] == '/')
1448 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1449 break;
1450 strcpy (tmp, fe->linkname);
1452 else
1454 /* at this point tmp looks always like this
1455 /directory/filename, i.e. no need to check
1456 strrchr's return value */
1457 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1458 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1459 break;
1460 strcat (tmp, fe->linkname);
1462 continue;
1464 else
1466 fel->l_stat = g_new (struct stat, 1);
1467 if (S_ISLNK (fe->s.st_mode))
1468 *fel->l_stat = *fe->l_stat;
1469 else
1470 *fel->l_stat = fe->s;
1471 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1474 break;
1478 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1481 /* --------------------------------------------------------------------------------------------- */
1483 static void
1484 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1485 struct vfs_s_inode *dir)
1487 char buffer[2048] = "", *filename;
1488 int sock;
1489 FILE *fp;
1490 struct stat s;
1491 struct linklist *flist;
1492 struct direntry *fe;
1493 int switch_method = 0;
1495 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1496 if (strchr (dir->remote_path, ' '))
1498 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1500 vfs_print_message (_("ftpfs: CWD failed."));
1501 return;
1503 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1505 else
1506 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1508 if (sock == -1)
1510 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1511 return;
1514 fp = fdopen (sock, "r");
1515 if (fp == NULL)
1517 close (sock);
1518 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1519 return;
1521 tty_enable_interrupt_key ();
1522 flist = dir->file_list->next;
1523 while (1)
1527 if (flist == dir->file_list)
1528 goto done;
1529 fe = flist->data;
1530 flist = flist->next;
1532 while (!S_ISLNK (fe->s.st_mode));
1533 while (1)
1535 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1536 goto done;
1537 if (MEDATA->logfile)
1539 fputs (buffer, MEDATA->logfile);
1540 fflush (MEDATA->logfile);
1542 vfs_die ("This code should be commented out\n");
1543 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1545 int r = strcmp (fe->name, filename);
1546 g_free (filename);
1547 if (r == 0)
1549 if (S_ISLNK (s.st_mode))
1551 /* This server doesn't understand LIST -lLa */
1552 switch_method = 1;
1553 goto done;
1555 fe->l_stat = g_new (struct stat, 1);
1556 if (fe->l_stat == NULL)
1557 goto done;
1558 *fe->l_stat = s;
1559 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1560 break;
1562 if (r < 0)
1563 break;
1567 done:
1568 while (fgets (buffer, sizeof (buffer), fp) != NULL);
1569 tty_disable_interrupt_key ();
1570 fclose (fp);
1571 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1574 /* --------------------------------------------------------------------------------------------- */
1576 static void
1577 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1579 vfs_print_message (_("Resolving symlink..."));
1581 if (SUP->strict_rfc959_list_cmd)
1582 resolve_symlink_without_ls_options (me, super, dir);
1583 else
1584 resolve_symlink_with_ls_options (me, super, dir);
1586 #endif
1588 /* --------------------------------------------------------------------------------------------- */
1590 static int
1591 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1593 struct vfs_s_entry *ent;
1594 struct vfs_s_super *super = dir->super;
1595 int sock, num_entries = 0;
1596 char lc_buffer[BUF_8K];
1597 int cd_first;
1599 cd_first = ftpfs_first_cd_then_ls || (SUP->strict == RFC_STRICT)
1600 || (strchr (remote_path, ' ') != NULL);
1602 again:
1603 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1604 remote_path,
1605 SUP->strict ==
1606 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1608 if (cd_first)
1610 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1612 ftpfs_errno = ENOENT;
1613 vfs_print_message (_("ftpfs: CWD failed."));
1614 return -1;
1618 gettimeofday (&dir->timestamp, NULL);
1619 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1621 if (SUP->strict == RFC_STRICT)
1622 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1623 else if (cd_first)
1624 /* Dirty hack to avoid autoprepending / to . */
1625 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1626 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1627 else
1629 char *path;
1631 /* Trailing "/." is necessary if remote_path is a symlink */
1632 path = mc_build_filename (remote_path, ".", NULL);
1633 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1634 g_free (path);
1637 if (sock == -1)
1638 goto fallback;
1640 /* Clear the interrupt flag */
1641 tty_enable_interrupt_key ();
1643 vfs_parse_ls_lga_init ();
1644 while (1)
1646 int i;
1647 size_t count_spaces = 0;
1648 int res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer),
1649 sock);
1650 if (!res)
1651 break;
1653 if (res == EINTR)
1655 me->verrno = ECONNRESET;
1656 close (sock);
1657 tty_disable_interrupt_key ();
1658 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1659 vfs_print_message (_("%s: failure"), me->name);
1660 return -1;
1663 if (MEDATA->logfile)
1665 fputs (lc_buffer, MEDATA->logfile);
1666 fputs ("\n", MEDATA->logfile);
1667 fflush (MEDATA->logfile);
1670 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1671 i = ent->ino->st.st_nlink;
1672 if (!vfs_parse_ls_lga
1673 (lc_buffer, &ent->ino->st, &ent->name, &ent->ino->linkname, &count_spaces))
1675 vfs_s_free_entry (me, ent);
1676 continue;
1678 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1679 num_entries++;
1680 vfs_s_store_filename_leading_spaces (ent, count_spaces);
1681 vfs_s_insert_entry (me, dir, ent);
1684 close (sock);
1685 me->verrno = E_REMOTE;
1686 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1687 goto fallback;
1689 if (num_entries == 0 && cd_first == 0)
1691 /* The LIST command may produce an empty output. In such scenario
1692 it is not clear whether this is caused by `remote_path' being
1693 a non-existent path or for some other reason (listing emtpy
1694 directory without the -a option, non-readable directory, etc.).
1696 Since `dir_load' is a crucial method, when it comes to determine
1697 whether a given path is a _directory_, the code must try its best
1698 to determine the type of `remote_path'. The only reliable way to
1699 achieve this is trough issuing a CWD command. */
1701 cd_first = 1;
1702 goto again;
1705 vfs_s_normalize_filename_leading_spaces (dir, vfs_parse_ls_lga_get_final_spaces ());
1707 if (SUP->strict == RFC_AUTODETECT)
1708 SUP->strict = RFC_DARING;
1710 vfs_print_message (_("%s: done."), me->name);
1711 return 0;
1713 fallback:
1714 if (SUP->strict == RFC_AUTODETECT)
1716 /* It's our first attempt to get a directory listing from this
1717 server (UNIX style LIST command) */
1718 SUP->strict = RFC_STRICT;
1719 /* I hate goto, but recursive call needs another 8K on stack */
1720 /* return ftpfs_dir_load (me, dir, remote_path); */
1721 cd_first = 1;
1722 goto again;
1724 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1725 ERRNOR (EACCES, -1);
1728 /* --------------------------------------------------------------------------------------------- */
1730 static int
1731 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname)
1733 int h, sock, n_read, n_written;
1734 off_t n_stored;
1735 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1736 struct linger li;
1737 #else
1738 int flag_one = 1;
1739 #endif
1740 char lc_buffer[BUF_8K];
1741 struct stat s;
1742 char *w_buf;
1743 struct vfs_s_super *super = FH_SUPER;
1744 ftp_fh_data_t *ftp = (ftp_fh_data_t *) fh->data;
1746 h = open (localname, O_RDONLY);
1747 if (h == -1)
1748 ERRNOR (EIO, -1);
1750 sock =
1751 ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1752 if (sock < 0 || fstat (h, &s) == -1)
1754 close (h);
1755 return -1;
1757 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1758 li.l_onoff = 1;
1759 li.l_linger = 120;
1760 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1761 #else
1762 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1763 #endif
1764 n_stored = 0;
1766 tty_enable_interrupt_key ();
1767 while (TRUE)
1769 while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1771 if (errno == EINTR)
1773 if (tty_got_interrupt ())
1775 ftpfs_errno = EINTR;
1776 goto error_return;
1778 else
1779 continue;
1781 ftpfs_errno = errno;
1782 goto error_return;
1784 if (n_read == 0)
1785 break;
1786 n_stored += n_read;
1787 w_buf = lc_buffer;
1788 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1790 if (n_written == -1)
1792 if (errno == EINTR && !tty_got_interrupt ())
1794 continue;
1796 ftpfs_errno = errno;
1797 goto error_return;
1799 w_buf += n_written;
1800 n_read -= n_written;
1802 vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
1803 _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
1805 tty_disable_interrupt_key ();
1806 close (sock);
1807 close (h);
1808 if (ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE)
1809 ERRNOR (EIO, -1);
1810 return 0;
1811 error_return:
1812 tty_disable_interrupt_key ();
1813 close (sock);
1814 close (h);
1815 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1816 return -1;
1819 /* --------------------------------------------------------------------------------------------- */
1821 static int
1822 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset)
1824 char *name;
1826 if (fh->data == NULL)
1827 fh->data = g_new0 (ftp_fh_data_t, 1);
1829 name = vfs_s_fullpath (me, fh->ino);
1830 if (name == NULL)
1831 return 0;
1832 FH_SOCK = ftpfs_open_data_connection (me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1833 g_free (name);
1834 if (FH_SOCK == -1)
1835 ERRNOR (EACCES, 0);
1836 fh->linear = LS_LINEAR_OPEN;
1837 ((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy = 1;
1838 ((ftp_fh_data_t *) fh->data)->append = 0;
1839 return 1;
1842 /* --------------------------------------------------------------------------------------------- */
1844 static int
1845 ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t * fh, void *buf, size_t len)
1847 ssize_t n;
1848 struct vfs_s_super *super = FH_SUPER;
1850 while ((n = read (FH_SOCK, buf, len)) < 0)
1852 if ((errno == EINTR) && !tty_got_interrupt ())
1853 continue;
1854 break;
1857 if (n < 0)
1858 ftpfs_linear_abort (me, fh);
1860 if (n == 0)
1862 SUP->ctl_connection_busy = 0;
1863 close (FH_SOCK);
1864 FH_SOCK = -1;
1865 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1866 ERRNOR (E_REMOTE, -1);
1867 return 0;
1869 ERRNOR (errno, n);
1872 /* --------------------------------------------------------------------------------------------- */
1874 static void
1875 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t * fh)
1877 if (FH_SOCK != -1)
1878 ftpfs_linear_abort (me, fh);
1881 /* --------------------------------------------------------------------------------------------- */
1883 static int
1884 ftpfs_ctl (void *fh, int ctlop, void *arg)
1886 (void) arg;
1888 switch (ctlop)
1890 case VFS_CTL_IS_NOTREADY:
1892 int v;
1894 if (!FH->linear)
1895 vfs_die ("You may not do this");
1896 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1897 return 0;
1899 v = vfs_s_select_on_two (((ftp_fh_data_t *) (FH->data))->sock, 0);
1900 return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
1902 default:
1903 return 0;
1907 /* --------------------------------------------------------------------------------------------- */
1909 static int
1910 ftpfs_send_command (const vfs_path_t * vpath, const char *cmd, int flags)
1912 const char *rpath;
1913 char *p;
1914 struct vfs_s_super *super;
1915 int r;
1916 const vfs_path_element_t *path_element;
1917 int flush_directory_cache = (flags & OPT_FLUSH);
1919 path_element = vfs_path_get_by_index (vpath, -1);
1921 rpath = vfs_s_get_path (vpath, &super, 0);
1922 if (rpath == NULL)
1923 return -1;
1925 p = ftpfs_translate_path (path_element->class, super, rpath);
1926 r = ftpfs_command (path_element->class, super, WAIT_REPLY, cmd, p);
1927 g_free (p);
1928 vfs_stamp_create (&vfs_ftpfs_ops, super);
1929 if (flags & OPT_IGNORE_ERROR)
1930 r = COMPLETE;
1931 if (r != COMPLETE)
1933 path_element->class->verrno = EPERM;
1934 return -1;
1936 if (flush_directory_cache)
1937 vfs_s_invalidate (path_element->class, super);
1938 return 0;
1941 /* --------------------------------------------------------------------------------------------- */
1943 static int
1944 ftpfs_chmod (const vfs_path_t * vpath, mode_t mode)
1946 char buf[BUF_SMALL];
1947 int ret;
1949 g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", (int) (mode & 07777));
1951 ret = ftpfs_send_command (vpath, buf, OPT_FLUSH);
1953 return ftpfs_ignore_chattr_errors ? 0 : ret;
1956 /* --------------------------------------------------------------------------------------------- */
1958 static int
1959 ftpfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
1961 #if 0
1962 (void) vpath;
1963 (void) owner;
1964 (void) group;
1966 ftpfs_errno = EPERM;
1967 return -1;
1968 #else
1969 /* Everyone knows it is not possible to chown remotely, so why bother them.
1970 If someone's root, then copy/move will always try to chown it... */
1971 (void) vpath;
1972 (void) owner;
1973 (void) group;
1974 return 0;
1975 #endif
1978 /* --------------------------------------------------------------------------------------------- */
1980 static int
1981 ftpfs_unlink (const vfs_path_t * vpath)
1983 return ftpfs_send_command (vpath, "DELE /%s", OPT_FLUSH);
1986 /* --------------------------------------------------------------------------------------------- */
1988 /* Return 1 if path is the same directory as the one we are in now */
1989 static int
1990 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1992 (void) me;
1994 if (super->path_element->path == NULL)
1995 return FALSE;
1996 return (strcmp (path, super->path_element->path) == 0);
1999 /* --------------------------------------------------------------------------------------------- */
2001 static int
2002 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
2004 int r;
2005 char *p;
2007 if (!SUP->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
2008 return COMPLETE;
2010 p = ftpfs_translate_path (me, super, remote_path);
2011 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
2012 g_free (p);
2014 if (r != COMPLETE)
2015 ftpfs_errno = EIO;
2016 else
2017 SUP->cwd_deferred = 0;
2018 return r;
2021 /* --------------------------------------------------------------------------------------------- */
2023 static int
2024 ftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
2026 ftpfs_send_command (vpath1, "RNFR /%s", OPT_FLUSH);
2027 return ftpfs_send_command (vpath2, "RNTO /%s", OPT_FLUSH);
2030 /* --------------------------------------------------------------------------------------------- */
2032 static int
2033 ftpfs_mkdir (const vfs_path_t * vpath, mode_t mode)
2035 (void) mode; /* FIXME: should be used */
2037 return ftpfs_send_command (vpath, "MKD /%s", OPT_FLUSH);
2040 /* --------------------------------------------------------------------------------------------- */
2042 static int
2043 ftpfs_rmdir (const vfs_path_t * vpath)
2045 return ftpfs_send_command (vpath, "RMD /%s", OPT_FLUSH);
2048 /* --------------------------------------------------------------------------------------------- */
2050 static void
2051 ftpfs_fh_free_data (vfs_file_handler_t * fh)
2053 if (fh != NULL)
2055 g_free (fh->data);
2056 fh->data = NULL;
2060 /* --------------------------------------------------------------------------------------------- */
2062 static int
2063 ftpfs_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
2065 ftp_fh_data_t *ftp;
2067 (void) mode;
2069 fh->data = g_new0 (ftp_fh_data_t, 1);
2070 ftp = (ftp_fh_data_t *) fh->data;
2071 /* File will be written only, so no need to retrieve it from ftp server */
2072 if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
2074 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2075 struct linger li;
2076 #else
2077 int li = 1;
2078 #endif
2079 char *name;
2081 /* ftpfs_linear_start() called, so data will be written
2082 * to local temporary file and stored to ftp server
2083 * by vfs_s_close later
2085 if (((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy)
2087 if (!fh->ino->localname)
2089 vfs_path_t *vpath;
2090 int handle;
2092 handle = vfs_mkstemps (&vpath, me->name, fh->ino->ent->name);
2093 if (handle == -1)
2095 vfs_path_free (vpath);
2096 goto fail;
2098 close (handle);
2099 fh->ino->localname = vfs_path_to_str (vpath);
2100 vfs_path_free (vpath);
2101 ftp->append = flags & O_APPEND;
2103 return 0;
2105 name = vfs_s_fullpath (me, fh->ino);
2106 if (name == NULL)
2107 goto fail;
2108 fh->handle =
2109 ftpfs_open_data_connection (me, fh->ino->super,
2110 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
2111 g_free (name);
2113 if (fh->handle < 0)
2114 goto fail;
2115 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2116 li.l_onoff = 1;
2117 li.l_linger = 120;
2118 #endif
2119 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
2121 if (fh->ino->localname)
2123 unlink (fh->ino->localname);
2124 g_free (fh->ino->localname);
2125 fh->ino->localname = NULL;
2127 return 0;
2130 if (!fh->ino->localname && vfs_s_retrieve_file (me, fh->ino) == -1)
2131 goto fail;
2132 if (!fh->ino->localname)
2133 vfs_die ("retrieve_file failed to fill in localname");
2134 return 0;
2136 fail:
2137 ftpfs_fh_free_data (fh);
2138 return -1;
2141 /* --------------------------------------------------------------------------------------------- */
2143 static int
2144 ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t * fh)
2146 if (fh->handle != -1 && !fh->ino->localname)
2148 ftp_super_data_t *ftp = (ftp_super_data_t *) fh->ino->super->data;
2150 close (fh->handle);
2151 fh->handle = -1;
2152 /* File is stored to destination already, so
2153 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2155 fh->changed = 0;
2156 if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE)
2157 ERRNOR (EIO, -1);
2158 vfs_s_invalidate (me, FH_SUPER);
2161 return 0;
2164 /* --------------------------------------------------------------------------------------------- */
2166 static void
2167 ftpfs_done (struct vfs_class *me)
2169 (void) me;
2171 g_slist_foreach (no_proxy, (GFunc) g_free, NULL);
2172 g_slist_free (no_proxy);
2174 g_free (ftpfs_anonymous_passwd);
2175 g_free (ftpfs_proxy_host);
2178 /* --------------------------------------------------------------------------------------------- */
2180 static void
2181 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
2183 GList *iter;
2185 for (iter = MEDATA->supers; iter != NULL; iter = g_list_next (iter))
2187 const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
2188 char *name;
2190 name = vfs_path_element_build_pretty_path_str (super->path_element);
2191 func (name);
2192 g_free (name);
2196 /* --------------------------------------------------------------------------------------------- */
2198 static keyword_t
2199 ftpfs_netrc_next (void)
2201 char *p;
2202 keyword_t i;
2203 static const char *const keywords[] = { "default", "machine",
2204 "login", "password", "passwd", "account", "macdef", NULL
2207 while (1)
2209 netrcp = skip_separators (netrcp);
2210 if (*netrcp != '\n')
2211 break;
2212 netrcp++;
2214 if (!*netrcp)
2215 return NETRC_NONE;
2216 p = buffer;
2217 if (*netrcp == '"')
2219 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++)
2221 if (*netrcp == '\\')
2222 netrcp++;
2223 *p++ = *netrcp;
2226 else
2228 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
2229 *netrcp != ',' && *netrcp; netrcp++)
2231 if (*netrcp == '\\')
2232 netrcp++;
2233 *p++ = *netrcp;
2236 *p = 0;
2237 if (!*buffer)
2238 return NETRC_NONE;
2240 for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2241 if (strcmp (keywords[i - 1], buffer) == 0)
2242 return i;
2244 return NETRC_UNKNOWN;
2247 /* --------------------------------------------------------------------------------------------- */
2249 static int
2250 ftpfs_netrc_bad_mode (const char *netrcname)
2252 static int be_angry = 1;
2253 struct stat mystat;
2255 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077))
2257 if (be_angry)
2259 message (D_ERROR, MSG_ERROR,
2260 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2261 be_angry = 0;
2263 return 1;
2265 return 0;
2268 /* --------------------------------------------------------------------------------------------- */
2269 /* Scan .netrc until we find matching "machine" or "default"
2270 * domain is used for additional matching
2271 * No search is done after "default" in compliance with "man netrc"
2272 * Return 0 if found, -1 otherwise */
2274 static int
2275 ftpfs_find_machine (const char *host, const char *domain)
2277 keyword_t keyword;
2279 if (!host)
2280 host = "";
2281 if (!domain)
2282 domain = "";
2284 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2286 if (keyword == NETRC_DEFAULT)
2287 return 0;
2289 if (keyword == NETRC_MACDEF)
2291 /* Scan for an empty line, which concludes "macdef" */
2294 while (*netrcp && *netrcp != '\n')
2295 netrcp++;
2296 if (*netrcp != '\n')
2297 break;
2298 netrcp++;
2300 while (*netrcp && *netrcp != '\n');
2301 continue;
2304 if (keyword != NETRC_MACHINE)
2305 continue;
2307 /* Take machine name */
2308 if (ftpfs_netrc_next () == NETRC_NONE)
2309 break;
2311 if (g_ascii_strcasecmp (host, buffer) != 0)
2313 /* Try adding our domain to short names in .netrc */
2314 const char *host_domain = strchr (host, '.');
2315 if (!host_domain)
2316 continue;
2318 /* Compare domain part */
2319 if (g_ascii_strcasecmp (host_domain, domain) != 0)
2320 continue;
2322 /* Compare local part */
2323 if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2324 continue;
2327 return 0;
2330 /* end of .netrc */
2331 return -1;
2334 /* --------------------------------------------------------------------------------------------- */
2335 /* Extract login and password from .netrc for the host.
2336 * pass may be NULL.
2337 * Returns 0 for success, -1 for error */
2339 static int
2340 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2342 char *netrcname;
2343 char *tmp_pass = NULL;
2344 char hostname[MAXHOSTNAMELEN];
2345 const char *domain;
2346 keyword_t keyword;
2347 static struct rupcache
2349 struct rupcache *next;
2350 char *host;
2351 char *login;
2352 char *pass;
2353 } *rup_cache = NULL, *rupp;
2355 /* Initialize *login and *pass */
2356 g_free (*login);
2357 *login = NULL;
2358 g_free (*pass);
2359 *pass = NULL;
2361 /* Look up in the cache first */
2362 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2364 if (!strcmp (host, rupp->host))
2366 if (rupp->login)
2367 *login = g_strdup (rupp->login);
2368 if (pass && rupp->pass)
2369 *pass = g_strdup (rupp->pass);
2370 return 0;
2374 /* Load current .netrc */
2375 netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2376 if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2378 g_free (netrcname);
2379 return 0;
2382 netrcp = netrc;
2384 /* Find our own domain name */
2385 if (gethostname (hostname, sizeof (hostname)) < 0)
2386 *hostname = '\0';
2388 domain = strchr (hostname, '.');
2389 if (domain == NULL)
2390 domain = "";
2392 /* Scan for "default" and matching "machine" keywords */
2393 ftpfs_find_machine (host, domain);
2395 /* Scan for keywords following "default" and "machine" */
2396 while (1)
2398 int need_break = 0;
2399 keyword = ftpfs_netrc_next ();
2401 switch (keyword)
2403 case NETRC_LOGIN:
2404 if (ftpfs_netrc_next () == NETRC_NONE)
2406 need_break = 1;
2407 break;
2410 /* We have another name already - should not happen */
2411 if (*login)
2413 need_break = 1;
2414 break;
2417 /* We have login name now */
2418 *login = g_strdup (buffer);
2419 break;
2421 case NETRC_PASSWORD:
2422 case NETRC_PASSWD:
2423 if (ftpfs_netrc_next () == NETRC_NONE)
2425 need_break = 1;
2426 break;
2429 /* Ignore unsafe passwords */
2430 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2431 && ftpfs_netrc_bad_mode (netrcname))
2433 need_break = 1;
2434 break;
2437 /* Remember password. pass may be NULL, so use tmp_pass */
2438 if (tmp_pass == NULL)
2439 tmp_pass = g_strdup (buffer);
2440 break;
2442 case NETRC_ACCOUNT:
2443 /* "account" is followed by a token which we ignore */
2444 if (ftpfs_netrc_next () == NETRC_NONE)
2446 need_break = 1;
2447 break;
2450 /* Ignore account, but warn user anyways */
2451 ftpfs_netrc_bad_mode (netrcname);
2452 break;
2454 default:
2455 /* Unexpected keyword or end of file */
2456 need_break = 1;
2457 break;
2460 if (need_break)
2461 break;
2464 g_free (netrc);
2465 g_free (netrcname);
2467 rupp = g_new (struct rupcache, 1);
2468 rupp->host = g_strdup (host);
2469 rupp->login = g_strdup (*login);
2470 rupp->pass = g_strdup (tmp_pass);
2472 rupp->next = rup_cache;
2473 rup_cache = rupp;
2475 *pass = tmp_pass;
2477 return 0;
2480 /* --------------------------------------------------------------------------------------------- */
2481 /*** public functions ****************************************************************************/
2482 /* --------------------------------------------------------------------------------------------- */
2484 /** This routine is called as the last step in load_setup */
2485 void
2486 ftpfs_init_passwd (void)
2488 ftpfs_anonymous_passwd = load_anon_passwd ();
2489 if (ftpfs_anonymous_passwd)
2490 return;
2492 /* If there is no anonymous ftp password specified
2493 * then we'll just use anonymous@
2494 * We don't send any other thing because:
2495 * - We want to remain anonymous
2496 * - We want to stop SPAM
2497 * - We don't want to let ftp sites to discriminate by the user,
2498 * host or country.
2500 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
2503 /* --------------------------------------------------------------------------------------------- */
2505 void
2506 init_ftpfs (void)
2508 static struct vfs_s_subclass ftpfs_subclass;
2510 tcp_init ();
2512 ftpfs_subclass.flags = VFS_S_REMOTE | VFS_S_USETMP;
2513 ftpfs_subclass.archive_same = ftpfs_archive_same;
2514 ftpfs_subclass.open_archive = ftpfs_open_archive;
2515 ftpfs_subclass.free_archive = ftpfs_free_archive;
2516 ftpfs_subclass.fh_open = ftpfs_fh_open;
2517 ftpfs_subclass.fh_close = ftpfs_fh_close;
2518 ftpfs_subclass.fh_free_data = ftpfs_fh_free_data;
2519 ftpfs_subclass.dir_load = ftpfs_dir_load;
2520 ftpfs_subclass.file_store = ftpfs_file_store;
2521 ftpfs_subclass.linear_start = ftpfs_linear_start;
2522 ftpfs_subclass.linear_read = ftpfs_linear_read;
2523 ftpfs_subclass.linear_close = ftpfs_linear_close;
2525 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2526 vfs_ftpfs_ops.name = "ftpfs";
2527 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2528 vfs_ftpfs_ops.prefix = "ftp";
2529 vfs_ftpfs_ops.done = &ftpfs_done;
2530 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2531 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2532 vfs_ftpfs_ops.chown = ftpfs_chown;
2533 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2534 vfs_ftpfs_ops.rename = ftpfs_rename;
2535 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2536 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2537 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2538 vfs_register_class (&vfs_ftpfs_ops);
2541 /* --------------------------------------------------------------------------------------------- */