Ticket #1396: Fixed logic of processing '--with-search-engine' configure option.
[midnight-commander.git] / vfs / ftpfs.c
blobb533c066c278117ab5b6c47a8c0ded7e3c7c0d20
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 "tcputil.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_ALLOW_ANON);
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_ALLOW_ANON);
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 return -1;
685 /* Hosts to connect to that start with a ! should use proxy */
686 tmp_port = SUP.port;
688 if (SUP.proxy){
689 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
692 port = g_strdup_printf("%hu", (unsigned short) tmp_port);
693 if (port == NULL) {
694 g_free (host);
695 ftpfs_errno = errno;
696 return -1;
699 tty_enable_interrupt_key(); /* clear the interrupt flag */
701 memset (&hints, 0, sizeof (struct addrinfo));
702 hints.ai_socktype = SOCK_STREAM;
703 hints.ai_flags = AI_ADDRCONFIG;
706 e = getaddrinfo (host, port, &hints, &res);
707 g_free (port);
708 port = NULL;
710 if ( e != 0 ) {
711 tty_disable_interrupt_key ();
712 print_vfs_message (_("ftpfs: %s"), gai_strerror (e));
713 g_free (host);
714 ftpfs_errno = EINVAL;
715 return -1;
718 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next) {
720 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
722 if (my_socket < 0) {
724 if (curr_res->ai_next != NULL)
725 continue;
727 tty_disable_interrupt_key();
728 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno));
729 g_free (host);
730 freeaddrinfo (res);
731 ftpfs_errno = errno;
732 return -1;
735 print_vfs_message (_("ftpfs: making connection to %s"), host);
736 g_free (host);
737 host = NULL;
739 if ( connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0 )
740 break;
742 ftpfs_errno = errno;
743 close (my_socket);
745 if (errno == EINTR && tty_got_interrupt ()) {
746 print_vfs_message (_("ftpfs: connection interrupted by user"));
747 } else if (res->ai_next == NULL) {
748 print_vfs_message (_("ftpfs: connection to server failed: %s"),
749 unix_error_string (errno));
750 } else {
751 continue;
754 freeaddrinfo (res);
755 tty_disable_interrupt_key ();
756 return -1;
759 freeaddrinfo (res);
760 tty_disable_interrupt_key ();
761 return my_socket;
764 static int
765 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
767 int retry_seconds, count_down;
769 /* We do not want to use the passive if we are using proxies */
770 if (SUP.proxy)
771 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
773 retry_seconds = 0;
774 do {
775 SUP.failed_on_login = 0;
777 SUP.sock = ftpfs_open_socket (me, super);
778 if (SUP.sock == -1)
779 return -1;
781 if (ftpfs_login_server (me, super, NULL)) {
782 /* Logged in, no need to retry the connection */
783 break;
784 } else {
785 if (SUP.failed_on_login){
786 /* Close only the socket descriptor */
787 close (SUP.sock);
788 } else {
789 return -1;
791 if (ftpfs_retry_seconds){
792 retry_seconds = ftpfs_retry_seconds;
793 tty_enable_interrupt_key ();
794 for (count_down = retry_seconds; count_down; count_down--){
795 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
796 sleep (1);
797 if (tty_got_interrupt ()) {
798 /* ftpfs_errno = E; */
799 tty_disable_interrupt_key ();
800 return 0;
803 tty_disable_interrupt_key ();
806 } while (retry_seconds);
808 SUP.cwdir = ftpfs_get_current_directory (me, super);
809 if (!SUP.cwdir)
810 SUP.cwdir = g_strdup (PATH_SEP_STR);
811 return 0;
814 static int
815 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
816 const char *archive_name, char *op)
818 char *host, *user, *password;
819 int port;
821 (void) archive_name;
823 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
825 SUP.host = host;
826 SUP.user = user;
827 SUP.port = port;
828 SUP.cwdir = NULL;
829 SUP.proxy = 0;
830 if (ftpfs_check_proxy (host))
831 SUP.proxy = ftpfs_proxy_host;
832 SUP.password = password;
833 SUP.use_passive_connection = ftpfs_use_passive_connections;
834 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
835 SUP.isbinary = TYPE_UNKNOWN;
836 SUP.remote_is_amiga = 0;
837 super->name = g_strdup ("/");
838 super->root =
839 vfs_s_new_inode (me, super,
840 vfs_s_default_stat (me, S_IFDIR | 0755));
842 return ftpfs_open_archive_int (me, super);
845 static int
846 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
847 const char *archive_name, char *op, void *cookie)
849 char *host, *user;
850 int port;
852 (void) me;
853 (void) archive_name;
854 (void) cookie;
856 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
858 port = ((strcmp (host, SUP.host) == 0)
859 && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
861 g_free (host);
862 g_free (user);
864 return port;
867 /* The returned directory should always contain a trailing slash */
868 static char *
869 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
871 char buf[BUF_8K], *bufp, *bufq;
873 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
874 ftpfs_get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
875 bufp = NULL;
876 for (bufq = buf; *bufq; bufq++)
877 if (*bufq == '"') {
878 if (!bufp) {
879 bufp = bufq + 1;
880 } else {
881 *bufq = 0;
882 if (*bufp) {
883 if (*(bufq - 1) != '/') {
884 *bufq++ = '/';
885 *bufq = 0;
887 if (*bufp == '/')
888 return g_strdup (bufp);
889 else {
890 /* If the remote server is an Amiga a leading slash
891 might be missing. MC needs it because it is used
892 as separator between hostname and path internally. */
893 return g_strconcat( "/", bufp, (char *) NULL);
895 } else {
896 ftpfs_errno = EIO;
897 return NULL;
902 ftpfs_errno = EIO;
903 return NULL;
907 /* Setup Passive ftp connection, we use it for source routed connections */
908 static int
909 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
910 int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
912 char *c;
914 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") == COMPLETE) {
915 int port;
916 /* (|||<port>|) */
917 c = strchr (reply_str, '|');
918 if (c == NULL)
919 return 0;
920 if(strlen(c) > 3)
921 c+=3;
922 else
923 return 0;
925 port = atoi (c);
926 if (port < 0 || port > 65535)
927 return 0;
928 port = htons (port);
930 switch (sa->ss_family) {
931 case AF_INET:
932 ((struct sockaddr_in *)sa)->sin_port = port;
933 break;
934 case AF_INET6:
935 ((struct sockaddr_in6 *)sa)->sin6_port = port;
936 break;
937 default:
938 print_vfs_message (_("ftpfs: invalid address family"));
939 ERRNOR (EINVAL, -1);
941 } else if (sa->ss_family == AF_INET) {
942 int xa, xb, xc, xd, xe, xf;
943 char n [6];
945 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
946 return 0;
948 /* Parse remote parameters */
949 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
951 if (!*c)
952 return 0;
953 if (!isdigit ((unsigned char) *c))
954 return 0;
955 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
956 return 0;
958 n [0] = (unsigned char) xa;
959 n [1] = (unsigned char) xb;
960 n [2] = (unsigned char) xc;
961 n [3] = (unsigned char) xd;
962 n [4] = (unsigned char) xe;
963 n [5] = (unsigned char) xf;
965 memcpy (&(((struct sockaddr_in *)sa)->sin_addr.s_addr), (void *)n, 4);
966 memcpy (&(((struct sockaddr_in *)sa)->sin_port), (void *)&n[4], 2);
967 } else
968 return 0;
970 if (connect (my_socket, (struct sockaddr *) sa, *salen ) < 0)
971 return 0;
973 return 1;
976 static int
977 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
979 struct sockaddr_storage data_addr;
980 socklen_t data_addrlen;
981 int data_sock, result;
983 again:
984 memset (&data_addr, 0, sizeof (struct sockaddr_storage));
985 data_addrlen = sizeof (struct sockaddr_storage);
987 if (SUP.use_passive_connection)
988 result = getpeername (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen);
989 else
990 result = getsockname (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen);
992 if (result == -1 )
993 return -1;
995 switch (data_addr.ss_family) {
996 case AF_INET:
997 ((struct sockaddr_in *)&data_addr)->sin_port = 0;
998 break;
999 case AF_INET6:
1000 ((struct sockaddr_in6 *)&data_addr)->sin6_port = 0;
1001 break;
1002 default:
1003 print_vfs_message (_("ftpfs: invalid address family"));
1004 ERRNOR(EINVAL, -1);
1007 data_sock = socket (data_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
1008 if (data_sock < 0) {
1009 if (SUP.use_passive_connection) {
1010 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno));
1011 SUP.use_passive_connection = 0;
1012 goto again;
1015 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1016 return -1;
1019 if (SUP.use_passive_connection) {
1021 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1022 return data_sock;
1024 SUP.use_passive_connection = 0;
1025 print_vfs_message (_("ftpfs: could not setup passive mode"));
1027 close (data_sock);
1028 goto again;
1031 /* If passive setup fails, fallback to active connections */
1032 /* Active FTP connection */
1033 if ((bind (data_sock, (struct sockaddr *)&data_addr, data_addrlen) == 0) &&
1034 (getsockname (data_sock, (struct sockaddr *)&data_addr, &data_addrlen) == 0) &&
1035 (listen (data_sock, 1) == 0)) {
1036 unsigned short int port;
1037 char *addr;
1038 unsigned int af;
1040 switch (data_addr.ss_family) {
1041 case AF_INET:
1042 af = FTP_INET;
1043 port = ((struct sockaddr_in *)&data_addr)->sin_port;
1044 break;
1045 case AF_INET6:
1046 af = FTP_INET6;
1047 port = ((struct sockaddr_in6 *)&data_addr)->sin6_port;
1048 break;
1049 default:
1050 print_vfs_message (_("ftpfs: invalid address family"));
1051 ERRNOR (EINVAL, -1);
1054 port = ntohs (port);
1056 addr = g_try_malloc (NI_MAXHOST);
1057 if (addr == NULL)
1058 ERRNOR (ENOMEM, -1);
1060 if (getnameinfo ((struct sockaddr *)&data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) {
1061 g_free (addr);
1062 ERRNOR (EIO, -1);
1065 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE) {
1066 g_free (addr);
1067 return data_sock;
1069 g_free (addr);
1071 if (FTP_INET == af) {
1072 unsigned char *a = (unsigned char *)&((struct sockaddr_in *)&data_addr)->sin_addr;
1073 unsigned char *p = (unsigned char *)&port;
1075 if (ftpfs_command (me, super, WAIT_REPLY,
1076 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1077 p[0], p[1]) == COMPLETE)
1078 return data_sock;
1081 close (data_sock);
1082 ftpfs_errno = EIO;
1083 return -1;
1086 static int
1087 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1088 const char *remote, int isbinary, int reget)
1090 struct sockaddr_storage from;
1091 int s, j, data;
1092 socklen_t fromlen = sizeof(from);
1094 if ((s = ftpfs_initconn (me, super)) == -1)
1095 return -1;
1096 if (ftpfs_changetype (me, super, isbinary) == -1)
1097 return -1;
1098 if (reget > 0){
1099 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1100 if (j != CONTINUE)
1101 return -1;
1103 if (remote) {
1104 char *remote_path = ftpfs_translate_path (me, super, remote);
1105 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1106 /* WarFtpD can't STORE //filename */
1107 (*remote_path == '/') ? remote_path + 1 : remote_path);
1108 g_free (remote_path);
1109 } else
1110 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1111 if (j != PRELIM)
1112 ERRNOR (EPERM, -1);
1113 tty_enable_interrupt_key ();
1114 if (SUP.use_passive_connection)
1115 data = s;
1116 else {
1117 data = accept (s, (struct sockaddr *)&from, &fromlen);
1118 if (data < 0) {
1119 ftpfs_errno = errno;
1120 close (s);
1121 return -1;
1123 close (s);
1125 tty_disable_interrupt_key ();
1126 return data;
1129 #define ABORT_TIMEOUT 5
1130 static void
1131 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
1133 struct vfs_s_super *super = FH_SUPER;
1134 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1135 fd_set mask;
1136 char buf[1024];
1137 int dsock = FH_SOCK;
1138 FH_SOCK = -1;
1139 SUP.ctl_connection_busy = 0;
1141 print_vfs_message (_("ftpfs: aborting transfer."));
1142 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
1143 print_vfs_message (_("ftpfs: abort error: %s"),
1144 unix_error_string (errno));
1145 if (dsock != -1)
1146 close (dsock);
1147 return;
1150 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
1151 print_vfs_message (_("ftpfs: abort failed"));
1152 if (dsock != -1)
1153 close (dsock);
1154 return;
1156 if (dsock != -1) {
1157 FD_ZERO (&mask);
1158 FD_SET (dsock, &mask);
1159 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1160 struct timeval start_tim, tim;
1161 gettimeofday (&start_tim, NULL);
1162 /* flush the remaining data */
1163 while (read (dsock, buf, sizeof (buf)) > 0) {
1164 gettimeofday (&tim, NULL);
1165 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1166 /* server keeps sending, drop the connection and ftpfs_reconnect */
1167 close (dsock);
1168 ftpfs_reconnect (me, super);
1169 return;
1173 close (dsock);
1175 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1176 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1179 #if 0
1180 static void
1181 resolve_symlink_without_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1183 struct linklist *flist;
1184 struct direntry *fe, *fel;
1185 char tmp[MC_MAXPATHLEN];
1186 int depth;
1188 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1189 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1190 /* flist->data->l_stat is alread initialized with 0 */
1191 fel = flist->data;
1192 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1193 if (fel->linkname[0] == '/') {
1194 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1195 continue;
1196 strcpy (tmp, fel->linkname);
1197 } else {
1198 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1199 continue;
1200 strcpy (tmp, dir->remote_path);
1201 if (tmp[1] != '\0')
1202 strcat (tmp, "/");
1203 strcat (tmp + 1, fel->linkname);
1205 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1206 canonicalize_pathname (tmp);
1207 fe = _get_file_entry(bucket, tmp, 0, 0);
1208 if (fe) {
1209 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1210 /* Symlink points to link which isn't resolved, yet. */
1211 if (fe->linkname[0] == '/') {
1212 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1213 break;
1214 strcpy (tmp, fe->linkname);
1215 } else {
1216 /* at this point tmp looks always like this
1217 /directory/filename, i.e. no need to check
1218 strrchr's return value */
1219 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1220 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1221 break;
1222 strcat (tmp, fe->linkname);
1224 continue;
1225 } else {
1226 fel->l_stat = g_new (struct stat, 1);
1227 if ( S_ISLNK (fe->s.st_mode))
1228 *fel->l_stat = *fe->l_stat;
1229 else
1230 *fel->l_stat = fe->s;
1231 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1234 break;
1238 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1241 static void
1242 resolve_symlink_with_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1244 char buffer[2048] = "", *filename;
1245 int sock;
1246 FILE *fp;
1247 struct stat s;
1248 struct linklist *flist;
1249 struct direntry *fe;
1250 int switch_method = 0;
1252 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1253 if (strchr (dir->remote_path, ' ')) {
1254 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1255 print_vfs_message(_("ftpfs: CWD failed."));
1256 return;
1258 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1260 else
1261 sock = ftpfs_open_data_connection (bucket, "LIST -lLa",
1262 dir->remote_path, TYPE_ASCII, 0);
1264 if (sock == -1) {
1265 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1266 return;
1269 fp = fdopen(sock, "r");
1270 if (fp == NULL) {
1271 close(sock);
1272 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1273 return;
1275 tty_enable_interrupt_key ();
1276 flist = dir->file_list->next;
1277 while (1) {
1278 do {
1279 if (flist == dir->file_list)
1280 goto done;
1281 fe = flist->data;
1282 flist = flist->next;
1283 } while (!S_ISLNK(fe->s.st_mode));
1284 while (1) {
1285 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1286 goto done;
1287 if (MEDATA->logfile){
1288 fputs (buffer, MEDATA->logfile);
1289 fflush (MEDATA->logfile);
1291 vfs_die("This code should be commented out\n");
1292 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1293 int r = strcmp(fe->name, filename);
1294 g_free(filename);
1295 if (r == 0) {
1296 if (S_ISLNK (s.st_mode)) {
1297 /* This server doesn't understand LIST -lLa */
1298 switch_method = 1;
1299 goto done;
1301 fe->l_stat = g_new (struct stat, 1);
1302 if (fe->l_stat == NULL)
1303 goto done;
1304 *fe->l_stat = s;
1305 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1306 break;
1308 if (r < 0)
1309 break;
1313 done:
1314 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1315 tty_disable_interrupt_key ();
1316 fclose(fp);
1317 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1320 static void
1321 resolve_symlink(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1323 print_vfs_message(_("Resolving symlink..."));
1325 if (SUP.strict_rfc959_list_cmd)
1326 resolve_symlink_without_ls_options(me, super, dir);
1327 else
1328 resolve_symlink_with_ls_options(me, super, dir);
1330 #endif
1332 static int
1333 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1335 struct vfs_s_entry *ent;
1336 struct vfs_s_super *super = dir->super;
1337 int sock, num_entries = 0;
1338 char buffer[BUF_8K];
1339 int cd_first;
1341 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1342 || (strchr (remote_path, ' ') != NULL);
1344 again:
1345 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1346 remote_path,
1347 SUP.strict ==
1348 RFC_STRICT ? _("(strict rfc959)") : "",
1349 cd_first ? _("(chdir first)") : "");
1351 if (cd_first) {
1352 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE) {
1353 ftpfs_errno = ENOENT;
1354 print_vfs_message (_("ftpfs: CWD failed."));
1355 return -1;
1359 gettimeofday (&dir->timestamp, NULL);
1360 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1362 if (SUP.strict == RFC_STRICT)
1363 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1364 else if (cd_first)
1365 /* Dirty hack to avoid autoprepending / to . */
1366 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1367 sock =
1368 ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1369 else {
1370 /* Trailing "/." is necessary if remote_path is a symlink */
1371 char *path = concat_dir_and_file (remote_path, ".");
1372 sock =
1373 ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII,
1375 g_free (path);
1378 if (sock == -1)
1379 goto fallback;
1381 /* Clear the interrupt flag */
1382 tty_enable_interrupt_key ();
1384 while (1) {
1385 int i;
1386 int res =
1387 vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1388 sock);
1389 if (!res)
1390 break;
1392 if (res == EINTR) {
1393 me->verrno = ECONNRESET;
1394 close (sock);
1395 tty_disable_interrupt_key ();
1396 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1397 print_vfs_message (_("%s: failure"), me->name);
1398 return -1;
1401 if (MEDATA->logfile) {
1402 fputs (buffer, MEDATA->logfile);
1403 fputs ("\n", MEDATA->logfile);
1404 fflush (MEDATA->logfile);
1407 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1408 i = ent->ino->st.st_nlink;
1409 if (!vfs_parse_ls_lga
1410 (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1411 vfs_s_free_entry (me, ent);
1412 continue;
1414 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1415 num_entries++;
1416 vfs_s_insert_entry (me, dir, ent);
1419 close (sock);
1420 me->verrno = E_REMOTE;
1421 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1422 goto fallback;
1424 if (num_entries == 0 && cd_first == 0) {
1425 /* The LIST command may produce an empty output. In such scenario
1426 it is not clear whether this is caused by `remote_path' being
1427 a non-existent path or for some other reason (listing emtpy
1428 directory without the -a option, non-readable directory, etc.).
1430 Since `dir_load' is a crucial method, when it comes to determine
1431 whether a given path is a _directory_, the code must try its best
1432 to determine the type of `remote_path'. The only reliable way to
1433 achieve this is trough issuing a CWD command. */
1435 cd_first = 1;
1436 goto again;
1439 if (SUP.strict == RFC_AUTODETECT)
1440 SUP.strict = RFC_DARING;
1442 print_vfs_message (_("%s: done."), me->name);
1443 return 0;
1445 fallback:
1446 if (SUP.strict == RFC_AUTODETECT) {
1447 /* It's our first attempt to get a directory listing from this
1448 server (UNIX style LIST command) */
1449 SUP.strict = RFC_STRICT;
1450 /* I hate goto, but recursive call needs another 8K on stack */
1451 /* return ftpfs_dir_load (me, dir, remote_path); */
1452 cd_first = 1;
1453 goto again;
1455 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1456 ERRNOR (EACCES, -1);
1459 static int
1460 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name,
1461 char *localname)
1463 int h, sock, n_read, n_written;
1464 off_t n_stored;
1465 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1466 struct linger li;
1467 #else
1468 int flag_one = 1;
1469 #endif
1470 char buffer[8192];
1471 struct stat s;
1472 char *w_buf;
1473 struct vfs_s_super *super = FH_SUPER;
1475 h = open (localname, O_RDONLY);
1476 if (h == -1)
1477 ERRNOR (EIO, -1);
1478 sock =
1479 ftpfs_open_data_connection (me, super,
1480 fh->u.ftp.append ? "APPE" : "STOR", name,
1481 TYPE_BINARY, 0);
1482 if (sock < 0 || fstat (h, &s) == -1) {
1483 close (h);
1484 return -1;
1486 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1487 li.l_onoff = 1;
1488 li.l_linger = 120;
1489 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1490 #else
1491 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1492 #endif
1493 n_stored = 0;
1495 tty_enable_interrupt_key ();
1496 while (1) {
1497 while ((n_read = read (h, buffer, sizeof (buffer))) == -1) {
1498 if (errno == EINTR) {
1499 if (tty_got_interrupt ()) {
1500 ftpfs_errno = EINTR;
1501 goto error_return;
1502 } else
1503 continue;
1505 ftpfs_errno = errno;
1506 goto error_return;
1508 if (n_read == 0)
1509 break;
1510 n_stored += n_read;
1511 w_buf = buffer;
1512 while ((n_written = write (sock, w_buf, n_read)) != n_read) {
1513 if (n_written == -1) {
1514 if (errno == EINTR && !tty_got_interrupt ()) {
1515 continue;
1517 ftpfs_errno = errno;
1518 goto error_return;
1520 w_buf += n_written;
1521 n_read -= n_written;
1523 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1524 (unsigned long) n_stored, (unsigned long) s.st_size);
1526 tty_disable_interrupt_key ();
1527 close (sock);
1528 close (h);
1529 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1530 ERRNOR (EIO, -1);
1531 return 0;
1532 error_return:
1533 tty_disable_interrupt_key ();
1534 close (sock);
1535 close (h);
1536 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1537 return -1;
1540 static int
1541 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1543 char *name = vfs_s_fullpath (me, fh->ino);
1545 if (!name)
1546 return 0;
1547 FH_SOCK = ftpfs_open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1548 g_free (name);
1549 if (FH_SOCK == -1)
1550 ERRNOR (EACCES, 0);
1551 fh->linear = LS_LINEAR_OPEN;
1552 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1553 fh->u.ftp.append = 0;
1554 return 1;
1557 static int
1558 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1560 int n;
1561 struct vfs_s_super *super = FH_SUPER;
1563 while ((n = read (FH_SOCK, buf, len))<0) {
1564 if ((errno == EINTR) && !tty_got_interrupt ())
1565 continue;
1566 break;
1569 if (n<0)
1570 ftpfs_linear_abort(me, fh);
1572 if (!n) {
1573 SUP.ctl_connection_busy = 0;
1574 close (FH_SOCK);
1575 FH_SOCK = -1;
1576 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1577 ERRNOR (E_REMOTE, -1);
1578 return 0;
1580 ERRNOR (errno, n);
1583 static void
1584 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1586 if (FH_SOCK != -1)
1587 ftpfs_linear_abort(me, fh);
1590 static int ftpfs_ctl (void *fh, int ctlop, void *arg)
1592 (void) arg;
1594 switch (ctlop) {
1595 case VFS_CTL_IS_NOTREADY:
1597 int v;
1599 if (!FH->linear)
1600 vfs_die ("You may not do this");
1601 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1602 return 0;
1604 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1605 if (((v < 0) && (errno == EINTR)) || v == 0)
1606 return 1;
1607 return 0;
1609 default:
1610 return 0;
1614 static int
1615 ftpfs_send_command(struct vfs_class *me, const char *filename, const char *cmd, int flags)
1617 const char *rpath;
1618 char *p, *mpath = g_strdup(filename);
1619 struct vfs_s_super *super;
1620 int r;
1621 int flush_directory_cache = (flags & OPT_FLUSH);
1623 if (!(rpath = vfs_s_get_path_mangle(me, mpath, &super, 0))) {
1624 g_free(mpath);
1625 return -1;
1627 p = ftpfs_translate_path (me, super, rpath);
1628 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1629 g_free (p);
1630 vfs_stamp_create (&vfs_ftpfs_ops, super);
1631 if (flags & OPT_IGNORE_ERROR)
1632 r = COMPLETE;
1633 if (r != COMPLETE) {
1634 me->verrno = EPERM;
1635 g_free (mpath);
1636 return -1;
1638 if (flush_directory_cache)
1639 vfs_s_invalidate(me, super);
1640 g_free(mpath);
1641 return 0;
1644 /* This routine is called as the last step in load_setup */
1645 void
1646 ftpfs_init_passwd(void)
1648 ftpfs_anonymous_passwd = load_anon_passwd ();
1649 if (ftpfs_anonymous_passwd)
1650 return;
1652 /* If there is no anonymous ftp password specified
1653 * then we'll just use anonymous@
1654 * We don't send any other thing because:
1655 * - We want to remain anonymous
1656 * - We want to stop SPAM
1657 * - We don't want to let ftp sites to discriminate by the user,
1658 * host or country.
1660 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1663 static int ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1665 char buf[BUF_SMALL];
1666 int ret;
1668 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1670 ret = ftpfs_send_command(me, path, buf, OPT_FLUSH);
1672 if ( mc_config_get_bool (mc_main_config, CONFIG_APP_SECTION,
1673 "ignore_ftp_chattr_errors", TRUE)) {
1674 return 0;
1677 return ret;
1680 static int ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1682 #if 0
1683 ftpfs_errno = EPERM;
1684 return -1;
1685 #else
1686 /* Everyone knows it is not possible to chown remotely, so why bother them.
1687 If someone's root, then copy/move will always try to chown it... */
1688 (void) me;
1689 (void) path;
1690 (void) owner;
1691 (void) group;
1692 return 0;
1693 #endif
1696 static int ftpfs_unlink (struct vfs_class *me, const char *path)
1698 return ftpfs_send_command(me, path, "DELE /%s", OPT_FLUSH);
1701 /* Return 1 if path is the same directory as the one we are in now */
1702 static int
1703 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1705 (void) me;
1707 if (!SUP.cwdir)
1708 return 0;
1709 if (strcmp (path, SUP.cwdir) == 0)
1710 return 1;
1711 return 0;
1714 static int
1715 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1717 int r;
1718 char *p;
1720 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1721 return COMPLETE;
1723 p = ftpfs_translate_path (me, super, remote_path);
1724 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1725 g_free (p);
1727 if (r != COMPLETE) {
1728 ftpfs_errno = EIO;
1729 } else {
1730 g_free(SUP.cwdir);
1731 SUP.cwdir = g_strdup (remote_path);
1732 SUP.cwd_deferred = 0;
1734 return r;
1737 static int ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1739 ftpfs_send_command(me, path1, "RNFR /%s", OPT_FLUSH);
1740 return ftpfs_send_command(me, path2, "RNTO /%s", OPT_FLUSH);
1743 static int ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1745 (void) mode; /* FIXME: should be used */
1747 return ftpfs_send_command(me, path, "MKD /%s", OPT_FLUSH);
1750 static int ftpfs_rmdir (struct vfs_class *me, const char *path)
1752 return ftpfs_send_command(me, path, "RMD /%s", OPT_FLUSH);
1755 static int
1756 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
1757 int mode)
1759 (void) mode;
1761 fh->u.ftp.append = 0;
1762 /* File will be written only, so no need to retrieve it from ftp server */
1763 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
1764 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1765 struct linger li;
1766 #else
1767 int li = 1;
1768 #endif
1769 char *name;
1771 /* ftpfs_linear_start() called, so data will be written
1772 * to local temporary file and stored to ftp server
1773 * by vfs_s_close later
1775 if (FH_SUPER->u.ftp.ctl_connection_busy) {
1776 if (!fh->ino->localname) {
1777 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1778 fh->ino->ent->name);
1779 if (handle == -1)
1780 return -1;
1781 close (handle);
1782 fh->u.ftp.append = flags & O_APPEND;
1784 return 0;
1786 name = vfs_s_fullpath (me, fh->ino);
1787 if (!name)
1788 return -1;
1789 fh->handle =
1790 ftpfs_open_data_connection (me, fh->ino->super,
1791 (flags & O_APPEND) ? "APPE" :
1792 "STOR", name, TYPE_BINARY, 0);
1793 g_free (name);
1795 if (fh->handle < 0)
1796 return -1;
1797 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1798 li.l_onoff = 1;
1799 li.l_linger = 120;
1800 #endif
1801 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1803 if (fh->ino->localname) {
1804 unlink (fh->ino->localname);
1805 g_free (fh->ino->localname);
1806 fh->ino->localname = NULL;
1808 return 0;
1811 if (!fh->ino->localname)
1812 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1813 return -1;
1814 if (!fh->ino->localname)
1815 vfs_die ("retrieve_file failed to fill in localname");
1816 return 0;
1819 static int ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1821 if (fh->handle != -1 && !fh->ino->localname){
1822 close (fh->handle);
1823 fh->handle = -1;
1824 /* File is stored to destination already, so
1825 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1827 fh->changed = 0;
1828 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1829 ERRNOR (EIO, -1);
1830 vfs_s_invalidate (me, FH_SUPER);
1832 return 0;
1835 static void
1836 ftpfs_done (struct vfs_class *me)
1838 struct no_proxy_entry *np;
1840 (void) me;
1842 while (no_proxy) {
1843 np = no_proxy->next;
1844 g_free (no_proxy->domain);
1845 g_free (no_proxy);
1846 no_proxy = np;
1848 g_free (ftpfs_anonymous_passwd);
1849 g_free (ftpfs_proxy_host);
1852 static void
1853 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
1855 struct vfs_s_super *super = MEDATA->supers;
1856 char *name;
1858 while (super){
1859 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
1860 (*func)(name);
1861 g_free (name);
1862 super = super->next;
1866 static char buffer[BUF_MEDIUM];
1867 static char *netrc;
1868 static const char *netrcp;
1870 /* This should match the keywords[] array below */
1871 typedef enum {
1872 NETRC_NONE = 0,
1873 NETRC_DEFAULT,
1874 NETRC_MACHINE,
1875 NETRC_LOGIN,
1876 NETRC_PASSWORD,
1877 NETRC_PASSWD,
1878 NETRC_ACCOUNT,
1879 NETRC_MACDEF,
1880 NETRC_UNKNOWN
1881 } keyword_t;
1883 static keyword_t ftpfs_netrc_next (void)
1885 char *p;
1886 keyword_t i;
1887 static const char *const keywords[] = { "default", "machine",
1888 "login", "password", "passwd", "account", "macdef", NULL
1892 while (1) {
1893 netrcp = skip_separators (netrcp);
1894 if (*netrcp != '\n')
1895 break;
1896 netrcp++;
1898 if (!*netrcp)
1899 return NETRC_NONE;
1900 p = buffer;
1901 if (*netrcp == '"') {
1902 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1903 if (*netrcp == '\\')
1904 netrcp++;
1905 *p++ = *netrcp;
1907 } else {
1908 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1909 *netrcp != ',' && *netrcp; netrcp++) {
1910 if (*netrcp == '\\')
1911 netrcp++;
1912 *p++ = *netrcp;
1915 *p = 0;
1916 if (!*buffer)
1917 return NETRC_NONE;
1919 i = NETRC_DEFAULT;
1920 while (keywords[i - 1]) {
1921 if (!strcmp (keywords[i - 1], buffer))
1922 return i;
1924 i++;
1927 return NETRC_UNKNOWN;
1930 static int ftpfs_netrc_bad_mode (const char *netrcname)
1932 static int be_angry = 1;
1933 struct stat mystat;
1935 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1936 if (be_angry) {
1937 message (D_ERROR, MSG_ERROR,
1938 _("~/.netrc file has incorrect mode.\n"
1939 "Remove password or correct mode."));
1940 be_angry = 0;
1942 return 1;
1944 return 0;
1947 /* Scan .netrc until we find matching "machine" or "default"
1948 * domain is used for additional matching
1949 * No search is done after "default" in compliance with "man netrc"
1950 * Return 0 if found, -1 otherwise */
1951 static int ftpfs_find_machine (const char *host, const char *domain)
1953 keyword_t keyword;
1955 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) {
1956 if (keyword == NETRC_DEFAULT)
1957 return 0;
1959 if (keyword == NETRC_MACDEF) {
1960 /* Scan for an empty line, which concludes "macdef" */
1961 do {
1962 while (*netrcp && *netrcp != '\n')
1963 netrcp++;
1964 if (*netrcp != '\n')
1965 break;
1966 netrcp++;
1967 } while (*netrcp && *netrcp != '\n');
1968 continue;
1971 if (keyword != NETRC_MACHINE)
1972 continue;
1974 /* Take machine name */
1975 if (ftpfs_netrc_next () == NETRC_NONE)
1976 break;
1978 if (g_strcasecmp (host, buffer)) {
1979 /* Try adding our domain to short names in .netrc */
1980 const char *host_domain = strchr (host, '.');
1981 if (!host_domain)
1982 continue;
1984 /* Compare domain part */
1985 if (g_strcasecmp (host_domain, domain))
1986 continue;
1988 /* Compare local part */
1989 if (g_strncasecmp (host, buffer, host_domain - host))
1990 continue;
1993 return 0;
1996 /* end of .netrc */
1997 return -1;
2000 /* Extract login and password from .netrc for the host.
2001 * pass may be NULL.
2002 * Returns 0 for success, -1 for error */
2003 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2005 char *netrcname;
2006 char *tmp_pass = NULL;
2007 char hostname[MAXHOSTNAMELEN];
2008 const char *domain;
2009 keyword_t keyword;
2010 static struct rupcache {
2011 struct rupcache *next;
2012 char *host;
2013 char *login;
2014 char *pass;
2015 } *rup_cache = NULL, *rupp;
2017 /* Initialize *login and *pass */
2018 if (!login)
2019 return 0;
2020 *login = NULL;
2021 if (pass)
2022 *pass = NULL;
2024 /* Look up in the cache first */
2025 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
2026 if (!strcmp (host, rupp->host)) {
2027 if (rupp->login)
2028 *login = g_strdup (rupp->login);
2029 if (pass && rupp->pass)
2030 *pass = g_strdup (rupp->pass);
2031 return 0;
2035 /* Load current .netrc */
2036 netrcname = concat_dir_and_file (home_dir, ".netrc");
2037 netrcp = netrc = load_file (netrcname);
2038 if (netrc == NULL) {
2039 g_free (netrcname);
2040 return 0;
2043 /* Find our own domain name */
2044 if (gethostname (hostname, sizeof (hostname)) < 0)
2045 *hostname = 0;
2046 if (!(domain = strchr (hostname, '.')))
2047 domain = "";
2049 /* Scan for "default" and matching "machine" keywords */
2050 ftpfs_find_machine (host, domain);
2052 /* Scan for keywords following "default" and "machine" */
2053 while (1) {
2054 int need_break = 0;
2055 keyword = ftpfs_netrc_next ();
2057 switch (keyword) {
2058 case NETRC_LOGIN:
2059 if (ftpfs_netrc_next () == NETRC_NONE) {
2060 need_break = 1;
2061 break;
2064 /* We have another name already - should not happen */
2065 if (*login) {
2066 need_break = 1;
2067 break;
2070 /* We have login name now */
2071 *login = g_strdup (buffer);
2072 break;
2074 case NETRC_PASSWORD:
2075 case NETRC_PASSWD:
2076 if (ftpfs_netrc_next () == NETRC_NONE) {
2077 need_break = 1;
2078 break;
2081 /* Ignore unsafe passwords */
2082 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2083 && ftpfs_netrc_bad_mode (netrcname)) {
2084 need_break = 1;
2085 break;
2088 /* Remember password. pass may be NULL, so use tmp_pass */
2089 if (tmp_pass == NULL)
2090 tmp_pass = g_strdup (buffer);
2091 break;
2093 case NETRC_ACCOUNT:
2094 /* "account" is followed by a token which we ignore */
2095 if (ftpfs_netrc_next () == NETRC_NONE) {
2096 need_break = 1;
2097 break;
2100 /* Ignore account, but warn user anyways */
2101 ftpfs_netrc_bad_mode (netrcname);
2102 break;
2104 default:
2105 /* Unexpected keyword or end of file */
2106 need_break = 1;
2107 break;
2110 if (need_break)
2111 break;
2114 g_free (netrc);
2115 g_free (netrcname);
2117 rupp = g_new (struct rupcache, 1);
2118 rupp->host = g_strdup (host);
2119 rupp->login = rupp->pass = 0;
2121 if (*login != NULL) {
2122 rupp->login = g_strdup (*login);
2124 if (tmp_pass != NULL)
2125 rupp->pass = g_strdup (tmp_pass);
2126 rupp->next = rup_cache;
2127 rup_cache = rupp;
2129 if (pass)
2130 *pass = tmp_pass;
2132 return 0;
2135 void
2136 init_ftpfs (void)
2138 static struct vfs_s_subclass ftpfs_subclass;
2140 ftpfs_subclass.flags = VFS_S_REMOTE;
2141 ftpfs_subclass.archive_same = ftpfs_archive_same;
2142 ftpfs_subclass.open_archive = ftpfs_open_archive;
2143 ftpfs_subclass.free_archive = ftpfs_free_archive;
2144 ftpfs_subclass.fh_open = ftpfs_fh_open;
2145 ftpfs_subclass.fh_close = ftpfs_fh_close;
2146 ftpfs_subclass.dir_load = ftpfs_dir_load;
2147 ftpfs_subclass.file_store = ftpfs_file_store;
2148 ftpfs_subclass.linear_start = ftpfs_linear_start;
2149 ftpfs_subclass.linear_read = ftpfs_linear_read;
2150 ftpfs_subclass.linear_close = ftpfs_linear_close;
2152 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2153 vfs_ftpfs_ops.name = "ftpfs";
2154 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2155 vfs_ftpfs_ops.prefix = "ftp:";
2156 vfs_ftpfs_ops.done = &ftpfs_done;
2157 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2158 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2159 vfs_ftpfs_ops.chown = ftpfs_chown;
2160 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2161 vfs_ftpfs_ops.rename = ftpfs_rename;
2162 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2163 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2164 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2165 vfs_register_class (&vfs_ftpfs_ops);