Ticket #2623: vfs: use data after free.
[midnight-commander.git] / src / vfs / ftpfs / ftpfs.c
blob92d73ed570ce77d115a924a2f98e1d619e74bba3
1 /*
2 Virtual File System: FTP file system.
4 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2006, 2007, 2008, 2009, 2010, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Ching Hui, 1995
10 Jakub Jelinek, 1995
11 Miguel de Icaza, 1995, 1996, 1997
12 Norbert Warmuth, 1997
13 Pavel Machek, 1998
14 Yury V. Zaytsev, 2010
15 Slava Zanko <slavazanko@gmail.com>, 2010
16 Andrew Borodin <aborodin@vmail.ru>, 2010
18 This file is part of the Midnight Commander.
20 The Midnight Commander is free software: you can redistribute it
21 and/or modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation, either version 3 of the License,
23 or (at your option) any later version.
25 The Midnight Commander is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <http://www.gnu.org/licenses/>.
34 /**
35 * \file
36 * \brief Source: Virtual File System: FTP file system
37 * \author Ching Hui
38 * \author Jakub Jelinek
39 * \author Miguel de Icaza
40 * \author Norbert Warmuth
41 * \author Pavel Machek
42 * \date 1995, 1997, 1998
44 * \todo
45 - make it more robust - all the connects etc. should handle EADDRINUSE and
46 ERETRY (have I spelled these names correctly?)
47 - make the user able to flush a connection - all the caches will get empty
48 etc., (tarfs as well), we should give there a user selectable timeout
49 and assign a key sequence.
50 - use hash table instead of linklist to cache ftpfs directory.
52 What to do with this?
55 * NOTE: Usage of tildes is deprecated, consider:
56 * \verbatim
57 cd /#ftp:pavel@hobit
58 cd ~
59 \endverbatim
60 * And now: what do I want to do? Do I want to go to /home/pavel or to
61 * /#ftp:hobit/home/pavel? I think first has better sense...
63 \verbatim
65 int f = !strcmp( remote_path, "/~" );
66 if (f || !strncmp( remote_path, "/~/", 3 )) {
67 char *s;
68 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
69 g_free (remote_path);
70 remote_path = s;
73 \endverbatim
76 /* \todo Fix: Namespace pollution: horrible */
78 #include <config.h>
79 #include <stdio.h> /* sscanf() */
80 #include <stdlib.h> /* atoi() */
81 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
82 #include <netdb.h> /* struct hostent */
83 #include <sys/socket.h> /* AF_INET */
84 #include <netinet/in.h> /* struct in_addr */
85 #ifdef HAVE_ARPA_INET_H
86 #include <arpa/inet.h>
87 #endif
88 #include <arpa/ftp.h>
89 #include <arpa/telnet.h>
90 #include <sys/param.h>
91 #include <errno.h>
92 #include <ctype.h>
93 #include <fcntl.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/mcconfig.h"
101 #include "lib/tty/tty.h" /* enable/disable interrupt key */
102 #include "lib/widget.h" /* message() */
104 #include "src/history.h"
105 #include "src/setup.h" /* for load_anon_passwd */
107 #include "lib/vfs/vfs.h"
108 #include "lib/vfs/utilvfs.h"
109 #include "lib/vfs/netutil.h"
110 #include "lib/vfs/xdirentry.h"
111 #include "lib/vfs/gc.h" /* vfs_stamp_create */
113 #include "ftpfs.h"
115 /*** global variables ****************************************************************************/
117 /* Delay to retry a connection */
118 int ftpfs_retry_seconds = 30;
120 /* Method to use to connect to ftp sites */
121 int ftpfs_use_passive_connections = 1;
122 int ftpfs_use_passive_connections_over_proxy = 0;
124 /* Method used to get directory listings:
125 * 1: try 'LIST -la <path>', if it fails
126 * fall back to CWD <path>; LIST
127 * 0: always use CWD <path>; LIST
129 int ftpfs_use_unix_list_options = 1;
131 /* First "CWD <path>", then "LIST -la ." */
132 int ftpfs_first_cd_then_ls = 1;
134 /* Use the ~/.netrc */
135 int ftpfs_use_netrc = 1;
137 /* Anonymous setup */
138 char *ftpfs_anonymous_passwd = NULL;
139 int ftpfs_directory_timeout = 900;
141 /* Proxy host */
142 char *ftpfs_proxy_host = NULL;
144 /* wether we have to use proxy by default? */
145 int ftpfs_always_use_proxy = 0;
147 int ftpfs_ignore_chattr_errors = 1;
149 /*** file scope macro definitions ****************************************************************/
151 #ifndef MAXHOSTNAMELEN
152 #define MAXHOSTNAMELEN 64
153 #endif
155 #define UPLOAD_ZERO_LENGTH_FILE
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 int 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 } ftp_super_data_t;
222 typedef struct
224 int sock;
225 int append;
226 } ftp_fh_data_t;
228 /*** file scope variables ************************************************************************/
230 static int ftpfs_errno;
231 static int code;
233 #ifdef FIXME_LATER_ALIGATOR
234 static struct linklist *connections_list;
235 #endif
237 static char reply_str[80];
239 static struct vfs_class vfs_ftpfs_ops;
241 static GSList *no_proxy;
243 static char buffer[BUF_MEDIUM];
244 static char *netrc;
245 static const char *netrcp;
247 /*** file scope functions ************************************************************************/
248 /* --------------------------------------------------------------------------------------------- */
250 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
251 Translate a Unix path, i.e. MC's internal path representation (e.g.
252 /somedir/somefile) to a path valid for the remote server. Every path
253 transfered to the remote server has to be mangled by this function
254 right prior to sending it.
255 Currently only Amiga ftp servers are handled in a special manner.
257 When the remote server is an amiga:
258 a) strip leading slash if necesarry
259 b) replace first occurance of ":/" with ":"
260 c) strip trailing "/."
263 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
264 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super,
265 const char *remote_path);
266 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply,
267 const char *fmt, ...) __attribute__ ((format (__printf__, 4, 5)));
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 char *
276 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
278 if (!SUP->remote_is_amiga)
279 return g_strdup (remote_path);
280 else
282 char *ret, *p;
284 if (MEDATA->logfile)
286 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
287 fflush (MEDATA->logfile);
290 /* strip leading slash(es) */
291 while (*remote_path == '/')
292 remote_path++;
295 * Don't change "/" into "", e.g. "CWD " would be
296 * invalid.
298 if (*remote_path == '\0')
299 return g_strdup (".");
301 ret = g_strdup (remote_path);
303 /* replace first occurance of ":/" with ":" */
304 p = strchr (ret, ':');
305 if ((p != NULL) && (*(p + 1) == '/'))
306 memmove (p + 1, p + 2, strlen (p + 2) + 1);
308 /* strip trailing "/." */
309 p = strrchr (ret, '/');
310 if ((p != NULL) && (*(p + 1) == '.') && (*(p + 2) == '\0'))
311 *p = '\0';
313 return ret;
317 /* --------------------------------------------------------------------------------------------- */
318 /** Extract the hostname and username from the path */
320 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
321 * ftp://sunsite.unc.edu/pub/linux
322 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
323 * ftp://tsx-11.mit.edu:8192/
324 * ftp://joe@foo.edu:11321/private
325 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
326 * is supplied.
329 static vfs_path_element_t *
330 ftpfs_correct_url_parameters (const vfs_path_element_t * velement)
332 vfs_path_element_t *path_element = vfs_path_element_clone (velement);
334 if (path_element->port == 0)
335 path_element->port = FTP_COMMAND_PORT;
337 if (path_element->user == NULL)
339 /* Look up user and password in netrc */
340 if (ftpfs_use_netrc)
341 ftpfs_netrc_lookup (path_element->host, &path_element->user, &path_element->password);
343 if (path_element->user == NULL)
344 path_element->user = g_strdup ("anonymous");
346 /* Look up password in netrc for known user */
347 if (ftpfs_use_netrc && path_element->user != NULL && path_element->password != NULL)
349 char *new_user = NULL;
350 char *new_passwd = NULL;
352 ftpfs_netrc_lookup (path_element->host, &new_user, &new_passwd);
354 /* If user is different, remove password */
355 if (new_user != NULL && strcmp (path_element->user, new_user) != 0)
357 g_free (path_element->password);
358 path_element->password = NULL;
361 g_free (new_user);
362 g_free (new_passwd);
365 return path_element;
368 /* --------------------------------------------------------------------------------------------- */
369 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
371 static int
372 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
374 char answer[BUF_1K];
375 int i;
377 for (;;)
379 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
381 if (string_buf)
382 *string_buf = 0;
383 code = 421;
384 return 4;
386 switch (sscanf (answer, "%d", &code))
388 case 0:
389 if (string_buf)
390 g_strlcpy (string_buf, answer, string_len);
391 code = 500;
392 return 5;
393 case 1:
394 if (answer[3] == '-')
396 while (1)
398 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
400 if (string_buf)
401 *string_buf = 0;
402 code = 421;
403 return 4;
405 if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' '))
406 break;
409 if (string_buf)
410 g_strlcpy (string_buf, answer, string_len);
411 return code / 100;
416 /* --------------------------------------------------------------------------------------------- */
418 static int
419 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
421 int sock;
423 sock = ftpfs_open_socket (me, super);
424 if (sock != -1)
426 char *cwdir = super->path_element->path;
428 close (SUP->sock);
429 SUP->sock = sock;
430 super->path_element->path = NULL;
433 if (ftpfs_login_server (me, super, super->path_element->password) != 0)
435 if (cwdir == NULL)
436 return 1;
437 sock = ftpfs_chdir_internal (me, super, cwdir);
438 g_free (cwdir);
439 return sock == COMPLETE ? 1 : 0;
442 super->path_element->path = cwdir;
445 return 0;
448 /* --------------------------------------------------------------------------------------------- */
450 static int
451 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt,
452 ...)
454 va_list ap;
455 char *cmdstr;
456 int status, cmdlen;
457 static int retry = 0;
458 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
460 va_start (ap, fmt);
461 cmdstr = g_strdup_vprintf (fmt, ap);
462 va_end (ap);
464 cmdlen = strlen (cmdstr);
465 cmdstr = g_realloc (cmdstr, cmdlen + 3);
466 strcpy (cmdstr + cmdlen, "\r\n");
467 cmdlen += 2;
469 if (MEDATA->logfile)
471 if (strncmp (cmdstr, "PASS ", 5) == 0)
473 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
475 else
477 size_t ret;
478 ret = fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
481 fflush (MEDATA->logfile);
484 got_sigpipe = 0;
485 tty_enable_interrupt_key ();
486 status = write (SUP->sock, cmdstr, cmdlen);
488 if (status < 0)
490 code = 421;
492 if (errno == EPIPE)
493 { /* Remote server has closed connection */
494 if (level == 0)
496 level = 1;
497 status = ftpfs_reconnect (me, super);
498 level = 0;
499 if (status && (write (SUP->sock, cmdstr, cmdlen) > 0))
501 goto ok;
505 got_sigpipe = 1;
507 g_free (cmdstr);
508 tty_disable_interrupt_key ();
509 return TRANSIENT;
511 retry = 0;
513 tty_disable_interrupt_key ();
515 if (wait_reply)
517 status = ftpfs_get_reply (me, SUP->sock,
518 (wait_reply & WANT_STRING) ? reply_str : NULL,
519 sizeof (reply_str) - 1);
520 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
522 retry = 1;
523 level = 1;
524 status = ftpfs_reconnect (me, super);
525 level = 0;
526 if (status && (write (SUP->sock, cmdstr, cmdlen) > 0))
528 goto ok;
531 retry = 0;
532 g_free (cmdstr);
533 return status;
535 g_free (cmdstr);
536 return COMPLETE;
539 /* --------------------------------------------------------------------------------------------- */
541 static void
542 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
544 if (SUP->sock != -1)
546 vfs_print_message (_("ftpfs: Disconnecting from %s"), super->path_element->host);
547 ftpfs_command (me, super, NONE, "QUIT");
548 close (SUP->sock);
550 g_free (super->data);
551 super->data = NULL;
554 /* --------------------------------------------------------------------------------------------- */
556 static int
557 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
559 if (binary != SUP->isbinary)
561 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
562 ERRNOR (EIO, -1);
563 SUP->isbinary = binary;
565 return binary;
568 /* --------------------------------------------------------------------------------------------- */
569 /* This routine logs the user in */
571 static int
572 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
574 char *pass;
575 char *op;
576 char *name; /* login user name */
577 int anon = 0;
578 char reply_string[BUF_MEDIUM];
580 SUP->isbinary = TYPE_UNKNOWN;
582 if (super->path_element->password != NULL) /* explicit password */
583 op = g_strdup (super->path_element->password);
584 else if (netrcpass != NULL) /* password from netrc */
585 op = g_strdup (netrcpass);
586 else if (strcmp (super->path_element->user, "anonymous") == 0
587 || strcmp (super->path_element->user, "ftp") == 0)
589 if (ftpfs_anonymous_passwd == NULL) /* default anonymous password */
590 ftpfs_init_passwd ();
591 op = g_strdup (ftpfs_anonymous_passwd);
592 anon = 1;
594 else
595 { /* ask user */
596 char *p;
598 p = g_strdup_printf (_("FTP: Password required for %s"), super->path_element->user);
599 op = vfs_get_password (p);
600 g_free (p);
601 if (op == NULL)
602 ERRNOR (EPERM, 0);
603 super->path_element->password = g_strdup (op);
606 if (!anon || MEDATA->logfile)
607 pass = op;
608 else
610 pass = g_strconcat ("-", op, (char *) NULL);
611 wipe_password (op);
614 /* Proxy server accepts: username@host-we-want-to-connect */
615 if (SUP->proxy)
616 name =
617 g_strconcat (super->path_element->user, "@",
618 super->path_element->host[0] ==
619 '!' ? super->path_element->host + 1 : super->path_element->host,
620 (char *) NULL);
621 else
622 name = g_strdup (super->path_element->user);
624 if (ftpfs_get_reply (me, SUP->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
626 char *reply_up;
628 reply_up = g_ascii_strup (reply_string, -1);
629 SUP->remote_is_amiga = strstr (reply_up, "AMIGA") != 0;
630 g_free (reply_up);
632 if (MEDATA->logfile)
634 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n", SUP->remote_is_amiga);
635 fflush (MEDATA->logfile);
638 vfs_print_message (_("ftpfs: sending login name"));
640 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
642 case CONTINUE:
643 vfs_print_message (_("ftpfs: sending user password"));
644 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
645 if (code == CONTINUE)
647 char *p;
649 p = g_strdup_printf (_("FTP: Account required for user %s"),
650 super->path_element->user);
651 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
652 g_free (p);
653 if (op == NULL)
654 ERRNOR (EPERM, 0);
655 vfs_print_message (_("ftpfs: sending user account"));
656 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
657 g_free (op);
659 if (code != COMPLETE)
660 break;
661 /* fall through */
663 case COMPLETE:
664 vfs_print_message (_("ftpfs: logged in"));
665 wipe_password (pass);
666 g_free (name);
667 return 1;
669 default:
670 SUP->failed_on_login = 1;
671 wipe_password (super->path_element->password);
672 super->path_element->password = NULL;
674 goto login_fail;
678 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
679 super->path_element->user);
681 login_fail:
682 wipe_password (pass);
683 g_free (name);
684 ERRNOR (EPERM, 0);
687 /* --------------------------------------------------------------------------------------------- */
689 static void
690 ftpfs_load_no_proxy_list (void)
692 /* FixMe: shouldn't be hardcoded!!! */
693 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
694 FILE *npf;
695 int c;
696 char *p;
697 static char *mc_file = NULL;
699 mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL);
700 if (exist_file (mc_file))
702 npf = fopen (mc_file, "r");
703 if (npf != NULL)
705 while (fgets (s, sizeof (s), npf) != NULL)
707 p = strchr (s, '\n');
708 if (p == NULL) /* skip bogus entries */
710 while ((c = fgetc (npf)) != EOF && c != '\n')
712 continue;
715 if (p != s)
717 *p = '\0';
718 no_proxy = g_slist_prepend (no_proxy, g_strdup (s));
721 fclose (npf);
724 g_free (mc_file);
727 /* --------------------------------------------------------------------------------------------- */
728 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
730 static int
731 ftpfs_check_proxy (const char *host)
733 GSList *npe;
735 if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0')
736 return 0; /* sanity check */
738 if (*host == '!')
739 return 1;
741 if (!ftpfs_always_use_proxy)
742 return 0;
744 if (strchr (host, '.') == NULL)
745 return 0;
747 ftpfs_load_no_proxy_list ();
748 for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe))
750 const char *domain = (const char *) npe->data;
752 if (domain[0] == '.')
754 size_t ld = strlen (domain);
755 size_t lh = strlen (host);
757 while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1])
759 ld--;
760 lh--;
763 if (ld == 0)
764 return 0;
766 else if (g_ascii_strcasecmp (host, domain) == 0)
767 return 0;
770 return 1;
773 /* --------------------------------------------------------------------------------------------- */
775 static void
776 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
778 vfs_path_element_t *path_element;
780 path_element = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
781 *host = g_strdup (path_element->host);
782 *port = path_element->port;
783 vfs_path_element_free (path_element);
786 /* --------------------------------------------------------------------------------------------- */
788 static int
789 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
791 struct addrinfo hints, *res, *curr_res;
792 int my_socket = 0;
793 char *host = NULL;
794 char port[8];
795 int tmp_port;
796 int e;
798 (void) me;
800 /* Use a proxy host? */
801 host = g_strdup (super->path_element->host);
803 if (host == NULL || *host == '\0')
805 vfs_print_message (_("ftpfs: Invalid host name."));
806 ftpfs_errno = EINVAL;
807 g_free (host);
808 return -1;
811 /* Hosts to connect to that start with a ! should use proxy */
812 tmp_port = super->path_element->port;
814 if (SUP->proxy != NULL)
815 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
817 g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_port);
818 if (port[0] == '\0')
820 g_free (host);
821 ftpfs_errno = errno;
822 return -1;
825 tty_enable_interrupt_key (); /* clear the interrupt flag */
827 memset (&hints, 0, sizeof (struct addrinfo));
828 hints.ai_family = AF_UNSPEC;
829 hints.ai_socktype = SOCK_STREAM;
831 #ifdef AI_ADDRCONFIG
832 /* By default, only look up addresses using address types for
833 * which a local interface is configured (i.e. no IPv6 if no IPv6
834 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
835 hints.ai_flags = AI_ADDRCONFIG;
836 #endif
838 e = getaddrinfo (host, port, &hints, &res);
840 #ifdef AI_ADDRCONFIG
841 if (e == EAI_BADFLAGS)
843 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
844 hints.ai_flags = 0;
845 e = getaddrinfo (host, port, &hints, &res);
847 #endif
849 *port = '\0';
851 if (e != 0)
853 tty_disable_interrupt_key ();
854 vfs_print_message (_("ftpfs: %s"), gai_strerror (e));
855 g_free (host);
856 ftpfs_errno = EINVAL;
857 return -1;
860 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
862 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
864 if (my_socket < 0)
866 if (curr_res->ai_next != NULL)
867 continue;
869 tty_disable_interrupt_key ();
870 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno));
871 g_free (host);
872 freeaddrinfo (res);
873 ftpfs_errno = errno;
874 return -1;
877 vfs_print_message (_("ftpfs: making connection to %s"), host);
878 g_free (host);
879 host = NULL;
881 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
882 break;
884 ftpfs_errno = errno;
885 close (my_socket);
887 if (errno == EINTR && tty_got_interrupt ())
888 vfs_print_message (_("ftpfs: connection interrupted by user"));
889 else if (res->ai_next == NULL)
890 vfs_print_message (_("ftpfs: connection to server failed: %s"),
891 unix_error_string (errno));
892 else
893 continue;
895 freeaddrinfo (res);
896 tty_disable_interrupt_key ();
897 return -1;
900 freeaddrinfo (res);
901 tty_disable_interrupt_key ();
902 return my_socket;
905 /* --------------------------------------------------------------------------------------------- */
907 static int
908 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
910 int retry_seconds = 0;
911 int count_down;
913 /* We do not want to use the passive if we are using proxies */
914 if (SUP->proxy)
915 SUP->use_passive_connection = ftpfs_use_passive_connections_over_proxy;
919 SUP->failed_on_login = 0;
921 SUP->sock = ftpfs_open_socket (me, super);
922 if (SUP->sock == -1)
923 return -1;
925 if (ftpfs_login_server (me, super, NULL) != 0)
927 /* Logged in, no need to retry the connection */
928 break;
930 else
932 if (!SUP->failed_on_login)
933 return -1;
935 /* Close only the socket descriptor */
936 close (SUP->sock);
938 if (ftpfs_retry_seconds != 0)
940 retry_seconds = ftpfs_retry_seconds;
941 tty_enable_interrupt_key ();
942 for (count_down = retry_seconds; count_down; count_down--)
944 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
945 count_down);
946 sleep (1);
947 if (tty_got_interrupt ())
949 /* ftpfs_errno = E; */
950 tty_disable_interrupt_key ();
951 return 0;
954 tty_disable_interrupt_key ();
958 while (retry_seconds != 0);
960 super->path_element->path = ftpfs_get_current_directory (me, super);
961 if (super->path_element->path == NULL)
962 super->path_element->path = g_strdup (PATH_SEP_STR);
964 return 0;
967 /* --------------------------------------------------------------------------------------------- */
969 static int
970 ftpfs_open_archive (struct vfs_s_super *super,
971 const vfs_path_t * vpath, const vfs_path_element_t * vpath_element)
973 (void) vpath;
975 super->data = g_new0 (ftp_super_data_t, 1);
977 super->path_element = ftpfs_correct_url_parameters (vpath_element);
978 SUP->proxy = NULL;
979 if (ftpfs_check_proxy (super->path_element->host))
980 SUP->proxy = ftpfs_proxy_host;
981 SUP->use_passive_connection = ftpfs_use_passive_connections;
982 SUP->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
983 SUP->isbinary = TYPE_UNKNOWN;
984 SUP->remote_is_amiga = 0;
985 super->name = g_strdup ("/");
986 super->root =
987 vfs_s_new_inode (vpath_element->class, super,
988 vfs_s_default_stat (vpath_element->class, S_IFDIR | 0755));
990 return ftpfs_open_archive_int (vpath_element->class, super);
993 /* --------------------------------------------------------------------------------------------- */
995 static int
996 ftpfs_archive_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *super,
997 const vfs_path_t * vpath, void *cookie)
999 vfs_path_element_t *path_element;
1000 int result;
1002 (void) vpath;
1003 (void) cookie;
1005 path_element = ftpfs_correct_url_parameters (vpath_element);
1007 result = ((strcmp (path_element->host, super->path_element->host) == 0)
1008 && (strcmp (path_element->user, super->path_element->user) == 0)
1009 && (path_element->port == super->path_element->port)) ? 1 : 0;
1011 vfs_path_element_free (path_element);
1012 return result;
1015 /* --------------------------------------------------------------------------------------------- */
1016 /* The returned directory should always contain a trailing slash */
1018 static char *
1019 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
1021 char buf[BUF_8K], *bufp, *bufq;
1023 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
1024 ftpfs_get_reply (me, SUP->sock, buf, sizeof (buf)) == COMPLETE)
1026 bufp = NULL;
1027 for (bufq = buf; *bufq; bufq++)
1028 if (*bufq == '"')
1030 if (!bufp)
1032 bufp = bufq + 1;
1034 else
1036 *bufq = 0;
1037 if (*bufp)
1039 if (*(bufq - 1) != '/')
1041 *bufq++ = '/';
1042 *bufq = 0;
1044 if (*bufp == '/')
1045 return g_strdup (bufp);
1046 else
1048 /* If the remote server is an Amiga a leading slash
1049 might be missing. MC needs it because it is used
1050 as separator between hostname and path internally. */
1051 return g_strconcat ("/", bufp, (char *) NULL);
1054 else
1056 ftpfs_errno = EIO;
1057 return NULL;
1062 ftpfs_errno = EIO;
1063 return NULL;
1066 /* --------------------------------------------------------------------------------------------- */
1067 /* Setup Passive PASV FTP connection */
1069 static int
1070 ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
1071 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1073 char *c;
1074 char n[6];
1075 int xa, xb, xc, xd, xe, xf;
1077 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
1078 return 0;
1080 /* Parse remote parameters */
1081 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
1083 if (!*c)
1084 return 0;
1085 if (!isdigit ((unsigned char) *c))
1086 return 0;
1087 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1088 return 0;
1090 n[0] = (unsigned char) xa;
1091 n[1] = (unsigned char) xb;
1092 n[2] = (unsigned char) xc;
1093 n[3] = (unsigned char) xd;
1094 n[4] = (unsigned char) xe;
1095 n[5] = (unsigned char) xf;
1097 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1098 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1100 if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
1101 return 0;
1103 return 1;
1106 /* --------------------------------------------------------------------------------------------- */
1107 /* Setup Passive EPSV FTP connection */
1109 static int
1110 ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
1111 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1113 char *c;
1114 int port;
1116 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") != COMPLETE)
1117 return 0;
1119 /* (|||<port>|) */
1120 c = strchr (reply_str, '|');
1121 if (c == NULL)
1122 return 0;
1123 if (strlen (c) > 3)
1124 c += 3;
1125 else
1126 return 0;
1128 port = atoi (c);
1129 if (port < 0 || port > 65535)
1130 return 0;
1131 port = htons (port);
1133 switch (sa->ss_family)
1135 case AF_INET:
1136 ((struct sockaddr_in *) sa)->sin_port = port;
1137 break;
1138 case AF_INET6:
1139 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1140 break;
1143 return (connect (my_socket, (struct sockaddr *) sa, *salen) < 0) ? 0 : 1;
1146 /* --------------------------------------------------------------------------------------------- */
1147 /* Setup Passive ftp connection, we use it for source routed connections */
1149 static int
1150 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
1151 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1153 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1154 if (sa->ss_family == AF_INET)
1156 if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
1157 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1158 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1159 return 0;
1161 /* It's IPV6, so EPSV is our only hope */
1162 else
1164 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1165 return 0;
1168 return 1;
1171 /* --------------------------------------------------------------------------------------------- */
1172 /* Setup Active PORT or EPRT FTP connection */
1174 static int
1175 ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super,
1176 struct sockaddr_storage data_addr, socklen_t data_addrlen)
1178 unsigned short int port;
1179 char *addr;
1180 unsigned int af;
1182 switch (data_addr.ss_family)
1184 case AF_INET:
1185 af = FTP_INET;
1186 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1187 break;
1188 case AF_INET6:
1189 af = FTP_INET6;
1190 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1191 break;
1192 /* Not implemented */
1193 default:
1194 return 0;
1197 addr = g_try_malloc (NI_MAXHOST);
1198 if (addr == NULL)
1199 ERRNOR (ENOMEM, -1);
1201 if (getnameinfo
1202 ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1203 NI_NUMERICHOST) != 0)
1205 g_free (addr);
1206 ERRNOR (EIO, -1);
1209 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1210 if (af == FTP_INET)
1212 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1213 unsigned char *p = (unsigned char *) &port;
1215 if (ftpfs_command (me, super, WAIT_REPLY,
1216 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1217 p[0], p[1]) == COMPLETE)
1219 g_free (addr);
1220 return 1;
1225 * Converts network MSB first order to host byte order (LSB
1226 * first on i386). If we do it earlier, we will run into an
1227 * endianness issue, because the server actually expects to see
1228 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1230 port = ntohs (port);
1232 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1233 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
1235 g_free (addr);
1236 return 1;
1239 g_free (addr);
1240 return 0;
1243 /* --------------------------------------------------------------------------------------------- */
1244 /* Initialize a socket for FTP DATA connection */
1246 static int
1247 ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super,
1248 struct sockaddr_storage *data_addr, socklen_t * data_addrlen)
1250 int result;
1252 memset (data_addr, 0, sizeof (struct sockaddr_storage));
1253 *data_addrlen = sizeof (struct sockaddr_storage);
1255 if (SUP->use_passive_connection)
1256 result = getpeername (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1257 else
1258 result = getsockname (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1260 if (result == -1)
1261 return -1;
1263 switch (data_addr->ss_family)
1265 case AF_INET:
1266 ((struct sockaddr_in *) data_addr)->sin_port = 0;
1267 break;
1268 case AF_INET6:
1269 ((struct sockaddr_in6 *) data_addr)->sin6_port = 0;
1270 break;
1271 default:
1272 vfs_print_message (_("ftpfs: invalid address family"));
1273 ERRNOR (EINVAL, -1);
1276 result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP);
1278 if (result < 0)
1280 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1281 return -1;
1284 return result;
1287 /* --------------------------------------------------------------------------------------------- */
1288 /* Initialize FTP DATA connection */
1290 static int
1291 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1293 struct sockaddr_storage data_addr;
1294 socklen_t data_addrlen;
1297 * Don't factor socket initialization out of these conditionals,
1298 * because ftpfs_init_data_socket initializes it in different way
1299 * depending on use_passive_connection flag.
1302 /* Try to establish a passive connection first (if requested) */
1303 if (SUP->use_passive_connection)
1305 int data_sock;
1307 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1308 if (data_sock < 0)
1309 return -1;
1311 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1312 return data_sock;
1314 vfs_print_message (_("ftpfs: could not setup passive mode"));
1315 SUP->use_passive_connection = 0;
1317 close (data_sock);
1320 /* If passive setup is diabled or failed, fallback to active connections */
1321 if (!SUP->use_passive_connection)
1323 int data_sock;
1325 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1326 if (data_sock < 0)
1327 return -1;
1329 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) == 0) &&
1330 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) == 0) &&
1331 (listen (data_sock, 1) == 0) &&
1332 (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0))
1333 return data_sock;
1335 close (data_sock);
1338 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1339 SUP->use_passive_connection = SUP->proxy != NULL ? ftpfs_use_passive_connections_over_proxy :
1340 ftpfs_use_passive_connections;
1342 ftpfs_errno = EIO;
1343 return -1;
1346 /* --------------------------------------------------------------------------------------------- */
1348 static int
1349 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1350 const char *remote, int isbinary, int reget)
1352 struct sockaddr_storage from;
1353 int s, j, data;
1354 socklen_t fromlen = sizeof (from);
1356 s = ftpfs_initconn (me, super);
1357 if (s == -1)
1358 return -1;
1360 if (ftpfs_changetype (me, super, isbinary) == -1)
1361 return -1;
1362 if (reget > 0)
1364 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1365 if (j != CONTINUE)
1366 return -1;
1368 if (remote)
1370 char *remote_path = ftpfs_translate_path (me, super, remote);
1371 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1372 /* WarFtpD can't STORE //filename */
1373 (*remote_path == '/') ? remote_path + 1 : remote_path);
1374 g_free (remote_path);
1376 else
1377 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1379 if (j != PRELIM)
1380 ERRNOR (EPERM, -1);
1381 tty_enable_interrupt_key ();
1382 if (SUP->use_passive_connection)
1383 data = s;
1384 else
1386 data = accept (s, (struct sockaddr *) &from, &fromlen);
1387 if (data < 0)
1389 ftpfs_errno = errno;
1390 close (s);
1391 return -1;
1393 close (s);
1395 tty_disable_interrupt_key ();
1396 return data;
1399 /* --------------------------------------------------------------------------------------------- */
1401 static void
1402 ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t * fh)
1404 struct vfs_s_super *super = FH_SUPER;
1405 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1406 fd_set mask;
1407 char buf[BUF_8K];
1408 int dsock = FH_SOCK;
1409 FH_SOCK = -1;
1410 SUP->ctl_connection_busy = 0;
1412 vfs_print_message (_("ftpfs: aborting transfer."));
1413 if (send (SUP->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1415 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1416 if (dsock != -1)
1417 close (dsock);
1418 return;
1421 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1423 vfs_print_message (_("ftpfs: abort failed"));
1424 if (dsock != -1)
1425 close (dsock);
1426 return;
1428 if (dsock != -1)
1430 FD_ZERO (&mask);
1431 FD_SET (dsock, &mask);
1432 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1434 struct timeval start_tim, tim;
1435 gettimeofday (&start_tim, NULL);
1436 /* flush the remaining data */
1437 while (read (dsock, buf, sizeof (buf)) > 0)
1439 gettimeofday (&tim, NULL);
1440 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT)
1442 /* server keeps sending, drop the connection and ftpfs_reconnect */
1443 close (dsock);
1444 ftpfs_reconnect (me, super);
1445 return;
1449 close (dsock);
1451 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) == TRANSIENT) && (code == 426))
1452 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1455 /* --------------------------------------------------------------------------------------------- */
1457 #if 0
1458 static void
1459 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1460 struct vfs_s_inode *dir)
1462 struct linklist *flist;
1463 struct direntry *fe, *fel;
1464 char tmp[MC_MAXPATHLEN];
1465 int depth;
1467 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1468 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1470 /* flist->data->l_stat is alread initialized with 0 */
1471 fel = flist->data;
1472 if (S_ISLNK (fel->s.st_mode) && fel->linkname)
1474 if (fel->linkname[0] == '/')
1476 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1477 continue;
1478 strcpy (tmp, fel->linkname);
1480 else
1482 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1483 continue;
1484 strcpy (tmp, dir->remote_path);
1485 if (tmp[1] != '\0')
1486 strcat (tmp, "/");
1487 strcat (tmp + 1, fel->linkname);
1489 for (depth = 0; depth < 100; depth++)
1490 { /* depth protects against recursive symbolic links */
1491 canonicalize_pathname (tmp);
1492 fe = _get_file_entry (bucket, tmp, 0, 0);
1493 if (fe)
1495 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1497 /* Symlink points to link which isn't resolved, yet. */
1498 if (fe->linkname[0] == '/')
1500 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1501 break;
1502 strcpy (tmp, fe->linkname);
1504 else
1506 /* at this point tmp looks always like this
1507 /directory/filename, i.e. no need to check
1508 strrchr's return value */
1509 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1510 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1511 break;
1512 strcat (tmp, fe->linkname);
1514 continue;
1516 else
1518 fel->l_stat = g_new (struct stat, 1);
1519 if (S_ISLNK (fe->s.st_mode))
1520 *fel->l_stat = *fe->l_stat;
1521 else
1522 *fel->l_stat = fe->s;
1523 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1526 break;
1530 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1533 /* --------------------------------------------------------------------------------------------- */
1535 static void
1536 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1537 struct vfs_s_inode *dir)
1539 char buffer[2048] = "", *filename;
1540 int sock;
1541 FILE *fp;
1542 struct stat s;
1543 struct linklist *flist;
1544 struct direntry *fe;
1545 int switch_method = 0;
1547 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1548 if (strchr (dir->remote_path, ' '))
1550 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1552 vfs_print_message (_("ftpfs: CWD failed."));
1553 return;
1555 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1557 else
1558 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1560 if (sock == -1)
1562 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1563 return;
1566 fp = fdopen (sock, "r");
1567 if (fp == NULL)
1569 close (sock);
1570 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1571 return;
1573 tty_enable_interrupt_key ();
1574 flist = dir->file_list->next;
1575 while (1)
1579 if (flist == dir->file_list)
1580 goto done;
1581 fe = flist->data;
1582 flist = flist->next;
1584 while (!S_ISLNK (fe->s.st_mode));
1585 while (1)
1587 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1588 goto done;
1589 if (MEDATA->logfile)
1591 fputs (buffer, MEDATA->logfile);
1592 fflush (MEDATA->logfile);
1594 vfs_die ("This code should be commented out\n");
1595 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1597 int r = strcmp (fe->name, filename);
1598 g_free (filename);
1599 if (r == 0)
1601 if (S_ISLNK (s.st_mode))
1603 /* This server doesn't understand LIST -lLa */
1604 switch_method = 1;
1605 goto done;
1607 fe->l_stat = g_new (struct stat, 1);
1608 if (fe->l_stat == NULL)
1609 goto done;
1610 *fe->l_stat = s;
1611 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1612 break;
1614 if (r < 0)
1615 break;
1619 done:
1620 while (fgets (buffer, sizeof (buffer), fp) != NULL);
1621 tty_disable_interrupt_key ();
1622 fclose (fp);
1623 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1626 /* --------------------------------------------------------------------------------------------- */
1628 static void
1629 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1631 vfs_print_message (_("Resolving symlink..."));
1633 if (SUP->strict_rfc959_list_cmd)
1634 resolve_symlink_without_ls_options (me, super, dir);
1635 else
1636 resolve_symlink_with_ls_options (me, super, dir);
1638 #endif
1640 /* --------------------------------------------------------------------------------------------- */
1642 static int
1643 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1645 struct vfs_s_entry *ent;
1646 struct vfs_s_super *super = dir->super;
1647 int sock, num_entries = 0;
1648 char lc_buffer[BUF_8K];
1649 int cd_first;
1651 cd_first = ftpfs_first_cd_then_ls || (SUP->strict == RFC_STRICT)
1652 || (strchr (remote_path, ' ') != NULL);
1654 again:
1655 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1656 remote_path,
1657 SUP->strict ==
1658 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1660 if (cd_first)
1662 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1664 ftpfs_errno = ENOENT;
1665 vfs_print_message (_("ftpfs: CWD failed."));
1666 return -1;
1670 gettimeofday (&dir->timestamp, NULL);
1671 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1673 if (SUP->strict == RFC_STRICT)
1674 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1675 else if (cd_first)
1676 /* Dirty hack to avoid autoprepending / to . */
1677 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1678 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1679 else
1681 /* Trailing "/." is necessary if remote_path is a symlink */
1682 char *path = concat_dir_and_file (remote_path, ".");
1683 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1684 g_free (path);
1687 if (sock == -1)
1688 goto fallback;
1690 /* Clear the interrupt flag */
1691 tty_enable_interrupt_key ();
1693 vfs_parse_ls_lga_init ();
1694 while (1)
1696 int i;
1697 size_t count_spaces = 0;
1698 int res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer),
1699 sock);
1700 if (!res)
1701 break;
1703 if (res == EINTR)
1705 me->verrno = ECONNRESET;
1706 close (sock);
1707 tty_disable_interrupt_key ();
1708 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1709 vfs_print_message (_("%s: failure"), me->name);
1710 return -1;
1713 if (MEDATA->logfile)
1715 fputs (lc_buffer, MEDATA->logfile);
1716 fputs ("\n", MEDATA->logfile);
1717 fflush (MEDATA->logfile);
1720 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1721 i = ent->ino->st.st_nlink;
1722 if (!vfs_parse_ls_lga
1723 (lc_buffer, &ent->ino->st, &ent->name, &ent->ino->linkname, &count_spaces))
1725 vfs_s_free_entry (me, ent);
1726 continue;
1728 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1729 num_entries++;
1730 vfs_s_store_filename_leading_spaces (ent, count_spaces);
1731 vfs_s_insert_entry (me, dir, ent);
1734 close (sock);
1735 me->verrno = E_REMOTE;
1736 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1737 goto fallback;
1739 if (num_entries == 0 && cd_first == 0)
1741 /* The LIST command may produce an empty output. In such scenario
1742 it is not clear whether this is caused by `remote_path' being
1743 a non-existent path or for some other reason (listing emtpy
1744 directory without the -a option, non-readable directory, etc.).
1746 Since `dir_load' is a crucial method, when it comes to determine
1747 whether a given path is a _directory_, the code must try its best
1748 to determine the type of `remote_path'. The only reliable way to
1749 achieve this is trough issuing a CWD command. */
1751 cd_first = 1;
1752 goto again;
1755 vfs_s_normalize_filename_leading_spaces (dir, vfs_parse_ls_lga_get_final_spaces ());
1757 if (SUP->strict == RFC_AUTODETECT)
1758 SUP->strict = RFC_DARING;
1760 vfs_print_message (_("%s: done."), me->name);
1761 return 0;
1763 fallback:
1764 if (SUP->strict == RFC_AUTODETECT)
1766 /* It's our first attempt to get a directory listing from this
1767 server (UNIX style LIST command) */
1768 SUP->strict = RFC_STRICT;
1769 /* I hate goto, but recursive call needs another 8K on stack */
1770 /* return ftpfs_dir_load (me, dir, remote_path); */
1771 cd_first = 1;
1772 goto again;
1774 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1775 ERRNOR (EACCES, -1);
1778 /* --------------------------------------------------------------------------------------------- */
1780 static int
1781 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname)
1783 int h, sock, n_read, n_written;
1784 off_t n_stored;
1785 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1786 struct linger li;
1787 #else
1788 int flag_one = 1;
1789 #endif
1790 char lc_buffer[BUF_8K];
1791 struct stat s;
1792 char *w_buf;
1793 struct vfs_s_super *super = FH_SUPER;
1794 ftp_fh_data_t *ftp = (ftp_fh_data_t *) fh->data;
1796 h = open (localname, O_RDONLY);
1797 if (h == -1)
1798 ERRNOR (EIO, -1);
1800 sock =
1801 ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1802 if (sock < 0 || fstat (h, &s) == -1)
1804 close (h);
1805 return -1;
1807 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1808 li.l_onoff = 1;
1809 li.l_linger = 120;
1810 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1811 #else
1812 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1813 #endif
1814 n_stored = 0;
1816 tty_enable_interrupt_key ();
1817 while (TRUE)
1819 while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1821 if (errno == EINTR)
1823 if (tty_got_interrupt ())
1825 ftpfs_errno = EINTR;
1826 goto error_return;
1828 else
1829 continue;
1831 ftpfs_errno = errno;
1832 goto error_return;
1834 if (n_read == 0)
1835 break;
1836 n_stored += n_read;
1837 w_buf = lc_buffer;
1838 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1840 if (n_written == -1)
1842 if (errno == EINTR && !tty_got_interrupt ())
1844 continue;
1846 ftpfs_errno = errno;
1847 goto error_return;
1849 w_buf += n_written;
1850 n_read -= n_written;
1852 vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
1853 _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
1855 tty_disable_interrupt_key ();
1856 close (sock);
1857 close (h);
1858 if (ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE)
1859 ERRNOR (EIO, -1);
1860 return 0;
1861 error_return:
1862 tty_disable_interrupt_key ();
1863 close (sock);
1864 close (h);
1865 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1866 return -1;
1869 /* --------------------------------------------------------------------------------------------- */
1871 static int
1872 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset)
1874 char *name;
1876 if (fh->data == NULL)
1877 fh->data = g_new0 (ftp_fh_data_t, 1);
1879 name = vfs_s_fullpath (me, fh->ino);
1880 if (name == NULL)
1881 return 0;
1882 FH_SOCK = ftpfs_open_data_connection (me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1883 g_free (name);
1884 if (FH_SOCK == -1)
1885 ERRNOR (EACCES, 0);
1886 fh->linear = LS_LINEAR_OPEN;
1887 ((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy = 1;
1888 ((ftp_fh_data_t *) fh->data)->append = 0;
1889 return 1;
1892 /* --------------------------------------------------------------------------------------------- */
1894 static int
1895 ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t * fh, void *buf, size_t len)
1897 ssize_t n;
1898 struct vfs_s_super *super = FH_SUPER;
1900 while ((n = read (FH_SOCK, buf, len)) < 0)
1902 if ((errno == EINTR) && !tty_got_interrupt ())
1903 continue;
1904 break;
1907 if (n < 0)
1908 ftpfs_linear_abort (me, fh);
1910 if (n == 0)
1912 SUP->ctl_connection_busy = 0;
1913 close (FH_SOCK);
1914 FH_SOCK = -1;
1915 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1916 ERRNOR (E_REMOTE, -1);
1917 return 0;
1919 ERRNOR (errno, n);
1922 /* --------------------------------------------------------------------------------------------- */
1924 static void
1925 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t * fh)
1927 if (FH_SOCK != -1)
1928 ftpfs_linear_abort (me, fh);
1931 /* --------------------------------------------------------------------------------------------- */
1933 static int
1934 ftpfs_ctl (void *fh, int ctlop, void *arg)
1936 (void) arg;
1938 switch (ctlop)
1940 case VFS_CTL_IS_NOTREADY:
1942 int v;
1944 if (!FH->linear)
1945 vfs_die ("You may not do this");
1946 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1947 return 0;
1949 v = vfs_s_select_on_two (((ftp_fh_data_t *) (FH->data))->sock, 0);
1950 return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
1952 default:
1953 return 0;
1957 /* --------------------------------------------------------------------------------------------- */
1959 static int
1960 ftpfs_send_command (const vfs_path_t * vpath, const char *cmd, int flags)
1962 const char *rpath;
1963 char *p;
1964 struct vfs_s_super *super;
1965 int r;
1966 vfs_path_element_t *path_element;
1968 int flush_directory_cache = (flags & OPT_FLUSH);
1970 path_element = vfs_path_get_by_index (vpath, -1);
1972 rpath = vfs_s_get_path (vpath, &super, 0);
1973 if (rpath == NULL)
1974 return -1;
1976 p = ftpfs_translate_path (path_element->class, super, rpath);
1977 r = ftpfs_command (path_element->class, super, WAIT_REPLY, cmd, p);
1978 g_free (p);
1979 vfs_stamp_create (&vfs_ftpfs_ops, super);
1980 if (flags & OPT_IGNORE_ERROR)
1981 r = COMPLETE;
1982 if (r != COMPLETE)
1984 path_element->class->verrno = EPERM;
1985 return -1;
1987 if (flush_directory_cache)
1988 vfs_s_invalidate (path_element->class, super);
1989 return 0;
1992 /* --------------------------------------------------------------------------------------------- */
1994 static int
1995 ftpfs_chmod (const vfs_path_t * vpath, mode_t mode)
1997 char buf[BUF_SMALL];
1998 int ret;
2000 g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", (int) (mode & 07777));
2002 ret = ftpfs_send_command (vpath, buf, OPT_FLUSH);
2004 return ftpfs_ignore_chattr_errors ? 0 : ret;
2007 /* --------------------------------------------------------------------------------------------- */
2009 static int
2010 ftpfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
2012 #if 0
2013 (void) vpath;
2014 (void) owner;
2015 (void) group;
2017 ftpfs_errno = EPERM;
2018 return -1;
2019 #else
2020 /* Everyone knows it is not possible to chown remotely, so why bother them.
2021 If someone's root, then copy/move will always try to chown it... */
2022 (void) vpath;
2023 (void) owner;
2024 (void) group;
2025 return 0;
2026 #endif
2029 /* --------------------------------------------------------------------------------------------- */
2031 static int
2032 ftpfs_unlink (const vfs_path_t * vpath)
2034 return ftpfs_send_command (vpath, "DELE /%s", OPT_FLUSH);
2037 /* --------------------------------------------------------------------------------------------- */
2039 /* Return 1 if path is the same directory as the one we are in now */
2040 static int
2041 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
2043 (void) me;
2045 if (super->path_element->path == NULL)
2046 return FALSE;
2047 return (strcmp (path, super->path_element->path) == 0);
2050 /* --------------------------------------------------------------------------------------------- */
2052 static int
2053 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
2055 int r;
2056 char *p;
2058 if (!SUP->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
2059 return COMPLETE;
2061 p = ftpfs_translate_path (me, super, remote_path);
2062 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
2063 g_free (p);
2065 if (r != COMPLETE)
2066 ftpfs_errno = EIO;
2067 else
2069 g_free (super->path_element->path);
2070 super->path_element->path = g_strdup (remote_path);
2071 SUP->cwd_deferred = 0;
2073 return r;
2076 /* --------------------------------------------------------------------------------------------- */
2078 static int
2079 ftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
2081 ftpfs_send_command (vpath1, "RNFR /%s", OPT_FLUSH);
2082 return ftpfs_send_command (vpath2, "RNTO /%s", OPT_FLUSH);
2085 /* --------------------------------------------------------------------------------------------- */
2087 static int
2088 ftpfs_mkdir (const vfs_path_t * vpath, mode_t mode)
2090 (void) mode; /* FIXME: should be used */
2092 return ftpfs_send_command (vpath, "MKD /%s", OPT_FLUSH);
2095 /* --------------------------------------------------------------------------------------------- */
2097 static int
2098 ftpfs_rmdir (const vfs_path_t * vpath)
2100 return ftpfs_send_command (vpath, "RMD /%s", OPT_FLUSH);
2103 /* --------------------------------------------------------------------------------------------- */
2105 static void
2106 ftpfs_fh_free_data (vfs_file_handler_t *fh)
2108 if (fh != NULL)
2110 g_free (fh->data);
2111 fh->data = NULL;
2115 /* --------------------------------------------------------------------------------------------- */
2117 static int
2118 ftpfs_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
2120 ftp_fh_data_t *ftp;
2122 (void) mode;
2124 fh->data = g_new0 (ftp_fh_data_t, 1);
2125 ftp = (ftp_fh_data_t *) fh->data;
2126 /* File will be written only, so no need to retrieve it from ftp server */
2127 if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
2129 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2130 struct linger li;
2131 #else
2132 int li = 1;
2133 #endif
2134 char *name;
2136 /* ftpfs_linear_start() called, so data will be written
2137 * to local temporary file and stored to ftp server
2138 * by vfs_s_close later
2140 if (((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy)
2142 if (!fh->ino->localname)
2144 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
2145 fh->ino->ent->name);
2146 if (handle == -1)
2147 goto fail;
2148 close (handle);
2149 ftp->append = flags & O_APPEND;
2151 return 0;
2153 name = vfs_s_fullpath (me, fh->ino);
2154 if (name == NULL)
2155 goto fail;
2156 fh->handle =
2157 ftpfs_open_data_connection (me, fh->ino->super,
2158 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
2159 g_free (name);
2161 if (fh->handle < 0)
2162 goto fail;
2163 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2164 li.l_onoff = 1;
2165 li.l_linger = 120;
2166 #endif
2167 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
2169 if (fh->ino->localname)
2171 unlink (fh->ino->localname);
2172 g_free (fh->ino->localname);
2173 fh->ino->localname = NULL;
2175 return 0;
2178 if (!fh->ino->localname && vfs_s_retrieve_file (me, fh->ino) == -1)
2179 goto fail;
2180 if (!fh->ino->localname)
2181 vfs_die ("retrieve_file failed to fill in localname");
2182 return 0;
2184 fail:
2185 ftpfs_fh_free_data (fh);
2186 return -1;
2189 /* --------------------------------------------------------------------------------------------- */
2191 static int
2192 ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t * fh)
2194 if (fh->handle != -1 && !fh->ino->localname)
2196 ftp_super_data_t *ftp = (ftp_super_data_t *) fh->ino->super->data;
2198 close (fh->handle);
2199 fh->handle = -1;
2200 /* File is stored to destination already, so
2201 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2203 fh->changed = 0;
2204 if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE)
2205 ERRNOR (EIO, -1);
2206 vfs_s_invalidate (me, FH_SUPER);
2209 return 0;
2212 /* --------------------------------------------------------------------------------------------- */
2214 static void
2215 ftpfs_done (struct vfs_class *me)
2217 (void) me;
2219 g_slist_foreach (no_proxy, (GFunc) g_free, NULL);
2220 g_slist_free (no_proxy);
2222 g_free (ftpfs_anonymous_passwd);
2223 g_free (ftpfs_proxy_host);
2226 /* --------------------------------------------------------------------------------------------- */
2228 static void
2229 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
2231 GList *iter;
2233 for (iter = MEDATA->supers; iter != NULL; iter = g_list_next (iter))
2235 const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
2236 char *name;
2238 name =
2239 g_strconcat (vfs_ftpfs_ops.prefix, VFS_PATH_URL_DELIMITER, super->path_element->user,
2240 "@", super->path_element->host, "/", super->path_element->path,
2241 (char *) NULL);
2242 func (name);
2243 g_free (name);
2247 /* --------------------------------------------------------------------------------------------- */
2249 static keyword_t
2250 ftpfs_netrc_next (void)
2252 char *p;
2253 keyword_t i;
2254 static const char *const keywords[] = { "default", "machine",
2255 "login", "password", "passwd", "account", "macdef", NULL
2258 while (1)
2260 netrcp = skip_separators (netrcp);
2261 if (*netrcp != '\n')
2262 break;
2263 netrcp++;
2265 if (!*netrcp)
2266 return NETRC_NONE;
2267 p = buffer;
2268 if (*netrcp == '"')
2270 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++)
2272 if (*netrcp == '\\')
2273 netrcp++;
2274 *p++ = *netrcp;
2277 else
2279 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
2280 *netrcp != ',' && *netrcp; netrcp++)
2282 if (*netrcp == '\\')
2283 netrcp++;
2284 *p++ = *netrcp;
2287 *p = 0;
2288 if (!*buffer)
2289 return NETRC_NONE;
2291 for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2292 if (strcmp (keywords[i - 1], buffer) == 0)
2293 return i;
2295 return NETRC_UNKNOWN;
2298 /* --------------------------------------------------------------------------------------------- */
2300 static int
2301 ftpfs_netrc_bad_mode (const char *netrcname)
2303 static int be_angry = 1;
2304 struct stat mystat;
2306 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077))
2308 if (be_angry)
2310 message (D_ERROR, MSG_ERROR,
2311 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2312 be_angry = 0;
2314 return 1;
2316 return 0;
2319 /* --------------------------------------------------------------------------------------------- */
2320 /* Scan .netrc until we find matching "machine" or "default"
2321 * domain is used for additional matching
2322 * No search is done after "default" in compliance with "man netrc"
2323 * Return 0 if found, -1 otherwise */
2325 static int
2326 ftpfs_find_machine (const char *host, const char *domain)
2328 keyword_t keyword;
2330 if (!host)
2331 host = "";
2332 if (!domain)
2333 domain = "";
2335 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2337 if (keyword == NETRC_DEFAULT)
2338 return 0;
2340 if (keyword == NETRC_MACDEF)
2342 /* Scan for an empty line, which concludes "macdef" */
2345 while (*netrcp && *netrcp != '\n')
2346 netrcp++;
2347 if (*netrcp != '\n')
2348 break;
2349 netrcp++;
2351 while (*netrcp && *netrcp != '\n');
2352 continue;
2355 if (keyword != NETRC_MACHINE)
2356 continue;
2358 /* Take machine name */
2359 if (ftpfs_netrc_next () == NETRC_NONE)
2360 break;
2362 if (g_ascii_strcasecmp (host, buffer) != 0)
2364 /* Try adding our domain to short names in .netrc */
2365 const char *host_domain = strchr (host, '.');
2366 if (!host_domain)
2367 continue;
2369 /* Compare domain part */
2370 if (g_ascii_strcasecmp (host_domain, domain) != 0)
2371 continue;
2373 /* Compare local part */
2374 if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2375 continue;
2378 return 0;
2381 /* end of .netrc */
2382 return -1;
2385 /* --------------------------------------------------------------------------------------------- */
2386 /* Extract login and password from .netrc for the host.
2387 * pass may be NULL.
2388 * Returns 0 for success, -1 for error */
2390 static int
2391 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2393 char *netrcname;
2394 char *tmp_pass = NULL;
2395 char hostname[MAXHOSTNAMELEN];
2396 const char *domain;
2397 keyword_t keyword;
2398 static struct rupcache
2400 struct rupcache *next;
2401 char *host;
2402 char *login;
2403 char *pass;
2404 } *rup_cache = NULL, *rupp;
2406 /* Initialize *login and *pass */
2407 g_free (*login);
2408 *login = NULL;
2409 g_free (*pass);
2410 *pass = NULL;
2412 /* Look up in the cache first */
2413 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2415 if (!strcmp (host, rupp->host))
2417 if (rupp->login)
2418 *login = g_strdup (rupp->login);
2419 if (pass && rupp->pass)
2420 *pass = g_strdup (rupp->pass);
2421 return 0;
2425 /* Load current .netrc */
2426 netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2427 if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2429 g_free (netrcname);
2430 return 0;
2433 netrcp = netrc;
2435 /* Find our own domain name */
2436 if (gethostname (hostname, sizeof (hostname)) < 0)
2437 *hostname = '\0';
2439 domain = strchr (hostname, '.');
2440 if (domain == NULL)
2441 domain = "";
2443 /* Scan for "default" and matching "machine" keywords */
2444 ftpfs_find_machine (host, domain);
2446 /* Scan for keywords following "default" and "machine" */
2447 while (1)
2449 int need_break = 0;
2450 keyword = ftpfs_netrc_next ();
2452 switch (keyword)
2454 case NETRC_LOGIN:
2455 if (ftpfs_netrc_next () == NETRC_NONE)
2457 need_break = 1;
2458 break;
2461 /* We have another name already - should not happen */
2462 if (*login)
2464 need_break = 1;
2465 break;
2468 /* We have login name now */
2469 *login = g_strdup (buffer);
2470 break;
2472 case NETRC_PASSWORD:
2473 case NETRC_PASSWD:
2474 if (ftpfs_netrc_next () == NETRC_NONE)
2476 need_break = 1;
2477 break;
2480 /* Ignore unsafe passwords */
2481 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2482 && ftpfs_netrc_bad_mode (netrcname))
2484 need_break = 1;
2485 break;
2488 /* Remember password. pass may be NULL, so use tmp_pass */
2489 if (tmp_pass == NULL)
2490 tmp_pass = g_strdup (buffer);
2491 break;
2493 case NETRC_ACCOUNT:
2494 /* "account" is followed by a token which we ignore */
2495 if (ftpfs_netrc_next () == NETRC_NONE)
2497 need_break = 1;
2498 break;
2501 /* Ignore account, but warn user anyways */
2502 ftpfs_netrc_bad_mode (netrcname);
2503 break;
2505 default:
2506 /* Unexpected keyword or end of file */
2507 need_break = 1;
2508 break;
2511 if (need_break)
2512 break;
2515 g_free (netrc);
2516 g_free (netrcname);
2518 rupp = g_new (struct rupcache, 1);
2519 rupp->host = g_strdup (host);
2520 rupp->login = g_strdup (*login);
2521 rupp->pass = g_strdup (tmp_pass);
2523 rupp->next = rup_cache;
2524 rup_cache = rupp;
2526 *pass = tmp_pass;
2528 return 0;
2531 /* --------------------------------------------------------------------------------------------- */
2532 /*** public functions ****************************************************************************/
2533 /* --------------------------------------------------------------------------------------------- */
2535 /** This routine is called as the last step in load_setup */
2536 void
2537 ftpfs_init_passwd (void)
2539 ftpfs_anonymous_passwd = load_anon_passwd ();
2540 if (ftpfs_anonymous_passwd)
2541 return;
2543 /* If there is no anonymous ftp password specified
2544 * then we'll just use anonymous@
2545 * We don't send any other thing because:
2546 * - We want to remain anonymous
2547 * - We want to stop SPAM
2548 * - We don't want to let ftp sites to discriminate by the user,
2549 * host or country.
2551 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
2554 /* --------------------------------------------------------------------------------------------- */
2556 void
2557 init_ftpfs (void)
2559 static struct vfs_s_subclass ftpfs_subclass;
2561 tcp_init ();
2563 ftpfs_subclass.flags = VFS_S_REMOTE;
2564 ftpfs_subclass.archive_same = ftpfs_archive_same;
2565 ftpfs_subclass.open_archive = ftpfs_open_archive;
2566 ftpfs_subclass.free_archive = ftpfs_free_archive;
2567 ftpfs_subclass.fh_open = ftpfs_fh_open;
2568 ftpfs_subclass.fh_close = ftpfs_fh_close;
2569 ftpfs_subclass.fh_free_data = ftpfs_fh_free_data;
2570 ftpfs_subclass.dir_load = ftpfs_dir_load;
2571 ftpfs_subclass.file_store = ftpfs_file_store;
2572 ftpfs_subclass.linear_start = ftpfs_linear_start;
2573 ftpfs_subclass.linear_read = ftpfs_linear_read;
2574 ftpfs_subclass.linear_close = ftpfs_linear_close;
2576 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2577 vfs_ftpfs_ops.name = "ftpfs";
2578 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2579 vfs_ftpfs_ops.prefix = "ftp";
2580 vfs_ftpfs_ops.done = &ftpfs_done;
2581 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2582 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2583 vfs_ftpfs_ops.chown = ftpfs_chown;
2584 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2585 vfs_ftpfs_ops.rename = ftpfs_rename;
2586 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2587 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2588 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2589 vfs_register_class (&vfs_ftpfs_ops);
2592 /* --------------------------------------------------------------------------------------------- */