Revert "VFS: make VFS-specific super class as derived one from vfs_s_super."
[midnight-commander.git] / src / vfs / ftpfs / ftpfs.c
blob2350cbb752c6ed1f90050dc6054b2e88fe227115
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_s_subclass ftpfs_subclass;
241 static struct vfs_class *vfs_ftpfs_ops = (struct vfs_class *) &ftpfs_subclass;
243 static GSList *no_proxy;
245 static char buffer[BUF_MEDIUM];
246 static char *netrc;
247 static const char *netrcp;
249 /* --------------------------------------------------------------------------------------------- */
250 /*** file scope functions ************************************************************************/
251 /* --------------------------------------------------------------------------------------------- */
253 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
254 Translate a Unix path, i.e. MC's internal path representation (e.g.
255 /somedir/somefile) to a path valid for the remote server. Every path
256 transferred to the remote server has to be mangled by this function
257 right prior to sending it.
258 Currently only Amiga ftp servers are handled in a special manner.
260 When the remote server is an amiga:
261 a) strip leading slash if necesarry
262 b) replace first occurrence of ":/" with ":"
263 c) strip trailing "/."
266 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
267 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super,
268 const char *remote_path);
269 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
270 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
271 const char *netrcpass);
272 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
274 /* --------------------------------------------------------------------------------------------- */
276 static void
277 ftpfs_set_blksize (struct stat *s)
279 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
280 /* redefine block size */
281 s->st_blksize = 64 * 1024; /* FIXME */
282 #endif
285 /* --------------------------------------------------------------------------------------------- */
287 static struct stat *
288 ftpfs_default_stat (struct vfs_class *me)
290 struct stat *s;
292 s = vfs_s_default_stat (me, S_IFDIR | 0755);
293 ftpfs_set_blksize (s);
294 vfs_adjust_stat (s);
296 return s;
299 /* --------------------------------------------------------------------------------------------- */
301 static char *
302 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
304 if (!SUP->remote_is_amiga)
305 return g_strdup (remote_path);
306 else
308 char *ret, *p;
310 if (MEDATA->logfile)
312 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
313 fflush (MEDATA->logfile);
316 /* strip leading slash(es) */
317 while (IS_PATH_SEP (*remote_path))
318 remote_path++;
321 * Don't change "/" into "", e.g. "CWD " would be
322 * invalid.
324 if (*remote_path == '\0')
325 return g_strdup (".");
327 ret = g_strdup (remote_path);
329 /* replace first occurrence of ":/" with ":" */
330 p = strchr (ret, ':');
331 if (p != NULL && IS_PATH_SEP (p[1]))
332 str_move (p + 1, p + 2);
334 /* strip trailing "/." */
335 p = strrchr (ret, PATH_SEP);
336 if ((p != NULL) && (*(p + 1) == '.') && (*(p + 2) == '\0'))
337 *p = '\0';
339 return ret;
343 /* --------------------------------------------------------------------------------------------- */
344 /** Extract the hostname and username from the path */
346 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
347 * ftp://sunsite.unc.edu/pub/linux
348 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
349 * ftp://tsx-11.mit.edu:8192/
350 * ftp://joe@foo.edu:11321/private
351 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
352 * is supplied.
355 static vfs_path_element_t *
356 ftpfs_correct_url_parameters (const vfs_path_element_t * velement)
358 vfs_path_element_t *path_element = vfs_path_element_clone (velement);
360 if (path_element->port == 0)
361 path_element->port = FTP_COMMAND_PORT;
363 if (path_element->user == NULL)
365 /* Look up user and password in netrc */
366 if (ftpfs_use_netrc)
367 ftpfs_netrc_lookup (path_element->host, &path_element->user, &path_element->password);
369 if (path_element->user == NULL)
370 path_element->user = g_strdup ("anonymous");
372 /* Look up password in netrc for known user */
373 if (ftpfs_use_netrc && path_element->password == NULL)
375 char *new_user = NULL;
376 char *new_passwd = NULL;
378 ftpfs_netrc_lookup (path_element->host, &new_user, &new_passwd);
380 /* If user is different, remove password */
381 if (new_user != NULL && strcmp (path_element->user, new_user) != 0)
382 MC_PTR_FREE (path_element->password);
384 g_free (new_user);
385 g_free (new_passwd);
388 return path_element;
391 /* --------------------------------------------------------------------------------------------- */
392 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
394 static int
395 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
397 char answer[BUF_1K];
398 int i;
400 while (TRUE)
402 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
404 if (string_buf != NULL)
405 *string_buf = '\0';
406 code = 421;
407 return 4;
409 /* cppcheck-suppress invalidscanf */
410 switch (sscanf (answer, "%d", &code))
412 case 0:
413 if (string_buf != NULL)
414 g_strlcpy (string_buf, answer, string_len);
415 code = 500;
416 return 5;
417 case 1:
418 if (answer[3] == '-')
420 while (TRUE)
422 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
424 if (string_buf != NULL)
425 *string_buf = '\0';
426 code = 421;
427 return 4;
429 /* cppcheck-suppress invalidscanf */
430 if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' '))
431 break;
434 if (string_buf != NULL)
435 g_strlcpy (string_buf, answer, string_len);
436 return code / 100;
437 default:
438 break;
443 /* --------------------------------------------------------------------------------------------- */
445 static int
446 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
448 int sock;
450 sock = ftpfs_open_socket (me, super);
451 if (sock != -1)
453 char *cwdir = SUP->current_dir;
455 close (SUP->sock);
456 SUP->sock = sock;
457 SUP->current_dir = NULL;
459 if (ftpfs_login_server (me, super, super->path_element->password) != 0)
461 if (cwdir == NULL)
462 return 1;
463 sock = ftpfs_chdir_internal (me, super, cwdir);
464 g_free (cwdir);
465 return sock == COMPLETE ? 1 : 0;
468 SUP->current_dir = cwdir;
471 return 0;
474 /* --------------------------------------------------------------------------------------------- */
476 static int
477 G_GNUC_PRINTF (4, 5)
478 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt,
479 ...)
481 va_list ap;
482 GString *cmdstr;
483 int status;
484 static int retry = 0;
485 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
487 cmdstr = g_string_sized_new (32);
488 va_start (ap, fmt);
489 g_string_vprintf (cmdstr, fmt, ap);
490 va_end (ap);
491 g_string_append (cmdstr, "\r\n");
493 if (MEDATA->logfile != NULL)
495 if (strncmp (cmdstr->str, "PASS ", 5) == 0)
496 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
497 else
499 size_t ret;
501 ret = fwrite (cmdstr->str, cmdstr->len, 1, MEDATA->logfile);
502 (void) ret;
505 fflush (MEDATA->logfile);
508 got_sigpipe = 0;
509 tty_enable_interrupt_key ();
510 status = write (SUP->sock, cmdstr->str, cmdstr->len);
512 if (status < 0)
514 code = 421;
516 if (errno == EPIPE)
517 { /* Remote server has closed connection */
518 if (level == 0)
520 level = 1;
521 status = ftpfs_reconnect (me, super);
522 level = 0;
523 if (status && (write (SUP->sock, cmdstr->str, cmdstr->len) > 0))
524 goto ok;
527 got_sigpipe = 1;
529 g_string_free (cmdstr, TRUE);
530 tty_disable_interrupt_key ();
531 return TRANSIENT;
533 retry = 0;
535 tty_disable_interrupt_key ();
537 if (wait_reply)
539 status = ftpfs_get_reply (me, SUP->sock,
540 (wait_reply & WANT_STRING) ? reply_str : NULL,
541 sizeof (reply_str) - 1);
542 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
544 retry = 1;
545 level = 1;
546 status = ftpfs_reconnect (me, super);
547 level = 0;
548 if (status && (write (SUP->sock, cmdstr->str, cmdstr->len) > 0))
549 goto ok;
551 retry = 0;
552 g_string_free (cmdstr, TRUE);
553 return status;
556 g_string_free (cmdstr, TRUE);
557 return COMPLETE;
560 /* --------------------------------------------------------------------------------------------- */
562 static void
563 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
565 if (SUP->sock != -1)
567 vfs_print_message (_("ftpfs: Disconnecting from %s"), super->path_element->host);
568 ftpfs_command (me, super, NONE, "%s", "QUIT");
569 close (SUP->sock);
571 g_free (SUP->current_dir);
572 MC_PTR_FREE (super->data);
575 /* --------------------------------------------------------------------------------------------- */
577 static int
578 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
580 if (binary != SUP->isbinary)
582 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
583 ERRNOR (EIO, -1);
584 SUP->isbinary = binary;
586 return binary;
589 /* --------------------------------------------------------------------------------------------- */
590 /* This routine logs the user in */
592 static int
593 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
595 char *pass;
596 char *op;
597 char *name; /* login user name */
598 int anon = 0;
599 char reply_string[BUF_MEDIUM];
601 SUP->isbinary = TYPE_UNKNOWN;
603 if (super->path_element->password != NULL) /* explicit password */
604 op = g_strdup (super->path_element->password);
605 else if (netrcpass != NULL) /* password from netrc */
606 op = g_strdup (netrcpass);
607 else if (strcmp (super->path_element->user, "anonymous") == 0
608 || strcmp (super->path_element->user, "ftp") == 0)
610 if (ftpfs_anonymous_passwd == NULL) /* default anonymous password */
611 ftpfs_init_passwd ();
612 op = g_strdup (ftpfs_anonymous_passwd);
613 anon = 1;
615 else
616 { /* ask user */
617 char *p;
619 p = g_strdup_printf (_("FTP: Password required for %s"), super->path_element->user);
620 op = vfs_get_password (p);
621 g_free (p);
622 if (op == NULL)
623 ERRNOR (EPERM, 0);
624 super->path_element->password = g_strdup (op);
627 if (!anon || MEDATA->logfile)
628 pass = op;
629 else
631 pass = g_strconcat ("-", op, (char *) NULL);
632 wipe_password (op);
635 /* Proxy server accepts: username@host-we-want-to-connect */
636 if (SUP->proxy)
637 name =
638 g_strconcat (super->path_element->user, "@",
639 super->path_element->host[0] ==
640 '!' ? super->path_element->host + 1 : super->path_element->host,
641 (char *) NULL);
642 else
643 name = g_strdup (super->path_element->user);
645 if (ftpfs_get_reply (me, SUP->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
647 char *reply_up;
649 reply_up = g_ascii_strup (reply_string, -1);
650 SUP->remote_is_amiga = strstr (reply_up, "AMIGA") != 0;
651 if (strstr (reply_up, " SPFTP/1.0.0000 SERVER ")) /* handles `LIST -la` in a weird way */
652 SUP->strict = RFC_STRICT;
653 g_free (reply_up);
655 if (MEDATA->logfile)
657 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n", SUP->remote_is_amiga);
658 fflush (MEDATA->logfile);
661 vfs_print_message ("%s", _("ftpfs: sending login name"));
663 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
665 case CONTINUE:
666 vfs_print_message ("%s", _("ftpfs: sending user password"));
667 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
668 if (code == CONTINUE)
670 char *p;
672 p = g_strdup_printf (_("FTP: Account required for user %s"),
673 super->path_element->user);
674 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "",
675 INPUT_COMPLETE_USERNAMES);
676 g_free (p);
677 if (op == NULL)
678 ERRNOR (EPERM, 0);
679 vfs_print_message ("%s", _("ftpfs: sending user account"));
680 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
681 g_free (op);
683 if (code != COMPLETE)
684 break;
685 MC_FALLTHROUGH;
687 case COMPLETE:
688 vfs_print_message ("%s", _("ftpfs: logged in"));
689 wipe_password (pass);
690 g_free (name);
691 return 1;
693 default:
694 SUP->failed_on_login = 1;
695 wipe_password (super->path_element->password);
696 super->path_element->password = NULL;
698 goto login_fail;
702 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
703 super->path_element->user);
705 login_fail:
706 wipe_password (pass);
707 g_free (name);
708 ERRNOR (EPERM, 0);
711 /* --------------------------------------------------------------------------------------------- */
713 static void
714 ftpfs_load_no_proxy_list (void)
716 /* FixMe: shouldn't be hardcoded!!! */
717 char *mc_file;
719 mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL);
720 if (exist_file (mc_file))
722 FILE *npf;
724 npf = fopen (mc_file, "r");
725 if (npf != NULL)
727 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
729 while (fgets (s, sizeof (s), npf) != NULL)
731 char *p;
733 p = strchr (s, '\n');
734 if (p == NULL) /* skip bogus entries */
736 int c;
738 while ((c = fgetc (npf)) != EOF && c != '\n')
740 continue;
743 if (p != s)
745 *p = '\0';
746 no_proxy = g_slist_prepend (no_proxy, g_strdup (s));
749 fclose (npf);
752 g_free (mc_file);
755 /* --------------------------------------------------------------------------------------------- */
756 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
758 static int
759 ftpfs_check_proxy (const char *host)
761 GSList *npe;
763 if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0')
764 return 0; /* sanity check */
766 if (*host == '!')
767 return 1;
769 if (!ftpfs_always_use_proxy)
770 return 0;
772 if (strchr (host, '.') == NULL)
773 return 0;
775 ftpfs_load_no_proxy_list ();
776 for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe))
778 const char *domain = (const char *) npe->data;
780 if (domain[0] == '.')
782 size_t ld = strlen (domain);
783 size_t lh = strlen (host);
785 while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1])
787 ld--;
788 lh--;
791 if (ld == 0)
792 return 0;
794 else if (g_ascii_strcasecmp (host, domain) == 0)
795 return 0;
798 return 1;
801 /* --------------------------------------------------------------------------------------------- */
803 static void
804 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
806 vfs_path_element_t *path_element;
808 path_element = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
809 *host = g_strdup (path_element->host);
810 *port = path_element->port;
811 vfs_path_element_free (path_element);
814 /* --------------------------------------------------------------------------------------------- */
816 static int
817 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
819 struct addrinfo hints, *res, *curr_res;
820 int my_socket = 0;
821 char *host = NULL;
822 char port[8];
823 int tmp_port;
824 int e;
826 (void) me;
828 /* Use a proxy host? */
829 host = g_strdup (super->path_element->host);
831 if (host == NULL || *host == '\0')
833 vfs_print_message ("%s", _("ftpfs: Invalid host name."));
834 ftpfs_errno = EINVAL;
835 g_free (host);
836 return -1;
839 /* Hosts to connect to that start with a ! should use proxy */
840 tmp_port = super->path_element->port;
842 if (SUP->proxy != NULL)
843 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
845 g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_port);
846 if (port[0] == '\0')
848 g_free (host);
849 ftpfs_errno = errno;
850 return -1;
853 tty_enable_interrupt_key (); /* clear the interrupt flag */
855 memset (&hints, 0, sizeof (hints));
856 hints.ai_family = AF_UNSPEC;
857 hints.ai_socktype = SOCK_STREAM;
859 #ifdef AI_ADDRCONFIG
860 /* By default, only look up addresses using address types for
861 * which a local interface is configured (i.e. no IPv6 if no IPv6
862 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
863 hints.ai_flags = AI_ADDRCONFIG;
864 #endif
866 e = getaddrinfo (host, port, &hints, &res);
868 #ifdef AI_ADDRCONFIG
869 if (e == EAI_BADFLAGS)
871 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
872 hints.ai_flags = 0;
873 e = getaddrinfo (host, port, &hints, &res);
875 #endif
877 *port = '\0';
879 if (e != 0)
881 tty_disable_interrupt_key ();
882 vfs_print_message (_("ftpfs: %s"), gai_strerror (e));
883 g_free (host);
884 ftpfs_errno = EINVAL;
885 return -1;
888 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
890 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
892 if (my_socket < 0)
894 if (curr_res->ai_next != NULL)
895 continue;
897 tty_disable_interrupt_key ();
898 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno));
899 g_free (host);
900 freeaddrinfo (res);
901 ftpfs_errno = errno;
902 return -1;
905 vfs_print_message (_("ftpfs: making connection to %s"), host);
906 MC_PTR_FREE (host);
908 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
909 break;
911 ftpfs_errno = errno;
912 close (my_socket);
914 if (errno == EINTR && tty_got_interrupt ())
915 vfs_print_message ("%s", _("ftpfs: connection interrupted by user"));
916 else if (res->ai_next == NULL)
917 vfs_print_message (_("ftpfs: connection to server failed: %s"),
918 unix_error_string (errno));
919 else
920 continue;
922 freeaddrinfo (res);
923 tty_disable_interrupt_key ();
924 return -1;
927 freeaddrinfo (res);
928 tty_disable_interrupt_key ();
929 return my_socket;
932 /* --------------------------------------------------------------------------------------------- */
934 static int
935 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
937 int retry_seconds = 0;
938 int count_down;
940 /* We do not want to use the passive if we are using proxies */
941 if (SUP->proxy)
942 SUP->use_passive_connection = ftpfs_use_passive_connections_over_proxy;
946 SUP->failed_on_login = 0;
948 SUP->sock = ftpfs_open_socket (me, super);
949 if (SUP->sock == -1)
950 return -1;
952 if (ftpfs_login_server (me, super, NULL) != 0)
954 /* Logged in, no need to retry the connection */
955 break;
957 else
959 if (!SUP->failed_on_login)
960 return -1;
962 /* Close only the socket descriptor */
963 close (SUP->sock);
965 if (ftpfs_retry_seconds != 0)
967 retry_seconds = ftpfs_retry_seconds;
968 tty_enable_interrupt_key ();
969 for (count_down = retry_seconds; count_down; count_down--)
971 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
972 count_down);
973 sleep (1);
974 if (tty_got_interrupt ())
976 /* ftpfs_errno = E; */
977 tty_disable_interrupt_key ();
978 return 0;
981 tty_disable_interrupt_key ();
985 while (retry_seconds != 0);
987 SUP->current_dir = ftpfs_get_current_directory (me, super);
988 if (SUP->current_dir == NULL)
989 SUP->current_dir = g_strdup (PATH_SEP_STR);
991 return 0;
994 /* --------------------------------------------------------------------------------------------- */
996 static int
997 ftpfs_open_archive (struct vfs_s_super *super,
998 const vfs_path_t * vpath, const vfs_path_element_t * vpath_element)
1000 (void) vpath;
1002 super->data = g_new0 (ftp_super_data_t, 1);
1004 super->path_element = ftpfs_correct_url_parameters (vpath_element);
1005 SUP->proxy = NULL;
1006 if (ftpfs_check_proxy (super->path_element->host))
1007 SUP->proxy = ftpfs_proxy_host;
1008 SUP->use_passive_connection = ftpfs_use_passive_connections;
1009 SUP->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
1010 SUP->isbinary = TYPE_UNKNOWN;
1011 SUP->remote_is_amiga = 0;
1012 SUP->ctl_connection_busy = 0;
1013 super->name = g_strdup (PATH_SEP_STR);
1014 super->root =
1015 vfs_s_new_inode (vpath_element->class, super, ftpfs_default_stat (vpath_element->class));
1017 return ftpfs_open_archive_int (vpath_element->class, super);
1020 /* --------------------------------------------------------------------------------------------- */
1022 static int
1023 ftpfs_archive_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *super,
1024 const vfs_path_t * vpath, void *cookie)
1026 vfs_path_element_t *path_element;
1027 int result;
1029 (void) vpath;
1030 (void) cookie;
1032 path_element = ftpfs_correct_url_parameters (vpath_element);
1034 result = ((strcmp (path_element->host, super->path_element->host) == 0)
1035 && (strcmp (path_element->user, super->path_element->user) == 0)
1036 && (path_element->port == super->path_element->port)) ? 1 : 0;
1038 vfs_path_element_free (path_element);
1039 return result;
1042 /* --------------------------------------------------------------------------------------------- */
1043 /* The returned directory should always contain a trailing slash */
1045 static char *
1046 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
1048 char buf[MC_MAXPATHLEN + 1];
1050 if (ftpfs_command (me, super, NONE, "%s", "PWD") == COMPLETE &&
1051 ftpfs_get_reply (me, SUP->sock, buf, sizeof (buf)) == COMPLETE)
1053 char *bufp = NULL;
1054 char *bufq;
1056 for (bufq = buf; *bufq != '\0'; bufq++)
1057 if (*bufq == '"')
1059 if (bufp == NULL)
1060 bufp = bufq + 1;
1061 else
1063 *bufq = '\0';
1065 if (*bufp != '\0')
1067 if (!IS_PATH_SEP (bufq[-1]))
1069 *bufq++ = PATH_SEP;
1070 *bufq = '\0';
1073 if (IS_PATH_SEP (*bufp))
1074 return g_strdup (bufp);
1076 /* If the remote server is an Amiga a leading slash
1077 might be missing. MC needs it because it is used
1078 as separator between hostname and path internally. */
1079 return g_strconcat (PATH_SEP_STR, bufp, (char *) NULL);
1082 break;
1087 ftpfs_errno = EIO;
1088 return NULL;
1091 /* --------------------------------------------------------------------------------------------- */
1092 /* Setup Passive PASV FTP connection */
1094 static int
1095 ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
1096 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1098 char *c;
1099 char n[6];
1100 int xa, xb, xc, xd, xe, xf;
1102 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "%s", "PASV") != COMPLETE)
1103 return 0;
1105 /* Parse remote parameters */
1106 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
1108 if (!*c)
1109 return 0;
1110 if (!isdigit ((unsigned char) *c))
1111 return 0;
1112 /* cppcheck-suppress invalidscanf */
1113 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1114 return 0;
1116 n[0] = (unsigned char) xa;
1117 n[1] = (unsigned char) xb;
1118 n[2] = (unsigned char) xc;
1119 n[3] = (unsigned char) xd;
1120 n[4] = (unsigned char) xe;
1121 n[5] = (unsigned char) xf;
1123 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1124 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1126 if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
1127 return 0;
1129 return 1;
1132 /* --------------------------------------------------------------------------------------------- */
1133 /* Setup Passive EPSV FTP connection */
1135 static int
1136 ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
1137 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1139 char *c;
1140 int port;
1142 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "%s", "EPSV") != COMPLETE)
1143 return 0;
1145 /* (|||<port>|) */
1146 c = strchr (reply_str, '|');
1147 if (c == NULL)
1148 return 0;
1149 if (strlen (c) > 3)
1150 c += 3;
1151 else
1152 return 0;
1154 port = atoi (c);
1155 if (port < 0 || port > 65535)
1156 return 0;
1157 port = htons (port);
1159 switch (sa->ss_family)
1161 case AF_INET:
1162 ((struct sockaddr_in *) sa)->sin_port = port;
1163 break;
1164 case AF_INET6:
1165 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1166 break;
1167 default:
1168 break;
1171 return (connect (my_socket, (struct sockaddr *) sa, *salen) < 0) ? 0 : 1;
1174 /* --------------------------------------------------------------------------------------------- */
1175 /* Setup Passive ftp connection, we use it for source routed connections */
1177 static int
1178 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
1179 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1181 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1182 if (sa->ss_family == AF_INET)
1184 if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
1185 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1186 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1187 return 0;
1189 /* It's IPV6, so EPSV is our only hope */
1190 else
1192 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1193 return 0;
1196 return 1;
1199 /* --------------------------------------------------------------------------------------------- */
1200 /* Setup Active PORT or EPRT FTP connection */
1202 static int
1203 ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super,
1204 struct sockaddr_storage data_addr, socklen_t data_addrlen)
1206 unsigned short int port;
1207 char *addr;
1208 unsigned int af;
1210 switch (data_addr.ss_family)
1212 case AF_INET:
1213 af = FTP_INET;
1214 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1215 break;
1216 case AF_INET6:
1217 af = FTP_INET6;
1218 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1219 break;
1220 /* Not implemented */
1221 default:
1222 return 0;
1225 addr = g_try_malloc (NI_MAXHOST);
1226 if (addr == NULL)
1227 ERRNOR (ENOMEM, -1);
1229 if (getnameinfo
1230 ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1231 NI_NUMERICHOST) != 0)
1233 g_free (addr);
1234 ERRNOR (EIO, -1);
1237 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1238 if (af == FTP_INET)
1240 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1241 unsigned char *p = (unsigned char *) &port;
1243 if (ftpfs_command (me, super, WAIT_REPLY,
1244 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1245 p[0], p[1]) == COMPLETE)
1247 g_free (addr);
1248 return 1;
1253 * Converts network MSB first order to host byte order (LSB
1254 * first on i386). If we do it earlier, we will run into an
1255 * endianness issue, because the server actually expects to see
1256 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1258 port = ntohs (port);
1260 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1261 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
1263 g_free (addr);
1264 return 1;
1267 g_free (addr);
1268 return 0;
1271 /* --------------------------------------------------------------------------------------------- */
1272 /* Initialize a socket for FTP DATA connection */
1274 static int
1275 ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super,
1276 struct sockaddr_storage *data_addr, socklen_t * data_addrlen)
1278 int result;
1280 memset (data_addr, 0, sizeof (*data_addr));
1281 *data_addrlen = sizeof (*data_addr);
1283 if (SUP->use_passive_connection)
1284 result = getpeername (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1285 else
1286 result = getsockname (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1288 if (result == -1)
1289 return -1;
1291 switch (data_addr->ss_family)
1293 case AF_INET:
1294 ((struct sockaddr_in *) data_addr)->sin_port = 0;
1295 break;
1296 case AF_INET6:
1297 ((struct sockaddr_in6 *) data_addr)->sin6_port = 0;
1298 break;
1299 default:
1300 vfs_print_message ("%s", _("ftpfs: invalid address family"));
1301 ERRNOR (EINVAL, -1);
1304 result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP);
1306 if (result < 0)
1308 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1309 return -1;
1312 return result;
1315 /* --------------------------------------------------------------------------------------------- */
1316 /* Initialize FTP DATA connection */
1318 static int
1319 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1321 struct sockaddr_storage data_addr;
1322 socklen_t data_addrlen;
1325 * Don't factor socket initialization out of these conditionals,
1326 * because ftpfs_init_data_socket initializes it in different way
1327 * depending on use_passive_connection flag.
1330 /* Try to establish a passive connection first (if requested) */
1331 if (SUP->use_passive_connection)
1333 int data_sock;
1335 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1336 if (data_sock < 0)
1337 return -1;
1339 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1340 return data_sock;
1342 vfs_print_message ("%s", _("ftpfs: could not setup passive mode"));
1343 SUP->use_passive_connection = FALSE;
1345 close (data_sock);
1348 /* If passive setup is diabled or failed, fallback to active connections */
1349 if (!SUP->use_passive_connection)
1351 int data_sock;
1353 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1354 if (data_sock < 0)
1355 return -1;
1357 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) == 0) &&
1358 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) == 0) &&
1359 (listen (data_sock, 1) == 0) &&
1360 (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0))
1361 return data_sock;
1363 close (data_sock);
1366 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1367 SUP->use_passive_connection = SUP->proxy != NULL ? ftpfs_use_passive_connections_over_proxy :
1368 ftpfs_use_passive_connections;
1370 ftpfs_errno = EIO;
1371 return -1;
1374 /* --------------------------------------------------------------------------------------------- */
1376 static int
1377 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1378 const char *remote, int isbinary, int reget)
1380 struct sockaddr_storage from;
1381 int s, j, data;
1382 socklen_t fromlen = sizeof (from);
1384 /* FTP doesn't allow to open more than one file at a time */
1385 if (SUP->ctl_connection_busy)
1386 return -1;
1388 s = ftpfs_initconn (me, super);
1389 if (s == -1)
1390 return -1;
1392 if (ftpfs_changetype (me, super, isbinary) == -1)
1394 close (s);
1395 return -1;
1398 if (reget > 0)
1400 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1401 if (j != CONTINUE)
1403 close (s);
1404 return -1;
1408 if (remote)
1410 char *remote_path = ftpfs_translate_path (me, super, remote);
1411 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1412 /* WarFtpD can't STORE //filename */
1413 IS_PATH_SEP (*remote_path) ? remote_path + 1 : remote_path);
1414 g_free (remote_path);
1416 else
1417 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1419 if (j != PRELIM)
1421 close (s);
1422 ERRNOR (EPERM, -1);
1425 if (SUP->use_passive_connection)
1426 data = s;
1427 else
1429 tty_enable_interrupt_key ();
1430 data = accept (s, (struct sockaddr *) &from, &fromlen);
1431 if (data < 0)
1432 ftpfs_errno = errno;
1433 tty_disable_interrupt_key ();
1434 close (s);
1435 if (data < 0)
1436 return -1;
1438 SUP->ctl_connection_busy = 1;
1439 return data;
1442 /* --------------------------------------------------------------------------------------------- */
1444 static void
1445 ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t * fh)
1447 struct vfs_s_super *super = FH_SUPER;
1448 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1449 fd_set mask;
1450 int dsock = FH_SOCK;
1451 FH_SOCK = -1;
1452 SUP->ctl_connection_busy = 0;
1454 vfs_print_message ("%s", _("ftpfs: aborting transfer."));
1455 if (send (SUP->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1457 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1458 if (dsock != -1)
1459 close (dsock);
1460 return;
1463 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1465 vfs_print_message ("%s", _("ftpfs: abort failed"));
1466 if (dsock != -1)
1467 close (dsock);
1468 return;
1470 if (dsock != -1)
1472 FD_ZERO (&mask);
1473 FD_SET (dsock, &mask);
1474 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1476 struct timeval start_tim, tim;
1477 char buf[BUF_8K];
1479 gettimeofday (&start_tim, NULL);
1480 /* flush the remaining data */
1481 while (read (dsock, buf, sizeof (buf)) > 0)
1483 gettimeofday (&tim, NULL);
1484 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT)
1486 /* server keeps sending, drop the connection and ftpfs_reconnect */
1487 close (dsock);
1488 ftpfs_reconnect (me, super);
1489 return;
1493 close (dsock);
1495 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) == TRANSIENT) && (code == 426))
1496 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1499 /* --------------------------------------------------------------------------------------------- */
1501 #if 0
1502 static void
1503 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1504 struct vfs_s_inode *dir)
1506 struct linklist *flist;
1507 struct direntry *fe, *fel;
1508 char tmp[MC_MAXPATHLEN];
1509 int depth;
1511 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1512 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1514 /* flist->data->l_stat is alread initialized with 0 */
1515 fel = flist->data;
1516 if (S_ISLNK (fel->s.st_mode) && fel->linkname)
1518 if (IS_PATH_SEP (fel->linkname[0]))
1520 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1521 continue;
1522 strcpy (tmp, fel->linkname);
1524 else
1526 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1527 continue;
1528 strcpy (tmp, dir->remote_path);
1529 if (tmp[1] != '\0')
1530 strcat (tmp, PATH_SEP_STR);
1531 strcat (tmp + 1, fel->linkname);
1533 for (depth = 0; depth < 100; depth++)
1534 { /* depth protects against recursive symbolic links */
1535 canonicalize_pathname (tmp);
1536 fe = _get_file_entry_t (bucket, tmp, 0, 0);
1537 if (fe)
1539 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1541 /* Symlink points to link which isn't resolved, yet. */
1542 if (IS_PATH_SEP (fe->linkname[0]))
1544 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1545 break;
1546 strcpy (tmp, fe->linkname);
1548 else
1550 /* at this point tmp looks always like this
1551 /directory/filename, i.e. no need to check
1552 strrchr's return value */
1553 *(strrchr (tmp, PATH_SEP) + 1) = '\0'; /* dirname */
1554 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1555 break;
1556 strcat (tmp, fe->linkname);
1558 continue;
1560 else
1562 fel->l_stat = g_new (struct stat, 1);
1563 if (S_ISLNK (fe->s.st_mode))
1564 *fel->l_stat = *fe->l_stat;
1565 else
1566 *fel->l_stat = fe->s;
1567 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1570 break;
1574 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1577 /* --------------------------------------------------------------------------------------------- */
1579 static void
1580 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1581 struct vfs_s_inode *dir)
1583 char buffer[2048] = "", *filename;
1584 int sock;
1585 FILE *fp;
1586 struct stat s;
1587 struct linklist *flist;
1588 struct direntry *fe;
1589 int switch_method = 0;
1591 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1592 if (strchr (dir->remote_path, ' '))
1594 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1596 vfs_print_message ("%s", _("ftpfs: CWD failed."));
1597 return;
1599 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1601 else
1602 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1604 if (sock == -1)
1606 vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink"));
1607 return;
1610 fp = fdopen (sock, "r");
1611 if (fp == NULL)
1613 close (sock);
1614 vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink"));
1615 return;
1617 tty_enable_interrupt_key ();
1618 flist = dir->file_list->next;
1619 while (1)
1623 if (flist == dir->file_list)
1624 goto done;
1625 fe = flist->data;
1626 flist = flist->next;
1628 while (!S_ISLNK (fe->s.st_mode));
1629 while (1)
1631 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1632 goto done;
1633 if (MEDATA->logfile)
1635 fputs (buffer, MEDATA->logfile);
1636 fflush (MEDATA->logfile);
1638 vfs_die ("This code should be commented out\n");
1639 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1641 int r = strcmp (fe->name, filename);
1642 g_free (filename);
1643 if (r == 0)
1645 if (S_ISLNK (s.st_mode))
1647 /* This server doesn't understand LIST -lLa */
1648 switch_method = 1;
1649 goto done;
1651 fe->l_stat = g_new (struct stat, 1);
1652 if (fe->l_stat == NULL)
1653 goto done;
1654 *fe->l_stat = s;
1655 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1656 break;
1658 if (r < 0)
1659 break;
1663 done:
1664 while (fgets (buffer, sizeof (buffer), fp) != NULL);
1665 tty_disable_interrupt_key ();
1666 fclose (fp);
1667 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1670 /* --------------------------------------------------------------------------------------------- */
1672 static void
1673 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1675 vfs_print_message ("%s", _("Resolving symlink..."));
1677 if (SUP->strict_rfc959_list_cmd)
1678 resolve_symlink_without_ls_options (me, super, dir);
1679 else
1680 resolve_symlink_with_ls_options (me, super, dir);
1682 #endif
1684 /* --------------------------------------------------------------------------------------------- */
1686 static int
1687 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1689 struct vfs_s_entry *ent;
1690 struct vfs_s_super *super = dir->super;
1691 int sock, num_entries = 0;
1692 gboolean cd_first;
1694 cd_first = ftpfs_first_cd_then_ls || (SUP->strict == RFC_STRICT)
1695 || (strchr (remote_path, ' ') != NULL);
1697 again:
1698 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1699 remote_path,
1700 SUP->strict ==
1701 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1703 if (cd_first)
1705 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1707 ftpfs_errno = ENOENT;
1708 vfs_print_message ("%s", _("ftpfs: CWD failed."));
1709 return -1;
1713 gettimeofday (&dir->timestamp, NULL);
1714 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1716 if (SUP->strict == RFC_STRICT)
1717 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1718 else if (cd_first)
1719 /* Dirty hack to avoid autoprepending / to . */
1720 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1721 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1722 else
1724 char *path;
1726 /* Trailing "/." is necessary if remote_path is a symlink */
1727 path = mc_build_filename (remote_path, ".", (char *) NULL);
1728 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1729 g_free (path);
1732 if (sock == -1)
1733 goto fallback;
1735 /* Clear the interrupt flag */
1736 tty_enable_interrupt_key ();
1738 vfs_parse_ls_lga_init ();
1739 while (TRUE)
1741 int i;
1742 size_t count_spaces = 0;
1743 int res;
1744 char lc_buffer[BUF_8K] = "\0";
1746 res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer), sock);
1747 if (res == 0)
1748 break;
1750 if (res == EINTR)
1752 me->verrno = ECONNRESET;
1753 close (sock);
1754 SUP->ctl_connection_busy = 0;
1755 tty_disable_interrupt_key ();
1756 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1757 vfs_print_message (_("%s: failure"), me->name);
1758 return -1;
1761 if (MEDATA->logfile)
1763 fputs (lc_buffer, MEDATA->logfile);
1764 fputs ("\n", MEDATA->logfile);
1765 fflush (MEDATA->logfile);
1768 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1769 i = ent->ino->st.st_nlink;
1770 if (!vfs_parse_ls_lga
1771 (lc_buffer, &ent->ino->st, &ent->name, &ent->ino->linkname, &count_spaces))
1773 vfs_s_free_entry (me, ent);
1774 continue;
1776 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1777 num_entries++;
1778 vfs_s_store_filename_leading_spaces (ent, count_spaces);
1779 vfs_s_insert_entry (me, dir, ent);
1782 close (sock);
1783 SUP->ctl_connection_busy = 0;
1784 me->verrno = E_REMOTE;
1785 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1786 goto fallback;
1788 if (num_entries == 0 && !cd_first)
1790 /* The LIST command may produce an empty output. In such scenario
1791 it is not clear whether this is caused by 'remote_path' being
1792 a non-existent path or for some other reason (listing emtpy
1793 directory without the -a option, non-readable directory, etc.).
1795 Since 'dir_load' is a crucial method, when it comes to determine
1796 whether a given path is a _directory_, the code must try its best
1797 to determine the type of 'remote_path'. The only reliable way to
1798 achieve this is trough issuing a CWD command. */
1800 cd_first = TRUE;
1801 goto again;
1804 vfs_s_normalize_filename_leading_spaces (dir, vfs_parse_ls_lga_get_final_spaces ());
1806 if (SUP->strict == RFC_AUTODETECT)
1807 SUP->strict = RFC_DARING;
1809 vfs_print_message (_("%s: done."), me->name);
1810 return 0;
1812 fallback:
1813 if (SUP->strict == RFC_AUTODETECT)
1815 /* It's our first attempt to get a directory listing from this
1816 server (UNIX style LIST command) */
1817 SUP->strict = RFC_STRICT;
1818 /* I hate goto, but recursive call needs another 8K on stack */
1819 /* return ftpfs_dir_load (me, dir, remote_path); */
1820 cd_first = TRUE;
1821 goto again;
1823 vfs_print_message ("%s", _("ftpfs: failed; nowhere to fallback to"));
1824 ERRNOR (EACCES, -1);
1827 /* --------------------------------------------------------------------------------------------- */
1829 static int
1830 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname)
1832 int h, sock;
1833 off_t n_stored;
1834 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1835 struct linger li;
1836 #else
1837 int flag_one = 1;
1838 #endif
1839 char lc_buffer[BUF_8K];
1840 struct stat s;
1841 char *w_buf;
1842 struct vfs_s_super *super = FH_SUPER;
1843 ftp_fh_data_t *ftp = (ftp_fh_data_t *) fh->data;
1845 h = open (localname, O_RDONLY);
1846 if (h == -1)
1847 ERRNOR (EIO, -1);
1849 if (fstat (h, &s) == -1 ||
1850 ((sock =
1851 ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY,
1852 0)) < 0))
1854 close (h);
1855 return -1;
1857 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1858 li.l_onoff = 1;
1859 li.l_linger = 120;
1860 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1861 #else
1862 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1863 #endif
1864 n_stored = 0;
1866 tty_enable_interrupt_key ();
1867 while (TRUE)
1869 ssize_t n_read, n_written;
1871 while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1873 if (errno != EINTR)
1875 ftpfs_errno = errno;
1876 goto error_return;
1878 if (tty_got_interrupt ())
1880 ftpfs_errno = EINTR;
1881 goto error_return;
1884 if (n_read == 0)
1885 break;
1886 n_stored += n_read;
1887 w_buf = lc_buffer;
1888 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1890 if (n_written == -1)
1892 if (errno == EINTR && !tty_got_interrupt ())
1893 continue;
1895 ftpfs_errno = errno;
1896 goto error_return;
1898 w_buf += n_written;
1899 n_read -= n_written;
1901 vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
1902 _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
1904 tty_disable_interrupt_key ();
1905 close (sock);
1906 SUP->ctl_connection_busy = 0;
1907 close (h);
1908 if (ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE)
1909 ERRNOR (EIO, -1);
1910 return 0;
1911 error_return:
1912 tty_disable_interrupt_key ();
1913 close (sock);
1914 SUP->ctl_connection_busy = 0;
1915 close (h);
1916 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1917 return -1;
1920 /* --------------------------------------------------------------------------------------------- */
1922 static int
1923 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset)
1925 char *name;
1927 if (fh->data == NULL)
1928 fh->data = g_new0 (ftp_fh_data_t, 1);
1930 name = vfs_s_fullpath (me, fh->ino);
1931 if (name == NULL)
1932 return 0;
1933 FH_SOCK = ftpfs_open_data_connection (me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1934 g_free (name);
1935 if (FH_SOCK == -1)
1936 ERRNOR (EACCES, 0);
1937 fh->linear = LS_LINEAR_OPEN;
1938 ((ftp_fh_data_t *) fh->data)->append = 0;
1939 return 1;
1942 /* --------------------------------------------------------------------------------------------- */
1944 static ssize_t
1945 ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t * fh, void *buf, size_t len)
1947 ssize_t n;
1948 struct vfs_s_super *super = FH_SUPER;
1950 while ((n = read (FH_SOCK, buf, len)) < 0)
1952 if ((errno == EINTR) && !tty_got_interrupt ())
1953 continue;
1954 break;
1957 if (n < 0)
1958 ftpfs_linear_abort (me, fh);
1960 if (n == 0)
1962 SUP->ctl_connection_busy = 0;
1963 close (FH_SOCK);
1964 FH_SOCK = -1;
1965 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1966 ERRNOR (E_REMOTE, -1);
1967 return 0;
1969 ERRNOR (errno, n);
1972 /* --------------------------------------------------------------------------------------------- */
1974 static void
1975 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t * fh)
1977 if (FH_SOCK != -1)
1978 ftpfs_linear_abort (me, fh);
1981 /* --------------------------------------------------------------------------------------------- */
1983 static int
1984 ftpfs_ctl (void *fh, int ctlop, void *arg)
1986 (void) arg;
1988 switch (ctlop)
1990 case VFS_CTL_IS_NOTREADY:
1992 int v;
1994 if (FH->linear == LS_NOT_LINEAR)
1995 vfs_die ("You may not do this");
1996 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1997 return 0;
1999 v = vfs_s_select_on_two (((ftp_fh_data_t *) (FH->data))->sock, 0);
2000 return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
2002 default:
2003 return 0;
2007 /* --------------------------------------------------------------------------------------------- */
2009 static int
2010 ftpfs_send_command (const vfs_path_t * vpath, const char *cmd, int flags)
2012 const char *rpath;
2013 char *p;
2014 struct vfs_s_super *super;
2015 int r;
2016 const vfs_path_element_t *path_element;
2017 int flush_directory_cache = (flags & OPT_FLUSH);
2019 path_element = vfs_path_get_by_index (vpath, -1);
2021 rpath = vfs_s_get_path (vpath, &super, 0);
2022 if (rpath == NULL)
2023 return -1;
2025 p = ftpfs_translate_path (path_element->class, super, rpath);
2026 r = ftpfs_command (path_element->class, super, WAIT_REPLY, cmd, p);
2027 g_free (p);
2028 vfs_stamp_create (vfs_ftpfs_ops, super);
2029 if (flags & OPT_IGNORE_ERROR)
2030 r = COMPLETE;
2031 if (r != COMPLETE)
2033 path_element->class->verrno = EPERM;
2034 return -1;
2036 if (flush_directory_cache)
2037 vfs_s_invalidate (path_element->class, super);
2038 return 0;
2041 /* --------------------------------------------------------------------------------------------- */
2043 static int
2044 ftpfs_stat (const vfs_path_t * vpath, struct stat *buf)
2046 int ret;
2048 ret = vfs_s_stat (vpath, buf);
2049 ftpfs_set_blksize (buf);
2050 return ret;
2053 /* --------------------------------------------------------------------------------------------- */
2055 static int
2056 ftpfs_lstat (const vfs_path_t * vpath, struct stat *buf)
2058 int ret;
2060 ret = vfs_s_lstat (vpath, buf);
2061 ftpfs_set_blksize (buf);
2062 return ret;
2065 /* --------------------------------------------------------------------------------------------- */
2067 static int
2068 ftpfs_fstat (void *vfs_info, struct stat *buf)
2070 int ret;
2072 ret = vfs_s_fstat (vfs_info, buf);
2073 ftpfs_set_blksize (buf);
2074 return ret;
2077 /* --------------------------------------------------------------------------------------------- */
2079 static int
2080 ftpfs_chmod (const vfs_path_t * vpath, mode_t mode)
2082 char buf[BUF_SMALL];
2083 int ret;
2085 g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", (unsigned int) (mode & 07777));
2087 ret = ftpfs_send_command (vpath, buf, OPT_FLUSH);
2089 return ftpfs_ignore_chattr_errors ? 0 : ret;
2092 /* --------------------------------------------------------------------------------------------- */
2094 static int
2095 ftpfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
2097 #if 0
2098 (void) vpath;
2099 (void) owner;
2100 (void) group;
2102 ftpfs_errno = EPERM;
2103 return -1;
2104 #else
2105 /* Everyone knows it is not possible to chown remotely, so why bother them.
2106 If someone's root, then copy/move will always try to chown it... */
2107 (void) vpath;
2108 (void) owner;
2109 (void) group;
2110 return 0;
2111 #endif
2114 /* --------------------------------------------------------------------------------------------- */
2116 static int
2117 ftpfs_unlink (const vfs_path_t * vpath)
2119 return ftpfs_send_command (vpath, "DELE /%s", OPT_FLUSH);
2122 /* --------------------------------------------------------------------------------------------- */
2124 /* Return 1 if path is the same directory as the one we are in now */
2125 static int
2126 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
2128 (void) me;
2130 if (SUP->current_dir == NULL)
2131 return FALSE;
2132 return (strcmp (path, SUP->current_dir) == 0);
2135 /* --------------------------------------------------------------------------------------------- */
2137 static int
2138 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
2140 int r;
2141 char *p;
2143 if (!SUP->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
2144 return COMPLETE;
2146 p = ftpfs_translate_path (me, super, remote_path);
2147 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
2148 g_free (p);
2150 if (r != COMPLETE)
2151 ftpfs_errno = EIO;
2152 else
2154 g_free (SUP->current_dir);
2155 SUP->current_dir = g_strdup (remote_path);
2156 SUP->cwd_deferred = 0;
2158 return r;
2161 /* --------------------------------------------------------------------------------------------- */
2163 static int
2164 ftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
2166 ftpfs_send_command (vpath1, "RNFR /%s", OPT_FLUSH);
2167 return ftpfs_send_command (vpath2, "RNTO /%s", OPT_FLUSH);
2170 /* --------------------------------------------------------------------------------------------- */
2172 static int
2173 ftpfs_mkdir (const vfs_path_t * vpath, mode_t mode)
2175 (void) mode; /* FIXME: should be used */
2177 return ftpfs_send_command (vpath, "MKD /%s", OPT_FLUSH);
2180 /* --------------------------------------------------------------------------------------------- */
2182 static int
2183 ftpfs_rmdir (const vfs_path_t * vpath)
2185 return ftpfs_send_command (vpath, "RMD /%s", OPT_FLUSH);
2188 /* --------------------------------------------------------------------------------------------- */
2190 static void
2191 ftpfs_fh_free_data (vfs_file_handler_t * fh)
2193 if (fh != NULL)
2194 MC_PTR_FREE (fh->data);
2197 /* --------------------------------------------------------------------------------------------- */
2199 static int
2200 ftpfs_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
2202 ftp_fh_data_t *ftp;
2204 (void) mode;
2206 fh->data = g_new0 (ftp_fh_data_t, 1);
2207 ftp = (ftp_fh_data_t *) fh->data;
2208 /* File will be written only, so no need to retrieve it from ftp server */
2209 if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
2211 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2212 struct linger li;
2213 #else
2214 int li = 1;
2215 #endif
2216 char *name;
2218 /* ftpfs_linear_start() called, so data will be written
2219 * to local temporary file and stored to ftp server
2220 * by vfs_s_close later
2222 if (((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy)
2224 if (!fh->ino->localname)
2226 vfs_path_t *vpath;
2227 int handle;
2229 handle = vfs_mkstemps (&vpath, me->name, fh->ino->ent->name);
2230 if (handle == -1)
2232 vfs_path_free (vpath);
2233 goto fail;
2235 close (handle);
2236 fh->ino->localname = g_strdup (vfs_path_as_str (vpath));
2237 vfs_path_free (vpath);
2238 ftp->append = flags & O_APPEND;
2240 return 0;
2242 name = vfs_s_fullpath (me, fh->ino);
2243 if (name == NULL)
2244 goto fail;
2245 fh->handle =
2246 ftpfs_open_data_connection (me, fh->ino->super,
2247 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
2248 g_free (name);
2250 if (fh->handle < 0)
2251 goto fail;
2252 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2253 li.l_onoff = 1;
2254 li.l_linger = 120;
2255 #endif
2256 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
2258 if (fh->ino->localname)
2260 unlink (fh->ino->localname);
2261 MC_PTR_FREE (fh->ino->localname);
2263 return 0;
2266 if (!fh->ino->localname && vfs_s_retrieve_file (me, fh->ino) == -1)
2267 goto fail;
2268 if (!fh->ino->localname)
2269 vfs_die ("retrieve_file failed to fill in localname");
2270 return 0;
2272 fail:
2273 ftpfs_fh_free_data (fh);
2274 return -1;
2277 /* --------------------------------------------------------------------------------------------- */
2279 static int
2280 ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t * fh)
2282 if (fh->handle != -1 && !fh->ino->localname)
2284 ftp_super_data_t *ftp = (ftp_super_data_t *) fh->ino->super->data;
2286 close (fh->handle);
2287 fh->handle = -1;
2288 ftp->ctl_connection_busy = 0;
2289 /* File is stored to destination already, so
2290 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2292 fh->changed = 0;
2293 if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE)
2294 ERRNOR (EIO, -1);
2295 vfs_s_invalidate (me, FH_SUPER);
2298 return 0;
2301 /* --------------------------------------------------------------------------------------------- */
2303 static void
2304 ftpfs_done (struct vfs_class *me)
2306 (void) me;
2308 g_slist_free_full (no_proxy, g_free);
2310 g_free (ftpfs_anonymous_passwd);
2311 g_free (ftpfs_proxy_host);
2314 /* --------------------------------------------------------------------------------------------- */
2316 static void
2317 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
2319 GList *iter;
2321 for (iter = MEDATA->supers; iter != NULL; iter = g_list_next (iter))
2323 const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
2324 char *name;
2326 name = vfs_path_element_build_pretty_path_str (super->path_element);
2328 func (name);
2329 g_free (name);
2333 /* --------------------------------------------------------------------------------------------- */
2335 static keyword_t
2336 ftpfs_netrc_next (void)
2338 char *p;
2339 keyword_t i;
2340 static const char *const keywords[] = { "default", "machine",
2341 "login", "password", "passwd", "account", "macdef", NULL
2344 while (1)
2346 netrcp = skip_separators (netrcp);
2347 if (*netrcp != '\n')
2348 break;
2349 netrcp++;
2351 if (!*netrcp)
2352 return NETRC_NONE;
2353 p = buffer;
2354 if (*netrcp == '"')
2356 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++)
2358 if (*netrcp == '\\')
2359 netrcp++;
2360 *p++ = *netrcp;
2363 else
2365 for (; *netrcp != '\0' && !whiteness (*netrcp) && *netrcp != ','; netrcp++)
2367 if (*netrcp == '\\')
2368 netrcp++;
2369 *p++ = *netrcp;
2372 *p = 0;
2373 if (!*buffer)
2374 return NETRC_NONE;
2376 for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2377 if (strcmp (keywords[i - 1], buffer) == 0)
2378 return i;
2380 return NETRC_UNKNOWN;
2383 /* --------------------------------------------------------------------------------------------- */
2385 static int
2386 ftpfs_netrc_bad_mode (const char *netrcname)
2388 struct stat mystat;
2390 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077))
2392 static int be_angry = 1;
2394 if (be_angry)
2396 message (D_ERROR, MSG_ERROR,
2397 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2398 be_angry = 0;
2400 return 1;
2402 return 0;
2405 /* --------------------------------------------------------------------------------------------- */
2406 /* Scan .netrc until we find matching "machine" or "default"
2407 * domain is used for additional matching
2408 * No search is done after "default" in compliance with "man netrc"
2409 * Return 0 if found, -1 otherwise */
2411 static int
2412 ftpfs_find_machine (const char *host, const char *domain)
2414 keyword_t keyword;
2416 if (!host)
2417 host = "";
2418 if (!domain)
2419 domain = "";
2421 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2423 if (keyword == NETRC_DEFAULT)
2424 return 0;
2426 if (keyword == NETRC_MACDEF)
2428 /* Scan for an empty line, which concludes "macdef" */
2431 while (*netrcp && *netrcp != '\n')
2432 netrcp++;
2433 if (*netrcp != '\n')
2434 break;
2435 netrcp++;
2437 while (*netrcp && *netrcp != '\n');
2438 continue;
2441 if (keyword != NETRC_MACHINE)
2442 continue;
2444 /* Take machine name */
2445 if (ftpfs_netrc_next () == NETRC_NONE)
2446 break;
2448 if (g_ascii_strcasecmp (host, buffer) != 0)
2450 /* Try adding our domain to short names in .netrc */
2451 const char *host_domain = strchr (host, '.');
2452 if (!host_domain)
2453 continue;
2455 /* Compare domain part */
2456 if (g_ascii_strcasecmp (host_domain, domain) != 0)
2457 continue;
2459 /* Compare local part */
2460 if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2461 continue;
2464 return 0;
2467 /* end of .netrc */
2468 return -1;
2471 /* --------------------------------------------------------------------------------------------- */
2472 /* Extract login and password from .netrc for the host.
2473 * pass may be NULL.
2474 * Returns 0 for success, -1 for error */
2476 static int
2477 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2479 char *netrcname;
2480 char *tmp_pass = NULL;
2481 char hostname[MAXHOSTNAMELEN];
2482 const char *domain;
2483 static struct rupcache
2485 struct rupcache *next;
2486 char *host;
2487 char *login;
2488 char *pass;
2489 } *rup_cache = NULL, *rupp;
2491 /* Initialize *login and *pass */
2492 MC_PTR_FREE (*login);
2493 MC_PTR_FREE (*pass);
2495 /* Look up in the cache first */
2496 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2498 if (strcmp (host, rupp->host) == 0)
2500 *login = g_strdup (rupp->login);
2501 *pass = g_strdup (rupp->pass);
2502 return 0;
2506 /* Load current .netrc */
2507 netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2508 if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2510 g_free (netrcname);
2511 return 0;
2514 netrcp = netrc;
2516 /* Find our own domain name */
2517 if (gethostname (hostname, sizeof (hostname)) < 0)
2518 *hostname = '\0';
2520 domain = strchr (hostname, '.');
2521 if (domain == NULL)
2522 domain = "";
2524 /* Scan for "default" and matching "machine" keywords */
2525 ftpfs_find_machine (host, domain);
2527 /* Scan for keywords following "default" and "machine" */
2528 while (1)
2530 keyword_t keyword;
2532 int need_break = 0;
2533 keyword = ftpfs_netrc_next ();
2535 switch (keyword)
2537 case NETRC_LOGIN:
2538 if (ftpfs_netrc_next () == NETRC_NONE)
2540 need_break = 1;
2541 break;
2544 /* We have another name already - should not happen */
2545 if (*login)
2547 need_break = 1;
2548 break;
2551 /* We have login name now */
2552 *login = g_strdup (buffer);
2553 break;
2555 case NETRC_PASSWORD:
2556 case NETRC_PASSWD:
2557 if (ftpfs_netrc_next () == NETRC_NONE)
2559 need_break = 1;
2560 break;
2563 /* Ignore unsafe passwords */
2564 if (*login != NULL &&
2565 strcmp (*login, "anonymous") != 0 && strcmp (*login, "ftp") != 0
2566 && ftpfs_netrc_bad_mode (netrcname))
2568 need_break = 1;
2569 break;
2572 /* Remember password. pass may be NULL, so use tmp_pass */
2573 if (tmp_pass == NULL)
2574 tmp_pass = g_strdup (buffer);
2575 break;
2577 case NETRC_ACCOUNT:
2578 /* "account" is followed by a token which we ignore */
2579 if (ftpfs_netrc_next () == NETRC_NONE)
2581 need_break = 1;
2582 break;
2585 /* Ignore account, but warn user anyways */
2586 ftpfs_netrc_bad_mode (netrcname);
2587 break;
2589 default:
2590 /* Unexpected keyword or end of file */
2591 need_break = 1;
2592 break;
2595 if (need_break)
2596 break;
2599 g_free (netrc);
2600 g_free (netrcname);
2602 rupp = g_new (struct rupcache, 1);
2603 rupp->host = g_strdup (host);
2604 rupp->login = g_strdup (*login);
2605 rupp->pass = g_strdup (tmp_pass);
2607 rupp->next = rup_cache;
2608 rup_cache = rupp;
2610 *pass = tmp_pass;
2612 return 0;
2615 /* --------------------------------------------------------------------------------------------- */
2616 /*** public functions ****************************************************************************/
2617 /* --------------------------------------------------------------------------------------------- */
2619 /** This routine is called as the last step in load_setup */
2620 void
2621 ftpfs_init_passwd (void)
2623 ftpfs_anonymous_passwd = load_anon_passwd ();
2624 if (ftpfs_anonymous_passwd)
2625 return;
2627 /* If there is no anonymous ftp password specified
2628 * then we'll just use anonymous@
2629 * We don't send any other thing because:
2630 * - We want to remain anonymous
2631 * - We want to stop SPAM
2632 * - We don't want to let ftp sites to discriminate by the user,
2633 * host or country.
2635 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
2638 /* --------------------------------------------------------------------------------------------- */
2640 void
2641 init_ftpfs (void)
2643 tcp_init ();
2645 ftpfs_subclass.flags = VFS_S_REMOTE | VFS_S_USETMP;
2646 ftpfs_subclass.archive_same = ftpfs_archive_same;
2647 ftpfs_subclass.open_archive = ftpfs_open_archive;
2648 ftpfs_subclass.free_archive = ftpfs_free_archive;
2649 ftpfs_subclass.fh_open = ftpfs_fh_open;
2650 ftpfs_subclass.fh_close = ftpfs_fh_close;
2651 ftpfs_subclass.fh_free_data = ftpfs_fh_free_data;
2652 ftpfs_subclass.dir_load = ftpfs_dir_load;
2653 ftpfs_subclass.file_store = ftpfs_file_store;
2654 ftpfs_subclass.linear_start = ftpfs_linear_start;
2655 ftpfs_subclass.linear_read = ftpfs_linear_read;
2656 ftpfs_subclass.linear_close = ftpfs_linear_close;
2658 vfs_s_init_class (&ftpfs_subclass);
2659 vfs_ftpfs_ops->name = "ftpfs";
2660 vfs_ftpfs_ops->flags = VFSF_NOLINKS;
2661 vfs_ftpfs_ops->prefix = "ftp";
2662 vfs_ftpfs_ops->done = &ftpfs_done;
2663 vfs_ftpfs_ops->fill_names = ftpfs_fill_names;
2664 vfs_ftpfs_ops->stat = ftpfs_stat;
2665 vfs_ftpfs_ops->lstat = ftpfs_lstat;
2666 vfs_ftpfs_ops->fstat = ftpfs_fstat;
2667 vfs_ftpfs_ops->chmod = ftpfs_chmod;
2668 vfs_ftpfs_ops->chown = ftpfs_chown;
2669 vfs_ftpfs_ops->unlink = ftpfs_unlink;
2670 vfs_ftpfs_ops->rename = ftpfs_rename;
2671 vfs_ftpfs_ops->mkdir = ftpfs_mkdir;
2672 vfs_ftpfs_ops->rmdir = ftpfs_rmdir;
2673 vfs_ftpfs_ops->ctl = ftpfs_ctl;
2674 vfs_register_class (vfs_ftpfs_ops);
2677 /* --------------------------------------------------------------------------------------------- */