Fixed errors and warnings in compilation stage.
[midnight-commander.git] / lib / vfs / mc-vfs / ftpfs.c
blob6eb2d3040593fc42c7343a08674125d46a0182c1
1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1995 Ching Hui
6 1995 Jakub Jelinek
7 1995, 1996, 1997 Miguel de Icaza
8 1997 Norbert Warmuth
9 1998 Pavel Machek
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Library General Public License
13 as published by the Free Software Foundation; either version 2 of
14 the License, or (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU Library General Public License for more details.
21 You should have received a copy of the GNU Library General Public
22 License along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
25 /**
26 * \file
27 * \brief Source: Virtual File System: FTP file system
28 * \author Ching Hui
29 * \author Jakub Jelinek
30 * \author Miguel de Icaza
31 * \author Norbert Warmuth
32 * \author Pavel Machek
33 * \date 1995, 1997, 1998
35 * \todo
36 - make it more robust - all the connects etc. should handle EADDRINUSE and
37 ERETRY (have I spelled these names correctly?)
38 - make the user able to flush a connection - all the caches will get empty
39 etc., (tarfs as well), we should give there a user selectable timeout
40 and assign a key sequence.
41 - use hash table instead of linklist to cache ftpfs directory.
43 What to do with this?
46 * NOTE: Usage of tildes is deprecated, consider:
47 * \verbatim
48 cd /#ftp:pavel@hobit
49 cd ~
50 \endverbatim
51 * And now: what do I want to do? Do I want to go to /home/pavel or to
52 * /#ftp:hobit/home/pavel? I think first has better sense...
54 \verbatim
56 int f = !strcmp( remote_path, "/~" );
57 if (f || !strncmp( remote_path, "/~/", 3 )) {
58 char *s;
59 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
60 g_free (remote_path);
61 remote_path = s;
64 \endverbatim
67 /* \todo Fix: Namespace pollution: horrible */
69 #include <config.h>
70 #include <stdlib.h> /* atoi() */
71 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
72 #include <netdb.h> /* struct hostent */
73 #include <sys/socket.h> /* AF_INET */
74 #include <netinet/in.h> /* struct in_addr */
75 #ifdef HAVE_ARPA_INET_H
76 #include <arpa/inet.h>
77 #endif
78 #include <arpa/ftp.h>
79 #include <arpa/telnet.h>
80 #include <sys/param.h>
81 #include <errno.h>
82 #include <ctype.h>
83 #include <fcntl.h>
84 #include <sys/time.h> /* gettimeofday() */
86 #include "lib/global.h"
88 #include "lib/tty/tty.h" /* enable/disable interrupt key */
90 #include "src/wtools.h" /* message() */
91 #include "src/main.h" /* print_vfs_message */
92 #include "src/history.h"
93 #include "src/setup.h" /* for load_anon_passwd */
94 #include "lib/mcconfig.h"
96 #include "utilvfs.h"
97 #include "xdirentry.h"
98 #include "vfs.h"
99 #include "vfs-impl.h"
100 #include "gc.h" /* vfs_stamp_create */
101 #include "netutil.h"
102 #include "ftpfs.h"
103 #ifndef MAXHOSTNAMELEN
104 # define MAXHOSTNAMELEN 64
105 #endif
107 #define UPLOAD_ZERO_LENGTH_FILE
108 #define SUP super->u.ftp
109 #define FH_SOCK fh->u.ftp.sock
111 #ifndef INADDR_NONE
112 #define INADDR_NONE 0xffffffff
113 #endif
115 /* for uclibc < 0.9.29 */
116 #ifndef AI_ADDRCONFIG
117 #define AI_ADDRCONFIG 0x0020
118 #endif
120 #define RFC_AUTODETECT 0
121 #define RFC_DARING 1
122 #define RFC_STRICT 2
124 #ifndef HAVE_SOCKLEN_T
125 typedef int socklen_t;
126 #endif
128 static int ftpfs_errno;
129 static int code;
131 /* Delay to retry a connection */
132 int ftpfs_retry_seconds = 30;
134 /* Method to use to connect to ftp sites */
135 int ftpfs_use_passive_connections = 1;
136 int ftpfs_use_passive_connections_over_proxy = 0;
138 /* Method used to get directory listings:
139 * 1: try 'LIST -la <path>', if it fails
140 * fall back to CWD <path>; LIST
141 * 0: always use CWD <path>; LIST
143 int ftpfs_use_unix_list_options = 1;
145 /* First "CWD <path>", then "LIST -la ." */
146 int ftpfs_first_cd_then_ls = 1;
148 /* Use the ~/.netrc */
149 int use_netrc = 1;
151 /* Anonymous setup */
152 char *ftpfs_anonymous_passwd = NULL;
153 int ftpfs_directory_timeout = 900;
155 /* Proxy host */
156 char *ftpfs_proxy_host = NULL;
158 /* wether we have to use proxy by default? */
159 int ftpfs_always_use_proxy;
161 #ifdef FIXME_LATER_ALIGATOR
162 static struct linklist *connections_list;
163 #endif
165 /* ftpfs_command wait_flag: */
166 #define NONE 0x00
167 #define WAIT_REPLY 0x01
168 #define WANT_STRING 0x02
169 static char reply_str[80];
171 static struct vfs_class vfs_ftpfs_ops;
173 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
174 Translate a Unix path, i.e. MC's internal path representation (e.g.
175 /somedir/somefile) to a path valid for the remote server. Every path
176 transfered to the remote server has to be mangled by this function
177 right prior to sending it.
178 Currently only Amiga ftp servers are handled in a special manner.
180 When the remote server is an amiga:
181 a) strip leading slash if necesarry
182 b) replace first occurance of ":/" with ":"
183 c) strip trailing "/."
186 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
187 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super,
188 const char *remote_path);
189 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply,
190 const char *fmt, ...) __attribute__ ((format (__printf__, 4, 5)));
191 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
192 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
193 const char *netrcpass);
194 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
196 static char *
197 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
199 if (!SUP.remote_is_amiga)
200 return g_strdup (remote_path);
201 else
203 char *ret, *p;
205 if (MEDATA->logfile)
207 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
208 fflush (MEDATA->logfile);
211 /* strip leading slash(es) */
212 while (*remote_path == '/')
213 remote_path++;
216 * Don't change "/" into "", e.g. "CWD " would be
217 * invalid.
219 if (*remote_path == '\0')
220 return g_strdup (".");
222 ret = g_strdup (remote_path);
224 /* replace first occurance of ":/" with ":" */
225 p = strchr (ret, ':');
226 if ((p != NULL) && (*(p + 1) == '/'))
227 memmove (p + 1, p + 2, strlen (p + 2) + 1);
229 /* strip trailing "/." */
230 p = strrchr (ret, '/');
231 if ((p != NULL) && (*(p + 1) == '.') && (*(p + 2) == '\0'))
232 *p = '\0';
234 return ret;
238 /* Extract the hostname and username from the path */
241 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
242 * ftp://sunsite.unc.edu/pub/linux
243 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
244 * ftp://tsx-11.mit.edu:8192/
245 * ftp://joe@foo.edu:11321/private
246 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
247 * is supplied.
251 #define FTP_COMMAND_PORT 21
253 static void
254 ftpfs_split_url (char *path, char **host, char **user, int *port, char **pass)
256 char *p;
258 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
260 if (!*user)
262 /* Look up user and password in netrc */
263 if (use_netrc)
264 ftpfs_netrc_lookup (*host, user, pass);
265 if (!*user)
266 *user = g_strdup ("anonymous");
269 /* Look up password in netrc for known user */
270 if (use_netrc && *user && pass && !*pass)
272 char *new_user;
274 ftpfs_netrc_lookup (*host, &new_user, pass);
276 /* If user is different, remove password */
277 if (new_user && strcmp (*user, new_user))
279 g_free (*pass);
280 *pass = NULL;
283 g_free (new_user);
286 g_free (p);
289 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
290 static int
291 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
293 char answer[BUF_1K];
294 int i;
296 for (;;)
298 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
300 if (string_buf)
301 *string_buf = 0;
302 code = 421;
303 return 4;
305 switch (sscanf (answer, "%d", &code))
307 case 0:
308 if (string_buf)
309 g_strlcpy (string_buf, answer, string_len);
310 code = 500;
311 return 5;
312 case 1:
313 if (answer[3] == '-')
315 while (1)
317 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
319 if (string_buf)
320 *string_buf = 0;
321 code = 421;
322 return 4;
324 if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' '))
325 break;
328 if (string_buf)
329 g_strlcpy (string_buf, answer, string_len);
330 return code / 100;
335 static int
336 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
338 int sock = ftpfs_open_socket (me, super);
339 if (sock != -1)
341 char *cwdir = SUP.cwdir;
342 close (SUP.sock);
343 SUP.sock = sock;
344 SUP.cwdir = NULL;
345 if (ftpfs_login_server (me, super, SUP.password))
347 if (!cwdir)
348 return 1;
349 sock = ftpfs_chdir_internal (me, super, cwdir);
350 g_free (cwdir);
351 return sock == COMPLETE;
353 SUP.cwdir = cwdir;
355 return 0;
358 static int
359 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt,
360 ...)
362 va_list ap;
363 char *cmdstr;
364 int status, cmdlen;
365 static int retry = 0;
366 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
368 va_start (ap, fmt);
369 cmdstr = g_strdup_vprintf (fmt, ap);
370 va_end (ap);
372 cmdlen = strlen (cmdstr);
373 cmdstr = g_realloc (cmdstr, cmdlen + 3);
374 strcpy (cmdstr + cmdlen, "\r\n");
375 cmdlen += 2;
377 if (MEDATA->logfile)
379 if (strncmp (cmdstr, "PASS ", 5) == 0)
381 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
383 else
385 size_t ret;
386 ret = fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
389 fflush (MEDATA->logfile);
392 got_sigpipe = 0;
393 tty_enable_interrupt_key ();
394 status = write (SUP.sock, cmdstr, cmdlen);
396 if (status < 0)
398 code = 421;
400 if (errno == EPIPE)
401 { /* Remote server has closed connection */
402 if (level == 0)
404 level = 1;
405 status = ftpfs_reconnect (me, super);
406 level = 0;
407 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0))
409 goto ok;
413 got_sigpipe = 1;
415 g_free (cmdstr);
416 tty_disable_interrupt_key ();
417 return TRANSIENT;
419 retry = 0;
421 tty_disable_interrupt_key ();
423 if (wait_reply)
425 status = ftpfs_get_reply (me, SUP.sock,
426 (wait_reply & WANT_STRING) ? reply_str : NULL,
427 sizeof (reply_str) - 1);
428 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
430 retry = 1;
431 level = 1;
432 status = ftpfs_reconnect (me, super);
433 level = 0;
434 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0))
436 goto ok;
439 retry = 0;
440 g_free (cmdstr);
441 return status;
443 g_free (cmdstr);
444 return COMPLETE;
447 static void
448 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
450 if (SUP.sock != -1)
452 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
453 ftpfs_command (me, super, NONE, "QUIT");
454 close (SUP.sock);
456 g_free (SUP.host);
457 g_free (SUP.user);
458 g_free (SUP.cwdir);
459 g_free (SUP.password);
462 /* some defines only used by ftpfs_changetype */
463 /* These two are valid values for the second parameter */
464 #define TYPE_ASCII 0
465 #define TYPE_BINARY 1
467 /* This one is only used to initialize bucket->isbinary, don't use it as
468 second parameter to ftpfs_changetype. */
469 #define TYPE_UNKNOWN -1
471 static int
472 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
474 if (binary != SUP.isbinary)
476 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
477 ERRNOR (EIO, -1);
478 SUP.isbinary = binary;
480 return binary;
483 /* This routine logs the user in */
484 static int
485 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
487 char *pass;
488 char *op;
489 char *name; /* login user name */
490 int anon = 0;
491 char reply_string[BUF_MEDIUM];
493 SUP.isbinary = TYPE_UNKNOWN;
495 if (SUP.password) /* explicit password */
496 op = g_strdup (SUP.password);
497 else if (netrcpass) /* password from netrc */
498 op = g_strdup (netrcpass);
499 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp"))
501 if (!ftpfs_anonymous_passwd) /* default anonymous password */
502 ftpfs_init_passwd ();
503 op = g_strdup (ftpfs_anonymous_passwd);
504 anon = 1;
506 else
507 { /* ask user */
508 char *p;
510 p = g_strconcat (_(" FTP: Password required for "), SUP.user, " ", (char *) NULL);
511 op = vfs_get_password (p);
512 g_free (p);
513 if (op == NULL)
514 ERRNOR (EPERM, 0);
515 SUP.password = g_strdup (op);
518 if (!anon || MEDATA->logfile)
519 pass = op;
520 else
522 pass = g_strconcat ("-", op, (char *) NULL);
523 wipe_password (op);
526 /* Proxy server accepts: username@host-we-want-to-connect */
527 if (SUP.proxy)
529 name =
530 g_strconcat (SUP.user, "@",
531 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host, (char *) NULL);
533 else
534 name = g_strdup (SUP.user);
536 if (ftpfs_get_reply (me, SUP.sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
538 g_strup (reply_string);
539 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
540 if (MEDATA->logfile)
542 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n", SUP.remote_is_amiga);
543 fflush (MEDATA->logfile);
546 print_vfs_message (_("ftpfs: sending login name"));
548 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
550 case CONTINUE:
551 print_vfs_message (_("ftpfs: sending user password"));
552 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
553 if (code == CONTINUE)
555 char *p;
557 p = g_strdup_printf (_("FTP: Account required for user %s"), SUP.user);
558 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
559 g_free (p);
560 if (op == NULL)
561 ERRNOR (EPERM, 0);
562 print_vfs_message (_("ftpfs: sending user account"));
563 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
564 g_free (op);
566 if (code != COMPLETE)
567 break;
568 /* fall through */
570 case COMPLETE:
571 print_vfs_message (_("ftpfs: logged in"));
572 wipe_password (pass);
573 g_free (name);
574 return 1;
576 default:
577 SUP.failed_on_login = 1;
578 if (SUP.password)
579 wipe_password (SUP.password);
580 SUP.password = 0;
582 goto login_fail;
585 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "), SUP.user);
586 login_fail:
587 wipe_password (pass);
588 g_free (name);
589 ERRNOR (EPERM, 0);
592 static struct no_proxy_entry
594 char *domain;
595 void *next;
596 } *no_proxy;
598 static void
599 ftpfs_load_no_proxy_list (void)
601 /* FixMe: shouldn't be hardcoded!!! */
602 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
603 struct no_proxy_entry *np, *current = 0;
604 FILE *npf;
605 int c;
606 char *p;
607 static char *mc_file;
609 if (mc_file)
610 return;
612 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
613 if (exist_file (mc_file))
615 npf = fopen (mc_file, "r");
616 if (npf != NULL)
618 while (fgets (s, sizeof (s), npf) != NULL)
620 p = strchr (s, '\n');
621 if (p == NULL) /* skip bogus entries */
623 while ((c = fgetc (npf)) != EOF && c != '\n')
625 continue;
628 if (p == s)
629 continue;
631 *p = '\0';
633 np = g_new (struct no_proxy_entry, 1);
634 np->domain = g_strdup (s);
635 np->next = NULL;
636 if (no_proxy)
637 current->next = np;
638 else
639 no_proxy = np;
640 current = np;
642 fclose (npf);
645 g_free (mc_file);
648 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
649 static int
650 ftpfs_check_proxy (const char *host)
652 struct no_proxy_entry *npe;
654 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
655 return 0; /* sanity check */
657 if (*host == '!')
658 return 1;
660 if (!ftpfs_always_use_proxy)
661 return 0;
663 if (!strchr (host, '.'))
664 return 0;
666 ftpfs_load_no_proxy_list ();
667 for (npe = no_proxy; npe; npe = npe->next)
669 char *domain = npe->domain;
671 if (domain[0] == '.')
673 int ld = strlen (domain);
674 int lh = strlen (host);
676 while (ld && lh && host[lh - 1] == domain[ld - 1])
678 ld--;
679 lh--;
682 if (!ld)
683 return 0;
685 else if (!g_strcasecmp (host, domain))
686 return 0;
689 return 1;
692 static void
693 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
695 char *user, *dir;
697 dir = vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
698 g_free (user);
699 g_free (dir);
702 static int
703 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
705 struct addrinfo hints, *res, *curr_res;
706 int my_socket = 0;
707 char *host = NULL;
708 char *port = NULL;
709 int tmp_port;
710 int e;
712 (void) me;
714 /* Use a proxy host? */
715 host = g_strdup (SUP.host);
717 if (!host || !*host)
719 print_vfs_message (_("ftpfs: Invalid host name."));
720 ftpfs_errno = EINVAL;
721 g_free (host);
722 return -1;
725 /* Hosts to connect to that start with a ! should use proxy */
726 tmp_port = SUP.port;
728 if (SUP.proxy)
730 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
733 port = g_strdup_printf ("%hu", (unsigned short) tmp_port);
734 if (port == NULL)
736 g_free (host);
737 ftpfs_errno = errno;
738 return -1;
741 tty_enable_interrupt_key (); /* clear the interrupt flag */
743 memset (&hints, 0, sizeof (struct addrinfo));
744 hints.ai_socktype = SOCK_STREAM;
745 hints.ai_flags = AI_ADDRCONFIG;
748 e = getaddrinfo (host, port, &hints, &res);
749 g_free (port);
750 port = NULL;
752 if (e != 0)
754 tty_disable_interrupt_key ();
755 print_vfs_message (_("ftpfs: %s"), gai_strerror (e));
756 g_free (host);
757 ftpfs_errno = EINVAL;
758 return -1;
761 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
764 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
766 if (my_socket < 0)
769 if (curr_res->ai_next != NULL)
770 continue;
772 tty_disable_interrupt_key ();
773 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno));
774 g_free (host);
775 freeaddrinfo (res);
776 ftpfs_errno = errno;
777 return -1;
780 print_vfs_message (_("ftpfs: making connection to %s"), host);
781 g_free (host);
782 host = NULL;
784 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
785 break;
787 ftpfs_errno = errno;
788 close (my_socket);
790 if (errno == EINTR && tty_got_interrupt ())
792 print_vfs_message (_("ftpfs: connection interrupted by user"));
794 else if (res->ai_next == NULL)
796 print_vfs_message (_("ftpfs: connection to server failed: %s"),
797 unix_error_string (errno));
799 else
801 continue;
804 freeaddrinfo (res);
805 tty_disable_interrupt_key ();
806 return -1;
809 freeaddrinfo (res);
810 tty_disable_interrupt_key ();
811 return my_socket;
814 static int
815 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
817 int retry_seconds, count_down;
819 /* We do not want to use the passive if we are using proxies */
820 if (SUP.proxy)
821 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
823 retry_seconds = 0;
826 SUP.failed_on_login = 0;
828 SUP.sock = ftpfs_open_socket (me, super);
829 if (SUP.sock == -1)
830 return -1;
832 if (ftpfs_login_server (me, super, NULL))
834 /* Logged in, no need to retry the connection */
835 break;
837 else
839 if (SUP.failed_on_login)
841 /* Close only the socket descriptor */
842 close (SUP.sock);
844 else
846 return -1;
848 if (ftpfs_retry_seconds)
850 retry_seconds = ftpfs_retry_seconds;
851 tty_enable_interrupt_key ();
852 for (count_down = retry_seconds; count_down; count_down--)
854 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"),
855 count_down);
856 sleep (1);
857 if (tty_got_interrupt ())
859 /* ftpfs_errno = E; */
860 tty_disable_interrupt_key ();
861 return 0;
864 tty_disable_interrupt_key ();
868 while (retry_seconds);
870 SUP.cwdir = ftpfs_get_current_directory (me, super);
871 if (!SUP.cwdir)
872 SUP.cwdir = g_strdup (PATH_SEP_STR);
873 return 0;
876 static int
877 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
878 const char *archive_name, char *op)
880 char *host, *user, *password;
881 int port;
883 (void) archive_name;
885 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
887 SUP.host = host;
888 SUP.user = user;
889 SUP.port = port;
890 SUP.cwdir = NULL;
891 SUP.proxy = 0;
892 if (ftpfs_check_proxy (host))
893 SUP.proxy = ftpfs_proxy_host;
894 SUP.password = password;
895 SUP.use_passive_connection = ftpfs_use_passive_connections;
896 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
897 SUP.isbinary = TYPE_UNKNOWN;
898 SUP.remote_is_amiga = 0;
899 super->name = g_strdup ("/");
900 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755));
902 return ftpfs_open_archive_int (me, super);
905 static int
906 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
907 const char *archive_name, char *op, void *cookie)
909 char *host, *user;
910 int port;
912 (void) me;
913 (void) archive_name;
914 (void) cookie;
916 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
918 port = ((strcmp (host, SUP.host) == 0) && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
920 g_free (host);
921 g_free (user);
923 return port;
926 /* The returned directory should always contain a trailing slash */
927 static char *
928 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
930 char buf[BUF_8K], *bufp, *bufq;
932 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
933 ftpfs_get_reply (me, SUP.sock, buf, sizeof (buf)) == COMPLETE)
935 bufp = NULL;
936 for (bufq = buf; *bufq; bufq++)
937 if (*bufq == '"')
939 if (!bufp)
941 bufp = bufq + 1;
943 else
945 *bufq = 0;
946 if (*bufp)
948 if (*(bufq - 1) != '/')
950 *bufq++ = '/';
951 *bufq = 0;
953 if (*bufp == '/')
954 return g_strdup (bufp);
955 else
957 /* If the remote server is an Amiga a leading slash
958 might be missing. MC needs it because it is used
959 as separator between hostname and path internally. */
960 return g_strconcat ("/", bufp, (char *) NULL);
963 else
965 ftpfs_errno = EIO;
966 return NULL;
971 ftpfs_errno = EIO;
972 return NULL;
976 /* Setup Passive ftp connection, we use it for source routed connections */
977 static int
978 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
979 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
981 char *c;
983 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") == COMPLETE)
985 int port;
986 /* (|||<port>|) */
987 c = strchr (reply_str, '|');
988 if (c == NULL)
989 return 0;
990 if (strlen (c) > 3)
991 c += 3;
992 else
993 return 0;
995 port = atoi (c);
996 if (port < 0 || port > 65535)
997 return 0;
998 port = htons (port);
1000 switch (sa->ss_family)
1002 case AF_INET:
1003 ((struct sockaddr_in *) sa)->sin_port = port;
1004 break;
1005 case AF_INET6:
1006 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1007 break;
1008 default:
1009 print_vfs_message (_("ftpfs: invalid address family"));
1010 ERRNOR (EINVAL, -1);
1013 else if (sa->ss_family == AF_INET)
1015 int xa, xb, xc, xd, xe, xf;
1016 char n[6];
1018 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
1019 return 0;
1021 /* Parse remote parameters */
1022 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
1024 if (!*c)
1025 return 0;
1026 if (!isdigit ((unsigned char) *c))
1027 return 0;
1028 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1029 return 0;
1031 n[0] = (unsigned char) xa;
1032 n[1] = (unsigned char) xb;
1033 n[2] = (unsigned char) xc;
1034 n[3] = (unsigned char) xd;
1035 n[4] = (unsigned char) xe;
1036 n[5] = (unsigned char) xf;
1038 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1039 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1041 else
1042 return 0;
1044 if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
1045 return 0;
1047 return 1;
1050 static int
1051 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1053 struct sockaddr_storage data_addr;
1054 socklen_t data_addrlen;
1055 int data_sock, result;
1057 again:
1058 memset (&data_addr, 0, sizeof (struct sockaddr_storage));
1059 data_addrlen = sizeof (struct sockaddr_storage);
1061 if (SUP.use_passive_connection)
1062 result = getpeername (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen);
1063 else
1064 result = getsockname (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen);
1066 if (result == -1)
1067 return -1;
1069 switch (data_addr.ss_family)
1071 case AF_INET:
1072 ((struct sockaddr_in *) &data_addr)->sin_port = 0;
1073 break;
1074 case AF_INET6:
1075 ((struct sockaddr_in6 *) &data_addr)->sin6_port = 0;
1076 break;
1077 default:
1078 print_vfs_message (_("ftpfs: invalid address family"));
1079 ERRNOR (EINVAL, -1);
1082 data_sock = socket (data_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
1083 if (data_sock < 0)
1085 if (SUP.use_passive_connection)
1087 print_vfs_message (_("ftpfs: could not setup passive mode: %s"),
1088 unix_error_string (errno));
1089 SUP.use_passive_connection = 0;
1090 goto again;
1093 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1094 return -1;
1097 if (SUP.use_passive_connection)
1100 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1101 return data_sock;
1103 SUP.use_passive_connection = 0;
1104 print_vfs_message (_("ftpfs: could not setup passive mode"));
1106 close (data_sock);
1107 goto again;
1110 /* If passive setup fails, fallback to active connections */
1111 /* Active FTP connection */
1112 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) == 0) &&
1113 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) == 0) &&
1114 (listen (data_sock, 1) == 0))
1116 unsigned short int port;
1117 char *addr;
1118 unsigned int af;
1120 switch (data_addr.ss_family)
1122 case AF_INET:
1123 af = FTP_INET;
1124 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1125 break;
1126 case AF_INET6:
1127 af = FTP_INET6;
1128 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1129 break;
1130 default:
1131 print_vfs_message (_("ftpfs: invalid address family"));
1132 ERRNOR (EINVAL, -1);
1135 port = ntohs (port);
1137 addr = g_try_malloc (NI_MAXHOST);
1138 if (addr == NULL)
1139 ERRNOR (ENOMEM, -1);
1141 if (getnameinfo
1142 ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1143 NI_NUMERICHOST) != 0)
1145 g_free (addr);
1146 ERRNOR (EIO, -1);
1149 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
1151 g_free (addr);
1152 return data_sock;
1154 g_free (addr);
1156 if (FTP_INET == af)
1158 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1159 unsigned char *p = (unsigned char *) &port;
1161 if (ftpfs_command (me, super, WAIT_REPLY,
1162 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1163 p[0], p[1]) == COMPLETE)
1164 return data_sock;
1167 close (data_sock);
1168 ftpfs_errno = EIO;
1169 return -1;
1172 static int
1173 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1174 const char *remote, int isbinary, int reget)
1176 struct sockaddr_storage from;
1177 int s, j, data;
1178 socklen_t fromlen = sizeof(from);
1180 s = ftpfs_initconn (me, super);
1181 if (s == -1)
1182 return -1;
1184 if (ftpfs_changetype (me, super, isbinary) == -1)
1185 return -1;
1186 if (reget > 0)
1188 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1189 if (j != CONTINUE)
1190 return -1;
1192 if (remote)
1194 char *remote_path = ftpfs_translate_path (me, super, remote);
1195 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1196 /* WarFtpD can't STORE //filename */
1197 (*remote_path == '/') ? remote_path + 1 : remote_path);
1198 g_free (remote_path);
1200 else
1201 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1203 if (j != PRELIM)
1204 ERRNOR (EPERM, -1);
1205 tty_enable_interrupt_key ();
1206 if (SUP.use_passive_connection)
1207 data = s;
1208 else
1210 data = accept (s, (struct sockaddr *) &from, &fromlen);
1211 if (data < 0)
1213 ftpfs_errno = errno;
1214 close (s);
1215 return -1;
1217 close (s);
1219 tty_disable_interrupt_key ();
1220 return data;
1223 #define ABORT_TIMEOUT 5
1224 static void
1225 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
1227 struct vfs_s_super *super = FH_SUPER;
1228 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1229 fd_set mask;
1230 char buf[1024];
1231 int dsock = FH_SOCK;
1232 FH_SOCK = -1;
1233 SUP.ctl_connection_busy = 0;
1235 print_vfs_message (_("ftpfs: aborting transfer."));
1236 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1238 print_vfs_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1239 if (dsock != -1)
1240 close (dsock);
1241 return;
1244 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1246 print_vfs_message (_("ftpfs: abort failed"));
1247 if (dsock != -1)
1248 close (dsock);
1249 return;
1251 if (dsock != -1)
1253 FD_ZERO (&mask);
1254 FD_SET (dsock, &mask);
1255 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1257 struct timeval start_tim, tim;
1258 gettimeofday (&start_tim, NULL);
1259 /* flush the remaining data */
1260 while (read (dsock, buf, sizeof (buf)) > 0)
1262 gettimeofday (&tim, NULL);
1263 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT)
1265 /* server keeps sending, drop the connection and ftpfs_reconnect */
1266 close (dsock);
1267 ftpfs_reconnect (me, super);
1268 return;
1272 close (dsock);
1274 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1275 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1278 #if 0
1279 static void
1280 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1281 struct vfs_s_inode *dir)
1283 struct linklist *flist;
1284 struct direntry *fe, *fel;
1285 char tmp[MC_MAXPATHLEN];
1286 int depth;
1288 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1289 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1291 /* flist->data->l_stat is alread initialized with 0 */
1292 fel = flist->data;
1293 if (S_ISLNK (fel->s.st_mode) && fel->linkname)
1295 if (fel->linkname[0] == '/')
1297 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1298 continue;
1299 strcpy (tmp, fel->linkname);
1301 else
1303 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1304 continue;
1305 strcpy (tmp, dir->remote_path);
1306 if (tmp[1] != '\0')
1307 strcat (tmp, "/");
1308 strcat (tmp + 1, fel->linkname);
1310 for (depth = 0; depth < 100; depth++)
1311 { /* depth protects against recursive symbolic links */
1312 canonicalize_pathname (tmp);
1313 fe = _get_file_entry (bucket, tmp, 0, 0);
1314 if (fe)
1316 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1318 /* Symlink points to link which isn't resolved, yet. */
1319 if (fe->linkname[0] == '/')
1321 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1322 break;
1323 strcpy (tmp, fe->linkname);
1325 else
1327 /* at this point tmp looks always like this
1328 /directory/filename, i.e. no need to check
1329 strrchr's return value */
1330 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1331 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1332 break;
1333 strcat (tmp, fe->linkname);
1335 continue;
1337 else
1339 fel->l_stat = g_new (struct stat, 1);
1340 if (S_ISLNK (fe->s.st_mode))
1341 *fel->l_stat = *fe->l_stat;
1342 else
1343 *fel->l_stat = fe->s;
1344 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1347 break;
1351 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1354 static void
1355 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1356 struct vfs_s_inode *dir)
1358 char buffer[2048] = "", *filename;
1359 int sock;
1360 FILE *fp;
1361 struct stat s;
1362 struct linklist *flist;
1363 struct direntry *fe;
1364 int switch_method = 0;
1366 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1367 if (strchr (dir->remote_path, ' '))
1369 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1371 print_vfs_message (_("ftpfs: CWD failed."));
1372 return;
1374 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1376 else
1377 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1379 if (sock == -1)
1381 print_vfs_message (_("ftpfs: couldn't resolve symlink"));
1382 return;
1385 fp = fdopen (sock, "r");
1386 if (fp == NULL)
1388 close (sock);
1389 print_vfs_message (_("ftpfs: couldn't resolve symlink"));
1390 return;
1392 tty_enable_interrupt_key ();
1393 flist = dir->file_list->next;
1394 while (1)
1398 if (flist == dir->file_list)
1399 goto done;
1400 fe = flist->data;
1401 flist = flist->next;
1403 while (!S_ISLNK (fe->s.st_mode));
1404 while (1)
1406 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1407 goto done;
1408 if (MEDATA->logfile)
1410 fputs (buffer, MEDATA->logfile);
1411 fflush (MEDATA->logfile);
1413 vfs_die ("This code should be commented out\n");
1414 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1416 int r = strcmp (fe->name, filename);
1417 g_free (filename);
1418 if (r == 0)
1420 if (S_ISLNK (s.st_mode))
1422 /* This server doesn't understand LIST -lLa */
1423 switch_method = 1;
1424 goto done;
1426 fe->l_stat = g_new (struct stat, 1);
1427 if (fe->l_stat == NULL)
1428 goto done;
1429 *fe->l_stat = s;
1430 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1431 break;
1433 if (r < 0)
1434 break;
1438 done:
1439 while (fgets (buffer, sizeof (buffer), fp) != NULL);
1440 tty_disable_interrupt_key ();
1441 fclose (fp);
1442 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1445 static void
1446 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1448 print_vfs_message (_("Resolving symlink..."));
1450 if (SUP.strict_rfc959_list_cmd)
1451 resolve_symlink_without_ls_options (me, super, dir);
1452 else
1453 resolve_symlink_with_ls_options (me, super, dir);
1455 #endif
1457 static int
1458 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1460 struct vfs_s_entry *ent;
1461 struct vfs_s_super *super = dir->super;
1462 int sock, num_entries = 0;
1463 char buffer[BUF_8K];
1464 int cd_first;
1466 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1467 || (strchr (remote_path, ' ') != NULL);
1469 again:
1470 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1471 remote_path,
1472 SUP.strict ==
1473 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1475 if (cd_first)
1477 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1479 ftpfs_errno = ENOENT;
1480 print_vfs_message (_("ftpfs: CWD failed."));
1481 return -1;
1485 gettimeofday (&dir->timestamp, NULL);
1486 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1488 if (SUP.strict == RFC_STRICT)
1489 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1490 else if (cd_first)
1491 /* Dirty hack to avoid autoprepending / to . */
1492 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1493 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1494 else
1496 /* Trailing "/." is necessary if remote_path is a symlink */
1497 char *path = concat_dir_and_file (remote_path, ".");
1498 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1499 g_free (path);
1502 if (sock == -1)
1503 goto fallback;
1505 /* Clear the interrupt flag */
1506 tty_enable_interrupt_key ();
1508 while (1)
1510 int i;
1511 int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1512 sock);
1513 if (!res)
1514 break;
1516 if (res == EINTR)
1518 me->verrno = ECONNRESET;
1519 close (sock);
1520 tty_disable_interrupt_key ();
1521 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1522 print_vfs_message (_("%s: failure"), me->name);
1523 return -1;
1526 if (MEDATA->logfile)
1528 fputs (buffer, MEDATA->logfile);
1529 fputs ("\n", MEDATA->logfile);
1530 fflush (MEDATA->logfile);
1533 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1534 i = ent->ino->st.st_nlink;
1535 if (!vfs_parse_ls_lga (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname))
1537 vfs_s_free_entry (me, ent);
1538 continue;
1540 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1541 num_entries++;
1542 vfs_s_insert_entry (me, dir, ent);
1545 close (sock);
1546 me->verrno = E_REMOTE;
1547 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1548 goto fallback;
1550 if (num_entries == 0 && cd_first == 0)
1552 /* The LIST command may produce an empty output. In such scenario
1553 it is not clear whether this is caused by `remote_path' being
1554 a non-existent path or for some other reason (listing emtpy
1555 directory without the -a option, non-readable directory, etc.).
1557 Since `dir_load' is a crucial method, when it comes to determine
1558 whether a given path is a _directory_, the code must try its best
1559 to determine the type of `remote_path'. The only reliable way to
1560 achieve this is trough issuing a CWD command. */
1562 cd_first = 1;
1563 goto again;
1566 if (SUP.strict == RFC_AUTODETECT)
1567 SUP.strict = RFC_DARING;
1569 print_vfs_message (_("%s: done."), me->name);
1570 return 0;
1572 fallback:
1573 if (SUP.strict == RFC_AUTODETECT)
1575 /* It's our first attempt to get a directory listing from this
1576 server (UNIX style LIST command) */
1577 SUP.strict = RFC_STRICT;
1578 /* I hate goto, but recursive call needs another 8K on stack */
1579 /* return ftpfs_dir_load (me, dir, remote_path); */
1580 cd_first = 1;
1581 goto again;
1583 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1584 ERRNOR (EACCES, -1);
1587 static int
1588 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *localname)
1590 int h, sock, n_read, n_written;
1591 off_t n_stored;
1592 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1593 struct linger li;
1594 #else
1595 int flag_one = 1;
1596 #endif
1597 char buffer[8192];
1598 struct stat s;
1599 char *w_buf;
1600 struct vfs_s_super *super = FH_SUPER;
1602 h = open (localname, O_RDONLY);
1603 if (h == -1)
1604 ERRNOR (EIO, -1);
1605 sock =
1606 ftpfs_open_data_connection (me, super,
1607 fh->u.ftp.append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1608 if (sock < 0 || fstat (h, &s) == -1)
1610 close (h);
1611 return -1;
1613 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1614 li.l_onoff = 1;
1615 li.l_linger = 120;
1616 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1617 #else
1618 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1619 #endif
1620 n_stored = 0;
1622 tty_enable_interrupt_key ();
1623 while (1)
1625 while ((n_read = read (h, buffer, sizeof (buffer))) == -1)
1627 if (errno == EINTR)
1629 if (tty_got_interrupt ())
1631 ftpfs_errno = EINTR;
1632 goto error_return;
1634 else
1635 continue;
1637 ftpfs_errno = errno;
1638 goto error_return;
1640 if (n_read == 0)
1641 break;
1642 n_stored += n_read;
1643 w_buf = buffer;
1644 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1646 if (n_written == -1)
1648 if (errno == EINTR && !tty_got_interrupt ())
1650 continue;
1652 ftpfs_errno = errno;
1653 goto error_return;
1655 w_buf += n_written;
1656 n_read -= n_written;
1658 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1659 (unsigned long) n_stored, (unsigned long) s.st_size);
1661 tty_disable_interrupt_key ();
1662 close (sock);
1663 close (h);
1664 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1665 ERRNOR (EIO, -1);
1666 return 0;
1667 error_return:
1668 tty_disable_interrupt_key ();
1669 close (sock);
1670 close (h);
1671 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1672 return -1;
1675 static int
1676 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1678 char *name = vfs_s_fullpath (me, fh->ino);
1680 if (!name)
1681 return 0;
1682 FH_SOCK = ftpfs_open_data_connection (me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1683 g_free (name);
1684 if (FH_SOCK == -1)
1685 ERRNOR (EACCES, 0);
1686 fh->linear = LS_LINEAR_OPEN;
1687 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1688 fh->u.ftp.append = 0;
1689 return 1;
1692 static int
1693 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1695 int n;
1696 struct vfs_s_super *super = FH_SUPER;
1698 while ((n = read (FH_SOCK, buf, len)) < 0)
1700 if ((errno == EINTR) && !tty_got_interrupt ())
1701 continue;
1702 break;
1705 if (n < 0)
1706 ftpfs_linear_abort (me, fh);
1708 if (!n)
1710 SUP.ctl_connection_busy = 0;
1711 close (FH_SOCK);
1712 FH_SOCK = -1;
1713 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1714 ERRNOR (E_REMOTE, -1);
1715 return 0;
1717 ERRNOR (errno, n);
1720 static void
1721 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1723 if (FH_SOCK != -1)
1724 ftpfs_linear_abort (me, fh);
1727 static int
1728 ftpfs_ctl (void *fh, int ctlop, void *arg)
1730 (void) arg;
1732 switch (ctlop)
1734 case VFS_CTL_IS_NOTREADY:
1736 int v;
1738 if (!FH->linear)
1739 vfs_die ("You may not do this");
1740 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1741 return 0;
1743 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1744 if (((v < 0) && (errno == EINTR)) || v == 0)
1745 return 1;
1746 return 0;
1748 default:
1749 return 0;
1753 static int
1754 ftpfs_send_command (struct vfs_class *me, const char *filename, const char *cmd, int flags)
1756 const char *rpath;
1757 char *p, *mpath = g_strdup (filename);
1758 struct vfs_s_super *super;
1759 int r;
1760 int flush_directory_cache = (flags & OPT_FLUSH);
1762 rpath = vfs_s_get_path_mangle (me, mpath, &super, 0);
1763 if (rpath == NULL)
1765 g_free (mpath);
1766 return -1;
1768 p = ftpfs_translate_path (me, super, rpath);
1769 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1770 g_free (p);
1771 vfs_stamp_create (&vfs_ftpfs_ops, super);
1772 if (flags & OPT_IGNORE_ERROR)
1773 r = COMPLETE;
1774 if (r != COMPLETE)
1776 me->verrno = EPERM;
1777 g_free (mpath);
1778 return -1;
1780 if (flush_directory_cache)
1781 vfs_s_invalidate (me, super);
1782 g_free (mpath);
1783 return 0;
1786 /* This routine is called as the last step in load_setup */
1787 void
1788 ftpfs_init_passwd (void)
1790 ftpfs_anonymous_passwd = load_anon_passwd ();
1791 if (ftpfs_anonymous_passwd)
1792 return;
1794 /* If there is no anonymous ftp password specified
1795 * then we'll just use anonymous@
1796 * We don't send any other thing because:
1797 * - We want to remain anonymous
1798 * - We want to stop SPAM
1799 * - We don't want to let ftp sites to discriminate by the user,
1800 * host or country.
1802 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1805 static int
1806 ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1808 char buf[BUF_SMALL];
1809 int ret;
1811 g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1813 ret = ftpfs_send_command (me, path, buf, OPT_FLUSH);
1815 if (mc_config_get_bool (mc_main_config, CONFIG_APP_SECTION, "ignore_ftp_chattr_errors", TRUE))
1817 return 0;
1820 return ret;
1823 static int
1824 ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1826 #if 0
1827 ftpfs_errno = EPERM;
1828 return -1;
1829 #else
1830 /* Everyone knows it is not possible to chown remotely, so why bother them.
1831 If someone's root, then copy/move will always try to chown it... */
1832 (void) me;
1833 (void) path;
1834 (void) owner;
1835 (void) group;
1836 return 0;
1837 #endif
1840 static int
1841 ftpfs_unlink (struct vfs_class *me, const char *path)
1843 return ftpfs_send_command (me, path, "DELE /%s", OPT_FLUSH);
1846 /* Return 1 if path is the same directory as the one we are in now */
1847 static int
1848 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1850 (void) me;
1852 if (!SUP.cwdir)
1853 return 0;
1854 if (strcmp (path, SUP.cwdir) == 0)
1855 return 1;
1856 return 0;
1859 static int
1860 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1862 int r;
1863 char *p;
1865 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1866 return COMPLETE;
1868 p = ftpfs_translate_path (me, super, remote_path);
1869 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1870 g_free (p);
1872 if (r != COMPLETE)
1874 ftpfs_errno = EIO;
1876 else
1878 g_free (SUP.cwdir);
1879 SUP.cwdir = g_strdup (remote_path);
1880 SUP.cwd_deferred = 0;
1882 return r;
1885 static int
1886 ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1888 ftpfs_send_command (me, path1, "RNFR /%s", OPT_FLUSH);
1889 return ftpfs_send_command (me, path2, "RNTO /%s", OPT_FLUSH);
1892 static int
1893 ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1895 (void) mode; /* FIXME: should be used */
1897 return ftpfs_send_command (me, path, "MKD /%s", OPT_FLUSH);
1900 static int
1901 ftpfs_rmdir (struct vfs_class *me, const char *path)
1903 return ftpfs_send_command (me, path, "RMD /%s", OPT_FLUSH);
1906 static int
1907 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags, int mode)
1909 (void) mode;
1911 fh->u.ftp.append = 0;
1912 /* File will be written only, so no need to retrieve it from ftp server */
1913 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR)))
1915 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1916 struct linger li;
1917 #else
1918 int li = 1;
1919 #endif
1920 char *name;
1922 /* ftpfs_linear_start() called, so data will be written
1923 * to local temporary file and stored to ftp server
1924 * by vfs_s_close later
1926 if (FH_SUPER->u.ftp.ctl_connection_busy)
1928 if (!fh->ino->localname)
1930 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1931 fh->ino->ent->name);
1932 if (handle == -1)
1933 return -1;
1934 close (handle);
1935 fh->u.ftp.append = flags & O_APPEND;
1937 return 0;
1939 name = vfs_s_fullpath (me, fh->ino);
1940 if (!name)
1941 return -1;
1942 fh->handle =
1943 ftpfs_open_data_connection (me, fh->ino->super,
1944 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1945 g_free (name);
1947 if (fh->handle < 0)
1948 return -1;
1949 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1950 li.l_onoff = 1;
1951 li.l_linger = 120;
1952 #endif
1953 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1955 if (fh->ino->localname)
1957 unlink (fh->ino->localname);
1958 g_free (fh->ino->localname);
1959 fh->ino->localname = NULL;
1961 return 0;
1964 if (!fh->ino->localname)
1965 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1966 return -1;
1967 if (!fh->ino->localname)
1968 vfs_die ("retrieve_file failed to fill in localname");
1969 return 0;
1972 static int
1973 ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1975 if (fh->handle != -1 && !fh->ino->localname)
1977 close (fh->handle);
1978 fh->handle = -1;
1979 /* File is stored to destination already, so
1980 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1982 fh->changed = 0;
1983 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1984 ERRNOR (EIO, -1);
1985 vfs_s_invalidate (me, FH_SUPER);
1987 return 0;
1990 static void
1991 ftpfs_done (struct vfs_class *me)
1993 struct no_proxy_entry *np;
1995 (void) me;
1997 while (no_proxy)
1999 np = no_proxy->next;
2000 g_free (no_proxy->domain);
2001 g_free (no_proxy);
2002 no_proxy = np;
2004 g_free (ftpfs_anonymous_passwd);
2005 g_free (ftpfs_proxy_host);
2008 static void
2009 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
2011 struct vfs_s_super *super = MEDATA->supers;
2012 char *name;
2014 while (super)
2016 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
2017 (*func) (name);
2018 g_free (name);
2019 super = super->next;
2023 static char buffer[BUF_MEDIUM];
2024 static char *netrc;
2025 static const char *netrcp;
2027 /* This should match the keywords[] array below */
2028 typedef enum
2030 NETRC_NONE = 0,
2031 NETRC_DEFAULT,
2032 NETRC_MACHINE,
2033 NETRC_LOGIN,
2034 NETRC_PASSWORD,
2035 NETRC_PASSWD,
2036 NETRC_ACCOUNT,
2037 NETRC_MACDEF,
2038 NETRC_UNKNOWN
2039 } keyword_t;
2041 static keyword_t
2042 ftpfs_netrc_next (void)
2044 char *p;
2045 keyword_t i;
2046 static const char *const keywords[] = { "default", "machine",
2047 "login", "password", "passwd", "account", "macdef", NULL
2051 while (1)
2053 netrcp = skip_separators (netrcp);
2054 if (*netrcp != '\n')
2055 break;
2056 netrcp++;
2058 if (!*netrcp)
2059 return NETRC_NONE;
2060 p = buffer;
2061 if (*netrcp == '"')
2063 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++)
2065 if (*netrcp == '\\')
2066 netrcp++;
2067 *p++ = *netrcp;
2070 else
2072 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
2073 *netrcp != ',' && *netrcp; netrcp++)
2075 if (*netrcp == '\\')
2076 netrcp++;
2077 *p++ = *netrcp;
2080 *p = 0;
2081 if (!*buffer)
2082 return NETRC_NONE;
2084 i = NETRC_DEFAULT;
2085 while (keywords[i - 1])
2087 if (!strcmp (keywords[i - 1], buffer))
2088 return i;
2090 i++;
2093 return NETRC_UNKNOWN;
2096 static int
2097 ftpfs_netrc_bad_mode (const char *netrcname)
2099 static int be_angry = 1;
2100 struct stat mystat;
2102 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077))
2104 if (be_angry)
2106 message (D_ERROR, MSG_ERROR,
2107 _("~/.netrc file has incorrect mode.\n" "Remove password or correct mode."));
2108 be_angry = 0;
2110 return 1;
2112 return 0;
2115 /* Scan .netrc until we find matching "machine" or "default"
2116 * domain is used for additional matching
2117 * No search is done after "default" in compliance with "man netrc"
2118 * Return 0 if found, -1 otherwise */
2119 static int
2120 ftpfs_find_machine (const char *host, const char *domain)
2122 keyword_t keyword;
2124 if (!host)
2125 host = "";
2126 if (!domain)
2127 domain = "";
2129 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2131 if (keyword == NETRC_DEFAULT)
2132 return 0;
2134 if (keyword == NETRC_MACDEF)
2136 /* Scan for an empty line, which concludes "macdef" */
2139 while (*netrcp && *netrcp != '\n')
2140 netrcp++;
2141 if (*netrcp != '\n')
2142 break;
2143 netrcp++;
2145 while (*netrcp && *netrcp != '\n');
2146 continue;
2149 if (keyword != NETRC_MACHINE)
2150 continue;
2152 /* Take machine name */
2153 if (ftpfs_netrc_next () == NETRC_NONE)
2154 break;
2156 if (g_strcasecmp (host, buffer))
2158 /* Try adding our domain to short names in .netrc */
2159 const char *host_domain = strchr (host, '.');
2160 if (!host_domain)
2161 continue;
2163 /* Compare domain part */
2164 if (g_strcasecmp (host_domain, domain))
2165 continue;
2167 /* Compare local part */
2168 if (g_strncasecmp (host, buffer, host_domain - host))
2169 continue;
2172 return 0;
2175 /* end of .netrc */
2176 return -1;
2179 /* Extract login and password from .netrc for the host.
2180 * pass may be NULL.
2181 * Returns 0 for success, -1 for error */
2182 static int
2183 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2185 char *netrcname;
2186 char *tmp_pass = NULL;
2187 char hostname[MAXHOSTNAMELEN];
2188 const char *domain;
2189 keyword_t keyword;
2190 static struct rupcache
2192 struct rupcache *next;
2193 char *host;
2194 char *login;
2195 char *pass;
2196 } *rup_cache = NULL, *rupp;
2198 /* Initialize *login and *pass */
2199 if (!login)
2200 return 0;
2201 *login = NULL;
2202 if (pass)
2203 *pass = NULL;
2205 /* Look up in the cache first */
2206 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2208 if (!strcmp (host, rupp->host))
2210 if (rupp->login)
2211 *login = g_strdup (rupp->login);
2212 if (pass && rupp->pass)
2213 *pass = g_strdup (rupp->pass);
2214 return 0;
2218 /* Load current .netrc */
2219 netrcname = concat_dir_and_file (home_dir, ".netrc");
2220 netrcp = netrc = load_file (netrcname);
2221 if (netrc == NULL)
2223 g_free (netrcname);
2224 return 0;
2227 /* Find our own domain name */
2228 if (gethostname (hostname, sizeof (hostname)) < 0)
2229 *hostname = '\0';
2231 domain = strchr (hostname, '.');
2232 if (domain == NULL)
2233 domain = "";
2235 /* Scan for "default" and matching "machine" keywords */
2236 ftpfs_find_machine (host, domain);
2238 /* Scan for keywords following "default" and "machine" */
2239 while (1)
2241 int need_break = 0;
2242 keyword = ftpfs_netrc_next ();
2244 switch (keyword)
2246 case NETRC_LOGIN:
2247 if (ftpfs_netrc_next () == NETRC_NONE)
2249 need_break = 1;
2250 break;
2253 /* We have another name already - should not happen */
2254 if (*login)
2256 need_break = 1;
2257 break;
2260 /* We have login name now */
2261 *login = g_strdup (buffer);
2262 break;
2264 case NETRC_PASSWORD:
2265 case NETRC_PASSWD:
2266 if (ftpfs_netrc_next () == NETRC_NONE)
2268 need_break = 1;
2269 break;
2272 /* Ignore unsafe passwords */
2273 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2274 && ftpfs_netrc_bad_mode (netrcname))
2276 need_break = 1;
2277 break;
2280 /* Remember password. pass may be NULL, so use tmp_pass */
2281 if (tmp_pass == NULL)
2282 tmp_pass = g_strdup (buffer);
2283 break;
2285 case NETRC_ACCOUNT:
2286 /* "account" is followed by a token which we ignore */
2287 if (ftpfs_netrc_next () == NETRC_NONE)
2289 need_break = 1;
2290 break;
2293 /* Ignore account, but warn user anyways */
2294 ftpfs_netrc_bad_mode (netrcname);
2295 break;
2297 default:
2298 /* Unexpected keyword or end of file */
2299 need_break = 1;
2300 break;
2303 if (need_break)
2304 break;
2307 g_free (netrc);
2308 g_free (netrcname);
2310 rupp = g_new (struct rupcache, 1);
2311 rupp->host = g_strdup (host);
2312 rupp->login = rupp->pass = 0;
2314 if (*login != NULL)
2316 rupp->login = g_strdup (*login);
2318 if (tmp_pass != NULL)
2319 rupp->pass = g_strdup (tmp_pass);
2320 rupp->next = rup_cache;
2321 rup_cache = rupp;
2323 if (pass)
2324 *pass = tmp_pass;
2326 return 0;
2329 void
2330 init_ftpfs (void)
2332 static struct vfs_s_subclass ftpfs_subclass;
2334 tcp_init ();
2336 ftpfs_subclass.flags = VFS_S_REMOTE;
2337 ftpfs_subclass.archive_same = ftpfs_archive_same;
2338 ftpfs_subclass.open_archive = ftpfs_open_archive;
2339 ftpfs_subclass.free_archive = ftpfs_free_archive;
2340 ftpfs_subclass.fh_open = ftpfs_fh_open;
2341 ftpfs_subclass.fh_close = ftpfs_fh_close;
2342 ftpfs_subclass.dir_load = ftpfs_dir_load;
2343 ftpfs_subclass.file_store = ftpfs_file_store;
2344 ftpfs_subclass.linear_start = ftpfs_linear_start;
2345 ftpfs_subclass.linear_read = ftpfs_linear_read;
2346 ftpfs_subclass.linear_close = ftpfs_linear_close;
2348 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2349 vfs_ftpfs_ops.name = "ftpfs";
2350 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2351 vfs_ftpfs_ops.prefix = "ftp:";
2352 vfs_ftpfs_ops.done = &ftpfs_done;
2353 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2354 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2355 vfs_ftpfs_ops.chown = ftpfs_chown;
2356 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2357 vfs_ftpfs_ops.rename = ftpfs_rename;
2358 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2359 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2360 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2361 vfs_register_class (&vfs_ftpfs_ops);