Ticket #212
[midnight-commander.git] / vfs / ftpfs.c
blob1357332700e0d044816accf5b08105f7323fa4b1
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 */
93 #include "utilvfs.h"
94 #include "xdirentry.h"
95 #include "vfs.h"
96 #include "vfs-impl.h"
97 #include "gc.h" /* vfs_stamp_create */
98 #include "tcputil.h"
99 #include "ftpfs.h"
100 #ifndef MAXHOSTNAMELEN
101 # define MAXHOSTNAMELEN 64
102 #endif
104 #define UPLOAD_ZERO_LENGTH_FILE
105 #define SUP super->u.ftp
106 #define FH_SOCK fh->u.ftp.sock
108 #ifndef INADDR_NONE
109 #define INADDR_NONE 0xffffffff
110 #endif
112 #define RFC_AUTODETECT 0
113 #define RFC_DARING 1
114 #define RFC_STRICT 2
116 #ifndef HAVE_SOCKLEN_T
117 typedef int socklen_t;
118 #endif
120 static int ftpfs_errno;
121 static int code;
123 /* Delay to retry a connection */
124 int ftpfs_retry_seconds = 30;
126 /* Method to use to connect to ftp sites */
127 int ftpfs_use_passive_connections = 1;
128 int ftpfs_use_passive_connections_over_proxy = 0;
130 /* Method used to get directory listings:
131 * 1: try 'LIST -la <path>', if it fails
132 * fall back to CWD <path>; LIST
133 * 0: always use CWD <path>; LIST
135 int ftpfs_use_unix_list_options = 1;
137 /* First "CWD <path>", then "LIST -la ." */
138 int ftpfs_first_cd_then_ls = 1;
140 /* Use the ~/.netrc */
141 int use_netrc = 1;
143 /* Anonymous setup */
144 char *ftpfs_anonymous_passwd = NULL;
145 int ftpfs_directory_timeout = 900;
147 /* Proxy host */
148 char *ftpfs_proxy_host = NULL;
150 /* wether we have to use proxy by default? */
151 int ftpfs_always_use_proxy;
153 #ifdef FIXME_LATER_ALIGATOR
154 static struct linklist *connections_list;
155 #endif
157 /* ftpfs_command wait_flag: */
158 #define NONE 0x00
159 #define WAIT_REPLY 0x01
160 #define WANT_STRING 0x02
161 static char reply_str [80];
163 static struct vfs_class vfs_ftpfs_ops;
165 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
166 Translate a Unix path, i.e. MC's internal path representation (e.g.
167 /somedir/somefile) to a path valid for the remote server. Every path
168 transfered to the remote server has to be mangled by this function
169 right prior to sending it.
170 Currently only Amiga ftp servers are handled in a special manner.
172 When the remote server is an amiga:
173 a) strip leading slash if necesarry
174 b) replace first occurance of ":/" with ":"
175 c) strip trailing "/."
178 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
179 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path);
180 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
181 __attribute__ ((format (__printf__, 4, 5)));
182 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
183 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass);
184 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
186 static char *
187 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
189 if (!SUP.remote_is_amiga)
190 return g_strdup (remote_path);
191 else {
192 char *ret, *p;
194 if (MEDATA->logfile) {
195 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
196 fflush (MEDATA->logfile);
199 /* strip leading slash(es) */
200 while (*remote_path == '/')
201 remote_path++;
204 * Don't change "/" into "", e.g. "CWD " would be
205 * invalid.
207 if (*remote_path == '\0')
208 return g_strdup (".");
210 ret = g_strdup (remote_path);
212 /* replace first occurance of ":/" with ":" */
213 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
214 strcpy (p + 1, p + 2);
216 /* strip trailing "/." */
217 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
218 *p = '\0';
219 return ret;
223 /* Extract the hostname and username from the path */
226 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
227 * ftp://sunsite.unc.edu/pub/linux
228 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
229 * ftp://tsx-11.mit.edu:8192/
230 * ftp://joe@foo.edu:11321/private
231 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
232 * is supplied.
236 #define FTP_COMMAND_PORT 21
238 static void
239 ftpfs_split_url(char *path, char **host, char **user, int *port, char **pass)
241 char *p;
243 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
244 URL_ALLOW_ANON);
246 if (!*user) {
247 /* Look up user and password in netrc */
248 if (use_netrc)
249 ftpfs_netrc_lookup (*host, user, pass);
250 if (!*user)
251 *user = g_strdup ("anonymous");
254 /* Look up password in netrc for known user */
255 if (use_netrc && *user && pass && !*pass) {
256 char *new_user;
258 ftpfs_netrc_lookup (*host, &new_user, pass);
260 /* If user is different, remove password */
261 if (new_user && strcmp (*user, new_user)) {
262 g_free (*pass);
263 *pass = NULL;
266 g_free (new_user);
269 g_free (p);
272 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
273 static int
274 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
276 char answer[BUF_1K];
277 int i;
279 for (;;) {
280 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
281 if (string_buf)
282 *string_buf = 0;
283 code = 421;
284 return 4;
286 switch (sscanf(answer, "%d", &code)){
287 case 0:
288 if (string_buf)
289 g_strlcpy (string_buf, answer, string_len);
290 code = 500;
291 return 5;
292 case 1:
293 if (answer[3] == '-') {
294 while (1) {
295 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
296 if (string_buf)
297 *string_buf = 0;
298 code = 421;
299 return 4;
301 if ((sscanf (answer, "%d", &i) > 0) &&
302 (code == i) && (answer[3] == ' '))
303 break;
306 if (string_buf)
307 g_strlcpy (string_buf, answer, string_len);
308 return code / 100;
313 static int
314 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
316 int sock = ftpfs_open_socket (me, super);
317 if (sock != -1){
318 char *cwdir = SUP.cwdir;
319 close (SUP.sock);
320 SUP.sock = sock;
321 SUP.cwdir = NULL;
322 if (ftpfs_login_server (me, super, SUP.password)){
323 if (!cwdir)
324 return 1;
325 sock = ftpfs_chdir_internal (me, super, cwdir);
326 g_free (cwdir);
327 return sock == COMPLETE;
329 SUP.cwdir = cwdir;
331 return 0;
334 static int
335 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
337 va_list ap;
338 char *cmdstr;
339 int status, cmdlen;
340 static int retry = 0;
341 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
343 va_start (ap, fmt);
344 cmdstr = g_strdup_vprintf (fmt, ap);
345 va_end (ap);
347 cmdlen = strlen (cmdstr);
348 cmdstr = g_realloc (cmdstr, cmdlen + 3);
349 strcpy (cmdstr + cmdlen, "\r\n");
350 cmdlen += 2;
352 if (MEDATA->logfile) {
353 if (strncmp (cmdstr, "PASS ", 5) == 0) {
354 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
355 } else
356 fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
358 fflush (MEDATA->logfile);
361 got_sigpipe = 0;
362 tty_enable_interrupt_key ();
363 status = write (SUP.sock, cmdstr, cmdlen);
365 if (status < 0) {
366 code = 421;
368 if (errno == EPIPE) { /* Remote server has closed connection */
369 if (level == 0) {
370 level = 1;
371 status = ftpfs_reconnect (me, super);
372 level = 0;
373 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
374 goto ok;
378 got_sigpipe = 1;
380 g_free (cmdstr);
381 tty_disable_interrupt_key ();
382 return TRANSIENT;
384 retry = 0;
386 tty_disable_interrupt_key ();
388 if (wait_reply)
390 status = ftpfs_get_reply (me, SUP.sock,
391 (wait_reply & WANT_STRING) ? reply_str : NULL,
392 sizeof (reply_str) - 1);
393 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
395 retry = 1;
396 level = 1;
397 status = ftpfs_reconnect (me, super);
398 level = 0;
399 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
400 goto ok;
403 retry = 0;
404 g_free (cmdstr);
405 return status;
407 g_free (cmdstr);
408 return COMPLETE;
411 static void
412 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
414 if (SUP.sock != -1){
415 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
416 ftpfs_command(me, super, NONE, "QUIT");
417 close(SUP.sock);
419 g_free (SUP.host);
420 g_free (SUP.user);
421 g_free (SUP.cwdir);
422 g_free (SUP.password);
425 /* some defines only used by ftpfs_changetype */
426 /* These two are valid values for the second parameter */
427 #define TYPE_ASCII 0
428 #define TYPE_BINARY 1
430 /* This one is only used to initialize bucket->isbinary, don't use it as
431 second parameter to ftpfs_changetype. */
432 #define TYPE_UNKNOWN -1
434 static int
435 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
437 if (binary != SUP.isbinary) {
438 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
439 ERRNOR (EIO, -1);
440 SUP.isbinary = binary;
442 return binary;
445 /* This routine logs the user in */
446 static int
447 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
448 const char *netrcpass)
450 char *pass;
451 char *op;
452 char *name; /* login user name */
453 int anon = 0;
454 char reply_string[BUF_MEDIUM];
456 SUP.isbinary = TYPE_UNKNOWN;
458 if (SUP.password) /* explicit password */
459 op = g_strdup (SUP.password);
460 else if (netrcpass) /* password from netrc */
461 op = g_strdup (netrcpass);
462 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp")) {
463 if (!ftpfs_anonymous_passwd) /* default anonymous password */
464 ftpfs_init_passwd ();
465 op = g_strdup (ftpfs_anonymous_passwd);
466 anon = 1;
467 } else { /* ask user */
468 char *p;
470 p = g_strconcat (_(" FTP: Password required for "), SUP.user, " ",
471 NULL);
472 op = vfs_get_password (p);
473 g_free (p);
474 if (op == NULL)
475 ERRNOR (EPERM, 0);
476 SUP.password = g_strdup (op);
479 if (!anon || MEDATA->logfile)
480 pass = op;
481 else {
482 pass = g_strconcat ("-", op, (char *) NULL);
483 wipe_password (op);
486 /* Proxy server accepts: username@host-we-want-to-connect */
487 if (SUP.proxy) {
488 name =
489 g_strconcat (SUP.user, "@",
490 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host,
491 NULL);
492 } else
493 name = g_strdup (SUP.user);
495 if (ftpfs_get_reply
496 (me, SUP.sock, reply_string,
497 sizeof (reply_string) - 1) == COMPLETE) {
498 g_strup (reply_string);
499 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
500 if (MEDATA->logfile) {
501 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n",
502 SUP.remote_is_amiga);
503 fflush (MEDATA->logfile);
506 print_vfs_message (_("ftpfs: sending login name"));
508 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name)) {
509 case CONTINUE:
510 print_vfs_message (_("ftpfs: sending user password"));
511 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
512 if (code == CONTINUE) {
513 char *p;
515 p = g_strdup_printf (_
516 ("FTP: Account required for user %s"),
517 SUP.user);
518 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
519 g_free (p);
520 if (op == NULL)
521 ERRNOR (EPERM, 0);
522 print_vfs_message (_("ftpfs: sending user account"));
523 code =
524 ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
525 g_free (op);
527 if (code != COMPLETE)
528 break;
529 /* fall through */
531 case COMPLETE:
532 print_vfs_message (_("ftpfs: logged in"));
533 wipe_password (pass);
534 g_free (name);
535 return 1;
537 default:
538 SUP.failed_on_login = 1;
539 if (SUP.password)
540 wipe_password (SUP.password);
541 SUP.password = 0;
543 goto login_fail;
546 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
547 SUP.user);
548 login_fail:
549 wipe_password (pass);
550 g_free (name);
551 ERRNOR (EPERM, 0);
554 static struct no_proxy_entry {
555 char *domain;
556 void *next;
557 } *no_proxy;
559 static void
560 ftpfs_load_no_proxy_list (void)
562 /* FixMe: shouldn't be hardcoded!!! */
563 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
564 struct no_proxy_entry *np, *current = 0;
565 FILE *npf;
566 int c;
567 char *p;
568 static char *mc_file;
570 if (mc_file)
571 return;
573 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
574 if (exist_file (mc_file) &&
575 (npf = fopen (mc_file, "r"))) {
576 while (fgets (s, sizeof (s), npf)) {
577 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
578 while ((c = fgetc (npf)) != EOF && c != '\n')
580 continue;
583 if (p == s)
584 continue;
586 *p = '\0';
588 np = g_new (struct no_proxy_entry, 1);
589 np->domain = g_strdup (s);
590 np->next = NULL;
591 if (no_proxy)
592 current->next = np;
593 else
594 no_proxy = np;
595 current = np;
598 fclose (npf);
600 g_free (mc_file);
603 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
604 static int
605 ftpfs_check_proxy (const char *host)
607 struct no_proxy_entry *npe;
609 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
610 return 0; /* sanity check */
612 if (*host == '!')
613 return 1;
615 if (!ftpfs_always_use_proxy)
616 return 0;
618 if (!strchr (host, '.'))
619 return 0;
621 ftpfs_load_no_proxy_list ();
622 for (npe = no_proxy; npe; npe=npe->next) {
623 char *domain = npe->domain;
625 if (domain[0] == '.') {
626 int ld = strlen (domain);
627 int lh = strlen (host);
629 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
630 ld--;
631 lh--;
634 if (!ld)
635 return 0;
636 } else
637 if (!g_strcasecmp (host, domain))
638 return 0;
641 return 1;
644 static void
645 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
647 char *user, *dir;
649 dir =
650 vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT,
651 URL_ALLOW_ANON);
652 g_free (user);
653 g_free (dir);
656 static int
657 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
659 struct addrinfo hints, *res, *curr_res;
660 int my_socket = 0;
661 char *host = NULL;
662 char *port = NULL;
663 int tmp_port;
664 int e;
666 (void) me;
668 /* Use a proxy host? */
669 host = g_strdup(SUP.host);
671 if (!host || !*host){
672 print_vfs_message (_("ftpfs: Invalid host name."));
673 ftpfs_errno = EINVAL;
674 return -1;
677 /* Hosts to connect to that start with a ! should use proxy */
678 tmp_port = SUP.port;
680 if (SUP.proxy){
681 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
684 port = g_strdup_printf("%hu", (unsigned short) tmp_port);
685 if (port == NULL) {
686 g_free (host);
687 ftpfs_errno = errno;
688 return -1;
691 tty_enable_interrupt_key(); /* clear the interrupt flag */
693 memset (&hints, 0, sizeof (struct addrinfo));
694 hints.ai_socktype = SOCK_STREAM;
695 hints.ai_flags = AI_ADDRCONFIG;
698 e = getaddrinfo (host, port, &hints, &res);
699 g_free (port);
700 port = NULL;
702 if ( e != 0 ) {
703 tty_disable_interrupt_key ();
704 print_vfs_message (_("ftpfs: %s"), gai_strerror (e));
705 g_free (host);
706 ftpfs_errno = EINVAL;
707 return -1;
710 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next) {
712 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
714 if (my_socket < 0) {
716 if (curr_res->ai_next != NULL)
717 continue;
719 tty_disable_interrupt_key();
720 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno));
721 g_free (host);
722 freeaddrinfo (res);
723 ftpfs_errno = errno;
724 return -1;
727 print_vfs_message (_("ftpfs: making connection to %s"), host);
728 g_free (host);
729 host = NULL;
731 if ( connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0 )
732 break;
734 ftpfs_errno = errno;
735 close (my_socket);
737 if (errno == EINTR && tty_got_interrupt ()) {
738 print_vfs_message (_("ftpfs: connection interrupted by user"));
739 } else if (res->ai_next == NULL) {
740 print_vfs_message (_("ftpfs: connection to server failed: %s"),
741 unix_error_string (errno));
742 } else {
743 continue;
746 freeaddrinfo (res);
747 tty_disable_interrupt_key ();
748 return -1;
751 freeaddrinfo (res);
752 tty_disable_interrupt_key ();
753 return my_socket;
756 static int
757 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
759 int retry_seconds, count_down;
761 /* We do not want to use the passive if we are using proxies */
762 if (SUP.proxy)
763 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
765 retry_seconds = 0;
766 do {
767 SUP.failed_on_login = 0;
769 SUP.sock = ftpfs_open_socket (me, super);
770 if (SUP.sock == -1)
771 return -1;
773 if (ftpfs_login_server (me, super, NULL)) {
774 /* Logged in, no need to retry the connection */
775 break;
776 } else {
777 if (SUP.failed_on_login){
778 /* Close only the socket descriptor */
779 close (SUP.sock);
780 } else {
781 return -1;
783 if (ftpfs_retry_seconds){
784 retry_seconds = ftpfs_retry_seconds;
785 tty_enable_interrupt_key ();
786 for (count_down = retry_seconds; count_down; count_down--){
787 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
788 sleep (1);
789 if (tty_got_interrupt ()) {
790 /* ftpfs_errno = E; */
791 tty_disable_interrupt_key ();
792 return 0;
795 tty_disable_interrupt_key ();
798 } while (retry_seconds);
800 SUP.cwdir = ftpfs_get_current_directory (me, super);
801 if (!SUP.cwdir)
802 SUP.cwdir = g_strdup (PATH_SEP_STR);
803 return 0;
806 static int
807 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
808 const char *archive_name, char *op)
810 char *host, *user, *password;
811 int port;
813 (void) archive_name;
815 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
817 SUP.host = host;
818 SUP.user = user;
819 SUP.port = port;
820 SUP.cwdir = NULL;
821 SUP.proxy = 0;
822 if (ftpfs_check_proxy (host))
823 SUP.proxy = ftpfs_proxy_host;
824 SUP.password = password;
825 SUP.use_passive_connection = ftpfs_use_passive_connections;
826 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
827 SUP.isbinary = TYPE_UNKNOWN;
828 SUP.remote_is_amiga = 0;
829 super->name = g_strdup ("/");
830 super->root =
831 vfs_s_new_inode (me, super,
832 vfs_s_default_stat (me, S_IFDIR | 0755));
834 return ftpfs_open_archive_int (me, super);
837 static int
838 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
839 const char *archive_name, char *op, void *cookie)
841 char *host, *user;
842 int port;
844 (void) me;
845 (void) archive_name;
846 (void) cookie;
848 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
850 port = ((strcmp (host, SUP.host) == 0)
851 && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
853 g_free (host);
854 g_free (user);
856 return port;
859 /* The returned directory should always contain a trailing slash */
860 static char *
861 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
863 char buf[BUF_8K], *bufp, *bufq;
865 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
866 ftpfs_get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
867 bufp = NULL;
868 for (bufq = buf; *bufq; bufq++)
869 if (*bufq == '"') {
870 if (!bufp) {
871 bufp = bufq + 1;
872 } else {
873 *bufq = 0;
874 if (*bufp) {
875 if (*(bufq - 1) != '/') {
876 *bufq++ = '/';
877 *bufq = 0;
879 if (*bufp == '/')
880 return g_strdup (bufp);
881 else {
882 /* If the remote server is an Amiga a leading slash
883 might be missing. MC needs it because it is used
884 as separator between hostname and path internally. */
885 return g_strconcat( "/", bufp, NULL);
887 } else {
888 ftpfs_errno = EIO;
889 return NULL;
894 ftpfs_errno = EIO;
895 return NULL;
899 /* Setup Passive ftp connection, we use it for source routed connections */
900 static int
901 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
902 int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
904 char *c;
906 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") == COMPLETE) {
907 int port;
908 /* (|||<port>|) */
909 c = strchr (reply_str, '|');
910 if (c == NULL)
911 return 0;
912 if(strlen(c) > 3)
913 c+=3;
914 else
915 return 0;
917 port = atoi (c);
918 if (port < 0 || port > 65535)
919 return 0;
920 port = htons (port);
922 switch (sa->ss_family) {
923 case AF_INET:
924 ((struct sockaddr_in *)sa)->sin_port = port;
925 break;
926 case AF_INET6:
927 ((struct sockaddr_in6 *)sa)->sin6_port = port;
928 break;
929 default:
930 print_vfs_message (_("ftpfs: invalid address family"));
931 ERRNOR (EINVAL, -1);
933 } else if (sa->ss_family == AF_INET) {
934 int xa, xb, xc, xd, xe, xf;
935 char n [6];
937 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
938 return 0;
940 /* Parse remote parameters */
941 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
943 if (!*c)
944 return 0;
945 if (!isdigit ((unsigned char) *c))
946 return 0;
947 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
948 return 0;
950 n [0] = (unsigned char) xa;
951 n [1] = (unsigned char) xb;
952 n [2] = (unsigned char) xc;
953 n [3] = (unsigned char) xd;
954 n [4] = (unsigned char) xe;
955 n [5] = (unsigned char) xf;
957 memcpy (&(((struct sockaddr_in *)sa)->sin_addr.s_addr), (void *)n, 4);
958 memcpy (&(((struct sockaddr_in *)sa)->sin_port), (void *)&n[4], 2);
959 } else
960 return 0;
962 if (connect (my_socket, (struct sockaddr *) sa, *salen ) < 0)
963 return 0;
965 return 1;
968 static int
969 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
971 struct sockaddr_storage data_addr;
972 socklen_t data_addrlen;
973 int data_sock;
975 again:
976 memset (&data_addr, 0, sizeof (struct sockaddr_storage));
977 data_addrlen = sizeof (struct sockaddr_storage);
979 if (getpeername (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen) == -1)
980 return -1;
982 switch (data_addr.ss_family) {
983 case AF_INET:
984 ((struct sockaddr_in *)&data_addr)->sin_port = 0;
985 break;
986 case AF_INET6:
987 ((struct sockaddr_in6 *)&data_addr)->sin6_port = 0;
988 break;
989 default:
990 print_vfs_message (_("ftpfs: invalid address family"));
991 ERRNOR(EINVAL, -1);
994 data_sock = socket (data_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
995 if (data_sock < 0) {
996 if (SUP.use_passive_connection) {
997 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno));
998 SUP.use_passive_connection = 0;
999 goto again;
1002 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1003 return -1;
1006 if (SUP.use_passive_connection) {
1008 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1009 return data_sock;
1011 SUP.use_passive_connection = 0;
1012 print_vfs_message (_("ftpfs: could not setup passive mode"));
1014 close (data_sock);
1015 goto again;
1018 /* If passive setup fails, fallback to active connections */
1019 /* Active FTP connection */
1020 if ((bind (data_sock, (struct sockaddr *)&data_addr, data_addrlen) == 0) &&
1021 (getsockname (data_sock, (struct sockaddr *)&data_addr, &data_addrlen) == 0) &&
1022 (listen (data_sock, 1) == 0)) {
1023 unsigned short int port;
1024 char *addr;
1025 unsigned int af;
1027 switch (data_addr.ss_family) {
1028 case AF_INET:
1029 af = FTP_INET;
1030 port = ((struct sockaddr_in *)&data_addr)->sin_port;
1031 break;
1032 case AF_INET6:
1033 af = FTP_INET6;
1034 port = ((struct sockaddr_in6 *)&data_addr)->sin6_port;
1035 break;
1036 default:
1037 print_vfs_message (_("ftpfs: invalid address family"));
1038 ERRNOR (EINVAL, -1);
1041 port = ntohs (port);
1043 addr = g_malloc (NI_MAXHOST);
1044 if (addr == NULL)
1045 ERRNOR (ENOMEM, -1);
1047 if (getnameinfo ((struct sockaddr *)&data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) {
1048 g_free (addr);
1049 ERRNOR (EIO, -1);
1052 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE) {
1053 g_free (addr);
1054 return data_sock;
1056 g_free (addr);
1058 if (FTP_INET == af) {
1059 unsigned char *a = (unsigned char *)&((struct sockaddr_in *)&data_addr)->sin_addr;
1060 unsigned char *p = (unsigned char *)&port;
1062 if (ftpfs_command (me, super, WAIT_REPLY,
1063 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1064 p[0], p[1]) == COMPLETE)
1065 return data_sock;
1068 close (data_sock);
1069 ftpfs_errno = EIO;
1070 return -1;
1073 static int
1074 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1075 const char *remote, int isbinary, int reget)
1077 struct sockaddr_storage from;
1078 int s, j, data;
1079 socklen_t fromlen = sizeof(from);
1081 if ((s = ftpfs_initconn (me, super)) == -1)
1082 return -1;
1083 if (ftpfs_changetype (me, super, isbinary) == -1)
1084 return -1;
1085 if (reget > 0){
1086 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1087 if (j != CONTINUE)
1088 return -1;
1090 if (remote) {
1091 char *remote_path = ftpfs_translate_path (me, super, remote);
1092 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1093 /* WarFtpD can't STORE //filename */
1094 (*remote_path == '/') ? remote_path + 1 : remote_path);
1095 g_free (remote_path);
1096 } else
1097 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1098 if (j != PRELIM)
1099 ERRNOR (EPERM, -1);
1100 tty_enable_interrupt_key ();
1101 if (SUP.use_passive_connection)
1102 data = s;
1103 else {
1104 data = accept (s, (struct sockaddr *)&from, &fromlen);
1105 if (data < 0) {
1106 ftpfs_errno = errno;
1107 close (s);
1108 return -1;
1110 close (s);
1112 tty_disable_interrupt_key ();
1113 return data;
1116 #define ABORT_TIMEOUT 5
1117 static void
1118 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
1120 struct vfs_s_super *super = FH_SUPER;
1121 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1122 fd_set mask;
1123 char buf[1024];
1124 int dsock = FH_SOCK;
1125 FH_SOCK = -1;
1126 SUP.ctl_connection_busy = 0;
1128 print_vfs_message (_("ftpfs: aborting transfer."));
1129 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
1130 print_vfs_message (_("ftpfs: abort error: %s"),
1131 unix_error_string (errno));
1132 if (dsock != -1)
1133 close (dsock);
1134 return;
1137 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
1138 print_vfs_message (_("ftpfs: abort failed"));
1139 if (dsock != -1)
1140 close (dsock);
1141 return;
1143 if (dsock != -1) {
1144 FD_ZERO (&mask);
1145 FD_SET (dsock, &mask);
1146 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1147 struct timeval start_tim, tim;
1148 gettimeofday (&start_tim, NULL);
1149 /* flush the remaining data */
1150 while (read (dsock, buf, sizeof (buf)) > 0) {
1151 gettimeofday (&tim, NULL);
1152 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1153 /* server keeps sending, drop the connection and ftpfs_reconnect */
1154 close (dsock);
1155 ftpfs_reconnect (me, super);
1156 return;
1160 close (dsock);
1162 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1163 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1166 #if 0
1167 static void
1168 resolve_symlink_without_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1170 struct linklist *flist;
1171 struct direntry *fe, *fel;
1172 char tmp[MC_MAXPATHLEN];
1173 int depth;
1175 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1176 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1177 /* flist->data->l_stat is alread initialized with 0 */
1178 fel = flist->data;
1179 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1180 if (fel->linkname[0] == '/') {
1181 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1182 continue;
1183 strcpy (tmp, fel->linkname);
1184 } else {
1185 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1186 continue;
1187 strcpy (tmp, dir->remote_path);
1188 if (tmp[1] != '\0')
1189 strcat (tmp, "/");
1190 strcat (tmp + 1, fel->linkname);
1192 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1193 canonicalize_pathname (tmp);
1194 fe = _get_file_entry(bucket, tmp, 0, 0);
1195 if (fe) {
1196 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1197 /* Symlink points to link which isn't resolved, yet. */
1198 if (fe->linkname[0] == '/') {
1199 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1200 break;
1201 strcpy (tmp, fe->linkname);
1202 } else {
1203 /* at this point tmp looks always like this
1204 /directory/filename, i.e. no need to check
1205 strrchr's return value */
1206 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1207 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1208 break;
1209 strcat (tmp, fe->linkname);
1211 continue;
1212 } else {
1213 fel->l_stat = g_new (struct stat, 1);
1214 if ( S_ISLNK (fe->s.st_mode))
1215 *fel->l_stat = *fe->l_stat;
1216 else
1217 *fel->l_stat = fe->s;
1218 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1221 break;
1225 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1228 static void
1229 resolve_symlink_with_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1231 char buffer[2048] = "", *filename;
1232 int sock;
1233 FILE *fp;
1234 struct stat s;
1235 struct linklist *flist;
1236 struct direntry *fe;
1237 int switch_method = 0;
1239 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1240 if (strchr (dir->remote_path, ' ')) {
1241 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1242 print_vfs_message(_("ftpfs: CWD failed."));
1243 return;
1245 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1247 else
1248 sock = ftpfs_open_data_connection (bucket, "LIST -lLa",
1249 dir->remote_path, TYPE_ASCII, 0);
1251 if (sock == -1) {
1252 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1253 return;
1256 fp = fdopen(sock, "r");
1257 if (fp == NULL) {
1258 close(sock);
1259 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1260 return;
1262 tty_enable_interrupt_key ();
1263 flist = dir->file_list->next;
1264 while (1) {
1265 do {
1266 if (flist == dir->file_list)
1267 goto done;
1268 fe = flist->data;
1269 flist = flist->next;
1270 } while (!S_ISLNK(fe->s.st_mode));
1271 while (1) {
1272 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1273 goto done;
1274 if (MEDATA->logfile){
1275 fputs (buffer, MEDATA->logfile);
1276 fflush (MEDATA->logfile);
1278 vfs_die("This code should be commented out\n");
1279 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1280 int r = strcmp(fe->name, filename);
1281 g_free(filename);
1282 if (r == 0) {
1283 if (S_ISLNK (s.st_mode)) {
1284 /* This server doesn't understand LIST -lLa */
1285 switch_method = 1;
1286 goto done;
1288 fe->l_stat = g_new (struct stat, 1);
1289 if (fe->l_stat == NULL)
1290 goto done;
1291 *fe->l_stat = s;
1292 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1293 break;
1295 if (r < 0)
1296 break;
1300 done:
1301 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1302 tty_disable_interrupt_key ();
1303 fclose(fp);
1304 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1307 static void
1308 resolve_symlink(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1310 print_vfs_message(_("Resolving symlink..."));
1312 if (SUP.strict_rfc959_list_cmd)
1313 resolve_symlink_without_ls_options(me, super, dir);
1314 else
1315 resolve_symlink_with_ls_options(me, super, dir);
1317 #endif
1319 static int
1320 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1322 struct vfs_s_entry *ent;
1323 struct vfs_s_super *super = dir->super;
1324 int sock, num_entries = 0;
1325 char buffer[BUF_8K];
1326 int cd_first;
1328 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1329 || (strchr (remote_path, ' ') != NULL);
1331 again:
1332 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1333 remote_path,
1334 SUP.strict ==
1335 RFC_STRICT ? _("(strict rfc959)") : "",
1336 cd_first ? _("(chdir first)") : "");
1338 if (cd_first) {
1339 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE) {
1340 ftpfs_errno = ENOENT;
1341 print_vfs_message (_("ftpfs: CWD failed."));
1342 return -1;
1346 gettimeofday (&dir->timestamp, NULL);
1347 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1349 if (SUP.strict == RFC_STRICT)
1350 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1351 else if (cd_first)
1352 /* Dirty hack to avoid autoprepending / to . */
1353 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1354 sock =
1355 ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1356 else {
1357 /* Trailing "/." is necessary if remote_path is a symlink */
1358 char *path = concat_dir_and_file (remote_path, ".");
1359 sock =
1360 ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII,
1362 g_free (path);
1365 if (sock == -1)
1366 goto fallback;
1368 /* Clear the interrupt flag */
1369 tty_enable_interrupt_key ();
1371 while (1) {
1372 int i;
1373 int res =
1374 vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1375 sock);
1376 if (!res)
1377 break;
1379 if (res == EINTR) {
1380 me->verrno = ECONNRESET;
1381 close (sock);
1382 tty_disable_interrupt_key ();
1383 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1384 print_vfs_message (_("%s: failure"), me->name);
1385 return -1;
1388 if (MEDATA->logfile) {
1389 fputs (buffer, MEDATA->logfile);
1390 fputs ("\n", MEDATA->logfile);
1391 fflush (MEDATA->logfile);
1394 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1395 i = ent->ino->st.st_nlink;
1396 if (!vfs_parse_ls_lga
1397 (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1398 vfs_s_free_entry (me, ent);
1399 continue;
1401 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1402 num_entries++;
1403 vfs_s_insert_entry (me, dir, ent);
1406 close (sock);
1407 me->verrno = E_REMOTE;
1408 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1409 goto fallback;
1411 if (num_entries == 0 && cd_first == 0) {
1412 /* The LIST command may produce an empty output. In such scenario
1413 it is not clear whether this is caused by `remote_path' being
1414 a non-existent path or for some other reason (listing emtpy
1415 directory without the -a option, non-readable directory, etc.).
1417 Since `dir_load' is a crucial method, when it comes to determine
1418 whether a given path is a _directory_, the code must try its best
1419 to determine the type of `remote_path'. The only reliable way to
1420 achieve this is trough issuing a CWD command. */
1422 cd_first = 1;
1423 goto again;
1426 if (SUP.strict == RFC_AUTODETECT)
1427 SUP.strict = RFC_DARING;
1429 print_vfs_message (_("%s: done."), me->name);
1430 return 0;
1432 fallback:
1433 if (SUP.strict == RFC_AUTODETECT) {
1434 /* It's our first attempt to get a directory listing from this
1435 server (UNIX style LIST command) */
1436 SUP.strict = RFC_STRICT;
1437 /* I hate goto, but recursive call needs another 8K on stack */
1438 /* return ftpfs_dir_load (me, dir, remote_path); */
1439 cd_first = 1;
1440 goto again;
1442 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1443 ERRNOR (EACCES, -1);
1446 static int
1447 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name,
1448 char *localname)
1450 int h, sock, n_read, n_written;
1451 off_t n_stored;
1452 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1453 struct linger li;
1454 #else
1455 int flag_one = 1;
1456 #endif
1457 char buffer[8192];
1458 struct stat s;
1459 char *w_buf;
1460 struct vfs_s_super *super = FH_SUPER;
1462 h = open (localname, O_RDONLY);
1463 if (h == -1)
1464 ERRNOR (EIO, -1);
1465 sock =
1466 ftpfs_open_data_connection (me, super,
1467 fh->u.ftp.append ? "APPE" : "STOR", name,
1468 TYPE_BINARY, 0);
1469 if (sock < 0 || fstat (h, &s) == -1) {
1470 close (h);
1471 return -1;
1473 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1474 li.l_onoff = 1;
1475 li.l_linger = 120;
1476 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1477 #else
1478 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1479 #endif
1480 n_stored = 0;
1482 tty_enable_interrupt_key ();
1483 while (1) {
1484 while ((n_read = read (h, buffer, sizeof (buffer))) == -1) {
1485 if (errno == EINTR) {
1486 if (tty_got_interrupt ()) {
1487 ftpfs_errno = EINTR;
1488 goto error_return;
1489 } else
1490 continue;
1492 ftpfs_errno = errno;
1493 goto error_return;
1495 if (n_read == 0)
1496 break;
1497 n_stored += n_read;
1498 w_buf = buffer;
1499 while ((n_written = write (sock, w_buf, n_read)) != n_read) {
1500 if (n_written == -1) {
1501 if (errno == EINTR && !tty_got_interrupt ()) {
1502 continue;
1504 ftpfs_errno = errno;
1505 goto error_return;
1507 w_buf += n_written;
1508 n_read -= n_written;
1510 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1511 (unsigned long) n_stored, (unsigned long) s.st_size);
1513 tty_disable_interrupt_key ();
1514 close (sock);
1515 close (h);
1516 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1517 ERRNOR (EIO, -1);
1518 return 0;
1519 error_return:
1520 tty_disable_interrupt_key ();
1521 close (sock);
1522 close (h);
1523 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1524 return -1;
1527 static int
1528 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1530 char *name = vfs_s_fullpath (me, fh->ino);
1532 if (!name)
1533 return 0;
1534 FH_SOCK = ftpfs_open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1535 g_free (name);
1536 if (FH_SOCK == -1)
1537 ERRNOR (EACCES, 0);
1538 fh->linear = LS_LINEAR_OPEN;
1539 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1540 fh->u.ftp.append = 0;
1541 return 1;
1544 static int
1545 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1547 int n;
1548 struct vfs_s_super *super = FH_SUPER;
1550 while ((n = read (FH_SOCK, buf, len))<0) {
1551 if ((errno == EINTR) && !tty_got_interrupt ())
1552 continue;
1553 break;
1556 if (n<0)
1557 ftpfs_linear_abort(me, fh);
1559 if (!n) {
1560 SUP.ctl_connection_busy = 0;
1561 close (FH_SOCK);
1562 FH_SOCK = -1;
1563 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1564 ERRNOR (E_REMOTE, -1);
1565 return 0;
1567 ERRNOR (errno, n);
1570 static void
1571 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1573 if (FH_SOCK != -1)
1574 ftpfs_linear_abort(me, fh);
1577 static int ftpfs_ctl (void *fh, int ctlop, void *arg)
1579 (void) arg;
1581 switch (ctlop) {
1582 case VFS_CTL_IS_NOTREADY:
1584 int v;
1586 if (!FH->linear)
1587 vfs_die ("You may not do this");
1588 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1589 return 0;
1591 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1592 if (((v < 0) && (errno == EINTR)) || v == 0)
1593 return 1;
1594 return 0;
1596 default:
1597 return 0;
1601 static int
1602 ftpfs_send_command(struct vfs_class *me, const char *filename, const char *cmd, int flags)
1604 const char *rpath;
1605 char *p, *mpath = g_strdup(filename);
1606 struct vfs_s_super *super;
1607 int r;
1608 int flush_directory_cache = (flags & OPT_FLUSH);
1610 if (!(rpath = vfs_s_get_path_mangle(me, mpath, &super, 0))) {
1611 g_free(mpath);
1612 return -1;
1614 p = ftpfs_translate_path (me, super, rpath);
1615 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1616 g_free (p);
1617 vfs_stamp_create (&vfs_ftpfs_ops, super);
1618 if (flags & OPT_IGNORE_ERROR)
1619 r = COMPLETE;
1620 if (r != COMPLETE) {
1621 me->verrno = EPERM;
1622 g_free (mpath);
1623 return -1;
1625 if (flush_directory_cache)
1626 vfs_s_invalidate(me, super);
1627 g_free(mpath);
1628 return 0;
1631 /* This routine is called as the last step in load_setup */
1632 void
1633 ftpfs_init_passwd(void)
1635 ftpfs_anonymous_passwd = load_anon_passwd ();
1636 if (ftpfs_anonymous_passwd)
1637 return;
1639 /* If there is no anonymous ftp password specified
1640 * then we'll just use anonymous@
1641 * We don't send any other thing because:
1642 * - We want to remain anonymous
1643 * - We want to stop SPAM
1644 * - We don't want to let ftp sites to discriminate by the user,
1645 * host or country.
1647 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1650 static int ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1652 char buf[BUF_SMALL];
1654 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1655 return ftpfs_send_command(me, path, buf, OPT_FLUSH);
1658 static int ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1660 #if 0
1661 ftpfs_errno = EPERM;
1662 return -1;
1663 #else
1664 /* Everyone knows it is not possible to chown remotely, so why bother them.
1665 If someone's root, then copy/move will always try to chown it... */
1666 (void) me;
1667 (void) path;
1668 (void) owner;
1669 (void) group;
1670 return 0;
1671 #endif
1674 static int ftpfs_unlink (struct vfs_class *me, const char *path)
1676 return ftpfs_send_command(me, path, "DELE /%s", OPT_FLUSH);
1679 /* Return 1 if path is the same directory as the one we are in now */
1680 static int
1681 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1683 (void) me;
1685 if (!SUP.cwdir)
1686 return 0;
1687 if (strcmp (path, SUP.cwdir) == 0)
1688 return 1;
1689 return 0;
1692 static int
1693 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1695 int r;
1696 char *p;
1698 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1699 return COMPLETE;
1701 p = ftpfs_translate_path (me, super, remote_path);
1702 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1703 g_free (p);
1705 if (r != COMPLETE) {
1706 ftpfs_errno = EIO;
1707 } else {
1708 g_free(SUP.cwdir);
1709 SUP.cwdir = g_strdup (remote_path);
1710 SUP.cwd_deferred = 0;
1712 return r;
1715 static int ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1717 ftpfs_send_command(me, path1, "RNFR /%s", OPT_FLUSH);
1718 return ftpfs_send_command(me, path2, "RNTO /%s", OPT_FLUSH);
1721 static int ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1723 (void) mode; /* FIXME: should be used */
1725 return ftpfs_send_command(me, path, "MKD /%s", OPT_FLUSH);
1728 static int ftpfs_rmdir (struct vfs_class *me, const char *path)
1730 return ftpfs_send_command(me, path, "RMD /%s", OPT_FLUSH);
1733 static int
1734 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
1735 int mode)
1737 (void) mode;
1739 fh->u.ftp.append = 0;
1740 /* File will be written only, so no need to retrieve it from ftp server */
1741 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
1742 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1743 struct linger li;
1744 #else
1745 int li = 1;
1746 #endif
1747 char *name;
1749 /* ftpfs_linear_start() called, so data will be written
1750 * to local temporary file and stored to ftp server
1751 * by vfs_s_close later
1753 if (FH_SUPER->u.ftp.ctl_connection_busy) {
1754 if (!fh->ino->localname) {
1755 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1756 fh->ino->ent->name);
1757 if (handle == -1)
1758 return -1;
1759 close (handle);
1760 fh->u.ftp.append = flags & O_APPEND;
1762 return 0;
1764 name = vfs_s_fullpath (me, fh->ino);
1765 if (!name)
1766 return -1;
1767 fh->handle =
1768 ftpfs_open_data_connection (me, fh->ino->super,
1769 (flags & O_APPEND) ? "APPE" :
1770 "STOR", name, TYPE_BINARY, 0);
1771 g_free (name);
1773 if (fh->handle < 0)
1774 return -1;
1775 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1776 li.l_onoff = 1;
1777 li.l_linger = 120;
1778 #endif
1779 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1781 if (fh->ino->localname) {
1782 unlink (fh->ino->localname);
1783 g_free (fh->ino->localname);
1784 fh->ino->localname = NULL;
1786 return 0;
1789 if (!fh->ino->localname)
1790 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1791 return -1;
1792 if (!fh->ino->localname)
1793 vfs_die ("retrieve_file failed to fill in localname");
1794 return 0;
1797 static int ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1799 if (fh->handle != -1 && !fh->ino->localname){
1800 close (fh->handle);
1801 fh->handle = -1;
1802 /* File is stored to destination already, so
1803 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1805 fh->changed = 0;
1806 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1807 ERRNOR (EIO, -1);
1808 vfs_s_invalidate (me, FH_SUPER);
1810 return 0;
1813 static void
1814 ftpfs_done (struct vfs_class *me)
1816 struct no_proxy_entry *np;
1818 (void) me;
1820 while (no_proxy) {
1821 np = no_proxy->next;
1822 g_free (no_proxy->domain);
1823 g_free (no_proxy);
1824 no_proxy = np;
1826 g_free (ftpfs_anonymous_passwd);
1827 g_free (ftpfs_proxy_host);
1830 static void
1831 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
1833 struct vfs_s_super *super = MEDATA->supers;
1834 char *name;
1836 while (super){
1837 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
1838 (*func)(name);
1839 g_free (name);
1840 super = super->next;
1844 static char buffer[BUF_MEDIUM];
1845 static char *netrc;
1846 static const char *netrcp;
1848 /* This should match the keywords[] array below */
1849 typedef enum {
1850 NETRC_NONE = 0,
1851 NETRC_DEFAULT,
1852 NETRC_MACHINE,
1853 NETRC_LOGIN,
1854 NETRC_PASSWORD,
1855 NETRC_PASSWD,
1856 NETRC_ACCOUNT,
1857 NETRC_MACDEF,
1858 NETRC_UNKNOWN
1859 } keyword_t;
1861 static keyword_t ftpfs_netrc_next (void)
1863 char *p;
1864 keyword_t i;
1865 static const char *const keywords[] = { "default", "machine",
1866 "login", "password", "passwd", "account", "macdef", NULL
1870 while (1) {
1871 netrcp = skip_separators (netrcp);
1872 if (*netrcp != '\n')
1873 break;
1874 netrcp++;
1876 if (!*netrcp)
1877 return NETRC_NONE;
1878 p = buffer;
1879 if (*netrcp == '"') {
1880 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1881 if (*netrcp == '\\')
1882 netrcp++;
1883 *p++ = *netrcp;
1885 } else {
1886 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1887 *netrcp != ',' && *netrcp; netrcp++) {
1888 if (*netrcp == '\\')
1889 netrcp++;
1890 *p++ = *netrcp;
1893 *p = 0;
1894 if (!*buffer)
1895 return NETRC_NONE;
1897 i = NETRC_DEFAULT;
1898 while (keywords[i - 1]) {
1899 if (!strcmp (keywords[i - 1], buffer))
1900 return i;
1902 i++;
1905 return NETRC_UNKNOWN;
1908 static int ftpfs_netrc_bad_mode (const char *netrcname)
1910 static int be_angry = 1;
1911 struct stat mystat;
1913 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1914 if (be_angry) {
1915 message (D_ERROR, MSG_ERROR,
1916 _("~/.netrc file has incorrect mode.\n"
1917 "Remove password or correct mode."));
1918 be_angry = 0;
1920 return 1;
1922 return 0;
1925 /* Scan .netrc until we find matching "machine" or "default"
1926 * domain is used for additional matching
1927 * No search is done after "default" in compliance with "man netrc"
1928 * Return 0 if found, -1 otherwise */
1929 static int ftpfs_find_machine (const char *host, const char *domain)
1931 keyword_t keyword;
1933 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) {
1934 if (keyword == NETRC_DEFAULT)
1935 return 0;
1937 if (keyword == NETRC_MACDEF) {
1938 /* Scan for an empty line, which concludes "macdef" */
1939 do {
1940 while (*netrcp && *netrcp != '\n')
1941 netrcp++;
1942 if (*netrcp != '\n')
1943 break;
1944 netrcp++;
1945 } while (*netrcp && *netrcp != '\n');
1946 continue;
1949 if (keyword != NETRC_MACHINE)
1950 continue;
1952 /* Take machine name */
1953 if (ftpfs_netrc_next () == NETRC_NONE)
1954 break;
1956 if (g_strcasecmp (host, buffer)) {
1957 /* Try adding our domain to short names in .netrc */
1958 const char *host_domain = strchr (host, '.');
1959 if (!host_domain)
1960 continue;
1962 /* Compare domain part */
1963 if (g_strcasecmp (host_domain, domain))
1964 continue;
1966 /* Compare local part */
1967 if (g_strncasecmp (host, buffer, host_domain - host))
1968 continue;
1971 return 0;
1974 /* end of .netrc */
1975 return -1;
1978 /* Extract login and password from .netrc for the host.
1979 * pass may be NULL.
1980 * Returns 0 for success, -1 for error */
1981 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass)
1983 char *netrcname;
1984 char *tmp_pass = NULL;
1985 char hostname[MAXHOSTNAMELEN];
1986 const char *domain;
1987 keyword_t keyword;
1988 static struct rupcache {
1989 struct rupcache *next;
1990 char *host;
1991 char *login;
1992 char *pass;
1993 } *rup_cache = NULL, *rupp;
1995 /* Initialize *login and *pass */
1996 if (!login)
1997 return 0;
1998 *login = NULL;
1999 if (pass)
2000 *pass = NULL;
2002 /* Look up in the cache first */
2003 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
2004 if (!strcmp (host, rupp->host)) {
2005 if (rupp->login)
2006 *login = g_strdup (rupp->login);
2007 if (pass && rupp->pass)
2008 *pass = g_strdup (rupp->pass);
2009 return 0;
2013 /* Load current .netrc */
2014 netrcname = concat_dir_and_file (home_dir, ".netrc");
2015 netrcp = netrc = load_file (netrcname);
2016 if (netrc == NULL) {
2017 g_free (netrcname);
2018 return 0;
2021 /* Find our own domain name */
2022 if (gethostname (hostname, sizeof (hostname)) < 0)
2023 *hostname = 0;
2024 if (!(domain = strchr (hostname, '.')))
2025 domain = "";
2027 /* Scan for "default" and matching "machine" keywords */
2028 ftpfs_find_machine (host, domain);
2030 /* Scan for keywords following "default" and "machine" */
2031 while (1) {
2032 int need_break = 0;
2033 keyword = ftpfs_netrc_next ();
2035 switch (keyword) {
2036 case NETRC_LOGIN:
2037 if (ftpfs_netrc_next () == NETRC_NONE) {
2038 need_break = 1;
2039 break;
2042 /* We have another name already - should not happen */
2043 if (*login) {
2044 need_break = 1;
2045 break;
2048 /* We have login name now */
2049 *login = g_strdup (buffer);
2050 break;
2052 case NETRC_PASSWORD:
2053 case NETRC_PASSWD:
2054 if (ftpfs_netrc_next () == NETRC_NONE) {
2055 need_break = 1;
2056 break;
2059 /* Ignore unsafe passwords */
2060 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2061 && ftpfs_netrc_bad_mode (netrcname)) {
2062 need_break = 1;
2063 break;
2066 /* Remember password. pass may be NULL, so use tmp_pass */
2067 if (tmp_pass == NULL)
2068 tmp_pass = g_strdup (buffer);
2069 break;
2071 case NETRC_ACCOUNT:
2072 /* "account" is followed by a token which we ignore */
2073 if (ftpfs_netrc_next () == NETRC_NONE) {
2074 need_break = 1;
2075 break;
2078 /* Ignore account, but warn user anyways */
2079 ftpfs_netrc_bad_mode (netrcname);
2080 break;
2082 default:
2083 /* Unexpected keyword or end of file */
2084 need_break = 1;
2085 break;
2088 if (need_break)
2089 break;
2092 g_free (netrc);
2093 g_free (netrcname);
2095 rupp = g_new (struct rupcache, 1);
2096 rupp->host = g_strdup (host);
2097 rupp->login = rupp->pass = 0;
2099 if (*login != NULL) {
2100 rupp->login = g_strdup (*login);
2102 if (tmp_pass != NULL)
2103 rupp->pass = g_strdup (tmp_pass);
2104 rupp->next = rup_cache;
2105 rup_cache = rupp;
2107 if (pass)
2108 *pass = tmp_pass;
2110 return 0;
2113 void
2114 init_ftpfs (void)
2116 static struct vfs_s_subclass ftpfs_subclass;
2118 ftpfs_subclass.flags = VFS_S_REMOTE;
2119 ftpfs_subclass.archive_same = ftpfs_archive_same;
2120 ftpfs_subclass.open_archive = ftpfs_open_archive;
2121 ftpfs_subclass.free_archive = ftpfs_free_archive;
2122 ftpfs_subclass.fh_open = ftpfs_fh_open;
2123 ftpfs_subclass.fh_close = ftpfs_fh_close;
2124 ftpfs_subclass.dir_load = ftpfs_dir_load;
2125 ftpfs_subclass.file_store = ftpfs_file_store;
2126 ftpfs_subclass.linear_start = ftpfs_linear_start;
2127 ftpfs_subclass.linear_read = ftpfs_linear_read;
2128 ftpfs_subclass.linear_close = ftpfs_linear_close;
2130 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2131 vfs_ftpfs_ops.name = "ftpfs";
2132 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2133 vfs_ftpfs_ops.prefix = "ftp:";
2134 vfs_ftpfs_ops.done = &ftpfs_done;
2135 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2136 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2137 vfs_ftpfs_ops.chown = ftpfs_chown;
2138 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2139 vfs_ftpfs_ops.rename = ftpfs_rename;
2140 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2141 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2142 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2143 vfs_register_class (&vfs_ftpfs_ops);