Partially revert "VFS: (vfs_s_subclass): make the derived class from vfs_class."
[midnight-commander.git] / src / vfs / ftpfs / ftpfs.c
blob7f5926471cff2b7f37b6aed5af8ec04c1468d680
1 /*
2 Virtual File System: FTP file system.
4 Copyright (C) 1995-2018
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
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 <sys/time.h> /* gettimeofday() */
95 #include <inttypes.h> /* uintmax_t */
97 #include "lib/global.h"
98 #include "lib/util.h"
99 #include "lib/strutil.h" /* str_move() */
100 #include "lib/mcconfig.h"
102 #include "lib/tty/tty.h" /* enable/disable interrupt key */
103 #include "lib/widget.h" /* message() */
105 #include "src/history.h"
106 #include "src/setup.h" /* for load_anon_passwd */
108 #include "lib/vfs/vfs.h"
109 #include "lib/vfs/utilvfs.h"
110 #include "lib/vfs/netutil.h"
111 #include "lib/vfs/xdirentry.h"
112 #include "lib/vfs/gc.h" /* vfs_stamp_create */
114 #include "ftpfs.h"
116 /*** global variables ****************************************************************************/
118 /* Delay to retry a connection */
119 int ftpfs_retry_seconds = 30;
121 /* Method to use to connect to ftp sites */
122 gboolean ftpfs_use_passive_connections = TRUE;
123 gboolean ftpfs_use_passive_connections_over_proxy = FALSE;
125 /* Method used to get directory listings:
126 * 1: try 'LIST -la <path>', if it fails
127 * fall back to CWD <path>; LIST
128 * 0: always use CWD <path>; LIST
130 gboolean ftpfs_use_unix_list_options = TRUE;
132 /* First "CWD <path>", then "LIST -la ." */
133 gboolean ftpfs_first_cd_then_ls = TRUE;
135 /* Use the ~/.netrc */
136 gboolean ftpfs_use_netrc = TRUE;
138 /* Anonymous setup */
139 char *ftpfs_anonymous_passwd = NULL;
140 int ftpfs_directory_timeout = 900;
142 /* Proxy host */
143 char *ftpfs_proxy_host = NULL;
145 /* whether we have to use proxy by default? */
146 gboolean ftpfs_always_use_proxy = FALSE;
148 gboolean ftpfs_ignore_chattr_errors = TRUE;
150 /*** file scope macro definitions ****************************************************************/
152 #ifndef MAXHOSTNAMELEN
153 #define MAXHOSTNAMELEN 64
154 #endif
156 #define SUP ((ftp_super_data_t *) super->data)
157 #define FH_SOCK ((ftp_fh_data_t *) fh->data)->sock
159 #ifndef INADDR_NONE
160 #define INADDR_NONE 0xffffffff
161 #endif
163 #define RFC_AUTODETECT 0
164 #define RFC_DARING 1
165 #define RFC_STRICT 2
167 /* ftpfs_command wait_flag: */
168 #define NONE 0x00
169 #define WAIT_REPLY 0x01
170 #define WANT_STRING 0x02
172 #define FTP_COMMAND_PORT 21
174 /* some defines only used by ftpfs_changetype */
175 /* These two are valid values for the second parameter */
176 #define TYPE_ASCII 0
177 #define TYPE_BINARY 1
179 /* This one is only used to initialize bucket->isbinary, don't use it as
180 second parameter to ftpfs_changetype. */
181 #define TYPE_UNKNOWN -1
183 #define ABORT_TIMEOUT 5
184 /*** file scope type declarations ****************************************************************/
186 #ifndef HAVE_SOCKLEN_T
187 typedef int socklen_t;
188 #endif
190 /* This should match the keywords[] array below */
191 typedef enum
193 NETRC_NONE = 0,
194 NETRC_DEFAULT,
195 NETRC_MACHINE,
196 NETRC_LOGIN,
197 NETRC_PASSWORD,
198 NETRC_PASSWD,
199 NETRC_ACCOUNT,
200 NETRC_MACDEF,
201 NETRC_UNKNOWN
202 } keyword_t;
204 typedef struct
206 int sock;
208 char *proxy; /* proxy server, NULL if no proxy */
209 int failed_on_login; /* used to pass the failure reason to upper levels */
210 gboolean use_passive_connection;
211 int remote_is_amiga; /* No leading slash allowed for AmiTCP (Amiga) */
212 int isbinary;
213 int cwd_deferred; /* current_directory was changed but CWD command hasn't
214 been sent yet */
215 int strict; /* ftp server doesn't understand
216 * "LIST -la <path>"; use "CWD <path>"/
217 * "LIST" instead
219 int ctl_connection_busy;
220 char *current_dir;
221 } ftp_super_data_t;
223 typedef struct
225 int sock;
226 int append;
227 } ftp_fh_data_t;
229 /*** file scope variables ************************************************************************/
231 static int ftpfs_errno;
232 static int code;
234 #ifdef FIXME_LATER_ALIGATOR
235 static struct linklist *connections_list;
236 #endif
238 static char reply_str[80];
240 static struct vfs_class vfs_ftpfs_ops;
242 static GSList *no_proxy;
244 static char buffer[BUF_MEDIUM];
245 static char *netrc;
246 static const char *netrcp;
248 /* --------------------------------------------------------------------------------------------- */
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 transferred 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 occurrence of ":/" with ":"
262 c) strip trailing "/."
265 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
266 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super,
267 const char *remote_path);
268 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
269 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
270 const char *netrcpass);
271 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
273 /* --------------------------------------------------------------------------------------------- */
275 static void
276 ftpfs_set_blksize (struct stat *s)
278 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
279 /* redefine block size */
280 s->st_blksize = 64 * 1024; /* FIXME */
281 #endif
284 /* --------------------------------------------------------------------------------------------- */
286 static struct stat *
287 ftpfs_default_stat (struct vfs_class *me)
289 struct stat *s;
291 s = vfs_s_default_stat (me, S_IFDIR | 0755);
292 ftpfs_set_blksize (s);
293 vfs_adjust_stat (s);
295 return s;
298 /* --------------------------------------------------------------------------------------------- */
300 static char *
301 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
303 if (!SUP->remote_is_amiga)
304 return g_strdup (remote_path);
305 else
307 char *ret, *p;
309 if (MEDATA->logfile)
311 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
312 fflush (MEDATA->logfile);
315 /* strip leading slash(es) */
316 while (IS_PATH_SEP (*remote_path))
317 remote_path++;
320 * Don't change "/" into "", e.g. "CWD " would be
321 * invalid.
323 if (*remote_path == '\0')
324 return g_strdup (".");
326 ret = g_strdup (remote_path);
328 /* replace first occurrence of ":/" with ":" */
329 p = strchr (ret, ':');
330 if (p != NULL && IS_PATH_SEP (p[1]))
331 str_move (p + 1, p + 2);
333 /* strip trailing "/." */
334 p = strrchr (ret, PATH_SEP);
335 if ((p != NULL) && (*(p + 1) == '.') && (*(p + 2) == '\0'))
336 *p = '\0';
338 return ret;
342 /* --------------------------------------------------------------------------------------------- */
343 /** Extract the hostname and username from the path */
345 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
346 * ftp://sunsite.unc.edu/pub/linux
347 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
348 * ftp://tsx-11.mit.edu:8192/
349 * ftp://joe@foo.edu:11321/private
350 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
351 * is supplied.
354 static vfs_path_element_t *
355 ftpfs_correct_url_parameters (const vfs_path_element_t * velement)
357 vfs_path_element_t *path_element = vfs_path_element_clone (velement);
359 if (path_element->port == 0)
360 path_element->port = FTP_COMMAND_PORT;
362 if (path_element->user == NULL)
364 /* Look up user and password in netrc */
365 if (ftpfs_use_netrc)
366 ftpfs_netrc_lookup (path_element->host, &path_element->user, &path_element->password);
368 if (path_element->user == NULL)
369 path_element->user = g_strdup ("anonymous");
371 /* Look up password in netrc for known user */
372 if (ftpfs_use_netrc && path_element->password == NULL)
374 char *new_user = NULL;
375 char *new_passwd = NULL;
377 ftpfs_netrc_lookup (path_element->host, &new_user, &new_passwd);
379 /* If user is different, remove password */
380 if (new_user != NULL && strcmp (path_element->user, new_user) != 0)
381 MC_PTR_FREE (path_element->password);
383 g_free (new_user);
384 g_free (new_passwd);
387 return path_element;
390 /* --------------------------------------------------------------------------------------------- */
391 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
393 static int
394 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
396 char answer[BUF_1K];
397 int i;
399 while (TRUE)
401 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
403 if (string_buf != NULL)
404 *string_buf = '\0';
405 code = 421;
406 return 4;
408 /* cppcheck-suppress invalidscanf */
409 switch (sscanf (answer, "%d", &code))
411 case 0:
412 if (string_buf != NULL)
413 g_strlcpy (string_buf, answer, string_len);
414 code = 500;
415 return 5;
416 case 1:
417 if (answer[3] == '-')
419 while (TRUE)
421 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
423 if (string_buf != NULL)
424 *string_buf = '\0';
425 code = 421;
426 return 4;
428 /* cppcheck-suppress invalidscanf */
429 if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' '))
430 break;
433 if (string_buf != NULL)
434 g_strlcpy (string_buf, answer, string_len);
435 return code / 100;
436 default:
437 break;
442 /* --------------------------------------------------------------------------------------------- */
444 static int
445 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
447 int sock;
449 sock = ftpfs_open_socket (me, super);
450 if (sock != -1)
452 char *cwdir = SUP->current_dir;
454 close (SUP->sock);
455 SUP->sock = sock;
456 SUP->current_dir = NULL;
458 if (ftpfs_login_server (me, super, super->path_element->password) != 0)
460 if (cwdir == NULL)
461 return 1;
462 sock = ftpfs_chdir_internal (me, super, cwdir);
463 g_free (cwdir);
464 return sock == COMPLETE ? 1 : 0;
467 SUP->current_dir = cwdir;
470 return 0;
473 /* --------------------------------------------------------------------------------------------- */
475 static int
476 G_GNUC_PRINTF (4, 5)
477 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt,
478 ...)
480 va_list ap;
481 GString *cmdstr;
482 int status;
483 static int retry = 0;
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 (MEDATA->logfile != NULL)
494 if (strncmp (cmdstr->str, "PASS ", 5) == 0)
495 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
496 else
498 size_t ret;
500 ret = fwrite (cmdstr->str, cmdstr->len, 1, MEDATA->logfile);
501 (void) ret;
504 fflush (MEDATA->logfile);
507 got_sigpipe = 0;
508 tty_enable_interrupt_key ();
509 status = write (SUP->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);
521 level = 0;
522 if (status && (write (SUP->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;
532 retry = 0;
534 tty_disable_interrupt_key ();
536 if (wait_reply)
538 status = ftpfs_get_reply (me, SUP->sock,
539 (wait_reply & WANT_STRING) ? reply_str : NULL,
540 sizeof (reply_str) - 1);
541 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
543 retry = 1;
544 level = 1;
545 status = ftpfs_reconnect (me, super);
546 level = 0;
547 if (status && (write (SUP->sock, cmdstr->str, cmdstr->len) > 0))
548 goto ok;
550 retry = 0;
551 g_string_free (cmdstr, TRUE);
552 return status;
555 g_string_free (cmdstr, TRUE);
556 return COMPLETE;
559 /* --------------------------------------------------------------------------------------------- */
561 static void
562 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
564 if (SUP->sock != -1)
566 vfs_print_message (_("ftpfs: Disconnecting from %s"), super->path_element->host);
567 ftpfs_command (me, super, NONE, "%s", "QUIT");
568 close (SUP->sock);
570 g_free (SUP->current_dir);
571 MC_PTR_FREE (super->data);
574 /* --------------------------------------------------------------------------------------------- */
576 static int
577 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
579 if (binary != SUP->isbinary)
581 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
582 ERRNOR (EIO, -1);
583 SUP->isbinary = binary;
585 return binary;
588 /* --------------------------------------------------------------------------------------------- */
589 /* This routine logs the user in */
591 static int
592 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
594 char *pass;
595 char *op;
596 char *name; /* login user name */
597 int anon = 0;
598 char reply_string[BUF_MEDIUM];
600 SUP->isbinary = TYPE_UNKNOWN;
602 if (super->path_element->password != NULL) /* explicit password */
603 op = g_strdup (super->path_element->password);
604 else if (netrcpass != NULL) /* password from netrc */
605 op = g_strdup (netrcpass);
606 else if (strcmp (super->path_element->user, "anonymous") == 0
607 || strcmp (super->path_element->user, "ftp") == 0)
609 if (ftpfs_anonymous_passwd == NULL) /* default anonymous password */
610 ftpfs_init_passwd ();
611 op = g_strdup (ftpfs_anonymous_passwd);
612 anon = 1;
614 else
615 { /* ask user */
616 char *p;
618 p = g_strdup_printf (_("FTP: Password required for %s"), super->path_element->user);
619 op = vfs_get_password (p);
620 g_free (p);
621 if (op == NULL)
622 ERRNOR (EPERM, 0);
623 super->path_element->password = g_strdup (op);
626 if (!anon || MEDATA->logfile)
627 pass = op;
628 else
630 pass = g_strconcat ("-", op, (char *) NULL);
631 wipe_password (op);
634 /* Proxy server accepts: username@host-we-want-to-connect */
635 if (SUP->proxy)
636 name =
637 g_strconcat (super->path_element->user, "@",
638 super->path_element->host[0] ==
639 '!' ? super->path_element->host + 1 : super->path_element->host,
640 (char *) NULL);
641 else
642 name = g_strdup (super->path_element->user);
644 if (ftpfs_get_reply (me, SUP->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
646 char *reply_up;
648 reply_up = g_ascii_strup (reply_string, -1);
649 SUP->remote_is_amiga = strstr (reply_up, "AMIGA") != 0;
650 if (strstr (reply_up, " SPFTP/1.0.0000 SERVER ")) /* handles `LIST -la` in a weird way */
651 SUP->strict = RFC_STRICT;
652 g_free (reply_up);
654 if (MEDATA->logfile)
656 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n", SUP->remote_is_amiga);
657 fflush (MEDATA->logfile);
660 vfs_print_message ("%s", _("ftpfs: sending login name"));
662 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
664 case CONTINUE:
665 vfs_print_message ("%s", _("ftpfs: sending user password"));
666 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
667 if (code == CONTINUE)
669 char *p;
671 p = g_strdup_printf (_("FTP: Account required for user %s"),
672 super->path_element->user);
673 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "",
674 INPUT_COMPLETE_USERNAMES);
675 g_free (p);
676 if (op == NULL)
677 ERRNOR (EPERM, 0);
678 vfs_print_message ("%s", _("ftpfs: sending user account"));
679 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
680 g_free (op);
682 if (code != COMPLETE)
683 break;
684 MC_FALLTHROUGH;
686 case COMPLETE:
687 vfs_print_message ("%s", _("ftpfs: logged in"));
688 wipe_password (pass);
689 g_free (name);
690 return 1;
692 default:
693 SUP->failed_on_login = 1;
694 wipe_password (super->path_element->password);
695 super->path_element->password = NULL;
697 goto login_fail;
701 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
702 super->path_element->user);
704 login_fail:
705 wipe_password (pass);
706 g_free (name);
707 ERRNOR (EPERM, 0);
710 /* --------------------------------------------------------------------------------------------- */
712 static void
713 ftpfs_load_no_proxy_list (void)
715 /* FixMe: shouldn't be hardcoded!!! */
716 char *mc_file;
718 mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL);
719 if (exist_file (mc_file))
721 FILE *npf;
723 npf = fopen (mc_file, "r");
724 if (npf != NULL)
726 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
728 while (fgets (s, sizeof (s), npf) != NULL)
730 char *p;
732 p = strchr (s, '\n');
733 if (p == NULL) /* skip bogus entries */
735 int c;
737 while ((c = fgetc (npf)) != EOF && c != '\n')
739 continue;
742 if (p != s)
744 *p = '\0';
745 no_proxy = g_slist_prepend (no_proxy, g_strdup (s));
748 fclose (npf);
751 g_free (mc_file);
754 /* --------------------------------------------------------------------------------------------- */
755 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
757 static int
758 ftpfs_check_proxy (const char *host)
760 GSList *npe;
762 if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0')
763 return 0; /* sanity check */
765 if (*host == '!')
766 return 1;
768 if (!ftpfs_always_use_proxy)
769 return 0;
771 if (strchr (host, '.') == NULL)
772 return 0;
774 ftpfs_load_no_proxy_list ();
775 for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe))
777 const char *domain = (const char *) npe->data;
779 if (domain[0] == '.')
781 size_t ld = strlen (domain);
782 size_t lh = strlen (host);
784 while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1])
786 ld--;
787 lh--;
790 if (ld == 0)
791 return 0;
793 else if (g_ascii_strcasecmp (host, domain) == 0)
794 return 0;
797 return 1;
800 /* --------------------------------------------------------------------------------------------- */
802 static void
803 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
805 vfs_path_element_t *path_element;
807 path_element = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
808 *host = g_strdup (path_element->host);
809 *port = path_element->port;
810 vfs_path_element_free (path_element);
813 /* --------------------------------------------------------------------------------------------- */
815 static int
816 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
818 struct addrinfo hints, *res, *curr_res;
819 int my_socket = 0;
820 char *host = NULL;
821 char port[8];
822 int tmp_port;
823 int e;
825 (void) me;
827 /* Use a proxy host? */
828 host = g_strdup (super->path_element->host);
830 if (host == NULL || *host == '\0')
832 vfs_print_message ("%s", _("ftpfs: Invalid host name."));
833 ftpfs_errno = EINVAL;
834 g_free (host);
835 return -1;
838 /* Hosts to connect to that start with a ! should use proxy */
839 tmp_port = super->path_element->port;
841 if (SUP->proxy != NULL)
842 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
844 g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_port);
845 if (port[0] == '\0')
847 g_free (host);
848 ftpfs_errno = errno;
849 return -1;
852 tty_enable_interrupt_key (); /* clear the interrupt flag */
854 memset (&hints, 0, sizeof (hints));
855 hints.ai_family = AF_UNSPEC;
856 hints.ai_socktype = SOCK_STREAM;
858 #ifdef AI_ADDRCONFIG
859 /* By default, only look up addresses using address types for
860 * which a local interface is configured (i.e. no IPv6 if no IPv6
861 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
862 hints.ai_flags = AI_ADDRCONFIG;
863 #endif
865 e = getaddrinfo (host, port, &hints, &res);
867 #ifdef AI_ADDRCONFIG
868 if (e == EAI_BADFLAGS)
870 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
871 hints.ai_flags = 0;
872 e = getaddrinfo (host, port, &hints, &res);
874 #endif
876 *port = '\0';
878 if (e != 0)
880 tty_disable_interrupt_key ();
881 vfs_print_message (_("ftpfs: %s"), gai_strerror (e));
882 g_free (host);
883 ftpfs_errno = EINVAL;
884 return -1;
887 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
889 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
891 if (my_socket < 0)
893 if (curr_res->ai_next != NULL)
894 continue;
896 tty_disable_interrupt_key ();
897 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno));
898 g_free (host);
899 freeaddrinfo (res);
900 ftpfs_errno = errno;
901 return -1;
904 vfs_print_message (_("ftpfs: making connection to %s"), host);
905 MC_PTR_FREE (host);
907 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
908 break;
910 ftpfs_errno = errno;
911 close (my_socket);
913 if (errno == EINTR && tty_got_interrupt ())
914 vfs_print_message ("%s", _("ftpfs: connection interrupted by user"));
915 else if (res->ai_next == NULL)
916 vfs_print_message (_("ftpfs: connection to server failed: %s"),
917 unix_error_string (errno));
918 else
919 continue;
921 freeaddrinfo (res);
922 tty_disable_interrupt_key ();
923 return -1;
926 freeaddrinfo (res);
927 tty_disable_interrupt_key ();
928 return my_socket;
931 /* --------------------------------------------------------------------------------------------- */
933 static int
934 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
936 int retry_seconds = 0;
937 int count_down;
939 /* We do not want to use the passive if we are using proxies */
940 if (SUP->proxy)
941 SUP->use_passive_connection = ftpfs_use_passive_connections_over_proxy;
945 SUP->failed_on_login = 0;
947 SUP->sock = ftpfs_open_socket (me, super);
948 if (SUP->sock == -1)
949 return -1;
951 if (ftpfs_login_server (me, super, NULL) != 0)
953 /* Logged in, no need to retry the connection */
954 break;
956 else
958 if (!SUP->failed_on_login)
959 return -1;
961 /* Close only the socket descriptor */
962 close (SUP->sock);
964 if (ftpfs_retry_seconds != 0)
966 retry_seconds = ftpfs_retry_seconds;
967 tty_enable_interrupt_key ();
968 for (count_down = retry_seconds; count_down; count_down--)
970 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
971 count_down);
972 sleep (1);
973 if (tty_got_interrupt ())
975 /* ftpfs_errno = E; */
976 tty_disable_interrupt_key ();
977 return 0;
980 tty_disable_interrupt_key ();
984 while (retry_seconds != 0);
986 SUP->current_dir = ftpfs_get_current_directory (me, super);
987 if (SUP->current_dir == NULL)
988 SUP->current_dir = g_strdup (PATH_SEP_STR);
990 return 0;
993 /* --------------------------------------------------------------------------------------------- */
995 static int
996 ftpfs_open_archive (struct vfs_s_super *super,
997 const vfs_path_t * vpath, const vfs_path_element_t * vpath_element)
999 (void) vpath;
1001 super->data = g_new0 (ftp_super_data_t, 1);
1003 super->path_element = ftpfs_correct_url_parameters (vpath_element);
1004 SUP->proxy = NULL;
1005 if (ftpfs_check_proxy (super->path_element->host))
1006 SUP->proxy = ftpfs_proxy_host;
1007 SUP->use_passive_connection = ftpfs_use_passive_connections;
1008 SUP->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
1009 SUP->isbinary = TYPE_UNKNOWN;
1010 SUP->remote_is_amiga = 0;
1011 SUP->ctl_connection_busy = 0;
1012 super->name = g_strdup (PATH_SEP_STR);
1013 super->root =
1014 vfs_s_new_inode (vpath_element->class, super, ftpfs_default_stat (vpath_element->class));
1016 return ftpfs_open_archive_int (vpath_element->class, super);
1019 /* --------------------------------------------------------------------------------------------- */
1021 static int
1022 ftpfs_archive_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *super,
1023 const vfs_path_t * vpath, void *cookie)
1025 vfs_path_element_t *path_element;
1026 int result;
1028 (void) vpath;
1029 (void) cookie;
1031 path_element = ftpfs_correct_url_parameters (vpath_element);
1033 result = ((strcmp (path_element->host, super->path_element->host) == 0)
1034 && (strcmp (path_element->user, super->path_element->user) == 0)
1035 && (path_element->port == super->path_element->port)) ? 1 : 0;
1037 vfs_path_element_free (path_element);
1038 return result;
1041 /* --------------------------------------------------------------------------------------------- */
1042 /* The returned directory should always contain a trailing slash */
1044 static char *
1045 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
1047 char buf[MC_MAXPATHLEN + 1];
1049 if (ftpfs_command (me, super, NONE, "%s", "PWD") == COMPLETE &&
1050 ftpfs_get_reply (me, SUP->sock, buf, sizeof (buf)) == COMPLETE)
1052 char *bufp = NULL;
1053 char *bufq;
1055 for (bufq = buf; *bufq != '\0'; bufq++)
1056 if (*bufq == '"')
1058 if (bufp == NULL)
1059 bufp = bufq + 1;
1060 else
1062 *bufq = '\0';
1064 if (*bufp != '\0')
1066 if (!IS_PATH_SEP (bufq[-1]))
1068 *bufq++ = PATH_SEP;
1069 *bufq = '\0';
1072 if (IS_PATH_SEP (*bufp))
1073 return g_strdup (bufp);
1075 /* If the remote server is an Amiga a leading slash
1076 might be missing. MC needs it because it is used
1077 as separator between hostname and path internally. */
1078 return g_strconcat (PATH_SEP_STR, bufp, (char *) NULL);
1081 break;
1086 ftpfs_errno = EIO;
1087 return NULL;
1090 /* --------------------------------------------------------------------------------------------- */
1091 /* Setup Passive PASV FTP connection */
1093 static int
1094 ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
1095 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1097 char *c;
1098 char n[6];
1099 int xa, xb, xc, xd, xe, xf;
1101 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "%s", "PASV") != COMPLETE)
1102 return 0;
1104 /* Parse remote parameters */
1105 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
1107 if (!*c)
1108 return 0;
1109 if (!isdigit ((unsigned char) *c))
1110 return 0;
1111 /* cppcheck-suppress invalidscanf */
1112 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1113 return 0;
1115 n[0] = (unsigned char) xa;
1116 n[1] = (unsigned char) xb;
1117 n[2] = (unsigned char) xc;
1118 n[3] = (unsigned char) xd;
1119 n[4] = (unsigned char) xe;
1120 n[5] = (unsigned char) xf;
1122 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1123 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1125 if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
1126 return 0;
1128 return 1;
1131 /* --------------------------------------------------------------------------------------------- */
1132 /* Setup Passive EPSV FTP connection */
1134 static int
1135 ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
1136 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1138 char *c;
1139 int port;
1141 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "%s", "EPSV") != COMPLETE)
1142 return 0;
1144 /* (|||<port>|) */
1145 c = strchr (reply_str, '|');
1146 if (c == NULL)
1147 return 0;
1148 if (strlen (c) > 3)
1149 c += 3;
1150 else
1151 return 0;
1153 port = atoi (c);
1154 if (port < 0 || port > 65535)
1155 return 0;
1156 port = htons (port);
1158 switch (sa->ss_family)
1160 case AF_INET:
1161 ((struct sockaddr_in *) sa)->sin_port = port;
1162 break;
1163 case AF_INET6:
1164 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1165 break;
1166 default:
1167 break;
1170 return (connect (my_socket, (struct sockaddr *) sa, *salen) < 0) ? 0 : 1;
1173 /* --------------------------------------------------------------------------------------------- */
1174 /* Setup Passive ftp connection, we use it for source routed connections */
1176 static int
1177 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
1178 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1180 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1181 if (sa->ss_family == AF_INET)
1183 if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
1184 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1185 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1186 return 0;
1188 /* It's IPV6, so EPSV is our only hope */
1189 else
1191 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1192 return 0;
1195 return 1;
1198 /* --------------------------------------------------------------------------------------------- */
1199 /* Setup Active PORT or EPRT FTP connection */
1201 static int
1202 ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super,
1203 struct sockaddr_storage data_addr, socklen_t data_addrlen)
1205 unsigned short int port;
1206 char *addr;
1207 unsigned int af;
1209 switch (data_addr.ss_family)
1211 case AF_INET:
1212 af = FTP_INET;
1213 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1214 break;
1215 case AF_INET6:
1216 af = FTP_INET6;
1217 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1218 break;
1219 /* Not implemented */
1220 default:
1221 return 0;
1224 addr = g_try_malloc (NI_MAXHOST);
1225 if (addr == NULL)
1226 ERRNOR (ENOMEM, -1);
1228 if (getnameinfo
1229 ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1230 NI_NUMERICHOST) != 0)
1232 g_free (addr);
1233 ERRNOR (EIO, -1);
1236 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1237 if (af == FTP_INET)
1239 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1240 unsigned char *p = (unsigned char *) &port;
1242 if (ftpfs_command (me, super, WAIT_REPLY,
1243 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1244 p[0], p[1]) == COMPLETE)
1246 g_free (addr);
1247 return 1;
1252 * Converts network MSB first order to host byte order (LSB
1253 * first on i386). If we do it earlier, we will run into an
1254 * endianness issue, because the server actually expects to see
1255 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1257 port = ntohs (port);
1259 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1260 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
1262 g_free (addr);
1263 return 1;
1266 g_free (addr);
1267 return 0;
1270 /* --------------------------------------------------------------------------------------------- */
1271 /* Initialize a socket for FTP DATA connection */
1273 static int
1274 ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super,
1275 struct sockaddr_storage *data_addr, socklen_t * data_addrlen)
1277 int result;
1279 memset (data_addr, 0, sizeof (*data_addr));
1280 *data_addrlen = sizeof (*data_addr);
1282 if (SUP->use_passive_connection)
1283 result = getpeername (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1284 else
1285 result = getsockname (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1287 if (result == -1)
1288 return -1;
1290 switch (data_addr->ss_family)
1292 case AF_INET:
1293 ((struct sockaddr_in *) data_addr)->sin_port = 0;
1294 break;
1295 case AF_INET6:
1296 ((struct sockaddr_in6 *) data_addr)->sin6_port = 0;
1297 break;
1298 default:
1299 vfs_print_message ("%s", _("ftpfs: invalid address family"));
1300 ERRNOR (EINVAL, -1);
1303 result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP);
1305 if (result < 0)
1307 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1308 return -1;
1311 return result;
1314 /* --------------------------------------------------------------------------------------------- */
1315 /* Initialize FTP DATA connection */
1317 static int
1318 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1320 struct sockaddr_storage data_addr;
1321 socklen_t data_addrlen;
1324 * Don't factor socket initialization out of these conditionals,
1325 * because ftpfs_init_data_socket initializes it in different way
1326 * depending on use_passive_connection flag.
1329 /* Try to establish a passive connection first (if requested) */
1330 if (SUP->use_passive_connection)
1332 int data_sock;
1334 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1335 if (data_sock < 0)
1336 return -1;
1338 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1339 return data_sock;
1341 vfs_print_message ("%s", _("ftpfs: could not setup passive mode"));
1342 SUP->use_passive_connection = FALSE;
1344 close (data_sock);
1347 /* If passive setup is diabled or failed, fallback to active connections */
1348 if (!SUP->use_passive_connection)
1350 int data_sock;
1352 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1353 if (data_sock < 0)
1354 return -1;
1356 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) == 0) &&
1357 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) == 0) &&
1358 (listen (data_sock, 1) == 0) &&
1359 (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0))
1360 return data_sock;
1362 close (data_sock);
1365 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1366 SUP->use_passive_connection = SUP->proxy != NULL ? ftpfs_use_passive_connections_over_proxy :
1367 ftpfs_use_passive_connections;
1369 ftpfs_errno = EIO;
1370 return -1;
1373 /* --------------------------------------------------------------------------------------------- */
1375 static int
1376 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1377 const char *remote, int isbinary, int reget)
1379 struct sockaddr_storage from;
1380 int s, j, data;
1381 socklen_t fromlen = sizeof (from);
1383 /* FTP doesn't allow to open more than one file at a time */
1384 if (SUP->ctl_connection_busy)
1385 return -1;
1387 s = ftpfs_initconn (me, super);
1388 if (s == -1)
1389 return -1;
1391 if (ftpfs_changetype (me, super, isbinary) == -1)
1393 close (s);
1394 return -1;
1397 if (reget > 0)
1399 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1400 if (j != CONTINUE)
1402 close (s);
1403 return -1;
1407 if (remote)
1409 char *remote_path = ftpfs_translate_path (me, super, remote);
1410 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1411 /* WarFtpD can't STORE //filename */
1412 IS_PATH_SEP (*remote_path) ? remote_path + 1 : remote_path);
1413 g_free (remote_path);
1415 else
1416 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1418 if (j != PRELIM)
1420 close (s);
1421 ERRNOR (EPERM, -1);
1424 if (SUP->use_passive_connection)
1425 data = s;
1426 else
1428 tty_enable_interrupt_key ();
1429 data = accept (s, (struct sockaddr *) &from, &fromlen);
1430 if (data < 0)
1431 ftpfs_errno = errno;
1432 tty_disable_interrupt_key ();
1433 close (s);
1434 if (data < 0)
1435 return -1;
1437 SUP->ctl_connection_busy = 1;
1438 return data;
1441 /* --------------------------------------------------------------------------------------------- */
1443 static void
1444 ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t * fh)
1446 struct vfs_s_super *super = FH_SUPER;
1447 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1448 fd_set mask;
1449 int dsock = FH_SOCK;
1450 FH_SOCK = -1;
1451 SUP->ctl_connection_busy = 0;
1453 vfs_print_message ("%s", _("ftpfs: aborting transfer."));
1454 if (send (SUP->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1456 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1457 if (dsock != -1)
1458 close (dsock);
1459 return;
1462 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1464 vfs_print_message ("%s", _("ftpfs: abort failed"));
1465 if (dsock != -1)
1466 close (dsock);
1467 return;
1469 if (dsock != -1)
1471 FD_ZERO (&mask);
1472 FD_SET (dsock, &mask);
1473 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1475 struct timeval start_tim, tim;
1476 char buf[BUF_8K];
1478 gettimeofday (&start_tim, NULL);
1479 /* flush the remaining data */
1480 while (read (dsock, buf, sizeof (buf)) > 0)
1482 gettimeofday (&tim, NULL);
1483 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT)
1485 /* server keeps sending, drop the connection and ftpfs_reconnect */
1486 close (dsock);
1487 ftpfs_reconnect (me, super);
1488 return;
1492 close (dsock);
1494 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) == TRANSIENT) && (code == 426))
1495 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1498 /* --------------------------------------------------------------------------------------------- */
1500 #if 0
1501 static void
1502 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1503 struct vfs_s_inode *dir)
1505 struct linklist *flist;
1506 struct direntry *fe, *fel;
1507 char tmp[MC_MAXPATHLEN];
1508 int depth;
1510 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1511 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1513 /* flist->data->l_stat is alread initialized with 0 */
1514 fel = flist->data;
1515 if (S_ISLNK (fel->s.st_mode) && fel->linkname)
1517 if (IS_PATH_SEP (fel->linkname[0]))
1519 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1520 continue;
1521 strcpy (tmp, fel->linkname);
1523 else
1525 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1526 continue;
1527 strcpy (tmp, dir->remote_path);
1528 if (tmp[1] != '\0')
1529 strcat (tmp, PATH_SEP_STR);
1530 strcat (tmp + 1, fel->linkname);
1532 for (depth = 0; depth < 100; depth++)
1533 { /* depth protects against recursive symbolic links */
1534 canonicalize_pathname (tmp);
1535 fe = _get_file_entry_t (bucket, tmp, 0, 0);
1536 if (fe)
1538 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1540 /* Symlink points to link which isn't resolved, yet. */
1541 if (IS_PATH_SEP (fe->linkname[0]))
1543 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1544 break;
1545 strcpy (tmp, fe->linkname);
1547 else
1549 /* at this point tmp looks always like this
1550 /directory/filename, i.e. no need to check
1551 strrchr's return value */
1552 *(strrchr (tmp, PATH_SEP) + 1) = '\0'; /* dirname */
1553 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1554 break;
1555 strcat (tmp, fe->linkname);
1557 continue;
1559 else
1561 fel->l_stat = g_new (struct stat, 1);
1562 if (S_ISLNK (fe->s.st_mode))
1563 *fel->l_stat = *fe->l_stat;
1564 else
1565 *fel->l_stat = fe->s;
1566 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1569 break;
1573 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1576 /* --------------------------------------------------------------------------------------------- */
1578 static void
1579 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1580 struct vfs_s_inode *dir)
1582 char buffer[2048] = "", *filename;
1583 int sock;
1584 FILE *fp;
1585 struct stat s;
1586 struct linklist *flist;
1587 struct direntry *fe;
1588 int switch_method = 0;
1590 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1591 if (strchr (dir->remote_path, ' '))
1593 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1595 vfs_print_message ("%s", _("ftpfs: CWD failed."));
1596 return;
1598 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1600 else
1601 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1603 if (sock == -1)
1605 vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink"));
1606 return;
1609 fp = fdopen (sock, "r");
1610 if (fp == NULL)
1612 close (sock);
1613 vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink"));
1614 return;
1616 tty_enable_interrupt_key ();
1617 flist = dir->file_list->next;
1618 while (1)
1622 if (flist == dir->file_list)
1623 goto done;
1624 fe = flist->data;
1625 flist = flist->next;
1627 while (!S_ISLNK (fe->s.st_mode));
1628 while (1)
1630 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1631 goto done;
1632 if (MEDATA->logfile)
1634 fputs (buffer, MEDATA->logfile);
1635 fflush (MEDATA->logfile);
1637 vfs_die ("This code should be commented out\n");
1638 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1640 int r = strcmp (fe->name, filename);
1641 g_free (filename);
1642 if (r == 0)
1644 if (S_ISLNK (s.st_mode))
1646 /* This server doesn't understand LIST -lLa */
1647 switch_method = 1;
1648 goto done;
1650 fe->l_stat = g_new (struct stat, 1);
1651 if (fe->l_stat == NULL)
1652 goto done;
1653 *fe->l_stat = s;
1654 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1655 break;
1657 if (r < 0)
1658 break;
1662 done:
1663 while (fgets (buffer, sizeof (buffer), fp) != NULL);
1664 tty_disable_interrupt_key ();
1665 fclose (fp);
1666 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1669 /* --------------------------------------------------------------------------------------------- */
1671 static void
1672 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1674 vfs_print_message ("%s", _("Resolving symlink..."));
1676 if (SUP->strict_rfc959_list_cmd)
1677 resolve_symlink_without_ls_options (me, super, dir);
1678 else
1679 resolve_symlink_with_ls_options (me, super, dir);
1681 #endif
1683 /* --------------------------------------------------------------------------------------------- */
1685 static int
1686 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1688 struct vfs_s_entry *ent;
1689 struct vfs_s_super *super = dir->super;
1690 int sock, num_entries = 0;
1691 gboolean cd_first;
1693 cd_first = ftpfs_first_cd_then_ls || (SUP->strict == RFC_STRICT)
1694 || (strchr (remote_path, ' ') != NULL);
1696 again:
1697 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1698 remote_path,
1699 SUP->strict ==
1700 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1702 if (cd_first)
1704 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1706 ftpfs_errno = ENOENT;
1707 vfs_print_message ("%s", _("ftpfs: CWD failed."));
1708 return -1;
1712 gettimeofday (&dir->timestamp, NULL);
1713 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1715 if (SUP->strict == RFC_STRICT)
1716 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1717 else if (cd_first)
1718 /* Dirty hack to avoid autoprepending / to . */
1719 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1720 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1721 else
1723 char *path;
1725 /* Trailing "/." is necessary if remote_path is a symlink */
1726 path = mc_build_filename (remote_path, ".", (char *) NULL);
1727 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1728 g_free (path);
1731 if (sock == -1)
1732 goto fallback;
1734 /* Clear the interrupt flag */
1735 tty_enable_interrupt_key ();
1737 vfs_parse_ls_lga_init ();
1738 while (TRUE)
1740 int i;
1741 size_t count_spaces = 0;
1742 int res;
1743 char lc_buffer[BUF_8K] = "\0";
1745 res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer), sock);
1746 if (res == 0)
1747 break;
1749 if (res == EINTR)
1751 me->verrno = ECONNRESET;
1752 close (sock);
1753 SUP->ctl_connection_busy = 0;
1754 tty_disable_interrupt_key ();
1755 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1756 vfs_print_message (_("%s: failure"), me->name);
1757 return -1;
1760 if (MEDATA->logfile)
1762 fputs (lc_buffer, MEDATA->logfile);
1763 fputs ("\n", MEDATA->logfile);
1764 fflush (MEDATA->logfile);
1767 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1768 i = ent->ino->st.st_nlink;
1769 if (!vfs_parse_ls_lga
1770 (lc_buffer, &ent->ino->st, &ent->name, &ent->ino->linkname, &count_spaces))
1772 vfs_s_free_entry (me, ent);
1773 continue;
1775 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1776 num_entries++;
1777 vfs_s_store_filename_leading_spaces (ent, count_spaces);
1778 vfs_s_insert_entry (me, dir, ent);
1781 close (sock);
1782 SUP->ctl_connection_busy = 0;
1783 me->verrno = E_REMOTE;
1784 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1785 goto fallback;
1787 if (num_entries == 0 && !cd_first)
1789 /* The LIST command may produce an empty output. In such scenario
1790 it is not clear whether this is caused by 'remote_path' being
1791 a non-existent path or for some other reason (listing emtpy
1792 directory without the -a option, non-readable directory, etc.).
1794 Since 'dir_load' is a crucial method, when it comes to determine
1795 whether a given path is a _directory_, the code must try its best
1796 to determine the type of 'remote_path'. The only reliable way to
1797 achieve this is trough issuing a CWD command. */
1799 cd_first = TRUE;
1800 goto again;
1803 vfs_s_normalize_filename_leading_spaces (dir, vfs_parse_ls_lga_get_final_spaces ());
1805 if (SUP->strict == RFC_AUTODETECT)
1806 SUP->strict = RFC_DARING;
1808 vfs_print_message (_("%s: done."), me->name);
1809 return 0;
1811 fallback:
1812 if (SUP->strict == RFC_AUTODETECT)
1814 /* It's our first attempt to get a directory listing from this
1815 server (UNIX style LIST command) */
1816 SUP->strict = RFC_STRICT;
1817 /* I hate goto, but recursive call needs another 8K on stack */
1818 /* return ftpfs_dir_load (me, dir, remote_path); */
1819 cd_first = TRUE;
1820 goto again;
1822 vfs_print_message ("%s", _("ftpfs: failed; nowhere to fallback to"));
1823 ERRNOR (EACCES, -1);
1826 /* --------------------------------------------------------------------------------------------- */
1828 static int
1829 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname)
1831 int h, sock;
1832 off_t n_stored;
1833 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1834 struct linger li;
1835 #else
1836 int flag_one = 1;
1837 #endif
1838 char lc_buffer[BUF_8K];
1839 struct stat s;
1840 char *w_buf;
1841 struct vfs_s_super *super = FH_SUPER;
1842 ftp_fh_data_t *ftp = (ftp_fh_data_t *) fh->data;
1844 h = open (localname, O_RDONLY);
1845 if (h == -1)
1846 ERRNOR (EIO, -1);
1848 if (fstat (h, &s) == -1 ||
1849 ((sock =
1850 ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY,
1851 0)) < 0))
1853 close (h);
1854 return -1;
1856 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1857 li.l_onoff = 1;
1858 li.l_linger = 120;
1859 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1860 #else
1861 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1862 #endif
1863 n_stored = 0;
1865 tty_enable_interrupt_key ();
1866 while (TRUE)
1868 ssize_t n_read, n_written;
1870 while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1872 if (errno != EINTR)
1874 ftpfs_errno = errno;
1875 goto error_return;
1877 if (tty_got_interrupt ())
1879 ftpfs_errno = EINTR;
1880 goto error_return;
1883 if (n_read == 0)
1884 break;
1885 n_stored += n_read;
1886 w_buf = lc_buffer;
1887 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1889 if (n_written == -1)
1891 if (errno == EINTR && !tty_got_interrupt ())
1892 continue;
1894 ftpfs_errno = errno;
1895 goto error_return;
1897 w_buf += n_written;
1898 n_read -= n_written;
1900 vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
1901 _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
1903 tty_disable_interrupt_key ();
1904 close (sock);
1905 SUP->ctl_connection_busy = 0;
1906 close (h);
1907 if (ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE)
1908 ERRNOR (EIO, -1);
1909 return 0;
1910 error_return:
1911 tty_disable_interrupt_key ();
1912 close (sock);
1913 SUP->ctl_connection_busy = 0;
1914 close (h);
1915 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1916 return -1;
1919 /* --------------------------------------------------------------------------------------------- */
1921 static int
1922 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset)
1924 char *name;
1926 if (fh->data == NULL)
1927 fh->data = g_new0 (ftp_fh_data_t, 1);
1929 name = vfs_s_fullpath (me, fh->ino);
1930 if (name == NULL)
1931 return 0;
1932 FH_SOCK = ftpfs_open_data_connection (me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1933 g_free (name);
1934 if (FH_SOCK == -1)
1935 ERRNOR (EACCES, 0);
1936 fh->linear = LS_LINEAR_OPEN;
1937 ((ftp_fh_data_t *) fh->data)->append = 0;
1938 return 1;
1941 /* --------------------------------------------------------------------------------------------- */
1943 static ssize_t
1944 ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t * fh, void *buf, size_t len)
1946 ssize_t n;
1947 struct vfs_s_super *super = FH_SUPER;
1949 while ((n = read (FH_SOCK, buf, len)) < 0)
1951 if ((errno == EINTR) && !tty_got_interrupt ())
1952 continue;
1953 break;
1956 if (n < 0)
1957 ftpfs_linear_abort (me, fh);
1959 if (n == 0)
1961 SUP->ctl_connection_busy = 0;
1962 close (FH_SOCK);
1963 FH_SOCK = -1;
1964 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1965 ERRNOR (E_REMOTE, -1);
1966 return 0;
1968 ERRNOR (errno, n);
1971 /* --------------------------------------------------------------------------------------------- */
1973 static void
1974 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t * fh)
1976 if (FH_SOCK != -1)
1977 ftpfs_linear_abort (me, fh);
1980 /* --------------------------------------------------------------------------------------------- */
1982 static int
1983 ftpfs_ctl (void *fh, int ctlop, void *arg)
1985 (void) arg;
1987 switch (ctlop)
1989 case VFS_CTL_IS_NOTREADY:
1991 int v;
1993 if (FH->linear == LS_NOT_LINEAR)
1994 vfs_die ("You may not do this");
1995 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1996 return 0;
1998 v = vfs_s_select_on_two (((ftp_fh_data_t *) (FH->data))->sock, 0);
1999 return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
2001 default:
2002 return 0;
2006 /* --------------------------------------------------------------------------------------------- */
2008 static int
2009 ftpfs_send_command (const vfs_path_t * vpath, const char *cmd, int flags)
2011 const char *rpath;
2012 char *p;
2013 struct vfs_s_super *super;
2014 int r;
2015 const vfs_path_element_t *path_element;
2016 int flush_directory_cache = (flags & OPT_FLUSH);
2018 path_element = vfs_path_get_by_index (vpath, -1);
2020 rpath = vfs_s_get_path (vpath, &super, 0);
2021 if (rpath == NULL)
2022 return -1;
2024 p = ftpfs_translate_path (path_element->class, super, rpath);
2025 r = ftpfs_command (path_element->class, super, WAIT_REPLY, cmd, p);
2026 g_free (p);
2027 vfs_stamp_create (&vfs_ftpfs_ops, super);
2028 if (flags & OPT_IGNORE_ERROR)
2029 r = COMPLETE;
2030 if (r != COMPLETE)
2032 path_element->class->verrno = EPERM;
2033 return -1;
2035 if (flush_directory_cache)
2036 vfs_s_invalidate (path_element->class, super);
2037 return 0;
2040 /* --------------------------------------------------------------------------------------------- */
2042 static int
2043 ftpfs_stat (const vfs_path_t * vpath, struct stat *buf)
2045 int ret;
2047 ret = vfs_s_stat (vpath, buf);
2048 ftpfs_set_blksize (buf);
2049 return ret;
2052 /* --------------------------------------------------------------------------------------------- */
2054 static int
2055 ftpfs_lstat (const vfs_path_t * vpath, struct stat *buf)
2057 int ret;
2059 ret = vfs_s_lstat (vpath, buf);
2060 ftpfs_set_blksize (buf);
2061 return ret;
2064 /* --------------------------------------------------------------------------------------------- */
2066 static int
2067 ftpfs_fstat (void *vfs_info, struct stat *buf)
2069 int ret;
2071 ret = vfs_s_fstat (vfs_info, buf);
2072 ftpfs_set_blksize (buf);
2073 return ret;
2076 /* --------------------------------------------------------------------------------------------- */
2078 static int
2079 ftpfs_chmod (const vfs_path_t * vpath, mode_t mode)
2081 char buf[BUF_SMALL];
2082 int ret;
2084 g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", (unsigned int) (mode & 07777));
2086 ret = ftpfs_send_command (vpath, buf, OPT_FLUSH);
2088 return ftpfs_ignore_chattr_errors ? 0 : ret;
2091 /* --------------------------------------------------------------------------------------------- */
2093 static int
2094 ftpfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
2096 #if 0
2097 (void) vpath;
2098 (void) owner;
2099 (void) group;
2101 ftpfs_errno = EPERM;
2102 return -1;
2103 #else
2104 /* Everyone knows it is not possible to chown remotely, so why bother them.
2105 If someone's root, then copy/move will always try to chown it... */
2106 (void) vpath;
2107 (void) owner;
2108 (void) group;
2109 return 0;
2110 #endif
2113 /* --------------------------------------------------------------------------------------------- */
2115 static int
2116 ftpfs_unlink (const vfs_path_t * vpath)
2118 return ftpfs_send_command (vpath, "DELE /%s", OPT_FLUSH);
2121 /* --------------------------------------------------------------------------------------------- */
2123 /* Return 1 if path is the same directory as the one we are in now */
2124 static int
2125 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
2127 (void) me;
2129 if (SUP->current_dir == NULL)
2130 return FALSE;
2131 return (strcmp (path, SUP->current_dir) == 0);
2134 /* --------------------------------------------------------------------------------------------- */
2136 static int
2137 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
2139 int r;
2140 char *p;
2142 if (!SUP->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
2143 return COMPLETE;
2145 p = ftpfs_translate_path (me, super, remote_path);
2146 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
2147 g_free (p);
2149 if (r != COMPLETE)
2150 ftpfs_errno = EIO;
2151 else
2153 g_free (SUP->current_dir);
2154 SUP->current_dir = g_strdup (remote_path);
2155 SUP->cwd_deferred = 0;
2157 return r;
2160 /* --------------------------------------------------------------------------------------------- */
2162 static int
2163 ftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
2165 ftpfs_send_command (vpath1, "RNFR /%s", OPT_FLUSH);
2166 return ftpfs_send_command (vpath2, "RNTO /%s", OPT_FLUSH);
2169 /* --------------------------------------------------------------------------------------------- */
2171 static int
2172 ftpfs_mkdir (const vfs_path_t * vpath, mode_t mode)
2174 (void) mode; /* FIXME: should be used */
2176 return ftpfs_send_command (vpath, "MKD /%s", OPT_FLUSH);
2179 /* --------------------------------------------------------------------------------------------- */
2181 static int
2182 ftpfs_rmdir (const vfs_path_t * vpath)
2184 return ftpfs_send_command (vpath, "RMD /%s", OPT_FLUSH);
2187 /* --------------------------------------------------------------------------------------------- */
2189 static void
2190 ftpfs_fh_free_data (vfs_file_handler_t * fh)
2192 if (fh != NULL)
2193 MC_PTR_FREE (fh->data);
2196 /* --------------------------------------------------------------------------------------------- */
2198 static int
2199 ftpfs_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
2201 ftp_fh_data_t *ftp;
2203 (void) mode;
2205 fh->data = g_new0 (ftp_fh_data_t, 1);
2206 ftp = (ftp_fh_data_t *) fh->data;
2207 /* File will be written only, so no need to retrieve it from ftp server */
2208 if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
2210 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2211 struct linger li;
2212 #else
2213 int li = 1;
2214 #endif
2215 char *name;
2217 /* ftpfs_linear_start() called, so data will be written
2218 * to local temporary file and stored to ftp server
2219 * by vfs_s_close later
2221 if (((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy)
2223 if (!fh->ino->localname)
2225 vfs_path_t *vpath;
2226 int handle;
2228 handle = vfs_mkstemps (&vpath, me->name, fh->ino->ent->name);
2229 if (handle == -1)
2231 vfs_path_free (vpath);
2232 goto fail;
2234 close (handle);
2235 fh->ino->localname = g_strdup (vfs_path_as_str (vpath));
2236 vfs_path_free (vpath);
2237 ftp->append = flags & O_APPEND;
2239 return 0;
2241 name = vfs_s_fullpath (me, fh->ino);
2242 if (name == NULL)
2243 goto fail;
2244 fh->handle =
2245 ftpfs_open_data_connection (me, fh->ino->super,
2246 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
2247 g_free (name);
2249 if (fh->handle < 0)
2250 goto fail;
2251 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2252 li.l_onoff = 1;
2253 li.l_linger = 120;
2254 #endif
2255 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
2257 if (fh->ino->localname)
2259 unlink (fh->ino->localname);
2260 MC_PTR_FREE (fh->ino->localname);
2262 return 0;
2265 if (!fh->ino->localname && vfs_s_retrieve_file (me, fh->ino) == -1)
2266 goto fail;
2267 if (!fh->ino->localname)
2268 vfs_die ("retrieve_file failed to fill in localname");
2269 return 0;
2271 fail:
2272 ftpfs_fh_free_data (fh);
2273 return -1;
2276 /* --------------------------------------------------------------------------------------------- */
2278 static int
2279 ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t * fh)
2281 if (fh->handle != -1 && !fh->ino->localname)
2283 ftp_super_data_t *ftp = (ftp_super_data_t *) fh->ino->super->data;
2285 close (fh->handle);
2286 fh->handle = -1;
2287 ftp->ctl_connection_busy = 0;
2288 /* File is stored to destination already, so
2289 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2291 fh->changed = 0;
2292 if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE)
2293 ERRNOR (EIO, -1);
2294 vfs_s_invalidate (me, FH_SUPER);
2297 return 0;
2300 /* --------------------------------------------------------------------------------------------- */
2302 static void
2303 ftpfs_done (struct vfs_class *me)
2305 (void) me;
2307 g_slist_free_full (no_proxy, g_free);
2309 g_free (ftpfs_anonymous_passwd);
2310 g_free (ftpfs_proxy_host);
2313 /* --------------------------------------------------------------------------------------------- */
2315 static void
2316 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
2318 GList *iter;
2320 for (iter = MEDATA->supers; iter != NULL; iter = g_list_next (iter))
2322 const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
2323 char *name;
2325 name = vfs_path_element_build_pretty_path_str (super->path_element);
2327 func (name);
2328 g_free (name);
2332 /* --------------------------------------------------------------------------------------------- */
2334 static keyword_t
2335 ftpfs_netrc_next (void)
2337 char *p;
2338 keyword_t i;
2339 static const char *const keywords[] = { "default", "machine",
2340 "login", "password", "passwd", "account", "macdef", NULL
2343 while (1)
2345 netrcp = skip_separators (netrcp);
2346 if (*netrcp != '\n')
2347 break;
2348 netrcp++;
2350 if (!*netrcp)
2351 return NETRC_NONE;
2352 p = buffer;
2353 if (*netrcp == '"')
2355 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++)
2357 if (*netrcp == '\\')
2358 netrcp++;
2359 *p++ = *netrcp;
2362 else
2364 for (; *netrcp != '\0' && !whiteness (*netrcp) && *netrcp != ','; netrcp++)
2366 if (*netrcp == '\\')
2367 netrcp++;
2368 *p++ = *netrcp;
2371 *p = 0;
2372 if (!*buffer)
2373 return NETRC_NONE;
2375 for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2376 if (strcmp (keywords[i - 1], buffer) == 0)
2377 return i;
2379 return NETRC_UNKNOWN;
2382 /* --------------------------------------------------------------------------------------------- */
2384 static int
2385 ftpfs_netrc_bad_mode (const char *netrcname)
2387 struct stat mystat;
2389 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077))
2391 static int be_angry = 1;
2393 if (be_angry)
2395 message (D_ERROR, MSG_ERROR,
2396 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2397 be_angry = 0;
2399 return 1;
2401 return 0;
2404 /* --------------------------------------------------------------------------------------------- */
2405 /* Scan .netrc until we find matching "machine" or "default"
2406 * domain is used for additional matching
2407 * No search is done after "default" in compliance with "man netrc"
2408 * Return 0 if found, -1 otherwise */
2410 static int
2411 ftpfs_find_machine (const char *host, const char *domain)
2413 keyword_t keyword;
2415 if (!host)
2416 host = "";
2417 if (!domain)
2418 domain = "";
2420 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2422 if (keyword == NETRC_DEFAULT)
2423 return 0;
2425 if (keyword == NETRC_MACDEF)
2427 /* Scan for an empty line, which concludes "macdef" */
2430 while (*netrcp && *netrcp != '\n')
2431 netrcp++;
2432 if (*netrcp != '\n')
2433 break;
2434 netrcp++;
2436 while (*netrcp && *netrcp != '\n');
2437 continue;
2440 if (keyword != NETRC_MACHINE)
2441 continue;
2443 /* Take machine name */
2444 if (ftpfs_netrc_next () == NETRC_NONE)
2445 break;
2447 if (g_ascii_strcasecmp (host, buffer) != 0)
2449 /* Try adding our domain to short names in .netrc */
2450 const char *host_domain = strchr (host, '.');
2451 if (!host_domain)
2452 continue;
2454 /* Compare domain part */
2455 if (g_ascii_strcasecmp (host_domain, domain) != 0)
2456 continue;
2458 /* Compare local part */
2459 if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2460 continue;
2463 return 0;
2466 /* end of .netrc */
2467 return -1;
2470 /* --------------------------------------------------------------------------------------------- */
2471 /* Extract login and password from .netrc for the host.
2472 * pass may be NULL.
2473 * Returns 0 for success, -1 for error */
2475 static int
2476 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2478 char *netrcname;
2479 char *tmp_pass = NULL;
2480 char hostname[MAXHOSTNAMELEN];
2481 const char *domain;
2482 static struct rupcache
2484 struct rupcache *next;
2485 char *host;
2486 char *login;
2487 char *pass;
2488 } *rup_cache = NULL, *rupp;
2490 /* Initialize *login and *pass */
2491 MC_PTR_FREE (*login);
2492 MC_PTR_FREE (*pass);
2494 /* Look up in the cache first */
2495 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2497 if (strcmp (host, rupp->host) == 0)
2499 *login = g_strdup (rupp->login);
2500 *pass = g_strdup (rupp->pass);
2501 return 0;
2505 /* Load current .netrc */
2506 netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2507 if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2509 g_free (netrcname);
2510 return 0;
2513 netrcp = netrc;
2515 /* Find our own domain name */
2516 if (gethostname (hostname, sizeof (hostname)) < 0)
2517 *hostname = '\0';
2519 domain = strchr (hostname, '.');
2520 if (domain == NULL)
2521 domain = "";
2523 /* Scan for "default" and matching "machine" keywords */
2524 ftpfs_find_machine (host, domain);
2526 /* Scan for keywords following "default" and "machine" */
2527 while (1)
2529 keyword_t keyword;
2531 int need_break = 0;
2532 keyword = ftpfs_netrc_next ();
2534 switch (keyword)
2536 case NETRC_LOGIN:
2537 if (ftpfs_netrc_next () == NETRC_NONE)
2539 need_break = 1;
2540 break;
2543 /* We have another name already - should not happen */
2544 if (*login)
2546 need_break = 1;
2547 break;
2550 /* We have login name now */
2551 *login = g_strdup (buffer);
2552 break;
2554 case NETRC_PASSWORD:
2555 case NETRC_PASSWD:
2556 if (ftpfs_netrc_next () == NETRC_NONE)
2558 need_break = 1;
2559 break;
2562 /* Ignore unsafe passwords */
2563 if (*login != NULL &&
2564 strcmp (*login, "anonymous") != 0 && strcmp (*login, "ftp") != 0
2565 && ftpfs_netrc_bad_mode (netrcname))
2567 need_break = 1;
2568 break;
2571 /* Remember password. pass may be NULL, so use tmp_pass */
2572 if (tmp_pass == NULL)
2573 tmp_pass = g_strdup (buffer);
2574 break;
2576 case NETRC_ACCOUNT:
2577 /* "account" is followed by a token which we ignore */
2578 if (ftpfs_netrc_next () == NETRC_NONE)
2580 need_break = 1;
2581 break;
2584 /* Ignore account, but warn user anyways */
2585 ftpfs_netrc_bad_mode (netrcname);
2586 break;
2588 default:
2589 /* Unexpected keyword or end of file */
2590 need_break = 1;
2591 break;
2594 if (need_break)
2595 break;
2598 g_free (netrc);
2599 g_free (netrcname);
2601 rupp = g_new (struct rupcache, 1);
2602 rupp->host = g_strdup (host);
2603 rupp->login = g_strdup (*login);
2604 rupp->pass = g_strdup (tmp_pass);
2606 rupp->next = rup_cache;
2607 rup_cache = rupp;
2609 *pass = tmp_pass;
2611 return 0;
2614 /* --------------------------------------------------------------------------------------------- */
2615 /*** public functions ****************************************************************************/
2616 /* --------------------------------------------------------------------------------------------- */
2618 /** This routine is called as the last step in load_setup */
2619 void
2620 ftpfs_init_passwd (void)
2622 ftpfs_anonymous_passwd = load_anon_passwd ();
2623 if (ftpfs_anonymous_passwd)
2624 return;
2626 /* If there is no anonymous ftp password specified
2627 * then we'll just use anonymous@
2628 * We don't send any other thing because:
2629 * - We want to remain anonymous
2630 * - We want to stop SPAM
2631 * - We don't want to let ftp sites to discriminate by the user,
2632 * host or country.
2634 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
2637 /* --------------------------------------------------------------------------------------------- */
2639 void
2640 init_ftpfs (void)
2642 static struct vfs_s_subclass ftpfs_subclass;
2644 tcp_init ();
2646 ftpfs_subclass.flags = VFS_S_REMOTE | VFS_S_USETMP;
2647 ftpfs_subclass.archive_same = ftpfs_archive_same;
2648 ftpfs_subclass.open_archive = ftpfs_open_archive;
2649 ftpfs_subclass.free_archive = ftpfs_free_archive;
2650 ftpfs_subclass.fh_open = ftpfs_fh_open;
2651 ftpfs_subclass.fh_close = ftpfs_fh_close;
2652 ftpfs_subclass.fh_free_data = ftpfs_fh_free_data;
2653 ftpfs_subclass.dir_load = ftpfs_dir_load;
2654 ftpfs_subclass.file_store = ftpfs_file_store;
2655 ftpfs_subclass.linear_start = ftpfs_linear_start;
2656 ftpfs_subclass.linear_read = ftpfs_linear_read;
2657 ftpfs_subclass.linear_close = ftpfs_linear_close;
2659 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2660 vfs_ftpfs_ops.name = "ftpfs";
2661 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2662 vfs_ftpfs_ops.prefix = "ftp";
2663 vfs_ftpfs_ops.done = &ftpfs_done;
2664 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2665 vfs_ftpfs_ops.stat = ftpfs_stat;
2666 vfs_ftpfs_ops.lstat = ftpfs_lstat;
2667 vfs_ftpfs_ops.fstat = ftpfs_fstat;
2668 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2669 vfs_ftpfs_ops.chown = ftpfs_chown;
2670 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2671 vfs_ftpfs_ops.rename = ftpfs_rename;
2672 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2673 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2674 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2675 vfs_register_class (&vfs_ftpfs_ops);
2678 /* --------------------------------------------------------------------------------------------- */