Ticket #121 (support IPv6).
[midnight-commander.git] / vfs / ftpfs.c
blob51a1f806a8814f84a316ce14dc0956b953272ab7
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 <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
69 #include <netdb.h> /* struct hostent */
70 #include <sys/socket.h> /* AF_INET */
71 #include <netinet/in.h> /* struct in_addr */
72 #ifdef HAVE_ARPA_INET_H
73 #include <arpa/inet.h>
74 #endif
75 #include <arpa/ftp.h>
76 #include <arpa/telnet.h>
77 #include <sys/param.h>
78 #include <errno.h>
79 #include <ctype.h>
80 #include <fcntl.h>
81 #include <sys/time.h> /* gettimeofday() */
83 #include "../src/global.h"
85 #include "../src/tty/tty.h" /* enable/disable interrupt key */
87 #include "../src/wtools.h" /* message() */
88 #include "../src/main.h" /* print_vfs_message */
89 #include "../src/history.h"
90 #include "../src/setup.h" /* for load_anon_passwd */
92 #include "utilvfs.h"
93 #include "xdirentry.h"
94 #include "vfs.h"
95 #include "vfs-impl.h"
96 #include "gc.h" /* vfs_stamp_create */
97 #include "tcputil.h"
98 #include "ftpfs.h"
99 #ifndef MAXHOSTNAMELEN
100 # define MAXHOSTNAMELEN 64
101 #endif
103 #define UPLOAD_ZERO_LENGTH_FILE
104 #define SUP super->u.ftp
105 #define FH_SOCK fh->u.ftp.sock
107 #ifndef INADDR_NONE
108 #define INADDR_NONE 0xffffffff
109 #endif
111 #define RFC_AUTODETECT 0
112 #define RFC_DARING 1
113 #define RFC_STRICT 2
115 #ifndef HAVE_SOCKLEN_T
116 typedef int socklen_t;
117 #endif
119 static int ftpfs_errno;
120 static int code;
122 /* Delay to retry a connection */
123 int ftpfs_retry_seconds = 30;
125 /* Method to use to connect to ftp sites */
126 int ftpfs_use_passive_connections = 1;
127 int ftpfs_use_passive_connections_over_proxy = 0;
129 /* Method used to get directory listings:
130 * 1: try 'LIST -la <path>', if it fails
131 * fall back to CWD <path>; LIST
132 * 0: always use CWD <path>; LIST
134 int ftpfs_use_unix_list_options = 1;
136 /* First "CWD <path>", then "LIST -la ." */
137 int ftpfs_first_cd_then_ls = 1;
139 /* Use the ~/.netrc */
140 int use_netrc = 1;
142 /* Anonymous setup */
143 char *ftpfs_anonymous_passwd = NULL;
144 int ftpfs_directory_timeout = 900;
146 /* Proxy host */
147 char *ftpfs_proxy_host = NULL;
149 /* wether we have to use proxy by default? */
150 int ftpfs_always_use_proxy;
152 #ifdef FIXME_LATER_ALIGATOR
153 static struct linklist *connections_list;
154 #endif
156 /* ftpfs_command wait_flag: */
157 #define NONE 0x00
158 #define WAIT_REPLY 0x01
159 #define WANT_STRING 0x02
160 static char reply_str [80];
162 static struct vfs_class vfs_ftpfs_ops;
164 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
165 Translate a Unix path, i.e. MC's internal path representation (e.g.
166 /somedir/somefile) to a path valid for the remote server. Every path
167 transfered to the remote server has to be mangled by this function
168 right prior to sending it.
169 Currently only Amiga ftp servers are handled in a special manner.
171 When the remote server is an amiga:
172 a) strip leading slash if necesarry
173 b) replace first occurance of ":/" with ":"
174 c) strip trailing "/."
177 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
178 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path);
179 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
180 __attribute__ ((format (__printf__, 4, 5)));
181 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
182 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass);
183 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
185 static char *
186 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
188 if (!SUP.remote_is_amiga)
189 return g_strdup (remote_path);
190 else {
191 char *ret, *p;
193 if (MEDATA->logfile) {
194 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
195 fflush (MEDATA->logfile);
198 /* strip leading slash(es) */
199 while (*remote_path == '/')
200 remote_path++;
203 * Don't change "/" into "", e.g. "CWD " would be
204 * invalid.
206 if (*remote_path == '\0')
207 return g_strdup (".");
209 ret = g_strdup (remote_path);
211 /* replace first occurance of ":/" with ":" */
212 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
213 strcpy (p + 1, p + 2);
215 /* strip trailing "/." */
216 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
217 *p = '\0';
218 return ret;
222 /* Extract the hostname and username from the path */
225 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
226 * ftp://sunsite.unc.edu/pub/linux
227 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
228 * ftp://tsx-11.mit.edu:8192/
229 * ftp://joe@foo.edu:11321/private
230 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
231 * is supplied.
235 #define FTP_COMMAND_PORT 21
237 static void
238 ftpfs_split_url(char *path, char **host, char **user, int *port, char **pass)
240 char *p;
242 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
243 URL_ALLOW_ANON);
245 if (!*user) {
246 /* Look up user and password in netrc */
247 if (use_netrc)
248 ftpfs_netrc_lookup (*host, user, pass);
249 if (!*user)
250 *user = g_strdup ("anonymous");
253 /* Look up password in netrc for known user */
254 if (use_netrc && *user && pass && !*pass) {
255 char *new_user;
257 ftpfs_netrc_lookup (*host, &new_user, pass);
259 /* If user is different, remove password */
260 if (new_user && strcmp (*user, new_user)) {
261 g_free (*pass);
262 *pass = NULL;
265 g_free (new_user);
268 g_free (p);
271 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
272 static int
273 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
275 char answer[BUF_1K];
276 int i;
278 for (;;) {
279 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
280 if (string_buf)
281 *string_buf = 0;
282 code = 421;
283 return 4;
285 switch (sscanf(answer, "%d", &code)){
286 case 0:
287 if (string_buf)
288 g_strlcpy (string_buf, answer, string_len);
289 code = 500;
290 return 5;
291 case 1:
292 if (answer[3] == '-') {
293 while (1) {
294 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
295 if (string_buf)
296 *string_buf = 0;
297 code = 421;
298 return 4;
300 if ((sscanf (answer, "%d", &i) > 0) &&
301 (code == i) && (answer[3] == ' '))
302 break;
305 if (string_buf)
306 g_strlcpy (string_buf, answer, string_len);
307 return code / 100;
312 static int
313 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
315 int sock = ftpfs_open_socket (me, super);
316 if (sock != -1){
317 char *cwdir = SUP.cwdir;
318 close (SUP.sock);
319 SUP.sock = sock;
320 SUP.cwdir = NULL;
321 if (ftpfs_login_server (me, super, SUP.password)){
322 if (!cwdir)
323 return 1;
324 sock = ftpfs_chdir_internal (me, super, cwdir);
325 g_free (cwdir);
326 return sock == COMPLETE;
328 SUP.cwdir = cwdir;
330 return 0;
333 static int
334 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
336 va_list ap;
337 char *cmdstr;
338 int status, cmdlen;
339 static int retry = 0;
340 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
342 va_start (ap, fmt);
343 cmdstr = g_strdup_vprintf (fmt, ap);
344 va_end (ap);
346 cmdlen = strlen (cmdstr);
347 cmdstr = g_realloc (cmdstr, cmdlen + 3);
348 strcpy (cmdstr + cmdlen, "\r\n");
349 cmdlen += 2;
351 if (MEDATA->logfile) {
352 if (strncmp (cmdstr, "PASS ", 5) == 0) {
353 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
354 } else
355 fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
357 fflush (MEDATA->logfile);
360 got_sigpipe = 0;
361 tty_enable_interrupt_key ();
362 status = write (SUP.sock, cmdstr, cmdlen);
364 if (status < 0) {
365 code = 421;
367 if (errno == EPIPE) { /* Remote server has closed connection */
368 if (level == 0) {
369 level = 1;
370 status = ftpfs_reconnect (me, super);
371 level = 0;
372 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
373 goto ok;
377 got_sigpipe = 1;
379 g_free (cmdstr);
380 tty_disable_interrupt_key ();
381 return TRANSIENT;
383 retry = 0;
385 tty_disable_interrupt_key ();
387 if (wait_reply)
389 status = ftpfs_get_reply (me, SUP.sock,
390 (wait_reply & WANT_STRING) ? reply_str : NULL,
391 sizeof (reply_str) - 1);
392 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
394 retry = 1;
395 level = 1;
396 status = ftpfs_reconnect (me, super);
397 level = 0;
398 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
399 goto ok;
402 retry = 0;
403 g_free (cmdstr);
404 return status;
406 g_free (cmdstr);
407 return COMPLETE;
410 static void
411 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
413 if (SUP.sock != -1){
414 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
415 ftpfs_command(me, super, NONE, "QUIT");
416 close(SUP.sock);
418 g_free (SUP.host);
419 g_free (SUP.user);
420 g_free (SUP.cwdir);
421 g_free (SUP.password);
424 /* some defines only used by ftpfs_changetype */
425 /* These two are valid values for the second parameter */
426 #define TYPE_ASCII 0
427 #define TYPE_BINARY 1
429 /* This one is only used to initialize bucket->isbinary, don't use it as
430 second parameter to ftpfs_changetype. */
431 #define TYPE_UNKNOWN -1
433 static int
434 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
436 if (binary != SUP.isbinary) {
437 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
438 ERRNOR (EIO, -1);
439 SUP.isbinary = binary;
441 return binary;
444 /* This routine logs the user in */
445 static int
446 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
447 const char *netrcpass)
449 char *pass;
450 char *op;
451 char *name; /* login user name */
452 int anon = 0;
453 char reply_string[BUF_MEDIUM];
455 SUP.isbinary = TYPE_UNKNOWN;
457 if (SUP.password) /* explicit password */
458 op = g_strdup (SUP.password);
459 else if (netrcpass) /* password from netrc */
460 op = g_strdup (netrcpass);
461 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp")) {
462 if (!ftpfs_anonymous_passwd) /* default anonymous password */
463 ftpfs_init_passwd ();
464 op = g_strdup (ftpfs_anonymous_passwd);
465 anon = 1;
466 } else { /* ask user */
467 char *p;
469 p = g_strconcat (_(" FTP: Password required for "), SUP.user, " ",
470 NULL);
471 op = vfs_get_password (p);
472 g_free (p);
473 if (op == NULL)
474 ERRNOR (EPERM, 0);
475 SUP.password = g_strdup (op);
478 if (!anon || MEDATA->logfile)
479 pass = op;
480 else {
481 pass = g_strconcat ("-", op, (char *) NULL);
482 wipe_password (op);
485 /* Proxy server accepts: username@host-we-want-to-connect */
486 if (SUP.proxy) {
487 name =
488 g_strconcat (SUP.user, "@",
489 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host,
490 NULL);
491 } else
492 name = g_strdup (SUP.user);
494 if (ftpfs_get_reply
495 (me, SUP.sock, reply_string,
496 sizeof (reply_string) - 1) == COMPLETE) {
497 g_strup (reply_string);
498 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
499 if (MEDATA->logfile) {
500 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n",
501 SUP.remote_is_amiga);
502 fflush (MEDATA->logfile);
505 print_vfs_message (_("ftpfs: sending login name"));
507 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name)) {
508 case CONTINUE:
509 print_vfs_message (_("ftpfs: sending user password"));
510 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
511 if (code == CONTINUE) {
512 char *p;
514 p = g_strdup_printf (_
515 ("FTP: Account required for user %s"),
516 SUP.user);
517 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
518 g_free (p);
519 if (op == NULL)
520 ERRNOR (EPERM, 0);
521 print_vfs_message (_("ftpfs: sending user account"));
522 code =
523 ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
524 g_free (op);
526 if (code != COMPLETE)
527 break;
528 /* fall through */
530 case COMPLETE:
531 print_vfs_message (_("ftpfs: logged in"));
532 wipe_password (pass);
533 g_free (name);
534 return 1;
536 default:
537 SUP.failed_on_login = 1;
538 if (SUP.password)
539 wipe_password (SUP.password);
540 SUP.password = 0;
542 goto login_fail;
545 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
546 SUP.user);
547 login_fail:
548 wipe_password (pass);
549 g_free (name);
550 ERRNOR (EPERM, 0);
553 static struct no_proxy_entry {
554 char *domain;
555 void *next;
556 } *no_proxy;
558 static void
559 ftpfs_load_no_proxy_list (void)
561 /* FixMe: shouldn't be hardcoded!!! */
562 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
563 struct no_proxy_entry *np, *current = 0;
564 FILE *npf;
565 int c;
566 char *p;
567 static char *mc_file;
569 if (mc_file)
570 return;
572 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
573 if (exist_file (mc_file) &&
574 (npf = fopen (mc_file, "r"))) {
575 while (fgets (s, sizeof (s), npf)) {
576 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
577 while ((c = fgetc (npf)) != EOF && c != '\n')
579 continue;
582 if (p == s)
583 continue;
585 *p = '\0';
587 np = g_new (struct no_proxy_entry, 1);
588 np->domain = g_strdup (s);
589 np->next = NULL;
590 if (no_proxy)
591 current->next = np;
592 else
593 no_proxy = np;
594 current = np;
597 fclose (npf);
599 g_free (mc_file);
602 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
603 static int
604 ftpfs_check_proxy (const char *host)
606 struct no_proxy_entry *npe;
608 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
609 return 0; /* sanity check */
611 if (*host == '!')
612 return 1;
614 if (!ftpfs_always_use_proxy)
615 return 0;
617 if (!strchr (host, '.'))
618 return 0;
620 ftpfs_load_no_proxy_list ();
621 for (npe = no_proxy; npe; npe=npe->next) {
622 char *domain = npe->domain;
624 if (domain[0] == '.') {
625 int ld = strlen (domain);
626 int lh = strlen (host);
628 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
629 ld--;
630 lh--;
633 if (!ld)
634 return 0;
635 } else
636 if (!g_strcasecmp (host, domain))
637 return 0;
640 return 1;
643 static void
644 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
646 char *user, *dir;
648 dir =
649 vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT,
650 URL_ALLOW_ANON);
651 g_free (user);
652 g_free (dir);
655 static int
656 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
658 struct addrinfo hints, *res, *curr_res;
659 int my_socket = 0;
660 char *host = NULL;
661 char *port = NULL;
662 int tmp_port;
663 int e;
665 (void) me;
667 /* Use a proxy host? */
668 host = g_strdup(SUP.host);
670 if (!host || !*host){
671 print_vfs_message (_("ftpfs: Invalid host name."));
672 ftpfs_errno = EINVAL;
673 return -1;
676 /* Hosts to connect to that start with a ! should use proxy */
677 tmp_port = SUP.port;
679 if (SUP.proxy){
680 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
683 port = g_strdup_printf("%hu", (unsigned short) tmp_port);
684 if (port == NULL) {
685 g_free (host);
686 ftpfs_errno = errno;
687 return -1;
690 tty_enable_interrupt_key(); /* clear the interrupt flag */
692 memset (&hints, 0, sizeof (struct addrinfo));
693 hints.ai_socktype = SOCK_STREAM;
694 hints.ai_flags = AI_ADDRCONFIG;
697 e = getaddrinfo (host, port, &hints, &res);
698 g_free (port);
699 port = NULL;
701 if ( e != 0 ) {
702 tty_disable_interrupt_key ();
703 print_vfs_message (_("ftpfs: %s"), gai_strerror (e));
704 g_free (host);
705 ftpfs_errno = EINVAL;
706 return -1;
709 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next) {
711 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
713 if (my_socket < 0) {
715 if (curr_res->ai_next != NULL)
716 continue;
718 tty_disable_interrupt_key();
719 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno));
720 g_free (host);
721 freeaddrinfo (res);
722 ftpfs_errno = errno;
723 return -1;
726 print_vfs_message (_("ftpfs: making connection to %s"), host);
727 g_free (host);
728 host = NULL;
730 if ( connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0 )
731 break;
733 ftpfs_errno = errno;
734 close (my_socket);
736 if (errno == EINTR && tty_got_interrupt ()) {
737 print_vfs_message (_("ftpfs: connection interrupted by user"));
738 } else if (res->ai_next == NULL) {
739 print_vfs_message (_("ftpfs: connection to server failed: %s"),
740 unix_error_string (errno));
741 } else {
742 continue;
745 freeaddrinfo (res);
746 tty_disable_interrupt_key ();
747 return -1;
750 freeaddrinfo (res);
751 tty_disable_interrupt_key ();
752 return my_socket;
755 static int
756 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
758 int retry_seconds, count_down;
760 /* We do not want to use the passive if we are using proxies */
761 if (SUP.proxy)
762 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
764 retry_seconds = 0;
765 do {
766 SUP.failed_on_login = 0;
768 SUP.sock = ftpfs_open_socket (me, super);
769 if (SUP.sock == -1)
770 return -1;
772 if (ftpfs_login_server (me, super, NULL)) {
773 /* Logged in, no need to retry the connection */
774 break;
775 } else {
776 if (SUP.failed_on_login){
777 /* Close only the socket descriptor */
778 close (SUP.sock);
779 } else {
780 return -1;
782 if (ftpfs_retry_seconds){
783 retry_seconds = ftpfs_retry_seconds;
784 tty_enable_interrupt_key ();
785 for (count_down = retry_seconds; count_down; count_down--){
786 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
787 sleep (1);
788 if (tty_got_interrupt ()) {
789 /* ftpfs_errno = E; */
790 tty_disable_interrupt_key ();
791 return 0;
794 tty_disable_interrupt_key ();
797 } while (retry_seconds);
799 SUP.cwdir = ftpfs_get_current_directory (me, super);
800 if (!SUP.cwdir)
801 SUP.cwdir = g_strdup (PATH_SEP_STR);
802 return 0;
805 static int
806 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
807 const char *archive_name, char *op)
809 char *host, *user, *password;
810 int port;
812 (void) archive_name;
814 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
816 SUP.host = host;
817 SUP.user = user;
818 SUP.port = port;
819 SUP.cwdir = NULL;
820 SUP.proxy = 0;
821 if (ftpfs_check_proxy (host))
822 SUP.proxy = ftpfs_proxy_host;
823 SUP.password = password;
824 SUP.use_passive_connection = ftpfs_use_passive_connections;
825 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
826 SUP.isbinary = TYPE_UNKNOWN;
827 SUP.remote_is_amiga = 0;
828 super->name = g_strdup ("/");
829 super->root =
830 vfs_s_new_inode (me, super,
831 vfs_s_default_stat (me, S_IFDIR | 0755));
833 return ftpfs_open_archive_int (me, super);
836 static int
837 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
838 const char *archive_name, char *op, void *cookie)
840 char *host, *user;
841 int port;
843 (void) me;
844 (void) archive_name;
845 (void) cookie;
847 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
849 port = ((strcmp (host, SUP.host) == 0)
850 && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
852 g_free (host);
853 g_free (user);
855 return port;
858 /* The returned directory should always contain a trailing slash */
859 static char *
860 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
862 char buf[BUF_8K], *bufp, *bufq;
864 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
865 ftpfs_get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
866 bufp = NULL;
867 for (bufq = buf; *bufq; bufq++)
868 if (*bufq == '"') {
869 if (!bufp) {
870 bufp = bufq + 1;
871 } else {
872 *bufq = 0;
873 if (*bufp) {
874 if (*(bufq - 1) != '/') {
875 *bufq++ = '/';
876 *bufq = 0;
878 if (*bufp == '/')
879 return g_strdup (bufp);
880 else {
881 /* If the remote server is an Amiga a leading slash
882 might be missing. MC needs it because it is used
883 as separator between hostname and path internally. */
884 return g_strconcat( "/", bufp, NULL);
886 } else {
887 ftpfs_errno = EIO;
888 return NULL;
893 ftpfs_errno = EIO;
894 return NULL;
898 /* Setup Passive ftp connection, we use it for source routed connections */
899 static int
900 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
901 int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
903 char *c;
905 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") == COMPLETE) {
906 int port;
907 /* (|||<port>|) */
908 c = strchr (reply_str, '|');
909 if (c == NULL)
910 return 0;
911 if(strlen(c) > 3)
912 c+=3;
913 else
914 return 0;
916 port = atoi (c);
917 if (port < 0 || port > 65535)
918 return 0;
919 port = htons (port);
921 switch (sa->ss_family) {
922 case AF_INET:
923 ((struct sockaddr_in *)sa)->sin_port = port;
924 break;
925 case AF_INET6:
926 ((struct sockaddr_in6 *)sa)->sin6_port = port;
927 break;
928 default:
929 print_vfs_message (_("ftpfs: invalid address family"));
930 ERRNOR (EINVAL, -1);
932 } else if (sa->ss_family == AF_INET) {
933 int xa, xb, xc, xd, xe, xf;
934 char n [6];
936 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
937 return 0;
939 /* Parse remote parameters */
940 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
942 if (!*c)
943 return 0;
944 if (!isdigit ((unsigned char) *c))
945 return 0;
946 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
947 return 0;
949 n [0] = (unsigned char) xa;
950 n [1] = (unsigned char) xb;
951 n [2] = (unsigned char) xc;
952 n [3] = (unsigned char) xd;
953 n [4] = (unsigned char) xe;
954 n [5] = (unsigned char) xf;
956 memcpy (&(((struct sockaddr_in *)sa)->sin_addr.s_addr), (void *)n, 4);
957 memcpy (&(((struct sockaddr_in *)sa)->sin_port), (void *)&n[4], 2);
958 } else
959 return 0;
961 if (connect (my_socket, (struct sockaddr *) sa, *salen ) < 0)
962 return 0;
964 return 1;
967 static int
968 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
970 struct sockaddr_storage data_addr;
971 socklen_t data_addrlen;
972 int data_sock;
974 again:
975 memset (&data_addr, 0, sizeof (struct sockaddr_storage));
976 data_addrlen = sizeof (struct sockaddr_storage);
978 if (getpeername (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen) == -1)
979 return -1;
981 switch (data_addr.ss_family) {
982 case AF_INET:
983 ((struct sockaddr_in *)&data_addr)->sin_port = 0;
984 break;
985 case AF_INET6:
986 ((struct sockaddr_in6 *)&data_addr)->sin6_port = 0;
987 break;
988 default:
989 print_vfs_message (_("ftpfs: invalid address family"));
990 ERRNOR(EINVAL, -1);
993 data_sock = socket (data_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
994 if (data_sock < 0) {
995 if (SUP.use_passive_connection) {
996 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno));
997 SUP.use_passive_connection = 0;
998 goto again;
1001 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1002 return -1;
1005 if (SUP.use_passive_connection) {
1007 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1008 return data_sock;
1010 SUP.use_passive_connection = 0;
1011 print_vfs_message (_("ftpfs: could not setup passive mode"));
1013 close (data_sock);
1014 goto again;
1017 /* If passive setup fails, fallback to active connections */
1018 /* Active FTP connection */
1019 if ((bind (data_sock, (struct sockaddr *)&data_addr, data_addrlen) == 0) &&
1020 (getsockname (data_sock, (struct sockaddr *)&data_addr, &data_addrlen) == 0) &&
1021 (listen (data_sock, 1) == 0)) {
1022 unsigned short int port;
1023 char *addr;
1024 unsigned int af;
1026 switch (data_addr.ss_family) {
1027 case AF_INET:
1028 af = FTP_INET;
1029 port = ((struct sockaddr_in *)&data_addr)->sin_port;
1030 break;
1031 case AF_INET6:
1032 af = FTP_INET6;
1033 port = ((struct sockaddr_in6 *)&data_addr)->sin6_port;
1034 break;
1035 default:
1036 print_vfs_message (_("ftpfs: invalid address family"));
1037 ERRNOR (EINVAL, -1);
1040 port = ntohs (port);
1042 addr = malloc (NI_MAXHOST);
1043 if (addr == NULL)
1044 ERRNOR (ENOMEM, -1);
1046 if (getnameinfo ((struct sockaddr *)&data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) {
1047 g_free (addr);
1048 ERRNOR (EIO, -1);
1051 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE) {
1052 g_free (addr);
1053 return data_sock;
1055 g_free (addr);
1057 if (FTP_INET == af) {
1058 unsigned char *a = (unsigned char *)&((struct sockaddr_in *)&data_addr)->sin_addr;
1059 unsigned char *p = (unsigned char *)&port;
1061 if (ftpfs_command (me, super, WAIT_REPLY,
1062 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1063 p[0], p[1]) == COMPLETE)
1064 return data_sock;
1067 close (data_sock);
1068 ftpfs_errno = EIO;
1069 return -1;
1072 static int
1073 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1074 const char *remote, int isbinary, int reget)
1076 struct sockaddr_storage from;
1077 int s, j, data;
1078 socklen_t fromlen = sizeof(from);
1080 if ((s = ftpfs_initconn (me, super)) == -1)
1081 return -1;
1082 if (ftpfs_changetype (me, super, isbinary) == -1)
1083 return -1;
1084 if (reget > 0){
1085 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1086 if (j != CONTINUE)
1087 return -1;
1089 if (remote) {
1090 char *remote_path = ftpfs_translate_path (me, super, remote);
1091 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1092 /* WarFtpD can't STORE //filename */
1093 (*remote_path == '/') ? remote_path + 1 : remote_path);
1094 g_free (remote_path);
1095 } else
1096 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1097 if (j != PRELIM)
1098 ERRNOR (EPERM, -1);
1099 tty_enable_interrupt_key ();
1100 if (SUP.use_passive_connection)
1101 data = s;
1102 else {
1103 data = accept (s, (struct sockaddr *)&from, &fromlen);
1104 if (data < 0) {
1105 ftpfs_errno = errno;
1106 close (s);
1107 return -1;
1109 close (s);
1111 tty_disable_interrupt_key ();
1112 return data;
1115 #define ABORT_TIMEOUT 5
1116 static void
1117 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
1119 struct vfs_s_super *super = FH_SUPER;
1120 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1121 fd_set mask;
1122 char buf[1024];
1123 int dsock = FH_SOCK;
1124 FH_SOCK = -1;
1125 SUP.ctl_connection_busy = 0;
1127 print_vfs_message (_("ftpfs: aborting transfer."));
1128 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
1129 print_vfs_message (_("ftpfs: abort error: %s"),
1130 unix_error_string (errno));
1131 if (dsock != -1)
1132 close (dsock);
1133 return;
1136 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
1137 print_vfs_message (_("ftpfs: abort failed"));
1138 if (dsock != -1)
1139 close (dsock);
1140 return;
1142 if (dsock != -1) {
1143 FD_ZERO (&mask);
1144 FD_SET (dsock, &mask);
1145 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1146 struct timeval start_tim, tim;
1147 gettimeofday (&start_tim, NULL);
1148 /* flush the remaining data */
1149 while (read (dsock, buf, sizeof (buf)) > 0) {
1150 gettimeofday (&tim, NULL);
1151 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1152 /* server keeps sending, drop the connection and ftpfs_reconnect */
1153 close (dsock);
1154 ftpfs_reconnect (me, super);
1155 return;
1159 close (dsock);
1161 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1162 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1165 #if 0
1166 static void
1167 resolve_symlink_without_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1169 struct linklist *flist;
1170 struct direntry *fe, *fel;
1171 char tmp[MC_MAXPATHLEN];
1172 int depth;
1174 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1175 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1176 /* flist->data->l_stat is alread initialized with 0 */
1177 fel = flist->data;
1178 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1179 if (fel->linkname[0] == '/') {
1180 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1181 continue;
1182 strcpy (tmp, fel->linkname);
1183 } else {
1184 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1185 continue;
1186 strcpy (tmp, dir->remote_path);
1187 if (tmp[1] != '\0')
1188 strcat (tmp, "/");
1189 strcat (tmp + 1, fel->linkname);
1191 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1192 canonicalize_pathname (tmp);
1193 fe = _get_file_entry(bucket, tmp, 0, 0);
1194 if (fe) {
1195 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1196 /* Symlink points to link which isn't resolved, yet. */
1197 if (fe->linkname[0] == '/') {
1198 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1199 break;
1200 strcpy (tmp, fe->linkname);
1201 } else {
1202 /* at this point tmp looks always like this
1203 /directory/filename, i.e. no need to check
1204 strrchr's return value */
1205 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1206 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1207 break;
1208 strcat (tmp, fe->linkname);
1210 continue;
1211 } else {
1212 fel->l_stat = g_new (struct stat, 1);
1213 if ( S_ISLNK (fe->s.st_mode))
1214 *fel->l_stat = *fe->l_stat;
1215 else
1216 *fel->l_stat = fe->s;
1217 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1220 break;
1224 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1227 static void
1228 resolve_symlink_with_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1230 char buffer[2048] = "", *filename;
1231 int sock;
1232 FILE *fp;
1233 struct stat s;
1234 struct linklist *flist;
1235 struct direntry *fe;
1236 int switch_method = 0;
1238 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1239 if (strchr (dir->remote_path, ' ')) {
1240 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1241 print_vfs_message(_("ftpfs: CWD failed."));
1242 return;
1244 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1246 else
1247 sock = ftpfs_open_data_connection (bucket, "LIST -lLa",
1248 dir->remote_path, TYPE_ASCII, 0);
1250 if (sock == -1) {
1251 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1252 return;
1255 fp = fdopen(sock, "r");
1256 if (fp == NULL) {
1257 close(sock);
1258 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1259 return;
1261 tty_enable_interrupt_key ();
1262 flist = dir->file_list->next;
1263 while (1) {
1264 do {
1265 if (flist == dir->file_list)
1266 goto done;
1267 fe = flist->data;
1268 flist = flist->next;
1269 } while (!S_ISLNK(fe->s.st_mode));
1270 while (1) {
1271 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1272 goto done;
1273 if (MEDATA->logfile){
1274 fputs (buffer, MEDATA->logfile);
1275 fflush (MEDATA->logfile);
1277 vfs_die("This code should be commented out\n");
1278 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1279 int r = strcmp(fe->name, filename);
1280 g_free(filename);
1281 if (r == 0) {
1282 if (S_ISLNK (s.st_mode)) {
1283 /* This server doesn't understand LIST -lLa */
1284 switch_method = 1;
1285 goto done;
1287 fe->l_stat = g_new (struct stat, 1);
1288 if (fe->l_stat == NULL)
1289 goto done;
1290 *fe->l_stat = s;
1291 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1292 break;
1294 if (r < 0)
1295 break;
1299 done:
1300 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1301 tty_disable_interrupt_key ();
1302 fclose(fp);
1303 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1306 static void
1307 resolve_symlink(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1309 print_vfs_message(_("Resolving symlink..."));
1311 if (SUP.strict_rfc959_list_cmd)
1312 resolve_symlink_without_ls_options(me, super, dir);
1313 else
1314 resolve_symlink_with_ls_options(me, super, dir);
1316 #endif
1318 static int
1319 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1321 struct vfs_s_entry *ent;
1322 struct vfs_s_super *super = dir->super;
1323 int sock, num_entries = 0;
1324 char buffer[BUF_8K];
1325 int cd_first;
1327 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1328 || (strchr (remote_path, ' ') != NULL);
1330 again:
1331 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1332 remote_path,
1333 SUP.strict ==
1334 RFC_STRICT ? _("(strict rfc959)") : "",
1335 cd_first ? _("(chdir first)") : "");
1337 if (cd_first) {
1338 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE) {
1339 ftpfs_errno = ENOENT;
1340 print_vfs_message (_("ftpfs: CWD failed."));
1341 return -1;
1345 gettimeofday (&dir->timestamp, NULL);
1346 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1348 if (SUP.strict == RFC_STRICT)
1349 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1350 else if (cd_first)
1351 /* Dirty hack to avoid autoprepending / to . */
1352 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1353 sock =
1354 ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1355 else {
1356 /* Trailing "/." is necessary if remote_path is a symlink */
1357 char *path = concat_dir_and_file (remote_path, ".");
1358 sock =
1359 ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII,
1361 g_free (path);
1364 if (sock == -1)
1365 goto fallback;
1367 /* Clear the interrupt flag */
1368 tty_enable_interrupt_key ();
1370 while (1) {
1371 int i;
1372 int res =
1373 vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1374 sock);
1375 if (!res)
1376 break;
1378 if (res == EINTR) {
1379 me->verrno = ECONNRESET;
1380 close (sock);
1381 tty_disable_interrupt_key ();
1382 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1383 print_vfs_message (_("%s: failure"), me->name);
1384 return -1;
1387 if (MEDATA->logfile) {
1388 fputs (buffer, MEDATA->logfile);
1389 fputs ("\n", MEDATA->logfile);
1390 fflush (MEDATA->logfile);
1393 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1394 i = ent->ino->st.st_nlink;
1395 if (!vfs_parse_ls_lga
1396 (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1397 vfs_s_free_entry (me, ent);
1398 continue;
1400 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1401 num_entries++;
1402 vfs_s_insert_entry (me, dir, ent);
1405 close (sock);
1406 me->verrno = E_REMOTE;
1407 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1408 goto fallback;
1410 if (num_entries == 0 && cd_first == 0) {
1411 /* The LIST command may produce an empty output. In such scenario
1412 it is not clear whether this is caused by `remote_path' being
1413 a non-existent path or for some other reason (listing emtpy
1414 directory without the -a option, non-readable directory, etc.).
1416 Since `dir_load' is a crucial method, when it comes to determine
1417 whether a given path is a _directory_, the code must try its best
1418 to determine the type of `remote_path'. The only reliable way to
1419 achieve this is trough issuing a CWD command. */
1421 cd_first = 1;
1422 goto again;
1425 if (SUP.strict == RFC_AUTODETECT)
1426 SUP.strict = RFC_DARING;
1428 print_vfs_message (_("%s: done."), me->name);
1429 return 0;
1431 fallback:
1432 if (SUP.strict == RFC_AUTODETECT) {
1433 /* It's our first attempt to get a directory listing from this
1434 server (UNIX style LIST command) */
1435 SUP.strict = RFC_STRICT;
1436 /* I hate goto, but recursive call needs another 8K on stack */
1437 /* return ftpfs_dir_load (me, dir, remote_path); */
1438 cd_first = 1;
1439 goto again;
1441 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1442 ERRNOR (EACCES, -1);
1445 static int
1446 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name,
1447 char *localname)
1449 int h, sock, n_read, n_written;
1450 off_t n_stored;
1451 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1452 struct linger li;
1453 #else
1454 int flag_one = 1;
1455 #endif
1456 char buffer[8192];
1457 struct stat s;
1458 char *w_buf;
1459 struct vfs_s_super *super = FH_SUPER;
1461 h = open (localname, O_RDONLY);
1462 if (h == -1)
1463 ERRNOR (EIO, -1);
1464 sock =
1465 ftpfs_open_data_connection (me, super,
1466 fh->u.ftp.append ? "APPE" : "STOR", name,
1467 TYPE_BINARY, 0);
1468 if (sock < 0 || fstat (h, &s) == -1) {
1469 close (h);
1470 return -1;
1472 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1473 li.l_onoff = 1;
1474 li.l_linger = 120;
1475 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1476 #else
1477 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1478 #endif
1479 n_stored = 0;
1481 tty_enable_interrupt_key ();
1482 while (1) {
1483 while ((n_read = read (h, buffer, sizeof (buffer))) == -1) {
1484 if (errno == EINTR) {
1485 if (tty_got_interrupt ()) {
1486 ftpfs_errno = EINTR;
1487 goto error_return;
1488 } else
1489 continue;
1491 ftpfs_errno = errno;
1492 goto error_return;
1494 if (n_read == 0)
1495 break;
1496 n_stored += n_read;
1497 w_buf = buffer;
1498 while ((n_written = write (sock, w_buf, n_read)) != n_read) {
1499 if (n_written == -1) {
1500 if (errno == EINTR && !tty_got_interrupt ()) {
1501 continue;
1503 ftpfs_errno = errno;
1504 goto error_return;
1506 w_buf += n_written;
1507 n_read -= n_written;
1509 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1510 (unsigned long) n_stored, (unsigned long) s.st_size);
1512 tty_disable_interrupt_key ();
1513 close (sock);
1514 close (h);
1515 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1516 ERRNOR (EIO, -1);
1517 return 0;
1518 error_return:
1519 tty_disable_interrupt_key ();
1520 close (sock);
1521 close (h);
1522 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1523 return -1;
1526 static int
1527 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1529 char *name = vfs_s_fullpath (me, fh->ino);
1531 if (!name)
1532 return 0;
1533 FH_SOCK = ftpfs_open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1534 g_free (name);
1535 if (FH_SOCK == -1)
1536 ERRNOR (EACCES, 0);
1537 fh->linear = LS_LINEAR_OPEN;
1538 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1539 fh->u.ftp.append = 0;
1540 return 1;
1543 static int
1544 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1546 int n;
1547 struct vfs_s_super *super = FH_SUPER;
1549 while ((n = read (FH_SOCK, buf, len))<0) {
1550 if ((errno == EINTR) && !tty_got_interrupt ())
1551 continue;
1552 break;
1555 if (n<0)
1556 ftpfs_linear_abort(me, fh);
1558 if (!n) {
1559 SUP.ctl_connection_busy = 0;
1560 close (FH_SOCK);
1561 FH_SOCK = -1;
1562 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1563 ERRNOR (E_REMOTE, -1);
1564 return 0;
1566 ERRNOR (errno, n);
1569 static void
1570 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1572 if (FH_SOCK != -1)
1573 ftpfs_linear_abort(me, fh);
1576 static int ftpfs_ctl (void *fh, int ctlop, void *arg)
1578 (void) arg;
1580 switch (ctlop) {
1581 case VFS_CTL_IS_NOTREADY:
1583 int v;
1585 if (!FH->linear)
1586 vfs_die ("You may not do this");
1587 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1588 return 0;
1590 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1591 if (((v < 0) && (errno == EINTR)) || v == 0)
1592 return 1;
1593 return 0;
1595 default:
1596 return 0;
1600 static int
1601 ftpfs_send_command(struct vfs_class *me, const char *filename, const char *cmd, int flags)
1603 const char *rpath;
1604 char *p, *mpath = g_strdup(filename);
1605 struct vfs_s_super *super;
1606 int r;
1607 int flush_directory_cache = (flags & OPT_FLUSH);
1609 if (!(rpath = vfs_s_get_path_mangle(me, mpath, &super, 0))) {
1610 g_free(mpath);
1611 return -1;
1613 p = ftpfs_translate_path (me, super, rpath);
1614 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1615 g_free (p);
1616 vfs_stamp_create (&vfs_ftpfs_ops, super);
1617 if (flags & OPT_IGNORE_ERROR)
1618 r = COMPLETE;
1619 if (r != COMPLETE) {
1620 me->verrno = EPERM;
1621 g_free (mpath);
1622 return -1;
1624 if (flush_directory_cache)
1625 vfs_s_invalidate(me, super);
1626 g_free(mpath);
1627 return 0;
1630 /* This routine is called as the last step in load_setup */
1631 void
1632 ftpfs_init_passwd(void)
1634 ftpfs_anonymous_passwd = load_anon_passwd ();
1635 if (ftpfs_anonymous_passwd)
1636 return;
1638 /* If there is no anonymous ftp password specified
1639 * then we'll just use anonymous@
1640 * We don't send any other thing because:
1641 * - We want to remain anonymous
1642 * - We want to stop SPAM
1643 * - We don't want to let ftp sites to discriminate by the user,
1644 * host or country.
1646 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1649 static int ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1651 char buf[BUF_SMALL];
1653 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1654 return ftpfs_send_command(me, path, buf, OPT_FLUSH);
1657 static int ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1659 #if 0
1660 ftpfs_errno = EPERM;
1661 return -1;
1662 #else
1663 /* Everyone knows it is not possible to chown remotely, so why bother them.
1664 If someone's root, then copy/move will always try to chown it... */
1665 (void) me;
1666 (void) path;
1667 (void) owner;
1668 (void) group;
1669 return 0;
1670 #endif
1673 static int ftpfs_unlink (struct vfs_class *me, const char *path)
1675 return ftpfs_send_command(me, path, "DELE /%s", OPT_FLUSH);
1678 /* Return 1 if path is the same directory as the one we are in now */
1679 static int
1680 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1682 (void) me;
1684 if (!SUP.cwdir)
1685 return 0;
1686 if (strcmp (path, SUP.cwdir) == 0)
1687 return 1;
1688 return 0;
1691 static int
1692 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1694 int r;
1695 char *p;
1697 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1698 return COMPLETE;
1700 p = ftpfs_translate_path (me, super, remote_path);
1701 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1702 g_free (p);
1704 if (r != COMPLETE) {
1705 ftpfs_errno = EIO;
1706 } else {
1707 g_free(SUP.cwdir);
1708 SUP.cwdir = g_strdup (remote_path);
1709 SUP.cwd_deferred = 0;
1711 return r;
1714 static int ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1716 ftpfs_send_command(me, path1, "RNFR /%s", OPT_FLUSH);
1717 return ftpfs_send_command(me, path2, "RNTO /%s", OPT_FLUSH);
1720 static int ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1722 (void) mode; /* FIXME: should be used */
1724 return ftpfs_send_command(me, path, "MKD /%s", OPT_FLUSH);
1727 static int ftpfs_rmdir (struct vfs_class *me, const char *path)
1729 return ftpfs_send_command(me, path, "RMD /%s", OPT_FLUSH);
1732 static int
1733 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
1734 int mode)
1736 (void) mode;
1738 fh->u.ftp.append = 0;
1739 /* File will be written only, so no need to retrieve it from ftp server */
1740 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
1741 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1742 struct linger li;
1743 #else
1744 int li = 1;
1745 #endif
1746 char *name;
1748 /* ftpfs_linear_start() called, so data will be written
1749 * to local temporary file and stored to ftp server
1750 * by vfs_s_close later
1752 if (FH_SUPER->u.ftp.ctl_connection_busy) {
1753 if (!fh->ino->localname) {
1754 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1755 fh->ino->ent->name);
1756 if (handle == -1)
1757 return -1;
1758 close (handle);
1759 fh->u.ftp.append = flags & O_APPEND;
1761 return 0;
1763 name = vfs_s_fullpath (me, fh->ino);
1764 if (!name)
1765 return -1;
1766 fh->handle =
1767 ftpfs_open_data_connection (me, fh->ino->super,
1768 (flags & O_APPEND) ? "APPE" :
1769 "STOR", name, TYPE_BINARY, 0);
1770 g_free (name);
1772 if (fh->handle < 0)
1773 return -1;
1774 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1775 li.l_onoff = 1;
1776 li.l_linger = 120;
1777 #endif
1778 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1780 if (fh->ino->localname) {
1781 unlink (fh->ino->localname);
1782 g_free (fh->ino->localname);
1783 fh->ino->localname = NULL;
1785 return 0;
1788 if (!fh->ino->localname)
1789 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1790 return -1;
1791 if (!fh->ino->localname)
1792 vfs_die ("retrieve_file failed to fill in localname");
1793 return 0;
1796 static int ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1798 if (fh->handle != -1 && !fh->ino->localname){
1799 close (fh->handle);
1800 fh->handle = -1;
1801 /* File is stored to destination already, so
1802 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1804 fh->changed = 0;
1805 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1806 ERRNOR (EIO, -1);
1807 vfs_s_invalidate (me, FH_SUPER);
1809 return 0;
1812 static void
1813 ftpfs_done (struct vfs_class *me)
1815 struct no_proxy_entry *np;
1817 (void) me;
1819 while (no_proxy) {
1820 np = no_proxy->next;
1821 g_free (no_proxy->domain);
1822 g_free (no_proxy);
1823 no_proxy = np;
1825 g_free (ftpfs_anonymous_passwd);
1826 g_free (ftpfs_proxy_host);
1829 static void
1830 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
1832 struct vfs_s_super *super = MEDATA->supers;
1833 char *name;
1835 while (super){
1836 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
1837 (*func)(name);
1838 g_free (name);
1839 super = super->next;
1843 static char buffer[BUF_MEDIUM];
1844 static char *netrc;
1845 static const char *netrcp;
1847 /* This should match the keywords[] array below */
1848 typedef enum {
1849 NETRC_NONE = 0,
1850 NETRC_DEFAULT,
1851 NETRC_MACHINE,
1852 NETRC_LOGIN,
1853 NETRC_PASSWORD,
1854 NETRC_PASSWD,
1855 NETRC_ACCOUNT,
1856 NETRC_MACDEF,
1857 NETRC_UNKNOWN
1858 } keyword_t;
1860 static keyword_t ftpfs_netrc_next (void)
1862 char *p;
1863 keyword_t i;
1864 static const char *const keywords[] = { "default", "machine",
1865 "login", "password", "passwd", "account", "macdef", NULL
1869 while (1) {
1870 netrcp = skip_separators (netrcp);
1871 if (*netrcp != '\n')
1872 break;
1873 netrcp++;
1875 if (!*netrcp)
1876 return NETRC_NONE;
1877 p = buffer;
1878 if (*netrcp == '"') {
1879 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1880 if (*netrcp == '\\')
1881 netrcp++;
1882 *p++ = *netrcp;
1884 } else {
1885 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1886 *netrcp != ',' && *netrcp; netrcp++) {
1887 if (*netrcp == '\\')
1888 netrcp++;
1889 *p++ = *netrcp;
1892 *p = 0;
1893 if (!*buffer)
1894 return NETRC_NONE;
1896 i = NETRC_DEFAULT;
1897 while (keywords[i - 1]) {
1898 if (!strcmp (keywords[i - 1], buffer))
1899 return i;
1901 i++;
1904 return NETRC_UNKNOWN;
1907 static int ftpfs_netrc_bad_mode (const char *netrcname)
1909 static int be_angry = 1;
1910 struct stat mystat;
1912 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1913 if (be_angry) {
1914 message (D_ERROR, MSG_ERROR,
1915 _("~/.netrc file has incorrect mode.\n"
1916 "Remove password or correct mode."));
1917 be_angry = 0;
1919 return 1;
1921 return 0;
1924 /* Scan .netrc until we find matching "machine" or "default"
1925 * domain is used for additional matching
1926 * No search is done after "default" in compliance with "man netrc"
1927 * Return 0 if found, -1 otherwise */
1928 static int ftpfs_find_machine (const char *host, const char *domain)
1930 keyword_t keyword;
1932 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) {
1933 if (keyword == NETRC_DEFAULT)
1934 return 0;
1936 if (keyword == NETRC_MACDEF) {
1937 /* Scan for an empty line, which concludes "macdef" */
1938 do {
1939 while (*netrcp && *netrcp != '\n')
1940 netrcp++;
1941 if (*netrcp != '\n')
1942 break;
1943 netrcp++;
1944 } while (*netrcp && *netrcp != '\n');
1945 continue;
1948 if (keyword != NETRC_MACHINE)
1949 continue;
1951 /* Take machine name */
1952 if (ftpfs_netrc_next () == NETRC_NONE)
1953 break;
1955 if (g_strcasecmp (host, buffer)) {
1956 /* Try adding our domain to short names in .netrc */
1957 const char *host_domain = strchr (host, '.');
1958 if (!host_domain)
1959 continue;
1961 /* Compare domain part */
1962 if (g_strcasecmp (host_domain, domain))
1963 continue;
1965 /* Compare local part */
1966 if (g_strncasecmp (host, buffer, host_domain - host))
1967 continue;
1970 return 0;
1973 /* end of .netrc */
1974 return -1;
1977 /* Extract login and password from .netrc for the host.
1978 * pass may be NULL.
1979 * Returns 0 for success, -1 for error */
1980 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass)
1982 char *netrcname;
1983 char *tmp_pass = NULL;
1984 char hostname[MAXHOSTNAMELEN];
1985 const char *domain;
1986 keyword_t keyword;
1987 static struct rupcache {
1988 struct rupcache *next;
1989 char *host;
1990 char *login;
1991 char *pass;
1992 } *rup_cache = NULL, *rupp;
1994 /* Initialize *login and *pass */
1995 if (!login)
1996 return 0;
1997 *login = NULL;
1998 if (pass)
1999 *pass = NULL;
2001 /* Look up in the cache first */
2002 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
2003 if (!strcmp (host, rupp->host)) {
2004 if (rupp->login)
2005 *login = g_strdup (rupp->login);
2006 if (pass && rupp->pass)
2007 *pass = g_strdup (rupp->pass);
2008 return 0;
2012 /* Load current .netrc */
2013 netrcname = concat_dir_and_file (home_dir, ".netrc");
2014 netrcp = netrc = load_file (netrcname);
2015 if (netrc == NULL) {
2016 g_free (netrcname);
2017 return 0;
2020 /* Find our own domain name */
2021 if (gethostname (hostname, sizeof (hostname)) < 0)
2022 *hostname = 0;
2023 if (!(domain = strchr (hostname, '.')))
2024 domain = "";
2026 /* Scan for "default" and matching "machine" keywords */
2027 ftpfs_find_machine (host, domain);
2029 /* Scan for keywords following "default" and "machine" */
2030 while (1) {
2031 int need_break = 0;
2032 keyword = ftpfs_netrc_next ();
2034 switch (keyword) {
2035 case NETRC_LOGIN:
2036 if (ftpfs_netrc_next () == NETRC_NONE) {
2037 need_break = 1;
2038 break;
2041 /* We have another name already - should not happen */
2042 if (*login) {
2043 need_break = 1;
2044 break;
2047 /* We have login name now */
2048 *login = g_strdup (buffer);
2049 break;
2051 case NETRC_PASSWORD:
2052 case NETRC_PASSWD:
2053 if (ftpfs_netrc_next () == NETRC_NONE) {
2054 need_break = 1;
2055 break;
2058 /* Ignore unsafe passwords */
2059 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2060 && ftpfs_netrc_bad_mode (netrcname)) {
2061 need_break = 1;
2062 break;
2065 /* Remember password. pass may be NULL, so use tmp_pass */
2066 if (tmp_pass == NULL)
2067 tmp_pass = g_strdup (buffer);
2068 break;
2070 case NETRC_ACCOUNT:
2071 /* "account" is followed by a token which we ignore */
2072 if (ftpfs_netrc_next () == NETRC_NONE) {
2073 need_break = 1;
2074 break;
2077 /* Ignore account, but warn user anyways */
2078 ftpfs_netrc_bad_mode (netrcname);
2079 break;
2081 default:
2082 /* Unexpected keyword or end of file */
2083 need_break = 1;
2084 break;
2087 if (need_break)
2088 break;
2091 g_free (netrc);
2092 g_free (netrcname);
2094 rupp = g_new (struct rupcache, 1);
2095 rupp->host = g_strdup (host);
2096 rupp->login = rupp->pass = 0;
2098 if (*login != NULL) {
2099 rupp->login = g_strdup (*login);
2101 if (tmp_pass != NULL)
2102 rupp->pass = g_strdup (tmp_pass);
2103 rupp->next = rup_cache;
2104 rup_cache = rupp;
2106 if (pass)
2107 *pass = tmp_pass;
2109 return 0;
2112 void
2113 init_ftpfs (void)
2115 static struct vfs_s_subclass ftpfs_subclass;
2117 ftpfs_subclass.flags = VFS_S_REMOTE;
2118 ftpfs_subclass.archive_same = ftpfs_archive_same;
2119 ftpfs_subclass.open_archive = ftpfs_open_archive;
2120 ftpfs_subclass.free_archive = ftpfs_free_archive;
2121 ftpfs_subclass.fh_open = ftpfs_fh_open;
2122 ftpfs_subclass.fh_close = ftpfs_fh_close;
2123 ftpfs_subclass.dir_load = ftpfs_dir_load;
2124 ftpfs_subclass.file_store = ftpfs_file_store;
2125 ftpfs_subclass.linear_start = ftpfs_linear_start;
2126 ftpfs_subclass.linear_read = ftpfs_linear_read;
2127 ftpfs_subclass.linear_close = ftpfs_linear_close;
2129 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2130 vfs_ftpfs_ops.name = "ftpfs";
2131 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2132 vfs_ftpfs_ops.prefix = "ftp:";
2133 vfs_ftpfs_ops.done = &ftpfs_done;
2134 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2135 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2136 vfs_ftpfs_ops.chown = ftpfs_chown;
2137 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2138 vfs_ftpfs_ops.rename = ftpfs_rename;
2139 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2140 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2141 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2142 vfs_register_class (&vfs_ftpfs_ops);