Ticket #1945 (new editor action - select all/mark all)
[free-mc.git] / vfs / ftpfs.c
blob6e4202343537ea25096319f256098c3f82ec2d15
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 "../src/global.h"
88 #include "../src/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 "../src/mcconfig/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, const char *remote_path);
188 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
189 __attribute__ ((format (__printf__, 4, 5)));
190 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
191 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass);
192 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
194 static char *
195 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
197 if (!SUP.remote_is_amiga)
198 return g_strdup (remote_path);
199 else {
200 char *ret, *p;
202 if (MEDATA->logfile) {
203 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
204 fflush (MEDATA->logfile);
207 /* strip leading slash(es) */
208 while (*remote_path == '/')
209 remote_path++;
212 * Don't change "/" into "", e.g. "CWD " would be
213 * invalid.
215 if (*remote_path == '\0')
216 return g_strdup (".");
218 ret = g_strdup (remote_path);
220 /* replace first occurance of ":/" with ":" */
221 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
222 strcpy (p + 1, p + 2);
224 /* strip trailing "/." */
225 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
226 *p = '\0';
227 return ret;
231 /* Extract the hostname and username from the path */
234 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
235 * ftp://sunsite.unc.edu/pub/linux
236 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
237 * ftp://tsx-11.mit.edu:8192/
238 * ftp://joe@foo.edu:11321/private
239 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
240 * is supplied.
244 #define FTP_COMMAND_PORT 21
246 static void
247 ftpfs_split_url(char *path, char **host, char **user, int *port, char **pass)
249 char *p;
251 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
252 URL_USE_ANONYMOUS);
254 if (!*user) {
255 /* Look up user and password in netrc */
256 if (use_netrc)
257 ftpfs_netrc_lookup (*host, user, pass);
258 if (!*user)
259 *user = g_strdup ("anonymous");
262 /* Look up password in netrc for known user */
263 if (use_netrc && *user && pass && !*pass) {
264 char *new_user;
266 ftpfs_netrc_lookup (*host, &new_user, pass);
268 /* If user is different, remove password */
269 if (new_user && strcmp (*user, new_user)) {
270 g_free (*pass);
271 *pass = NULL;
274 g_free (new_user);
277 g_free (p);
280 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
281 static int
282 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
284 char answer[BUF_1K];
285 int i;
287 for (;;) {
288 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
289 if (string_buf)
290 *string_buf = 0;
291 code = 421;
292 return 4;
294 switch (sscanf(answer, "%d", &code)){
295 case 0:
296 if (string_buf)
297 g_strlcpy (string_buf, answer, string_len);
298 code = 500;
299 return 5;
300 case 1:
301 if (answer[3] == '-') {
302 while (1) {
303 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
304 if (string_buf)
305 *string_buf = 0;
306 code = 421;
307 return 4;
309 if ((sscanf (answer, "%d", &i) > 0) &&
310 (code == i) && (answer[3] == ' '))
311 break;
314 if (string_buf)
315 g_strlcpy (string_buf, answer, string_len);
316 return code / 100;
321 static int
322 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
324 int sock = ftpfs_open_socket (me, super);
325 if (sock != -1){
326 char *cwdir = SUP.cwdir;
327 close (SUP.sock);
328 SUP.sock = sock;
329 SUP.cwdir = NULL;
330 if (ftpfs_login_server (me, super, SUP.password)){
331 if (!cwdir)
332 return 1;
333 sock = ftpfs_chdir_internal (me, super, cwdir);
334 g_free (cwdir);
335 return sock == COMPLETE;
337 SUP.cwdir = cwdir;
339 return 0;
342 static int
343 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
345 va_list ap;
346 char *cmdstr;
347 int status, cmdlen;
348 static int retry = 0;
349 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
351 va_start (ap, fmt);
352 cmdstr = g_strdup_vprintf (fmt, ap);
353 va_end (ap);
355 cmdlen = strlen (cmdstr);
356 cmdstr = g_realloc (cmdstr, cmdlen + 3);
357 strcpy (cmdstr + cmdlen, "\r\n");
358 cmdlen += 2;
360 if (MEDATA->logfile) {
361 if (strncmp (cmdstr, "PASS ", 5) == 0) {
362 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
363 } else
364 fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
366 fflush (MEDATA->logfile);
369 got_sigpipe = 0;
370 tty_enable_interrupt_key ();
371 status = write (SUP.sock, cmdstr, cmdlen);
373 if (status < 0) {
374 code = 421;
376 if (errno == EPIPE) { /* Remote server has closed connection */
377 if (level == 0) {
378 level = 1;
379 status = ftpfs_reconnect (me, super);
380 level = 0;
381 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
382 goto ok;
386 got_sigpipe = 1;
388 g_free (cmdstr);
389 tty_disable_interrupt_key ();
390 return TRANSIENT;
392 retry = 0;
394 tty_disable_interrupt_key ();
396 if (wait_reply)
398 status = ftpfs_get_reply (me, SUP.sock,
399 (wait_reply & WANT_STRING) ? reply_str : NULL,
400 sizeof (reply_str) - 1);
401 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
403 retry = 1;
404 level = 1;
405 status = ftpfs_reconnect (me, super);
406 level = 0;
407 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
408 goto ok;
411 retry = 0;
412 g_free (cmdstr);
413 return status;
415 g_free (cmdstr);
416 return COMPLETE;
419 static void
420 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
422 if (SUP.sock != -1){
423 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
424 ftpfs_command(me, super, NONE, "QUIT");
425 close(SUP.sock);
427 g_free (SUP.host);
428 g_free (SUP.user);
429 g_free (SUP.cwdir);
430 g_free (SUP.password);
433 /* some defines only used by ftpfs_changetype */
434 /* These two are valid values for the second parameter */
435 #define TYPE_ASCII 0
436 #define TYPE_BINARY 1
438 /* This one is only used to initialize bucket->isbinary, don't use it as
439 second parameter to ftpfs_changetype. */
440 #define TYPE_UNKNOWN -1
442 static int
443 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
445 if (binary != SUP.isbinary) {
446 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
447 ERRNOR (EIO, -1);
448 SUP.isbinary = binary;
450 return binary;
453 /* This routine logs the user in */
454 static int
455 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
456 const char *netrcpass)
458 char *pass;
459 char *op;
460 char *name; /* login user name */
461 int anon = 0;
462 char reply_string[BUF_MEDIUM];
464 SUP.isbinary = TYPE_UNKNOWN;
466 if (SUP.password) /* explicit password */
467 op = g_strdup (SUP.password);
468 else if (netrcpass) /* password from netrc */
469 op = g_strdup (netrcpass);
470 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp")) {
471 if (!ftpfs_anonymous_passwd) /* default anonymous password */
472 ftpfs_init_passwd ();
473 op = g_strdup (ftpfs_anonymous_passwd);
474 anon = 1;
475 } else { /* ask user */
476 char *p;
478 p = g_strconcat (_(" FTP: Password required for "),
479 SUP.user, " ", (char *) NULL);
480 op = vfs_get_password (p);
481 g_free (p);
482 if (op == NULL)
483 ERRNOR (EPERM, 0);
484 SUP.password = g_strdup (op);
487 if (!anon || MEDATA->logfile)
488 pass = op;
489 else {
490 pass = g_strconcat ("-", op, (char *) NULL);
491 wipe_password (op);
494 /* Proxy server accepts: username@host-we-want-to-connect */
495 if (SUP.proxy) {
496 name =
497 g_strconcat (SUP.user, "@",
498 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host,
499 (char *) NULL);
500 } else
501 name = g_strdup (SUP.user);
503 if (ftpfs_get_reply
504 (me, SUP.sock, reply_string,
505 sizeof (reply_string) - 1) == COMPLETE) {
506 g_strup (reply_string);
507 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
508 if (MEDATA->logfile) {
509 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n",
510 SUP.remote_is_amiga);
511 fflush (MEDATA->logfile);
514 print_vfs_message (_("ftpfs: sending login name"));
516 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name)) {
517 case CONTINUE:
518 print_vfs_message (_("ftpfs: sending user password"));
519 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
520 if (code == CONTINUE) {
521 char *p;
523 p = g_strdup_printf (_
524 ("FTP: Account required for user %s"),
525 SUP.user);
526 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
527 g_free (p);
528 if (op == NULL)
529 ERRNOR (EPERM, 0);
530 print_vfs_message (_("ftpfs: sending user account"));
531 code =
532 ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
533 g_free (op);
535 if (code != COMPLETE)
536 break;
537 /* fall through */
539 case COMPLETE:
540 print_vfs_message (_("ftpfs: logged in"));
541 wipe_password (pass);
542 g_free (name);
543 return 1;
545 default:
546 SUP.failed_on_login = 1;
547 if (SUP.password)
548 wipe_password (SUP.password);
549 SUP.password = 0;
551 goto login_fail;
554 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
555 SUP.user);
556 login_fail:
557 wipe_password (pass);
558 g_free (name);
559 ERRNOR (EPERM, 0);
562 static struct no_proxy_entry {
563 char *domain;
564 void *next;
565 } *no_proxy;
567 static void
568 ftpfs_load_no_proxy_list (void)
570 /* FixMe: shouldn't be hardcoded!!! */
571 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
572 struct no_proxy_entry *np, *current = 0;
573 FILE *npf;
574 int c;
575 char *p;
576 static char *mc_file;
578 if (mc_file)
579 return;
581 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
582 if (exist_file (mc_file) &&
583 (npf = fopen (mc_file, "r"))) {
584 while (fgets (s, sizeof (s), npf)) {
585 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
586 while ((c = fgetc (npf)) != EOF && c != '\n')
588 continue;
591 if (p == s)
592 continue;
594 *p = '\0';
596 np = g_new (struct no_proxy_entry, 1);
597 np->domain = g_strdup (s);
598 np->next = NULL;
599 if (no_proxy)
600 current->next = np;
601 else
602 no_proxy = np;
603 current = np;
606 fclose (npf);
608 g_free (mc_file);
611 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
612 static int
613 ftpfs_check_proxy (const char *host)
615 struct no_proxy_entry *npe;
617 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
618 return 0; /* sanity check */
620 if (*host == '!')
621 return 1;
623 if (!ftpfs_always_use_proxy)
624 return 0;
626 if (!strchr (host, '.'))
627 return 0;
629 ftpfs_load_no_proxy_list ();
630 for (npe = no_proxy; npe; npe=npe->next) {
631 char *domain = npe->domain;
633 if (domain[0] == '.') {
634 int ld = strlen (domain);
635 int lh = strlen (host);
637 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
638 ld--;
639 lh--;
642 if (!ld)
643 return 0;
644 } else
645 if (!g_strcasecmp (host, domain))
646 return 0;
649 return 1;
652 static void
653 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
655 char *user, *dir;
657 dir =
658 vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT,
659 URL_USE_ANONYMOUS);
660 g_free (user);
661 g_free (dir);
664 static int
665 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
667 struct addrinfo hints, *res, *curr_res;
668 int my_socket = 0;
669 char *host = NULL;
670 char *port = NULL;
671 int tmp_port;
672 int e;
674 (void) me;
676 /* Use a proxy host? */
677 host = g_strdup(SUP.host);
679 if (!host || !*host){
680 print_vfs_message (_("ftpfs: Invalid host name."));
681 ftpfs_errno = EINVAL;
682 g_free (host);
683 return -1;
686 /* Hosts to connect to that start with a ! should use proxy */
687 tmp_port = SUP.port;
689 if (SUP.proxy){
690 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
693 port = g_strdup_printf("%hu", (unsigned short) tmp_port);
694 if (port == NULL) {
695 g_free (host);
696 ftpfs_errno = errno;
697 return -1;
700 tty_enable_interrupt_key(); /* clear the interrupt flag */
702 memset (&hints, 0, sizeof (struct addrinfo));
703 hints.ai_socktype = SOCK_STREAM;
704 hints.ai_flags = AI_ADDRCONFIG;
707 e = getaddrinfo (host, port, &hints, &res);
708 g_free (port);
709 port = NULL;
711 if ( e != 0 ) {
712 tty_disable_interrupt_key ();
713 print_vfs_message (_("ftpfs: %s"), gai_strerror (e));
714 g_free (host);
715 ftpfs_errno = EINVAL;
716 return -1;
719 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next) {
721 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
723 if (my_socket < 0) {
725 if (curr_res->ai_next != NULL)
726 continue;
728 tty_disable_interrupt_key();
729 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno));
730 g_free (host);
731 freeaddrinfo (res);
732 ftpfs_errno = errno;
733 return -1;
736 print_vfs_message (_("ftpfs: making connection to %s"), host);
737 g_free (host);
738 host = NULL;
740 if ( connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0 )
741 break;
743 ftpfs_errno = errno;
744 close (my_socket);
746 if (errno == EINTR && tty_got_interrupt ()) {
747 print_vfs_message (_("ftpfs: connection interrupted by user"));
748 } else if (res->ai_next == NULL) {
749 print_vfs_message (_("ftpfs: connection to server failed: %s"),
750 unix_error_string (errno));
751 } else {
752 continue;
755 freeaddrinfo (res);
756 tty_disable_interrupt_key ();
757 return -1;
760 freeaddrinfo (res);
761 tty_disable_interrupt_key ();
762 return my_socket;
765 static int
766 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
768 int retry_seconds, count_down;
770 /* We do not want to use the passive if we are using proxies */
771 if (SUP.proxy)
772 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
774 retry_seconds = 0;
775 do {
776 SUP.failed_on_login = 0;
778 SUP.sock = ftpfs_open_socket (me, super);
779 if (SUP.sock == -1)
780 return -1;
782 if (ftpfs_login_server (me, super, NULL)) {
783 /* Logged in, no need to retry the connection */
784 break;
785 } else {
786 if (SUP.failed_on_login){
787 /* Close only the socket descriptor */
788 close (SUP.sock);
789 } else {
790 return -1;
792 if (ftpfs_retry_seconds){
793 retry_seconds = ftpfs_retry_seconds;
794 tty_enable_interrupt_key ();
795 for (count_down = retry_seconds; count_down; count_down--){
796 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
797 sleep (1);
798 if (tty_got_interrupt ()) {
799 /* ftpfs_errno = E; */
800 tty_disable_interrupt_key ();
801 return 0;
804 tty_disable_interrupt_key ();
807 } while (retry_seconds);
809 SUP.cwdir = ftpfs_get_current_directory (me, super);
810 if (!SUP.cwdir)
811 SUP.cwdir = g_strdup (PATH_SEP_STR);
812 return 0;
815 static int
816 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
817 const char *archive_name, char *op)
819 char *host, *user, *password;
820 int port;
822 (void) archive_name;
824 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
826 SUP.host = host;
827 SUP.user = user;
828 SUP.port = port;
829 SUP.cwdir = NULL;
830 SUP.proxy = 0;
831 if (ftpfs_check_proxy (host))
832 SUP.proxy = ftpfs_proxy_host;
833 SUP.password = password;
834 SUP.use_passive_connection = ftpfs_use_passive_connections;
835 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
836 SUP.isbinary = TYPE_UNKNOWN;
837 SUP.remote_is_amiga = 0;
838 super->name = g_strdup ("/");
839 super->root =
840 vfs_s_new_inode (me, super,
841 vfs_s_default_stat (me, S_IFDIR | 0755));
843 return ftpfs_open_archive_int (me, super);
846 static int
847 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
848 const char *archive_name, char *op, void *cookie)
850 char *host, *user;
851 int port;
853 (void) me;
854 (void) archive_name;
855 (void) cookie;
857 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
859 port = ((strcmp (host, SUP.host) == 0)
860 && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
862 g_free (host);
863 g_free (user);
865 return port;
868 /* The returned directory should always contain a trailing slash */
869 static char *
870 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
872 char buf[BUF_8K], *bufp, *bufq;
874 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
875 ftpfs_get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
876 bufp = NULL;
877 for (bufq = buf; *bufq; bufq++)
878 if (*bufq == '"') {
879 if (!bufp) {
880 bufp = bufq + 1;
881 } else {
882 *bufq = 0;
883 if (*bufp) {
884 if (*(bufq - 1) != '/') {
885 *bufq++ = '/';
886 *bufq = 0;
888 if (*bufp == '/')
889 return g_strdup (bufp);
890 else {
891 /* If the remote server is an Amiga a leading slash
892 might be missing. MC needs it because it is used
893 as separator between hostname and path internally. */
894 return g_strconcat( "/", bufp, (char *) NULL);
896 } else {
897 ftpfs_errno = EIO;
898 return NULL;
903 ftpfs_errno = EIO;
904 return NULL;
908 /* Setup Passive ftp connection, we use it for source routed connections */
909 static int
910 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
911 int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
913 char *c;
915 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") == COMPLETE) {
916 int port;
917 /* (|||<port>|) */
918 c = strchr (reply_str, '|');
919 if (c == NULL)
920 return 0;
921 if(strlen(c) > 3)
922 c+=3;
923 else
924 return 0;
926 port = atoi (c);
927 if (port < 0 || port > 65535)
928 return 0;
929 port = htons (port);
931 switch (sa->ss_family) {
932 case AF_INET:
933 ((struct sockaddr_in *)sa)->sin_port = port;
934 break;
935 case AF_INET6:
936 ((struct sockaddr_in6 *)sa)->sin6_port = port;
937 break;
938 default:
939 print_vfs_message (_("ftpfs: invalid address family"));
940 ERRNOR (EINVAL, -1);
942 } else if (sa->ss_family == AF_INET) {
943 int xa, xb, xc, xd, xe, xf;
944 char n [6];
946 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
947 return 0;
949 /* Parse remote parameters */
950 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
952 if (!*c)
953 return 0;
954 if (!isdigit ((unsigned char) *c))
955 return 0;
956 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
957 return 0;
959 n [0] = (unsigned char) xa;
960 n [1] = (unsigned char) xb;
961 n [2] = (unsigned char) xc;
962 n [3] = (unsigned char) xd;
963 n [4] = (unsigned char) xe;
964 n [5] = (unsigned char) xf;
966 memcpy (&(((struct sockaddr_in *)sa)->sin_addr.s_addr), (void *)n, 4);
967 memcpy (&(((struct sockaddr_in *)sa)->sin_port), (void *)&n[4], 2);
968 } else
969 return 0;
971 if (connect (my_socket, (struct sockaddr *) sa, *salen ) < 0)
972 return 0;
974 return 1;
977 static int
978 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
980 struct sockaddr_storage data_addr;
981 socklen_t data_addrlen;
982 int data_sock, result;
984 again:
985 memset (&data_addr, 0, sizeof (struct sockaddr_storage));
986 data_addrlen = sizeof (struct sockaddr_storage);
988 if (SUP.use_passive_connection)
989 result = getpeername (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen);
990 else
991 result = getsockname (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen);
993 if (result == -1 )
994 return -1;
996 switch (data_addr.ss_family) {
997 case AF_INET:
998 ((struct sockaddr_in *)&data_addr)->sin_port = 0;
999 break;
1000 case AF_INET6:
1001 ((struct sockaddr_in6 *)&data_addr)->sin6_port = 0;
1002 break;
1003 default:
1004 print_vfs_message (_("ftpfs: invalid address family"));
1005 ERRNOR(EINVAL, -1);
1008 data_sock = socket (data_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
1009 if (data_sock < 0) {
1010 if (SUP.use_passive_connection) {
1011 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno));
1012 SUP.use_passive_connection = 0;
1013 goto again;
1016 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1017 return -1;
1020 if (SUP.use_passive_connection) {
1022 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1023 return data_sock;
1025 SUP.use_passive_connection = 0;
1026 print_vfs_message (_("ftpfs: could not setup passive mode"));
1028 close (data_sock);
1029 goto again;
1032 /* If passive setup fails, fallback to active connections */
1033 /* Active FTP connection */
1034 if ((bind (data_sock, (struct sockaddr *)&data_addr, data_addrlen) == 0) &&
1035 (getsockname (data_sock, (struct sockaddr *)&data_addr, &data_addrlen) == 0) &&
1036 (listen (data_sock, 1) == 0)) {
1037 unsigned short int port;
1038 char *addr;
1039 unsigned int af;
1041 switch (data_addr.ss_family) {
1042 case AF_INET:
1043 af = FTP_INET;
1044 port = ((struct sockaddr_in *)&data_addr)->sin_port;
1045 break;
1046 case AF_INET6:
1047 af = FTP_INET6;
1048 port = ((struct sockaddr_in6 *)&data_addr)->sin6_port;
1049 break;
1050 default:
1051 print_vfs_message (_("ftpfs: invalid address family"));
1052 ERRNOR (EINVAL, -1);
1055 port = ntohs (port);
1057 addr = g_try_malloc (NI_MAXHOST);
1058 if (addr == NULL)
1059 ERRNOR (ENOMEM, -1);
1061 if (getnameinfo ((struct sockaddr *)&data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) {
1062 g_free (addr);
1063 ERRNOR (EIO, -1);
1066 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE) {
1067 g_free (addr);
1068 return data_sock;
1070 g_free (addr);
1072 if (FTP_INET == af) {
1073 unsigned char *a = (unsigned char *)&((struct sockaddr_in *)&data_addr)->sin_addr;
1074 unsigned char *p = (unsigned char *)&port;
1076 if (ftpfs_command (me, super, WAIT_REPLY,
1077 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1078 p[0], p[1]) == COMPLETE)
1079 return data_sock;
1082 close (data_sock);
1083 ftpfs_errno = EIO;
1084 return -1;
1087 static int
1088 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1089 const char *remote, int isbinary, int reget)
1091 struct sockaddr_storage from;
1092 int s, j, data;
1093 socklen_t fromlen = sizeof(from);
1095 if ((s = ftpfs_initconn (me, super)) == -1)
1096 return -1;
1097 if (ftpfs_changetype (me, super, isbinary) == -1)
1098 return -1;
1099 if (reget > 0){
1100 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1101 if (j != CONTINUE)
1102 return -1;
1104 if (remote) {
1105 char *remote_path = ftpfs_translate_path (me, super, remote);
1106 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1107 /* WarFtpD can't STORE //filename */
1108 (*remote_path == '/') ? remote_path + 1 : remote_path);
1109 g_free (remote_path);
1110 } else
1111 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1112 if (j != PRELIM)
1113 ERRNOR (EPERM, -1);
1114 tty_enable_interrupt_key ();
1115 if (SUP.use_passive_connection)
1116 data = s;
1117 else {
1118 data = accept (s, (struct sockaddr *)&from, &fromlen);
1119 if (data < 0) {
1120 ftpfs_errno = errno;
1121 close (s);
1122 return -1;
1124 close (s);
1126 tty_disable_interrupt_key ();
1127 return data;
1130 #define ABORT_TIMEOUT 5
1131 static void
1132 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
1134 struct vfs_s_super *super = FH_SUPER;
1135 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1136 fd_set mask;
1137 char buf[1024];
1138 int dsock = FH_SOCK;
1139 FH_SOCK = -1;
1140 SUP.ctl_connection_busy = 0;
1142 print_vfs_message (_("ftpfs: aborting transfer."));
1143 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
1144 print_vfs_message (_("ftpfs: abort error: %s"),
1145 unix_error_string (errno));
1146 if (dsock != -1)
1147 close (dsock);
1148 return;
1151 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
1152 print_vfs_message (_("ftpfs: abort failed"));
1153 if (dsock != -1)
1154 close (dsock);
1155 return;
1157 if (dsock != -1) {
1158 FD_ZERO (&mask);
1159 FD_SET (dsock, &mask);
1160 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1161 struct timeval start_tim, tim;
1162 gettimeofday (&start_tim, NULL);
1163 /* flush the remaining data */
1164 while (read (dsock, buf, sizeof (buf)) > 0) {
1165 gettimeofday (&tim, NULL);
1166 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1167 /* server keeps sending, drop the connection and ftpfs_reconnect */
1168 close (dsock);
1169 ftpfs_reconnect (me, super);
1170 return;
1174 close (dsock);
1176 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1177 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1180 #if 0
1181 static void
1182 resolve_symlink_without_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1184 struct linklist *flist;
1185 struct direntry *fe, *fel;
1186 char tmp[MC_MAXPATHLEN];
1187 int depth;
1189 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1190 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1191 /* flist->data->l_stat is alread initialized with 0 */
1192 fel = flist->data;
1193 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1194 if (fel->linkname[0] == '/') {
1195 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1196 continue;
1197 strcpy (tmp, fel->linkname);
1198 } else {
1199 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1200 continue;
1201 strcpy (tmp, dir->remote_path);
1202 if (tmp[1] != '\0')
1203 strcat (tmp, "/");
1204 strcat (tmp + 1, fel->linkname);
1206 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1207 canonicalize_pathname (tmp);
1208 fe = _get_file_entry(bucket, tmp, 0, 0);
1209 if (fe) {
1210 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1211 /* Symlink points to link which isn't resolved, yet. */
1212 if (fe->linkname[0] == '/') {
1213 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1214 break;
1215 strcpy (tmp, fe->linkname);
1216 } else {
1217 /* at this point tmp looks always like this
1218 /directory/filename, i.e. no need to check
1219 strrchr's return value */
1220 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1221 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1222 break;
1223 strcat (tmp, fe->linkname);
1225 continue;
1226 } else {
1227 fel->l_stat = g_new (struct stat, 1);
1228 if ( S_ISLNK (fe->s.st_mode))
1229 *fel->l_stat = *fe->l_stat;
1230 else
1231 *fel->l_stat = fe->s;
1232 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1235 break;
1239 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1242 static void
1243 resolve_symlink_with_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1245 char buffer[2048] = "", *filename;
1246 int sock;
1247 FILE *fp;
1248 struct stat s;
1249 struct linklist *flist;
1250 struct direntry *fe;
1251 int switch_method = 0;
1253 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1254 if (strchr (dir->remote_path, ' ')) {
1255 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1256 print_vfs_message(_("ftpfs: CWD failed."));
1257 return;
1259 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1261 else
1262 sock = ftpfs_open_data_connection (bucket, "LIST -lLa",
1263 dir->remote_path, TYPE_ASCII, 0);
1265 if (sock == -1) {
1266 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1267 return;
1270 fp = fdopen(sock, "r");
1271 if (fp == NULL) {
1272 close(sock);
1273 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1274 return;
1276 tty_enable_interrupt_key ();
1277 flist = dir->file_list->next;
1278 while (1) {
1279 do {
1280 if (flist == dir->file_list)
1281 goto done;
1282 fe = flist->data;
1283 flist = flist->next;
1284 } while (!S_ISLNK(fe->s.st_mode));
1285 while (1) {
1286 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1287 goto done;
1288 if (MEDATA->logfile){
1289 fputs (buffer, MEDATA->logfile);
1290 fflush (MEDATA->logfile);
1292 vfs_die("This code should be commented out\n");
1293 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1294 int r = strcmp(fe->name, filename);
1295 g_free(filename);
1296 if (r == 0) {
1297 if (S_ISLNK (s.st_mode)) {
1298 /* This server doesn't understand LIST -lLa */
1299 switch_method = 1;
1300 goto done;
1302 fe->l_stat = g_new (struct stat, 1);
1303 if (fe->l_stat == NULL)
1304 goto done;
1305 *fe->l_stat = s;
1306 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1307 break;
1309 if (r < 0)
1310 break;
1314 done:
1315 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1316 tty_disable_interrupt_key ();
1317 fclose(fp);
1318 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1321 static void
1322 resolve_symlink(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1324 print_vfs_message(_("Resolving symlink..."));
1326 if (SUP.strict_rfc959_list_cmd)
1327 resolve_symlink_without_ls_options(me, super, dir);
1328 else
1329 resolve_symlink_with_ls_options(me, super, dir);
1331 #endif
1333 static int
1334 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1336 struct vfs_s_entry *ent;
1337 struct vfs_s_super *super = dir->super;
1338 int sock, num_entries = 0;
1339 char buffer[BUF_8K];
1340 int cd_first;
1342 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1343 || (strchr (remote_path, ' ') != NULL);
1345 again:
1346 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1347 remote_path,
1348 SUP.strict ==
1349 RFC_STRICT ? _("(strict rfc959)") : "",
1350 cd_first ? _("(chdir first)") : "");
1352 if (cd_first) {
1353 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE) {
1354 ftpfs_errno = ENOENT;
1355 print_vfs_message (_("ftpfs: CWD failed."));
1356 return -1;
1360 gettimeofday (&dir->timestamp, NULL);
1361 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1363 if (SUP.strict == RFC_STRICT)
1364 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1365 else if (cd_first)
1366 /* Dirty hack to avoid autoprepending / to . */
1367 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1368 sock =
1369 ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1370 else {
1371 /* Trailing "/." is necessary if remote_path is a symlink */
1372 char *path = concat_dir_and_file (remote_path, ".");
1373 sock =
1374 ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII,
1376 g_free (path);
1379 if (sock == -1)
1380 goto fallback;
1382 /* Clear the interrupt flag */
1383 tty_enable_interrupt_key ();
1385 while (1) {
1386 int i;
1387 int res =
1388 vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1389 sock);
1390 if (!res)
1391 break;
1393 if (res == EINTR) {
1394 me->verrno = ECONNRESET;
1395 close (sock);
1396 tty_disable_interrupt_key ();
1397 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1398 print_vfs_message (_("%s: failure"), me->name);
1399 return -1;
1402 if (MEDATA->logfile) {
1403 fputs (buffer, MEDATA->logfile);
1404 fputs ("\n", MEDATA->logfile);
1405 fflush (MEDATA->logfile);
1408 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1409 i = ent->ino->st.st_nlink;
1410 if (!vfs_parse_ls_lga
1411 (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1412 vfs_s_free_entry (me, ent);
1413 continue;
1415 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1416 num_entries++;
1417 vfs_s_insert_entry (me, dir, ent);
1420 close (sock);
1421 me->verrno = E_REMOTE;
1422 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1423 goto fallback;
1425 if (num_entries == 0 && cd_first == 0) {
1426 /* The LIST command may produce an empty output. In such scenario
1427 it is not clear whether this is caused by `remote_path' being
1428 a non-existent path or for some other reason (listing emtpy
1429 directory without the -a option, non-readable directory, etc.).
1431 Since `dir_load' is a crucial method, when it comes to determine
1432 whether a given path is a _directory_, the code must try its best
1433 to determine the type of `remote_path'. The only reliable way to
1434 achieve this is trough issuing a CWD command. */
1436 cd_first = 1;
1437 goto again;
1440 if (SUP.strict == RFC_AUTODETECT)
1441 SUP.strict = RFC_DARING;
1443 print_vfs_message (_("%s: done."), me->name);
1444 return 0;
1446 fallback:
1447 if (SUP.strict == RFC_AUTODETECT) {
1448 /* It's our first attempt to get a directory listing from this
1449 server (UNIX style LIST command) */
1450 SUP.strict = RFC_STRICT;
1451 /* I hate goto, but recursive call needs another 8K on stack */
1452 /* return ftpfs_dir_load (me, dir, remote_path); */
1453 cd_first = 1;
1454 goto again;
1456 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1457 ERRNOR (EACCES, -1);
1460 static int
1461 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name,
1462 char *localname)
1464 int h, sock, n_read, n_written;
1465 off_t n_stored;
1466 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1467 struct linger li;
1468 #else
1469 int flag_one = 1;
1470 #endif
1471 char buffer[8192];
1472 struct stat s;
1473 char *w_buf;
1474 struct vfs_s_super *super = FH_SUPER;
1476 h = open (localname, O_RDONLY);
1477 if (h == -1)
1478 ERRNOR (EIO, -1);
1479 sock =
1480 ftpfs_open_data_connection (me, super,
1481 fh->u.ftp.append ? "APPE" : "STOR", name,
1482 TYPE_BINARY, 0);
1483 if (sock < 0 || fstat (h, &s) == -1) {
1484 close (h);
1485 return -1;
1487 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1488 li.l_onoff = 1;
1489 li.l_linger = 120;
1490 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1491 #else
1492 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1493 #endif
1494 n_stored = 0;
1496 tty_enable_interrupt_key ();
1497 while (1) {
1498 while ((n_read = read (h, buffer, sizeof (buffer))) == -1) {
1499 if (errno == EINTR) {
1500 if (tty_got_interrupt ()) {
1501 ftpfs_errno = EINTR;
1502 goto error_return;
1503 } else
1504 continue;
1506 ftpfs_errno = errno;
1507 goto error_return;
1509 if (n_read == 0)
1510 break;
1511 n_stored += n_read;
1512 w_buf = buffer;
1513 while ((n_written = write (sock, w_buf, n_read)) != n_read) {
1514 if (n_written == -1) {
1515 if (errno == EINTR && !tty_got_interrupt ()) {
1516 continue;
1518 ftpfs_errno = errno;
1519 goto error_return;
1521 w_buf += n_written;
1522 n_read -= n_written;
1524 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1525 (unsigned long) n_stored, (unsigned long) s.st_size);
1527 tty_disable_interrupt_key ();
1528 close (sock);
1529 close (h);
1530 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1531 ERRNOR (EIO, -1);
1532 return 0;
1533 error_return:
1534 tty_disable_interrupt_key ();
1535 close (sock);
1536 close (h);
1537 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1538 return -1;
1541 static int
1542 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1544 char *name = vfs_s_fullpath (me, fh->ino);
1546 if (!name)
1547 return 0;
1548 FH_SOCK = ftpfs_open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1549 g_free (name);
1550 if (FH_SOCK == -1)
1551 ERRNOR (EACCES, 0);
1552 fh->linear = LS_LINEAR_OPEN;
1553 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1554 fh->u.ftp.append = 0;
1555 return 1;
1558 static int
1559 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1561 int n;
1562 struct vfs_s_super *super = FH_SUPER;
1564 while ((n = read (FH_SOCK, buf, len))<0) {
1565 if ((errno == EINTR) && !tty_got_interrupt ())
1566 continue;
1567 break;
1570 if (n<0)
1571 ftpfs_linear_abort(me, fh);
1573 if (!n) {
1574 SUP.ctl_connection_busy = 0;
1575 close (FH_SOCK);
1576 FH_SOCK = -1;
1577 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1578 ERRNOR (E_REMOTE, -1);
1579 return 0;
1581 ERRNOR (errno, n);
1584 static void
1585 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1587 if (FH_SOCK != -1)
1588 ftpfs_linear_abort(me, fh);
1591 static int ftpfs_ctl (void *fh, int ctlop, void *arg)
1593 (void) arg;
1595 switch (ctlop) {
1596 case VFS_CTL_IS_NOTREADY:
1598 int v;
1600 if (!FH->linear)
1601 vfs_die ("You may not do this");
1602 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1603 return 0;
1605 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1606 if (((v < 0) && (errno == EINTR)) || v == 0)
1607 return 1;
1608 return 0;
1610 default:
1611 return 0;
1615 static int
1616 ftpfs_send_command(struct vfs_class *me, const char *filename, const char *cmd, int flags)
1618 const char *rpath;
1619 char *p, *mpath = g_strdup(filename);
1620 struct vfs_s_super *super;
1621 int r;
1622 int flush_directory_cache = (flags & OPT_FLUSH);
1624 if (!(rpath = vfs_s_get_path_mangle(me, mpath, &super, 0))) {
1625 g_free(mpath);
1626 return -1;
1628 p = ftpfs_translate_path (me, super, rpath);
1629 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1630 g_free (p);
1631 vfs_stamp_create (&vfs_ftpfs_ops, super);
1632 if (flags & OPT_IGNORE_ERROR)
1633 r = COMPLETE;
1634 if (r != COMPLETE) {
1635 me->verrno = EPERM;
1636 g_free (mpath);
1637 return -1;
1639 if (flush_directory_cache)
1640 vfs_s_invalidate(me, super);
1641 g_free(mpath);
1642 return 0;
1645 /* This routine is called as the last step in load_setup */
1646 void
1647 ftpfs_init_passwd(void)
1649 ftpfs_anonymous_passwd = load_anon_passwd ();
1650 if (ftpfs_anonymous_passwd)
1651 return;
1653 /* If there is no anonymous ftp password specified
1654 * then we'll just use anonymous@
1655 * We don't send any other thing because:
1656 * - We want to remain anonymous
1657 * - We want to stop SPAM
1658 * - We don't want to let ftp sites to discriminate by the user,
1659 * host or country.
1661 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1664 static int ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1666 char buf[BUF_SMALL];
1667 int ret;
1669 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1671 ret = ftpfs_send_command(me, path, buf, OPT_FLUSH);
1673 if ( mc_config_get_bool (mc_main_config, CONFIG_APP_SECTION,
1674 "ignore_ftp_chattr_errors", TRUE)) {
1675 return 0;
1678 return ret;
1681 static int ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1683 #if 0
1684 ftpfs_errno = EPERM;
1685 return -1;
1686 #else
1687 /* Everyone knows it is not possible to chown remotely, so why bother them.
1688 If someone's root, then copy/move will always try to chown it... */
1689 (void) me;
1690 (void) path;
1691 (void) owner;
1692 (void) group;
1693 return 0;
1694 #endif
1697 static int ftpfs_unlink (struct vfs_class *me, const char *path)
1699 return ftpfs_send_command(me, path, "DELE /%s", OPT_FLUSH);
1702 /* Return 1 if path is the same directory as the one we are in now */
1703 static int
1704 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1706 (void) me;
1708 if (!SUP.cwdir)
1709 return 0;
1710 if (strcmp (path, SUP.cwdir) == 0)
1711 return 1;
1712 return 0;
1715 static int
1716 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1718 int r;
1719 char *p;
1721 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1722 return COMPLETE;
1724 p = ftpfs_translate_path (me, super, remote_path);
1725 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1726 g_free (p);
1728 if (r != COMPLETE) {
1729 ftpfs_errno = EIO;
1730 } else {
1731 g_free(SUP.cwdir);
1732 SUP.cwdir = g_strdup (remote_path);
1733 SUP.cwd_deferred = 0;
1735 return r;
1738 static int ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1740 ftpfs_send_command(me, path1, "RNFR /%s", OPT_FLUSH);
1741 return ftpfs_send_command(me, path2, "RNTO /%s", OPT_FLUSH);
1744 static int ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1746 (void) mode; /* FIXME: should be used */
1748 return ftpfs_send_command(me, path, "MKD /%s", OPT_FLUSH);
1751 static int ftpfs_rmdir (struct vfs_class *me, const char *path)
1753 return ftpfs_send_command(me, path, "RMD /%s", OPT_FLUSH);
1756 static int
1757 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
1758 int mode)
1760 (void) mode;
1762 fh->u.ftp.append = 0;
1763 /* File will be written only, so no need to retrieve it from ftp server */
1764 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
1765 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1766 struct linger li;
1767 #else
1768 int li = 1;
1769 #endif
1770 char *name;
1772 /* ftpfs_linear_start() called, so data will be written
1773 * to local temporary file and stored to ftp server
1774 * by vfs_s_close later
1776 if (FH_SUPER->u.ftp.ctl_connection_busy) {
1777 if (!fh->ino->localname) {
1778 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1779 fh->ino->ent->name);
1780 if (handle == -1)
1781 return -1;
1782 close (handle);
1783 fh->u.ftp.append = flags & O_APPEND;
1785 return 0;
1787 name = vfs_s_fullpath (me, fh->ino);
1788 if (!name)
1789 return -1;
1790 fh->handle =
1791 ftpfs_open_data_connection (me, fh->ino->super,
1792 (flags & O_APPEND) ? "APPE" :
1793 "STOR", name, TYPE_BINARY, 0);
1794 g_free (name);
1796 if (fh->handle < 0)
1797 return -1;
1798 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1799 li.l_onoff = 1;
1800 li.l_linger = 120;
1801 #endif
1802 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1804 if (fh->ino->localname) {
1805 unlink (fh->ino->localname);
1806 g_free (fh->ino->localname);
1807 fh->ino->localname = NULL;
1809 return 0;
1812 if (!fh->ino->localname)
1813 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1814 return -1;
1815 if (!fh->ino->localname)
1816 vfs_die ("retrieve_file failed to fill in localname");
1817 return 0;
1820 static int ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1822 if (fh->handle != -1 && !fh->ino->localname){
1823 close (fh->handle);
1824 fh->handle = -1;
1825 /* File is stored to destination already, so
1826 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1828 fh->changed = 0;
1829 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1830 ERRNOR (EIO, -1);
1831 vfs_s_invalidate (me, FH_SUPER);
1833 return 0;
1836 static void
1837 ftpfs_done (struct vfs_class *me)
1839 struct no_proxy_entry *np;
1841 (void) me;
1843 while (no_proxy) {
1844 np = no_proxy->next;
1845 g_free (no_proxy->domain);
1846 g_free (no_proxy);
1847 no_proxy = np;
1849 g_free (ftpfs_anonymous_passwd);
1850 g_free (ftpfs_proxy_host);
1853 static void
1854 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
1856 struct vfs_s_super *super = MEDATA->supers;
1857 char *name;
1859 while (super){
1860 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
1861 (*func)(name);
1862 g_free (name);
1863 super = super->next;
1867 static char buffer[BUF_MEDIUM];
1868 static char *netrc;
1869 static const char *netrcp;
1871 /* This should match the keywords[] array below */
1872 typedef enum {
1873 NETRC_NONE = 0,
1874 NETRC_DEFAULT,
1875 NETRC_MACHINE,
1876 NETRC_LOGIN,
1877 NETRC_PASSWORD,
1878 NETRC_PASSWD,
1879 NETRC_ACCOUNT,
1880 NETRC_MACDEF,
1881 NETRC_UNKNOWN
1882 } keyword_t;
1884 static keyword_t ftpfs_netrc_next (void)
1886 char *p;
1887 keyword_t i;
1888 static const char *const keywords[] = { "default", "machine",
1889 "login", "password", "passwd", "account", "macdef", NULL
1893 while (1) {
1894 netrcp = skip_separators (netrcp);
1895 if (*netrcp != '\n')
1896 break;
1897 netrcp++;
1899 if (!*netrcp)
1900 return NETRC_NONE;
1901 p = buffer;
1902 if (*netrcp == '"') {
1903 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1904 if (*netrcp == '\\')
1905 netrcp++;
1906 *p++ = *netrcp;
1908 } else {
1909 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1910 *netrcp != ',' && *netrcp; netrcp++) {
1911 if (*netrcp == '\\')
1912 netrcp++;
1913 *p++ = *netrcp;
1916 *p = 0;
1917 if (!*buffer)
1918 return NETRC_NONE;
1920 i = NETRC_DEFAULT;
1921 while (keywords[i - 1]) {
1922 if (!strcmp (keywords[i - 1], buffer))
1923 return i;
1925 i++;
1928 return NETRC_UNKNOWN;
1931 static int ftpfs_netrc_bad_mode (const char *netrcname)
1933 static int be_angry = 1;
1934 struct stat mystat;
1936 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1937 if (be_angry) {
1938 message (D_ERROR, MSG_ERROR,
1939 _("~/.netrc file has incorrect mode.\n"
1940 "Remove password or correct mode."));
1941 be_angry = 0;
1943 return 1;
1945 return 0;
1948 /* Scan .netrc until we find matching "machine" or "default"
1949 * domain is used for additional matching
1950 * No search is done after "default" in compliance with "man netrc"
1951 * Return 0 if found, -1 otherwise */
1952 static int ftpfs_find_machine (const char *host, const char *domain)
1954 keyword_t keyword;
1956 if (!host) host = "";
1957 if (!domain) domain = "";
1959 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) {
1960 if (keyword == NETRC_DEFAULT)
1961 return 0;
1963 if (keyword == NETRC_MACDEF) {
1964 /* Scan for an empty line, which concludes "macdef" */
1965 do {
1966 while (*netrcp && *netrcp != '\n')
1967 netrcp++;
1968 if (*netrcp != '\n')
1969 break;
1970 netrcp++;
1971 } while (*netrcp && *netrcp != '\n');
1972 continue;
1975 if (keyword != NETRC_MACHINE)
1976 continue;
1978 /* Take machine name */
1979 if (ftpfs_netrc_next () == NETRC_NONE)
1980 break;
1982 if (g_strcasecmp (host, buffer)) {
1983 /* Try adding our domain to short names in .netrc */
1984 const char *host_domain = strchr (host, '.');
1985 if (!host_domain)
1986 continue;
1988 /* Compare domain part */
1989 if (g_strcasecmp (host_domain, domain))
1990 continue;
1992 /* Compare local part */
1993 if (g_strncasecmp (host, buffer, host_domain - host))
1994 continue;
1997 return 0;
2000 /* end of .netrc */
2001 return -1;
2004 /* Extract login and password from .netrc for the host.
2005 * pass may be NULL.
2006 * Returns 0 for success, -1 for error */
2007 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2009 char *netrcname;
2010 char *tmp_pass = NULL;
2011 char hostname[MAXHOSTNAMELEN];
2012 const char *domain;
2013 keyword_t keyword;
2014 static struct rupcache {
2015 struct rupcache *next;
2016 char *host;
2017 char *login;
2018 char *pass;
2019 } *rup_cache = NULL, *rupp;
2021 /* Initialize *login and *pass */
2022 if (!login)
2023 return 0;
2024 *login = NULL;
2025 if (pass)
2026 *pass = NULL;
2028 /* Look up in the cache first */
2029 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
2030 if (!strcmp (host, rupp->host)) {
2031 if (rupp->login)
2032 *login = g_strdup (rupp->login);
2033 if (pass && rupp->pass)
2034 *pass = g_strdup (rupp->pass);
2035 return 0;
2039 /* Load current .netrc */
2040 netrcname = concat_dir_and_file (home_dir, ".netrc");
2041 netrcp = netrc = load_file (netrcname);
2042 if (netrc == NULL) {
2043 g_free (netrcname);
2044 return 0;
2047 /* Find our own domain name */
2048 if (gethostname (hostname, sizeof (hostname)) < 0)
2049 *hostname = 0;
2050 if (!(domain = strchr (hostname, '.')))
2051 domain = "";
2053 /* Scan for "default" and matching "machine" keywords */
2054 ftpfs_find_machine (host, domain);
2056 /* Scan for keywords following "default" and "machine" */
2057 while (1) {
2058 int need_break = 0;
2059 keyword = ftpfs_netrc_next ();
2061 switch (keyword) {
2062 case NETRC_LOGIN:
2063 if (ftpfs_netrc_next () == NETRC_NONE) {
2064 need_break = 1;
2065 break;
2068 /* We have another name already - should not happen */
2069 if (*login) {
2070 need_break = 1;
2071 break;
2074 /* We have login name now */
2075 *login = g_strdup (buffer);
2076 break;
2078 case NETRC_PASSWORD:
2079 case NETRC_PASSWD:
2080 if (ftpfs_netrc_next () == NETRC_NONE) {
2081 need_break = 1;
2082 break;
2085 /* Ignore unsafe passwords */
2086 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2087 && ftpfs_netrc_bad_mode (netrcname)) {
2088 need_break = 1;
2089 break;
2092 /* Remember password. pass may be NULL, so use tmp_pass */
2093 if (tmp_pass == NULL)
2094 tmp_pass = g_strdup (buffer);
2095 break;
2097 case NETRC_ACCOUNT:
2098 /* "account" is followed by a token which we ignore */
2099 if (ftpfs_netrc_next () == NETRC_NONE) {
2100 need_break = 1;
2101 break;
2104 /* Ignore account, but warn user anyways */
2105 ftpfs_netrc_bad_mode (netrcname);
2106 break;
2108 default:
2109 /* Unexpected keyword or end of file */
2110 need_break = 1;
2111 break;
2114 if (need_break)
2115 break;
2118 g_free (netrc);
2119 g_free (netrcname);
2121 rupp = g_new (struct rupcache, 1);
2122 rupp->host = g_strdup (host);
2123 rupp->login = rupp->pass = 0;
2125 if (*login != NULL) {
2126 rupp->login = g_strdup (*login);
2128 if (tmp_pass != NULL)
2129 rupp->pass = g_strdup (tmp_pass);
2130 rupp->next = rup_cache;
2131 rup_cache = rupp;
2133 if (pass)
2134 *pass = tmp_pass;
2136 return 0;
2139 void
2140 init_ftpfs (void)
2142 static struct vfs_s_subclass ftpfs_subclass;
2144 tcp_init();
2146 ftpfs_subclass.flags = VFS_S_REMOTE;
2147 ftpfs_subclass.archive_same = ftpfs_archive_same;
2148 ftpfs_subclass.open_archive = ftpfs_open_archive;
2149 ftpfs_subclass.free_archive = ftpfs_free_archive;
2150 ftpfs_subclass.fh_open = ftpfs_fh_open;
2151 ftpfs_subclass.fh_close = ftpfs_fh_close;
2152 ftpfs_subclass.dir_load = ftpfs_dir_load;
2153 ftpfs_subclass.file_store = ftpfs_file_store;
2154 ftpfs_subclass.linear_start = ftpfs_linear_start;
2155 ftpfs_subclass.linear_read = ftpfs_linear_read;
2156 ftpfs_subclass.linear_close = ftpfs_linear_close;
2158 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2159 vfs_ftpfs_ops.name = "ftpfs";
2160 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2161 vfs_ftpfs_ops.prefix = "ftp:";
2162 vfs_ftpfs_ops.done = &ftpfs_done;
2163 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2164 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2165 vfs_ftpfs_ops.chown = ftpfs_chown;
2166 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2167 vfs_ftpfs_ops.rename = ftpfs_rename;
2168 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2169 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2170 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2171 vfs_register_class (&vfs_ftpfs_ops);