Changed input parameters of mc_mkstemp() and mc_tempdir() functions
[midnight-commander.git] / src / vfs / ftpfs / ftpfs.c
blobfe788db00208dfb5cd1f75b9183794ec84809bef
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 #ifdef HAVE_SYS_PARAM_H
91 #include <sys/param.h>
92 #endif
93 #include <errno.h>
94 #include <ctype.h>
95 #include <fcntl.h>
96 #include <sys/time.h> /* gettimeofday() */
97 #include <inttypes.h> /* uintmax_t */
99 #include "lib/global.h"
100 #include "lib/util.h"
101 #include "lib/mcconfig.h"
103 #include "lib/tty/tty.h" /* enable/disable interrupt key */
104 #include "lib/widget.h" /* message() */
106 #include "src/history.h"
107 #include "src/setup.h" /* for load_anon_passwd */
109 #include "lib/vfs/vfs.h"
110 #include "lib/vfs/utilvfs.h"
111 #include "lib/vfs/netutil.h"
112 #include "lib/vfs/xdirentry.h"
113 #include "lib/vfs/gc.h" /* vfs_stamp_create */
115 #include "ftpfs.h"
117 /*** global variables ****************************************************************************/
119 /* Delay to retry a connection */
120 int ftpfs_retry_seconds = 30;
122 /* Method to use to connect to ftp sites */
123 int ftpfs_use_passive_connections = 1;
124 int ftpfs_use_passive_connections_over_proxy = 0;
126 /* Method used to get directory listings:
127 * 1: try 'LIST -la <path>', if it fails
128 * fall back to CWD <path>; LIST
129 * 0: always use CWD <path>; LIST
131 int ftpfs_use_unix_list_options = 1;
133 /* First "CWD <path>", then "LIST -la ." */
134 int ftpfs_first_cd_then_ls = 1;
136 /* Use the ~/.netrc */
137 int ftpfs_use_netrc = 1;
139 /* Anonymous setup */
140 char *ftpfs_anonymous_passwd = NULL;
141 int ftpfs_directory_timeout = 900;
143 /* Proxy host */
144 char *ftpfs_proxy_host = NULL;
146 /* wether we have to use proxy by default? */
147 int ftpfs_always_use_proxy = 0;
149 int ftpfs_ignore_chattr_errors = 1;
151 /*** file scope macro definitions ****************************************************************/
153 #ifndef MAXHOSTNAMELEN
154 #define MAXHOSTNAMELEN 64
155 #endif
157 #define UPLOAD_ZERO_LENGTH_FILE
158 #define SUP ((ftp_super_data_t *) super->data)
159 #define FH_SOCK ((ftp_fh_data_t *) fh->data)->sock
161 #ifndef INADDR_NONE
162 #define INADDR_NONE 0xffffffff
163 #endif
165 #define RFC_AUTODETECT 0
166 #define RFC_DARING 1
167 #define RFC_STRICT 2
169 /* ftpfs_command wait_flag: */
170 #define NONE 0x00
171 #define WAIT_REPLY 0x01
172 #define WANT_STRING 0x02
174 #define FTP_COMMAND_PORT 21
176 /* some defines only used by ftpfs_changetype */
177 /* These two are valid values for the second parameter */
178 #define TYPE_ASCII 0
179 #define TYPE_BINARY 1
181 /* This one is only used to initialize bucket->isbinary, don't use it as
182 second parameter to ftpfs_changetype. */
183 #define TYPE_UNKNOWN -1
185 #define ABORT_TIMEOUT 5
186 /*** file scope type declarations ****************************************************************/
188 #ifndef HAVE_SOCKLEN_T
189 typedef int socklen_t;
190 #endif
192 /* This should match the keywords[] array below */
193 typedef enum
195 NETRC_NONE = 0,
196 NETRC_DEFAULT,
197 NETRC_MACHINE,
198 NETRC_LOGIN,
199 NETRC_PASSWORD,
200 NETRC_PASSWD,
201 NETRC_ACCOUNT,
202 NETRC_MACDEF,
203 NETRC_UNKNOWN
204 } keyword_t;
206 typedef struct
208 int sock;
210 char *proxy; /* proxy server, NULL if no proxy */
211 int failed_on_login; /* used to pass the failure reason to upper levels */
212 int use_passive_connection;
213 int remote_is_amiga; /* No leading slash allowed for AmiTCP (Amiga) */
214 int isbinary;
215 int cwd_deferred; /* current_directory was changed but CWD command hasn't
216 been sent yet */
217 int strict; /* ftp server doesn't understand
218 * "LIST -la <path>"; use "CWD <path>"/
219 * "LIST" instead
221 int ctl_connection_busy;
222 } ftp_super_data_t;
224 typedef struct
226 int sock;
227 int append;
228 } ftp_fh_data_t;
230 /*** file scope variables ************************************************************************/
232 static int ftpfs_errno;
233 static int code;
235 #ifdef FIXME_LATER_ALIGATOR
236 static struct linklist *connections_list;
237 #endif
239 static char reply_str[80];
241 static struct vfs_class vfs_ftpfs_ops;
243 static GSList *no_proxy;
245 static char buffer[BUF_MEDIUM];
246 static char *netrc;
247 static const char *netrcp;
249 /*** file scope functions ************************************************************************/
250 /* --------------------------------------------------------------------------------------------- */
252 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
253 Translate a Unix path, i.e. MC's internal path representation (e.g.
254 /somedir/somefile) to a path valid for the remote server. Every path
255 transfered to the remote server has to be mangled by this function
256 right prior to sending it.
257 Currently only Amiga ftp servers are handled in a special manner.
259 When the remote server is an amiga:
260 a) strip leading slash if necesarry
261 b) replace first occurance of ":/" with ":"
262 c) strip trailing "/."
265 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
266 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super,
267 const char *remote_path);
268 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply,
269 const char *fmt, ...) __attribute__ ((format (__printf__, 4, 5)));
270 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
271 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
272 const char *netrcpass);
273 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
275 /* --------------------------------------------------------------------------------------------- */
277 static char *
278 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
280 if (!SUP->remote_is_amiga)
281 return g_strdup (remote_path);
282 else
284 char *ret, *p;
286 if (MEDATA->logfile)
288 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
289 fflush (MEDATA->logfile);
292 /* strip leading slash(es) */
293 while (*remote_path == '/')
294 remote_path++;
297 * Don't change "/" into "", e.g. "CWD " would be
298 * invalid.
300 if (*remote_path == '\0')
301 return g_strdup (".");
303 ret = g_strdup (remote_path);
305 /* replace first occurance of ":/" with ":" */
306 p = strchr (ret, ':');
307 if ((p != NULL) && (*(p + 1) == '/'))
308 memmove (p + 1, p + 2, strlen (p + 2) + 1);
310 /* strip trailing "/." */
311 p = strrchr (ret, '/');
312 if ((p != NULL) && (*(p + 1) == '.') && (*(p + 2) == '\0'))
313 *p = '\0';
315 return ret;
319 /* --------------------------------------------------------------------------------------------- */
320 /** Extract the hostname and username from the path */
322 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
323 * ftp://sunsite.unc.edu/pub/linux
324 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
325 * ftp://tsx-11.mit.edu:8192/
326 * ftp://joe@foo.edu:11321/private
327 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
328 * is supplied.
331 static vfs_path_element_t *
332 ftpfs_correct_url_parameters (const vfs_path_element_t * velement)
334 vfs_path_element_t *path_element = vfs_path_element_clone (velement);
336 if (path_element->port == 0)
337 path_element->port = FTP_COMMAND_PORT;
339 if (path_element->user == NULL)
341 /* Look up user and password in netrc */
342 if (ftpfs_use_netrc)
343 ftpfs_netrc_lookup (path_element->host, &path_element->user, &path_element->password);
345 if (path_element->user == NULL)
346 path_element->user = g_strdup ("anonymous");
348 /* Look up password in netrc for known user */
349 if (ftpfs_use_netrc && path_element->user != NULL && path_element->password != NULL)
351 char *new_user = NULL;
352 char *new_passwd = NULL;
354 ftpfs_netrc_lookup (path_element->host, &new_user, &new_passwd);
356 /* If user is different, remove password */
357 if (new_user != NULL && strcmp (path_element->user, new_user) != 0)
359 g_free (path_element->password);
360 path_element->password = NULL;
363 g_free (new_user);
364 g_free (new_passwd);
367 return path_element;
370 /* --------------------------------------------------------------------------------------------- */
371 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
373 static int
374 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
376 char answer[BUF_1K];
377 int i;
379 for (;;)
381 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
383 if (string_buf)
384 *string_buf = 0;
385 code = 421;
386 return 4;
388 switch (sscanf (answer, "%d", &code))
390 case 0:
391 if (string_buf)
392 g_strlcpy (string_buf, answer, string_len);
393 code = 500;
394 return 5;
395 case 1:
396 if (answer[3] == '-')
398 while (1)
400 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
402 if (string_buf)
403 *string_buf = 0;
404 code = 421;
405 return 4;
407 if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' '))
408 break;
411 if (string_buf)
412 g_strlcpy (string_buf, answer, string_len);
413 return code / 100;
418 /* --------------------------------------------------------------------------------------------- */
420 static int
421 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
423 int sock;
425 sock = ftpfs_open_socket (me, super);
426 if (sock != -1)
428 char *cwdir = super->path_element->path;
430 close (SUP->sock);
431 SUP->sock = sock;
432 super->path_element->path = NULL;
435 if (ftpfs_login_server (me, super, super->path_element->password) != 0)
437 if (cwdir == NULL)
438 return 1;
439 sock = ftpfs_chdir_internal (me, super, cwdir);
440 g_free (cwdir);
441 return sock == COMPLETE ? 1 : 0;
444 super->path_element->path = cwdir;
447 return 0;
450 /* --------------------------------------------------------------------------------------------- */
452 static int
453 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt,
454 ...)
456 va_list ap;
457 char *cmdstr;
458 int status, cmdlen;
459 static int retry = 0;
460 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
462 va_start (ap, fmt);
463 cmdstr = g_strdup_vprintf (fmt, ap);
464 va_end (ap);
466 cmdlen = strlen (cmdstr);
467 cmdstr = g_realloc (cmdstr, cmdlen + 3);
468 strcpy (cmdstr + cmdlen, "\r\n");
469 cmdlen += 2;
471 if (MEDATA->logfile)
473 if (strncmp (cmdstr, "PASS ", 5) == 0)
475 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
477 else
479 size_t ret;
480 ret = fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
483 fflush (MEDATA->logfile);
486 got_sigpipe = 0;
487 tty_enable_interrupt_key ();
488 status = write (SUP->sock, cmdstr, cmdlen);
490 if (status < 0)
492 code = 421;
494 if (errno == EPIPE)
495 { /* Remote server has closed connection */
496 if (level == 0)
498 level = 1;
499 status = ftpfs_reconnect (me, super);
500 level = 0;
501 if (status && (write (SUP->sock, cmdstr, cmdlen) > 0))
503 goto ok;
507 got_sigpipe = 1;
509 g_free (cmdstr);
510 tty_disable_interrupt_key ();
511 return TRANSIENT;
513 retry = 0;
515 tty_disable_interrupt_key ();
517 if (wait_reply)
519 status = ftpfs_get_reply (me, SUP->sock,
520 (wait_reply & WANT_STRING) ? reply_str : NULL,
521 sizeof (reply_str) - 1);
522 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
524 retry = 1;
525 level = 1;
526 status = ftpfs_reconnect (me, super);
527 level = 0;
528 if (status && (write (SUP->sock, cmdstr, cmdlen) > 0))
530 goto ok;
533 retry = 0;
534 g_free (cmdstr);
535 return status;
537 g_free (cmdstr);
538 return COMPLETE;
541 /* --------------------------------------------------------------------------------------------- */
543 static void
544 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
546 if (SUP->sock != -1)
548 vfs_print_message (_("ftpfs: Disconnecting from %s"), super->path_element->host);
549 ftpfs_command (me, super, NONE, "QUIT");
550 close (SUP->sock);
552 g_free (super->data);
553 super->data = NULL;
556 /* --------------------------------------------------------------------------------------------- */
558 static int
559 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
561 if (binary != SUP->isbinary)
563 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
564 ERRNOR (EIO, -1);
565 SUP->isbinary = binary;
567 return binary;
570 /* --------------------------------------------------------------------------------------------- */
571 /* This routine logs the user in */
573 static int
574 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
576 char *pass;
577 char *op;
578 char *name; /* login user name */
579 int anon = 0;
580 char reply_string[BUF_MEDIUM];
582 SUP->isbinary = TYPE_UNKNOWN;
584 if (super->path_element->password != NULL) /* explicit password */
585 op = g_strdup (super->path_element->password);
586 else if (netrcpass != NULL) /* password from netrc */
587 op = g_strdup (netrcpass);
588 else if (strcmp (super->path_element->user, "anonymous") == 0
589 || strcmp (super->path_element->user, "ftp") == 0)
591 if (ftpfs_anonymous_passwd == NULL) /* default anonymous password */
592 ftpfs_init_passwd ();
593 op = g_strdup (ftpfs_anonymous_passwd);
594 anon = 1;
596 else
597 { /* ask user */
598 char *p;
600 p = g_strdup_printf (_("FTP: Password required for %s"), super->path_element->user);
601 op = vfs_get_password (p);
602 g_free (p);
603 if (op == NULL)
604 ERRNOR (EPERM, 0);
605 super->path_element->password = g_strdup (op);
608 if (!anon || MEDATA->logfile)
609 pass = op;
610 else
612 pass = g_strconcat ("-", op, (char *) NULL);
613 wipe_password (op);
616 /* Proxy server accepts: username@host-we-want-to-connect */
617 if (SUP->proxy)
618 name =
619 g_strconcat (super->path_element->user, "@",
620 super->path_element->host[0] ==
621 '!' ? super->path_element->host + 1 : super->path_element->host,
622 (char *) NULL);
623 else
624 name = g_strdup (super->path_element->user);
626 if (ftpfs_get_reply (me, SUP->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
628 char *reply_up;
630 reply_up = g_ascii_strup (reply_string, -1);
631 SUP->remote_is_amiga = strstr (reply_up, "AMIGA") != 0;
632 if (strstr (reply_up, " SPFTP/1.0.0000 SERVER ")) /* handles `LIST -la` in a weird way */
633 SUP->strict = RFC_STRICT;
634 g_free (reply_up);
636 if (MEDATA->logfile)
638 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n", SUP->remote_is_amiga);
639 fflush (MEDATA->logfile);
642 vfs_print_message (_("ftpfs: sending login name"));
644 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
646 case CONTINUE:
647 vfs_print_message (_("ftpfs: sending user password"));
648 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
649 if (code == CONTINUE)
651 char *p;
653 p = g_strdup_printf (_("FTP: Account required for user %s"),
654 super->path_element->user);
655 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
656 g_free (p);
657 if (op == NULL)
658 ERRNOR (EPERM, 0);
659 vfs_print_message (_("ftpfs: sending user account"));
660 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
661 g_free (op);
663 if (code != COMPLETE)
664 break;
665 /* fall through */
667 case COMPLETE:
668 vfs_print_message (_("ftpfs: logged in"));
669 wipe_password (pass);
670 g_free (name);
671 return 1;
673 default:
674 SUP->failed_on_login = 1;
675 wipe_password (super->path_element->password);
676 super->path_element->password = NULL;
678 goto login_fail;
682 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
683 super->path_element->user);
685 login_fail:
686 wipe_password (pass);
687 g_free (name);
688 ERRNOR (EPERM, 0);
691 /* --------------------------------------------------------------------------------------------- */
693 static void
694 ftpfs_load_no_proxy_list (void)
696 /* FixMe: shouldn't be hardcoded!!! */
697 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
698 FILE *npf;
699 int c;
700 char *p;
701 static char *mc_file = NULL;
703 mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL);
704 if (exist_file (mc_file))
706 npf = fopen (mc_file, "r");
707 if (npf != NULL)
709 while (fgets (s, sizeof (s), npf) != NULL)
711 p = strchr (s, '\n');
712 if (p == NULL) /* skip bogus entries */
714 while ((c = fgetc (npf)) != EOF && c != '\n')
716 continue;
719 if (p != s)
721 *p = '\0';
722 no_proxy = g_slist_prepend (no_proxy, g_strdup (s));
725 fclose (npf);
728 g_free (mc_file);
731 /* --------------------------------------------------------------------------------------------- */
732 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
734 static int
735 ftpfs_check_proxy (const char *host)
737 GSList *npe;
739 if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0')
740 return 0; /* sanity check */
742 if (*host == '!')
743 return 1;
745 if (!ftpfs_always_use_proxy)
746 return 0;
748 if (strchr (host, '.') == NULL)
749 return 0;
751 ftpfs_load_no_proxy_list ();
752 for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe))
754 const char *domain = (const char *) npe->data;
756 if (domain[0] == '.')
758 size_t ld = strlen (domain);
759 size_t lh = strlen (host);
761 while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1])
763 ld--;
764 lh--;
767 if (ld == 0)
768 return 0;
770 else if (g_ascii_strcasecmp (host, domain) == 0)
771 return 0;
774 return 1;
777 /* --------------------------------------------------------------------------------------------- */
779 static void
780 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
782 vfs_path_element_t *path_element;
784 path_element = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
785 *host = g_strdup (path_element->host);
786 *port = path_element->port;
787 vfs_path_element_free (path_element);
790 /* --------------------------------------------------------------------------------------------- */
792 static int
793 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
795 struct addrinfo hints, *res, *curr_res;
796 int my_socket = 0;
797 char *host = NULL;
798 char port[8];
799 int tmp_port;
800 int e;
802 (void) me;
804 /* Use a proxy host? */
805 host = g_strdup (super->path_element->host);
807 if (host == NULL || *host == '\0')
809 vfs_print_message (_("ftpfs: Invalid host name."));
810 ftpfs_errno = EINVAL;
811 g_free (host);
812 return -1;
815 /* Hosts to connect to that start with a ! should use proxy */
816 tmp_port = super->path_element->port;
818 if (SUP->proxy != NULL)
819 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
821 g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_port);
822 if (port[0] == '\0')
824 g_free (host);
825 ftpfs_errno = errno;
826 return -1;
829 tty_enable_interrupt_key (); /* clear the interrupt flag */
831 memset (&hints, 0, sizeof (struct addrinfo));
832 hints.ai_family = AF_UNSPEC;
833 hints.ai_socktype = SOCK_STREAM;
835 #ifdef AI_ADDRCONFIG
836 /* By default, only look up addresses using address types for
837 * which a local interface is configured (i.e. no IPv6 if no IPv6
838 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
839 hints.ai_flags = AI_ADDRCONFIG;
840 #endif
842 e = getaddrinfo (host, port, &hints, &res);
844 #ifdef AI_ADDRCONFIG
845 if (e == EAI_BADFLAGS)
847 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
848 hints.ai_flags = 0;
849 e = getaddrinfo (host, port, &hints, &res);
851 #endif
853 *port = '\0';
855 if (e != 0)
857 tty_disable_interrupt_key ();
858 vfs_print_message (_("ftpfs: %s"), gai_strerror (e));
859 g_free (host);
860 ftpfs_errno = EINVAL;
861 return -1;
864 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
866 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
868 if (my_socket < 0)
870 if (curr_res->ai_next != NULL)
871 continue;
873 tty_disable_interrupt_key ();
874 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno));
875 g_free (host);
876 freeaddrinfo (res);
877 ftpfs_errno = errno;
878 return -1;
881 vfs_print_message (_("ftpfs: making connection to %s"), host);
882 g_free (host);
883 host = NULL;
885 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
886 break;
888 ftpfs_errno = errno;
889 close (my_socket);
891 if (errno == EINTR && tty_got_interrupt ())
892 vfs_print_message (_("ftpfs: connection interrupted by user"));
893 else if (res->ai_next == NULL)
894 vfs_print_message (_("ftpfs: connection to server failed: %s"),
895 unix_error_string (errno));
896 else
897 continue;
899 freeaddrinfo (res);
900 tty_disable_interrupt_key ();
901 return -1;
904 freeaddrinfo (res);
905 tty_disable_interrupt_key ();
906 return my_socket;
909 /* --------------------------------------------------------------------------------------------- */
911 static int
912 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
914 int retry_seconds = 0;
915 int count_down;
917 /* We do not want to use the passive if we are using proxies */
918 if (SUP->proxy)
919 SUP->use_passive_connection = ftpfs_use_passive_connections_over_proxy;
923 SUP->failed_on_login = 0;
925 SUP->sock = ftpfs_open_socket (me, super);
926 if (SUP->sock == -1)
927 return -1;
929 if (ftpfs_login_server (me, super, NULL) != 0)
931 /* Logged in, no need to retry the connection */
932 break;
934 else
936 if (!SUP->failed_on_login)
937 return -1;
939 /* Close only the socket descriptor */
940 close (SUP->sock);
942 if (ftpfs_retry_seconds != 0)
944 retry_seconds = ftpfs_retry_seconds;
945 tty_enable_interrupt_key ();
946 for (count_down = retry_seconds; count_down; count_down--)
948 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
949 count_down);
950 sleep (1);
951 if (tty_got_interrupt ())
953 /* ftpfs_errno = E; */
954 tty_disable_interrupt_key ();
955 return 0;
958 tty_disable_interrupt_key ();
962 while (retry_seconds != 0);
964 super->path_element->path = ftpfs_get_current_directory (me, super);
965 if (super->path_element->path == NULL)
966 super->path_element->path = g_strdup (PATH_SEP_STR);
968 return 0;
971 /* --------------------------------------------------------------------------------------------- */
973 static int
974 ftpfs_open_archive (struct vfs_s_super *super,
975 const vfs_path_t * vpath, const vfs_path_element_t * vpath_element)
977 (void) vpath;
979 super->data = g_new0 (ftp_super_data_t, 1);
981 super->path_element = ftpfs_correct_url_parameters (vpath_element);
982 SUP->proxy = NULL;
983 if (ftpfs_check_proxy (super->path_element->host))
984 SUP->proxy = ftpfs_proxy_host;
985 SUP->use_passive_connection = ftpfs_use_passive_connections;
986 SUP->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
987 SUP->isbinary = TYPE_UNKNOWN;
988 SUP->remote_is_amiga = 0;
989 super->name = g_strdup ("/");
990 super->root =
991 vfs_s_new_inode (vpath_element->class, super,
992 vfs_s_default_stat (vpath_element->class, S_IFDIR | 0755));
994 return ftpfs_open_archive_int (vpath_element->class, super);
997 /* --------------------------------------------------------------------------------------------- */
999 static int
1000 ftpfs_archive_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *super,
1001 const vfs_path_t * vpath, void *cookie)
1003 vfs_path_element_t *path_element;
1004 int result;
1006 (void) vpath;
1007 (void) cookie;
1009 path_element = ftpfs_correct_url_parameters (vpath_element);
1011 result = ((strcmp (path_element->host, super->path_element->host) == 0)
1012 && (strcmp (path_element->user, super->path_element->user) == 0)
1013 && (path_element->port == super->path_element->port)) ? 1 : 0;
1015 vfs_path_element_free (path_element);
1016 return result;
1019 /* --------------------------------------------------------------------------------------------- */
1020 /* The returned directory should always contain a trailing slash */
1022 static char *
1023 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
1025 char buf[BUF_8K], *bufp, *bufq;
1027 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
1028 ftpfs_get_reply (me, SUP->sock, buf, sizeof (buf)) == COMPLETE)
1030 bufp = NULL;
1031 for (bufq = buf; *bufq; bufq++)
1032 if (*bufq == '"')
1034 if (!bufp)
1036 bufp = bufq + 1;
1038 else
1040 *bufq = 0;
1041 if (*bufp)
1043 if (*(bufq - 1) != '/')
1045 *bufq++ = '/';
1046 *bufq = 0;
1048 if (*bufp == '/')
1049 return g_strdup (bufp);
1050 else
1052 /* If the remote server is an Amiga a leading slash
1053 might be missing. MC needs it because it is used
1054 as separator between hostname and path internally. */
1055 return g_strconcat ("/", bufp, (char *) NULL);
1058 else
1060 ftpfs_errno = EIO;
1061 return NULL;
1066 ftpfs_errno = EIO;
1067 return NULL;
1070 /* --------------------------------------------------------------------------------------------- */
1071 /* Setup Passive PASV FTP connection */
1073 static int
1074 ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
1075 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1077 char *c;
1078 char n[6];
1079 int xa, xb, xc, xd, xe, xf;
1081 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
1082 return 0;
1084 /* Parse remote parameters */
1085 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
1087 if (!*c)
1088 return 0;
1089 if (!isdigit ((unsigned char) *c))
1090 return 0;
1091 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1092 return 0;
1094 n[0] = (unsigned char) xa;
1095 n[1] = (unsigned char) xb;
1096 n[2] = (unsigned char) xc;
1097 n[3] = (unsigned char) xd;
1098 n[4] = (unsigned char) xe;
1099 n[5] = (unsigned char) xf;
1101 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1102 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1104 if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
1105 return 0;
1107 return 1;
1110 /* --------------------------------------------------------------------------------------------- */
1111 /* Setup Passive EPSV FTP connection */
1113 static int
1114 ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
1115 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1117 char *c;
1118 int port;
1120 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") != COMPLETE)
1121 return 0;
1123 /* (|||<port>|) */
1124 c = strchr (reply_str, '|');
1125 if (c == NULL)
1126 return 0;
1127 if (strlen (c) > 3)
1128 c += 3;
1129 else
1130 return 0;
1132 port = atoi (c);
1133 if (port < 0 || port > 65535)
1134 return 0;
1135 port = htons (port);
1137 switch (sa->ss_family)
1139 case AF_INET:
1140 ((struct sockaddr_in *) sa)->sin_port = port;
1141 break;
1142 case AF_INET6:
1143 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1144 break;
1147 return (connect (my_socket, (struct sockaddr *) sa, *salen) < 0) ? 0 : 1;
1150 /* --------------------------------------------------------------------------------------------- */
1151 /* Setup Passive ftp connection, we use it for source routed connections */
1153 static int
1154 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
1155 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1157 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1158 if (sa->ss_family == AF_INET)
1160 if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
1161 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1162 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1163 return 0;
1165 /* It's IPV6, so EPSV is our only hope */
1166 else
1168 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1169 return 0;
1172 return 1;
1175 /* --------------------------------------------------------------------------------------------- */
1176 /* Setup Active PORT or EPRT FTP connection */
1178 static int
1179 ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super,
1180 struct sockaddr_storage data_addr, socklen_t data_addrlen)
1182 unsigned short int port;
1183 char *addr;
1184 unsigned int af;
1186 switch (data_addr.ss_family)
1188 case AF_INET:
1189 af = FTP_INET;
1190 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1191 break;
1192 case AF_INET6:
1193 af = FTP_INET6;
1194 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1195 break;
1196 /* Not implemented */
1197 default:
1198 return 0;
1201 addr = g_try_malloc (NI_MAXHOST);
1202 if (addr == NULL)
1203 ERRNOR (ENOMEM, -1);
1205 if (getnameinfo
1206 ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1207 NI_NUMERICHOST) != 0)
1209 g_free (addr);
1210 ERRNOR (EIO, -1);
1213 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1214 if (af == FTP_INET)
1216 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1217 unsigned char *p = (unsigned char *) &port;
1219 if (ftpfs_command (me, super, WAIT_REPLY,
1220 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1221 p[0], p[1]) == COMPLETE)
1223 g_free (addr);
1224 return 1;
1229 * Converts network MSB first order to host byte order (LSB
1230 * first on i386). If we do it earlier, we will run into an
1231 * endianness issue, because the server actually expects to see
1232 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1234 port = ntohs (port);
1236 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1237 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
1239 g_free (addr);
1240 return 1;
1243 g_free (addr);
1244 return 0;
1247 /* --------------------------------------------------------------------------------------------- */
1248 /* Initialize a socket for FTP DATA connection */
1250 static int
1251 ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super,
1252 struct sockaddr_storage *data_addr, socklen_t * data_addrlen)
1254 int result;
1256 memset (data_addr, 0, sizeof (struct sockaddr_storage));
1257 *data_addrlen = sizeof (struct sockaddr_storage);
1259 if (SUP->use_passive_connection)
1260 result = getpeername (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1261 else
1262 result = getsockname (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1264 if (result == -1)
1265 return -1;
1267 switch (data_addr->ss_family)
1269 case AF_INET:
1270 ((struct sockaddr_in *) data_addr)->sin_port = 0;
1271 break;
1272 case AF_INET6:
1273 ((struct sockaddr_in6 *) data_addr)->sin6_port = 0;
1274 break;
1275 default:
1276 vfs_print_message (_("ftpfs: invalid address family"));
1277 ERRNOR (EINVAL, -1);
1280 result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP);
1282 if (result < 0)
1284 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1285 return -1;
1288 return result;
1291 /* --------------------------------------------------------------------------------------------- */
1292 /* Initialize FTP DATA connection */
1294 static int
1295 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1297 struct sockaddr_storage data_addr;
1298 socklen_t data_addrlen;
1301 * Don't factor socket initialization out of these conditionals,
1302 * because ftpfs_init_data_socket initializes it in different way
1303 * depending on use_passive_connection flag.
1306 /* Try to establish a passive connection first (if requested) */
1307 if (SUP->use_passive_connection)
1309 int data_sock;
1311 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1312 if (data_sock < 0)
1313 return -1;
1315 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1316 return data_sock;
1318 vfs_print_message (_("ftpfs: could not setup passive mode"));
1319 SUP->use_passive_connection = 0;
1321 close (data_sock);
1324 /* If passive setup is diabled or failed, fallback to active connections */
1325 if (!SUP->use_passive_connection)
1327 int data_sock;
1329 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1330 if (data_sock < 0)
1331 return -1;
1333 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) == 0) &&
1334 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) == 0) &&
1335 (listen (data_sock, 1) == 0) &&
1336 (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0))
1337 return data_sock;
1339 close (data_sock);
1342 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1343 SUP->use_passive_connection = SUP->proxy != NULL ? ftpfs_use_passive_connections_over_proxy :
1344 ftpfs_use_passive_connections;
1346 ftpfs_errno = EIO;
1347 return -1;
1350 /* --------------------------------------------------------------------------------------------- */
1352 static int
1353 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1354 const char *remote, int isbinary, int reget)
1356 struct sockaddr_storage from;
1357 int s, j, data;
1358 socklen_t fromlen = sizeof (from);
1360 s = ftpfs_initconn (me, super);
1361 if (s == -1)
1362 return -1;
1364 if (ftpfs_changetype (me, super, isbinary) == -1)
1365 return -1;
1366 if (reget > 0)
1368 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1369 if (j != CONTINUE)
1370 return -1;
1372 if (remote)
1374 char *remote_path = ftpfs_translate_path (me, super, remote);
1375 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1376 /* WarFtpD can't STORE //filename */
1377 (*remote_path == '/') ? remote_path + 1 : remote_path);
1378 g_free (remote_path);
1380 else
1381 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1383 if (j != PRELIM)
1384 ERRNOR (EPERM, -1);
1385 tty_enable_interrupt_key ();
1386 if (SUP->use_passive_connection)
1387 data = s;
1388 else
1390 data = accept (s, (struct sockaddr *) &from, &fromlen);
1391 if (data < 0)
1393 ftpfs_errno = errno;
1394 close (s);
1395 return -1;
1397 close (s);
1399 tty_disable_interrupt_key ();
1400 return data;
1403 /* --------------------------------------------------------------------------------------------- */
1405 static void
1406 ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t * fh)
1408 struct vfs_s_super *super = FH_SUPER;
1409 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1410 fd_set mask;
1411 char buf[BUF_8K];
1412 int dsock = FH_SOCK;
1413 FH_SOCK = -1;
1414 SUP->ctl_connection_busy = 0;
1416 vfs_print_message (_("ftpfs: aborting transfer."));
1417 if (send (SUP->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1419 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1420 if (dsock != -1)
1421 close (dsock);
1422 return;
1425 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1427 vfs_print_message (_("ftpfs: abort failed"));
1428 if (dsock != -1)
1429 close (dsock);
1430 return;
1432 if (dsock != -1)
1434 FD_ZERO (&mask);
1435 FD_SET (dsock, &mask);
1436 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1438 struct timeval start_tim, tim;
1439 gettimeofday (&start_tim, NULL);
1440 /* flush the remaining data */
1441 while (read (dsock, buf, sizeof (buf)) > 0)
1443 gettimeofday (&tim, NULL);
1444 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT)
1446 /* server keeps sending, drop the connection and ftpfs_reconnect */
1447 close (dsock);
1448 ftpfs_reconnect (me, super);
1449 return;
1453 close (dsock);
1455 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) == TRANSIENT) && (code == 426))
1456 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1459 /* --------------------------------------------------------------------------------------------- */
1461 #if 0
1462 static void
1463 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1464 struct vfs_s_inode *dir)
1466 struct linklist *flist;
1467 struct direntry *fe, *fel;
1468 char tmp[MC_MAXPATHLEN];
1469 int depth;
1471 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1472 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1474 /* flist->data->l_stat is alread initialized with 0 */
1475 fel = flist->data;
1476 if (S_ISLNK (fel->s.st_mode) && fel->linkname)
1478 if (fel->linkname[0] == '/')
1480 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1481 continue;
1482 strcpy (tmp, fel->linkname);
1484 else
1486 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1487 continue;
1488 strcpy (tmp, dir->remote_path);
1489 if (tmp[1] != '\0')
1490 strcat (tmp, "/");
1491 strcat (tmp + 1, fel->linkname);
1493 for (depth = 0; depth < 100; depth++)
1494 { /* depth protects against recursive symbolic links */
1495 canonicalize_pathname (tmp);
1496 fe = _get_file_entry (bucket, tmp, 0, 0);
1497 if (fe)
1499 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1501 /* Symlink points to link which isn't resolved, yet. */
1502 if (fe->linkname[0] == '/')
1504 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1505 break;
1506 strcpy (tmp, fe->linkname);
1508 else
1510 /* at this point tmp looks always like this
1511 /directory/filename, i.e. no need to check
1512 strrchr's return value */
1513 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1514 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1515 break;
1516 strcat (tmp, fe->linkname);
1518 continue;
1520 else
1522 fel->l_stat = g_new (struct stat, 1);
1523 if (S_ISLNK (fe->s.st_mode))
1524 *fel->l_stat = *fe->l_stat;
1525 else
1526 *fel->l_stat = fe->s;
1527 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1530 break;
1534 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1537 /* --------------------------------------------------------------------------------------------- */
1539 static void
1540 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1541 struct vfs_s_inode *dir)
1543 char buffer[2048] = "", *filename;
1544 int sock;
1545 FILE *fp;
1546 struct stat s;
1547 struct linklist *flist;
1548 struct direntry *fe;
1549 int switch_method = 0;
1551 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1552 if (strchr (dir->remote_path, ' '))
1554 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1556 vfs_print_message (_("ftpfs: CWD failed."));
1557 return;
1559 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1561 else
1562 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1564 if (sock == -1)
1566 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1567 return;
1570 fp = fdopen (sock, "r");
1571 if (fp == NULL)
1573 close (sock);
1574 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1575 return;
1577 tty_enable_interrupt_key ();
1578 flist = dir->file_list->next;
1579 while (1)
1583 if (flist == dir->file_list)
1584 goto done;
1585 fe = flist->data;
1586 flist = flist->next;
1588 while (!S_ISLNK (fe->s.st_mode));
1589 while (1)
1591 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1592 goto done;
1593 if (MEDATA->logfile)
1595 fputs (buffer, MEDATA->logfile);
1596 fflush (MEDATA->logfile);
1598 vfs_die ("This code should be commented out\n");
1599 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1601 int r = strcmp (fe->name, filename);
1602 g_free (filename);
1603 if (r == 0)
1605 if (S_ISLNK (s.st_mode))
1607 /* This server doesn't understand LIST -lLa */
1608 switch_method = 1;
1609 goto done;
1611 fe->l_stat = g_new (struct stat, 1);
1612 if (fe->l_stat == NULL)
1613 goto done;
1614 *fe->l_stat = s;
1615 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1616 break;
1618 if (r < 0)
1619 break;
1623 done:
1624 while (fgets (buffer, sizeof (buffer), fp) != NULL);
1625 tty_disable_interrupt_key ();
1626 fclose (fp);
1627 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1630 /* --------------------------------------------------------------------------------------------- */
1632 static void
1633 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1635 vfs_print_message (_("Resolving symlink..."));
1637 if (SUP->strict_rfc959_list_cmd)
1638 resolve_symlink_without_ls_options (me, super, dir);
1639 else
1640 resolve_symlink_with_ls_options (me, super, dir);
1642 #endif
1644 /* --------------------------------------------------------------------------------------------- */
1646 static int
1647 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1649 struct vfs_s_entry *ent;
1650 struct vfs_s_super *super = dir->super;
1651 int sock, num_entries = 0;
1652 char lc_buffer[BUF_8K];
1653 int cd_first;
1655 cd_first = ftpfs_first_cd_then_ls || (SUP->strict == RFC_STRICT)
1656 || (strchr (remote_path, ' ') != NULL);
1658 again:
1659 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1660 remote_path,
1661 SUP->strict ==
1662 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1664 if (cd_first)
1666 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1668 ftpfs_errno = ENOENT;
1669 vfs_print_message (_("ftpfs: CWD failed."));
1670 return -1;
1674 gettimeofday (&dir->timestamp, NULL);
1675 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1677 if (SUP->strict == RFC_STRICT)
1678 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1679 else if (cd_first)
1680 /* Dirty hack to avoid autoprepending / to . */
1681 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1682 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1683 else
1685 /* Trailing "/." is necessary if remote_path is a symlink */
1686 char *path = concat_dir_and_file (remote_path, ".");
1687 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1688 g_free (path);
1691 if (sock == -1)
1692 goto fallback;
1694 /* Clear the interrupt flag */
1695 tty_enable_interrupt_key ();
1697 vfs_parse_ls_lga_init ();
1698 while (1)
1700 int i;
1701 size_t count_spaces = 0;
1702 int res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer),
1703 sock);
1704 if (!res)
1705 break;
1707 if (res == EINTR)
1709 me->verrno = ECONNRESET;
1710 close (sock);
1711 tty_disable_interrupt_key ();
1712 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1713 vfs_print_message (_("%s: failure"), me->name);
1714 return -1;
1717 if (MEDATA->logfile)
1719 fputs (lc_buffer, MEDATA->logfile);
1720 fputs ("\n", MEDATA->logfile);
1721 fflush (MEDATA->logfile);
1724 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1725 i = ent->ino->st.st_nlink;
1726 if (!vfs_parse_ls_lga
1727 (lc_buffer, &ent->ino->st, &ent->name, &ent->ino->linkname, &count_spaces))
1729 vfs_s_free_entry (me, ent);
1730 continue;
1732 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1733 num_entries++;
1734 vfs_s_store_filename_leading_spaces (ent, count_spaces);
1735 vfs_s_insert_entry (me, dir, ent);
1738 close (sock);
1739 me->verrno = E_REMOTE;
1740 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1741 goto fallback;
1743 if (num_entries == 0 && cd_first == 0)
1745 /* The LIST command may produce an empty output. In such scenario
1746 it is not clear whether this is caused by `remote_path' being
1747 a non-existent path or for some other reason (listing emtpy
1748 directory without the -a option, non-readable directory, etc.).
1750 Since `dir_load' is a crucial method, when it comes to determine
1751 whether a given path is a _directory_, the code must try its best
1752 to determine the type of `remote_path'. The only reliable way to
1753 achieve this is trough issuing a CWD command. */
1755 cd_first = 1;
1756 goto again;
1759 vfs_s_normalize_filename_leading_spaces (dir, vfs_parse_ls_lga_get_final_spaces ());
1761 if (SUP->strict == RFC_AUTODETECT)
1762 SUP->strict = RFC_DARING;
1764 vfs_print_message (_("%s: done."), me->name);
1765 return 0;
1767 fallback:
1768 if (SUP->strict == RFC_AUTODETECT)
1770 /* It's our first attempt to get a directory listing from this
1771 server (UNIX style LIST command) */
1772 SUP->strict = RFC_STRICT;
1773 /* I hate goto, but recursive call needs another 8K on stack */
1774 /* return ftpfs_dir_load (me, dir, remote_path); */
1775 cd_first = 1;
1776 goto again;
1778 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1779 ERRNOR (EACCES, -1);
1782 /* --------------------------------------------------------------------------------------------- */
1784 static int
1785 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname)
1787 int h, sock, n_read, n_written;
1788 off_t n_stored;
1789 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1790 struct linger li;
1791 #else
1792 int flag_one = 1;
1793 #endif
1794 char lc_buffer[BUF_8K];
1795 struct stat s;
1796 char *w_buf;
1797 struct vfs_s_super *super = FH_SUPER;
1798 ftp_fh_data_t *ftp = (ftp_fh_data_t *) fh->data;
1800 h = open (localname, O_RDONLY);
1801 if (h == -1)
1802 ERRNOR (EIO, -1);
1804 sock =
1805 ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1806 if (sock < 0 || fstat (h, &s) == -1)
1808 close (h);
1809 return -1;
1811 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1812 li.l_onoff = 1;
1813 li.l_linger = 120;
1814 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1815 #else
1816 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1817 #endif
1818 n_stored = 0;
1820 tty_enable_interrupt_key ();
1821 while (TRUE)
1823 while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1825 if (errno == EINTR)
1827 if (tty_got_interrupt ())
1829 ftpfs_errno = EINTR;
1830 goto error_return;
1832 else
1833 continue;
1835 ftpfs_errno = errno;
1836 goto error_return;
1838 if (n_read == 0)
1839 break;
1840 n_stored += n_read;
1841 w_buf = lc_buffer;
1842 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1844 if (n_written == -1)
1846 if (errno == EINTR && !tty_got_interrupt ())
1848 continue;
1850 ftpfs_errno = errno;
1851 goto error_return;
1853 w_buf += n_written;
1854 n_read -= n_written;
1856 vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
1857 _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
1859 tty_disable_interrupt_key ();
1860 close (sock);
1861 close (h);
1862 if (ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE)
1863 ERRNOR (EIO, -1);
1864 return 0;
1865 error_return:
1866 tty_disable_interrupt_key ();
1867 close (sock);
1868 close (h);
1869 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1870 return -1;
1873 /* --------------------------------------------------------------------------------------------- */
1875 static int
1876 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset)
1878 char *name;
1880 if (fh->data == NULL)
1881 fh->data = g_new0 (ftp_fh_data_t, 1);
1883 name = vfs_s_fullpath (me, fh->ino);
1884 if (name == NULL)
1885 return 0;
1886 FH_SOCK = ftpfs_open_data_connection (me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1887 g_free (name);
1888 if (FH_SOCK == -1)
1889 ERRNOR (EACCES, 0);
1890 fh->linear = LS_LINEAR_OPEN;
1891 ((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy = 1;
1892 ((ftp_fh_data_t *) fh->data)->append = 0;
1893 return 1;
1896 /* --------------------------------------------------------------------------------------------- */
1898 static int
1899 ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t * fh, void *buf, size_t len)
1901 ssize_t n;
1902 struct vfs_s_super *super = FH_SUPER;
1904 while ((n = read (FH_SOCK, buf, len)) < 0)
1906 if ((errno == EINTR) && !tty_got_interrupt ())
1907 continue;
1908 break;
1911 if (n < 0)
1912 ftpfs_linear_abort (me, fh);
1914 if (n == 0)
1916 SUP->ctl_connection_busy = 0;
1917 close (FH_SOCK);
1918 FH_SOCK = -1;
1919 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1920 ERRNOR (E_REMOTE, -1);
1921 return 0;
1923 ERRNOR (errno, n);
1926 /* --------------------------------------------------------------------------------------------- */
1928 static void
1929 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t * fh)
1931 if (FH_SOCK != -1)
1932 ftpfs_linear_abort (me, fh);
1935 /* --------------------------------------------------------------------------------------------- */
1937 static int
1938 ftpfs_ctl (void *fh, int ctlop, void *arg)
1940 (void) arg;
1942 switch (ctlop)
1944 case VFS_CTL_IS_NOTREADY:
1946 int v;
1948 if (!FH->linear)
1949 vfs_die ("You may not do this");
1950 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1951 return 0;
1953 v = vfs_s_select_on_two (((ftp_fh_data_t *) (FH->data))->sock, 0);
1954 return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
1956 default:
1957 return 0;
1961 /* --------------------------------------------------------------------------------------------- */
1963 static int
1964 ftpfs_send_command (const vfs_path_t * vpath, const char *cmd, int flags)
1966 const char *rpath;
1967 char *p;
1968 struct vfs_s_super *super;
1969 int r;
1970 vfs_path_element_t *path_element;
1972 int flush_directory_cache = (flags & OPT_FLUSH);
1974 path_element = vfs_path_get_by_index (vpath, -1);
1976 rpath = vfs_s_get_path (vpath, &super, 0);
1977 if (rpath == NULL)
1978 return -1;
1980 p = ftpfs_translate_path (path_element->class, super, rpath);
1981 r = ftpfs_command (path_element->class, super, WAIT_REPLY, cmd, p);
1982 g_free (p);
1983 vfs_stamp_create (&vfs_ftpfs_ops, super);
1984 if (flags & OPT_IGNORE_ERROR)
1985 r = COMPLETE;
1986 if (r != COMPLETE)
1988 path_element->class->verrno = EPERM;
1989 return -1;
1991 if (flush_directory_cache)
1992 vfs_s_invalidate (path_element->class, super);
1993 return 0;
1996 /* --------------------------------------------------------------------------------------------- */
1998 static int
1999 ftpfs_chmod (const vfs_path_t * vpath, mode_t mode)
2001 char buf[BUF_SMALL];
2002 int ret;
2004 g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", (int) (mode & 07777));
2006 ret = ftpfs_send_command (vpath, buf, OPT_FLUSH);
2008 return ftpfs_ignore_chattr_errors ? 0 : ret;
2011 /* --------------------------------------------------------------------------------------------- */
2013 static int
2014 ftpfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
2016 #if 0
2017 (void) vpath;
2018 (void) owner;
2019 (void) group;
2021 ftpfs_errno = EPERM;
2022 return -1;
2023 #else
2024 /* Everyone knows it is not possible to chown remotely, so why bother them.
2025 If someone's root, then copy/move will always try to chown it... */
2026 (void) vpath;
2027 (void) owner;
2028 (void) group;
2029 return 0;
2030 #endif
2033 /* --------------------------------------------------------------------------------------------- */
2035 static int
2036 ftpfs_unlink (const vfs_path_t * vpath)
2038 return ftpfs_send_command (vpath, "DELE /%s", OPT_FLUSH);
2041 /* --------------------------------------------------------------------------------------------- */
2043 /* Return 1 if path is the same directory as the one we are in now */
2044 static int
2045 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
2047 (void) me;
2049 if (super->path_element->path == NULL)
2050 return FALSE;
2051 return (strcmp (path, super->path_element->path) == 0);
2054 /* --------------------------------------------------------------------------------------------- */
2056 static int
2057 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
2059 int r;
2060 char *p;
2062 if (!SUP->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
2063 return COMPLETE;
2065 p = ftpfs_translate_path (me, super, remote_path);
2066 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
2067 g_free (p);
2069 if (r != COMPLETE)
2070 ftpfs_errno = EIO;
2071 else
2073 g_free (super->path_element->path);
2074 super->path_element->path = g_strdup (remote_path);
2075 SUP->cwd_deferred = 0;
2077 return r;
2080 /* --------------------------------------------------------------------------------------------- */
2082 static int
2083 ftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
2085 ftpfs_send_command (vpath1, "RNFR /%s", OPT_FLUSH);
2086 return ftpfs_send_command (vpath2, "RNTO /%s", OPT_FLUSH);
2089 /* --------------------------------------------------------------------------------------------- */
2091 static int
2092 ftpfs_mkdir (const vfs_path_t * vpath, mode_t mode)
2094 (void) mode; /* FIXME: should be used */
2096 return ftpfs_send_command (vpath, "MKD /%s", OPT_FLUSH);
2099 /* --------------------------------------------------------------------------------------------- */
2101 static int
2102 ftpfs_rmdir (const vfs_path_t * vpath)
2104 return ftpfs_send_command (vpath, "RMD /%s", OPT_FLUSH);
2107 /* --------------------------------------------------------------------------------------------- */
2109 static void
2110 ftpfs_fh_free_data (vfs_file_handler_t * fh)
2112 if (fh != NULL)
2114 g_free (fh->data);
2115 fh->data = NULL;
2119 /* --------------------------------------------------------------------------------------------- */
2121 static int
2122 ftpfs_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
2124 ftp_fh_data_t *ftp;
2126 (void) mode;
2128 fh->data = g_new0 (ftp_fh_data_t, 1);
2129 ftp = (ftp_fh_data_t *) fh->data;
2130 /* File will be written only, so no need to retrieve it from ftp server */
2131 if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
2133 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2134 struct linger li;
2135 #else
2136 int li = 1;
2137 #endif
2138 char *name;
2140 /* ftpfs_linear_start() called, so data will be written
2141 * to local temporary file and stored to ftp server
2142 * by vfs_s_close later
2144 if (((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy)
2146 if (!fh->ino->localname)
2148 vfs_path_t *vpath;
2149 int handle;
2151 handle = vfs_mkstemps (&vpath, me->name, fh->ino->ent->name);
2152 if (handle == -1)
2154 vfs_path_free (vpath);
2155 goto fail;
2157 close (handle);
2158 fh->ino->localname = vfs_path_to_str (vpath);
2159 vfs_path_free (vpath);
2160 ftp->append = flags & O_APPEND;
2162 return 0;
2164 name = vfs_s_fullpath (me, fh->ino);
2165 if (name == NULL)
2166 goto fail;
2167 fh->handle =
2168 ftpfs_open_data_connection (me, fh->ino->super,
2169 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
2170 g_free (name);
2172 if (fh->handle < 0)
2173 goto fail;
2174 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2175 li.l_onoff = 1;
2176 li.l_linger = 120;
2177 #endif
2178 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
2180 if (fh->ino->localname)
2182 unlink (fh->ino->localname);
2183 g_free (fh->ino->localname);
2184 fh->ino->localname = NULL;
2186 return 0;
2189 if (!fh->ino->localname && vfs_s_retrieve_file (me, fh->ino) == -1)
2190 goto fail;
2191 if (!fh->ino->localname)
2192 vfs_die ("retrieve_file failed to fill in localname");
2193 return 0;
2195 fail:
2196 ftpfs_fh_free_data (fh);
2197 return -1;
2200 /* --------------------------------------------------------------------------------------------- */
2202 static int
2203 ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t * fh)
2205 if (fh->handle != -1 && !fh->ino->localname)
2207 ftp_super_data_t *ftp = (ftp_super_data_t *) fh->ino->super->data;
2209 close (fh->handle);
2210 fh->handle = -1;
2211 /* File is stored to destination already, so
2212 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2214 fh->changed = 0;
2215 if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE)
2216 ERRNOR (EIO, -1);
2217 vfs_s_invalidate (me, FH_SUPER);
2220 return 0;
2223 /* --------------------------------------------------------------------------------------------- */
2225 static void
2226 ftpfs_done (struct vfs_class *me)
2228 (void) me;
2230 g_slist_foreach (no_proxy, (GFunc) g_free, NULL);
2231 g_slist_free (no_proxy);
2233 g_free (ftpfs_anonymous_passwd);
2234 g_free (ftpfs_proxy_host);
2237 /* --------------------------------------------------------------------------------------------- */
2239 static void
2240 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
2242 GList *iter;
2244 for (iter = MEDATA->supers; iter != NULL; iter = g_list_next (iter))
2246 const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
2247 char *name;
2249 name =
2250 g_strconcat (vfs_ftpfs_ops.prefix, VFS_PATH_URL_DELIMITER, super->path_element->user,
2251 "@", super->path_element->host, "/", super->path_element->path,
2252 (char *) NULL);
2253 func (name);
2254 g_free (name);
2258 /* --------------------------------------------------------------------------------------------- */
2260 static keyword_t
2261 ftpfs_netrc_next (void)
2263 char *p;
2264 keyword_t i;
2265 static const char *const keywords[] = { "default", "machine",
2266 "login", "password", "passwd", "account", "macdef", NULL
2269 while (1)
2271 netrcp = skip_separators (netrcp);
2272 if (*netrcp != '\n')
2273 break;
2274 netrcp++;
2276 if (!*netrcp)
2277 return NETRC_NONE;
2278 p = buffer;
2279 if (*netrcp == '"')
2281 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++)
2283 if (*netrcp == '\\')
2284 netrcp++;
2285 *p++ = *netrcp;
2288 else
2290 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
2291 *netrcp != ',' && *netrcp; netrcp++)
2293 if (*netrcp == '\\')
2294 netrcp++;
2295 *p++ = *netrcp;
2298 *p = 0;
2299 if (!*buffer)
2300 return NETRC_NONE;
2302 for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2303 if (strcmp (keywords[i - 1], buffer) == 0)
2304 return i;
2306 return NETRC_UNKNOWN;
2309 /* --------------------------------------------------------------------------------------------- */
2311 static int
2312 ftpfs_netrc_bad_mode (const char *netrcname)
2314 static int be_angry = 1;
2315 struct stat mystat;
2317 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077))
2319 if (be_angry)
2321 message (D_ERROR, MSG_ERROR,
2322 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2323 be_angry = 0;
2325 return 1;
2327 return 0;
2330 /* --------------------------------------------------------------------------------------------- */
2331 /* Scan .netrc until we find matching "machine" or "default"
2332 * domain is used for additional matching
2333 * No search is done after "default" in compliance with "man netrc"
2334 * Return 0 if found, -1 otherwise */
2336 static int
2337 ftpfs_find_machine (const char *host, const char *domain)
2339 keyword_t keyword;
2341 if (!host)
2342 host = "";
2343 if (!domain)
2344 domain = "";
2346 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2348 if (keyword == NETRC_DEFAULT)
2349 return 0;
2351 if (keyword == NETRC_MACDEF)
2353 /* Scan for an empty line, which concludes "macdef" */
2356 while (*netrcp && *netrcp != '\n')
2357 netrcp++;
2358 if (*netrcp != '\n')
2359 break;
2360 netrcp++;
2362 while (*netrcp && *netrcp != '\n');
2363 continue;
2366 if (keyword != NETRC_MACHINE)
2367 continue;
2369 /* Take machine name */
2370 if (ftpfs_netrc_next () == NETRC_NONE)
2371 break;
2373 if (g_ascii_strcasecmp (host, buffer) != 0)
2375 /* Try adding our domain to short names in .netrc */
2376 const char *host_domain = strchr (host, '.');
2377 if (!host_domain)
2378 continue;
2380 /* Compare domain part */
2381 if (g_ascii_strcasecmp (host_domain, domain) != 0)
2382 continue;
2384 /* Compare local part */
2385 if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2386 continue;
2389 return 0;
2392 /* end of .netrc */
2393 return -1;
2396 /* --------------------------------------------------------------------------------------------- */
2397 /* Extract login and password from .netrc for the host.
2398 * pass may be NULL.
2399 * Returns 0 for success, -1 for error */
2401 static int
2402 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2404 char *netrcname;
2405 char *tmp_pass = NULL;
2406 char hostname[MAXHOSTNAMELEN];
2407 const char *domain;
2408 keyword_t keyword;
2409 static struct rupcache
2411 struct rupcache *next;
2412 char *host;
2413 char *login;
2414 char *pass;
2415 } *rup_cache = NULL, *rupp;
2417 /* Initialize *login and *pass */
2418 g_free (*login);
2419 *login = NULL;
2420 g_free (*pass);
2421 *pass = NULL;
2423 /* Look up in the cache first */
2424 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2426 if (!strcmp (host, rupp->host))
2428 if (rupp->login)
2429 *login = g_strdup (rupp->login);
2430 if (pass && rupp->pass)
2431 *pass = g_strdup (rupp->pass);
2432 return 0;
2436 /* Load current .netrc */
2437 netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2438 if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2440 g_free (netrcname);
2441 return 0;
2444 netrcp = netrc;
2446 /* Find our own domain name */
2447 if (gethostname (hostname, sizeof (hostname)) < 0)
2448 *hostname = '\0';
2450 domain = strchr (hostname, '.');
2451 if (domain == NULL)
2452 domain = "";
2454 /* Scan for "default" and matching "machine" keywords */
2455 ftpfs_find_machine (host, domain);
2457 /* Scan for keywords following "default" and "machine" */
2458 while (1)
2460 int need_break = 0;
2461 keyword = ftpfs_netrc_next ();
2463 switch (keyword)
2465 case NETRC_LOGIN:
2466 if (ftpfs_netrc_next () == NETRC_NONE)
2468 need_break = 1;
2469 break;
2472 /* We have another name already - should not happen */
2473 if (*login)
2475 need_break = 1;
2476 break;
2479 /* We have login name now */
2480 *login = g_strdup (buffer);
2481 break;
2483 case NETRC_PASSWORD:
2484 case NETRC_PASSWD:
2485 if (ftpfs_netrc_next () == NETRC_NONE)
2487 need_break = 1;
2488 break;
2491 /* Ignore unsafe passwords */
2492 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2493 && ftpfs_netrc_bad_mode (netrcname))
2495 need_break = 1;
2496 break;
2499 /* Remember password. pass may be NULL, so use tmp_pass */
2500 if (tmp_pass == NULL)
2501 tmp_pass = g_strdup (buffer);
2502 break;
2504 case NETRC_ACCOUNT:
2505 /* "account" is followed by a token which we ignore */
2506 if (ftpfs_netrc_next () == NETRC_NONE)
2508 need_break = 1;
2509 break;
2512 /* Ignore account, but warn user anyways */
2513 ftpfs_netrc_bad_mode (netrcname);
2514 break;
2516 default:
2517 /* Unexpected keyword or end of file */
2518 need_break = 1;
2519 break;
2522 if (need_break)
2523 break;
2526 g_free (netrc);
2527 g_free (netrcname);
2529 rupp = g_new (struct rupcache, 1);
2530 rupp->host = g_strdup (host);
2531 rupp->login = g_strdup (*login);
2532 rupp->pass = g_strdup (tmp_pass);
2534 rupp->next = rup_cache;
2535 rup_cache = rupp;
2537 *pass = tmp_pass;
2539 return 0;
2542 /* --------------------------------------------------------------------------------------------- */
2543 /*** public functions ****************************************************************************/
2544 /* --------------------------------------------------------------------------------------------- */
2546 /** This routine is called as the last step in load_setup */
2547 void
2548 ftpfs_init_passwd (void)
2550 ftpfs_anonymous_passwd = load_anon_passwd ();
2551 if (ftpfs_anonymous_passwd)
2552 return;
2554 /* If there is no anonymous ftp password specified
2555 * then we'll just use anonymous@
2556 * We don't send any other thing because:
2557 * - We want to remain anonymous
2558 * - We want to stop SPAM
2559 * - We don't want to let ftp sites to discriminate by the user,
2560 * host or country.
2562 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
2565 /* --------------------------------------------------------------------------------------------- */
2567 void
2568 init_ftpfs (void)
2570 static struct vfs_s_subclass ftpfs_subclass;
2572 tcp_init ();
2574 ftpfs_subclass.flags = VFS_S_REMOTE | VFS_S_USETMP;
2575 ftpfs_subclass.archive_same = ftpfs_archive_same;
2576 ftpfs_subclass.open_archive = ftpfs_open_archive;
2577 ftpfs_subclass.free_archive = ftpfs_free_archive;
2578 ftpfs_subclass.fh_open = ftpfs_fh_open;
2579 ftpfs_subclass.fh_close = ftpfs_fh_close;
2580 ftpfs_subclass.fh_free_data = ftpfs_fh_free_data;
2581 ftpfs_subclass.dir_load = ftpfs_dir_load;
2582 ftpfs_subclass.file_store = ftpfs_file_store;
2583 ftpfs_subclass.linear_start = ftpfs_linear_start;
2584 ftpfs_subclass.linear_read = ftpfs_linear_read;
2585 ftpfs_subclass.linear_close = ftpfs_linear_close;
2587 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2588 vfs_ftpfs_ops.name = "ftpfs";
2589 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2590 vfs_ftpfs_ops.prefix = "ftp";
2591 vfs_ftpfs_ops.done = &ftpfs_done;
2592 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2593 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2594 vfs_ftpfs_ops.chown = ftpfs_chown;
2595 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2596 vfs_ftpfs_ops.rename = ftpfs_rename;
2597 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2598 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2599 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2600 vfs_register_class (&vfs_ftpfs_ops);
2603 /* --------------------------------------------------------------------------------------------- */