Try fix of compile warnings about assigned but unused variables
[midnight-commander.git] / src / vfs / ftpfs / ftpfs.c
blobfa5f14ebe9c294043aac1a47c97f35c117f3b050
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 for (;;)
382 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
384 if (string_buf)
385 *string_buf = 0;
386 code = 421;
387 return 4;
389 switch (sscanf (answer, "%d", &code))
391 case 0:
392 if (string_buf)
393 g_strlcpy (string_buf, answer, string_len);
394 code = 500;
395 return 5;
396 case 1:
397 if (answer[3] == '-')
399 while (1)
401 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
403 if (string_buf)
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)
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;
436 if (ftpfs_login_server (me, super, super->path_element->password) != 0)
438 if (cwdir == NULL)
439 return 1;
440 sock = ftpfs_chdir_internal (me, super, cwdir);
441 g_free (cwdir);
442 return sock == COMPLETE ? 1 : 0;
445 SUP->current_dir = cwdir;
448 return 0;
451 /* --------------------------------------------------------------------------------------------- */
453 static int
454 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt,
455 ...)
457 va_list ap;
458 char *cmdstr;
459 int status, cmdlen;
460 static int retry = 0;
461 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
463 va_start (ap, fmt);
464 cmdstr = g_strdup_vprintf (fmt, ap);
465 va_end (ap);
467 cmdlen = strlen (cmdstr);
468 cmdstr = g_realloc (cmdstr, cmdlen + 3);
469 strcpy (cmdstr + cmdlen, "\r\n");
470 cmdlen += 2;
472 if (MEDATA->logfile)
474 if (strncmp (cmdstr, "PASS ", 5) == 0)
476 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
478 else
480 (void) 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 (SUP->current_dir);
553 g_free (super->data);
554 super->data = NULL;
557 /* --------------------------------------------------------------------------------------------- */
559 static int
560 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
562 if (binary != SUP->isbinary)
564 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
565 ERRNOR (EIO, -1);
566 SUP->isbinary = binary;
568 return binary;
571 /* --------------------------------------------------------------------------------------------- */
572 /* This routine logs the user in */
574 static int
575 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
577 char *pass;
578 char *op;
579 char *name; /* login user name */
580 int anon = 0;
581 char reply_string[BUF_MEDIUM];
583 SUP->isbinary = TYPE_UNKNOWN;
585 if (super->path_element->password != NULL) /* explicit password */
586 op = g_strdup (super->path_element->password);
587 else if (netrcpass != NULL) /* password from netrc */
588 op = g_strdup (netrcpass);
589 else if (strcmp (super->path_element->user, "anonymous") == 0
590 || strcmp (super->path_element->user, "ftp") == 0)
592 if (ftpfs_anonymous_passwd == NULL) /* default anonymous password */
593 ftpfs_init_passwd ();
594 op = g_strdup (ftpfs_anonymous_passwd);
595 anon = 1;
597 else
598 { /* ask user */
599 char *p;
601 p = g_strdup_printf (_("FTP: Password required for %s"), super->path_element->user);
602 op = vfs_get_password (p);
603 g_free (p);
604 if (op == NULL)
605 ERRNOR (EPERM, 0);
606 super->path_element->password = g_strdup (op);
609 if (!anon || MEDATA->logfile)
610 pass = op;
611 else
613 pass = g_strconcat ("-", op, (char *) NULL);
614 wipe_password (op);
617 /* Proxy server accepts: username@host-we-want-to-connect */
618 if (SUP->proxy)
619 name =
620 g_strconcat (super->path_element->user, "@",
621 super->path_element->host[0] ==
622 '!' ? super->path_element->host + 1 : super->path_element->host,
623 (char *) NULL);
624 else
625 name = g_strdup (super->path_element->user);
627 if (ftpfs_get_reply (me, SUP->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
629 char *reply_up;
631 reply_up = g_ascii_strup (reply_string, -1);
632 SUP->remote_is_amiga = strstr (reply_up, "AMIGA") != 0;
633 if (strstr (reply_up, " SPFTP/1.0.0000 SERVER ")) /* handles `LIST -la` in a weird way */
634 SUP->strict = RFC_STRICT;
635 g_free (reply_up);
637 if (MEDATA->logfile)
639 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n", SUP->remote_is_amiga);
640 fflush (MEDATA->logfile);
643 vfs_print_message (_("ftpfs: sending login name"));
645 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
647 case CONTINUE:
648 vfs_print_message (_("ftpfs: sending user password"));
649 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
650 if (code == CONTINUE)
652 char *p;
654 p = g_strdup_printf (_("FTP: Account required for user %s"),
655 super->path_element->user);
656 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
657 g_free (p);
658 if (op == NULL)
659 ERRNOR (EPERM, 0);
660 vfs_print_message (_("ftpfs: sending user account"));
661 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
662 g_free (op);
664 if (code != COMPLETE)
665 break;
666 /* fall through */
668 case COMPLETE:
669 vfs_print_message (_("ftpfs: logged in"));
670 wipe_password (pass);
671 g_free (name);
672 return 1;
674 default:
675 SUP->failed_on_login = 1;
676 wipe_password (super->path_element->password);
677 super->path_element->password = NULL;
679 goto login_fail;
683 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
684 super->path_element->user);
686 login_fail:
687 wipe_password (pass);
688 g_free (name);
689 ERRNOR (EPERM, 0);
692 /* --------------------------------------------------------------------------------------------- */
694 static void
695 ftpfs_load_no_proxy_list (void)
697 /* FixMe: shouldn't be hardcoded!!! */
698 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
699 FILE *npf;
700 int c;
701 char *p;
702 static char *mc_file = NULL;
704 mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL);
705 if (exist_file (mc_file))
707 npf = fopen (mc_file, "r");
708 if (npf != NULL)
710 while (fgets (s, sizeof (s), npf) != NULL)
712 p = strchr (s, '\n');
713 if (p == NULL) /* skip bogus entries */
715 while ((c = fgetc (npf)) != EOF && c != '\n')
717 continue;
720 if (p != s)
722 *p = '\0';
723 no_proxy = g_slist_prepend (no_proxy, g_strdup (s));
726 fclose (npf);
729 g_free (mc_file);
732 /* --------------------------------------------------------------------------------------------- */
733 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
735 static int
736 ftpfs_check_proxy (const char *host)
738 GSList *npe;
740 if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0')
741 return 0; /* sanity check */
743 if (*host == '!')
744 return 1;
746 if (!ftpfs_always_use_proxy)
747 return 0;
749 if (strchr (host, '.') == NULL)
750 return 0;
752 ftpfs_load_no_proxy_list ();
753 for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe))
755 const char *domain = (const char *) npe->data;
757 if (domain[0] == '.')
759 size_t ld = strlen (domain);
760 size_t lh = strlen (host);
762 while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1])
764 ld--;
765 lh--;
768 if (ld == 0)
769 return 0;
771 else if (g_ascii_strcasecmp (host, domain) == 0)
772 return 0;
775 return 1;
778 /* --------------------------------------------------------------------------------------------- */
780 static void
781 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
783 vfs_path_element_t *path_element;
785 path_element = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
786 *host = g_strdup (path_element->host);
787 *port = path_element->port;
788 vfs_path_element_free (path_element);
791 /* --------------------------------------------------------------------------------------------- */
793 static int
794 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
796 struct addrinfo hints, *res, *curr_res;
797 int my_socket = 0;
798 char *host = NULL;
799 char port[8];
800 int tmp_port;
801 int e;
803 (void) me;
805 /* Use a proxy host? */
806 host = g_strdup (super->path_element->host);
808 if (host == NULL || *host == '\0')
810 vfs_print_message (_("ftpfs: Invalid host name."));
811 ftpfs_errno = EINVAL;
812 g_free (host);
813 return -1;
816 /* Hosts to connect to that start with a ! should use proxy */
817 tmp_port = super->path_element->port;
819 if (SUP->proxy != NULL)
820 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
822 g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_port);
823 if (port[0] == '\0')
825 g_free (host);
826 ftpfs_errno = errno;
827 return -1;
830 tty_enable_interrupt_key (); /* clear the interrupt flag */
832 memset (&hints, 0, sizeof (struct addrinfo));
833 hints.ai_family = AF_UNSPEC;
834 hints.ai_socktype = SOCK_STREAM;
836 #ifdef AI_ADDRCONFIG
837 /* By default, only look up addresses using address types for
838 * which a local interface is configured (i.e. no IPv6 if no IPv6
839 * interfaces, likewise for IPv4 (see RFC 3493 for details). */
840 hints.ai_flags = AI_ADDRCONFIG;
841 #endif
843 e = getaddrinfo (host, port, &hints, &res);
845 #ifdef AI_ADDRCONFIG
846 if (e == EAI_BADFLAGS)
848 /* Retry with no flags if AI_ADDRCONFIG was rejected. */
849 hints.ai_flags = 0;
850 e = getaddrinfo (host, port, &hints, &res);
852 #endif
854 *port = '\0';
856 if (e != 0)
858 tty_disable_interrupt_key ();
859 vfs_print_message (_("ftpfs: %s"), gai_strerror (e));
860 g_free (host);
861 ftpfs_errno = EINVAL;
862 return -1;
865 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
867 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
869 if (my_socket < 0)
871 if (curr_res->ai_next != NULL)
872 continue;
874 tty_disable_interrupt_key ();
875 vfs_print_message (_("ftpfs: %s"), unix_error_string (errno));
876 g_free (host);
877 freeaddrinfo (res);
878 ftpfs_errno = errno;
879 return -1;
882 vfs_print_message (_("ftpfs: making connection to %s"), host);
883 g_free (host);
884 host = NULL;
886 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
887 break;
889 ftpfs_errno = errno;
890 close (my_socket);
892 if (errno == EINTR && tty_got_interrupt ())
893 vfs_print_message (_("ftpfs: connection interrupted by user"));
894 else if (res->ai_next == NULL)
895 vfs_print_message (_("ftpfs: connection to server failed: %s"),
896 unix_error_string (errno));
897 else
898 continue;
900 freeaddrinfo (res);
901 tty_disable_interrupt_key ();
902 return -1;
905 freeaddrinfo (res);
906 tty_disable_interrupt_key ();
907 return my_socket;
910 /* --------------------------------------------------------------------------------------------- */
912 static int
913 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
915 int retry_seconds = 0;
916 int count_down;
918 /* We do not want to use the passive if we are using proxies */
919 if (SUP->proxy)
920 SUP->use_passive_connection = ftpfs_use_passive_connections_over_proxy;
924 SUP->failed_on_login = 0;
926 SUP->sock = ftpfs_open_socket (me, super);
927 if (SUP->sock == -1)
928 return -1;
930 if (ftpfs_login_server (me, super, NULL) != 0)
932 /* Logged in, no need to retry the connection */
933 break;
935 else
937 if (!SUP->failed_on_login)
938 return -1;
940 /* Close only the socket descriptor */
941 close (SUP->sock);
943 if (ftpfs_retry_seconds != 0)
945 retry_seconds = ftpfs_retry_seconds;
946 tty_enable_interrupt_key ();
947 for (count_down = retry_seconds; count_down; count_down--)
949 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"),
950 count_down);
951 sleep (1);
952 if (tty_got_interrupt ())
954 /* ftpfs_errno = E; */
955 tty_disable_interrupt_key ();
956 return 0;
959 tty_disable_interrupt_key ();
963 while (retry_seconds != 0);
965 SUP->current_dir = ftpfs_get_current_directory (me, super);
966 if (SUP->current_dir == NULL)
967 SUP->current_dir = g_strdup (PATH_SEP_STR);
969 return 0;
972 /* --------------------------------------------------------------------------------------------- */
974 static int
975 ftpfs_open_archive (struct vfs_s_super *super,
976 const vfs_path_t * vpath, const vfs_path_element_t * vpath_element)
978 (void) vpath;
980 super->data = g_new0 (ftp_super_data_t, 1);
982 super->path_element = ftpfs_correct_url_parameters (vpath_element);
983 SUP->proxy = NULL;
984 if (ftpfs_check_proxy (super->path_element->host))
985 SUP->proxy = ftpfs_proxy_host;
986 SUP->use_passive_connection = ftpfs_use_passive_connections;
987 SUP->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
988 SUP->isbinary = TYPE_UNKNOWN;
989 SUP->remote_is_amiga = 0;
990 super->name = g_strdup ("/");
991 super->root =
992 vfs_s_new_inode (vpath_element->class, super,
993 vfs_s_default_stat (vpath_element->class, S_IFDIR | 0755));
995 return ftpfs_open_archive_int (vpath_element->class, super);
998 /* --------------------------------------------------------------------------------------------- */
1000 static int
1001 ftpfs_archive_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *super,
1002 const vfs_path_t * vpath, void *cookie)
1004 vfs_path_element_t *path_element;
1005 int result;
1007 (void) vpath;
1008 (void) cookie;
1010 path_element = ftpfs_correct_url_parameters (vpath_element);
1012 result = ((strcmp (path_element->host, super->path_element->host) == 0)
1013 && (strcmp (path_element->user, super->path_element->user) == 0)
1014 && (path_element->port == super->path_element->port)) ? 1 : 0;
1016 vfs_path_element_free (path_element);
1017 return result;
1020 /* --------------------------------------------------------------------------------------------- */
1021 /* The returned directory should always contain a trailing slash */
1023 static char *
1024 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
1026 char buf[MC_MAXPATHLEN + 1];
1028 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
1029 ftpfs_get_reply (me, SUP->sock, buf, sizeof (buf)) == COMPLETE)
1031 char *bufp = NULL;
1032 char *bufq;
1034 for (bufq = buf; *bufq != '\0'; bufq++)
1035 if (*bufq == '"')
1037 if (bufp == NULL)
1038 bufp = bufq + 1;
1039 else
1041 *bufq = '\0';
1043 if (*bufp != '\0')
1045 if (*(bufq - 1) != '/')
1047 *bufq++ = '/';
1048 *bufq = '\0';
1051 if (*bufp == '/')
1052 return g_strdup (bufp);
1054 /* If the remote server is an Amiga a leading slash
1055 might be missing. MC needs it because it is used
1056 as separator between hostname and path internally. */
1057 return g_strconcat ("/", bufp, (char *) NULL);
1060 break;
1065 ftpfs_errno = EIO;
1066 return NULL;
1069 /* --------------------------------------------------------------------------------------------- */
1070 /* Setup Passive PASV FTP connection */
1072 static int
1073 ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
1074 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1076 char *c;
1077 char n[6];
1078 int xa, xb, xc, xd, xe, xf;
1080 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
1081 return 0;
1083 /* Parse remote parameters */
1084 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
1086 if (!*c)
1087 return 0;
1088 if (!isdigit ((unsigned char) *c))
1089 return 0;
1090 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1091 return 0;
1093 n[0] = (unsigned char) xa;
1094 n[1] = (unsigned char) xb;
1095 n[2] = (unsigned char) xc;
1096 n[3] = (unsigned char) xd;
1097 n[4] = (unsigned char) xe;
1098 n[5] = (unsigned char) xf;
1100 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1101 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1103 if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
1104 return 0;
1106 return 1;
1109 /* --------------------------------------------------------------------------------------------- */
1110 /* Setup Passive EPSV FTP connection */
1112 static int
1113 ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
1114 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1116 char *c;
1117 int port;
1119 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") != COMPLETE)
1120 return 0;
1122 /* (|||<port>|) */
1123 c = strchr (reply_str, '|');
1124 if (c == NULL)
1125 return 0;
1126 if (strlen (c) > 3)
1127 c += 3;
1128 else
1129 return 0;
1131 port = atoi (c);
1132 if (port < 0 || port > 65535)
1133 return 0;
1134 port = htons (port);
1136 switch (sa->ss_family)
1138 case AF_INET:
1139 ((struct sockaddr_in *) sa)->sin_port = port;
1140 break;
1141 case AF_INET6:
1142 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1143 break;
1146 return (connect (my_socket, (struct sockaddr *) sa, *salen) < 0) ? 0 : 1;
1149 /* --------------------------------------------------------------------------------------------- */
1150 /* Setup Passive ftp connection, we use it for source routed connections */
1152 static int
1153 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
1154 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
1156 /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1157 if (sa->ss_family == AF_INET)
1159 if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
1160 /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1161 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1162 return 0;
1164 /* It's IPV6, so EPSV is our only hope */
1165 else
1167 if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1168 return 0;
1171 return 1;
1174 /* --------------------------------------------------------------------------------------------- */
1175 /* Setup Active PORT or EPRT FTP connection */
1177 static int
1178 ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super,
1179 struct sockaddr_storage data_addr, socklen_t data_addrlen)
1181 unsigned short int port;
1182 char *addr;
1183 unsigned int af;
1185 switch (data_addr.ss_family)
1187 case AF_INET:
1188 af = FTP_INET;
1189 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1190 break;
1191 case AF_INET6:
1192 af = FTP_INET6;
1193 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1194 break;
1195 /* Not implemented */
1196 default:
1197 return 0;
1200 addr = g_try_malloc (NI_MAXHOST);
1201 if (addr == NULL)
1202 ERRNOR (ENOMEM, -1);
1204 if (getnameinfo
1205 ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1206 NI_NUMERICHOST) != 0)
1208 g_free (addr);
1209 ERRNOR (EIO, -1);
1212 /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1213 if (af == FTP_INET)
1215 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1216 unsigned char *p = (unsigned char *) &port;
1218 if (ftpfs_command (me, super, WAIT_REPLY,
1219 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1220 p[0], p[1]) == COMPLETE)
1222 g_free (addr);
1223 return 1;
1228 * Converts network MSB first order to host byte order (LSB
1229 * first on i386). If we do it earlier, we will run into an
1230 * endianness issue, because the server actually expects to see
1231 * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1233 port = ntohs (port);
1235 /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1236 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
1238 g_free (addr);
1239 return 1;
1242 g_free (addr);
1243 return 0;
1246 /* --------------------------------------------------------------------------------------------- */
1247 /* Initialize a socket for FTP DATA connection */
1249 static int
1250 ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super,
1251 struct sockaddr_storage *data_addr, socklen_t * data_addrlen)
1253 int result;
1255 memset (data_addr, 0, sizeof (struct sockaddr_storage));
1256 *data_addrlen = sizeof (struct sockaddr_storage);
1258 if (SUP->use_passive_connection)
1259 result = getpeername (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1260 else
1261 result = getsockname (SUP->sock, (struct sockaddr *) data_addr, data_addrlen);
1263 if (result == -1)
1264 return -1;
1266 switch (data_addr->ss_family)
1268 case AF_INET:
1269 ((struct sockaddr_in *) data_addr)->sin_port = 0;
1270 break;
1271 case AF_INET6:
1272 ((struct sockaddr_in6 *) data_addr)->sin6_port = 0;
1273 break;
1274 default:
1275 vfs_print_message (_("ftpfs: invalid address family"));
1276 ERRNOR (EINVAL, -1);
1279 result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP);
1281 if (result < 0)
1283 vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1284 return -1;
1287 return result;
1290 /* --------------------------------------------------------------------------------------------- */
1291 /* Initialize FTP DATA connection */
1293 static int
1294 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1296 struct sockaddr_storage data_addr;
1297 socklen_t data_addrlen;
1300 * Don't factor socket initialization out of these conditionals,
1301 * because ftpfs_init_data_socket initializes it in different way
1302 * depending on use_passive_connection flag.
1305 /* Try to establish a passive connection first (if requested) */
1306 if (SUP->use_passive_connection)
1308 int data_sock;
1310 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1311 if (data_sock < 0)
1312 return -1;
1314 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1315 return data_sock;
1317 vfs_print_message (_("ftpfs: could not setup passive mode"));
1318 SUP->use_passive_connection = 0;
1320 close (data_sock);
1323 /* If passive setup is diabled or failed, fallback to active connections */
1324 if (!SUP->use_passive_connection)
1326 int data_sock;
1328 data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1329 if (data_sock < 0)
1330 return -1;
1332 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) == 0) &&
1333 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) == 0) &&
1334 (listen (data_sock, 1) == 0) &&
1335 (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0))
1336 return data_sock;
1338 close (data_sock);
1341 /* Restore the initial value of use_passive_connection (for subsequent retries) */
1342 SUP->use_passive_connection = SUP->proxy != NULL ? ftpfs_use_passive_connections_over_proxy :
1343 ftpfs_use_passive_connections;
1345 ftpfs_errno = EIO;
1346 return -1;
1349 /* --------------------------------------------------------------------------------------------- */
1351 static int
1352 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1353 const char *remote, int isbinary, int reget)
1355 struct sockaddr_storage from;
1356 int s, j, data;
1357 socklen_t fromlen = sizeof (from);
1359 s = ftpfs_initconn (me, super);
1360 if (s == -1)
1361 return -1;
1363 if (ftpfs_changetype (me, super, isbinary) == -1)
1364 return -1;
1365 if (reget > 0)
1367 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1368 if (j != CONTINUE)
1369 return -1;
1371 if (remote)
1373 char *remote_path = ftpfs_translate_path (me, super, remote);
1374 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1375 /* WarFtpD can't STORE //filename */
1376 (*remote_path == '/') ? remote_path + 1 : remote_path);
1377 g_free (remote_path);
1379 else
1380 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1382 if (j != PRELIM)
1383 ERRNOR (EPERM, -1);
1384 tty_enable_interrupt_key ();
1385 if (SUP->use_passive_connection)
1386 data = s;
1387 else
1389 data = accept (s, (struct sockaddr *) &from, &fromlen);
1390 if (data < 0)
1392 ftpfs_errno = errno;
1393 close (s);
1394 return -1;
1396 close (s);
1398 tty_disable_interrupt_key ();
1399 return data;
1402 /* --------------------------------------------------------------------------------------------- */
1404 static void
1405 ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t * fh)
1407 struct vfs_s_super *super = FH_SUPER;
1408 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1409 fd_set mask;
1410 char buf[BUF_8K];
1411 int dsock = FH_SOCK;
1412 FH_SOCK = -1;
1413 SUP->ctl_connection_busy = 0;
1415 vfs_print_message (_("ftpfs: aborting transfer."));
1416 if (send (SUP->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1418 vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1419 if (dsock != -1)
1420 close (dsock);
1421 return;
1424 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1426 vfs_print_message (_("ftpfs: abort failed"));
1427 if (dsock != -1)
1428 close (dsock);
1429 return;
1431 if (dsock != -1)
1433 FD_ZERO (&mask);
1434 FD_SET (dsock, &mask);
1435 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1437 struct timeval start_tim, tim;
1438 gettimeofday (&start_tim, NULL);
1439 /* flush the remaining data */
1440 while (read (dsock, buf, sizeof (buf)) > 0)
1442 gettimeofday (&tim, NULL);
1443 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT)
1445 /* server keeps sending, drop the connection and ftpfs_reconnect */
1446 close (dsock);
1447 ftpfs_reconnect (me, super);
1448 return;
1452 close (dsock);
1454 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) == TRANSIENT) && (code == 426))
1455 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1458 /* --------------------------------------------------------------------------------------------- */
1460 #if 0
1461 static void
1462 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1463 struct vfs_s_inode *dir)
1465 struct linklist *flist;
1466 struct direntry *fe, *fel;
1467 char tmp[MC_MAXPATHLEN];
1468 int depth;
1470 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1471 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1473 /* flist->data->l_stat is alread initialized with 0 */
1474 fel = flist->data;
1475 if (S_ISLNK (fel->s.st_mode) && fel->linkname)
1477 if (fel->linkname[0] == '/')
1479 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1480 continue;
1481 strcpy (tmp, fel->linkname);
1483 else
1485 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1486 continue;
1487 strcpy (tmp, dir->remote_path);
1488 if (tmp[1] != '\0')
1489 strcat (tmp, "/");
1490 strcat (tmp + 1, fel->linkname);
1492 for (depth = 0; depth < 100; depth++)
1493 { /* depth protects against recursive symbolic links */
1494 canonicalize_pathname (tmp);
1495 fe = _get_file_entry (bucket, tmp, 0, 0);
1496 if (fe)
1498 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1500 /* Symlink points to link which isn't resolved, yet. */
1501 if (fe->linkname[0] == '/')
1503 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1504 break;
1505 strcpy (tmp, fe->linkname);
1507 else
1509 /* at this point tmp looks always like this
1510 /directory/filename, i.e. no need to check
1511 strrchr's return value */
1512 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1513 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1514 break;
1515 strcat (tmp, fe->linkname);
1517 continue;
1519 else
1521 fel->l_stat = g_new (struct stat, 1);
1522 if (S_ISLNK (fe->s.st_mode))
1523 *fel->l_stat = *fe->l_stat;
1524 else
1525 *fel->l_stat = fe->s;
1526 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1529 break;
1533 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1536 /* --------------------------------------------------------------------------------------------- */
1538 static void
1539 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1540 struct vfs_s_inode *dir)
1542 char buffer[2048] = "", *filename;
1543 int sock;
1544 FILE *fp;
1545 struct stat s;
1546 struct linklist *flist;
1547 struct direntry *fe;
1548 int switch_method = 0;
1550 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1551 if (strchr (dir->remote_path, ' '))
1553 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1555 vfs_print_message (_("ftpfs: CWD failed."));
1556 return;
1558 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1560 else
1561 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1563 if (sock == -1)
1565 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1566 return;
1569 fp = fdopen (sock, "r");
1570 if (fp == NULL)
1572 close (sock);
1573 vfs_print_message (_("ftpfs: couldn't resolve symlink"));
1574 return;
1576 tty_enable_interrupt_key ();
1577 flist = dir->file_list->next;
1578 while (1)
1582 if (flist == dir->file_list)
1583 goto done;
1584 fe = flist->data;
1585 flist = flist->next;
1587 while (!S_ISLNK (fe->s.st_mode));
1588 while (1)
1590 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1591 goto done;
1592 if (MEDATA->logfile)
1594 fputs (buffer, MEDATA->logfile);
1595 fflush (MEDATA->logfile);
1597 vfs_die ("This code should be commented out\n");
1598 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1600 int r = strcmp (fe->name, filename);
1601 g_free (filename);
1602 if (r == 0)
1604 if (S_ISLNK (s.st_mode))
1606 /* This server doesn't understand LIST -lLa */
1607 switch_method = 1;
1608 goto done;
1610 fe->l_stat = g_new (struct stat, 1);
1611 if (fe->l_stat == NULL)
1612 goto done;
1613 *fe->l_stat = s;
1614 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1615 break;
1617 if (r < 0)
1618 break;
1622 done:
1623 while (fgets (buffer, sizeof (buffer), fp) != NULL);
1624 tty_disable_interrupt_key ();
1625 fclose (fp);
1626 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1629 /* --------------------------------------------------------------------------------------------- */
1631 static void
1632 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1634 vfs_print_message (_("Resolving symlink..."));
1636 if (SUP->strict_rfc959_list_cmd)
1637 resolve_symlink_without_ls_options (me, super, dir);
1638 else
1639 resolve_symlink_with_ls_options (me, super, dir);
1641 #endif
1643 /* --------------------------------------------------------------------------------------------- */
1645 static int
1646 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1648 struct vfs_s_entry *ent;
1649 struct vfs_s_super *super = dir->super;
1650 int sock, num_entries = 0;
1651 char lc_buffer[BUF_8K];
1652 int cd_first;
1654 cd_first = ftpfs_first_cd_then_ls || (SUP->strict == RFC_STRICT)
1655 || (strchr (remote_path, ' ') != NULL);
1657 again:
1658 vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1659 remote_path,
1660 SUP->strict ==
1661 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1663 if (cd_first)
1665 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1667 ftpfs_errno = ENOENT;
1668 vfs_print_message (_("ftpfs: CWD failed."));
1669 return -1;
1673 gettimeofday (&dir->timestamp, NULL);
1674 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1676 if (SUP->strict == RFC_STRICT)
1677 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1678 else if (cd_first)
1679 /* Dirty hack to avoid autoprepending / to . */
1680 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1681 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1682 else
1684 char *path;
1686 /* Trailing "/." is necessary if remote_path is a symlink */
1687 path = mc_build_filename (remote_path, ".", NULL);
1688 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1689 g_free (path);
1692 if (sock == -1)
1693 goto fallback;
1695 /* Clear the interrupt flag */
1696 tty_enable_interrupt_key ();
1698 vfs_parse_ls_lga_init ();
1699 while (1)
1701 int i;
1702 size_t count_spaces = 0;
1703 int res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer),
1704 sock);
1705 if (!res)
1706 break;
1708 if (res == EINTR)
1710 me->verrno = ECONNRESET;
1711 close (sock);
1712 tty_disable_interrupt_key ();
1713 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1714 vfs_print_message (_("%s: failure"), me->name);
1715 return -1;
1718 if (MEDATA->logfile)
1720 fputs (lc_buffer, MEDATA->logfile);
1721 fputs ("\n", MEDATA->logfile);
1722 fflush (MEDATA->logfile);
1725 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1726 i = ent->ino->st.st_nlink;
1727 if (!vfs_parse_ls_lga
1728 (lc_buffer, &ent->ino->st, &ent->name, &ent->ino->linkname, &count_spaces))
1730 vfs_s_free_entry (me, ent);
1731 continue;
1733 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1734 num_entries++;
1735 vfs_s_store_filename_leading_spaces (ent, count_spaces);
1736 vfs_s_insert_entry (me, dir, ent);
1739 close (sock);
1740 me->verrno = E_REMOTE;
1741 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1742 goto fallback;
1744 if (num_entries == 0 && cd_first == 0)
1746 /* The LIST command may produce an empty output. In such scenario
1747 it is not clear whether this is caused by `remote_path' being
1748 a non-existent path or for some other reason (listing emtpy
1749 directory without the -a option, non-readable directory, etc.).
1751 Since `dir_load' is a crucial method, when it comes to determine
1752 whether a given path is a _directory_, the code must try its best
1753 to determine the type of `remote_path'. The only reliable way to
1754 achieve this is trough issuing a CWD command. */
1756 cd_first = 1;
1757 goto again;
1760 vfs_s_normalize_filename_leading_spaces (dir, vfs_parse_ls_lga_get_final_spaces ());
1762 if (SUP->strict == RFC_AUTODETECT)
1763 SUP->strict = RFC_DARING;
1765 vfs_print_message (_("%s: done."), me->name);
1766 return 0;
1768 fallback:
1769 if (SUP->strict == RFC_AUTODETECT)
1771 /* It's our first attempt to get a directory listing from this
1772 server (UNIX style LIST command) */
1773 SUP->strict = RFC_STRICT;
1774 /* I hate goto, but recursive call needs another 8K on stack */
1775 /* return ftpfs_dir_load (me, dir, remote_path); */
1776 cd_first = 1;
1777 goto again;
1779 vfs_print_message (_("ftpfs: failed; nowhere to fallback to"));
1780 ERRNOR (EACCES, -1);
1783 /* --------------------------------------------------------------------------------------------- */
1785 static int
1786 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname)
1788 int h, sock, n_read, n_written;
1789 off_t n_stored;
1790 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1791 struct linger li;
1792 #else
1793 int flag_one = 1;
1794 #endif
1795 char lc_buffer[BUF_8K];
1796 struct stat s;
1797 char *w_buf;
1798 struct vfs_s_super *super = FH_SUPER;
1799 ftp_fh_data_t *ftp = (ftp_fh_data_t *) fh->data;
1801 h = open (localname, O_RDONLY);
1802 if (h == -1)
1803 ERRNOR (EIO, -1);
1805 sock =
1806 ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1807 if (sock < 0 || fstat (h, &s) == -1)
1809 close (h);
1810 return -1;
1812 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1813 li.l_onoff = 1;
1814 li.l_linger = 120;
1815 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1816 #else
1817 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1818 #endif
1819 n_stored = 0;
1821 tty_enable_interrupt_key ();
1822 while (TRUE)
1824 while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1826 if (errno == EINTR)
1828 if (tty_got_interrupt ())
1830 ftpfs_errno = EINTR;
1831 goto error_return;
1833 else
1834 continue;
1836 ftpfs_errno = errno;
1837 goto error_return;
1839 if (n_read == 0)
1840 break;
1841 n_stored += n_read;
1842 w_buf = lc_buffer;
1843 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1845 if (n_written == -1)
1847 if (errno == EINTR && !tty_got_interrupt ())
1849 continue;
1851 ftpfs_errno = errno;
1852 goto error_return;
1854 w_buf += n_written;
1855 n_read -= n_written;
1857 vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
1858 _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
1860 tty_disable_interrupt_key ();
1861 close (sock);
1862 close (h);
1863 if (ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE)
1864 ERRNOR (EIO, -1);
1865 return 0;
1866 error_return:
1867 tty_disable_interrupt_key ();
1868 close (sock);
1869 close (h);
1870 ftpfs_get_reply (me, SUP->sock, NULL, 0);
1871 return -1;
1874 /* --------------------------------------------------------------------------------------------- */
1876 static int
1877 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset)
1879 char *name;
1881 if (fh->data == NULL)
1882 fh->data = g_new0 (ftp_fh_data_t, 1);
1884 name = vfs_s_fullpath (me, fh->ino);
1885 if (name == NULL)
1886 return 0;
1887 FH_SOCK = ftpfs_open_data_connection (me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1888 g_free (name);
1889 if (FH_SOCK == -1)
1890 ERRNOR (EACCES, 0);
1891 fh->linear = LS_LINEAR_OPEN;
1892 ((ftp_super_data_t *) (FH_SUPER->data))->ctl_connection_busy = 1;
1893 ((ftp_fh_data_t *) fh->data)->append = 0;
1894 return 1;
1897 /* --------------------------------------------------------------------------------------------- */
1899 static int
1900 ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t * fh, void *buf, size_t len)
1902 ssize_t n;
1903 struct vfs_s_super *super = FH_SUPER;
1905 while ((n = read (FH_SOCK, buf, len)) < 0)
1907 if ((errno == EINTR) && !tty_got_interrupt ())
1908 continue;
1909 break;
1912 if (n < 0)
1913 ftpfs_linear_abort (me, fh);
1915 if (n == 0)
1917 SUP->ctl_connection_busy = 0;
1918 close (FH_SOCK);
1919 FH_SOCK = -1;
1920 if ((ftpfs_get_reply (me, SUP->sock, NULL, 0) != COMPLETE))
1921 ERRNOR (E_REMOTE, -1);
1922 return 0;
1924 ERRNOR (errno, n);
1927 /* --------------------------------------------------------------------------------------------- */
1929 static void
1930 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t * fh)
1932 if (FH_SOCK != -1)
1933 ftpfs_linear_abort (me, fh);
1936 /* --------------------------------------------------------------------------------------------- */
1938 static int
1939 ftpfs_ctl (void *fh, int ctlop, void *arg)
1941 (void) arg;
1943 switch (ctlop)
1945 case VFS_CTL_IS_NOTREADY:
1947 int v;
1949 if (!FH->linear)
1950 vfs_die ("You may not do this");
1951 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1952 return 0;
1954 v = vfs_s_select_on_two (((ftp_fh_data_t *) (FH->data))->sock, 0);
1955 return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
1957 default:
1958 return 0;
1962 /* --------------------------------------------------------------------------------------------- */
1964 static int
1965 ftpfs_send_command (const vfs_path_t * vpath, const char *cmd, int flags)
1967 const char *rpath;
1968 char *p;
1969 struct vfs_s_super *super;
1970 int r;
1971 const 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 (SUP->current_dir == NULL)
2050 return FALSE;
2051 return (strcmp (path, SUP->current_dir) == 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 (SUP->current_dir);
2074 SUP->current_dir = 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 = vfs_path_element_build_pretty_path_str (super->path_element);
2251 func (name);
2252 g_free (name);
2256 /* --------------------------------------------------------------------------------------------- */
2258 static keyword_t
2259 ftpfs_netrc_next (void)
2261 char *p;
2262 keyword_t i;
2263 static const char *const keywords[] = { "default", "machine",
2264 "login", "password", "passwd", "account", "macdef", NULL
2267 while (1)
2269 netrcp = skip_separators (netrcp);
2270 if (*netrcp != '\n')
2271 break;
2272 netrcp++;
2274 if (!*netrcp)
2275 return NETRC_NONE;
2276 p = buffer;
2277 if (*netrcp == '"')
2279 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++)
2281 if (*netrcp == '\\')
2282 netrcp++;
2283 *p++ = *netrcp;
2286 else
2288 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
2289 *netrcp != ',' && *netrcp; netrcp++)
2291 if (*netrcp == '\\')
2292 netrcp++;
2293 *p++ = *netrcp;
2296 *p = 0;
2297 if (!*buffer)
2298 return NETRC_NONE;
2300 for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2301 if (strcmp (keywords[i - 1], buffer) == 0)
2302 return i;
2304 return NETRC_UNKNOWN;
2307 /* --------------------------------------------------------------------------------------------- */
2309 static int
2310 ftpfs_netrc_bad_mode (const char *netrcname)
2312 static int be_angry = 1;
2313 struct stat mystat;
2315 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077))
2317 if (be_angry)
2319 message (D_ERROR, MSG_ERROR,
2320 _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2321 be_angry = 0;
2323 return 1;
2325 return 0;
2328 /* --------------------------------------------------------------------------------------------- */
2329 /* Scan .netrc until we find matching "machine" or "default"
2330 * domain is used for additional matching
2331 * No search is done after "default" in compliance with "man netrc"
2332 * Return 0 if found, -1 otherwise */
2334 static int
2335 ftpfs_find_machine (const char *host, const char *domain)
2337 keyword_t keyword;
2339 if (!host)
2340 host = "";
2341 if (!domain)
2342 domain = "";
2344 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2346 if (keyword == NETRC_DEFAULT)
2347 return 0;
2349 if (keyword == NETRC_MACDEF)
2351 /* Scan for an empty line, which concludes "macdef" */
2354 while (*netrcp && *netrcp != '\n')
2355 netrcp++;
2356 if (*netrcp != '\n')
2357 break;
2358 netrcp++;
2360 while (*netrcp && *netrcp != '\n');
2361 continue;
2364 if (keyword != NETRC_MACHINE)
2365 continue;
2367 /* Take machine name */
2368 if (ftpfs_netrc_next () == NETRC_NONE)
2369 break;
2371 if (g_ascii_strcasecmp (host, buffer) != 0)
2373 /* Try adding our domain to short names in .netrc */
2374 const char *host_domain = strchr (host, '.');
2375 if (!host_domain)
2376 continue;
2378 /* Compare domain part */
2379 if (g_ascii_strcasecmp (host_domain, domain) != 0)
2380 continue;
2382 /* Compare local part */
2383 if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2384 continue;
2387 return 0;
2390 /* end of .netrc */
2391 return -1;
2394 /* --------------------------------------------------------------------------------------------- */
2395 /* Extract login and password from .netrc for the host.
2396 * pass may be NULL.
2397 * Returns 0 for success, -1 for error */
2399 static int
2400 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2402 char *netrcname;
2403 char *tmp_pass = NULL;
2404 char hostname[MAXHOSTNAMELEN];
2405 const char *domain;
2406 keyword_t keyword;
2407 static struct rupcache
2409 struct rupcache *next;
2410 char *host;
2411 char *login;
2412 char *pass;
2413 } *rup_cache = NULL, *rupp;
2415 /* Initialize *login and *pass */
2416 g_free (*login);
2417 *login = NULL;
2418 g_free (*pass);
2419 *pass = NULL;
2421 /* Look up in the cache first */
2422 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2424 if (!strcmp (host, rupp->host))
2426 if (rupp->login)
2427 *login = g_strdup (rupp->login);
2428 if (pass && rupp->pass)
2429 *pass = g_strdup (rupp->pass);
2430 return 0;
2434 /* Load current .netrc */
2435 netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2436 if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2438 g_free (netrcname);
2439 return 0;
2442 netrcp = netrc;
2444 /* Find our own domain name */
2445 if (gethostname (hostname, sizeof (hostname)) < 0)
2446 *hostname = '\0';
2448 domain = strchr (hostname, '.');
2449 if (domain == NULL)
2450 domain = "";
2452 /* Scan for "default" and matching "machine" keywords */
2453 ftpfs_find_machine (host, domain);
2455 /* Scan for keywords following "default" and "machine" */
2456 while (1)
2458 int need_break = 0;
2459 keyword = ftpfs_netrc_next ();
2461 switch (keyword)
2463 case NETRC_LOGIN:
2464 if (ftpfs_netrc_next () == NETRC_NONE)
2466 need_break = 1;
2467 break;
2470 /* We have another name already - should not happen */
2471 if (*login)
2473 need_break = 1;
2474 break;
2477 /* We have login name now */
2478 *login = g_strdup (buffer);
2479 break;
2481 case NETRC_PASSWORD:
2482 case NETRC_PASSWD:
2483 if (ftpfs_netrc_next () == NETRC_NONE)
2485 need_break = 1;
2486 break;
2489 /* Ignore unsafe passwords */
2490 if (*login != NULL &&
2491 strcmp (*login, "anonymous") != 0 && strcmp (*login, "ftp") != 0
2492 && ftpfs_netrc_bad_mode (netrcname))
2494 need_break = 1;
2495 break;
2498 /* Remember password. pass may be NULL, so use tmp_pass */
2499 if (tmp_pass == NULL)
2500 tmp_pass = g_strdup (buffer);
2501 break;
2503 case NETRC_ACCOUNT:
2504 /* "account" is followed by a token which we ignore */
2505 if (ftpfs_netrc_next () == NETRC_NONE)
2507 need_break = 1;
2508 break;
2511 /* Ignore account, but warn user anyways */
2512 ftpfs_netrc_bad_mode (netrcname);
2513 break;
2515 default:
2516 /* Unexpected keyword or end of file */
2517 need_break = 1;
2518 break;
2521 if (need_break)
2522 break;
2525 g_free (netrc);
2526 g_free (netrcname);
2528 rupp = g_new (struct rupcache, 1);
2529 rupp->host = g_strdup (host);
2530 rupp->login = g_strdup (*login);
2531 rupp->pass = g_strdup (tmp_pass);
2533 rupp->next = rup_cache;
2534 rup_cache = rupp;
2536 *pass = tmp_pass;
2538 return 0;
2541 /* --------------------------------------------------------------------------------------------- */
2542 /*** public functions ****************************************************************************/
2543 /* --------------------------------------------------------------------------------------------- */
2545 /** This routine is called as the last step in load_setup */
2546 void
2547 ftpfs_init_passwd (void)
2549 ftpfs_anonymous_passwd = load_anon_passwd ();
2550 if (ftpfs_anonymous_passwd)
2551 return;
2553 /* If there is no anonymous ftp password specified
2554 * then we'll just use anonymous@
2555 * We don't send any other thing because:
2556 * - We want to remain anonymous
2557 * - We want to stop SPAM
2558 * - We don't want to let ftp sites to discriminate by the user,
2559 * host or country.
2561 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
2564 /* --------------------------------------------------------------------------------------------- */
2566 void
2567 init_ftpfs (void)
2569 static struct vfs_s_subclass ftpfs_subclass;
2571 tcp_init ();
2573 ftpfs_subclass.flags = VFS_S_REMOTE | VFS_S_USETMP;
2574 ftpfs_subclass.archive_same = ftpfs_archive_same;
2575 ftpfs_subclass.open_archive = ftpfs_open_archive;
2576 ftpfs_subclass.free_archive = ftpfs_free_archive;
2577 ftpfs_subclass.fh_open = ftpfs_fh_open;
2578 ftpfs_subclass.fh_close = ftpfs_fh_close;
2579 ftpfs_subclass.fh_free_data = ftpfs_fh_free_data;
2580 ftpfs_subclass.dir_load = ftpfs_dir_load;
2581 ftpfs_subclass.file_store = ftpfs_file_store;
2582 ftpfs_subclass.linear_start = ftpfs_linear_start;
2583 ftpfs_subclass.linear_read = ftpfs_linear_read;
2584 ftpfs_subclass.linear_close = ftpfs_linear_close;
2586 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2587 vfs_ftpfs_ops.name = "ftpfs";
2588 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2589 vfs_ftpfs_ops.prefix = "ftp";
2590 vfs_ftpfs_ops.done = &ftpfs_done;
2591 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2592 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2593 vfs_ftpfs_ops.chown = ftpfs_chown;
2594 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2595 vfs_ftpfs_ops.rename = ftpfs_rename;
2596 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2597 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2598 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2599 vfs_register_class (&vfs_ftpfs_ops);
2602 /* --------------------------------------------------------------------------------------------- */