Ticket #2018: strcpy() is used for overlaping strings.
[kaloumi3.git] / lib / vfs / mc-vfs / ftpfs.c
blob93bfab6242f46b6c4851f4335d781808078d92b2
1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1995 Ching Hui
6 1995 Jakub Jelinek
7 1995, 1996, 1997 Miguel de Icaza
8 1997 Norbert Warmuth
9 1998 Pavel Machek
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Library General Public License
13 as published by the Free Software Foundation; either version 2 of
14 the License, or (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU Library General Public License for more details.
21 You should have received a copy of the GNU Library General Public
22 License along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
25 /**
26 * \file
27 * \brief Source: Virtual File System: FTP file system
28 * \author Ching Hui
29 * \author Jakub Jelinek
30 * \author Miguel de Icaza
31 * \author Norbert Warmuth
32 * \author Pavel Machek
33 * \date 1995, 1997, 1998
35 * \todo
36 - make it more robust - all the connects etc. should handle EADDRINUSE and
37 ERETRY (have I spelled these names correctly?)
38 - make the user able to flush a connection - all the caches will get empty
39 etc., (tarfs as well), we should give there a user selectable timeout
40 and assign a key sequence.
41 - use hash table instead of linklist to cache ftpfs directory.
43 What to do with this?
46 * NOTE: Usage of tildes is deprecated, consider:
47 * \verbatim
48 cd /#ftp:pavel@hobit
49 cd ~
50 \endverbatim
51 * And now: what do I want to do? Do I want to go to /home/pavel or to
52 * /#ftp:hobit/home/pavel? I think first has better sense...
54 \verbatim
56 int f = !strcmp( remote_path, "/~" );
57 if (f || !strncmp( remote_path, "/~/", 3 )) {
58 char *s;
59 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
60 g_free (remote_path);
61 remote_path = s;
64 \endverbatim
67 /* \todo Fix: Namespace pollution: horrible */
69 #include <config.h>
70 #include <stdlib.h> /* atoi() */
71 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
72 #include <netdb.h> /* struct hostent */
73 #include <sys/socket.h> /* AF_INET */
74 #include <netinet/in.h> /* struct in_addr */
75 #ifdef HAVE_ARPA_INET_H
76 #include <arpa/inet.h>
77 #endif
78 #include <arpa/ftp.h>
79 #include <arpa/telnet.h>
80 #include <sys/param.h>
81 #include <errno.h>
82 #include <ctype.h>
83 #include <fcntl.h>
84 #include <sys/time.h> /* gettimeofday() */
86 #include "lib/global.h"
88 #include "lib/tty/tty.h" /* enable/disable interrupt key */
90 #include "src/wtools.h" /* message() */
91 #include "src/main.h" /* print_vfs_message */
92 #include "src/history.h"
93 #include "src/setup.h" /* for load_anon_passwd */
94 #include "lib/mcconfig.h"
96 #include "utilvfs.h"
97 #include "xdirentry.h"
98 #include "vfs.h"
99 #include "vfs-impl.h"
100 #include "gc.h" /* vfs_stamp_create */
101 #include "netutil.h"
102 #include "ftpfs.h"
103 #ifndef MAXHOSTNAMELEN
104 # define MAXHOSTNAMELEN 64
105 #endif
107 #define UPLOAD_ZERO_LENGTH_FILE
108 #define SUP super->u.ftp
109 #define FH_SOCK fh->u.ftp.sock
111 #ifndef INADDR_NONE
112 #define INADDR_NONE 0xffffffff
113 #endif
115 /* for uclibc < 0.9.29 */
116 #ifndef AI_ADDRCONFIG
117 #define AI_ADDRCONFIG 0x0020
118 #endif
120 #define RFC_AUTODETECT 0
121 #define RFC_DARING 1
122 #define RFC_STRICT 2
124 #ifndef HAVE_SOCKLEN_T
125 typedef int socklen_t;
126 #endif
128 static int ftpfs_errno;
129 static int code;
131 /* Delay to retry a connection */
132 int ftpfs_retry_seconds = 30;
134 /* Method to use to connect to ftp sites */
135 int ftpfs_use_passive_connections = 1;
136 int ftpfs_use_passive_connections_over_proxy = 0;
138 /* Method used to get directory listings:
139 * 1: try 'LIST -la <path>', if it fails
140 * fall back to CWD <path>; LIST
141 * 0: always use CWD <path>; LIST
143 int ftpfs_use_unix_list_options = 1;
145 /* First "CWD <path>", then "LIST -la ." */
146 int ftpfs_first_cd_then_ls = 1;
148 /* Use the ~/.netrc */
149 int use_netrc = 1;
151 /* Anonymous setup */
152 char *ftpfs_anonymous_passwd = NULL;
153 int ftpfs_directory_timeout = 900;
155 /* Proxy host */
156 char *ftpfs_proxy_host = NULL;
158 /* wether we have to use proxy by default? */
159 int ftpfs_always_use_proxy;
161 #ifdef FIXME_LATER_ALIGATOR
162 static struct linklist *connections_list;
163 #endif
165 /* ftpfs_command wait_flag: */
166 #define NONE 0x00
167 #define WAIT_REPLY 0x01
168 #define WANT_STRING 0x02
169 static char reply_str [80];
171 static struct vfs_class vfs_ftpfs_ops;
173 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
174 Translate a Unix path, i.e. MC's internal path representation (e.g.
175 /somedir/somefile) to a path valid for the remote server. Every path
176 transfered to the remote server has to be mangled by this function
177 right prior to sending it.
178 Currently only Amiga ftp servers are handled in a special manner.
180 When the remote server is an amiga:
181 a) strip leading slash if necesarry
182 b) replace first occurance of ":/" with ":"
183 c) strip trailing "/."
186 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
187 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, 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 p = strchr (ret, ':');
222 if ((p != NULL) && (*(p + 1) == '/'))
223 memmove (p + 1, p + 2, strlen (p + 2) + 1);
225 /* strip trailing "/." */
226 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
227 *p = '\0';
228 return ret;
232 /* Extract the hostname and username from the path */
235 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
236 * ftp://sunsite.unc.edu/pub/linux
237 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
238 * ftp://tsx-11.mit.edu:8192/
239 * ftp://joe@foo.edu:11321/private
240 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
241 * is supplied.
245 #define FTP_COMMAND_PORT 21
247 static void
248 ftpfs_split_url(char *path, char **host, char **user, int *port, char **pass)
250 char *p;
252 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
253 URL_USE_ANONYMOUS);
255 if (!*user) {
256 /* Look up user and password in netrc */
257 if (use_netrc)
258 ftpfs_netrc_lookup (*host, user, pass);
259 if (!*user)
260 *user = g_strdup ("anonymous");
263 /* Look up password in netrc for known user */
264 if (use_netrc && *user && pass && !*pass) {
265 char *new_user;
267 ftpfs_netrc_lookup (*host, &new_user, pass);
269 /* If user is different, remove password */
270 if (new_user && strcmp (*user, new_user)) {
271 g_free (*pass);
272 *pass = NULL;
275 g_free (new_user);
278 g_free (p);
281 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
282 static int
283 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
285 char answer[BUF_1K];
286 int i;
288 for (;;) {
289 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
290 if (string_buf)
291 *string_buf = 0;
292 code = 421;
293 return 4;
295 switch (sscanf(answer, "%d", &code)){
296 case 0:
297 if (string_buf)
298 g_strlcpy (string_buf, answer, string_len);
299 code = 500;
300 return 5;
301 case 1:
302 if (answer[3] == '-') {
303 while (1) {
304 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
305 if (string_buf)
306 *string_buf = 0;
307 code = 421;
308 return 4;
310 if ((sscanf (answer, "%d", &i) > 0) &&
311 (code == i) && (answer[3] == ' '))
312 break;
315 if (string_buf)
316 g_strlcpy (string_buf, answer, string_len);
317 return code / 100;
322 static int
323 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
325 int sock = ftpfs_open_socket (me, super);
326 if (sock != -1){
327 char *cwdir = SUP.cwdir;
328 close (SUP.sock);
329 SUP.sock = sock;
330 SUP.cwdir = NULL;
331 if (ftpfs_login_server (me, super, SUP.password)){
332 if (!cwdir)
333 return 1;
334 sock = ftpfs_chdir_internal (me, super, cwdir);
335 g_free (cwdir);
336 return sock == COMPLETE;
338 SUP.cwdir = cwdir;
340 return 0;
343 static int
344 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
346 va_list ap;
347 char *cmdstr;
348 int status, cmdlen;
349 static int retry = 0;
350 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
352 va_start (ap, fmt);
353 cmdstr = g_strdup_vprintf (fmt, ap);
354 va_end (ap);
356 cmdlen = strlen (cmdstr);
357 cmdstr = g_realloc (cmdstr, cmdlen + 3);
358 strcpy (cmdstr + cmdlen, "\r\n");
359 cmdlen += 2;
361 if (MEDATA->logfile) {
362 if (strncmp (cmdstr, "PASS ", 5) == 0) {
363 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
364 } else
365 fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
367 fflush (MEDATA->logfile);
370 got_sigpipe = 0;
371 tty_enable_interrupt_key ();
372 status = write (SUP.sock, cmdstr, cmdlen);
374 if (status < 0) {
375 code = 421;
377 if (errno == EPIPE) { /* Remote server has closed connection */
378 if (level == 0) {
379 level = 1;
380 status = ftpfs_reconnect (me, super);
381 level = 0;
382 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
383 goto ok;
387 got_sigpipe = 1;
389 g_free (cmdstr);
390 tty_disable_interrupt_key ();
391 return TRANSIENT;
393 retry = 0;
395 tty_disable_interrupt_key ();
397 if (wait_reply)
399 status = ftpfs_get_reply (me, SUP.sock,
400 (wait_reply & WANT_STRING) ? reply_str : NULL,
401 sizeof (reply_str) - 1);
402 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
404 retry = 1;
405 level = 1;
406 status = ftpfs_reconnect (me, super);
407 level = 0;
408 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
409 goto ok;
412 retry = 0;
413 g_free (cmdstr);
414 return status;
416 g_free (cmdstr);
417 return COMPLETE;
420 static void
421 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
423 if (SUP.sock != -1){
424 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
425 ftpfs_command(me, super, NONE, "QUIT");
426 close(SUP.sock);
428 g_free (SUP.host);
429 g_free (SUP.user);
430 g_free (SUP.cwdir);
431 g_free (SUP.password);
434 /* some defines only used by ftpfs_changetype */
435 /* These two are valid values for the second parameter */
436 #define TYPE_ASCII 0
437 #define TYPE_BINARY 1
439 /* This one is only used to initialize bucket->isbinary, don't use it as
440 second parameter to ftpfs_changetype. */
441 #define TYPE_UNKNOWN -1
443 static int
444 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
446 if (binary != SUP.isbinary) {
447 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
448 ERRNOR (EIO, -1);
449 SUP.isbinary = binary;
451 return binary;
454 /* This routine logs the user in */
455 static int
456 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
457 const char *netrcpass)
459 char *pass;
460 char *op;
461 char *name; /* login user name */
462 int anon = 0;
463 char reply_string[BUF_MEDIUM];
465 SUP.isbinary = TYPE_UNKNOWN;
467 if (SUP.password) /* explicit password */
468 op = g_strdup (SUP.password);
469 else if (netrcpass) /* password from netrc */
470 op = g_strdup (netrcpass);
471 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp")) {
472 if (!ftpfs_anonymous_passwd) /* default anonymous password */
473 ftpfs_init_passwd ();
474 op = g_strdup (ftpfs_anonymous_passwd);
475 anon = 1;
476 } else { /* ask user */
477 char *p;
479 p = g_strconcat (_(" FTP: Password required for "),
480 SUP.user, " ", (char *) NULL);
481 op = vfs_get_password (p);
482 g_free (p);
483 if (op == NULL)
484 ERRNOR (EPERM, 0);
485 SUP.password = g_strdup (op);
488 if (!anon || MEDATA->logfile)
489 pass = op;
490 else {
491 pass = g_strconcat ("-", op, (char *) NULL);
492 wipe_password (op);
495 /* Proxy server accepts: username@host-we-want-to-connect */
496 if (SUP.proxy) {
497 name =
498 g_strconcat (SUP.user, "@",
499 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host,
500 (char *) NULL);
501 } else
502 name = g_strdup (SUP.user);
504 if (ftpfs_get_reply
505 (me, SUP.sock, reply_string,
506 sizeof (reply_string) - 1) == COMPLETE) {
507 g_strup (reply_string);
508 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
509 if (MEDATA->logfile) {
510 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n",
511 SUP.remote_is_amiga);
512 fflush (MEDATA->logfile);
515 print_vfs_message (_("ftpfs: sending login name"));
517 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name)) {
518 case CONTINUE:
519 print_vfs_message (_("ftpfs: sending user password"));
520 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
521 if (code == CONTINUE) {
522 char *p;
524 p = g_strdup_printf (_
525 ("FTP: Account required for user %s"),
526 SUP.user);
527 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
528 g_free (p);
529 if (op == NULL)
530 ERRNOR (EPERM, 0);
531 print_vfs_message (_("ftpfs: sending user account"));
532 code =
533 ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
534 g_free (op);
536 if (code != COMPLETE)
537 break;
538 /* fall through */
540 case COMPLETE:
541 print_vfs_message (_("ftpfs: logged in"));
542 wipe_password (pass);
543 g_free (name);
544 return 1;
546 default:
547 SUP.failed_on_login = 1;
548 if (SUP.password)
549 wipe_password (SUP.password);
550 SUP.password = 0;
552 goto login_fail;
555 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
556 SUP.user);
557 login_fail:
558 wipe_password (pass);
559 g_free (name);
560 ERRNOR (EPERM, 0);
563 static struct no_proxy_entry {
564 char *domain;
565 void *next;
566 } *no_proxy;
568 static void
569 ftpfs_load_no_proxy_list (void)
571 /* FixMe: shouldn't be hardcoded!!! */
572 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
573 struct no_proxy_entry *np, *current = 0;
574 FILE *npf;
575 int c;
576 char *p;
577 static char *mc_file;
579 if (mc_file)
580 return;
582 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
583 if (exist_file (mc_file) &&
584 (npf = fopen (mc_file, "r"))) {
585 while (fgets (s, sizeof (s), npf)) {
586 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
587 while ((c = fgetc (npf)) != EOF && c != '\n')
589 continue;
592 if (p == s)
593 continue;
595 *p = '\0';
597 np = g_new (struct no_proxy_entry, 1);
598 np->domain = g_strdup (s);
599 np->next = NULL;
600 if (no_proxy)
601 current->next = np;
602 else
603 no_proxy = np;
604 current = np;
607 fclose (npf);
609 g_free (mc_file);
612 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
613 static int
614 ftpfs_check_proxy (const char *host)
616 struct no_proxy_entry *npe;
618 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
619 return 0; /* sanity check */
621 if (*host == '!')
622 return 1;
624 if (!ftpfs_always_use_proxy)
625 return 0;
627 if (!strchr (host, '.'))
628 return 0;
630 ftpfs_load_no_proxy_list ();
631 for (npe = no_proxy; npe; npe=npe->next) {
632 char *domain = npe->domain;
634 if (domain[0] == '.') {
635 int ld = strlen (domain);
636 int lh = strlen (host);
638 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
639 ld--;
640 lh--;
643 if (!ld)
644 return 0;
645 } else
646 if (!g_strcasecmp (host, domain))
647 return 0;
650 return 1;
653 static void
654 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
656 char *user, *dir;
658 dir =
659 vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT,
660 URL_USE_ANONYMOUS);
661 g_free (user);
662 g_free (dir);
665 static int
666 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
668 struct addrinfo hints, *res, *curr_res;
669 int my_socket = 0;
670 char *host = NULL;
671 char *port = NULL;
672 int tmp_port;
673 int e;
675 (void) me;
677 /* Use a proxy host? */
678 host = g_strdup(SUP.host);
680 if (!host || !*host){
681 print_vfs_message (_("ftpfs: Invalid host name."));
682 ftpfs_errno = EINVAL;
683 g_free (host);
684 return -1;
687 /* Hosts to connect to that start with a ! should use proxy */
688 tmp_port = SUP.port;
690 if (SUP.proxy){
691 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
694 port = g_strdup_printf("%hu", (unsigned short) tmp_port);
695 if (port == NULL) {
696 g_free (host);
697 ftpfs_errno = errno;
698 return -1;
701 tty_enable_interrupt_key(); /* clear the interrupt flag */
703 memset (&hints, 0, sizeof (struct addrinfo));
704 hints.ai_socktype = SOCK_STREAM;
705 hints.ai_flags = AI_ADDRCONFIG;
708 e = getaddrinfo (host, port, &hints, &res);
709 g_free (port);
710 port = NULL;
712 if ( e != 0 ) {
713 tty_disable_interrupt_key ();
714 print_vfs_message (_("ftpfs: %s"), gai_strerror (e));
715 g_free (host);
716 ftpfs_errno = EINVAL;
717 return -1;
720 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next) {
722 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
724 if (my_socket < 0) {
726 if (curr_res->ai_next != NULL)
727 continue;
729 tty_disable_interrupt_key();
730 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno));
731 g_free (host);
732 freeaddrinfo (res);
733 ftpfs_errno = errno;
734 return -1;
737 print_vfs_message (_("ftpfs: making connection to %s"), host);
738 g_free (host);
739 host = NULL;
741 if ( connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0 )
742 break;
744 ftpfs_errno = errno;
745 close (my_socket);
747 if (errno == EINTR && tty_got_interrupt ()) {
748 print_vfs_message (_("ftpfs: connection interrupted by user"));
749 } else if (res->ai_next == NULL) {
750 print_vfs_message (_("ftpfs: connection to server failed: %s"),
751 unix_error_string (errno));
752 } else {
753 continue;
756 freeaddrinfo (res);
757 tty_disable_interrupt_key ();
758 return -1;
761 freeaddrinfo (res);
762 tty_disable_interrupt_key ();
763 return my_socket;
766 static int
767 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
769 int retry_seconds, count_down;
771 /* We do not want to use the passive if we are using proxies */
772 if (SUP.proxy)
773 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
775 retry_seconds = 0;
776 do {
777 SUP.failed_on_login = 0;
779 SUP.sock = ftpfs_open_socket (me, super);
780 if (SUP.sock == -1)
781 return -1;
783 if (ftpfs_login_server (me, super, NULL)) {
784 /* Logged in, no need to retry the connection */
785 break;
786 } else {
787 if (SUP.failed_on_login){
788 /* Close only the socket descriptor */
789 close (SUP.sock);
790 } else {
791 return -1;
793 if (ftpfs_retry_seconds){
794 retry_seconds = ftpfs_retry_seconds;
795 tty_enable_interrupt_key ();
796 for (count_down = retry_seconds; count_down; count_down--){
797 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
798 sleep (1);
799 if (tty_got_interrupt ()) {
800 /* ftpfs_errno = E; */
801 tty_disable_interrupt_key ();
802 return 0;
805 tty_disable_interrupt_key ();
808 } while (retry_seconds);
810 SUP.cwdir = ftpfs_get_current_directory (me, super);
811 if (!SUP.cwdir)
812 SUP.cwdir = g_strdup (PATH_SEP_STR);
813 return 0;
816 static int
817 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
818 const char *archive_name, char *op)
820 char *host, *user, *password;
821 int port;
823 (void) archive_name;
825 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
827 SUP.host = host;
828 SUP.user = user;
829 SUP.port = port;
830 SUP.cwdir = NULL;
831 SUP.proxy = 0;
832 if (ftpfs_check_proxy (host))
833 SUP.proxy = ftpfs_proxy_host;
834 SUP.password = password;
835 SUP.use_passive_connection = ftpfs_use_passive_connections;
836 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
837 SUP.isbinary = TYPE_UNKNOWN;
838 SUP.remote_is_amiga = 0;
839 super->name = g_strdup ("/");
840 super->root =
841 vfs_s_new_inode (me, super,
842 vfs_s_default_stat (me, S_IFDIR | 0755));
844 return ftpfs_open_archive_int (me, super);
847 static int
848 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
849 const char *archive_name, char *op, void *cookie)
851 char *host, *user;
852 int port;
854 (void) me;
855 (void) archive_name;
856 (void) cookie;
858 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
860 port = ((strcmp (host, SUP.host) == 0)
861 && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
863 g_free (host);
864 g_free (user);
866 return port;
869 /* The returned directory should always contain a trailing slash */
870 static char *
871 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
873 char buf[BUF_8K], *bufp, *bufq;
875 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
876 ftpfs_get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
877 bufp = NULL;
878 for (bufq = buf; *bufq; bufq++)
879 if (*bufq == '"') {
880 if (!bufp) {
881 bufp = bufq + 1;
882 } else {
883 *bufq = 0;
884 if (*bufp) {
885 if (*(bufq - 1) != '/') {
886 *bufq++ = '/';
887 *bufq = 0;
889 if (*bufp == '/')
890 return g_strdup (bufp);
891 else {
892 /* If the remote server is an Amiga a leading slash
893 might be missing. MC needs it because it is used
894 as separator between hostname and path internally. */
895 return g_strconcat( "/", bufp, (char *) NULL);
897 } else {
898 ftpfs_errno = EIO;
899 return NULL;
904 ftpfs_errno = EIO;
905 return NULL;
909 /* Setup Passive ftp connection, we use it for source routed connections */
910 static int
911 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
912 int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
914 char *c;
916 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") == COMPLETE) {
917 int port;
918 /* (|||<port>|) */
919 c = strchr (reply_str, '|');
920 if (c == NULL)
921 return 0;
922 if(strlen(c) > 3)
923 c+=3;
924 else
925 return 0;
927 port = atoi (c);
928 if (port < 0 || port > 65535)
929 return 0;
930 port = htons (port);
932 switch (sa->ss_family) {
933 case AF_INET:
934 ((struct sockaddr_in *)sa)->sin_port = port;
935 break;
936 case AF_INET6:
937 ((struct sockaddr_in6 *)sa)->sin6_port = port;
938 break;
939 default:
940 print_vfs_message (_("ftpfs: invalid address family"));
941 ERRNOR (EINVAL, -1);
943 } else if (sa->ss_family == AF_INET) {
944 int xa, xb, xc, xd, xe, xf;
945 char n [6];
947 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
948 return 0;
950 /* Parse remote parameters */
951 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
953 if (!*c)
954 return 0;
955 if (!isdigit ((unsigned char) *c))
956 return 0;
957 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
958 return 0;
960 n [0] = (unsigned char) xa;
961 n [1] = (unsigned char) xb;
962 n [2] = (unsigned char) xc;
963 n [3] = (unsigned char) xd;
964 n [4] = (unsigned char) xe;
965 n [5] = (unsigned char) xf;
967 memcpy (&(((struct sockaddr_in *)sa)->sin_addr.s_addr), (void *)n, 4);
968 memcpy (&(((struct sockaddr_in *)sa)->sin_port), (void *)&n[4], 2);
969 } else
970 return 0;
972 if (connect (my_socket, (struct sockaddr *) sa, *salen ) < 0)
973 return 0;
975 return 1;
978 static int
979 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
981 struct sockaddr_storage data_addr;
982 socklen_t data_addrlen;
983 int data_sock, result;
985 again:
986 memset (&data_addr, 0, sizeof (struct sockaddr_storage));
987 data_addrlen = sizeof (struct sockaddr_storage);
989 if (SUP.use_passive_connection)
990 result = getpeername (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen);
991 else
992 result = getsockname (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen);
994 if (result == -1 )
995 return -1;
997 switch (data_addr.ss_family) {
998 case AF_INET:
999 ((struct sockaddr_in *)&data_addr)->sin_port = 0;
1000 break;
1001 case AF_INET6:
1002 ((struct sockaddr_in6 *)&data_addr)->sin6_port = 0;
1003 break;
1004 default:
1005 print_vfs_message (_("ftpfs: invalid address family"));
1006 ERRNOR(EINVAL, -1);
1009 data_sock = socket (data_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
1010 if (data_sock < 0) {
1011 if (SUP.use_passive_connection) {
1012 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno));
1013 SUP.use_passive_connection = 0;
1014 goto again;
1017 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1018 return -1;
1021 if (SUP.use_passive_connection) {
1023 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1024 return data_sock;
1026 SUP.use_passive_connection = 0;
1027 print_vfs_message (_("ftpfs: could not setup passive mode"));
1029 close (data_sock);
1030 goto again;
1033 /* If passive setup fails, fallback to active connections */
1034 /* Active FTP connection */
1035 if ((bind (data_sock, (struct sockaddr *)&data_addr, data_addrlen) == 0) &&
1036 (getsockname (data_sock, (struct sockaddr *)&data_addr, &data_addrlen) == 0) &&
1037 (listen (data_sock, 1) == 0)) {
1038 unsigned short int port;
1039 char *addr;
1040 unsigned int af;
1042 switch (data_addr.ss_family) {
1043 case AF_INET:
1044 af = FTP_INET;
1045 port = ((struct sockaddr_in *)&data_addr)->sin_port;
1046 break;
1047 case AF_INET6:
1048 af = FTP_INET6;
1049 port = ((struct sockaddr_in6 *)&data_addr)->sin6_port;
1050 break;
1051 default:
1052 print_vfs_message (_("ftpfs: invalid address family"));
1053 ERRNOR (EINVAL, -1);
1056 port = ntohs (port);
1058 addr = g_try_malloc (NI_MAXHOST);
1059 if (addr == NULL)
1060 ERRNOR (ENOMEM, -1);
1062 if (getnameinfo ((struct sockaddr *)&data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) {
1063 g_free (addr);
1064 ERRNOR (EIO, -1);
1067 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE) {
1068 g_free (addr);
1069 return data_sock;
1071 g_free (addr);
1073 if (FTP_INET == af) {
1074 unsigned char *a = (unsigned char *)&((struct sockaddr_in *)&data_addr)->sin_addr;
1075 unsigned char *p = (unsigned char *)&port;
1077 if (ftpfs_command (me, super, WAIT_REPLY,
1078 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1079 p[0], p[1]) == COMPLETE)
1080 return data_sock;
1083 close (data_sock);
1084 ftpfs_errno = EIO;
1085 return -1;
1088 static int
1089 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1090 const char *remote, int isbinary, int reget)
1092 struct sockaddr_storage from;
1093 int s, j, data;
1094 socklen_t fromlen = sizeof(from);
1096 if ((s = ftpfs_initconn (me, super)) == -1)
1097 return -1;
1098 if (ftpfs_changetype (me, super, isbinary) == -1)
1099 return -1;
1100 if (reget > 0){
1101 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1102 if (j != CONTINUE)
1103 return -1;
1105 if (remote) {
1106 char *remote_path = ftpfs_translate_path (me, super, remote);
1107 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1108 /* WarFtpD can't STORE //filename */
1109 (*remote_path == '/') ? remote_path + 1 : remote_path);
1110 g_free (remote_path);
1111 } else
1112 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1113 if (j != PRELIM)
1114 ERRNOR (EPERM, -1);
1115 tty_enable_interrupt_key ();
1116 if (SUP.use_passive_connection)
1117 data = s;
1118 else {
1119 data = accept (s, (struct sockaddr *)&from, &fromlen);
1120 if (data < 0) {
1121 ftpfs_errno = errno;
1122 close (s);
1123 return -1;
1125 close (s);
1127 tty_disable_interrupt_key ();
1128 return data;
1131 #define ABORT_TIMEOUT 5
1132 static void
1133 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
1135 struct vfs_s_super *super = FH_SUPER;
1136 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1137 fd_set mask;
1138 char buf[1024];
1139 int dsock = FH_SOCK;
1140 FH_SOCK = -1;
1141 SUP.ctl_connection_busy = 0;
1143 print_vfs_message (_("ftpfs: aborting transfer."));
1144 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
1145 print_vfs_message (_("ftpfs: abort error: %s"),
1146 unix_error_string (errno));
1147 if (dsock != -1)
1148 close (dsock);
1149 return;
1152 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
1153 print_vfs_message (_("ftpfs: abort failed"));
1154 if (dsock != -1)
1155 close (dsock);
1156 return;
1158 if (dsock != -1) {
1159 FD_ZERO (&mask);
1160 FD_SET (dsock, &mask);
1161 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1162 struct timeval start_tim, tim;
1163 gettimeofday (&start_tim, NULL);
1164 /* flush the remaining data */
1165 while (read (dsock, buf, sizeof (buf)) > 0) {
1166 gettimeofday (&tim, NULL);
1167 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1168 /* server keeps sending, drop the connection and ftpfs_reconnect */
1169 close (dsock);
1170 ftpfs_reconnect (me, super);
1171 return;
1175 close (dsock);
1177 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1178 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1181 #if 0
1182 static void
1183 resolve_symlink_without_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1185 struct linklist *flist;
1186 struct direntry *fe, *fel;
1187 char tmp[MC_MAXPATHLEN];
1188 int depth;
1190 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1191 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1192 /* flist->data->l_stat is alread initialized with 0 */
1193 fel = flist->data;
1194 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1195 if (fel->linkname[0] == '/') {
1196 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1197 continue;
1198 strcpy (tmp, fel->linkname);
1199 } else {
1200 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1201 continue;
1202 strcpy (tmp, dir->remote_path);
1203 if (tmp[1] != '\0')
1204 strcat (tmp, "/");
1205 strcat (tmp + 1, fel->linkname);
1207 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1208 canonicalize_pathname (tmp);
1209 fe = _get_file_entry(bucket, tmp, 0, 0);
1210 if (fe) {
1211 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1212 /* Symlink points to link which isn't resolved, yet. */
1213 if (fe->linkname[0] == '/') {
1214 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1215 break;
1216 strcpy (tmp, fe->linkname);
1217 } else {
1218 /* at this point tmp looks always like this
1219 /directory/filename, i.e. no need to check
1220 strrchr's return value */
1221 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1222 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1223 break;
1224 strcat (tmp, fe->linkname);
1226 continue;
1227 } else {
1228 fel->l_stat = g_new (struct stat, 1);
1229 if ( S_ISLNK (fe->s.st_mode))
1230 *fel->l_stat = *fe->l_stat;
1231 else
1232 *fel->l_stat = fe->s;
1233 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1236 break;
1240 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1243 static void
1244 resolve_symlink_with_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1246 char buffer[2048] = "", *filename;
1247 int sock;
1248 FILE *fp;
1249 struct stat s;
1250 struct linklist *flist;
1251 struct direntry *fe;
1252 int switch_method = 0;
1254 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1255 if (strchr (dir->remote_path, ' ')) {
1256 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1257 print_vfs_message(_("ftpfs: CWD failed."));
1258 return;
1260 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1262 else
1263 sock = ftpfs_open_data_connection (bucket, "LIST -lLa",
1264 dir->remote_path, TYPE_ASCII, 0);
1266 if (sock == -1) {
1267 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1268 return;
1271 fp = fdopen(sock, "r");
1272 if (fp == NULL) {
1273 close(sock);
1274 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1275 return;
1277 tty_enable_interrupt_key ();
1278 flist = dir->file_list->next;
1279 while (1) {
1280 do {
1281 if (flist == dir->file_list)
1282 goto done;
1283 fe = flist->data;
1284 flist = flist->next;
1285 } while (!S_ISLNK(fe->s.st_mode));
1286 while (1) {
1287 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1288 goto done;
1289 if (MEDATA->logfile){
1290 fputs (buffer, MEDATA->logfile);
1291 fflush (MEDATA->logfile);
1293 vfs_die("This code should be commented out\n");
1294 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1295 int r = strcmp(fe->name, filename);
1296 g_free(filename);
1297 if (r == 0) {
1298 if (S_ISLNK (s.st_mode)) {
1299 /* This server doesn't understand LIST -lLa */
1300 switch_method = 1;
1301 goto done;
1303 fe->l_stat = g_new (struct stat, 1);
1304 if (fe->l_stat == NULL)
1305 goto done;
1306 *fe->l_stat = s;
1307 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1308 break;
1310 if (r < 0)
1311 break;
1315 done:
1316 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1317 tty_disable_interrupt_key ();
1318 fclose(fp);
1319 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1322 static void
1323 resolve_symlink(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1325 print_vfs_message(_("Resolving symlink..."));
1327 if (SUP.strict_rfc959_list_cmd)
1328 resolve_symlink_without_ls_options(me, super, dir);
1329 else
1330 resolve_symlink_with_ls_options(me, super, dir);
1332 #endif
1334 static int
1335 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1337 struct vfs_s_entry *ent;
1338 struct vfs_s_super *super = dir->super;
1339 int sock, num_entries = 0;
1340 char buffer[BUF_8K];
1341 int cd_first;
1343 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1344 || (strchr (remote_path, ' ') != NULL);
1346 again:
1347 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1348 remote_path,
1349 SUP.strict ==
1350 RFC_STRICT ? _("(strict rfc959)") : "",
1351 cd_first ? _("(chdir first)") : "");
1353 if (cd_first) {
1354 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE) {
1355 ftpfs_errno = ENOENT;
1356 print_vfs_message (_("ftpfs: CWD failed."));
1357 return -1;
1361 gettimeofday (&dir->timestamp, NULL);
1362 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1364 if (SUP.strict == RFC_STRICT)
1365 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1366 else if (cd_first)
1367 /* Dirty hack to avoid autoprepending / to . */
1368 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1369 sock =
1370 ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1371 else {
1372 /* Trailing "/." is necessary if remote_path is a symlink */
1373 char *path = concat_dir_and_file (remote_path, ".");
1374 sock =
1375 ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII,
1377 g_free (path);
1380 if (sock == -1)
1381 goto fallback;
1383 /* Clear the interrupt flag */
1384 tty_enable_interrupt_key ();
1386 while (1) {
1387 int i;
1388 int res =
1389 vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1390 sock);
1391 if (!res)
1392 break;
1394 if (res == EINTR) {
1395 me->verrno = ECONNRESET;
1396 close (sock);
1397 tty_disable_interrupt_key ();
1398 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1399 print_vfs_message (_("%s: failure"), me->name);
1400 return -1;
1403 if (MEDATA->logfile) {
1404 fputs (buffer, MEDATA->logfile);
1405 fputs ("\n", MEDATA->logfile);
1406 fflush (MEDATA->logfile);
1409 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1410 i = ent->ino->st.st_nlink;
1411 if (!vfs_parse_ls_lga
1412 (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1413 vfs_s_free_entry (me, ent);
1414 continue;
1416 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1417 num_entries++;
1418 vfs_s_insert_entry (me, dir, ent);
1421 close (sock);
1422 me->verrno = E_REMOTE;
1423 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1424 goto fallback;
1426 if (num_entries == 0 && cd_first == 0) {
1427 /* The LIST command may produce an empty output. In such scenario
1428 it is not clear whether this is caused by `remote_path' being
1429 a non-existent path or for some other reason (listing emtpy
1430 directory without the -a option, non-readable directory, etc.).
1432 Since `dir_load' is a crucial method, when it comes to determine
1433 whether a given path is a _directory_, the code must try its best
1434 to determine the type of `remote_path'. The only reliable way to
1435 achieve this is trough issuing a CWD command. */
1437 cd_first = 1;
1438 goto again;
1441 if (SUP.strict == RFC_AUTODETECT)
1442 SUP.strict = RFC_DARING;
1444 print_vfs_message (_("%s: done."), me->name);
1445 return 0;
1447 fallback:
1448 if (SUP.strict == RFC_AUTODETECT) {
1449 /* It's our first attempt to get a directory listing from this
1450 server (UNIX style LIST command) */
1451 SUP.strict = RFC_STRICT;
1452 /* I hate goto, but recursive call needs another 8K on stack */
1453 /* return ftpfs_dir_load (me, dir, remote_path); */
1454 cd_first = 1;
1455 goto again;
1457 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1458 ERRNOR (EACCES, -1);
1461 static int
1462 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name,
1463 char *localname)
1465 int h, sock, n_read, n_written;
1466 off_t n_stored;
1467 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1468 struct linger li;
1469 #else
1470 int flag_one = 1;
1471 #endif
1472 char buffer[8192];
1473 struct stat s;
1474 char *w_buf;
1475 struct vfs_s_super *super = FH_SUPER;
1477 h = open (localname, O_RDONLY);
1478 if (h == -1)
1479 ERRNOR (EIO, -1);
1480 sock =
1481 ftpfs_open_data_connection (me, super,
1482 fh->u.ftp.append ? "APPE" : "STOR", name,
1483 TYPE_BINARY, 0);
1484 if (sock < 0 || fstat (h, &s) == -1) {
1485 close (h);
1486 return -1;
1488 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1489 li.l_onoff = 1;
1490 li.l_linger = 120;
1491 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1492 #else
1493 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1494 #endif
1495 n_stored = 0;
1497 tty_enable_interrupt_key ();
1498 while (1) {
1499 while ((n_read = read (h, buffer, sizeof (buffer))) == -1) {
1500 if (errno == EINTR) {
1501 if (tty_got_interrupt ()) {
1502 ftpfs_errno = EINTR;
1503 goto error_return;
1504 } else
1505 continue;
1507 ftpfs_errno = errno;
1508 goto error_return;
1510 if (n_read == 0)
1511 break;
1512 n_stored += n_read;
1513 w_buf = buffer;
1514 while ((n_written = write (sock, w_buf, n_read)) != n_read) {
1515 if (n_written == -1) {
1516 if (errno == EINTR && !tty_got_interrupt ()) {
1517 continue;
1519 ftpfs_errno = errno;
1520 goto error_return;
1522 w_buf += n_written;
1523 n_read -= n_written;
1525 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1526 (unsigned long) n_stored, (unsigned long) s.st_size);
1528 tty_disable_interrupt_key ();
1529 close (sock);
1530 close (h);
1531 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1532 ERRNOR (EIO, -1);
1533 return 0;
1534 error_return:
1535 tty_disable_interrupt_key ();
1536 close (sock);
1537 close (h);
1538 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1539 return -1;
1542 static int
1543 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1545 char *name = vfs_s_fullpath (me, fh->ino);
1547 if (!name)
1548 return 0;
1549 FH_SOCK = ftpfs_open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1550 g_free (name);
1551 if (FH_SOCK == -1)
1552 ERRNOR (EACCES, 0);
1553 fh->linear = LS_LINEAR_OPEN;
1554 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1555 fh->u.ftp.append = 0;
1556 return 1;
1559 static int
1560 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1562 int n;
1563 struct vfs_s_super *super = FH_SUPER;
1565 while ((n = read (FH_SOCK, buf, len))<0) {
1566 if ((errno == EINTR) && !tty_got_interrupt ())
1567 continue;
1568 break;
1571 if (n<0)
1572 ftpfs_linear_abort(me, fh);
1574 if (!n) {
1575 SUP.ctl_connection_busy = 0;
1576 close (FH_SOCK);
1577 FH_SOCK = -1;
1578 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1579 ERRNOR (E_REMOTE, -1);
1580 return 0;
1582 ERRNOR (errno, n);
1585 static void
1586 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1588 if (FH_SOCK != -1)
1589 ftpfs_linear_abort(me, fh);
1592 static int ftpfs_ctl (void *fh, int ctlop, void *arg)
1594 (void) arg;
1596 switch (ctlop) {
1597 case VFS_CTL_IS_NOTREADY:
1599 int v;
1601 if (!FH->linear)
1602 vfs_die ("You may not do this");
1603 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1604 return 0;
1606 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1607 if (((v < 0) && (errno == EINTR)) || v == 0)
1608 return 1;
1609 return 0;
1611 default:
1612 return 0;
1616 static int
1617 ftpfs_send_command(struct vfs_class *me, const char *filename, const char *cmd, int flags)
1619 const char *rpath;
1620 char *p, *mpath = g_strdup(filename);
1621 struct vfs_s_super *super;
1622 int r;
1623 int flush_directory_cache = (flags & OPT_FLUSH);
1625 if (!(rpath = vfs_s_get_path_mangle(me, mpath, &super, 0))) {
1626 g_free(mpath);
1627 return -1;
1629 p = ftpfs_translate_path (me, super, rpath);
1630 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1631 g_free (p);
1632 vfs_stamp_create (&vfs_ftpfs_ops, super);
1633 if (flags & OPT_IGNORE_ERROR)
1634 r = COMPLETE;
1635 if (r != COMPLETE) {
1636 me->verrno = EPERM;
1637 g_free (mpath);
1638 return -1;
1640 if (flush_directory_cache)
1641 vfs_s_invalidate(me, super);
1642 g_free(mpath);
1643 return 0;
1646 /* This routine is called as the last step in load_setup */
1647 void
1648 ftpfs_init_passwd(void)
1650 ftpfs_anonymous_passwd = load_anon_passwd ();
1651 if (ftpfs_anonymous_passwd)
1652 return;
1654 /* If there is no anonymous ftp password specified
1655 * then we'll just use anonymous@
1656 * We don't send any other thing because:
1657 * - We want to remain anonymous
1658 * - We want to stop SPAM
1659 * - We don't want to let ftp sites to discriminate by the user,
1660 * host or country.
1662 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1665 static int ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1667 char buf[BUF_SMALL];
1668 int ret;
1670 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1672 ret = ftpfs_send_command(me, path, buf, OPT_FLUSH);
1674 if ( mc_config_get_bool (mc_main_config, CONFIG_APP_SECTION,
1675 "ignore_ftp_chattr_errors", TRUE)) {
1676 return 0;
1679 return ret;
1682 static int ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1684 #if 0
1685 ftpfs_errno = EPERM;
1686 return -1;
1687 #else
1688 /* Everyone knows it is not possible to chown remotely, so why bother them.
1689 If someone's root, then copy/move will always try to chown it... */
1690 (void) me;
1691 (void) path;
1692 (void) owner;
1693 (void) group;
1694 return 0;
1695 #endif
1698 static int ftpfs_unlink (struct vfs_class *me, const char *path)
1700 return ftpfs_send_command(me, path, "DELE /%s", OPT_FLUSH);
1703 /* Return 1 if path is the same directory as the one we are in now */
1704 static int
1705 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1707 (void) me;
1709 if (!SUP.cwdir)
1710 return 0;
1711 if (strcmp (path, SUP.cwdir) == 0)
1712 return 1;
1713 return 0;
1716 static int
1717 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1719 int r;
1720 char *p;
1722 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1723 return COMPLETE;
1725 p = ftpfs_translate_path (me, super, remote_path);
1726 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1727 g_free (p);
1729 if (r != COMPLETE) {
1730 ftpfs_errno = EIO;
1731 } else {
1732 g_free(SUP.cwdir);
1733 SUP.cwdir = g_strdup (remote_path);
1734 SUP.cwd_deferred = 0;
1736 return r;
1739 static int ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1741 ftpfs_send_command(me, path1, "RNFR /%s", OPT_FLUSH);
1742 return ftpfs_send_command(me, path2, "RNTO /%s", OPT_FLUSH);
1745 static int ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1747 (void) mode; /* FIXME: should be used */
1749 return ftpfs_send_command(me, path, "MKD /%s", OPT_FLUSH);
1752 static int ftpfs_rmdir (struct vfs_class *me, const char *path)
1754 return ftpfs_send_command(me, path, "RMD /%s", OPT_FLUSH);
1757 static int
1758 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
1759 int mode)
1761 (void) mode;
1763 fh->u.ftp.append = 0;
1764 /* File will be written only, so no need to retrieve it from ftp server */
1765 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
1766 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1767 struct linger li;
1768 #else
1769 int li = 1;
1770 #endif
1771 char *name;
1773 /* ftpfs_linear_start() called, so data will be written
1774 * to local temporary file and stored to ftp server
1775 * by vfs_s_close later
1777 if (FH_SUPER->u.ftp.ctl_connection_busy) {
1778 if (!fh->ino->localname) {
1779 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1780 fh->ino->ent->name);
1781 if (handle == -1)
1782 return -1;
1783 close (handle);
1784 fh->u.ftp.append = flags & O_APPEND;
1786 return 0;
1788 name = vfs_s_fullpath (me, fh->ino);
1789 if (!name)
1790 return -1;
1791 fh->handle =
1792 ftpfs_open_data_connection (me, fh->ino->super,
1793 (flags & O_APPEND) ? "APPE" :
1794 "STOR", name, TYPE_BINARY, 0);
1795 g_free (name);
1797 if (fh->handle < 0)
1798 return -1;
1799 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1800 li.l_onoff = 1;
1801 li.l_linger = 120;
1802 #endif
1803 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1805 if (fh->ino->localname) {
1806 unlink (fh->ino->localname);
1807 g_free (fh->ino->localname);
1808 fh->ino->localname = NULL;
1810 return 0;
1813 if (!fh->ino->localname)
1814 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1815 return -1;
1816 if (!fh->ino->localname)
1817 vfs_die ("retrieve_file failed to fill in localname");
1818 return 0;
1821 static int ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1823 if (fh->handle != -1 && !fh->ino->localname){
1824 close (fh->handle);
1825 fh->handle = -1;
1826 /* File is stored to destination already, so
1827 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1829 fh->changed = 0;
1830 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1831 ERRNOR (EIO, -1);
1832 vfs_s_invalidate (me, FH_SUPER);
1834 return 0;
1837 static void
1838 ftpfs_done (struct vfs_class *me)
1840 struct no_proxy_entry *np;
1842 (void) me;
1844 while (no_proxy) {
1845 np = no_proxy->next;
1846 g_free (no_proxy->domain);
1847 g_free (no_proxy);
1848 no_proxy = np;
1850 g_free (ftpfs_anonymous_passwd);
1851 g_free (ftpfs_proxy_host);
1854 static void
1855 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
1857 struct vfs_s_super *super = MEDATA->supers;
1858 char *name;
1860 while (super){
1861 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
1862 (*func)(name);
1863 g_free (name);
1864 super = super->next;
1868 static char buffer[BUF_MEDIUM];
1869 static char *netrc;
1870 static const char *netrcp;
1872 /* This should match the keywords[] array below */
1873 typedef enum {
1874 NETRC_NONE = 0,
1875 NETRC_DEFAULT,
1876 NETRC_MACHINE,
1877 NETRC_LOGIN,
1878 NETRC_PASSWORD,
1879 NETRC_PASSWD,
1880 NETRC_ACCOUNT,
1881 NETRC_MACDEF,
1882 NETRC_UNKNOWN
1883 } keyword_t;
1885 static keyword_t ftpfs_netrc_next (void)
1887 char *p;
1888 keyword_t i;
1889 static const char *const keywords[] = { "default", "machine",
1890 "login", "password", "passwd", "account", "macdef", NULL
1894 while (1) {
1895 netrcp = skip_separators (netrcp);
1896 if (*netrcp != '\n')
1897 break;
1898 netrcp++;
1900 if (!*netrcp)
1901 return NETRC_NONE;
1902 p = buffer;
1903 if (*netrcp == '"') {
1904 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1905 if (*netrcp == '\\')
1906 netrcp++;
1907 *p++ = *netrcp;
1909 } else {
1910 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1911 *netrcp != ',' && *netrcp; netrcp++) {
1912 if (*netrcp == '\\')
1913 netrcp++;
1914 *p++ = *netrcp;
1917 *p = 0;
1918 if (!*buffer)
1919 return NETRC_NONE;
1921 i = NETRC_DEFAULT;
1922 while (keywords[i - 1]) {
1923 if (!strcmp (keywords[i - 1], buffer))
1924 return i;
1926 i++;
1929 return NETRC_UNKNOWN;
1932 static int ftpfs_netrc_bad_mode (const char *netrcname)
1934 static int be_angry = 1;
1935 struct stat mystat;
1937 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1938 if (be_angry) {
1939 message (D_ERROR, MSG_ERROR,
1940 _("~/.netrc file has incorrect mode.\n"
1941 "Remove password or correct mode."));
1942 be_angry = 0;
1944 return 1;
1946 return 0;
1949 /* Scan .netrc until we find matching "machine" or "default"
1950 * domain is used for additional matching
1951 * No search is done after "default" in compliance with "man netrc"
1952 * Return 0 if found, -1 otherwise */
1953 static int ftpfs_find_machine (const char *host, const char *domain)
1955 keyword_t keyword;
1957 if (!host) host = "";
1958 if (!domain) domain = "";
1960 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) {
1961 if (keyword == NETRC_DEFAULT)
1962 return 0;
1964 if (keyword == NETRC_MACDEF) {
1965 /* Scan for an empty line, which concludes "macdef" */
1966 do {
1967 while (*netrcp && *netrcp != '\n')
1968 netrcp++;
1969 if (*netrcp != '\n')
1970 break;
1971 netrcp++;
1972 } while (*netrcp && *netrcp != '\n');
1973 continue;
1976 if (keyword != NETRC_MACHINE)
1977 continue;
1979 /* Take machine name */
1980 if (ftpfs_netrc_next () == NETRC_NONE)
1981 break;
1983 if (g_strcasecmp (host, buffer)) {
1984 /* Try adding our domain to short names in .netrc */
1985 const char *host_domain = strchr (host, '.');
1986 if (!host_domain)
1987 continue;
1989 /* Compare domain part */
1990 if (g_strcasecmp (host_domain, domain))
1991 continue;
1993 /* Compare local part */
1994 if (g_strncasecmp (host, buffer, host_domain - host))
1995 continue;
1998 return 0;
2001 /* end of .netrc */
2002 return -1;
2005 /* Extract login and password from .netrc for the host.
2006 * pass may be NULL.
2007 * Returns 0 for success, -1 for error */
2008 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass)
2010 char *netrcname;
2011 char *tmp_pass = NULL;
2012 char hostname[MAXHOSTNAMELEN];
2013 const char *domain;
2014 keyword_t keyword;
2015 static struct rupcache {
2016 struct rupcache *next;
2017 char *host;
2018 char *login;
2019 char *pass;
2020 } *rup_cache = NULL, *rupp;
2022 /* Initialize *login and *pass */
2023 if (!login)
2024 return 0;
2025 *login = NULL;
2026 if (pass)
2027 *pass = NULL;
2029 /* Look up in the cache first */
2030 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
2031 if (!strcmp (host, rupp->host)) {
2032 if (rupp->login)
2033 *login = g_strdup (rupp->login);
2034 if (pass && rupp->pass)
2035 *pass = g_strdup (rupp->pass);
2036 return 0;
2040 /* Load current .netrc */
2041 netrcname = concat_dir_and_file (home_dir, ".netrc");
2042 netrcp = netrc = load_file (netrcname);
2043 if (netrc == NULL) {
2044 g_free (netrcname);
2045 return 0;
2048 /* Find our own domain name */
2049 if (gethostname (hostname, sizeof (hostname)) < 0)
2050 *hostname = 0;
2051 if (!(domain = strchr (hostname, '.')))
2052 domain = "";
2054 /* Scan for "default" and matching "machine" keywords */
2055 ftpfs_find_machine (host, domain);
2057 /* Scan for keywords following "default" and "machine" */
2058 while (1) {
2059 int need_break = 0;
2060 keyword = ftpfs_netrc_next ();
2062 switch (keyword) {
2063 case NETRC_LOGIN:
2064 if (ftpfs_netrc_next () == NETRC_NONE) {
2065 need_break = 1;
2066 break;
2069 /* We have another name already - should not happen */
2070 if (*login) {
2071 need_break = 1;
2072 break;
2075 /* We have login name now */
2076 *login = g_strdup (buffer);
2077 break;
2079 case NETRC_PASSWORD:
2080 case NETRC_PASSWD:
2081 if (ftpfs_netrc_next () == NETRC_NONE) {
2082 need_break = 1;
2083 break;
2086 /* Ignore unsafe passwords */
2087 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2088 && ftpfs_netrc_bad_mode (netrcname)) {
2089 need_break = 1;
2090 break;
2093 /* Remember password. pass may be NULL, so use tmp_pass */
2094 if (tmp_pass == NULL)
2095 tmp_pass = g_strdup (buffer);
2096 break;
2098 case NETRC_ACCOUNT:
2099 /* "account" is followed by a token which we ignore */
2100 if (ftpfs_netrc_next () == NETRC_NONE) {
2101 need_break = 1;
2102 break;
2105 /* Ignore account, but warn user anyways */
2106 ftpfs_netrc_bad_mode (netrcname);
2107 break;
2109 default:
2110 /* Unexpected keyword or end of file */
2111 need_break = 1;
2112 break;
2115 if (need_break)
2116 break;
2119 g_free (netrc);
2120 g_free (netrcname);
2122 rupp = g_new (struct rupcache, 1);
2123 rupp->host = g_strdup (host);
2124 rupp->login = rupp->pass = 0;
2126 if (*login != NULL) {
2127 rupp->login = g_strdup (*login);
2129 if (tmp_pass != NULL)
2130 rupp->pass = g_strdup (tmp_pass);
2131 rupp->next = rup_cache;
2132 rup_cache = rupp;
2134 if (pass)
2135 *pass = tmp_pass;
2137 return 0;
2140 void
2141 init_ftpfs (void)
2143 static struct vfs_s_subclass ftpfs_subclass;
2145 tcp_init();
2147 ftpfs_subclass.flags = VFS_S_REMOTE;
2148 ftpfs_subclass.archive_same = ftpfs_archive_same;
2149 ftpfs_subclass.open_archive = ftpfs_open_archive;
2150 ftpfs_subclass.free_archive = ftpfs_free_archive;
2151 ftpfs_subclass.fh_open = ftpfs_fh_open;
2152 ftpfs_subclass.fh_close = ftpfs_fh_close;
2153 ftpfs_subclass.dir_load = ftpfs_dir_load;
2154 ftpfs_subclass.file_store = ftpfs_file_store;
2155 ftpfs_subclass.linear_start = ftpfs_linear_start;
2156 ftpfs_subclass.linear_read = ftpfs_linear_read;
2157 ftpfs_subclass.linear_close = ftpfs_linear_close;
2159 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2160 vfs_ftpfs_ops.name = "ftpfs";
2161 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2162 vfs_ftpfs_ops.prefix = "ftp:";
2163 vfs_ftpfs_ops.done = &ftpfs_done;
2164 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2165 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2166 vfs_ftpfs_ops.chown = ftpfs_chown;
2167 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2168 vfs_ftpfs_ops.rename = ftpfs_rename;
2169 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2170 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2171 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2172 vfs_register_class (&vfs_ftpfs_ops);