Final Indentation of all touched files
[midnight-commander.git] / lib / vfs / mc-vfs / ftpfs.c
blob1b355bfb411fa1efba977c09a1f7c051b5d4bcfb
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 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
231 *p = '\0';
232 return ret;
236 /* Extract the hostname and username from the path */
239 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
240 * ftp://sunsite.unc.edu/pub/linux
241 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
242 * ftp://tsx-11.mit.edu:8192/
243 * ftp://joe@foo.edu:11321/private
244 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
245 * is supplied.
249 #define FTP_COMMAND_PORT 21
251 static void
252 ftpfs_split_url (char *path, char **host, char **user, int *port, char **pass)
254 char *p;
256 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
258 if (!*user)
260 /* Look up user and password in netrc */
261 if (use_netrc)
262 ftpfs_netrc_lookup (*host, user, pass);
263 if (!*user)
264 *user = g_strdup ("anonymous");
267 /* Look up password in netrc for known user */
268 if (use_netrc && *user && pass && !*pass)
270 char *new_user;
272 ftpfs_netrc_lookup (*host, &new_user, pass);
274 /* If user is different, remove password */
275 if (new_user && strcmp (*user, new_user))
277 g_free (*pass);
278 *pass = NULL;
281 g_free (new_user);
284 g_free (p);
287 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
288 static int
289 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
291 char answer[BUF_1K];
292 int i;
294 for (;;)
296 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
298 if (string_buf)
299 *string_buf = 0;
300 code = 421;
301 return 4;
303 switch (sscanf (answer, "%d", &code))
305 case 0:
306 if (string_buf)
307 g_strlcpy (string_buf, answer, string_len);
308 code = 500;
309 return 5;
310 case 1:
311 if (answer[3] == '-')
313 while (1)
315 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
317 if (string_buf)
318 *string_buf = 0;
319 code = 421;
320 return 4;
322 if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' '))
323 break;
326 if (string_buf)
327 g_strlcpy (string_buf, answer, string_len);
328 return code / 100;
333 static int
334 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
336 int sock = ftpfs_open_socket (me, super);
337 if (sock != -1)
339 char *cwdir = SUP.cwdir;
340 close (SUP.sock);
341 SUP.sock = sock;
342 SUP.cwdir = NULL;
343 if (ftpfs_login_server (me, super, SUP.password))
345 if (!cwdir)
346 return 1;
347 sock = ftpfs_chdir_internal (me, super, cwdir);
348 g_free (cwdir);
349 return sock == COMPLETE;
351 SUP.cwdir = cwdir;
353 return 0;
356 static int
357 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt,
358 ...)
360 va_list ap;
361 char *cmdstr;
362 int status, cmdlen;
363 static int retry = 0;
364 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
366 va_start (ap, fmt);
367 cmdstr = g_strdup_vprintf (fmt, ap);
368 va_end (ap);
370 cmdlen = strlen (cmdstr);
371 cmdstr = g_realloc (cmdstr, cmdlen + 3);
372 strcpy (cmdstr + cmdlen, "\r\n");
373 cmdlen += 2;
375 if (MEDATA->logfile)
377 if (strncmp (cmdstr, "PASS ", 5) == 0)
379 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
381 else
383 size_t ret;
384 ret = fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
387 fflush (MEDATA->logfile);
390 got_sigpipe = 0;
391 tty_enable_interrupt_key ();
392 status = write (SUP.sock, cmdstr, cmdlen);
394 if (status < 0)
396 code = 421;
398 if (errno == EPIPE)
399 { /* Remote server has closed connection */
400 if (level == 0)
402 level = 1;
403 status = ftpfs_reconnect (me, super);
404 level = 0;
405 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0))
407 goto ok;
411 got_sigpipe = 1;
413 g_free (cmdstr);
414 tty_disable_interrupt_key ();
415 return TRANSIENT;
417 retry = 0;
419 tty_disable_interrupt_key ();
421 if (wait_reply)
423 status = ftpfs_get_reply (me, SUP.sock,
424 (wait_reply & WANT_STRING) ? reply_str : NULL,
425 sizeof (reply_str) - 1);
426 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
428 retry = 1;
429 level = 1;
430 status = ftpfs_reconnect (me, super);
431 level = 0;
432 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0))
434 goto ok;
437 retry = 0;
438 g_free (cmdstr);
439 return status;
441 g_free (cmdstr);
442 return COMPLETE;
445 static void
446 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
448 if (SUP.sock != -1)
450 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
451 ftpfs_command (me, super, NONE, "QUIT");
452 close (SUP.sock);
454 g_free (SUP.host);
455 g_free (SUP.user);
456 g_free (SUP.cwdir);
457 g_free (SUP.password);
460 /* some defines only used by ftpfs_changetype */
461 /* These two are valid values for the second parameter */
462 #define TYPE_ASCII 0
463 #define TYPE_BINARY 1
465 /* This one is only used to initialize bucket->isbinary, don't use it as
466 second parameter to ftpfs_changetype. */
467 #define TYPE_UNKNOWN -1
469 static int
470 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
472 if (binary != SUP.isbinary)
474 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
475 ERRNOR (EIO, -1);
476 SUP.isbinary = binary;
478 return binary;
481 /* This routine logs the user in */
482 static int
483 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
485 char *pass;
486 char *op;
487 char *name; /* login user name */
488 int anon = 0;
489 char reply_string[BUF_MEDIUM];
491 SUP.isbinary = TYPE_UNKNOWN;
493 if (SUP.password) /* explicit password */
494 op = g_strdup (SUP.password);
495 else if (netrcpass) /* password from netrc */
496 op = g_strdup (netrcpass);
497 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp"))
499 if (!ftpfs_anonymous_passwd) /* default anonymous password */
500 ftpfs_init_passwd ();
501 op = g_strdup (ftpfs_anonymous_passwd);
502 anon = 1;
504 else
505 { /* ask user */
506 char *p;
508 p = g_strconcat (_(" FTP: Password required for "), SUP.user, " ", (char *) NULL);
509 op = vfs_get_password (p);
510 g_free (p);
511 if (op == NULL)
512 ERRNOR (EPERM, 0);
513 SUP.password = g_strdup (op);
516 if (!anon || MEDATA->logfile)
517 pass = op;
518 else
520 pass = g_strconcat ("-", op, (char *) NULL);
521 wipe_password (op);
524 /* Proxy server accepts: username@host-we-want-to-connect */
525 if (SUP.proxy)
527 name =
528 g_strconcat (SUP.user, "@",
529 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host, (char *) NULL);
531 else
532 name = g_strdup (SUP.user);
534 if (ftpfs_get_reply (me, SUP.sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
536 g_strup (reply_string);
537 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
538 if (MEDATA->logfile)
540 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n", SUP.remote_is_amiga);
541 fflush (MEDATA->logfile);
544 print_vfs_message (_("ftpfs: sending login name"));
546 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
548 case CONTINUE:
549 print_vfs_message (_("ftpfs: sending user password"));
550 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
551 if (code == CONTINUE)
553 char *p;
555 p = g_strdup_printf (_("FTP: Account required for user %s"), SUP.user);
556 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
557 g_free (p);
558 if (op == NULL)
559 ERRNOR (EPERM, 0);
560 print_vfs_message (_("ftpfs: sending user account"));
561 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
562 g_free (op);
564 if (code != COMPLETE)
565 break;
566 /* fall through */
568 case COMPLETE:
569 print_vfs_message (_("ftpfs: logged in"));
570 wipe_password (pass);
571 g_free (name);
572 return 1;
574 default:
575 SUP.failed_on_login = 1;
576 if (SUP.password)
577 wipe_password (SUP.password);
578 SUP.password = 0;
580 goto login_fail;
583 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "), SUP.user);
584 login_fail:
585 wipe_password (pass);
586 g_free (name);
587 ERRNOR (EPERM, 0);
590 static struct no_proxy_entry
592 char *domain;
593 void *next;
594 } *no_proxy;
596 static void
597 ftpfs_load_no_proxy_list (void)
599 /* FixMe: shouldn't be hardcoded!!! */
600 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
601 struct no_proxy_entry *np, *current = 0;
602 FILE *npf;
603 int c;
604 char *p;
605 static char *mc_file;
607 if (mc_file)
608 return;
610 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
611 if (exist_file (mc_file) && (npf = fopen (mc_file, "r")))
613 while (fgets (s, sizeof (s), npf))
615 if (!(p = strchr (s, '\n')))
616 { /* skip bogus entries */
617 while ((c = fgetc (npf)) != EOF && c != '\n')
619 continue;
622 if (p == s)
623 continue;
625 *p = '\0';
627 np = g_new (struct no_proxy_entry, 1);
628 np->domain = g_strdup (s);
629 np->next = NULL;
630 if (no_proxy)
631 current->next = np;
632 else
633 no_proxy = np;
634 current = np;
637 fclose (npf);
639 g_free (mc_file);
642 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
643 static int
644 ftpfs_check_proxy (const char *host)
646 struct no_proxy_entry *npe;
648 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
649 return 0; /* sanity check */
651 if (*host == '!')
652 return 1;
654 if (!ftpfs_always_use_proxy)
655 return 0;
657 if (!strchr (host, '.'))
658 return 0;
660 ftpfs_load_no_proxy_list ();
661 for (npe = no_proxy; npe; npe = npe->next)
663 char *domain = npe->domain;
665 if (domain[0] == '.')
667 int ld = strlen (domain);
668 int lh = strlen (host);
670 while (ld && lh && host[lh - 1] == domain[ld - 1])
672 ld--;
673 lh--;
676 if (!ld)
677 return 0;
679 else if (!g_strcasecmp (host, domain))
680 return 0;
683 return 1;
686 static void
687 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
689 char *user, *dir;
691 dir = vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
692 g_free (user);
693 g_free (dir);
696 static int
697 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
699 struct addrinfo hints, *res, *curr_res;
700 int my_socket = 0;
701 char *host = NULL;
702 char *port = NULL;
703 int tmp_port;
704 int e;
706 (void) me;
708 /* Use a proxy host? */
709 host = g_strdup (SUP.host);
711 if (!host || !*host)
713 print_vfs_message (_("ftpfs: Invalid host name."));
714 ftpfs_errno = EINVAL;
715 g_free (host);
716 return -1;
719 /* Hosts to connect to that start with a ! should use proxy */
720 tmp_port = SUP.port;
722 if (SUP.proxy)
724 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
727 port = g_strdup_printf ("%hu", (unsigned short) tmp_port);
728 if (port == NULL)
730 g_free (host);
731 ftpfs_errno = errno;
732 return -1;
735 tty_enable_interrupt_key (); /* clear the interrupt flag */
737 memset (&hints, 0, sizeof (struct addrinfo));
738 hints.ai_socktype = SOCK_STREAM;
739 hints.ai_flags = AI_ADDRCONFIG;
742 e = getaddrinfo (host, port, &hints, &res);
743 g_free (port);
744 port = NULL;
746 if (e != 0)
748 tty_disable_interrupt_key ();
749 print_vfs_message (_("ftpfs: %s"), gai_strerror (e));
750 g_free (host);
751 ftpfs_errno = EINVAL;
752 return -1;
755 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
758 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
760 if (my_socket < 0)
763 if (curr_res->ai_next != NULL)
764 continue;
766 tty_disable_interrupt_key ();
767 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno));
768 g_free (host);
769 freeaddrinfo (res);
770 ftpfs_errno = errno;
771 return -1;
774 print_vfs_message (_("ftpfs: making connection to %s"), host);
775 g_free (host);
776 host = NULL;
778 if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
779 break;
781 ftpfs_errno = errno;
782 close (my_socket);
784 if (errno == EINTR && tty_got_interrupt ())
786 print_vfs_message (_("ftpfs: connection interrupted by user"));
788 else if (res->ai_next == NULL)
790 print_vfs_message (_("ftpfs: connection to server failed: %s"),
791 unix_error_string (errno));
793 else
795 continue;
798 freeaddrinfo (res);
799 tty_disable_interrupt_key ();
800 return -1;
803 freeaddrinfo (res);
804 tty_disable_interrupt_key ();
805 return my_socket;
808 static int
809 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
811 int retry_seconds, count_down;
813 /* We do not want to use the passive if we are using proxies */
814 if (SUP.proxy)
815 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
817 retry_seconds = 0;
820 SUP.failed_on_login = 0;
822 SUP.sock = ftpfs_open_socket (me, super);
823 if (SUP.sock == -1)
824 return -1;
826 if (ftpfs_login_server (me, super, NULL))
828 /* Logged in, no need to retry the connection */
829 break;
831 else
833 if (SUP.failed_on_login)
835 /* Close only the socket descriptor */
836 close (SUP.sock);
838 else
840 return -1;
842 if (ftpfs_retry_seconds)
844 retry_seconds = ftpfs_retry_seconds;
845 tty_enable_interrupt_key ();
846 for (count_down = retry_seconds; count_down; count_down--)
848 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"),
849 count_down);
850 sleep (1);
851 if (tty_got_interrupt ())
853 /* ftpfs_errno = E; */
854 tty_disable_interrupt_key ();
855 return 0;
858 tty_disable_interrupt_key ();
862 while (retry_seconds);
864 SUP.cwdir = ftpfs_get_current_directory (me, super);
865 if (!SUP.cwdir)
866 SUP.cwdir = g_strdup (PATH_SEP_STR);
867 return 0;
870 static int
871 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
872 const char *archive_name, char *op)
874 char *host, *user, *password;
875 int port;
877 (void) archive_name;
879 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
881 SUP.host = host;
882 SUP.user = user;
883 SUP.port = port;
884 SUP.cwdir = NULL;
885 SUP.proxy = 0;
886 if (ftpfs_check_proxy (host))
887 SUP.proxy = ftpfs_proxy_host;
888 SUP.password = password;
889 SUP.use_passive_connection = ftpfs_use_passive_connections;
890 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
891 SUP.isbinary = TYPE_UNKNOWN;
892 SUP.remote_is_amiga = 0;
893 super->name = g_strdup ("/");
894 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755));
896 return ftpfs_open_archive_int (me, super);
899 static int
900 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
901 const char *archive_name, char *op, void *cookie)
903 char *host, *user;
904 int port;
906 (void) me;
907 (void) archive_name;
908 (void) cookie;
910 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
912 port = ((strcmp (host, SUP.host) == 0) && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
914 g_free (host);
915 g_free (user);
917 return port;
920 /* The returned directory should always contain a trailing slash */
921 static char *
922 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
924 char buf[BUF_8K], *bufp, *bufq;
926 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
927 ftpfs_get_reply (me, SUP.sock, buf, sizeof (buf)) == COMPLETE)
929 bufp = NULL;
930 for (bufq = buf; *bufq; bufq++)
931 if (*bufq == '"')
933 if (!bufp)
935 bufp = bufq + 1;
937 else
939 *bufq = 0;
940 if (*bufp)
942 if (*(bufq - 1) != '/')
944 *bufq++ = '/';
945 *bufq = 0;
947 if (*bufp == '/')
948 return g_strdup (bufp);
949 else
951 /* If the remote server is an Amiga a leading slash
952 might be missing. MC needs it because it is used
953 as separator between hostname and path internally. */
954 return g_strconcat ("/", bufp, (char *) NULL);
957 else
959 ftpfs_errno = EIO;
960 return NULL;
965 ftpfs_errno = EIO;
966 return NULL;
970 /* Setup Passive ftp connection, we use it for source routed connections */
971 static int
972 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
973 int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
975 char *c;
977 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") == COMPLETE)
979 int port;
980 /* (|||<port>|) */
981 c = strchr (reply_str, '|');
982 if (c == NULL)
983 return 0;
984 if (strlen (c) > 3)
985 c += 3;
986 else
987 return 0;
989 port = atoi (c);
990 if (port < 0 || port > 65535)
991 return 0;
992 port = htons (port);
994 switch (sa->ss_family)
996 case AF_INET:
997 ((struct sockaddr_in *) sa)->sin_port = port;
998 break;
999 case AF_INET6:
1000 ((struct sockaddr_in6 *) sa)->sin6_port = port;
1001 break;
1002 default:
1003 print_vfs_message (_("ftpfs: invalid address family"));
1004 ERRNOR (EINVAL, -1);
1007 else if (sa->ss_family == AF_INET)
1009 int xa, xb, xc, xd, xe, xf;
1010 char n[6];
1012 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
1013 return 0;
1015 /* Parse remote parameters */
1016 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
1018 if (!*c)
1019 return 0;
1020 if (!isdigit ((unsigned char) *c))
1021 return 0;
1022 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1023 return 0;
1025 n[0] = (unsigned char) xa;
1026 n[1] = (unsigned char) xb;
1027 n[2] = (unsigned char) xc;
1028 n[3] = (unsigned char) xd;
1029 n[4] = (unsigned char) xe;
1030 n[5] = (unsigned char) xf;
1032 memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1033 memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1035 else
1036 return 0;
1038 if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
1039 return 0;
1041 return 1;
1044 static int
1045 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
1047 struct sockaddr_storage data_addr;
1048 socklen_t data_addrlen;
1049 int data_sock, result;
1051 again:
1052 memset (&data_addr, 0, sizeof (struct sockaddr_storage));
1053 data_addrlen = sizeof (struct sockaddr_storage);
1055 if (SUP.use_passive_connection)
1056 result = getpeername (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen);
1057 else
1058 result = getsockname (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen);
1060 if (result == -1)
1061 return -1;
1063 switch (data_addr.ss_family)
1065 case AF_INET:
1066 ((struct sockaddr_in *) &data_addr)->sin_port = 0;
1067 break;
1068 case AF_INET6:
1069 ((struct sockaddr_in6 *) &data_addr)->sin6_port = 0;
1070 break;
1071 default:
1072 print_vfs_message (_("ftpfs: invalid address family"));
1073 ERRNOR (EINVAL, -1);
1076 data_sock = socket (data_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
1077 if (data_sock < 0)
1079 if (SUP.use_passive_connection)
1081 print_vfs_message (_("ftpfs: could not setup passive mode: %s"),
1082 unix_error_string (errno));
1083 SUP.use_passive_connection = 0;
1084 goto again;
1087 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1088 return -1;
1091 if (SUP.use_passive_connection)
1094 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1095 return data_sock;
1097 SUP.use_passive_connection = 0;
1098 print_vfs_message (_("ftpfs: could not setup passive mode"));
1100 close (data_sock);
1101 goto again;
1104 /* If passive setup fails, fallback to active connections */
1105 /* Active FTP connection */
1106 if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) == 0) &&
1107 (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) == 0) &&
1108 (listen (data_sock, 1) == 0))
1110 unsigned short int port;
1111 char *addr;
1112 unsigned int af;
1114 switch (data_addr.ss_family)
1116 case AF_INET:
1117 af = FTP_INET;
1118 port = ((struct sockaddr_in *) &data_addr)->sin_port;
1119 break;
1120 case AF_INET6:
1121 af = FTP_INET6;
1122 port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1123 break;
1124 default:
1125 print_vfs_message (_("ftpfs: invalid address family"));
1126 ERRNOR (EINVAL, -1);
1129 port = ntohs (port);
1131 addr = g_try_malloc (NI_MAXHOST);
1132 if (addr == NULL)
1133 ERRNOR (ENOMEM, -1);
1135 if (getnameinfo
1136 ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1137 NI_NUMERICHOST) != 0)
1139 g_free (addr);
1140 ERRNOR (EIO, -1);
1143 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
1145 g_free (addr);
1146 return data_sock;
1148 g_free (addr);
1150 if (FTP_INET == af)
1152 unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1153 unsigned char *p = (unsigned char *) &port;
1155 if (ftpfs_command (me, super, WAIT_REPLY,
1156 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1157 p[0], p[1]) == COMPLETE)
1158 return data_sock;
1161 close (data_sock);
1162 ftpfs_errno = EIO;
1163 return -1;
1166 static int
1167 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1168 const char *remote, int isbinary, int reget)
1170 struct sockaddr_storage from;
1171 int s, j, data;
1172 socklen_t fromlen = sizeof (from);
1174 if ((s = ftpfs_initconn (me, super)) == -1)
1175 return -1;
1176 if (ftpfs_changetype (me, super, isbinary) == -1)
1177 return -1;
1178 if (reget > 0)
1180 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1181 if (j != CONTINUE)
1182 return -1;
1184 if (remote)
1186 char *remote_path = ftpfs_translate_path (me, super, remote);
1187 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1188 /* WarFtpD can't STORE //filename */
1189 (*remote_path == '/') ? remote_path + 1 : remote_path);
1190 g_free (remote_path);
1192 else
1193 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1194 if (j != PRELIM)
1195 ERRNOR (EPERM, -1);
1196 tty_enable_interrupt_key ();
1197 if (SUP.use_passive_connection)
1198 data = s;
1199 else
1201 data = accept (s, (struct sockaddr *) &from, &fromlen);
1202 if (data < 0)
1204 ftpfs_errno = errno;
1205 close (s);
1206 return -1;
1208 close (s);
1210 tty_disable_interrupt_key ();
1211 return data;
1214 #define ABORT_TIMEOUT 5
1215 static void
1216 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
1218 struct vfs_s_super *super = FH_SUPER;
1219 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1220 fd_set mask;
1221 char buf[1024];
1222 int dsock = FH_SOCK;
1223 FH_SOCK = -1;
1224 SUP.ctl_connection_busy = 0;
1226 print_vfs_message (_("ftpfs: aborting transfer."));
1227 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1229 print_vfs_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1230 if (dsock != -1)
1231 close (dsock);
1232 return;
1235 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1237 print_vfs_message (_("ftpfs: abort failed"));
1238 if (dsock != -1)
1239 close (dsock);
1240 return;
1242 if (dsock != -1)
1244 FD_ZERO (&mask);
1245 FD_SET (dsock, &mask);
1246 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1248 struct timeval start_tim, tim;
1249 gettimeofday (&start_tim, NULL);
1250 /* flush the remaining data */
1251 while (read (dsock, buf, sizeof (buf)) > 0)
1253 gettimeofday (&tim, NULL);
1254 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT)
1256 /* server keeps sending, drop the connection and ftpfs_reconnect */
1257 close (dsock);
1258 ftpfs_reconnect (me, super);
1259 return;
1263 close (dsock);
1265 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1266 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1269 #if 0
1270 static void
1271 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1272 struct vfs_s_inode *dir)
1274 struct linklist *flist;
1275 struct direntry *fe, *fel;
1276 char tmp[MC_MAXPATHLEN];
1277 int depth;
1279 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1280 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1282 /* flist->data->l_stat is alread initialized with 0 */
1283 fel = flist->data;
1284 if (S_ISLNK (fel->s.st_mode) && fel->linkname)
1286 if (fel->linkname[0] == '/')
1288 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1289 continue;
1290 strcpy (tmp, fel->linkname);
1292 else
1294 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1295 continue;
1296 strcpy (tmp, dir->remote_path);
1297 if (tmp[1] != '\0')
1298 strcat (tmp, "/");
1299 strcat (tmp + 1, fel->linkname);
1301 for (depth = 0; depth < 100; depth++)
1302 { /* depth protects against recursive symbolic links */
1303 canonicalize_pathname (tmp);
1304 fe = _get_file_entry (bucket, tmp, 0, 0);
1305 if (fe)
1307 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1309 /* Symlink points to link which isn't resolved, yet. */
1310 if (fe->linkname[0] == '/')
1312 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1313 break;
1314 strcpy (tmp, fe->linkname);
1316 else
1318 /* at this point tmp looks always like this
1319 /directory/filename, i.e. no need to check
1320 strrchr's return value */
1321 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1322 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1323 break;
1324 strcat (tmp, fe->linkname);
1326 continue;
1328 else
1330 fel->l_stat = g_new (struct stat, 1);
1331 if (S_ISLNK (fe->s.st_mode))
1332 *fel->l_stat = *fe->l_stat;
1333 else
1334 *fel->l_stat = fe->s;
1335 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1338 break;
1342 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1345 static void
1346 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
1347 struct vfs_s_inode *dir)
1349 char buffer[2048] = "", *filename;
1350 int sock;
1351 FILE *fp;
1352 struct stat s;
1353 struct linklist *flist;
1354 struct direntry *fe;
1355 int switch_method = 0;
1357 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1358 if (strchr (dir->remote_path, ' '))
1360 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1362 print_vfs_message (_("ftpfs: CWD failed."));
1363 return;
1365 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1367 else
1368 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1370 if (sock == -1)
1372 print_vfs_message (_("ftpfs: couldn't resolve symlink"));
1373 return;
1376 fp = fdopen (sock, "r");
1377 if (fp == NULL)
1379 close (sock);
1380 print_vfs_message (_("ftpfs: couldn't resolve symlink"));
1381 return;
1383 tty_enable_interrupt_key ();
1384 flist = dir->file_list->next;
1385 while (1)
1389 if (flist == dir->file_list)
1390 goto done;
1391 fe = flist->data;
1392 flist = flist->next;
1394 while (!S_ISLNK (fe->s.st_mode));
1395 while (1)
1397 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1398 goto done;
1399 if (MEDATA->logfile)
1401 fputs (buffer, MEDATA->logfile);
1402 fflush (MEDATA->logfile);
1404 vfs_die ("This code should be commented out\n");
1405 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1407 int r = strcmp (fe->name, filename);
1408 g_free (filename);
1409 if (r == 0)
1411 if (S_ISLNK (s.st_mode))
1413 /* This server doesn't understand LIST -lLa */
1414 switch_method = 1;
1415 goto done;
1417 fe->l_stat = g_new (struct stat, 1);
1418 if (fe->l_stat == NULL)
1419 goto done;
1420 *fe->l_stat = s;
1421 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1422 break;
1424 if (r < 0)
1425 break;
1429 done:
1430 while (fgets (buffer, sizeof (buffer), fp) != NULL);
1431 tty_disable_interrupt_key ();
1432 fclose (fp);
1433 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1436 static void
1437 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1439 print_vfs_message (_("Resolving symlink..."));
1441 if (SUP.strict_rfc959_list_cmd)
1442 resolve_symlink_without_ls_options (me, super, dir);
1443 else
1444 resolve_symlink_with_ls_options (me, super, dir);
1446 #endif
1448 static int
1449 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1451 struct vfs_s_entry *ent;
1452 struct vfs_s_super *super = dir->super;
1453 int sock, num_entries = 0;
1454 char buffer[BUF_8K];
1455 int cd_first;
1457 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1458 || (strchr (remote_path, ' ') != NULL);
1460 again:
1461 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1462 remote_path,
1463 SUP.strict ==
1464 RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1466 if (cd_first)
1468 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1470 ftpfs_errno = ENOENT;
1471 print_vfs_message (_("ftpfs: CWD failed."));
1472 return -1;
1476 gettimeofday (&dir->timestamp, NULL);
1477 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1479 if (SUP.strict == RFC_STRICT)
1480 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1481 else if (cd_first)
1482 /* Dirty hack to avoid autoprepending / to . */
1483 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1484 sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1485 else
1487 /* Trailing "/." is necessary if remote_path is a symlink */
1488 char *path = concat_dir_and_file (remote_path, ".");
1489 sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1490 g_free (path);
1493 if (sock == -1)
1494 goto fallback;
1496 /* Clear the interrupt flag */
1497 tty_enable_interrupt_key ();
1499 while (1)
1501 int i;
1502 int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1503 sock);
1504 if (!res)
1505 break;
1507 if (res == EINTR)
1509 me->verrno = ECONNRESET;
1510 close (sock);
1511 tty_disable_interrupt_key ();
1512 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1513 print_vfs_message (_("%s: failure"), me->name);
1514 return -1;
1517 if (MEDATA->logfile)
1519 fputs (buffer, MEDATA->logfile);
1520 fputs ("\n", MEDATA->logfile);
1521 fflush (MEDATA->logfile);
1524 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1525 i = ent->ino->st.st_nlink;
1526 if (!vfs_parse_ls_lga (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname))
1528 vfs_s_free_entry (me, ent);
1529 continue;
1531 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1532 num_entries++;
1533 vfs_s_insert_entry (me, dir, ent);
1536 close (sock);
1537 me->verrno = E_REMOTE;
1538 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1539 goto fallback;
1541 if (num_entries == 0 && cd_first == 0)
1543 /* The LIST command may produce an empty output. In such scenario
1544 it is not clear whether this is caused by `remote_path' being
1545 a non-existent path or for some other reason (listing emtpy
1546 directory without the -a option, non-readable directory, etc.).
1548 Since `dir_load' is a crucial method, when it comes to determine
1549 whether a given path is a _directory_, the code must try its best
1550 to determine the type of `remote_path'. The only reliable way to
1551 achieve this is trough issuing a CWD command. */
1553 cd_first = 1;
1554 goto again;
1557 if (SUP.strict == RFC_AUTODETECT)
1558 SUP.strict = RFC_DARING;
1560 print_vfs_message (_("%s: done."), me->name);
1561 return 0;
1563 fallback:
1564 if (SUP.strict == RFC_AUTODETECT)
1566 /* It's our first attempt to get a directory listing from this
1567 server (UNIX style LIST command) */
1568 SUP.strict = RFC_STRICT;
1569 /* I hate goto, but recursive call needs another 8K on stack */
1570 /* return ftpfs_dir_load (me, dir, remote_path); */
1571 cd_first = 1;
1572 goto again;
1574 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1575 ERRNOR (EACCES, -1);
1578 static int
1579 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *localname)
1581 int h, sock, n_read, n_written;
1582 off_t n_stored;
1583 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1584 struct linger li;
1585 #else
1586 int flag_one = 1;
1587 #endif
1588 char buffer[8192];
1589 struct stat s;
1590 char *w_buf;
1591 struct vfs_s_super *super = FH_SUPER;
1593 h = open (localname, O_RDONLY);
1594 if (h == -1)
1595 ERRNOR (EIO, -1);
1596 sock =
1597 ftpfs_open_data_connection (me, super,
1598 fh->u.ftp.append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1599 if (sock < 0 || fstat (h, &s) == -1)
1601 close (h);
1602 return -1;
1604 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1605 li.l_onoff = 1;
1606 li.l_linger = 120;
1607 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1608 #else
1609 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1610 #endif
1611 n_stored = 0;
1613 tty_enable_interrupt_key ();
1614 while (1)
1616 while ((n_read = read (h, buffer, sizeof (buffer))) == -1)
1618 if (errno == EINTR)
1620 if (tty_got_interrupt ())
1622 ftpfs_errno = EINTR;
1623 goto error_return;
1625 else
1626 continue;
1628 ftpfs_errno = errno;
1629 goto error_return;
1631 if (n_read == 0)
1632 break;
1633 n_stored += n_read;
1634 w_buf = buffer;
1635 while ((n_written = write (sock, w_buf, n_read)) != n_read)
1637 if (n_written == -1)
1639 if (errno == EINTR && !tty_got_interrupt ())
1641 continue;
1643 ftpfs_errno = errno;
1644 goto error_return;
1646 w_buf += n_written;
1647 n_read -= n_written;
1649 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1650 (unsigned long) n_stored, (unsigned long) s.st_size);
1652 tty_disable_interrupt_key ();
1653 close (sock);
1654 close (h);
1655 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1656 ERRNOR (EIO, -1);
1657 return 0;
1658 error_return:
1659 tty_disable_interrupt_key ();
1660 close (sock);
1661 close (h);
1662 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1663 return -1;
1666 static int
1667 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1669 char *name = vfs_s_fullpath (me, fh->ino);
1671 if (!name)
1672 return 0;
1673 FH_SOCK = ftpfs_open_data_connection (me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1674 g_free (name);
1675 if (FH_SOCK == -1)
1676 ERRNOR (EACCES, 0);
1677 fh->linear = LS_LINEAR_OPEN;
1678 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1679 fh->u.ftp.append = 0;
1680 return 1;
1683 static int
1684 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1686 int n;
1687 struct vfs_s_super *super = FH_SUPER;
1689 while ((n = read (FH_SOCK, buf, len)) < 0)
1691 if ((errno == EINTR) && !tty_got_interrupt ())
1692 continue;
1693 break;
1696 if (n < 0)
1697 ftpfs_linear_abort (me, fh);
1699 if (!n)
1701 SUP.ctl_connection_busy = 0;
1702 close (FH_SOCK);
1703 FH_SOCK = -1;
1704 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1705 ERRNOR (E_REMOTE, -1);
1706 return 0;
1708 ERRNOR (errno, n);
1711 static void
1712 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1714 if (FH_SOCK != -1)
1715 ftpfs_linear_abort (me, fh);
1718 static int
1719 ftpfs_ctl (void *fh, int ctlop, void *arg)
1721 (void) arg;
1723 switch (ctlop)
1725 case VFS_CTL_IS_NOTREADY:
1727 int v;
1729 if (!FH->linear)
1730 vfs_die ("You may not do this");
1731 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1732 return 0;
1734 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1735 if (((v < 0) && (errno == EINTR)) || v == 0)
1736 return 1;
1737 return 0;
1739 default:
1740 return 0;
1744 static int
1745 ftpfs_send_command (struct vfs_class *me, const char *filename, const char *cmd, int flags)
1747 const char *rpath;
1748 char *p, *mpath = g_strdup (filename);
1749 struct vfs_s_super *super;
1750 int r;
1751 int flush_directory_cache = (flags & OPT_FLUSH);
1753 if (!(rpath = vfs_s_get_path_mangle (me, mpath, &super, 0)))
1755 g_free (mpath);
1756 return -1;
1758 p = ftpfs_translate_path (me, super, rpath);
1759 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1760 g_free (p);
1761 vfs_stamp_create (&vfs_ftpfs_ops, super);
1762 if (flags & OPT_IGNORE_ERROR)
1763 r = COMPLETE;
1764 if (r != COMPLETE)
1766 me->verrno = EPERM;
1767 g_free (mpath);
1768 return -1;
1770 if (flush_directory_cache)
1771 vfs_s_invalidate (me, super);
1772 g_free (mpath);
1773 return 0;
1776 /* This routine is called as the last step in load_setup */
1777 void
1778 ftpfs_init_passwd (void)
1780 ftpfs_anonymous_passwd = load_anon_passwd ();
1781 if (ftpfs_anonymous_passwd)
1782 return;
1784 /* If there is no anonymous ftp password specified
1785 * then we'll just use anonymous@
1786 * We don't send any other thing because:
1787 * - We want to remain anonymous
1788 * - We want to stop SPAM
1789 * - We don't want to let ftp sites to discriminate by the user,
1790 * host or country.
1792 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1795 static int
1796 ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1798 char buf[BUF_SMALL];
1799 int ret;
1801 g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1803 ret = ftpfs_send_command (me, path, buf, OPT_FLUSH);
1805 if (mc_config_get_bool (mc_main_config, CONFIG_APP_SECTION, "ignore_ftp_chattr_errors", TRUE))
1807 return 0;
1810 return ret;
1813 static int
1814 ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1816 #if 0
1817 ftpfs_errno = EPERM;
1818 return -1;
1819 #else
1820 /* Everyone knows it is not possible to chown remotely, so why bother them.
1821 If someone's root, then copy/move will always try to chown it... */
1822 (void) me;
1823 (void) path;
1824 (void) owner;
1825 (void) group;
1826 return 0;
1827 #endif
1830 static int
1831 ftpfs_unlink (struct vfs_class *me, const char *path)
1833 return ftpfs_send_command (me, path, "DELE /%s", OPT_FLUSH);
1836 /* Return 1 if path is the same directory as the one we are in now */
1837 static int
1838 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1840 (void) me;
1842 if (!SUP.cwdir)
1843 return 0;
1844 if (strcmp (path, SUP.cwdir) == 0)
1845 return 1;
1846 return 0;
1849 static int
1850 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1852 int r;
1853 char *p;
1855 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1856 return COMPLETE;
1858 p = ftpfs_translate_path (me, super, remote_path);
1859 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1860 g_free (p);
1862 if (r != COMPLETE)
1864 ftpfs_errno = EIO;
1866 else
1868 g_free (SUP.cwdir);
1869 SUP.cwdir = g_strdup (remote_path);
1870 SUP.cwd_deferred = 0;
1872 return r;
1875 static int
1876 ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1878 ftpfs_send_command (me, path1, "RNFR /%s", OPT_FLUSH);
1879 return ftpfs_send_command (me, path2, "RNTO /%s", OPT_FLUSH);
1882 static int
1883 ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1885 (void) mode; /* FIXME: should be used */
1887 return ftpfs_send_command (me, path, "MKD /%s", OPT_FLUSH);
1890 static int
1891 ftpfs_rmdir (struct vfs_class *me, const char *path)
1893 return ftpfs_send_command (me, path, "RMD /%s", OPT_FLUSH);
1896 static int
1897 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags, int mode)
1899 (void) mode;
1901 fh->u.ftp.append = 0;
1902 /* File will be written only, so no need to retrieve it from ftp server */
1903 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR)))
1905 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1906 struct linger li;
1907 #else
1908 int li = 1;
1909 #endif
1910 char *name;
1912 /* ftpfs_linear_start() called, so data will be written
1913 * to local temporary file and stored to ftp server
1914 * by vfs_s_close later
1916 if (FH_SUPER->u.ftp.ctl_connection_busy)
1918 if (!fh->ino->localname)
1920 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1921 fh->ino->ent->name);
1922 if (handle == -1)
1923 return -1;
1924 close (handle);
1925 fh->u.ftp.append = flags & O_APPEND;
1927 return 0;
1929 name = vfs_s_fullpath (me, fh->ino);
1930 if (!name)
1931 return -1;
1932 fh->handle =
1933 ftpfs_open_data_connection (me, fh->ino->super,
1934 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1935 g_free (name);
1937 if (fh->handle < 0)
1938 return -1;
1939 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1940 li.l_onoff = 1;
1941 li.l_linger = 120;
1942 #endif
1943 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1945 if (fh->ino->localname)
1947 unlink (fh->ino->localname);
1948 g_free (fh->ino->localname);
1949 fh->ino->localname = NULL;
1951 return 0;
1954 if (!fh->ino->localname)
1955 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1956 return -1;
1957 if (!fh->ino->localname)
1958 vfs_die ("retrieve_file failed to fill in localname");
1959 return 0;
1962 static int
1963 ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1965 if (fh->handle != -1 && !fh->ino->localname)
1967 close (fh->handle);
1968 fh->handle = -1;
1969 /* File is stored to destination already, so
1970 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1972 fh->changed = 0;
1973 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1974 ERRNOR (EIO, -1);
1975 vfs_s_invalidate (me, FH_SUPER);
1977 return 0;
1980 static void
1981 ftpfs_done (struct vfs_class *me)
1983 struct no_proxy_entry *np;
1985 (void) me;
1987 while (no_proxy)
1989 np = no_proxy->next;
1990 g_free (no_proxy->domain);
1991 g_free (no_proxy);
1992 no_proxy = np;
1994 g_free (ftpfs_anonymous_passwd);
1995 g_free (ftpfs_proxy_host);
1998 static void
1999 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
2001 struct vfs_s_super *super = MEDATA->supers;
2002 char *name;
2004 while (super)
2006 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
2007 (*func) (name);
2008 g_free (name);
2009 super = super->next;
2013 static char buffer[BUF_MEDIUM];
2014 static char *netrc;
2015 static const char *netrcp;
2017 /* This should match the keywords[] array below */
2018 typedef enum
2020 NETRC_NONE = 0,
2021 NETRC_DEFAULT,
2022 NETRC_MACHINE,
2023 NETRC_LOGIN,
2024 NETRC_PASSWORD,
2025 NETRC_PASSWD,
2026 NETRC_ACCOUNT,
2027 NETRC_MACDEF,
2028 NETRC_UNKNOWN
2029 } keyword_t;
2031 static keyword_t
2032 ftpfs_netrc_next (void)
2034 char *p;
2035 keyword_t i;
2036 static const char *const keywords[] = { "default", "machine",
2037 "login", "password", "passwd", "account", "macdef", NULL
2041 while (1)
2043 netrcp = skip_separators (netrcp);
2044 if (*netrcp != '\n')
2045 break;
2046 netrcp++;
2048 if (!*netrcp)
2049 return NETRC_NONE;
2050 p = buffer;
2051 if (*netrcp == '"')
2053 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++)
2055 if (*netrcp == '\\')
2056 netrcp++;
2057 *p++ = *netrcp;
2060 else
2062 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
2063 *netrcp != ',' && *netrcp; netrcp++)
2065 if (*netrcp == '\\')
2066 netrcp++;
2067 *p++ = *netrcp;
2070 *p = 0;
2071 if (!*buffer)
2072 return NETRC_NONE;
2074 i = NETRC_DEFAULT;
2075 while (keywords[i - 1])
2077 if (!strcmp (keywords[i - 1], buffer))
2078 return i;
2080 i++;
2083 return NETRC_UNKNOWN;
2086 static int
2087 ftpfs_netrc_bad_mode (const char *netrcname)
2089 static int be_angry = 1;
2090 struct stat mystat;
2092 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077))
2094 if (be_angry)
2096 message (D_ERROR, MSG_ERROR,
2097 _("~/.netrc file has incorrect mode.\n" "Remove password or correct mode."));
2098 be_angry = 0;
2100 return 1;
2102 return 0;
2105 /* Scan .netrc until we find matching "machine" or "default"
2106 * domain is used for additional matching
2107 * No search is done after "default" in compliance with "man netrc"
2108 * Return 0 if found, -1 otherwise */
2109 static int
2110 ftpfs_find_machine (const char *host, const char *domain)
2112 keyword_t keyword;
2114 if (!host)
2115 host = "";
2116 if (!domain)
2117 domain = "";
2119 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2121 if (keyword == NETRC_DEFAULT)
2122 return 0;
2124 if (keyword == NETRC_MACDEF)
2126 /* Scan for an empty line, which concludes "macdef" */
2129 while (*netrcp && *netrcp != '\n')
2130 netrcp++;
2131 if (*netrcp != '\n')
2132 break;
2133 netrcp++;
2135 while (*netrcp && *netrcp != '\n');
2136 continue;
2139 if (keyword != NETRC_MACHINE)
2140 continue;
2142 /* Take machine name */
2143 if (ftpfs_netrc_next () == NETRC_NONE)
2144 break;
2146 if (g_strcasecmp (host, buffer))
2148 /* Try adding our domain to short names in .netrc */
2149 const char *host_domain = strchr (host, '.');
2150 if (!host_domain)
2151 continue;
2153 /* Compare domain part */
2154 if (g_strcasecmp (host_domain, domain))
2155 continue;
2157 /* Compare local part */
2158 if (g_strncasecmp (host, buffer, host_domain - host))
2159 continue;
2162 return 0;
2165 /* end of .netrc */
2166 return -1;
2169 /* Extract login and password from .netrc for the host.
2170 * pass may be NULL.
2171 * Returns 0 for success, -1 for error */
2172 static int
2173 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2175 char *netrcname;
2176 char *tmp_pass = NULL;
2177 char hostname[MAXHOSTNAMELEN];
2178 const char *domain;
2179 keyword_t keyword;
2180 static struct rupcache
2182 struct rupcache *next;
2183 char *host;
2184 char *login;
2185 char *pass;
2186 } *rup_cache = NULL, *rupp;
2188 /* Initialize *login and *pass */
2189 if (!login)
2190 return 0;
2191 *login = NULL;
2192 if (pass)
2193 *pass = NULL;
2195 /* Look up in the cache first */
2196 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2198 if (!strcmp (host, rupp->host))
2200 if (rupp->login)
2201 *login = g_strdup (rupp->login);
2202 if (pass && rupp->pass)
2203 *pass = g_strdup (rupp->pass);
2204 return 0;
2208 /* Load current .netrc */
2209 netrcname = concat_dir_and_file (home_dir, ".netrc");
2210 netrcp = netrc = load_file (netrcname);
2211 if (netrc == NULL)
2213 g_free (netrcname);
2214 return 0;
2217 /* Find our own domain name */
2218 if (gethostname (hostname, sizeof (hostname)) < 0)
2219 *hostname = 0;
2220 if (!(domain = strchr (hostname, '.')))
2221 domain = "";
2223 /* Scan for "default" and matching "machine" keywords */
2224 ftpfs_find_machine (host, domain);
2226 /* Scan for keywords following "default" and "machine" */
2227 while (1)
2229 int need_break = 0;
2230 keyword = ftpfs_netrc_next ();
2232 switch (keyword)
2234 case NETRC_LOGIN:
2235 if (ftpfs_netrc_next () == NETRC_NONE)
2237 need_break = 1;
2238 break;
2241 /* We have another name already - should not happen */
2242 if (*login)
2244 need_break = 1;
2245 break;
2248 /* We have login name now */
2249 *login = g_strdup (buffer);
2250 break;
2252 case NETRC_PASSWORD:
2253 case NETRC_PASSWD:
2254 if (ftpfs_netrc_next () == NETRC_NONE)
2256 need_break = 1;
2257 break;
2260 /* Ignore unsafe passwords */
2261 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2262 && ftpfs_netrc_bad_mode (netrcname))
2264 need_break = 1;
2265 break;
2268 /* Remember password. pass may be NULL, so use tmp_pass */
2269 if (tmp_pass == NULL)
2270 tmp_pass = g_strdup (buffer);
2271 break;
2273 case NETRC_ACCOUNT:
2274 /* "account" is followed by a token which we ignore */
2275 if (ftpfs_netrc_next () == NETRC_NONE)
2277 need_break = 1;
2278 break;
2281 /* Ignore account, but warn user anyways */
2282 ftpfs_netrc_bad_mode (netrcname);
2283 break;
2285 default:
2286 /* Unexpected keyword or end of file */
2287 need_break = 1;
2288 break;
2291 if (need_break)
2292 break;
2295 g_free (netrc);
2296 g_free (netrcname);
2298 rupp = g_new (struct rupcache, 1);
2299 rupp->host = g_strdup (host);
2300 rupp->login = rupp->pass = 0;
2302 if (*login != NULL)
2304 rupp->login = g_strdup (*login);
2306 if (tmp_pass != NULL)
2307 rupp->pass = g_strdup (tmp_pass);
2308 rupp->next = rup_cache;
2309 rup_cache = rupp;
2311 if (pass)
2312 *pass = tmp_pass;
2314 return 0;
2317 void
2318 init_ftpfs (void)
2320 static struct vfs_s_subclass ftpfs_subclass;
2322 tcp_init ();
2324 ftpfs_subclass.flags = VFS_S_REMOTE;
2325 ftpfs_subclass.archive_same = ftpfs_archive_same;
2326 ftpfs_subclass.open_archive = ftpfs_open_archive;
2327 ftpfs_subclass.free_archive = ftpfs_free_archive;
2328 ftpfs_subclass.fh_open = ftpfs_fh_open;
2329 ftpfs_subclass.fh_close = ftpfs_fh_close;
2330 ftpfs_subclass.dir_load = ftpfs_dir_load;
2331 ftpfs_subclass.file_store = ftpfs_file_store;
2332 ftpfs_subclass.linear_start = ftpfs_linear_start;
2333 ftpfs_subclass.linear_read = ftpfs_linear_read;
2334 ftpfs_subclass.linear_close = ftpfs_linear_close;
2336 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2337 vfs_ftpfs_ops.name = "ftpfs";
2338 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2339 vfs_ftpfs_ops.prefix = "ftp:";
2340 vfs_ftpfs_ops.done = &ftpfs_done;
2341 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2342 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2343 vfs_ftpfs_ops.chown = ftpfs_chown;
2344 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2345 vfs_ftpfs_ops.rename = ftpfs_rename;
2346 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2347 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2348 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2349 vfs_register_class (&vfs_ftpfs_ops);