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