Ticket #1796: FTPFS does not work in active mode.
[midnight-commander.git] / vfs / ftpfs.c
blob2e94219804e585fe77d0b709edc4fe650dce165a
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 * cd /#ftp:pavel@hobit
48 * cd ~
49 * And now: what do I want to do? Do I want to go to /home/pavel or to
50 * /#ftp:hobit/home/pavel? I think first has better sense...
53 int f = !strcmp( remote_path, "/~" );
54 if (f || !strncmp( remote_path, "/~/", 3 )) {
55 char *s;
56 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
57 g_free (remote_path);
58 remote_path = s;
65 /* \todo Fix: Namespace pollution: horrible */
67 #include <config.h>
68 #include <stdlib.h> /* atoi() */
69 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
70 #include <netdb.h> /* struct hostent */
71 #include <sys/socket.h> /* AF_INET */
72 #include <netinet/in.h> /* struct in_addr */
73 #ifdef HAVE_ARPA_INET_H
74 #include <arpa/inet.h>
75 #endif
76 #include <arpa/ftp.h>
77 #include <arpa/telnet.h>
78 #include <sys/param.h>
79 #include <errno.h>
80 #include <ctype.h>
81 #include <fcntl.h>
82 #include <sys/time.h> /* gettimeofday() */
84 #include "../src/global.h"
86 #include "../src/tty/tty.h" /* enable/disable interrupt key */
88 #include "../src/wtools.h" /* message() */
89 #include "../src/main.h" /* print_vfs_message */
90 #include "../src/history.h"
91 #include "../src/setup.h" /* for load_anon_passwd */
92 #include "../src/mcconfig/mcconfig.h"
94 #include "utilvfs.h"
95 #include "xdirentry.h"
96 #include "vfs.h"
97 #include "vfs-impl.h"
98 #include "gc.h" /* vfs_stamp_create */
99 #include "tcputil.h"
100 #include "ftpfs.h"
101 #ifndef MAXHOSTNAMELEN
102 # define MAXHOSTNAMELEN 64
103 #endif
105 #define UPLOAD_ZERO_LENGTH_FILE
106 #define SUP super->u.ftp
107 #define FH_SOCK fh->u.ftp.sock
109 #ifndef INADDR_NONE
110 #define INADDR_NONE 0xffffffff
111 #endif
113 /* for uclibc < 0.9.29 */
114 #ifndef AI_ADDRCONFIG
115 #define AI_ADDRCONFIG 0x0020
116 #endif
118 #define RFC_AUTODETECT 0
119 #define RFC_DARING 1
120 #define RFC_STRICT 2
122 #ifndef HAVE_SOCKLEN_T
123 typedef int socklen_t;
124 #endif
126 static int ftpfs_errno;
127 static int code;
129 /* Delay to retry a connection */
130 int ftpfs_retry_seconds = 30;
132 /* Method to use to connect to ftp sites */
133 int ftpfs_use_passive_connections = 1;
134 int ftpfs_use_passive_connections_over_proxy = 0;
136 /* Method used to get directory listings:
137 * 1: try 'LIST -la <path>', if it fails
138 * fall back to CWD <path>; LIST
139 * 0: always use CWD <path>; LIST
141 int ftpfs_use_unix_list_options = 1;
143 /* First "CWD <path>", then "LIST -la ." */
144 int ftpfs_first_cd_then_ls = 1;
146 /* Use the ~/.netrc */
147 int use_netrc = 1;
149 /* Anonymous setup */
150 char *ftpfs_anonymous_passwd = NULL;
151 int ftpfs_directory_timeout = 900;
153 /* Proxy host */
154 char *ftpfs_proxy_host = NULL;
156 /* wether we have to use proxy by default? */
157 int ftpfs_always_use_proxy;
159 #ifdef FIXME_LATER_ALIGATOR
160 static struct linklist *connections_list;
161 #endif
163 /* ftpfs_command wait_flag: */
164 #define NONE 0x00
165 #define WAIT_REPLY 0x01
166 #define WANT_STRING 0x02
167 static char reply_str [80];
169 static struct vfs_class vfs_ftpfs_ops;
171 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
172 Translate a Unix path, i.e. MC's internal path representation (e.g.
173 /somedir/somefile) to a path valid for the remote server. Every path
174 transfered to the remote server has to be mangled by this function
175 right prior to sending it.
176 Currently only Amiga ftp servers are handled in a special manner.
178 When the remote server is an amiga:
179 a) strip leading slash if necesarry
180 b) replace first occurance of ":/" with ":"
181 c) strip trailing "/."
184 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
185 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path);
186 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
187 __attribute__ ((format (__printf__, 4, 5)));
188 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
189 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass);
190 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
192 static char *
193 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
195 if (!SUP.remote_is_amiga)
196 return g_strdup (remote_path);
197 else {
198 char *ret, *p;
200 if (MEDATA->logfile) {
201 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
202 fflush (MEDATA->logfile);
205 /* strip leading slash(es) */
206 while (*remote_path == '/')
207 remote_path++;
210 * Don't change "/" into "", e.g. "CWD " would be
211 * invalid.
213 if (*remote_path == '\0')
214 return g_strdup (".");
216 ret = g_strdup (remote_path);
218 /* replace first occurance of ":/" with ":" */
219 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
220 strcpy (p + 1, p + 2);
222 /* strip trailing "/." */
223 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
224 *p = '\0';
225 return ret;
229 /* Extract the hostname and username from the path */
232 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
233 * ftp://sunsite.unc.edu/pub/linux
234 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
235 * ftp://tsx-11.mit.edu:8192/
236 * ftp://joe@foo.edu:11321/private
237 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
238 * is supplied.
242 #define FTP_COMMAND_PORT 21
244 static void
245 ftpfs_split_url(char *path, char **host, char **user, int *port, char **pass)
247 char *p;
249 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
250 URL_ALLOW_ANON);
252 if (!*user) {
253 /* Look up user and password in netrc */
254 if (use_netrc)
255 ftpfs_netrc_lookup (*host, user, pass);
256 if (!*user)
257 *user = g_strdup ("anonymous");
260 /* Look up password in netrc for known user */
261 if (use_netrc && *user && pass && !*pass) {
262 char *new_user;
264 ftpfs_netrc_lookup (*host, &new_user, pass);
266 /* If user is different, remove password */
267 if (new_user && strcmp (*user, new_user)) {
268 g_free (*pass);
269 *pass = NULL;
272 g_free (new_user);
275 g_free (p);
278 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
279 static int
280 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
282 char answer[BUF_1K];
283 int i;
285 for (;;) {
286 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
287 if (string_buf)
288 *string_buf = 0;
289 code = 421;
290 return 4;
292 switch (sscanf(answer, "%d", &code)){
293 case 0:
294 if (string_buf)
295 g_strlcpy (string_buf, answer, string_len);
296 code = 500;
297 return 5;
298 case 1:
299 if (answer[3] == '-') {
300 while (1) {
301 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
302 if (string_buf)
303 *string_buf = 0;
304 code = 421;
305 return 4;
307 if ((sscanf (answer, "%d", &i) > 0) &&
308 (code == i) && (answer[3] == ' '))
309 break;
312 if (string_buf)
313 g_strlcpy (string_buf, answer, string_len);
314 return code / 100;
319 static int
320 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
322 int sock = ftpfs_open_socket (me, super);
323 if (sock != -1){
324 char *cwdir = SUP.cwdir;
325 close (SUP.sock);
326 SUP.sock = sock;
327 SUP.cwdir = NULL;
328 if (ftpfs_login_server (me, super, SUP.password)){
329 if (!cwdir)
330 return 1;
331 sock = ftpfs_chdir_internal (me, super, cwdir);
332 g_free (cwdir);
333 return sock == COMPLETE;
335 SUP.cwdir = cwdir;
337 return 0;
340 static int
341 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
343 va_list ap;
344 char *cmdstr;
345 int status, cmdlen;
346 static int retry = 0;
347 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
349 va_start (ap, fmt);
350 cmdstr = g_strdup_vprintf (fmt, ap);
351 va_end (ap);
353 cmdlen = strlen (cmdstr);
354 cmdstr = g_realloc (cmdstr, cmdlen + 3);
355 strcpy (cmdstr + cmdlen, "\r\n");
356 cmdlen += 2;
358 if (MEDATA->logfile) {
359 if (strncmp (cmdstr, "PASS ", 5) == 0) {
360 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
361 } else
362 fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
364 fflush (MEDATA->logfile);
367 got_sigpipe = 0;
368 tty_enable_interrupt_key ();
369 status = write (SUP.sock, cmdstr, cmdlen);
371 if (status < 0) {
372 code = 421;
374 if (errno == EPIPE) { /* Remote server has closed connection */
375 if (level == 0) {
376 level = 1;
377 status = ftpfs_reconnect (me, super);
378 level = 0;
379 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
380 goto ok;
384 got_sigpipe = 1;
386 g_free (cmdstr);
387 tty_disable_interrupt_key ();
388 return TRANSIENT;
390 retry = 0;
392 tty_disable_interrupt_key ();
394 if (wait_reply)
396 status = ftpfs_get_reply (me, SUP.sock,
397 (wait_reply & WANT_STRING) ? reply_str : NULL,
398 sizeof (reply_str) - 1);
399 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
401 retry = 1;
402 level = 1;
403 status = ftpfs_reconnect (me, super);
404 level = 0;
405 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
406 goto ok;
409 retry = 0;
410 g_free (cmdstr);
411 return status;
413 g_free (cmdstr);
414 return COMPLETE;
417 static void
418 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
420 if (SUP.sock != -1){
421 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
422 ftpfs_command(me, super, NONE, "QUIT");
423 close(SUP.sock);
425 g_free (SUP.host);
426 g_free (SUP.user);
427 g_free (SUP.cwdir);
428 g_free (SUP.password);
431 /* some defines only used by ftpfs_changetype */
432 /* These two are valid values for the second parameter */
433 #define TYPE_ASCII 0
434 #define TYPE_BINARY 1
436 /* This one is only used to initialize bucket->isbinary, don't use it as
437 second parameter to ftpfs_changetype. */
438 #define TYPE_UNKNOWN -1
440 static int
441 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
443 if (binary != SUP.isbinary) {
444 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
445 ERRNOR (EIO, -1);
446 SUP.isbinary = binary;
448 return binary;
451 /* This routine logs the user in */
452 static int
453 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
454 const char *netrcpass)
456 char *pass;
457 char *op;
458 char *name; /* login user name */
459 int anon = 0;
460 char reply_string[BUF_MEDIUM];
462 SUP.isbinary = TYPE_UNKNOWN;
464 if (SUP.password) /* explicit password */
465 op = g_strdup (SUP.password);
466 else if (netrcpass) /* password from netrc */
467 op = g_strdup (netrcpass);
468 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp")) {
469 if (!ftpfs_anonymous_passwd) /* default anonymous password */
470 ftpfs_init_passwd ();
471 op = g_strdup (ftpfs_anonymous_passwd);
472 anon = 1;
473 } else { /* ask user */
474 char *p;
476 p = g_strconcat (_(" FTP: Password required for "), SUP.user, " ",
477 NULL);
478 op = vfs_get_password (p);
479 g_free (p);
480 if (op == NULL)
481 ERRNOR (EPERM, 0);
482 SUP.password = g_strdup (op);
485 if (!anon || MEDATA->logfile)
486 pass = op;
487 else {
488 pass = g_strconcat ("-", op, (char *) NULL);
489 wipe_password (op);
492 /* Proxy server accepts: username@host-we-want-to-connect */
493 if (SUP.proxy) {
494 name =
495 g_strconcat (SUP.user, "@",
496 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host,
497 NULL);
498 } else
499 name = g_strdup (SUP.user);
501 if (ftpfs_get_reply
502 (me, SUP.sock, reply_string,
503 sizeof (reply_string) - 1) == COMPLETE) {
504 g_strup (reply_string);
505 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
506 if (MEDATA->logfile) {
507 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n",
508 SUP.remote_is_amiga);
509 fflush (MEDATA->logfile);
512 print_vfs_message (_("ftpfs: sending login name"));
514 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name)) {
515 case CONTINUE:
516 print_vfs_message (_("ftpfs: sending user password"));
517 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
518 if (code == CONTINUE) {
519 char *p;
521 p = g_strdup_printf (_
522 ("FTP: Account required for user %s"),
523 SUP.user);
524 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
525 g_free (p);
526 if (op == NULL)
527 ERRNOR (EPERM, 0);
528 print_vfs_message (_("ftpfs: sending user account"));
529 code =
530 ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
531 g_free (op);
533 if (code != COMPLETE)
534 break;
535 /* fall through */
537 case COMPLETE:
538 print_vfs_message (_("ftpfs: logged in"));
539 wipe_password (pass);
540 g_free (name);
541 return 1;
543 default:
544 SUP.failed_on_login = 1;
545 if (SUP.password)
546 wipe_password (SUP.password);
547 SUP.password = 0;
549 goto login_fail;
552 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
553 SUP.user);
554 login_fail:
555 wipe_password (pass);
556 g_free (name);
557 ERRNOR (EPERM, 0);
560 static struct no_proxy_entry {
561 char *domain;
562 void *next;
563 } *no_proxy;
565 static void
566 ftpfs_load_no_proxy_list (void)
568 /* FixMe: shouldn't be hardcoded!!! */
569 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
570 struct no_proxy_entry *np, *current = 0;
571 FILE *npf;
572 int c;
573 char *p;
574 static char *mc_file;
576 if (mc_file)
577 return;
579 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
580 if (exist_file (mc_file) &&
581 (npf = fopen (mc_file, "r"))) {
582 while (fgets (s, sizeof (s), npf)) {
583 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
584 while ((c = fgetc (npf)) != EOF && c != '\n')
586 continue;
589 if (p == s)
590 continue;
592 *p = '\0';
594 np = g_new (struct no_proxy_entry, 1);
595 np->domain = g_strdup (s);
596 np->next = NULL;
597 if (no_proxy)
598 current->next = np;
599 else
600 no_proxy = np;
601 current = np;
604 fclose (npf);
606 g_free (mc_file);
609 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
610 static int
611 ftpfs_check_proxy (const char *host)
613 struct no_proxy_entry *npe;
615 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
616 return 0; /* sanity check */
618 if (*host == '!')
619 return 1;
621 if (!ftpfs_always_use_proxy)
622 return 0;
624 if (!strchr (host, '.'))
625 return 0;
627 ftpfs_load_no_proxy_list ();
628 for (npe = no_proxy; npe; npe=npe->next) {
629 char *domain = npe->domain;
631 if (domain[0] == '.') {
632 int ld = strlen (domain);
633 int lh = strlen (host);
635 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
636 ld--;
637 lh--;
640 if (!ld)
641 return 0;
642 } else
643 if (!g_strcasecmp (host, domain))
644 return 0;
647 return 1;
650 static void
651 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
653 char *user, *dir;
655 dir =
656 vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT,
657 URL_ALLOW_ANON);
658 g_free (user);
659 g_free (dir);
662 static int
663 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
665 struct addrinfo hints, *res, *curr_res;
666 int my_socket = 0;
667 char *host = NULL;
668 char *port = NULL;
669 int tmp_port;
670 int e;
672 (void) me;
674 /* Use a proxy host? */
675 host = g_strdup(SUP.host);
677 if (!host || !*host){
678 print_vfs_message (_("ftpfs: Invalid host name."));
679 ftpfs_errno = EINVAL;
680 return -1;
683 /* Hosts to connect to that start with a ! should use proxy */
684 tmp_port = SUP.port;
686 if (SUP.proxy){
687 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
690 port = g_strdup_printf("%hu", (unsigned short) tmp_port);
691 if (port == NULL) {
692 g_free (host);
693 ftpfs_errno = errno;
694 return -1;
697 tty_enable_interrupt_key(); /* clear the interrupt flag */
699 memset (&hints, 0, sizeof (struct addrinfo));
700 hints.ai_socktype = SOCK_STREAM;
701 hints.ai_flags = AI_ADDRCONFIG;
704 e = getaddrinfo (host, port, &hints, &res);
705 g_free (port);
706 port = NULL;
708 if ( e != 0 ) {
709 tty_disable_interrupt_key ();
710 print_vfs_message (_("ftpfs: %s"), gai_strerror (e));
711 g_free (host);
712 ftpfs_errno = EINVAL;
713 return -1;
716 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next) {
718 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
720 if (my_socket < 0) {
722 if (curr_res->ai_next != NULL)
723 continue;
725 tty_disable_interrupt_key();
726 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno));
727 g_free (host);
728 freeaddrinfo (res);
729 ftpfs_errno = errno;
730 return -1;
733 print_vfs_message (_("ftpfs: making connection to %s"), host);
734 g_free (host);
735 host = NULL;
737 if ( connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0 )
738 break;
740 ftpfs_errno = errno;
741 close (my_socket);
743 if (errno == EINTR && tty_got_interrupt ()) {
744 print_vfs_message (_("ftpfs: connection interrupted by user"));
745 } else if (res->ai_next == NULL) {
746 print_vfs_message (_("ftpfs: connection to server failed: %s"),
747 unix_error_string (errno));
748 } else {
749 continue;
752 freeaddrinfo (res);
753 tty_disable_interrupt_key ();
754 return -1;
757 freeaddrinfo (res);
758 tty_disable_interrupt_key ();
759 return my_socket;
762 static int
763 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
765 int retry_seconds, count_down;
767 /* We do not want to use the passive if we are using proxies */
768 if (SUP.proxy)
769 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
771 retry_seconds = 0;
772 do {
773 SUP.failed_on_login = 0;
775 SUP.sock = ftpfs_open_socket (me, super);
776 if (SUP.sock == -1)
777 return -1;
779 if (ftpfs_login_server (me, super, NULL)) {
780 /* Logged in, no need to retry the connection */
781 break;
782 } else {
783 if (SUP.failed_on_login){
784 /* Close only the socket descriptor */
785 close (SUP.sock);
786 } else {
787 return -1;
789 if (ftpfs_retry_seconds){
790 retry_seconds = ftpfs_retry_seconds;
791 tty_enable_interrupt_key ();
792 for (count_down = retry_seconds; count_down; count_down--){
793 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
794 sleep (1);
795 if (tty_got_interrupt ()) {
796 /* ftpfs_errno = E; */
797 tty_disable_interrupt_key ();
798 return 0;
801 tty_disable_interrupt_key ();
804 } while (retry_seconds);
806 SUP.cwdir = ftpfs_get_current_directory (me, super);
807 if (!SUP.cwdir)
808 SUP.cwdir = g_strdup (PATH_SEP_STR);
809 return 0;
812 static int
813 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
814 const char *archive_name, char *op)
816 char *host, *user, *password;
817 int port;
819 (void) archive_name;
821 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
823 SUP.host = host;
824 SUP.user = user;
825 SUP.port = port;
826 SUP.cwdir = NULL;
827 SUP.proxy = 0;
828 if (ftpfs_check_proxy (host))
829 SUP.proxy = ftpfs_proxy_host;
830 SUP.password = password;
831 SUP.use_passive_connection = ftpfs_use_passive_connections;
832 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
833 SUP.isbinary = TYPE_UNKNOWN;
834 SUP.remote_is_amiga = 0;
835 super->name = g_strdup ("/");
836 super->root =
837 vfs_s_new_inode (me, super,
838 vfs_s_default_stat (me, S_IFDIR | 0755));
840 return ftpfs_open_archive_int (me, super);
843 static int
844 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
845 const char *archive_name, char *op, void *cookie)
847 char *host, *user;
848 int port;
850 (void) me;
851 (void) archive_name;
852 (void) cookie;
854 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
856 port = ((strcmp (host, SUP.host) == 0)
857 && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
859 g_free (host);
860 g_free (user);
862 return port;
865 /* The returned directory should always contain a trailing slash */
866 static char *
867 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
869 char buf[BUF_8K], *bufp, *bufq;
871 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
872 ftpfs_get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
873 bufp = NULL;
874 for (bufq = buf; *bufq; bufq++)
875 if (*bufq == '"') {
876 if (!bufp) {
877 bufp = bufq + 1;
878 } else {
879 *bufq = 0;
880 if (*bufp) {
881 if (*(bufq - 1) != '/') {
882 *bufq++ = '/';
883 *bufq = 0;
885 if (*bufp == '/')
886 return g_strdup (bufp);
887 else {
888 /* If the remote server is an Amiga a leading slash
889 might be missing. MC needs it because it is used
890 as separator between hostname and path internally. */
891 return g_strconcat( "/", bufp, NULL);
893 } else {
894 ftpfs_errno = EIO;
895 return NULL;
900 ftpfs_errno = EIO;
901 return NULL;
905 /* Setup Passive ftp connection, we use it for source routed connections */
906 static int
907 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
908 int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
910 char *c;
912 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") == COMPLETE) {
913 int port;
914 /* (|||<port>|) */
915 c = strchr (reply_str, '|');
916 if (c == NULL)
917 return 0;
918 if(strlen(c) > 3)
919 c+=3;
920 else
921 return 0;
923 port = atoi (c);
924 if (port < 0 || port > 65535)
925 return 0;
926 port = htons (port);
928 switch (sa->ss_family) {
929 case AF_INET:
930 ((struct sockaddr_in *)sa)->sin_port = port;
931 break;
932 case AF_INET6:
933 ((struct sockaddr_in6 *)sa)->sin6_port = port;
934 break;
935 default:
936 print_vfs_message (_("ftpfs: invalid address family"));
937 ERRNOR (EINVAL, -1);
939 } else if (sa->ss_family == AF_INET) {
940 int xa, xb, xc, xd, xe, xf;
941 char n [6];
943 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
944 return 0;
946 /* Parse remote parameters */
947 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
949 if (!*c)
950 return 0;
951 if (!isdigit ((unsigned char) *c))
952 return 0;
953 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
954 return 0;
956 n [0] = (unsigned char) xa;
957 n [1] = (unsigned char) xb;
958 n [2] = (unsigned char) xc;
959 n [3] = (unsigned char) xd;
960 n [4] = (unsigned char) xe;
961 n [5] = (unsigned char) xf;
963 memcpy (&(((struct sockaddr_in *)sa)->sin_addr.s_addr), (void *)n, 4);
964 memcpy (&(((struct sockaddr_in *)sa)->sin_port), (void *)&n[4], 2);
965 } else
966 return 0;
968 if (connect (my_socket, (struct sockaddr *) sa, *salen ) < 0)
969 return 0;
971 return 1;
974 static int
975 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
977 struct sockaddr_storage data_addr;
978 socklen_t data_addrlen;
979 int data_sock, result;
981 again:
982 memset (&data_addr, 0, sizeof (struct sockaddr_storage));
983 data_addrlen = sizeof (struct sockaddr_storage);
985 if (SUP.use_passive_connection)
986 result = getpeername (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen);
987 else
988 result = getsockname (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen);
990 if (result == -1 )
991 return -1;
993 switch (data_addr.ss_family) {
994 case AF_INET:
995 ((struct sockaddr_in *)&data_addr)->sin_port = 0;
996 break;
997 case AF_INET6:
998 ((struct sockaddr_in6 *)&data_addr)->sin6_port = 0;
999 break;
1000 default:
1001 print_vfs_message (_("ftpfs: invalid address family"));
1002 ERRNOR(EINVAL, -1);
1005 data_sock = socket (data_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
1006 if (data_sock < 0) {
1007 if (SUP.use_passive_connection) {
1008 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno));
1009 SUP.use_passive_connection = 0;
1010 goto again;
1013 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1014 return -1;
1017 if (SUP.use_passive_connection) {
1019 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1020 return data_sock;
1022 SUP.use_passive_connection = 0;
1023 print_vfs_message (_("ftpfs: could not setup passive mode"));
1025 close (data_sock);
1026 goto again;
1029 /* If passive setup fails, fallback to active connections */
1030 /* Active FTP connection */
1031 if ((bind (data_sock, (struct sockaddr *)&data_addr, data_addrlen) == 0) &&
1032 (getsockname (data_sock, (struct sockaddr *)&data_addr, &data_addrlen) == 0) &&
1033 (listen (data_sock, 1) == 0)) {
1034 unsigned short int port;
1035 char *addr;
1036 unsigned int af;
1038 switch (data_addr.ss_family) {
1039 case AF_INET:
1040 af = FTP_INET;
1041 port = ((struct sockaddr_in *)&data_addr)->sin_port;
1042 break;
1043 case AF_INET6:
1044 af = FTP_INET6;
1045 port = ((struct sockaddr_in6 *)&data_addr)->sin6_port;
1046 break;
1047 default:
1048 print_vfs_message (_("ftpfs: invalid address family"));
1049 ERRNOR (EINVAL, -1);
1052 port = ntohs (port);
1054 addr = g_malloc (NI_MAXHOST);
1055 if (addr == NULL)
1056 ERRNOR (ENOMEM, -1);
1058 if (getnameinfo ((struct sockaddr *)&data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) {
1059 g_free (addr);
1060 ERRNOR (EIO, -1);
1063 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE) {
1064 g_free (addr);
1065 return data_sock;
1067 g_free (addr);
1069 if (FTP_INET == af) {
1070 unsigned char *a = (unsigned char *)&((struct sockaddr_in *)&data_addr)->sin_addr;
1071 unsigned char *p = (unsigned char *)&port;
1073 if (ftpfs_command (me, super, WAIT_REPLY,
1074 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1075 p[0], p[1]) == COMPLETE)
1076 return data_sock;
1079 close (data_sock);
1080 ftpfs_errno = EIO;
1081 return -1;
1084 static int
1085 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1086 const char *remote, int isbinary, int reget)
1088 struct sockaddr_storage from;
1089 int s, j, data;
1090 socklen_t fromlen = sizeof(from);
1092 if ((s = ftpfs_initconn (me, super)) == -1)
1093 return -1;
1094 if (ftpfs_changetype (me, super, isbinary) == -1)
1095 return -1;
1096 if (reget > 0){
1097 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1098 if (j != CONTINUE)
1099 return -1;
1101 if (remote) {
1102 char *remote_path = ftpfs_translate_path (me, super, remote);
1103 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1104 /* WarFtpD can't STORE //filename */
1105 (*remote_path == '/') ? remote_path + 1 : remote_path);
1106 g_free (remote_path);
1107 } else
1108 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1109 if (j != PRELIM)
1110 ERRNOR (EPERM, -1);
1111 tty_enable_interrupt_key ();
1112 if (SUP.use_passive_connection)
1113 data = s;
1114 else {
1115 data = accept (s, (struct sockaddr *)&from, &fromlen);
1116 if (data < 0) {
1117 ftpfs_errno = errno;
1118 close (s);
1119 return -1;
1121 close (s);
1123 tty_disable_interrupt_key ();
1124 return data;
1127 #define ABORT_TIMEOUT 5
1128 static void
1129 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
1131 struct vfs_s_super *super = FH_SUPER;
1132 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1133 fd_set mask;
1134 char buf[1024];
1135 int dsock = FH_SOCK;
1136 FH_SOCK = -1;
1137 SUP.ctl_connection_busy = 0;
1139 print_vfs_message (_("ftpfs: aborting transfer."));
1140 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
1141 print_vfs_message (_("ftpfs: abort error: %s"),
1142 unix_error_string (errno));
1143 if (dsock != -1)
1144 close (dsock);
1145 return;
1148 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
1149 print_vfs_message (_("ftpfs: abort failed"));
1150 if (dsock != -1)
1151 close (dsock);
1152 return;
1154 if (dsock != -1) {
1155 FD_ZERO (&mask);
1156 FD_SET (dsock, &mask);
1157 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1158 struct timeval start_tim, tim;
1159 gettimeofday (&start_tim, NULL);
1160 /* flush the remaining data */
1161 while (read (dsock, buf, sizeof (buf)) > 0) {
1162 gettimeofday (&tim, NULL);
1163 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1164 /* server keeps sending, drop the connection and ftpfs_reconnect */
1165 close (dsock);
1166 ftpfs_reconnect (me, super);
1167 return;
1171 close (dsock);
1173 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1174 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1177 #if 0
1178 static void
1179 resolve_symlink_without_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1181 struct linklist *flist;
1182 struct direntry *fe, *fel;
1183 char tmp[MC_MAXPATHLEN];
1184 int depth;
1186 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1187 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1188 /* flist->data->l_stat is alread initialized with 0 */
1189 fel = flist->data;
1190 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1191 if (fel->linkname[0] == '/') {
1192 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1193 continue;
1194 strcpy (tmp, fel->linkname);
1195 } else {
1196 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1197 continue;
1198 strcpy (tmp, dir->remote_path);
1199 if (tmp[1] != '\0')
1200 strcat (tmp, "/");
1201 strcat (tmp + 1, fel->linkname);
1203 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1204 canonicalize_pathname (tmp);
1205 fe = _get_file_entry(bucket, tmp, 0, 0);
1206 if (fe) {
1207 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1208 /* Symlink points to link which isn't resolved, yet. */
1209 if (fe->linkname[0] == '/') {
1210 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1211 break;
1212 strcpy (tmp, fe->linkname);
1213 } else {
1214 /* at this point tmp looks always like this
1215 /directory/filename, i.e. no need to check
1216 strrchr's return value */
1217 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1218 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1219 break;
1220 strcat (tmp, fe->linkname);
1222 continue;
1223 } else {
1224 fel->l_stat = g_new (struct stat, 1);
1225 if ( S_ISLNK (fe->s.st_mode))
1226 *fel->l_stat = *fe->l_stat;
1227 else
1228 *fel->l_stat = fe->s;
1229 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1232 break;
1236 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1239 static void
1240 resolve_symlink_with_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1242 char buffer[2048] = "", *filename;
1243 int sock;
1244 FILE *fp;
1245 struct stat s;
1246 struct linklist *flist;
1247 struct direntry *fe;
1248 int switch_method = 0;
1250 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1251 if (strchr (dir->remote_path, ' ')) {
1252 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1253 print_vfs_message(_("ftpfs: CWD failed."));
1254 return;
1256 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1258 else
1259 sock = ftpfs_open_data_connection (bucket, "LIST -lLa",
1260 dir->remote_path, TYPE_ASCII, 0);
1262 if (sock == -1) {
1263 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1264 return;
1267 fp = fdopen(sock, "r");
1268 if (fp == NULL) {
1269 close(sock);
1270 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1271 return;
1273 tty_enable_interrupt_key ();
1274 flist = dir->file_list->next;
1275 while (1) {
1276 do {
1277 if (flist == dir->file_list)
1278 goto done;
1279 fe = flist->data;
1280 flist = flist->next;
1281 } while (!S_ISLNK(fe->s.st_mode));
1282 while (1) {
1283 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1284 goto done;
1285 if (MEDATA->logfile){
1286 fputs (buffer, MEDATA->logfile);
1287 fflush (MEDATA->logfile);
1289 vfs_die("This code should be commented out\n");
1290 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1291 int r = strcmp(fe->name, filename);
1292 g_free(filename);
1293 if (r == 0) {
1294 if (S_ISLNK (s.st_mode)) {
1295 /* This server doesn't understand LIST -lLa */
1296 switch_method = 1;
1297 goto done;
1299 fe->l_stat = g_new (struct stat, 1);
1300 if (fe->l_stat == NULL)
1301 goto done;
1302 *fe->l_stat = s;
1303 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1304 break;
1306 if (r < 0)
1307 break;
1311 done:
1312 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1313 tty_disable_interrupt_key ();
1314 fclose(fp);
1315 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1318 static void
1319 resolve_symlink(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1321 print_vfs_message(_("Resolving symlink..."));
1323 if (SUP.strict_rfc959_list_cmd)
1324 resolve_symlink_without_ls_options(me, super, dir);
1325 else
1326 resolve_symlink_with_ls_options(me, super, dir);
1328 #endif
1330 static int
1331 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1333 struct vfs_s_entry *ent;
1334 struct vfs_s_super *super = dir->super;
1335 int sock, num_entries = 0;
1336 char buffer[BUF_8K];
1337 int cd_first;
1339 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1340 || (strchr (remote_path, ' ') != NULL);
1342 again:
1343 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1344 remote_path,
1345 SUP.strict ==
1346 RFC_STRICT ? _("(strict rfc959)") : "",
1347 cd_first ? _("(chdir first)") : "");
1349 if (cd_first) {
1350 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE) {
1351 ftpfs_errno = ENOENT;
1352 print_vfs_message (_("ftpfs: CWD failed."));
1353 return -1;
1357 gettimeofday (&dir->timestamp, NULL);
1358 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1360 if (SUP.strict == RFC_STRICT)
1361 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1362 else if (cd_first)
1363 /* Dirty hack to avoid autoprepending / to . */
1364 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1365 sock =
1366 ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1367 else {
1368 /* Trailing "/." is necessary if remote_path is a symlink */
1369 char *path = concat_dir_and_file (remote_path, ".");
1370 sock =
1371 ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII,
1373 g_free (path);
1376 if (sock == -1)
1377 goto fallback;
1379 /* Clear the interrupt flag */
1380 tty_enable_interrupt_key ();
1382 while (1) {
1383 int i;
1384 int res =
1385 vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1386 sock);
1387 if (!res)
1388 break;
1390 if (res == EINTR) {
1391 me->verrno = ECONNRESET;
1392 close (sock);
1393 tty_disable_interrupt_key ();
1394 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1395 print_vfs_message (_("%s: failure"), me->name);
1396 return -1;
1399 if (MEDATA->logfile) {
1400 fputs (buffer, MEDATA->logfile);
1401 fputs ("\n", MEDATA->logfile);
1402 fflush (MEDATA->logfile);
1405 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1406 i = ent->ino->st.st_nlink;
1407 if (!vfs_parse_ls_lga
1408 (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1409 vfs_s_free_entry (me, ent);
1410 continue;
1412 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1413 num_entries++;
1414 vfs_s_insert_entry (me, dir, ent);
1417 close (sock);
1418 me->verrno = E_REMOTE;
1419 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1420 goto fallback;
1422 if (num_entries == 0 && cd_first == 0) {
1423 /* The LIST command may produce an empty output. In such scenario
1424 it is not clear whether this is caused by `remote_path' being
1425 a non-existent path or for some other reason (listing emtpy
1426 directory without the -a option, non-readable directory, etc.).
1428 Since `dir_load' is a crucial method, when it comes to determine
1429 whether a given path is a _directory_, the code must try its best
1430 to determine the type of `remote_path'. The only reliable way to
1431 achieve this is trough issuing a CWD command. */
1433 cd_first = 1;
1434 goto again;
1437 if (SUP.strict == RFC_AUTODETECT)
1438 SUP.strict = RFC_DARING;
1440 print_vfs_message (_("%s: done."), me->name);
1441 return 0;
1443 fallback:
1444 if (SUP.strict == RFC_AUTODETECT) {
1445 /* It's our first attempt to get a directory listing from this
1446 server (UNIX style LIST command) */
1447 SUP.strict = RFC_STRICT;
1448 /* I hate goto, but recursive call needs another 8K on stack */
1449 /* return ftpfs_dir_load (me, dir, remote_path); */
1450 cd_first = 1;
1451 goto again;
1453 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1454 ERRNOR (EACCES, -1);
1457 static int
1458 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name,
1459 char *localname)
1461 int h, sock, n_read, n_written;
1462 off_t n_stored;
1463 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1464 struct linger li;
1465 #else
1466 int flag_one = 1;
1467 #endif
1468 char buffer[8192];
1469 struct stat s;
1470 char *w_buf;
1471 struct vfs_s_super *super = FH_SUPER;
1473 h = open (localname, O_RDONLY);
1474 if (h == -1)
1475 ERRNOR (EIO, -1);
1476 sock =
1477 ftpfs_open_data_connection (me, super,
1478 fh->u.ftp.append ? "APPE" : "STOR", name,
1479 TYPE_BINARY, 0);
1480 if (sock < 0 || fstat (h, &s) == -1) {
1481 close (h);
1482 return -1;
1484 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1485 li.l_onoff = 1;
1486 li.l_linger = 120;
1487 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1488 #else
1489 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1490 #endif
1491 n_stored = 0;
1493 tty_enable_interrupt_key ();
1494 while (1) {
1495 while ((n_read = read (h, buffer, sizeof (buffer))) == -1) {
1496 if (errno == EINTR) {
1497 if (tty_got_interrupt ()) {
1498 ftpfs_errno = EINTR;
1499 goto error_return;
1500 } else
1501 continue;
1503 ftpfs_errno = errno;
1504 goto error_return;
1506 if (n_read == 0)
1507 break;
1508 n_stored += n_read;
1509 w_buf = buffer;
1510 while ((n_written = write (sock, w_buf, n_read)) != n_read) {
1511 if (n_written == -1) {
1512 if (errno == EINTR && !tty_got_interrupt ()) {
1513 continue;
1515 ftpfs_errno = errno;
1516 goto error_return;
1518 w_buf += n_written;
1519 n_read -= n_written;
1521 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1522 (unsigned long) n_stored, (unsigned long) s.st_size);
1524 tty_disable_interrupt_key ();
1525 close (sock);
1526 close (h);
1527 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1528 ERRNOR (EIO, -1);
1529 return 0;
1530 error_return:
1531 tty_disable_interrupt_key ();
1532 close (sock);
1533 close (h);
1534 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1535 return -1;
1538 static int
1539 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1541 char *name = vfs_s_fullpath (me, fh->ino);
1543 if (!name)
1544 return 0;
1545 FH_SOCK = ftpfs_open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1546 g_free (name);
1547 if (FH_SOCK == -1)
1548 ERRNOR (EACCES, 0);
1549 fh->linear = LS_LINEAR_OPEN;
1550 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1551 fh->u.ftp.append = 0;
1552 return 1;
1555 static int
1556 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1558 int n;
1559 struct vfs_s_super *super = FH_SUPER;
1561 while ((n = read (FH_SOCK, buf, len))<0) {
1562 if ((errno == EINTR) && !tty_got_interrupt ())
1563 continue;
1564 break;
1567 if (n<0)
1568 ftpfs_linear_abort(me, fh);
1570 if (!n) {
1571 SUP.ctl_connection_busy = 0;
1572 close (FH_SOCK);
1573 FH_SOCK = -1;
1574 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1575 ERRNOR (E_REMOTE, -1);
1576 return 0;
1578 ERRNOR (errno, n);
1581 static void
1582 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1584 if (FH_SOCK != -1)
1585 ftpfs_linear_abort(me, fh);
1588 static int ftpfs_ctl (void *fh, int ctlop, void *arg)
1590 (void) arg;
1592 switch (ctlop) {
1593 case VFS_CTL_IS_NOTREADY:
1595 int v;
1597 if (!FH->linear)
1598 vfs_die ("You may not do this");
1599 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1600 return 0;
1602 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1603 if (((v < 0) && (errno == EINTR)) || v == 0)
1604 return 1;
1605 return 0;
1607 default:
1608 return 0;
1612 static int
1613 ftpfs_send_command(struct vfs_class *me, const char *filename, const char *cmd, int flags)
1615 const char *rpath;
1616 char *p, *mpath = g_strdup(filename);
1617 struct vfs_s_super *super;
1618 int r;
1619 int flush_directory_cache = (flags & OPT_FLUSH);
1621 if (!(rpath = vfs_s_get_path_mangle(me, mpath, &super, 0))) {
1622 g_free(mpath);
1623 return -1;
1625 p = ftpfs_translate_path (me, super, rpath);
1626 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1627 g_free (p);
1628 vfs_stamp_create (&vfs_ftpfs_ops, super);
1629 if (flags & OPT_IGNORE_ERROR)
1630 r = COMPLETE;
1631 if (r != COMPLETE) {
1632 me->verrno = EPERM;
1633 g_free (mpath);
1634 return -1;
1636 if (flush_directory_cache)
1637 vfs_s_invalidate(me, super);
1638 g_free(mpath);
1639 return 0;
1642 /* This routine is called as the last step in load_setup */
1643 void
1644 ftpfs_init_passwd(void)
1646 ftpfs_anonymous_passwd = load_anon_passwd ();
1647 if (ftpfs_anonymous_passwd)
1648 return;
1650 /* If there is no anonymous ftp password specified
1651 * then we'll just use anonymous@
1652 * We don't send any other thing because:
1653 * - We want to remain anonymous
1654 * - We want to stop SPAM
1655 * - We don't want to let ftp sites to discriminate by the user,
1656 * host or country.
1658 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1661 static int ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1663 char buf[BUF_SMALL];
1664 int ret;
1666 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1668 ret = ftpfs_send_command(me, path, buf, OPT_FLUSH);
1670 if ( mc_config_get_bool (mc_main_config, CONFIG_APP_SECTION,
1671 "ignore_ftp_chattr_errors", TRUE)) {
1672 return 0;
1675 return ret;
1678 static int ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1680 #if 0
1681 ftpfs_errno = EPERM;
1682 return -1;
1683 #else
1684 /* Everyone knows it is not possible to chown remotely, so why bother them.
1685 If someone's root, then copy/move will always try to chown it... */
1686 (void) me;
1687 (void) path;
1688 (void) owner;
1689 (void) group;
1690 return 0;
1691 #endif
1694 static int ftpfs_unlink (struct vfs_class *me, const char *path)
1696 return ftpfs_send_command(me, path, "DELE /%s", OPT_FLUSH);
1699 /* Return 1 if path is the same directory as the one we are in now */
1700 static int
1701 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1703 (void) me;
1705 if (!SUP.cwdir)
1706 return 0;
1707 if (strcmp (path, SUP.cwdir) == 0)
1708 return 1;
1709 return 0;
1712 static int
1713 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1715 int r;
1716 char *p;
1718 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1719 return COMPLETE;
1721 p = ftpfs_translate_path (me, super, remote_path);
1722 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1723 g_free (p);
1725 if (r != COMPLETE) {
1726 ftpfs_errno = EIO;
1727 } else {
1728 g_free(SUP.cwdir);
1729 SUP.cwdir = g_strdup (remote_path);
1730 SUP.cwd_deferred = 0;
1732 return r;
1735 static int ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1737 ftpfs_send_command(me, path1, "RNFR /%s", OPT_FLUSH);
1738 return ftpfs_send_command(me, path2, "RNTO /%s", OPT_FLUSH);
1741 static int ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1743 (void) mode; /* FIXME: should be used */
1745 return ftpfs_send_command(me, path, "MKD /%s", OPT_FLUSH);
1748 static int ftpfs_rmdir (struct vfs_class *me, const char *path)
1750 return ftpfs_send_command(me, path, "RMD /%s", OPT_FLUSH);
1753 static int
1754 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
1755 int mode)
1757 (void) mode;
1759 fh->u.ftp.append = 0;
1760 /* File will be written only, so no need to retrieve it from ftp server */
1761 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
1762 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1763 struct linger li;
1764 #else
1765 int li = 1;
1766 #endif
1767 char *name;
1769 /* ftpfs_linear_start() called, so data will be written
1770 * to local temporary file and stored to ftp server
1771 * by vfs_s_close later
1773 if (FH_SUPER->u.ftp.ctl_connection_busy) {
1774 if (!fh->ino->localname) {
1775 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1776 fh->ino->ent->name);
1777 if (handle == -1)
1778 return -1;
1779 close (handle);
1780 fh->u.ftp.append = flags & O_APPEND;
1782 return 0;
1784 name = vfs_s_fullpath (me, fh->ino);
1785 if (!name)
1786 return -1;
1787 fh->handle =
1788 ftpfs_open_data_connection (me, fh->ino->super,
1789 (flags & O_APPEND) ? "APPE" :
1790 "STOR", name, TYPE_BINARY, 0);
1791 g_free (name);
1793 if (fh->handle < 0)
1794 return -1;
1795 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1796 li.l_onoff = 1;
1797 li.l_linger = 120;
1798 #endif
1799 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1801 if (fh->ino->localname) {
1802 unlink (fh->ino->localname);
1803 g_free (fh->ino->localname);
1804 fh->ino->localname = NULL;
1806 return 0;
1809 if (!fh->ino->localname)
1810 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1811 return -1;
1812 if (!fh->ino->localname)
1813 vfs_die ("retrieve_file failed to fill in localname");
1814 return 0;
1817 static int ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1819 if (fh->handle != -1 && !fh->ino->localname){
1820 close (fh->handle);
1821 fh->handle = -1;
1822 /* File is stored to destination already, so
1823 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1825 fh->changed = 0;
1826 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1827 ERRNOR (EIO, -1);
1828 vfs_s_invalidate (me, FH_SUPER);
1830 return 0;
1833 static void
1834 ftpfs_done (struct vfs_class *me)
1836 struct no_proxy_entry *np;
1838 (void) me;
1840 while (no_proxy) {
1841 np = no_proxy->next;
1842 g_free (no_proxy->domain);
1843 g_free (no_proxy);
1844 no_proxy = np;
1846 g_free (ftpfs_anonymous_passwd);
1847 g_free (ftpfs_proxy_host);
1850 static void
1851 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
1853 struct vfs_s_super *super = MEDATA->supers;
1854 char *name;
1856 while (super){
1857 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
1858 (*func)(name);
1859 g_free (name);
1860 super = super->next;
1864 static char buffer[BUF_MEDIUM];
1865 static char *netrc;
1866 static const char *netrcp;
1868 /* This should match the keywords[] array below */
1869 typedef enum {
1870 NETRC_NONE = 0,
1871 NETRC_DEFAULT,
1872 NETRC_MACHINE,
1873 NETRC_LOGIN,
1874 NETRC_PASSWORD,
1875 NETRC_PASSWD,
1876 NETRC_ACCOUNT,
1877 NETRC_MACDEF,
1878 NETRC_UNKNOWN
1879 } keyword_t;
1881 static keyword_t ftpfs_netrc_next (void)
1883 char *p;
1884 keyword_t i;
1885 static const char *const keywords[] = { "default", "machine",
1886 "login", "password", "passwd", "account", "macdef", NULL
1890 while (1) {
1891 netrcp = skip_separators (netrcp);
1892 if (*netrcp != '\n')
1893 break;
1894 netrcp++;
1896 if (!*netrcp)
1897 return NETRC_NONE;
1898 p = buffer;
1899 if (*netrcp == '"') {
1900 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1901 if (*netrcp == '\\')
1902 netrcp++;
1903 *p++ = *netrcp;
1905 } else {
1906 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1907 *netrcp != ',' && *netrcp; netrcp++) {
1908 if (*netrcp == '\\')
1909 netrcp++;
1910 *p++ = *netrcp;
1913 *p = 0;
1914 if (!*buffer)
1915 return NETRC_NONE;
1917 i = NETRC_DEFAULT;
1918 while (keywords[i - 1]) {
1919 if (!strcmp (keywords[i - 1], buffer))
1920 return i;
1922 i++;
1925 return NETRC_UNKNOWN;
1928 static int ftpfs_netrc_bad_mode (const char *netrcname)
1930 static int be_angry = 1;
1931 struct stat mystat;
1933 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1934 if (be_angry) {
1935 message (D_ERROR, MSG_ERROR,
1936 _("~/.netrc file has incorrect mode.\n"
1937 "Remove password or correct mode."));
1938 be_angry = 0;
1940 return 1;
1942 return 0;
1945 /* Scan .netrc until we find matching "machine" or "default"
1946 * domain is used for additional matching
1947 * No search is done after "default" in compliance with "man netrc"
1948 * Return 0 if found, -1 otherwise */
1949 static int ftpfs_find_machine (const char *host, const char *domain)
1951 keyword_t keyword;
1953 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) {
1954 if (keyword == NETRC_DEFAULT)
1955 return 0;
1957 if (keyword == NETRC_MACDEF) {
1958 /* Scan for an empty line, which concludes "macdef" */
1959 do {
1960 while (*netrcp && *netrcp != '\n')
1961 netrcp++;
1962 if (*netrcp != '\n')
1963 break;
1964 netrcp++;
1965 } while (*netrcp && *netrcp != '\n');
1966 continue;
1969 if (keyword != NETRC_MACHINE)
1970 continue;
1972 /* Take machine name */
1973 if (ftpfs_netrc_next () == NETRC_NONE)
1974 break;
1976 if (g_strcasecmp (host, buffer)) {
1977 /* Try adding our domain to short names in .netrc */
1978 const char *host_domain = strchr (host, '.');
1979 if (!host_domain)
1980 continue;
1982 /* Compare domain part */
1983 if (g_strcasecmp (host_domain, domain))
1984 continue;
1986 /* Compare local part */
1987 if (g_strncasecmp (host, buffer, host_domain - host))
1988 continue;
1991 return 0;
1994 /* end of .netrc */
1995 return -1;
1998 /* Extract login and password from .netrc for the host.
1999 * pass may be NULL.
2000 * Returns 0 for success, -1 for error */
2001 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2003 char *netrcname;
2004 char *tmp_pass = NULL;
2005 char hostname[MAXHOSTNAMELEN];
2006 const char *domain;
2007 keyword_t keyword;
2008 static struct rupcache {
2009 struct rupcache *next;
2010 char *host;
2011 char *login;
2012 char *pass;
2013 } *rup_cache = NULL, *rupp;
2015 /* Initialize *login and *pass */
2016 if (!login)
2017 return 0;
2018 *login = NULL;
2019 if (pass)
2020 *pass = NULL;
2022 /* Look up in the cache first */
2023 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
2024 if (!strcmp (host, rupp->host)) {
2025 if (rupp->login)
2026 *login = g_strdup (rupp->login);
2027 if (pass && rupp->pass)
2028 *pass = g_strdup (rupp->pass);
2029 return 0;
2033 /* Load current .netrc */
2034 netrcname = concat_dir_and_file (home_dir, ".netrc");
2035 netrcp = netrc = load_file (netrcname);
2036 if (netrc == NULL) {
2037 g_free (netrcname);
2038 return 0;
2041 /* Find our own domain name */
2042 if (gethostname (hostname, sizeof (hostname)) < 0)
2043 *hostname = 0;
2044 if (!(domain = strchr (hostname, '.')))
2045 domain = "";
2047 /* Scan for "default" and matching "machine" keywords */
2048 ftpfs_find_machine (host, domain);
2050 /* Scan for keywords following "default" and "machine" */
2051 while (1) {
2052 int need_break = 0;
2053 keyword = ftpfs_netrc_next ();
2055 switch (keyword) {
2056 case NETRC_LOGIN:
2057 if (ftpfs_netrc_next () == NETRC_NONE) {
2058 need_break = 1;
2059 break;
2062 /* We have another name already - should not happen */
2063 if (*login) {
2064 need_break = 1;
2065 break;
2068 /* We have login name now */
2069 *login = g_strdup (buffer);
2070 break;
2072 case NETRC_PASSWORD:
2073 case NETRC_PASSWD:
2074 if (ftpfs_netrc_next () == NETRC_NONE) {
2075 need_break = 1;
2076 break;
2079 /* Ignore unsafe passwords */
2080 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2081 && ftpfs_netrc_bad_mode (netrcname)) {
2082 need_break = 1;
2083 break;
2086 /* Remember password. pass may be NULL, so use tmp_pass */
2087 if (tmp_pass == NULL)
2088 tmp_pass = g_strdup (buffer);
2089 break;
2091 case NETRC_ACCOUNT:
2092 /* "account" is followed by a token which we ignore */
2093 if (ftpfs_netrc_next () == NETRC_NONE) {
2094 need_break = 1;
2095 break;
2098 /* Ignore account, but warn user anyways */
2099 ftpfs_netrc_bad_mode (netrcname);
2100 break;
2102 default:
2103 /* Unexpected keyword or end of file */
2104 need_break = 1;
2105 break;
2108 if (need_break)
2109 break;
2112 g_free (netrc);
2113 g_free (netrcname);
2115 rupp = g_new (struct rupcache, 1);
2116 rupp->host = g_strdup (host);
2117 rupp->login = rupp->pass = 0;
2119 if (*login != NULL) {
2120 rupp->login = g_strdup (*login);
2122 if (tmp_pass != NULL)
2123 rupp->pass = g_strdup (tmp_pass);
2124 rupp->next = rup_cache;
2125 rup_cache = rupp;
2127 if (pass)
2128 *pass = tmp_pass;
2130 return 0;
2133 void
2134 init_ftpfs (void)
2136 static struct vfs_s_subclass ftpfs_subclass;
2138 ftpfs_subclass.flags = VFS_S_REMOTE;
2139 ftpfs_subclass.archive_same = ftpfs_archive_same;
2140 ftpfs_subclass.open_archive = ftpfs_open_archive;
2141 ftpfs_subclass.free_archive = ftpfs_free_archive;
2142 ftpfs_subclass.fh_open = ftpfs_fh_open;
2143 ftpfs_subclass.fh_close = ftpfs_fh_close;
2144 ftpfs_subclass.dir_load = ftpfs_dir_load;
2145 ftpfs_subclass.file_store = ftpfs_file_store;
2146 ftpfs_subclass.linear_start = ftpfs_linear_start;
2147 ftpfs_subclass.linear_read = ftpfs_linear_read;
2148 ftpfs_subclass.linear_close = ftpfs_linear_close;
2150 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2151 vfs_ftpfs_ops.name = "ftpfs";
2152 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2153 vfs_ftpfs_ops.prefix = "ftp:";
2154 vfs_ftpfs_ops.done = &ftpfs_done;
2155 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2156 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2157 vfs_ftpfs_ops.chown = ftpfs_chown;
2158 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2159 vfs_ftpfs_ops.rename = ftpfs_rename;
2160 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2161 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2162 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2163 vfs_register_class (&vfs_ftpfs_ops);