b7e6625b123847543e0b7f9c4d12245e06a485e7
[midnight-commander.git] / src / vfs / ftpfs / ftpfs.c
blobb7e6625b123847543e0b7f9c4d12245e06a485e7
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 = mc_build_filename ( qhome (*bucket), remote_path +3-f, NULL );
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 char *current_dir;
223 } ftp_super_data_t;
225 typedef struct
227 int sock;
228 int append;
229 } ftp_fh_data_t;
231 /*** file scope variables ************************************************************************/
233 static int ftpfs_errno;
234 static int code;
236 #ifdef FIXME_LATER_ALIGATOR
237 static struct linklist *connections_list;
238 #endif
240 static char reply_str[80];
242 static struct vfs_class vfs_ftpfs_ops;
244 static GSList *no_proxy;
246 static char buffer[BUF_MEDIUM];
247 static char *netrc;
248 static const char *netrcp;
250 /*** file scope functions ************************************************************************/
251 /* --------------------------------------------------------------------------------------------- */
253 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
254 Translate a Unix path, i.e. MC's internal path representation (e.g.
255 /somedir/somefile) to a path valid for the remote server. Every path
256 transfered to the remote server has to be mangled by this function
257 right prior to sending it.
258 Currently only Amiga ftp servers are handled in a special manner.
260 When the remote server is an amiga:
261 a) strip leading slash if necesarry
262 b) replace first occurance of ":/" with ":"
263 c) strip trailing "/."
266 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
267 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super,
268 const char *remote_path);
269 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply,
270 const char *fmt, ...) __attribute__ ((format (__printf__, 4, 5)));
271 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
272 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
273 const char *netrcpass);
274 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
276 /* --------------------------------------------------------------------------------------------- */
278 static char *
279 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
281 if (!SUP->remote_is_amiga)
282 return g_strdup (remote_path);
283 else
285 char *ret, *p;
287 if (MEDATA->logfile)
289 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
290 fflush (MEDATA->logfile);
293 /* strip leading slash(es) */
294 while (*remote_path == '/')
295 remote_path++;
298 * Don't change "/" into "", e.g. "CWD " would be
299 * invalid.
301 if (*remote_path == '\0')
302 return g_strdup (".");
304 ret = g_strdup (remote_path);
306 /* replace first occurance of ":/" with ":" */
307 p = strchr (ret, ':');
308 if ((p != NULL) && (*(p + 1) == '/'))
309 memmove (p + 1, p + 2, strlen (p + 2) + 1);
311 /* strip trailing "/." */
312 p = strrchr (ret, '/');
313 if ((p != NULL) && (*(p + 1) == '.') && (*(p + 2) == '\0'))
314 *p = '\0';
316 return ret;
320 /* --------------------------------------------------------------------------------------------- */
321 /** Extract the hostname and username from the path */
323 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
324 * ftp://sunsite.unc.edu/pub/linux
325 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
326 * ftp://tsx-11.mit.edu:8192/
327 * ftp://joe@foo.edu:11321/private
328 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
329 * is supplied.
332 static vfs_path_element_t *
333 ftpfs_correct_url_parameters (const vfs_path_element_t * velement)
335 vfs_path_element_t *path_element = vfs_path_element_clone (velement);
337 if (path_element->port == 0)
338 path_element->port = FTP_COMMAND_PORT;
340 if (path_element->user == NULL)
342 /* Look up user and password in netrc */
343 if (ftpfs_use_netrc)
344 ftpfs_netrc_lookup (path_element->host, &path_element->user, &path_element->password);
346 if (path_element->user == NULL)
347 path_element->user = g_strdup ("anonymous");
349 /* Look up password in netrc for known user */
350 if (ftpfs_use_netrc && path_element->user != NULL && path_element->password != NULL)
352 char *new_user = NULL;
353 char *new_passwd = NULL;
355 ftpfs_netrc_lookup (path_element->host, &new_user, &new_passwd);
357 /* If user is different, remove password */
358 if (new_user != NULL && strcmp (path_element->user, new_user) != 0)
360 g_free (path_element->password);
361 path_element->password = NULL;
364 g_free (new_user);
365 g_free (new_passwd);
368 return path_element;
371 /* --------------------------------------------------------------------------------------------- */
372 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
374 static int
375 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
377 char answer[BUF_1K];
378 int i;
380 while (TRUE)
382 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
384 if (string_buf != NULL)
385 *string_buf = '\0';
386 code = 421;
387 return 4;
389 switch (sscanf (answer, "%d", &code))
391 case 0:
392 if (string_buf != NULL)
393 g_strlcpy (string_buf, answer, string_len);
394 code = 500;
395 return 5;
396 case 1:
397 if (answer[3] == '-')
399 while (TRUE)
401 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
403 if (string_buf != NULL)
404 *string_buf = '\0';
405 code = 421;
406 return 4;
408 if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' '))
409 break;
412 if (string_buf != NULL)
413 g_strlcpy (string_buf, answer, string_len);
414 return code / 100;
419 /* --------------------------------------------------------------------------------------------- */
421 static int
422 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
424 int sock;
426 sock = ftpfs_open_socket (me, super);
427 if (sock != -1)
429 char *cwdir = SUP->current_dir;
431 close (SUP->sock);
432 SUP->sock = sock;
433 SUP->current_dir = 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 SUP->current_dir = 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);
481 (void) ret;
484 fflush (MEDATA->logfile);
487 got_sigpipe = 0;
488 tty_enable_interrupt_key ();
489 status = write (SUP->sock, cmdstr, cmdlen);
491 if (status < 0)
493 code = 421;
495 if (errno == EPIPE)
496 { /* Remote server has closed connection */
497 if (level == 0)
499 level = 1;
500 status = ftpfs_reconnect (me, super);
501 level = 0;
502 if (status && (write (SUP->sock, cmdstr, cmdlen) > 0))
504 goto ok;
508 got_sigpipe = 1;
510 g_free (cmdstr);
511 tty_disable_interrupt_key ();
512 return TRANSIENT;
514 retry = 0;
516 tty_disable_interrupt_key ();
518 if (wait_reply)
520 status = ftpfs_get_reply (me, SUP->sock,
521 (wait_reply & WANT_STRING) ? reply_str : NULL,
522 sizeof (reply_str) - 1);
523 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
525 retry = 1;
526 level = 1;
527 status = ftpfs_reconnect (me, super);
528 level = 0;
529 if (status && (write (SUP->sock, cmdstr, cmdlen) > 0))
531 goto ok;
534 retry = 0;
535 g_free (cmdstr);
536 return status;
538 g_free (cmdstr);
539 return COMPLETE;
542 /* --------------------------------------------------------------------------------------------- */
544 static void
545 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
547 if (SUP->sock != -1)
549 vfs_print_message (_("ftpfs: Disconnecting from %s"), super->path_element->host);
550 ftpfs_command (me, super, NONE, "QUIT");
551 close (SUP->sock);
553 g_free (SUP->current_dir);
554 g_free (super->data);
555 super->data = NULL;
558 /* --------------------------------------------------------------------------------------------- */
560 static int
561 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
563 if (binary != SUP->isbinary)
565 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
566 ERRNOR (EIO, -1);
567 SUP->isbinary = binary;
569 return binary;
572 /* --------------------------------------------------------------------------------------------- */
573 /* This routine logs the user in */
575 static int
576 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
578 char *pass;
579 char *op;
580 char *name; /* login user name */
581 int anon = 0;
582 char reply_string[BUF_MEDIUM];
584 SUP->isbinary = TYPE_UNKNOWN;
586 if (super->path_element->password != NULL) /* explicit password */
587 op = g_strdup (super->path_element->password);
588 else if (netrcpass != NULL) /* password from netrc */
589 op = g_strdup (netrcpass);
590 else if (strcmp (super->path_element->user, "anonymous") == 0
591 || strcmp (super->path_element->user, "ftp") == 0)
593 if (ftpfs_anonymous_passwd == NULL) /* default anonymous password */
594 ftpfs_init_passwd ();
595 op = g_strdup (ftpfs_anonymous_passwd);
596 anon = 1;
598 else
599 { /* ask user */
600 char *p;
602 p = g_strdup_printf (_("FTP: Password required for %s"), super->path_element->user);
603 op = vfs_get_password (p);
604 g_free (p);
605 if (op == NULL)
606 ERRNOR (EPERM, 0);
607 super->path_element->password = g_strdup (op);
610 if (!anon || MEDATA->logfile)
611 pass = op;
612 else
614 pass = g_strconcat ("-", op, (char *) NULL);
615 wipe_password (op);
618 /* Proxy server accepts: username@host-we-want-to-connect */
619 if (SUP->proxy)
620 name =
621 g_strconcat (super->path_element->user, "@",
622 super->path_element->host[0] ==
623 '!' ? super->path_element->host + 1 : super->path_element->host,
624 (char *) NULL);
625 else
626 name = g_strdup (super->path_element->user);
628 if (ftpfs_get_reply (me, SUP->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
630 char *reply_up;
632 reply_up = g_ascii_strup (reply_string, -1);
633 SUP->remote_is_amiga = strstr (reply_up, "AMIGA") != 0;
634 if (strstr (reply_up, " SPFTP/1.0.0000 SERVER ")) /* handles `LIST -la` in a weird way */
635 SUP->strict = RFC_STRICT;
636 g_free (reply_up);
638 if (MEDATA->logfile)
640 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n", SUP->remote_is_amiga);
641 fflush (MEDATA->logfile);
644 vfs_print_message (_("ftpfs: sending login name"));
646 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
648 case CONTINUE:
649 vfs_print_message (_("ftpfs: sending user password"));
650 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
651 if (code == CONTINUE)
653 char *p;
655 p = g_strdup_printf (_("FTP: Account required for user %s"),
656 super->path_element->user);
657 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "",
658 INPUT_COMPLETE_DEFAULT);
659 g_free (p);
660 if (op == NULL)
661 ERRNOR (EPERM, 0);
662 vfs_print_message (_("ftpfs: sending user account"));
663 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
664 g_free (op);
666 if (code != COMPLETE)
667 break;
668 /* fall through */
670 case COMPLETE:
671 vfs_print_message (_("ftpfs: logged in"));
672 wipe_password (pass);
673 g_free (name);
674 return 1;
676 default:
677 SUP->failed_on_login = 1;
678 wipe_password (super->path_element->password);
679 super->path_element->password = NULL;
681 goto login_fail;
685 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
686 super->path_element->user);
688 login_fail:
689 wipe_password (pass);
690 g_free (name);
691 ERRNOR (EPERM, 0);
694 /* --------------------------------------------------------------------------------------------- */
696 static void
697 ftpfs_load_no_proxy_list (void)
699 /* FixMe: shouldn't be hardcoded!!! */
700 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
701 FILE *npf;
702 int c;
703 char *p;
704 static char *mc_file = NULL;
706 mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL);
707 if (exist_file (mc_file))
709 npf = fopen (mc_file, "r");
710 if (npf != NULL)
712 while (fgets (s, sizeof (s), npf) != NULL)
714 p = strchr (s, '\n');
715 if (p == NULL) /* skip bogus entries */
717 while ((c = fgetc (npf)) != EOF && c != '\n')
719 continue;
722 if (p != s)
724 *p = '\0';
725 no_proxy = g_slist_prepend (no_proxy, g_strdup (s));
728 fclose (npf);
731 g_free (mc_file);
734 /* --------------------------------------------------------------------------------------------- */
735 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
737 static int
738 ftpfs_check_proxy (const char *host)
740 GSList *npe;
742 if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0')
743 return 0; /* sanity check */
745 if (*host == '!')
746 return 1;
748 if (!ftpfs_always_use_proxy)
749 return 0;
751 if (strchr (host, '.') == NULL)
752 return 0;
754 ftpfs_load_no_proxy_list ();
755 for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe))
757 const char *domain = (const char *) npe->data;
759 if (domain[0] == '.')
761 size_t ld = strlen (domain);
762 size_t lh = strlen (host);
764 while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1])
766 ld--;
767 lh--;
770 if (ld == 0)
771 return 0;
773 else if (g_ascii_strcasecmp (host, domain) == 0)
774 return 0;
777 return 1;
780 /* --------------------------------------------------------------------------------------------- */
782 static void
783 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
785 vfs_path_element_t *path_element;
787 path_element = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
788 *host = g_strdup (path_element->host);
789 *port = path_element->port;
790 vfs_path_element_free (path_element);
793 /* --------------------------------------------------------------------------------------------- */
795 static int
796 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
798 struct addrinfo hints, *res, *curr_res;
799 int my_socket = 0;
800 char *host = NULL;
801 char port[8];
802 int tmp_port;
803 int e;
805 (void) me;
807 /* Use a proxy host? */
808 host = g_strdup (super->path_element->host);
810 if (host == NULL || *host == '\0')
812 vfs_print_message (_("ftpfs: Invalid host name."));
813 ftpfs_errno = EINVAL;
814 g_free (host);
815 return -1;
818 /* Hosts to connect to that start with a ! should use proxy */
819 tmp_port = super->path_element->port;
821 if (SUP->proxy != NULL)
822 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
824 g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_port);
825 if (port[0] == '\0')
827 g_free (host);
828 ftpfs_errno = errno;
829 return -1;
832 tty_enable_interrupt_key (); /* clear the interrupt flag */
834 memset (&hints, 0, sizeof (struct addrinfo));
835 hints.ai_family = AF_UNSPEC;
836 hints.ai_socktype = SOCK_STREAM;
838 #ifdef AI_ADDRCONFIG
839 /* By default, only look up addresses using address types for
840 * which a local interface is configured (i.e. no IPv6 if no IPv6
841 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
842 hints.ai_flags = AI_ADDRCONFIG;
843 #endif
845 e = getaddrinfo (host, port, &hints, &res);
847 #ifdef AI_ADDRCONFIG
848 if (e == EAI_BADFLAGS)
850 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
851 hints.ai_flags = 0;
852 e = getaddrinfo (host, port, &hints, &res);
854 #endif
856 *port = '\0';
858 if (e != 0)
860 tty_disable_interrupt_key ();
861 vfs_print_message (_("ftpfs: %s"), gai_strerror (e));
862 g_free (host);
863 ftpfs_errno = EINVAL;
864 return -1;
867 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
869 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
871 if (my_socket < 0)
873 if (curr_res->ai_next != NULL)
874 continue;
876 tty_disable_interrupt_key ();
877 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno));
878 g_free (host);
879 freeaddrinfo (res);
880 ftpfs_errno = errno;
881 return -1;
884 vfs_print_message (_("ftpfs: making connection to %s"), host);
885 g_free (host);
886 host = NULL;
888 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
889 break;
891 ftpfs_errno = errno;
892 close (my_socket);
894 if (errno == EINTR && tty_got_interrupt ())
895 vfs_print_message (_("ftpfs: connection interrupted by user"));
896 else if (res->ai_next == NULL)
897 vfs_print_message (_("ftpfs: connection to server failed: %s"),
898 unix_error_string (errno));
899 else
900 continue;
902 freeaddrinfo (res);
903 tty_disable_interrupt_key ();
904 return -1;
907 freeaddrinfo (res);
908 tty_disable_interrupt_key ();
909 return my_socket;
912 /* --------------------------------------------------------------------------------------------- */
914 static int
915 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
917 int retry_seconds = 0;
918 int count_down;
920 /* We do not want to use the passive if we are using proxies */
921 if (SUP->proxy)
922 SUP->use_passive_connection = ftpfs_use_passive_connections_over_proxy;
926 SUP->failed_on_login = 0;
928 SUP->sock = ftpfs_open_socket (me, super);
929 if (SUP->sock == -1)
930 return -1;
932 if (ftpfs_login_server (me, super, NULL) != 0)
934 /* Logged in, no need to retry the connection */
935 break;
937 else
939 if (!SUP->failed_on_login)
940 return -1;
942 /* Close only the socket descriptor */
943 close (SUP->sock);
945 if (ftpfs_retry_seconds != 0)
947 retry_seconds = ftpfs_retry_seconds;
948 tty_enable_interrupt_key ();
949 for (count_down = retry_seconds; count_down; count_down--)
951 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
952 count_down);
953 sleep (1);
954 if (tty_got_interrupt ())
956 /* ftpfs_errno = E; */
957 tty_disable_interrupt_key ();
958 return 0;
961 tty_disable_interrupt_key ();
965 while (retry_seconds != 0);
967 SUP->current_dir = ftpfs_get_current_directory (me, super);
968 if (SUP->current_dir == NULL)
969 SUP->current_dir = g_strdup (PATH_SEP_STR);
971 return 0;
974 /* --------------------------------------------------------------------------------------------- */
976 static int
977 ftpfs_open_archive (struct vfs_s_super *super,
978 const vfs_path_t * vpath, const vfs_path_element_t * vpath_element)
980 (void) vpath;
982 super->data = g_new0 (ftp_super_data_t, 1);
984 super->path_element = ftpfs_correct_url_parameters (vpath_element);
985 SUP->proxy = NULL;
986 if (ftpfs_check_proxy (super->path_element->host))
987 SUP->proxy = ftpfs_proxy_host;
988 SUP->use_passive_connection = ftpfs_use_passive_connections;
989 SUP->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
990 SUP->isbinary = TYPE_UNKNOWN;
991 SUP->remote_is_amiga = 0;
992 super->name = g_strdup ("/");
993 super->root =
994 vfs_s_new_inode (vpath_element->class, super,
995 vfs_s_default_stat (vpath_element->class, S_IFDIR | 0755));
997 return ftpfs_open_archive_int (vpath_element->class, super);
1000 /* --------------------------------------------------------------------------------------------- */
1002 static int
1003 ftpfs_archive_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *super,
1004 const vfs_path_t * vpath, void *cookie)
1006 vfs_path_element_t *path_element;
1007 int result;
1009 (void) vpath;
1010 (void) cookie;
1012 path_element = ftpfs_correct_url_parameters (vpath_element);
1014 result = ((strcmp (path_element->host, super->path_element->host) == 0)
1015 && (strcmp (path_element->user, super->path_element->user) == 0)
1016 && (path_element->port == super->path_element->port)) ? 1 : 0;
1018 vfs_path_element_free (path_element);
1019 return result;
1022 /* --------------------------------------------------------------------------------------------- */
1023 /* The returned directory should always contain a trailing slash */
1025 static char *
1026 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
1028 char buf[MC_MAXPATHLEN + 1];
1030 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
1031 ftpfs_get_reply (me, SUP->sock, buf, sizeof (buf)) == COMPLETE)
1033 char *bufp = NULL;
1034 char *bufq;
1036 for (bufq = buf; *bufq != '\0'; bufq++)
1037 if (*bufq == '"')
1039 if (bufp == NULL)
1040 bufp = bufq + 1;
1041 else
1043 *bufq = '\0';
1045 if (*bufp != '\0')
1047 if (*(bufq - 1) != '/')
1049 *bufq++ = '/';
1050 *bufq = '\0';
1053 if (*bufp == '/')
1054 return g_strdup (bufp);
1056 /* If the remote server is an Amiga a leading slash
1057 might be missing. MC needs it because it is used
1058 as separator between hostname and path internally. */
1059 return g_strconcat ("/", bufp, (char *) NULL);
1062 break;
1067 ftpfs_errno = EIO;
1068 return NULL;
1071 /* --------------------------------------------------------------------------------------------- */
1072 /* Setup Passive PASV FTP connection */
1074 static int
1075 ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
1076 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1078 char *c;
1079 char n[6];
1080 int xa, xb, xc, xd, xe, xf;
1082 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
1083 return 0;
1085 /* Parse remote parameters */
1086 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
1088 if (!*c)
1089 return 0;
1090 if (!isdigit ((unsigned char) *c))
1091 return 0;
1092 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1093 return 0;
1095 n[0] = (unsigned char) xa;
1096 n[1] = (unsigned char) xb;
1097 n[2] = (unsigned char) xc;
1098 n[3] = (unsigned char) xd;
1099 n[4] = (unsigned char) xe;
1100 n[5] = (unsigned char) xf;
1102 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1103 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1105 if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
1106 return 0;
1108 return 1;
1111 /* --------------------------------------------------------------------------------------------- */
1112 /* Setup Passive EPSV FTP connection */
1114 static int
1115 ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
1116 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1118 char *c;
1119 int port;
1121 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") != COMPLETE)
1122 return 0;
1124 /* (|||<port>|) */
1125 c = strchr (reply_str, '|');
1126 if (c == NULL)
1127 return 0;
1128 if (strlen (c) > 3)
1129 c += 3;
1130 else
1131 return 0;
1133 port = atoi (c);
1134 if (port < 0 || port > 65535)
1135 return 0;
1136 port = htons (port);
1138 switch (sa->ss_family)
1140 case AF_INET:
1141 ((struct sockaddr_in *) sa)->sin_port = port;
1142 break;
1143 case AF_INET6:
1144 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1145 break;
1148 return (connect (my_socket, (struct sockaddr *) sa, *salen) < 0) ? 0 : 1;
1151 /* --------------------------------------------------------------------------------------------- */
1152 /* Setup Passive ftp connection, we use it for source routed connections */
1154 static int
1155 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
1156 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1158 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1159 if (sa->ss_family == AF_INET)
1161 if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
1162 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1163 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1164 return 0;
1166 /* It's IPV6, so EPSV is our only hope */
1167 else
1169 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1170 return 0;
1173 return 1;
1176 /* --------------------------------------------------------------------------------------------- */
1177 /* Setup Active PORT or EPRT FTP connection */
1179 static int
1180 ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super,
1181 struct sockaddr_storage data_addr, socklen_t data_addrlen)
1183 unsigned short int port;
1184 char *addr;
1185 unsigned int af;
1187 switch (data_addr.ss_family)
1189 case AF_INET:
1190 af = FTP_INET;
1191 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1192 break;
1193 case AF_INET6:
1194 af = FTP_INET6;
1195 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1196 break;
1197 /* Not implemented */
1198 default:
1199 return 0;
1202 addr = g_try_malloc (NI_MAXHOST);
1203 if (addr == NULL)
1204 ERRNOR (ENOMEM, -1);
1206 if (getnameinfo
1207 ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1208 NI_NUMERICHOST) != 0)
1210 g_free (addr);
1211 ERRNOR (EIO, -1);
1214 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1215 if (af == FTP_INET)
1217 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1218 unsigned char *p = (unsigned char *) &port;
1220 if (ftpfs_command (me, super, WAIT_REPLY,
1221 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1222 p[0], p[1]) == COMPLETE)
1224 g_free (addr);
1225 return 1;
1230 * Converts network MSB first order to host byte order (LSB
1231 * first on i386). If we do it earlier, we will run into an
1232 * endianness issue, because the server actually expects to see
1233 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1235 port = ntohs (port);
1237 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1238 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
1240 g_free (addr);
1241 return 1;
1244 g_free (addr);
1245 return 0;
1248 /* --------------------------------------------------------------------------------------------- */
1249 /* Initialize a socket for FTP DATA connection */
1251 static int
1252 ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super,
1253 struct sockaddr_storage *data_addr, socklen_t * data_addrlen)
1255 int result;
1257 memset (data_addr, 0, sizeof (struct sockaddr_storage));
1258 *data_addrlen = sizeof (struct sockaddr_storage);
1260 if (SUP->use_passive_connection)
1261 result = getpeername (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1262 else
1263 result = getsockname (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1265 if (result == -1)
1266 return -1;
1268 switch (data_addr->ss_family)
1270 case AF_INET:
1271 ((struct sockaddr_in *) data_addr)->sin_port = 0;
1272 break;
1273 case AF_INET6:
1274 ((struct sockaddr_in6 *) data_addr)->sin6_port = 0;
1275 break;
1276 default:
1277 vfs_print_message (_("ftpfs: invalid address family"));
1278 ERRNOR (EINVAL, -1);
1281 result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP);
1283 if (result < 0)
1285 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1286 return -1;
1289 return result;
1292 /* --------------------------------------------------------------------------------------------- */
1293 /* Initialize FTP DATA connection */
1295 static int
1296 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1298 struct sockaddr_storage data_addr;
1299 socklen_t data_addrlen;
1302 * Don't factor socket initialization out of these conditionals,
1303 * because ftpfs_init_data_socket initializes it in different way
1304 * depending on use_passive_connection flag.
1307 /* Try to establish a passive connection first (if requested) */
1308 if (SUP->use_passive_connection)
1310 int data_sock;
1312 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1313 if (data_sock < 0)
1314 return -1;
1316 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1317 return data_sock;
1319 vfs_print_message (_("ftpfs: could not setup passive mode"));
1320 SUP->use_passive_connection = 0;
1322 close (data_sock);
1325 /* If passive setup is diabled or failed, fallback to active connections */
1326 if (!SUP->use_passive_connection)
1328 int data_sock;
1330 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1331 if (data_sock < 0)
1332 return -1;
1334 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) == 0) &&
1335 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) == 0) &&
1336 (listen (data_sock, 1) == 0) &&
1337 (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0))
1338 return data_sock;
1340 close (data_sock);
1343 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1344 SUP->use_passive_connection = SUP->proxy != NULL ? ftpfs_use_passive_connections_over_proxy :
1345 ftpfs_use_passive_connections;
1347 ftpfs_errno = EIO;
1348 return -1;
1351 /* --------------------------------------------------------------------------------------------- */
1353 static int
1354 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1355 const char *remote, int isbinary, int reget)
1357 struct sockaddr_storage from;
1358 int s, j, data;
1359 socklen_t fromlen = sizeof (from);
1361 s = ftpfs_initconn (me, super);
1362 if (s == -1)
1363 return -1;
1365 if (ftpfs_changetype (me, super, isbinary) == -1)
1366 return -1;
1367 if (reget > 0)
1369 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1370 if (j != CONTINUE)
1371 return -1;
1373 if (remote)
1375 char *remote_path = ftpfs_translate_path (me, super, remote);
1376 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1377 /* WarFtpD can't STORE //filename */
1378 (*remote_path == '/') ? remote_path + 1 : remote_path);
1379 g_free (remote_path);
1381 else
1382 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1384 if (j != PRELIM)
1385 ERRNOR (EPERM, -1);
1386 tty_enable_interrupt_key ();
1387 if (SUP->use_passive_connection)
1388 data = s;
1389 else
1391 data = accept (s, (struct sockaddr *) &from, &fromlen);
1392 if (data < 0)
1394 ftpfs_errno = errno;
1395 close (s);
1396 return -1;
1398 close (s);
1400 tty_disable_interrupt_key ();
1401 return data;
1404 /* --------------------------------------------------------------------------------------------- */
1406 static void
1407 ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t * fh)
1409 struct vfs_s_super *super = FH_SUPER;
1410 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1411 fd_set mask;
1412 char buf[BUF_8K];
1413 int dsock = FH_SOCK;
1414 FH_SOCK = -1;
1415 SUP->ctl_connection_busy = 0;
1417 vfs_print_message (_("ftpfs: aborting transfer."));
1418 if (send (SUP->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1420 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1421 if (dsock != -1)
1422 close (dsock);
1423 return;
1426 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1428 vfs_print_message (_("ftpfs: abort failed"));
1429 if (dsock != -1)
1430 close (dsock);
1431 return;
1433 if (dsock != -1)
1435 FD_ZERO (&mask);
1436 FD_SET (dsock, &mask);
1437 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1439 struct timeval start_tim, tim;
1440 gettimeofday (&start_tim, NULL);
1441 /* flush the remaining data */
1442 while (read (dsock, buf, sizeof (buf)) > 0)
1444 gettimeofday (&tim, NULL);
1445 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT)
1447 /* server keeps sending, drop the connection and ftpfs_reconnect */
1448 close (dsock);
1449 ftpfs_reconnect (me, super);
1450 return;
1454 close (dsock);
1456 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) == TRANSIENT) && (code == 426))
1457 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1460 /* --------------------------------------------------------------------------------------------- */
1462 #if 0
1463 static void
1464 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1465 struct vfs_s_inode *dir)
1467 struct linklist *flist;
1468 struct direntry *fe, *fel;
1469 char tmp[MC_MAXPATHLEN];
1470 int depth;
1472 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1473 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1475 /* flist->data->l_stat is alread initialized with 0 */
1476 fel = flist->data;
1477 if (S_ISLNK (fel->s.st_mode) && fel->linkname)
1479 if (fel->linkname[0] == '/')
1481 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1482 continue;
1483 strcpy (tmp, fel->linkname);
1485 else
1487 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1488 continue;
1489 strcpy (tmp, dir->remote_path);
1490 if (tmp[1] != '\0')
1491 strcat (tmp, "/");
1492 strcat (tmp + 1, fel->linkname);
1494 for (depth = 0; depth < 100; depth++)
1495 { /* depth protects against recursive symbolic links */
1496 canonicalize_pathname (tmp);
1497 fe = _get_file_entry (bucket, tmp, 0, 0);
1498 if (fe)
1500 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1502 /* Symlink points to link which isn't resolved, yet. */
1503 if (fe->linkname[0] == '/')
1505 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1506 break;
1507 strcpy (tmp, fe->linkname);
1509 else
1511 /* at this point tmp looks always like this
1512 /directory/filename, i.e. no need to check
1513 strrchr's return value */
1514 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1515 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1516 break;
1517 strcat (tmp, fe->linkname);
1519 continue;
1521 else
1523 fel->l_stat = g_new (struct stat, 1);
1524 if (S_ISLNK (fe->s.st_mode))
1525 *fel->l_stat = *fe->l_stat;
1526 else
1527 *fel->l_stat = fe->s;
1528 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1531 break;
1535 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1538 /* --------------------------------------------------------------------------------------------- */
1540 static void
1541 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1542 struct vfs_s_inode *dir)
1544 char buffer[2048] = "", *filename;
1545 int sock;
1546 FILE *fp;
1547 struct stat s;
1548 struct linklist *flist;
1549 struct direntry *fe;
1550 int switch_method = 0;
1552 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1553 if (strchr (dir->remote_path, ' '))
1555 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1557 vfs_print_message (_("ftpfs: CWD failed."));
1558 return;
1560 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1562 else
1563 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1565 if (sock == -1)
1567 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1568 return;
1571 fp = fdopen (sock, "r");
1572 if (fp == NULL)
1574 close (sock);
1575 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1576 return;
1578 tty_enable_interrupt_key ();
1579 flist = dir->file_list->next;
1580 while (1)
1584 if (flist == dir->file_list)
1585 goto done;
1586 fe = flist->data;
1587 flist = flist->next;
1589 while (!S_ISLNK (fe->s.st_mode));
1590 while (1)
1592 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1593 goto done;
1594 if (MEDATA->logfile)
1596 fputs (buffer, MEDATA->logfile);
1597 fflush (MEDATA->logfile);
1599 vfs_die ("This code should be commented out\n");
1600 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1602 int r = strcmp (fe->name, filename);
1603 g_free (filename);
1604 if (r == 0)
1606 if (S_ISLNK (s.st_mode))
1608 /* This server doesn't understand LIST -lLa */
1609 switch_method = 1;
1610 goto done;
1612 fe->l_stat = g_new (struct stat, 1);
1613 if (fe->l_stat == NULL)
1614 goto done;
1615 *fe->l_stat = s;
1616 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1617 break;
1619 if (r < 0)
1620 break;
1624 done:
1625 while (fgets (buffer, sizeof (buffer), fp) != NULL);
1626 tty_disable_interrupt_key ();
1627 fclose (fp);
1628 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1631 /* --------------------------------------------------------------------------------------------- */
1633 static void
1634 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1636 vfs_print_message (_("Resolving symlink..."));
1638 if (SUP->strict_rfc959_list_cmd)
1639 resolve_symlink_without_ls_options (me, super, dir);
1640 else
1641 resolve_symlink_with_ls_options (me, super, dir);
1643 #endif
1645 /* --------------------------------------------------------------------------------------------- */
1647 static int
1648 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1650 struct vfs_s_entry *ent;
1651 struct vfs_s_super *super = dir->super;
1652 int sock, num_entries = 0;
1653 char lc_buffer[BUF_8K];
1654 int cd_first;
1656 cd_first = ftpfs_first_cd_then_ls || (SUP->strict == RFC_STRICT)
1657 || (strchr (remote_path, ' ') != NULL);
1659 again:
1660 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1661 remote_path,
1662 SUP->strict ==
1663 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1665 if (cd_first)
1667 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1669 ftpfs_errno = ENOENT;
1670 vfs_print_message (_("ftpfs: CWD failed."));
1671 return -1;
1675 gettimeofday (&dir->timestamp, NULL);
1676 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1678 if (SUP->strict == RFC_STRICT)
1679 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1680 else if (cd_first)
1681 /* Dirty hack to avoid autoprepending / to . */
1682 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1683 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1684 else
1686 char *path;
1688 /* Trailing "/." is necessary if remote_path is a symlink */
1689 path = mc_build_filename (remote_path, ".", NULL);
1690 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1691 g_free (path);
1694 if (sock == -1)
1695 goto fallback;
1697 /* Clear the interrupt flag */
1698 tty_enable_interrupt_key ();
1700 vfs_parse_ls_lga_init ();
1701 while (1)
1703 int i;
1704 size_t count_spaces = 0;
1705 int res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer),
1706 sock);
1707 if (!res)
1708 break;
1710 if (res == EINTR)
1712 me->verrno = ECONNRESET;
1713 close (sock);
1714 tty_disable_interrupt_key ();
1715 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1716 vfs_print_message (_("%s: failure"), me->name);
1717 return -1;
1720 if (MEDATA->logfile)
1722 fputs (lc_buffer, MEDATA->logfile);
1723 fputs ("\n", MEDATA->logfile);
1724 fflush (MEDATA->logfile);
1727 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1728 i = ent->ino->st.st_nlink;
1729 if (!vfs_parse_ls_lga
1730 (lc_buffer, &ent->ino->st, &ent->name, &ent->ino->linkname, &count_spaces))
1732 vfs_s_free_entry (me, ent);
1733 continue;
1735 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1736 num_entries++;
1737 vfs_s_store_filename_leading_spaces (ent, count_spaces);
1738 vfs_s_insert_entry (me, dir, ent);
1741 close (sock);
1742 me->verrno = E_REMOTE;
1743 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1744 goto fallback;
1746 if (num_entries == 0 && cd_first == 0)
1748 /* The LIST command may produce an empty output. In such scenario
1749 it is not clear whether this is caused by `remote_path' being
1750 a non-existent path or for some other reason (listing emtpy
1751 directory without the -a option, non-readable directory, etc.).
1753 Since `dir_load' is a crucial method, when it comes to determine
1754 whether a given path is a _directory_, the code must try its best
1755 to determine the type of `remote_path'. The only reliable way to
1756 achieve this is trough issuing a CWD command. */
1758 cd_first = 1;
1759 goto again;
1762 vfs_s_normalize_filename_leading_spaces (dir, vfs_parse_ls_lga_get_final_spaces ());
1764 if (SUP->strict == RFC_AUTODETECT)
1765 SUP->strict = RFC_DARING;
1767 vfs_print_message (_("%s: done."), me->name);
1768 return 0;
1770 fallback:
1771 if (SUP->strict == RFC_AUTODETECT)
1773 /* It's our first attempt to get a directory listing from this
1774 server (UNIX style LIST command) */
1775 SUP->strict = RFC_STRICT;
1776 /* I hate goto, but recursive call needs another 8K on stack */
1777 /* return ftpfs_dir_load (me, dir, remote_path); */
1778 cd_first = 1;
1779 goto again;
1781 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1782 ERRNOR (EACCES, -1);
1785 /* --------------------------------------------------------------------------------------------- */
1787 static int
1788 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname)
1790 int h, sock;
1791 off_t n_stored;
1792 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1793 struct linger li;
1794 #else
1795 int flag_one = 1;
1796 #endif
1797 char lc_buffer[BUF_8K];
1798 struct stat s;
1799 char *w_buf;
1800 struct vfs_s_super *super = FH_SUPER;
1801 ftp_fh_data_t *ftp = (ftp_fh_data_t *) fh->data;
1803 h = open (localname, O_RDONLY);
1804 if (h == -1)
1805 ERRNOR (EIO, -1);
1807 sock =
1808 ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1809 if (sock < 0 || fstat (h, &s) == -1)
1811 close (h);
1812 return -1;
1814 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1815 li.l_onoff = 1;
1816 li.l_linger = 120;
1817 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1818 #else
1819 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1820 #endif
1821 n_stored = 0;
1823 tty_enable_interrupt_key ();
1824 while (TRUE)
1826 ssize_t n_read, n_written;
1828 while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1830 if (errno != EINTR)
1832 ftpfs_errno = errno;
1833 goto error_return;
1835 if (tty_got_interrupt ())
1837 ftpfs_errno = EINTR;
1838 goto error_return;
1841 if (n_read == 0)
1842 break;
1843 n_stored += n_read;
1844 w_buf = lc_buffer;
1845 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1847 if (n_written == -1)
1849 if (errno == EINTR && !tty_got_interrupt ())
1850 continue;
1852 ftpfs_errno = errno;
1853 goto error_return;
1855 w_buf += n_written;
1856 n_read -= n_written;
1858 vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
1859 _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
1861 tty_disable_interrupt_key ();
1862 close (sock);
1863 close (h);
1864 if (ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE)
1865 ERRNOR (EIO, -1);
1866 return 0;
1867 error_return:
1868 tty_disable_interrupt_key ();
1869 close (sock);
1870 close (h);
1871 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1872 return -1;
1875 /* --------------------------------------------------------------------------------------------- */
1877 static int
1878 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset)
1880 char *name;
1882 if (fh->data == NULL)
1883 fh->data = g_new0 (ftp_fh_data_t, 1);
1885 name = vfs_s_fullpath (me, fh->ino);
1886 if (name == NULL)
1887 return 0;
1888 FH_SOCK = ftpfs_open_data_connection (me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1889 g_free (name);
1890 if (FH_SOCK == -1)
1891 ERRNOR (EACCES, 0);
1892 fh->linear = LS_LINEAR_OPEN;
1893 ((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy = 1;
1894 ((ftp_fh_data_t *) fh->data)->append = 0;
1895 return 1;
1898 /* --------------------------------------------------------------------------------------------- */
1900 static ssize_t
1901 ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t * fh, void *buf, size_t len)
1903 ssize_t n;
1904 struct vfs_s_super *super = FH_SUPER;
1906 while ((n = read (FH_SOCK, buf, len)) < 0)
1908 if ((errno == EINTR) && !tty_got_interrupt ())
1909 continue;
1910 break;
1913 if (n < 0)
1914 ftpfs_linear_abort (me, fh);
1916 if (n == 0)
1918 SUP->ctl_connection_busy = 0;
1919 close (FH_SOCK);
1920 FH_SOCK = -1;
1921 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1922 ERRNOR (E_REMOTE, -1);
1923 return 0;
1925 ERRNOR (errno, n);
1928 /* --------------------------------------------------------------------------------------------- */
1930 static void
1931 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t * fh)
1933 if (FH_SOCK != -1)
1934 ftpfs_linear_abort (me, fh);
1937 /* --------------------------------------------------------------------------------------------- */
1939 static int
1940 ftpfs_ctl (void *fh, int ctlop, void *arg)
1942 (void) arg;
1944 switch (ctlop)
1946 case VFS_CTL_IS_NOTREADY:
1948 int v;
1950 if (!FH->linear)
1951 vfs_die ("You may not do this");
1952 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1953 return 0;
1955 v = vfs_s_select_on_two (((ftp_fh_data_t *) (FH->data))->sock, 0);
1956 return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
1958 default:
1959 return 0;
1963 /* --------------------------------------------------------------------------------------------- */
1965 static int
1966 ftpfs_send_command (const vfs_path_t * vpath, const char *cmd, int flags)
1968 const char *rpath;
1969 char *p;
1970 struct vfs_s_super *super;
1971 int r;
1972 const vfs_path_element_t *path_element;
1973 int flush_directory_cache = (flags & OPT_FLUSH);
1975 path_element = vfs_path_get_by_index (vpath, -1);
1977 rpath = vfs_s_get_path (vpath, &super, 0);
1978 if (rpath == NULL)
1979 return -1;
1981 p = ftpfs_translate_path (path_element->class, super, rpath);
1982 r = ftpfs_command (path_element->class, super, WAIT_REPLY, cmd, p);
1983 g_free (p);
1984 vfs_stamp_create (&vfs_ftpfs_ops, super);
1985 if (flags & OPT_IGNORE_ERROR)
1986 r = COMPLETE;
1987 if (r != COMPLETE)
1989 path_element->class->verrno = EPERM;
1990 return -1;
1992 if (flush_directory_cache)
1993 vfs_s_invalidate (path_element->class, super);
1994 return 0;
1997 /* --------------------------------------------------------------------------------------------- */
1999 static int
2000 ftpfs_chmod (const vfs_path_t * vpath, mode_t mode)
2002 char buf[BUF_SMALL];
2003 int ret;
2005 g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", (int) (mode & 07777));
2007 ret = ftpfs_send_command (vpath, buf, OPT_FLUSH);
2009 return ftpfs_ignore_chattr_errors ? 0 : ret;
2012 /* --------------------------------------------------------------------------------------------- */
2014 static int
2015 ftpfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
2017 #if 0
2018 (void) vpath;
2019 (void) owner;
2020 (void) group;
2022 ftpfs_errno = EPERM;
2023 return -1;
2024 #else
2025 /* Everyone knows it is not possible to chown remotely, so why bother them.
2026 If someone's root, then copy/move will always try to chown it... */
2027 (void) vpath;
2028 (void) owner;
2029 (void) group;
2030 return 0;
2031 #endif
2034 /* --------------------------------------------------------------------------------------------- */
2036 static int
2037 ftpfs_unlink (const vfs_path_t * vpath)
2039 return ftpfs_send_command (vpath, "DELE /%s", OPT_FLUSH);
2042 /* --------------------------------------------------------------------------------------------- */
2044 /* Return 1 if path is the same directory as the one we are in now */
2045 static int
2046 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
2048 (void) me;
2050 if (SUP->current_dir == NULL)
2051 return FALSE;
2052 return (strcmp (path, SUP->current_dir) == 0);
2055 /* --------------------------------------------------------------------------------------------- */
2057 static int
2058 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
2060 int r;
2061 char *p;
2063 if (!SUP->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
2064 return COMPLETE;
2066 p = ftpfs_translate_path (me, super, remote_path);
2067 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
2068 g_free (p);
2070 if (r != COMPLETE)
2071 ftpfs_errno = EIO;
2072 else
2074 g_free (SUP->current_dir);
2075 SUP->current_dir = g_strdup (remote_path);
2076 SUP->cwd_deferred = 0;
2078 return r;
2081 /* --------------------------------------------------------------------------------------------- */
2083 static int
2084 ftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
2086 ftpfs_send_command (vpath1, "RNFR /%s", OPT_FLUSH);
2087 return ftpfs_send_command (vpath2, "RNTO /%s", OPT_FLUSH);
2090 /* --------------------------------------------------------------------------------------------- */
2092 static int
2093 ftpfs_mkdir (const vfs_path_t * vpath, mode_t mode)
2095 (void) mode; /* FIXME: should be used */
2097 return ftpfs_send_command (vpath, "MKD /%s", OPT_FLUSH);
2100 /* --------------------------------------------------------------------------------------------- */
2102 static int
2103 ftpfs_rmdir (const vfs_path_t * vpath)
2105 return ftpfs_send_command (vpath, "RMD /%s", OPT_FLUSH);
2108 /* --------------------------------------------------------------------------------------------- */
2110 static void
2111 ftpfs_fh_free_data (vfs_file_handler_t * fh)
2113 if (fh != NULL)
2115 g_free (fh->data);
2116 fh->data = NULL;
2120 /* --------------------------------------------------------------------------------------------- */
2122 static int
2123 ftpfs_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
2125 ftp_fh_data_t *ftp;
2127 (void) mode;
2129 fh->data = g_new0 (ftp_fh_data_t, 1);
2130 ftp = (ftp_fh_data_t *) fh->data;
2131 /* File will be written only, so no need to retrieve it from ftp server */
2132 if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
2134 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2135 struct linger li;
2136 #else
2137 int li = 1;
2138 #endif
2139 char *name;
2141 /* ftpfs_linear_start() called, so data will be written
2142 * to local temporary file and stored to ftp server
2143 * by vfs_s_close later
2145 if (((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy)
2147 if (!fh->ino->localname)
2149 vfs_path_t *vpath;
2150 int handle;
2152 handle = vfs_mkstemps (&vpath, me->name, fh->ino->ent->name);
2153 if (handle == -1)
2155 vfs_path_free (vpath);
2156 goto fail;
2158 close (handle);
2159 fh->ino->localname = vfs_path_to_str (vpath);
2160 vfs_path_free (vpath);
2161 ftp->append = flags & O_APPEND;
2163 return 0;
2165 name = vfs_s_fullpath (me, fh->ino);
2166 if (name == NULL)
2167 goto fail;
2168 fh->handle =
2169 ftpfs_open_data_connection (me, fh->ino->super,
2170 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
2171 g_free (name);
2173 if (fh->handle < 0)
2174 goto fail;
2175 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2176 li.l_onoff = 1;
2177 li.l_linger = 120;
2178 #endif
2179 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
2181 if (fh->ino->localname)
2183 unlink (fh->ino->localname);
2184 g_free (fh->ino->localname);
2185 fh->ino->localname = NULL;
2187 return 0;
2190 if (!fh->ino->localname && vfs_s_retrieve_file (me, fh->ino) == -1)
2191 goto fail;
2192 if (!fh->ino->localname)
2193 vfs_die ("retrieve_file failed to fill in localname");
2194 return 0;
2196 fail:
2197 ftpfs_fh_free_data (fh);
2198 return -1;
2201 /* --------------------------------------------------------------------------------------------- */
2203 static int
2204 ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t * fh)
2206 if (fh->handle != -1 && !fh->ino->localname)
2208 ftp_super_data_t *ftp = (ftp_super_data_t *) fh->ino->super->data;
2210 close (fh->handle);
2211 fh->handle = -1;
2212 /* File is stored to destination already, so
2213 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
2215 fh->changed = 0;
2216 if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE)
2217 ERRNOR (EIO, -1);
2218 vfs_s_invalidate (me, FH_SUPER);
2221 return 0;
2224 /* --------------------------------------------------------------------------------------------- */
2226 static void
2227 ftpfs_done (struct vfs_class *me)
2229 (void) me;
2231 g_slist_foreach (no_proxy, (GFunc) g_free, NULL);
2232 g_slist_free (no_proxy);
2234 g_free (ftpfs_anonymous_passwd);
2235 g_free (ftpfs_proxy_host);
2238 /* --------------------------------------------------------------------------------------------- */
2240 static void
2241 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
2243 GList *iter;
2245 for (iter = MEDATA->supers; iter != NULL; iter = g_list_next (iter))
2247 const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
2248 char *name;
2250 name = vfs_path_element_build_pretty_path_str (super->path_element);
2252 func (name);
2253 g_free (name);
2257 /* --------------------------------------------------------------------------------------------- */
2259 static keyword_t
2260 ftpfs_netrc_next (void)
2262 char *p;
2263 keyword_t i;
2264 static const char *const keywords[] = { "default", "machine",
2265 "login", "password", "passwd", "account", "macdef", NULL
2268 while (1)
2270 netrcp = skip_separators (netrcp);
2271 if (*netrcp != '\n')
2272 break;
2273 netrcp++;
2275 if (!*netrcp)
2276 return NETRC_NONE;
2277 p = buffer;
2278 if (*netrcp == '"')
2280 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++)
2282 if (*netrcp == '\\')
2283 netrcp++;
2284 *p++ = *netrcp;
2287 else
2289 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
2290 *netrcp != ',' && *netrcp; netrcp++)
2292 if (*netrcp == '\\')
2293 netrcp++;
2294 *p++ = *netrcp;
2297 *p = 0;
2298 if (!*buffer)
2299 return NETRC_NONE;
2301 for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2302 if (strcmp (keywords[i - 1], buffer) == 0)
2303 return i;
2305 return NETRC_UNKNOWN;
2308 /* --------------------------------------------------------------------------------------------- */
2310 static int
2311 ftpfs_netrc_bad_mode (const char *netrcname)
2313 static int be_angry = 1;
2314 struct stat mystat;
2316 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077))
2318 if (be_angry)
2320 message (D_ERROR, MSG_ERROR,
2321 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2322 be_angry = 0;
2324 return 1;
2326 return 0;
2329 /* --------------------------------------------------------------------------------------------- */
2330 /* Scan .netrc until we find matching "machine" or "default"
2331 * domain is used for additional matching
2332 * No search is done after "default" in compliance with "man netrc"
2333 * Return 0 if found, -1 otherwise */
2335 static int
2336 ftpfs_find_machine (const char *host, const char *domain)
2338 keyword_t keyword;
2340 if (!host)
2341 host = "";
2342 if (!domain)
2343 domain = "";
2345 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2347 if (keyword == NETRC_DEFAULT)
2348 return 0;
2350 if (keyword == NETRC_MACDEF)
2352 /* Scan for an empty line, which concludes "macdef" */
2355 while (*netrcp && *netrcp != '\n')
2356 netrcp++;
2357 if (*netrcp != '\n')
2358 break;
2359 netrcp++;
2361 while (*netrcp && *netrcp != '\n');
2362 continue;
2365 if (keyword != NETRC_MACHINE)
2366 continue;
2368 /* Take machine name */
2369 if (ftpfs_netrc_next () == NETRC_NONE)
2370 break;
2372 if (g_ascii_strcasecmp (host, buffer) != 0)
2374 /* Try adding our domain to short names in .netrc */
2375 const char *host_domain = strchr (host, '.');
2376 if (!host_domain)
2377 continue;
2379 /* Compare domain part */
2380 if (g_ascii_strcasecmp (host_domain, domain) != 0)
2381 continue;
2383 /* Compare local part */
2384 if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2385 continue;
2388 return 0;
2391 /* end of .netrc */
2392 return -1;
2395 /* --------------------------------------------------------------------------------------------- */
2396 /* Extract login and password from .netrc for the host.
2397 * pass may be NULL.
2398 * Returns 0 for success, -1 for error */
2400 static int
2401 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2403 char *netrcname;
2404 char *tmp_pass = NULL;
2405 char hostname[MAXHOSTNAMELEN];
2406 const char *domain;
2407 keyword_t keyword;
2408 static struct rupcache
2410 struct rupcache *next;
2411 char *host;
2412 char *login;
2413 char *pass;
2414 } *rup_cache = NULL, *rupp;
2416 /* Initialize *login and *pass */
2417 g_free (*login);
2418 *login = NULL;
2419 g_free (*pass);
2420 *pass = NULL;
2422 /* Look up in the cache first */
2423 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2425 if (!strcmp (host, rupp->host))
2427 if (rupp->login)
2428 *login = g_strdup (rupp->login);
2429 if (pass && rupp->pass)
2430 *pass = g_strdup (rupp->pass);
2431 return 0;
2435 /* Load current .netrc */
2436 netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2437 if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2439 g_free (netrcname);
2440 return 0;
2443 netrcp = netrc;
2445 /* Find our own domain name */
2446 if (gethostname (hostname, sizeof (hostname)) < 0)
2447 *hostname = '\0';
2449 domain = strchr (hostname, '.');
2450 if (domain == NULL)
2451 domain = "";
2453 /* Scan for "default" and matching "machine" keywords */
2454 ftpfs_find_machine (host, domain);
2456 /* Scan for keywords following "default" and "machine" */
2457 while (1)
2459 int need_break = 0;
2460 keyword = ftpfs_netrc_next ();
2462 switch (keyword)
2464 case NETRC_LOGIN:
2465 if (ftpfs_netrc_next () == NETRC_NONE)
2467 need_break = 1;
2468 break;
2471 /* We have another name already - should not happen */
2472 if (*login)
2474 need_break = 1;
2475 break;
2478 /* We have login name now */
2479 *login = g_strdup (buffer);
2480 break;
2482 case NETRC_PASSWORD:
2483 case NETRC_PASSWD:
2484 if (ftpfs_netrc_next () == NETRC_NONE)
2486 need_break = 1;
2487 break;
2490 /* Ignore unsafe passwords */
2491 if (*login != NULL &&
2492 strcmp (*login, "anonymous") != 0 && strcmp (*login, "ftp") != 0
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 /* --------------------------------------------------------------------------------------------- */