tar: avoid need for base64_init and extra table.
[midnight-commander.git] / src / vfs / ftpfs / ftpfs.c
blob6eb9c2aaf3cbced0366113b8d5e16e82fe98b82f
1 /*
2 Virtual File System: FTP file system.
4 Copyright (C) 1995-2024
5 Free Software Foundation, Inc.
7 Written by:
8 Ching Hui, 1995
9 Jakub Jelinek, 1995
10 Miguel de Icaza, 1995, 1996, 1997
11 Norbert Warmuth, 1997
12 Pavel Machek, 1998
13 Yury V. Zaytsev, 2010
14 Slava Zanko <slavazanko@gmail.com>, 2010, 2013
15 Andrew Borodin <aborodin@vmail.ru>, 2010-2022
17 This file is part of the Midnight Commander.
19 The Midnight Commander is free software: you can redistribute it
20 and/or modify it under the terms of the GNU General Public License as
21 published by the Free Software Foundation, either version 3 of the License,
22 or (at your option) any later version.
24 The Midnight Commander is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
29 You should have received a copy of the GNU General Public License
30 along with this program. If not, see <http://www.gnu.org/licenses/>.
33 /**
34 * \file
35 * \brief Source: Virtual File System: FTP file system
36 * \author Ching Hui
37 * \author Jakub Jelinek
38 * \author Miguel de Icaza
39 * \author Norbert Warmuth
40 * \author Pavel Machek
41 * \date 1995, 1997, 1998
43 * \todo
44 - make it more robust - all the connects etc. should handle EADDRINUSE and
45 ERETRY (have I spelled these names correctly?)
46 - make the user able to flush a connection - all the caches will get empty
47 etc., (tarfs as well), we should give there a user selectable timeout
48 and assign a key sequence.
49 - use hash table instead of linklist to cache ftpfs directory.
51 What to do with this?
54 * NOTE: Usage of tildes is deprecated, consider:
55 * \verbatim
56 cd ftp//:pavel@hobit
57 cd ~
58 \endverbatim
59 * And now: what do I want to do? Do I want to go to /home/pavel or to
60 * ftp://hobit/home/pavel? I think first has better sense...
62 \verbatim
64 int f = !strcmp( remote_path, "/~" );
65 if (f || !strncmp( remote_path, "/~/", 3 )) {
66 char *s;
67 s = mc_build_filename ( qhome (*bucket), remote_path +3-f, (char *) NULL );
68 g_free (remote_path);
69 remote_path = s;
72 \endverbatim
75 /* \todo Fix: Namespace pollution: horrible */
77 #include <config.h>
78 #include <stdio.h> /* sscanf() */
79 #include <stdlib.h> /* atoi() */
80 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
81 #include <netdb.h> /* struct hostent */
82 #include <sys/socket.h> /* AF_INET */
83 #include <netinet/in.h> /* struct in_addr */
84 #ifdef HAVE_ARPA_INET_H
85 #include <arpa/inet.h>
86 #endif
87 #include <arpa/ftp.h>
88 #include <arpa/telnet.h>
89 #ifdef HAVE_SYS_PARAM_H
90 #include <sys/param.h>
91 #endif
92 #include <errno.h>
93 #include <ctype.h>
94 #include <fcntl.h>
95 #include <inttypes.h> /* uintmax_t */
97 #include "lib/global.h"
98 #include "lib/file-entry.h"
99 #include "lib/util.h"
100 #include "lib/strutil.h" /* str_move() */
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 gboolean ftpfs_use_passive_connections = TRUE;
124 gboolean ftpfs_use_passive_connections_over_proxy = FALSE;
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 gboolean ftpfs_use_unix_list_options = TRUE;
133 /* First "CWD <path>", then "LIST -la ." */
134 gboolean ftpfs_first_cd_then_ls = TRUE;
136 /* Use the ~/.netrc */
137 gboolean ftpfs_use_netrc = TRUE;
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 /* whether we have to use proxy by default? */
147 gboolean ftpfs_always_use_proxy = FALSE;
149 gboolean ftpfs_ignore_chattr_errors = TRUE;
151 /*** file scope macro definitions ****************************************************************/
153 #ifndef MAXHOSTNAMELEN
154 #define MAXHOSTNAMELEN 64
155 #endif
157 #define FTP_SUPER(super) ((ftp_super_t *) (super))
158 #define FTP_FILE_HANDLER(fh) ((ftp_file_handler_t *) (fh))
159 #define FH_SOCK FTP_FILE_HANDLER(fh)->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 * G_USEC_PER_SEC)
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 struct vfs_s_super base; /* base class */
210 int sock;
212 char *proxy; /* proxy server, NULL if no proxy */
213 gboolean failed_on_login; /* used to pass the failure reason to upper levels */
214 gboolean use_passive_connection;
215 gboolean remote_is_amiga; /* No leading slash allowed for AmiTCP (Amiga) */
216 int isbinary;
217 gboolean cwd_deferred; /* current_directory was changed but CWD command hasn't
218 been sent yet */
219 int strict; /* ftp server doesn't understand
220 * "LIST -la <path>"; use "CWD <path>"/
221 * "LIST" instead
223 gboolean ctl_connection_busy;
224 char *current_dir;
225 } ftp_super_t;
227 typedef struct
229 vfs_file_handler_t base; /* base class */
231 int sock;
232 gboolean append;
233 } ftp_file_handler_t;
235 /*** forward declarations (file scope functions) *************************************************/
237 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
238 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super,
239 const char *remote_path);
240 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
241 static gboolean ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
242 const char *netrcpass);
243 static gboolean ftpfs_netrc_lookup (const char *host, char **login, char **pass);
245 /*** file scope variables ************************************************************************/
247 static int code;
249 static char reply_str[80];
251 static struct vfs_s_subclass ftpfs_subclass;
252 static struct vfs_class *vfs_ftpfs_ops = VFS_CLASS (&ftpfs_subclass);
254 static GSList *no_proxy = NULL;
256 static char buffer[BUF_MEDIUM];
257 static char *netrc = NULL;
258 static const char *netrcp;
260 /* --------------------------------------------------------------------------------------------- */
261 /*** file scope functions ************************************************************************/
262 /* --------------------------------------------------------------------------------------------- */
264 static void
265 ftpfs_set_blksize (struct stat *s)
267 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
268 /* redefine block size */
269 s->st_blksize = 64 * 1024; /* FIXME */
270 #endif
273 /* --------------------------------------------------------------------------------------------- */
275 static struct stat *
276 ftpfs_default_stat (struct vfs_class *me)
278 struct stat *s;
280 s = vfs_s_default_stat (me, S_IFDIR | 0755);
281 ftpfs_set_blksize (s);
282 vfs_adjust_stat (s);
284 return s;
287 /* --------------------------------------------------------------------------------------------- */
289 /* Translate a Unix path, i.e. MC's internal path representation (e.g.
290 /somedir/somefile) to a path valid for the remote server. Every path
291 transferred to the remote server has to be mangled by this function
292 right prior to sending it.
293 Currently only Amiga ftp servers are handled in a special manner.
295 When the remote server is an amiga:
296 a) strip leading slash if necessary
297 b) replace first occurrence of ":/" with ":"
298 c) strip trailing "/."
300 static char *
301 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
303 char *ret, *p;
305 if (!FTP_SUPER (super)->remote_is_amiga)
306 return g_strdup (remote_path);
308 if (me->logfile != NULL)
310 fprintf (me->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
311 fflush (me->logfile);
314 /* strip leading slash(es) */
315 while (IS_PATH_SEP (*remote_path))
316 remote_path++;
318 /* Don't change "/" into "", e.g. "CWD " would be invalid. */
319 if (*remote_path == '\0')
320 return g_strdup (".");
322 ret = g_strdup (remote_path);
324 /* replace first occurrence of ":/" with ":" */
325 p = strchr (ret, ':');
326 if (p != NULL && IS_PATH_SEP (p[1]))
327 str_move (p + 1, p + 2);
329 /* strip trailing "/." */
330 p = strrchr (ret, PATH_SEP);
331 if ((p != NULL) && (*(p + 1) == '.') && (*(p + 2) == '\0'))
332 *p = '\0';
334 return ret;
337 /* --------------------------------------------------------------------------------------------- */
338 /** Extract the hostname and username from the path */
340 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
341 * ftp://sunsite.unc.edu/pub/linux
342 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
343 * ftp://tsx-11.mit.edu:8192/
344 * ftp://joe@foo.edu:11321/private
345 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
346 * is supplied.
349 static vfs_path_element_t *
350 ftpfs_correct_url_parameters (const vfs_path_element_t *velement)
352 vfs_path_element_t *path_element = vfs_path_element_clone (velement);
354 if (path_element->port == 0)
355 path_element->port = FTP_COMMAND_PORT;
357 if (path_element->user == NULL)
359 /* Look up user and password in netrc */
360 if (ftpfs_use_netrc)
361 ftpfs_netrc_lookup (path_element->host, &path_element->user, &path_element->password);
363 if (path_element->user == NULL)
364 path_element->user = g_strdup ("anonymous");
366 /* Look up password in netrc for known user */
367 if (ftpfs_use_netrc && path_element->password == NULL)
369 char *new_user = NULL;
370 char *new_passwd = NULL;
372 ftpfs_netrc_lookup (path_element->host, &new_user, &new_passwd);
374 /* If user is different, remove password */
375 if (new_user != NULL && strcmp (path_element->user, new_user) != 0)
376 MC_PTR_FREE (path_element->password);
378 g_free (new_user);
379 g_free (new_passwd);
382 return path_element;
385 /* --------------------------------------------------------------------------------------------- */
386 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
388 static int
389 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
391 while (TRUE)
393 char answer[BUF_1K];
395 if (vfs_s_get_line (me, sock, answer, sizeof (answer), '\n') == 0)
397 if (string_buf != NULL)
398 *string_buf = '\0';
399 code = 421;
400 return 4;
403 /* cppcheck-suppress invalidscanf */
404 switch (sscanf (answer, "%d", &code))
406 case 0:
407 if (string_buf != NULL)
408 g_strlcpy (string_buf, answer, string_len);
409 code = 500;
410 return 5;
411 case 1:
412 if (answer[3] == '-')
414 while (TRUE)
416 int i;
418 if (vfs_s_get_line (me, sock, answer, sizeof (answer), '\n') == 0)
420 if (string_buf != NULL)
421 *string_buf = '\0';
422 code = 421;
423 return 4;
425 /* cppcheck-suppress invalidscanf */
426 if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' '))
427 break;
430 if (string_buf != NULL)
431 g_strlcpy (string_buf, answer, string_len);
432 return code / 100;
433 default:
434 break;
439 /* --------------------------------------------------------------------------------------------- */
441 static gboolean
442 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
444 ftp_super_t *ftp_super = FTP_SUPER (super);
445 int sock;
447 sock = ftpfs_open_socket (me, super);
448 if (sock != -1)
450 char *cwdir = ftp_super->current_dir;
452 close (ftp_super->sock);
453 ftp_super->sock = sock;
454 ftp_super->current_dir = NULL;
456 if (ftpfs_login_server (me, super, super->path_element->password))
458 if (cwdir == NULL)
459 return TRUE;
461 sock = ftpfs_chdir_internal (me, super, cwdir);
462 g_free (cwdir);
463 return (sock == COMPLETE);
466 ftp_super->current_dir = cwdir;
469 return FALSE;
472 /* --------------------------------------------------------------------------------------------- */
474 static int
475 G_GNUC_PRINTF (4, 5)
476 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt,
477 ...)
479 ftp_super_t *ftp_super = FTP_SUPER (super);
480 va_list ap;
481 GString *cmdstr;
482 int status;
483 static gboolean retry = FALSE;
484 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
486 cmdstr = g_string_sized_new (32);
487 va_start (ap, fmt);
488 g_string_vprintf (cmdstr, fmt, ap);
489 va_end (ap);
490 g_string_append (cmdstr, "\r\n");
492 if (me->logfile != NULL)
494 if (strncmp (cmdstr->str, "PASS ", 5) == 0)
495 fputs ("PASS <Password not logged>\r\n", me->logfile);
496 else
498 size_t ret;
500 ret = fwrite (cmdstr->str, cmdstr->len, 1, me->logfile);
501 (void) ret;
504 fflush (me->logfile);
507 got_sigpipe = 0;
508 tty_enable_interrupt_key ();
509 status = write (ftp_super->sock, cmdstr->str, cmdstr->len);
511 if (status < 0)
513 code = 421;
515 if (errno == EPIPE)
516 { /* Remote server has closed connection */
517 if (level == 0)
519 level = 1;
520 status = ftpfs_reconnect (me, super) ? 1 : 0;
521 level = 0;
522 if (status != 0 && (write (ftp_super->sock, cmdstr->str, cmdstr->len) > 0))
523 goto ok;
526 got_sigpipe = 1;
528 g_string_free (cmdstr, TRUE);
529 tty_disable_interrupt_key ();
530 return TRANSIENT;
533 retry = FALSE;
536 tty_disable_interrupt_key ();
538 if (wait_reply != NONE)
540 status = ftpfs_get_reply (me, ftp_super->sock,
541 (wait_reply & WANT_STRING) != 0 ? reply_str : NULL,
542 sizeof (reply_str) - 1);
543 if ((wait_reply & WANT_STRING) != 0 && !retry && level == 0 && code == 421)
545 retry = TRUE;
546 level = 1;
547 status = ftpfs_reconnect (me, super) ? 1 : 0;
548 level = 0;
549 if (status != 0 && (write (ftp_super->sock, cmdstr->str, cmdstr->len) > 0))
550 goto ok;
552 retry = FALSE;
553 g_string_free (cmdstr, TRUE);
554 return status;
557 g_string_free (cmdstr, TRUE);
558 return COMPLETE;
561 /* --------------------------------------------------------------------------------------------- */
563 static struct vfs_s_super *
564 ftpfs_new_archive (struct vfs_class *me)
566 ftp_super_t *arch;
568 arch = g_new0 (ftp_super_t, 1);
569 arch->base.me = me;
570 arch->base.name = g_strdup (PATH_SEP_STR);
571 arch->sock = -1;
572 arch->use_passive_connection = ftpfs_use_passive_connections;
573 arch->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
574 arch->isbinary = TYPE_UNKNOWN;
576 return VFS_SUPER (arch);
579 /* --------------------------------------------------------------------------------------------- */
581 static void
582 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
584 ftp_super_t *ftp_super = FTP_SUPER (super);
586 if (ftp_super->sock != -1)
588 vfs_print_message (_("ftpfs: Disconnecting from %s"), super->path_element->host);
589 ftpfs_command (me, super, NONE, "%s", "QUIT");
590 close (ftp_super->sock);
592 g_free (ftp_super->current_dir);
595 /* --------------------------------------------------------------------------------------------- */
597 static int
598 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
600 if (binary != FTP_SUPER (super)->isbinary)
602 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
603 ERRNOR (EIO, -1);
604 FTP_SUPER (super)->isbinary = binary;
606 return binary;
609 /* --------------------------------------------------------------------------------------------- */
610 /* This routine logs the user in */
612 static int
613 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
615 ftp_super_t *ftp_super = FTP_SUPER (super);
616 char *pass;
617 char *op;
618 char *name; /* login user name */
619 gboolean anon = FALSE;
620 char reply_string[BUF_MEDIUM];
622 ftp_super->isbinary = TYPE_UNKNOWN;
624 if (super->path_element->password != NULL) /* explicit password */
625 op = g_strdup (super->path_element->password);
626 else if (netrcpass != NULL) /* password from netrc */
627 op = g_strdup (netrcpass);
628 else if (strcmp (super->path_element->user, "anonymous") == 0
629 || strcmp (super->path_element->user, "ftp") == 0)
631 if (ftpfs_anonymous_passwd == NULL) /* default anonymous password */
632 ftpfs_init_passwd ();
633 op = g_strdup (ftpfs_anonymous_passwd);
634 anon = TRUE;
636 else
637 { /* ask user */
638 char *p;
640 p = g_strdup_printf (_("FTP: Password required for %s"), super->path_element->user);
641 op = vfs_get_password (p);
642 g_free (p);
643 if (op == NULL)
644 ERRNOR (EPERM, 0);
645 super->path_element->password = g_strdup (op);
648 if (!anon || me->logfile != NULL)
649 pass = op;
650 else
652 pass = g_strconcat ("-", op, (char *) NULL);
653 wipe_password (op);
656 /* Proxy server accepts: username@host-we-want-to-connect */
657 if (ftp_super->proxy != NULL)
658 name =
659 g_strconcat (super->path_element->user, "@",
660 super->path_element->host[0] ==
661 '!' ? super->path_element->host + 1 : super->path_element->host,
662 (char *) NULL);
663 else
664 name = g_strdup (super->path_element->user);
666 if (ftpfs_get_reply (me, ftp_super->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
668 char *reply_up;
670 reply_up = g_ascii_strup (reply_string, -1);
671 ftp_super->remote_is_amiga = strstr (reply_up, "AMIGA") != NULL;
672 if (strstr (reply_up, " SPFTP/1.0.0000 SERVER ") != NULL) /* handles `LIST -la` in a weird way */
673 ftp_super->strict = RFC_STRICT;
674 g_free (reply_up);
676 if (me->logfile != NULL)
678 fprintf (me->logfile, "MC -- remote_is_amiga = %s\n",
679 ftp_super->remote_is_amiga ? "yes" : "no");
680 fflush (me->logfile);
683 vfs_print_message ("%s", _("ftpfs: sending login name"));
685 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
687 case CONTINUE:
688 vfs_print_message ("%s", _("ftpfs: sending user password"));
689 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
690 if (code == CONTINUE)
692 char *p;
694 p = g_strdup_printf (_("FTP: Account required for user %s"),
695 super->path_element->user);
696 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "",
697 INPUT_COMPLETE_USERNAMES);
698 g_free (p);
699 if (op == NULL)
700 ERRNOR (EPERM, 0);
701 vfs_print_message ("%s", _("ftpfs: sending user account"));
702 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
703 g_free (op);
705 if (code != COMPLETE)
706 break;
708 MC_FALLTHROUGH;
710 case COMPLETE:
711 vfs_print_message ("%s", _("ftpfs: logged in"));
712 wipe_password (pass);
713 g_free (name);
714 return TRUE;
716 default:
717 ftp_super->failed_on_login = TRUE;
718 wipe_password (super->path_element->password);
719 super->path_element->password = NULL;
720 goto login_fail;
724 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
725 super->path_element->user);
727 login_fail:
728 wipe_password (pass);
729 g_free (name);
730 ERRNOR (EPERM, FALSE);
733 /* --------------------------------------------------------------------------------------------- */
735 static void
736 ftpfs_load_no_proxy_list (void)
738 /* FixMe: shouldn't be hardcoded!!! */
739 char *mc_file;
741 mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL);
742 if (exist_file (mc_file))
744 FILE *npf;
746 npf = fopen (mc_file, "r");
747 if (npf != NULL)
749 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
751 while (fgets (s, sizeof (s), npf) != NULL)
753 char *p;
755 p = strchr (s, '\n');
756 if (p == NULL) /* skip bogus entries */
758 int c;
760 while ((c = fgetc (npf)) != EOF && c != '\n')
763 else if (p != s)
765 *p = '\0';
766 no_proxy = g_slist_prepend (no_proxy, g_strdup (s));
770 fclose (npf);
774 g_free (mc_file);
777 /* --------------------------------------------------------------------------------------------- */
778 /* Return TRUE if FTP proxy should be used for this host, FALSE otherwise */
780 static gboolean
781 ftpfs_check_proxy (const char *host)
784 if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0')
785 return FALSE; /* sanity check */
787 if (*host == '!')
788 return TRUE;
790 if (!ftpfs_always_use_proxy)
791 return FALSE;
793 if (strchr (host, '.') == NULL)
794 return FALSE;
796 if (no_proxy == NULL)
798 GSList *npe;
800 ftpfs_load_no_proxy_list ();
802 for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe))
804 const char *domain = (const char *) npe->data;
806 if (domain[0] == '.')
808 size_t ld, lh;
810 ld = strlen (domain);
811 lh = strlen (host);
813 while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1])
815 ld--;
816 lh--;
819 if (ld == 0)
820 return FALSE;
822 else if (g_ascii_strcasecmp (host, domain) == 0)
823 return FALSE;
827 return TRUE;
830 /* --------------------------------------------------------------------------------------------- */
832 static void
833 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
835 vfs_path_element_t *path_element;
837 path_element = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
838 *host = path_element->host;
839 path_element->host = NULL;
840 *port = path_element->port;
841 vfs_path_element_free (path_element);
844 /* --------------------------------------------------------------------------------------------- */
846 static int
847 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
849 struct addrinfo hints, *res, *curr_res;
850 int my_socket = 0;
851 char *host = NULL;
852 char port[8];
853 int tmp_port = 0;
854 int e;
856 (void) me;
858 if (super->path_element->host == NULL || *super->path_element->host == '\0')
860 vfs_print_message ("%s", _("ftpfs: Invalid host name."));
861 me->verrno = EINVAL;
862 return (-1);
865 /* Use a proxy host? */
866 /* Hosts to connect to that start with a ! should use proxy */
867 if (FTP_SUPER (super)->proxy != NULL)
868 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
869 else
871 host = g_strdup (super->path_element->host);
872 tmp_port = super->path_element->port;
875 g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_port);
877 tty_enable_interrupt_key (); /* clear the interrupt flag */
879 memset (&hints, 0, sizeof (hints));
880 hints.ai_family = AF_UNSPEC;
881 hints.ai_socktype = SOCK_STREAM;
883 #ifdef AI_ADDRCONFIG
884 /* By default, only look up addresses using address types for
885 * which a local interface is configured (i.e. no IPv6 if no IPv6
886 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
887 hints.ai_flags = AI_ADDRCONFIG;
888 #endif
890 e = getaddrinfo (host, port, &hints, &res);
892 #ifdef AI_ADDRCONFIG
893 if (e == EAI_BADFLAGS)
895 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
896 hints.ai_flags = 0;
897 e = getaddrinfo (host, port, &hints, &res);
899 #endif
901 *port = '\0';
903 if (e != 0)
905 tty_disable_interrupt_key ();
906 vfs_print_message (_("ftpfs: %s"), gai_strerror (e));
907 g_free (host);
908 me->verrno = EINVAL;
909 return (-1);
912 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
914 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
916 if (my_socket < 0)
918 if (curr_res->ai_next != NULL)
919 continue;
921 tty_disable_interrupt_key ();
922 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno));
923 g_free (host);
924 freeaddrinfo (res);
925 me->verrno = errno;
926 return (-1);
929 vfs_print_message (_("ftpfs: making connection to %s"), host);
930 MC_PTR_FREE (host);
932 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
933 break;
935 me->verrno = errno;
936 close (my_socket);
938 if (errno == EINTR && tty_got_interrupt ())
939 vfs_print_message ("%s", _("ftpfs: connection interrupted by user"));
940 else if (res->ai_next == NULL)
941 vfs_print_message (_("ftpfs: connection to server failed: %s"),
942 unix_error_string (errno));
943 else
944 continue;
946 freeaddrinfo (res);
947 tty_disable_interrupt_key ();
948 return (-1);
951 freeaddrinfo (res);
952 tty_disable_interrupt_key ();
953 return my_socket;
956 /* --------------------------------------------------------------------------------------------- */
958 static int
959 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
961 ftp_super_t *ftp_super = FTP_SUPER (super);
962 int retry_seconds = 0;
964 /* We do not want to use the passive if we are using proxies */
965 if (ftp_super->proxy != NULL)
966 ftp_super->use_passive_connection = ftpfs_use_passive_connections_over_proxy;
970 ftp_super->failed_on_login = FALSE;
972 ftp_super->sock = ftpfs_open_socket (me, super);
973 if (ftp_super->sock == -1)
974 return (-1);
976 if (ftpfs_login_server (me, super, NULL))
978 /* Logged in, no need to retry the connection */
979 break;
982 if (!ftp_super->failed_on_login)
983 return (-1);
985 /* Close only the socket descriptor */
986 close (ftp_super->sock);
988 if (ftpfs_retry_seconds != 0)
990 int count_down;
992 retry_seconds = ftpfs_retry_seconds;
993 tty_enable_interrupt_key ();
994 for (count_down = retry_seconds; count_down != 0; count_down--)
996 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"), count_down);
997 sleep (1);
998 if (tty_got_interrupt ())
1000 /* me->verrno = E; */
1001 tty_disable_interrupt_key ();
1002 return 0;
1005 tty_disable_interrupt_key ();
1008 while (retry_seconds != 0);
1010 ftp_super->current_dir = ftpfs_get_current_directory (me, super);
1011 if (ftp_super->current_dir == NULL)
1012 ftp_super->current_dir = g_strdup (PATH_SEP_STR);
1014 return 0;
1017 /* --------------------------------------------------------------------------------------------- */
1019 static int
1020 ftpfs_open_archive (struct vfs_s_super *super,
1021 const vfs_path_t *vpath, const vfs_path_element_t *vpath_element)
1023 (void) vpath;
1025 super->path_element = ftpfs_correct_url_parameters (vpath_element);
1026 if (ftpfs_check_proxy (super->path_element->host))
1027 FTP_SUPER (super)->proxy = ftpfs_proxy_host;
1028 super->root =
1029 vfs_s_new_inode (vpath_element->class, super, ftpfs_default_stat (vpath_element->class));
1031 return ftpfs_open_archive_int (vpath_element->class, super);
1034 /* --------------------------------------------------------------------------------------------- */
1036 static int
1037 ftpfs_archive_same (const vfs_path_element_t *vpath_element, struct vfs_s_super *super,
1038 const vfs_path_t *vpath, void *cookie)
1040 vfs_path_element_t *path_element;
1041 int result;
1043 (void) vpath;
1044 (void) cookie;
1046 path_element = ftpfs_correct_url_parameters (vpath_element);
1048 result = ((strcmp (path_element->host, super->path_element->host) == 0)
1049 && (strcmp (path_element->user, super->path_element->user) == 0)
1050 && (path_element->port == super->path_element->port)) ? 1 : 0;
1052 vfs_path_element_free (path_element);
1053 return result;
1056 /* --------------------------------------------------------------------------------------------- */
1057 /* The returned directory should always contain a trailing slash */
1059 static char *
1060 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
1062 char buf[MC_MAXPATHLEN + 1];
1064 if (ftpfs_command (me, super, NONE, "%s", "PWD") == COMPLETE &&
1065 ftpfs_get_reply (me, FTP_SUPER (super)->sock, buf, sizeof (buf)) == COMPLETE)
1067 char *bufp = NULL;
1068 char *bufq;
1070 for (bufq = buf; *bufq != '\0'; bufq++)
1071 if (*bufq == '"')
1073 if (bufp == NULL)
1074 bufp = bufq + 1;
1075 else
1077 *bufq = '\0';
1079 if (*bufp != '\0')
1081 if (!IS_PATH_SEP (bufq[-1]))
1083 *bufq++ = PATH_SEP;
1084 *bufq = '\0';
1087 if (IS_PATH_SEP (*bufp))
1088 return g_strdup (bufp);
1090 /* If the remote server is an Amiga a leading slash
1091 might be missing. MC needs it because it is used
1092 as separator between hostname and path internally. */
1093 return g_strconcat (PATH_SEP_STR, bufp, (char *) NULL);
1096 break;
1101 me->verrno = EIO;
1102 return NULL;
1105 /* --------------------------------------------------------------------------------------------- */
1106 /* Setup Passive PASV FTP connection */
1108 static gboolean
1109 ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
1110 int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
1112 char *c;
1113 char n[6];
1114 int xa, xb, xc, xd, xe, xf;
1116 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "%s", "PASV") != COMPLETE)
1117 return FALSE;
1119 /* Parse remote parameters */
1120 for (c = reply_str + 4; *c != '\0' && !isdigit ((unsigned char) *c); c++)
1123 if (*c == '\0' || !isdigit ((unsigned char) *c))
1124 return FALSE;
1126 /* cppcheck-suppress invalidscanf */
1127 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1128 return FALSE;
1130 n[0] = (unsigned char) xa;
1131 n[1] = (unsigned char) xb;
1132 n[2] = (unsigned char) xc;
1133 n[3] = (unsigned char) xd;
1134 n[4] = (unsigned char) xe;
1135 n[5] = (unsigned char) xf;
1137 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1138 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1140 return (connect (my_socket, (struct sockaddr *) sa, *salen) >= 0);
1143 /* --------------------------------------------------------------------------------------------- */
1144 /* Setup Passive EPSV FTP connection */
1146 static gboolean
1147 ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
1148 int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
1150 char *c;
1151 int port;
1153 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "%s", "EPSV") != COMPLETE)
1154 return FALSE;
1156 /* (|||<port>|) */
1157 c = strchr (reply_str, '|');
1158 if (c == NULL || strlen (c) <= 3)
1159 return FALSE;
1161 c += 3;
1162 port = atoi (c);
1163 if (port < 0 || port > 65535)
1164 return FALSE;
1166 port = htons (port);
1168 switch (sa->ss_family)
1170 case AF_INET:
1171 ((struct sockaddr_in *) sa)->sin_port = port;
1172 break;
1173 case AF_INET6:
1174 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1175 break;
1176 default:
1177 break;
1180 return (connect (my_socket, (struct sockaddr *) sa, *salen) >= 0);
1183 /* --------------------------------------------------------------------------------------------- */
1184 /* Setup Passive ftp connection, we use it for source routed connections */
1186 static gboolean
1187 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
1188 int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
1190 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1191 if (sa->ss_family == AF_INET)
1193 if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
1194 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1195 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1196 return FALSE;
1198 /* It's IPV6, so EPSV is our only hope */
1199 else if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1200 return FALSE;
1202 return TRUE;
1205 /* --------------------------------------------------------------------------------------------- */
1206 /* Setup Active PORT or EPRT FTP connection */
1208 static int
1209 ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super,
1210 struct sockaddr_storage data_addr, socklen_t data_addrlen)
1212 unsigned short int port;
1213 char *addr;
1214 unsigned int af;
1215 int res;
1217 switch (data_addr.ss_family)
1219 case AF_INET:
1220 af = FTP_INET;
1221 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1222 break;
1223 case AF_INET6:
1224 af = FTP_INET6;
1225 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1226 break;
1227 default:
1228 /* Not implemented */
1229 return 0;
1232 addr = g_try_malloc (NI_MAXHOST);
1233 if (addr == NULL)
1234 ERRNOR (ENOMEM, -1);
1236 res =
1237 getnameinfo ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1238 NI_NUMERICHOST);
1239 if (res != 0)
1241 const char *err_str;
1243 g_free (addr);
1245 if (res == EAI_SYSTEM)
1247 me->verrno = errno;
1248 err_str = unix_error_string (me->verrno);
1250 else
1252 me->verrno = EIO;
1253 err_str = gai_strerror (res);
1256 vfs_print_message (_("ftpfs: could not make address-to-name translation: %s"), err_str);
1258 return (-1);
1261 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1262 if (af == FTP_INET)
1264 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1265 unsigned char *p = (unsigned char *) &port;
1267 if (ftpfs_command (me, super, WAIT_REPLY,
1268 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1269 p[0], p[1]) == COMPLETE)
1271 g_free (addr);
1272 return 1;
1277 * Converts network MSB first order to host byte order (LSB
1278 * first on i386). If we do it earlier, we will run into an
1279 * endianness issue, because the server actually expects to see
1280 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1282 port = ntohs (port);
1284 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1285 res =
1286 (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) ==
1287 COMPLETE) ? 1 : 0;
1288 g_free (addr);
1289 return res;
1292 /* --------------------------------------------------------------------------------------------- */
1293 /* Initialize a socket for FTP DATA connection */
1295 static int
1296 ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super,
1297 struct sockaddr_storage *data_addr, socklen_t *data_addrlen)
1299 const unsigned int attempts = 10;
1300 unsigned int i;
1301 ftp_super_t *ftp_super = FTP_SUPER (super);
1302 int result;
1304 for (i = 0; i < attempts; i++)
1306 memset (data_addr, 0, sizeof (*data_addr));
1307 *data_addrlen = sizeof (*data_addr);
1309 if (ftp_super->use_passive_connection)
1311 result = getpeername (ftp_super->sock, (struct sockaddr *) data_addr, data_addrlen);
1312 if (result == 0)
1313 break;
1315 me->verrno = errno;
1317 if (me->verrno == ENOTCONN)
1319 vfs_print_message (_("ftpfs: try reconnect to server, attempt %u"), i);
1320 if (ftpfs_reconnect (me, super))
1321 continue; /* get name of new socket */
1323 else
1325 /* error -- stop loop */
1326 vfs_print_message (_("ftpfs: could not get socket name: %s"),
1327 unix_error_string (me->verrno));
1330 else
1332 result = getsockname (ftp_super->sock, (struct sockaddr *) data_addr, data_addrlen);
1333 if (result == 0)
1334 break;
1336 me->verrno = errno;
1338 vfs_print_message (_("ftpfs: try reconnect to server, attempt %u"), i);
1339 if (ftpfs_reconnect (me, super))
1340 continue; /* get name of new socket */
1342 /* error -- stop loop */
1343 vfs_print_message ("%s", _("ftpfs: could not reconnect to server"));
1346 i = attempts;
1349 if (i >= attempts)
1350 return (-1);
1352 switch (data_addr->ss_family)
1354 case AF_INET:
1355 ((struct sockaddr_in *) data_addr)->sin_port = 0;
1356 break;
1357 case AF_INET6:
1358 ((struct sockaddr_in6 *) data_addr)->sin6_port = 0;
1359 break;
1360 default:
1361 vfs_print_message ("%s", _("ftpfs: invalid address family"));
1362 ERRNOR (EINVAL, -1);
1365 result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP);
1366 if (result < 0)
1368 me->verrno = errno;
1369 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (me->verrno));
1370 result = -1;
1373 return result;
1376 /* --------------------------------------------------------------------------------------------- */
1377 /* Initialize FTP DATA connection */
1379 static int
1380 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1382 ftp_super_t *ftp_super = FTP_SUPER (super);
1383 struct sockaddr_storage data_addr;
1384 socklen_t data_addrlen;
1387 * Don't factor socket initialization out of these conditionals,
1388 * because ftpfs_init_data_socket initializes it in different way
1389 * depending on use_passive_connection flag.
1392 /* Try to establish a passive connection first (if requested) */
1393 if (ftp_super->use_passive_connection)
1395 int data_sock;
1397 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1398 if (data_sock < 0)
1399 return (-1);
1401 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1402 return data_sock;
1404 vfs_print_message ("%s", _("ftpfs: could not setup passive mode"));
1405 ftp_super->use_passive_connection = FALSE;
1407 close (data_sock);
1410 /* If passive setup is disabled or failed, fallback to active connections */
1411 if (!ftp_super->use_passive_connection)
1413 int data_sock;
1415 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1416 if (data_sock < 0)
1417 return (-1);
1419 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) != 0) ||
1420 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) != 0) ||
1421 (listen (data_sock, 1) != 0))
1423 close (data_sock);
1424 ERRNOR (errno, -1);
1427 if (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0)
1428 return data_sock;
1430 close (data_sock);
1433 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1434 ftp_super->use_passive_connection =
1435 ftp_super->proxy !=
1436 NULL ? ftpfs_use_passive_connections_over_proxy : ftpfs_use_passive_connections;
1438 me->verrno = EIO;
1439 return (-1);
1442 /* --------------------------------------------------------------------------------------------- */
1444 static int
1445 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1446 const char *remote, int isbinary, int reget)
1448 ftp_super_t *ftp_super = FTP_SUPER (super);
1449 int s, j, data;
1451 /* FTP doesn't allow to open more than one file at a time */
1452 if (ftp_super->ctl_connection_busy)
1453 return (-1);
1455 s = ftpfs_initconn (me, super);
1456 if (s == -1)
1457 return (-1);
1459 if (ftpfs_changetype (me, super, isbinary) == -1)
1461 close (s);
1462 return (-1);
1465 if (reget > 0)
1467 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1468 if (j != CONTINUE)
1470 close (s);
1471 ERRNOR (EIO, -1);
1475 if (remote == NULL)
1476 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1477 else
1479 char *remote_path;
1481 remote_path = ftpfs_translate_path (me, super, remote);
1482 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1483 /* WarFtpD can't STORE //filename */
1484 IS_PATH_SEP (*remote_path) ? remote_path + 1 : remote_path);
1485 g_free (remote_path);
1488 if (j != PRELIM)
1490 close (s);
1491 ERRNOR (EPERM, -1);
1494 if (ftp_super->use_passive_connection)
1495 data = s;
1496 else
1498 struct sockaddr_storage from;
1499 socklen_t fromlen = sizeof (from);
1501 tty_enable_interrupt_key ();
1502 data = accept (s, (struct sockaddr *) &from, &fromlen);
1503 if (data < 0)
1504 me->verrno = errno;
1505 tty_disable_interrupt_key ();
1506 close (s);
1507 if (data < 0)
1508 return (-1);
1511 ftp_super->ctl_connection_busy = TRUE;
1512 return data;
1515 /* --------------------------------------------------------------------------------------------- */
1517 static void
1518 ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t *fh)
1520 struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
1521 ftp_super_t *ftp_super = FTP_SUPER (super);
1522 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1523 fd_set mask;
1524 int dsock = FH_SOCK;
1526 FH_SOCK = -1;
1527 ftp_super->ctl_connection_busy = FALSE;
1529 vfs_print_message ("%s", _("ftpfs: aborting transfer."));
1531 if (send (ftp_super->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1533 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1534 if (dsock != -1)
1535 close (dsock);
1536 return;
1539 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1541 vfs_print_message ("%s", _("ftpfs: abort failed"));
1542 if (dsock != -1)
1543 close (dsock);
1544 return;
1547 if (dsock != -1)
1549 FD_ZERO (&mask);
1550 FD_SET (dsock, &mask);
1552 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1554 gint64 start_tim;
1555 char buf[BUF_8K];
1557 start_tim = g_get_monotonic_time ();
1559 /* flush the remaining data */
1560 while (read (dsock, buf, sizeof (buf)) > 0)
1562 gint64 tim;
1564 tim = g_get_monotonic_time ();
1566 if (tim > start_tim + ABORT_TIMEOUT)
1568 /* server keeps sending, drop the connection and ftpfs_reconnect */
1569 close (dsock);
1570 ftpfs_reconnect (me, super);
1571 return;
1575 close (dsock);
1578 if ((ftpfs_get_reply (me, ftp_super->sock, NULL, 0) == TRANSIENT) && (code == 426))
1579 ftpfs_get_reply (me, ftp_super->sock, NULL, 0);
1582 /* --------------------------------------------------------------------------------------------- */
1584 #if 0
1585 static void
1586 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1587 struct vfs_s_inode *dir)
1589 struct linklist *flist;
1590 struct direntry *fe, *fel;
1591 char tmp[MC_MAXPATHLEN];
1593 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1594 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1596 /* flist->data->l_stat is already initialized with 0 */
1597 fel = flist->data;
1598 if (S_ISLNK (fel->s.st_mode) && fel->linkname != NULL)
1600 int depth;
1602 if (IS_PATH_SEP (fel->linkname[0]))
1604 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1605 continue;
1606 strcpy (tmp, fel->linkname);
1608 else
1610 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1611 continue;
1612 strcpy (tmp, dir->remote_path);
1613 if (tmp[1] != '\0')
1614 strcat (tmp, PATH_SEP_STR);
1615 strcat (tmp + 1, fel->linkname);
1618 for (depth = 0; depth < 100; depth++)
1619 { /* depth protects against recursive symbolic links */
1620 canonicalize_pathname (tmp);
1621 fe = _get_file_entry_t (bucket, tmp, 0, 0);
1622 if (fe != NULL)
1624 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1626 /* Symlink points to link which isn't resolved, yet. */
1627 if (IS_PATH_SEP (fe->linkname[0]))
1629 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1630 break;
1631 strcpy (tmp, fe->linkname);
1633 else
1635 /* at this point tmp looks always like this
1636 /directory/filename, i.e. no need to check
1637 strrchr's return value */
1638 *(strrchr (tmp, PATH_SEP) + 1) = '\0'; /* dirname */
1639 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1640 break;
1641 strcat (tmp, fe->linkname);
1643 continue;
1645 else
1647 fel->l_stat = g_new (struct stat, 1);
1648 if (S_ISLNK (fe->s.st_mode))
1649 *fel->l_stat = *fe->l_stat;
1650 else
1651 *fel->l_stat = fe->s;
1652 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1655 break;
1660 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1663 /* --------------------------------------------------------------------------------------------- */
1665 static void
1666 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1667 struct vfs_s_inode *dir)
1669 char buffer[2048] = "", *filename;
1670 int sock;
1671 FILE *fp;
1672 struct stat s;
1673 struct linklist *flist;
1674 struct direntry *fe;
1675 int switch_method = 0;
1677 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1678 if (strchr (dir->remote_path, ' ') == NULL)
1679 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1680 else
1682 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1684 vfs_print_message ("%s", _("ftpfs: CWD failed."));
1685 return;
1688 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1691 if (sock == -1)
1693 vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink"));
1694 return;
1697 fp = fdopen (sock, "r");
1698 if (fp == NULL)
1700 close (sock);
1701 vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink"));
1702 return;
1704 tty_enable_interrupt_key ();
1705 flist = dir->file_list->next;
1707 while (TRUE)
1711 if (flist == dir->file_list)
1712 goto done;
1714 fe = flist->data;
1715 flist = flist->next;
1717 while (!S_ISLNK (fe->s.st_mode));
1719 while (TRUE)
1721 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1722 goto done;
1724 if (me->logfile != NULL)
1726 fputs (buffer, me->logfile);
1727 fflush (me->logfile);
1730 vfs_die ("This code should be commented out\n");
1732 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1734 int r;
1736 r = strcmp (fe->name, filename);
1737 g_free (filename);
1738 if (r == 0)
1740 if (S_ISLNK (s.st_mode))
1742 /* This server doesn't understand LIST -lLa */
1743 switch_method = 1;
1744 goto done;
1747 fe->l_stat = g_try_new (struct stat, 1);
1748 if (fe->l_stat == NULL)
1749 goto done;
1751 *fe->l_stat = s;
1752 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1753 break;
1756 if (r < 0)
1757 break;
1762 done:
1763 while (fgets (buffer, sizeof (buffer), fp) != NULL)
1765 tty_disable_interrupt_key ();
1766 fclose (fp);
1767 ftpfs_get_reply (me, FTP_SUPER (super)->sock, NULL, 0);
1770 /* --------------------------------------------------------------------------------------------- */
1772 static void
1773 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1775 vfs_print_message ("%s", _("Resolving symlink..."));
1777 if (FTP_SUPER (super)->strict_rfc959_list_cmd)
1778 resolve_symlink_without_ls_options (me, super, dir);
1779 else
1780 resolve_symlink_with_ls_options (me, super, dir);
1782 #endif
1784 /* --------------------------------------------------------------------------------------------- */
1786 static int
1787 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, const char *remote_path)
1789 struct vfs_s_super *super = dir->super;
1790 ftp_super_t *ftp_super = FTP_SUPER (super);
1791 int sock;
1792 char lc_buffer[BUF_8K];
1793 int res;
1794 gboolean cd_first;
1795 GSList *dirlist = NULL;
1796 GSList *entlist;
1797 GSList *iter;
1798 int err_count = 0;
1800 cd_first = ftpfs_first_cd_then_ls || (ftp_super->strict == RFC_STRICT)
1801 || (strchr (remote_path, ' ') != NULL);
1803 again:
1804 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1805 remote_path,
1806 ftp_super->strict ==
1807 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1809 if (cd_first && ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1811 me->verrno = ENOENT;
1812 vfs_print_message ("%s", _("ftpfs: CWD failed."));
1813 return (-1);
1816 dir->timestamp = g_get_monotonic_time () + ftpfs_directory_timeout * G_USEC_PER_SEC;
1818 if (ftp_super->strict == RFC_STRICT)
1819 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1820 else if (cd_first)
1821 /* Dirty hack to avoid autoprepending / to . */
1822 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1823 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1824 else
1826 char *path;
1828 /* Trailing "/." is necessary if remote_path is a symlink */
1829 path = g_strconcat (remote_path, PATH_SEP_STR ".", (char *) NULL);
1830 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1831 g_free (path);
1834 if (sock == -1)
1836 fallback:
1837 if (ftp_super->strict == RFC_AUTODETECT)
1839 /* It's our first attempt to get a directory listing from this
1840 server (UNIX style LIST command) */
1841 ftp_super->strict = RFC_STRICT;
1842 /* I hate goto, but recursive call needs another 8K on stack */
1843 /* return ftpfs_dir_load (me, dir, remote_path); */
1844 cd_first = TRUE;
1845 goto again;
1848 vfs_print_message ("%s", _("ftpfs: failed; nowhere to fallback to"));
1849 ERRNOR (EACCES, -1);
1852 /* read full directory list, then parse it */
1853 while ((res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer), sock)) != 0)
1855 if (res == EINTR)
1857 me->verrno = ECONNRESET;
1858 close (sock);
1859 ftp_super->ctl_connection_busy = FALSE;
1860 ftpfs_get_reply (me, ftp_super->sock, NULL, 0);
1861 g_slist_free_full (dirlist, g_free);
1862 vfs_print_message (_("%s: failure"), me->name);
1863 return (-1);
1866 if (me->logfile != NULL)
1868 fputs (lc_buffer, me->logfile);
1869 fputs ("\n", me->logfile);
1870 fflush (me->logfile);
1873 dirlist = g_slist_prepend (dirlist, g_strdup (lc_buffer));
1876 close (sock);
1877 ftp_super->ctl_connection_busy = FALSE;
1878 me->verrno = E_REMOTE;
1879 if ((ftpfs_get_reply (me, ftp_super->sock, NULL, 0) != COMPLETE))
1881 g_slist_free_full (dirlist, g_free);
1882 goto fallback;
1885 if (dirlist == NULL && !cd_first)
1887 /* The LIST command may produce an empty output. In such scenario
1888 it is not clear whether this is caused by 'remote_path' being
1889 a non-existent path or for some other reason (listing empty
1890 directory without the -a option, non-readable directory, etc.).
1892 Since 'dir_load' is a crucial method, when it comes to determine
1893 whether a given path is a _directory_, the code must try its best
1894 to determine the type of 'remote_path'. The only reliable way to
1895 achieve this is through issuing a CWD command. */
1897 cd_first = TRUE;
1898 goto again;
1901 /* parse server's reply */
1902 dirlist = g_slist_reverse (dirlist); /* restore order */
1903 entlist = ftpfs_parse_long_list (me, dir, dirlist, &err_count);
1904 g_slist_free_full (dirlist, g_free);
1906 for (iter = entlist; iter != NULL; iter = g_slist_next (iter))
1907 vfs_s_insert_entry (me, dir, VFS_ENTRY (iter->data));
1909 g_slist_free (entlist);
1911 if (ftp_super->strict == RFC_AUTODETECT)
1912 ftp_super->strict = RFC_DARING;
1914 vfs_print_message (_("%s: done."), me->name);
1915 return 0;
1918 /* --------------------------------------------------------------------------------------------- */
1920 static int
1921 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t *fh, char *name, char *localname)
1923 struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
1924 ftp_super_t *ftp_super = FTP_SUPER (super);
1925 ftp_file_handler_t *ftp = FTP_FILE_HANDLER (fh);
1927 int h, sock;
1928 off_t n_stored = 0;
1929 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1930 struct linger li;
1931 #else
1932 int flag_one = 1;
1933 #endif
1934 char lc_buffer[BUF_8K];
1935 struct stat s;
1936 char *w_buf;
1938 h = open (localname, O_RDONLY);
1939 if (h == -1)
1940 ERRNOR (EIO, -1);
1942 if (fstat (h, &s) == -1)
1944 me->verrno = errno;
1945 close (h);
1946 return (-1);
1949 sock =
1950 ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1951 if (sock < 0)
1953 close (h);
1954 return (-1);
1956 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1957 li.l_onoff = 1;
1958 li.l_linger = 120;
1959 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1960 #else
1961 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1962 #endif
1964 tty_enable_interrupt_key ();
1965 while (TRUE)
1967 ssize_t n_read, n_written;
1969 while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1971 if (errno != EINTR)
1973 me->verrno = errno;
1974 goto error_return;
1976 if (tty_got_interrupt ())
1978 me->verrno = EINTR;
1979 goto error_return;
1982 if (n_read == 0)
1983 break;
1985 n_stored += n_read;
1986 w_buf = lc_buffer;
1988 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1990 if (n_written == -1)
1992 if (errno == EINTR && !tty_got_interrupt ())
1993 continue;
1995 me->verrno = errno;
1996 goto error_return;
1999 w_buf += n_written;
2000 n_read -= n_written;
2003 vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
2004 _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
2006 tty_disable_interrupt_key ();
2008 close (sock);
2009 ftp_super->ctl_connection_busy = FALSE;
2010 close (h);
2012 if (ftpfs_get_reply (me, ftp_super->sock, NULL, 0) != COMPLETE)
2013 ERRNOR (EIO, -1);
2014 return 0;
2016 error_return:
2017 tty_disable_interrupt_key ();
2018 close (sock);
2019 ftp_super->ctl_connection_busy = FALSE;
2020 close (h);
2022 ftpfs_get_reply (me, ftp_super->sock, NULL, 0);
2023 return (-1);
2026 /* --------------------------------------------------------------------------------------------- */
2028 static int
2029 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t *fh, off_t offset)
2031 char *name;
2033 name = vfs_s_fullpath (me, fh->ino);
2034 if (name == NULL)
2035 return 0;
2037 FH_SOCK =
2038 ftpfs_open_data_connection (me, VFS_FILE_HANDLER_SUPER (fh), "RETR", name, TYPE_BINARY,
2039 offset);
2040 g_free (name);
2041 if (FH_SOCK == -1)
2042 ERRNOR (EACCES, 0);
2044 fh->linear = LS_LINEAR_OPEN;
2045 FTP_FILE_HANDLER (fh)->append = FALSE;
2046 return 1;
2049 /* --------------------------------------------------------------------------------------------- */
2051 static ssize_t
2052 ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t *fh, void *buf, size_t len)
2054 ssize_t n;
2055 struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
2057 while ((n = read (FH_SOCK, buf, len)) < 0)
2059 if ((errno == EINTR) && !tty_got_interrupt ())
2060 continue;
2061 break;
2064 if (n < 0)
2065 ftpfs_linear_abort (me, fh);
2066 else if (n == 0)
2068 FTP_SUPER (super)->ctl_connection_busy = FALSE;
2069 close (FH_SOCK);
2070 FH_SOCK = -1;
2071 if ((ftpfs_get_reply (me, FTP_SUPER (super)->sock, NULL, 0) != COMPLETE))
2072 ERRNOR (E_REMOTE, -1);
2073 return 0;
2076 ERRNOR (errno, n);
2079 /* --------------------------------------------------------------------------------------------- */
2081 static void
2082 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t *fh)
2084 if (FH_SOCK != -1)
2085 ftpfs_linear_abort (me, fh);
2088 /* --------------------------------------------------------------------------------------------- */
2090 static int
2091 ftpfs_ctl (void *fh, int ctlop, void *arg)
2093 (void) arg;
2095 switch (ctlop)
2097 case VFS_CTL_IS_NOTREADY:
2099 vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
2100 int v;
2102 if (file->linear == LS_NOT_LINEAR)
2103 vfs_die ("You may not do this");
2104 if (file->linear == LS_LINEAR_CLOSED || file->linear == LS_LINEAR_PREOPEN)
2105 return 0;
2107 v = vfs_s_select_on_two (FH_SOCK, 0);
2108 return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
2110 default:
2111 return 0;
2115 /* --------------------------------------------------------------------------------------------- */
2117 static int
2118 ftpfs_send_command (const vfs_path_t *vpath, const char *cmd, int flags)
2120 const char *rpath;
2121 char *p;
2122 struct vfs_s_super *super;
2123 int r;
2124 struct vfs_class *me;
2125 gboolean flush_directory_cache = (flags & OPT_FLUSH) != 0;
2127 me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
2129 rpath = vfs_s_get_path (vpath, &super, 0);
2130 if (rpath == NULL)
2131 return (-1);
2133 p = ftpfs_translate_path (me, super, rpath);
2134 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
2135 g_free (p);
2136 vfs_stamp_create (vfs_ftpfs_ops, super);
2137 if ((flags & OPT_IGNORE_ERROR) != 0)
2138 r = COMPLETE;
2139 if (r != COMPLETE)
2141 me->verrno = EPERM;
2142 return (-1);
2144 if (flush_directory_cache)
2145 vfs_s_invalidate (me, super);
2146 return 0;
2149 /* --------------------------------------------------------------------------------------------- */
2151 static int
2152 ftpfs_stat (const vfs_path_t *vpath, struct stat *buf)
2154 int ret;
2156 ret = vfs_s_stat (vpath, buf);
2157 ftpfs_set_blksize (buf);
2158 return ret;
2161 /* --------------------------------------------------------------------------------------------- */
2163 static int
2164 ftpfs_lstat (const vfs_path_t *vpath, struct stat *buf)
2166 int ret;
2168 ret = vfs_s_lstat (vpath, buf);
2169 ftpfs_set_blksize (buf);
2170 return ret;
2173 /* --------------------------------------------------------------------------------------------- */
2175 static int
2176 ftpfs_fstat (void *vfs_info, struct stat *buf)
2178 int ret;
2180 ret = vfs_s_fstat (vfs_info, buf);
2181 ftpfs_set_blksize (buf);
2182 return ret;
2185 /* --------------------------------------------------------------------------------------------- */
2187 static int
2188 ftpfs_chmod (const vfs_path_t *vpath, mode_t mode)
2190 char buf[BUF_SMALL];
2191 int ret;
2193 g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", (unsigned int) (mode & 07777));
2194 ret = ftpfs_send_command (vpath, buf, OPT_FLUSH);
2195 return ftpfs_ignore_chattr_errors ? 0 : ret;
2198 /* --------------------------------------------------------------------------------------------- */
2200 static int
2201 ftpfs_chown (const vfs_path_t *vpath, uid_t owner, gid_t group)
2203 #if 0
2204 (void) vpath;
2205 (void) owner;
2206 (void) group;
2208 me->verrno = EPERM;
2209 return (-1);
2210 #else
2211 /* Everyone knows it is not possible to chown remotely, so why bother them.
2212 If someone's root, then copy/move will always try to chown it... */
2213 (void) vpath;
2214 (void) owner;
2215 (void) group;
2216 return 0;
2217 #endif
2220 /* --------------------------------------------------------------------------------------------- */
2222 static int
2223 ftpfs_unlink (const vfs_path_t *vpath)
2225 return ftpfs_send_command (vpath, "DELE /%s", OPT_FLUSH);
2228 /* --------------------------------------------------------------------------------------------- */
2230 /* Return TRUE if path is the same directory as the one we are in now */
2231 static gboolean
2232 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
2234 (void) me;
2236 return (FTP_SUPER (super)->current_dir != NULL
2237 && strcmp (path, FTP_SUPER (super)->current_dir) == 0);
2240 /* --------------------------------------------------------------------------------------------- */
2242 static int
2243 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
2245 ftp_super_t *ftp_super = FTP_SUPER (super);
2246 int r;
2247 char *p;
2249 if (!ftp_super->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
2250 return COMPLETE;
2252 p = ftpfs_translate_path (me, super, remote_path);
2253 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
2254 g_free (p);
2256 if (r != COMPLETE)
2257 me->verrno = EIO;
2258 else
2260 g_free (ftp_super->current_dir);
2261 ftp_super->current_dir = g_strdup (remote_path);
2262 ftp_super->cwd_deferred = FALSE;
2264 return r;
2267 /* --------------------------------------------------------------------------------------------- */
2269 static int
2270 ftpfs_rename (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
2272 ftpfs_send_command (vpath1, "RNFR /%s", OPT_FLUSH);
2273 return ftpfs_send_command (vpath2, "RNTO /%s", OPT_FLUSH);
2276 /* --------------------------------------------------------------------------------------------- */
2278 static int
2279 ftpfs_mkdir (const vfs_path_t *vpath, mode_t mode)
2281 (void) mode; /* FIXME: should be used */
2283 return ftpfs_send_command (vpath, "MKD /%s", OPT_FLUSH);
2286 /* --------------------------------------------------------------------------------------------- */
2288 static int
2289 ftpfs_rmdir (const vfs_path_t *vpath)
2291 return ftpfs_send_command (vpath, "RMD /%s", OPT_FLUSH);
2294 /* --------------------------------------------------------------------------------------------- */
2296 static vfs_file_handler_t *
2297 ftpfs_fh_new (struct vfs_s_inode *ino, gboolean changed)
2299 ftp_file_handler_t *fh;
2301 fh = g_new0 (ftp_file_handler_t, 1);
2302 vfs_s_init_fh (VFS_FILE_HANDLER (fh), ino, changed);
2303 fh->sock = -1;
2305 return VFS_FILE_HANDLER (fh);
2308 /* --------------------------------------------------------------------------------------------- */
2310 static int
2311 ftpfs_fh_open (struct vfs_class *me, vfs_file_handler_t *fh, int flags, mode_t mode)
2313 ftp_file_handler_t *ftp = FTP_FILE_HANDLER (fh);
2315 (void) mode;
2317 /* File will be written only, so no need to retrieve it from ftp server */
2318 if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
2320 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2321 struct linger li;
2322 #else
2323 int li = 1;
2324 #endif
2325 char *name;
2327 /* ftpfs_linear_start() called, so data will be written
2328 * to local temporary file and stored to ftp server
2329 * by vfs_s_close later
2331 if (FTP_SUPER (VFS_FILE_HANDLER_SUPER (fh))->ctl_connection_busy)
2333 if (fh->ino->localname == NULL)
2335 vfs_path_t *vpath;
2336 int handle;
2338 handle = vfs_mkstemps (&vpath, me->name, fh->ino->ent->name);
2339 if (handle == -1)
2340 return (-1);
2342 close (handle);
2343 fh->ino->localname = vfs_path_free (vpath, FALSE);
2344 ftp->append = (flags & O_APPEND) != 0;
2346 return 0;
2348 name = vfs_s_fullpath (me, fh->ino);
2349 if (name == NULL)
2350 return (-1);
2352 fh->handle =
2353 ftpfs_open_data_connection (me, VFS_FILE_HANDLER_SUPER (fh),
2354 (flags & O_APPEND) != 0 ? "APPE" : "STOR", name,
2355 TYPE_BINARY, 0);
2356 g_free (name);
2358 if (fh->handle < 0)
2359 return (-1);
2361 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2362 li.l_onoff = 1;
2363 li.l_linger = 120;
2364 #endif
2365 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
2367 if (fh->ino->localname != NULL)
2369 unlink (fh->ino->localname);
2370 MC_PTR_FREE (fh->ino->localname);
2372 return 0;
2375 if (fh->ino->localname == NULL && vfs_s_retrieve_file (me, fh->ino) == -1)
2376 return (-1);
2378 if (fh->ino->localname == NULL)
2379 vfs_die ("retrieve_file failed to fill in localname");
2380 return 0;
2383 /* --------------------------------------------------------------------------------------------- */
2385 static int
2386 ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t *fh)
2388 if (fh->handle != -1 && fh->ino->localname == NULL)
2390 ftp_super_t *ftp = FTP_SUPER (VFS_FILE_HANDLER_SUPER (fh));
2392 close (fh->handle);
2393 fh->handle = -1;
2394 ftp->ctl_connection_busy = FALSE;
2395 /* File is stored to destination already, so
2396 * we prevent VFS_SUBCLASS (me)->ftpfs_file_store() call from vfs_s_close ()
2398 fh->changed = FALSE;
2399 if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE)
2400 ERRNOR (EIO, -1);
2401 vfs_s_invalidate (me, VFS_FILE_HANDLER_SUPER (fh));
2404 return 0;
2407 /* --------------------------------------------------------------------------------------------- */
2409 static void
2410 ftpfs_done (struct vfs_class *me)
2412 (void) me;
2414 g_slist_free_full (no_proxy, g_free);
2416 g_free (ftpfs_anonymous_passwd);
2417 g_free (ftpfs_proxy_host);
2420 /* --------------------------------------------------------------------------------------------- */
2422 static void
2423 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
2425 GList *iter;
2427 for (iter = VFS_SUBCLASS (me)->supers; iter != NULL; iter = g_list_next (iter))
2429 const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
2430 GString *name;
2432 name = vfs_path_element_build_pretty_path_str (super->path_element);
2434 func (name->str);
2435 g_string_free (name, TRUE);
2439 /* --------------------------------------------------------------------------------------------- */
2441 static keyword_t
2442 ftpfs_netrc_next (void)
2444 char *p;
2445 keyword_t i;
2446 static const char *const keywords[] = { "default", "machine",
2447 "login", "password", "passwd", "account", "macdef", NULL
2450 while (TRUE)
2452 netrcp = skip_separators (netrcp);
2453 if (*netrcp != '\n')
2454 break;
2455 netrcp++;
2457 if (*netrcp == '\0')
2458 return NETRC_NONE;
2460 p = buffer;
2461 if (*netrcp == '"')
2463 for (netrcp++; *netrcp != '"' && *netrcp != '\0'; netrcp++)
2465 if (*netrcp == '\\')
2466 netrcp++;
2467 *p++ = *netrcp;
2470 else
2472 for (; *netrcp != '\0' && !whiteness (*netrcp) && *netrcp != ','; netrcp++)
2474 if (*netrcp == '\\')
2475 netrcp++;
2476 *p++ = *netrcp;
2480 *p = '\0';
2481 if (*buffer == '\0')
2482 return NETRC_NONE;
2484 for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2485 if (strcmp (keywords[i - 1], buffer) == 0)
2486 return i;
2488 return NETRC_UNKNOWN;
2491 /* --------------------------------------------------------------------------------------------- */
2493 static gboolean
2494 ftpfs_netrc_bad_mode (const char *netrcname)
2496 struct stat mystat;
2498 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077) != 0)
2500 static gboolean be_angry = TRUE;
2502 if (be_angry)
2504 message (D_ERROR, MSG_ERROR,
2505 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2506 be_angry = FALSE;
2508 return TRUE;
2511 return FALSE;
2514 /* --------------------------------------------------------------------------------------------- */
2515 /* Scan .netrc until we find matching "machine" or "default"
2516 * domain is used for additional matching
2517 * No search is done after "default" in compliance with "man netrc"
2518 * Return TRUE if found, FALSE otherwise */
2520 static gboolean
2521 ftpfs_find_machine (const char *host, const char *domain)
2523 keyword_t keyword;
2525 if (host == NULL)
2526 host = "";
2527 if (domain == NULL)
2528 domain = "";
2530 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2532 if (keyword == NETRC_DEFAULT)
2533 return TRUE;
2535 if (keyword == NETRC_MACDEF)
2537 /* Scan for an empty line, which concludes "macdef" */
2540 while (*netrcp != '\0' && *netrcp != '\n')
2541 netrcp++;
2542 if (*netrcp != '\n')
2543 break;
2544 netrcp++;
2546 while (*netrcp != '\0' && *netrcp != '\n');
2548 continue;
2551 if (keyword != NETRC_MACHINE)
2552 continue;
2554 /* Take machine name */
2555 if (ftpfs_netrc_next () == NETRC_NONE)
2556 break;
2558 if (g_ascii_strcasecmp (host, buffer) != 0)
2560 const char *host_domain;
2562 /* Try adding our domain to short names in .netrc */
2563 host_domain = strchr (host, '.');
2564 if (host_domain == NULL)
2565 continue;
2567 /* Compare domain part */
2568 if (g_ascii_strcasecmp (host_domain, domain) != 0)
2569 continue;
2571 /* Compare local part */
2572 if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2573 continue;
2576 return TRUE;
2579 /* end of .netrc */
2580 return FALSE;
2583 /* --------------------------------------------------------------------------------------------- */
2584 /* Extract login and password from .netrc for the host.
2585 * pass may be NULL.
2586 * Returns TRUE for success, FALSE for error */
2588 static gboolean
2589 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2591 char *netrcname;
2592 char *tmp_pass = NULL;
2593 char hostname[MAXHOSTNAMELEN];
2594 const char *domain;
2595 static struct rupcache
2597 struct rupcache *next;
2598 char *host;
2599 char *login;
2600 char *pass;
2601 } *rup_cache = NULL, *rupp;
2603 /* Initialize *login and *pass */
2604 MC_PTR_FREE (*login);
2605 MC_PTR_FREE (*pass);
2607 /* Look up in the cache first */
2608 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2609 if (strcmp (host, rupp->host) == 0)
2611 *login = g_strdup (rupp->login);
2612 *pass = g_strdup (rupp->pass);
2613 return TRUE;
2616 /* Load current .netrc */
2617 netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2618 if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2620 g_free (netrcname);
2621 return TRUE;
2624 netrcp = netrc;
2626 /* Find our own domain name */
2627 if (gethostname (hostname, sizeof (hostname)) < 0)
2628 *hostname = '\0';
2630 domain = strchr (hostname, '.');
2631 if (domain == NULL)
2632 domain = "";
2634 /* Scan for "default" and matching "machine" keywords */
2635 ftpfs_find_machine (host, domain);
2637 /* Scan for keywords following "default" and "machine" */
2638 while (TRUE)
2640 keyword_t keyword;
2642 gboolean need_break = FALSE;
2643 keyword = ftpfs_netrc_next ();
2645 switch (keyword)
2647 case NETRC_LOGIN:
2648 if (ftpfs_netrc_next () == NETRC_NONE)
2650 need_break = TRUE;
2651 break;
2654 /* We have another name already - should not happen */
2655 if (*login != NULL)
2657 need_break = TRUE;
2658 break;
2661 /* We have login name now */
2662 *login = g_strdup (buffer);
2663 break;
2665 case NETRC_PASSWORD:
2666 case NETRC_PASSWD:
2667 if (ftpfs_netrc_next () == NETRC_NONE)
2669 need_break = TRUE;
2670 break;
2673 /* Ignore unsafe passwords */
2674 if (*login != NULL &&
2675 strcmp (*login, "anonymous") != 0 && strcmp (*login, "ftp") != 0
2676 && ftpfs_netrc_bad_mode (netrcname))
2678 need_break = TRUE;
2679 break;
2682 /* Remember password. pass may be NULL, so use tmp_pass */
2683 if (tmp_pass == NULL)
2684 tmp_pass = g_strdup (buffer);
2685 break;
2687 case NETRC_ACCOUNT:
2688 /* "account" is followed by a token which we ignore */
2689 if (ftpfs_netrc_next () == NETRC_NONE)
2691 need_break = TRUE;
2692 break;
2695 /* Ignore account, but warn user anyways */
2696 ftpfs_netrc_bad_mode (netrcname);
2697 break;
2699 default:
2700 /* Unexpected keyword or end of file */
2701 need_break = TRUE;
2702 break;
2705 if (need_break)
2706 break;
2709 MC_PTR_FREE (netrc);
2710 g_free (netrcname);
2712 rupp = g_new (struct rupcache, 1);
2713 rupp->host = g_strdup (host);
2714 rupp->login = g_strdup (*login);
2715 rupp->pass = g_strdup (tmp_pass);
2717 rupp->next = rup_cache;
2718 rup_cache = rupp;
2720 *pass = tmp_pass;
2722 return TRUE;
2725 /* --------------------------------------------------------------------------------------------- */
2726 /*** public functions ****************************************************************************/
2727 /* --------------------------------------------------------------------------------------------- */
2729 /** This routine is called as the last step in load_setup */
2730 void
2731 ftpfs_init_passwd (void)
2733 ftpfs_anonymous_passwd = load_anon_passwd ();
2735 if (ftpfs_anonymous_passwd == NULL)
2737 /* If there is no anonymous ftp password specified
2738 * then we'll just use anonymous@
2739 * We don't send any other thing because:
2740 * - We want to remain anonymous
2741 * - We want to stop SPAM
2742 * - We don't want to let ftp sites to discriminate by the user,
2743 * host or country.
2745 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
2749 /* --------------------------------------------------------------------------------------------- */
2751 void
2752 vfs_init_ftpfs (void)
2754 tcp_init ();
2756 vfs_init_subclass (&ftpfs_subclass, "ftpfs", VFSF_NOLINKS | VFSF_REMOTE | VFSF_USETMP, "ftp");
2757 vfs_ftpfs_ops->done = ftpfs_done;
2758 vfs_ftpfs_ops->fill_names = ftpfs_fill_names;
2759 vfs_ftpfs_ops->stat = ftpfs_stat;
2760 vfs_ftpfs_ops->lstat = ftpfs_lstat;
2761 vfs_ftpfs_ops->fstat = ftpfs_fstat;
2762 vfs_ftpfs_ops->chmod = ftpfs_chmod;
2763 vfs_ftpfs_ops->chown = ftpfs_chown;
2764 vfs_ftpfs_ops->unlink = ftpfs_unlink;
2765 vfs_ftpfs_ops->rename = ftpfs_rename;
2766 vfs_ftpfs_ops->mkdir = ftpfs_mkdir;
2767 vfs_ftpfs_ops->rmdir = ftpfs_rmdir;
2768 vfs_ftpfs_ops->ctl = ftpfs_ctl;
2769 ftpfs_subclass.archive_same = ftpfs_archive_same;
2770 ftpfs_subclass.new_archive = ftpfs_new_archive;
2771 ftpfs_subclass.open_archive = ftpfs_open_archive;
2772 ftpfs_subclass.free_archive = ftpfs_free_archive;
2773 ftpfs_subclass.fh_new = ftpfs_fh_new;
2774 ftpfs_subclass.fh_open = ftpfs_fh_open;
2775 ftpfs_subclass.fh_close = ftpfs_fh_close;
2776 ftpfs_subclass.dir_load = ftpfs_dir_load;
2777 ftpfs_subclass.file_store = ftpfs_file_store;
2778 ftpfs_subclass.linear_start = ftpfs_linear_start;
2779 ftpfs_subclass.linear_read = ftpfs_linear_read;
2780 ftpfs_subclass.linear_close = ftpfs_linear_close;
2781 vfs_register_class (vfs_ftpfs_ops);
2784 /* --------------------------------------------------------------------------------------------- */