Makefile.am: Fixed incorrect variable name if maintainer mode is active.
[midnight-commander.git] / vfs / ftpfs.c
blob846890a40f7bf998807e903034d25e8d313e60ce
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 /* for uclibc < 0.9.29 */
113 #ifndef AI_ADDRCONFIG
114 #define AI_ADDRCONFIG 0x0020
115 #endif
117 #define RFC_AUTODETECT 0
118 #define RFC_DARING 1
119 #define RFC_STRICT 2
121 #ifndef HAVE_SOCKLEN_T
122 typedef int socklen_t;
123 #endif
125 static int ftpfs_errno;
126 static int code;
128 /* Delay to retry a connection */
129 int ftpfs_retry_seconds = 30;
131 /* Method to use to connect to ftp sites */
132 int ftpfs_use_passive_connections = 1;
133 int ftpfs_use_passive_connections_over_proxy = 0;
135 /* Method used to get directory listings:
136 * 1: try 'LIST -la <path>', if it fails
137 * fall back to CWD <path>; LIST
138 * 0: always use CWD <path>; LIST
140 int ftpfs_use_unix_list_options = 1;
142 /* First "CWD <path>", then "LIST -la ." */
143 int ftpfs_first_cd_then_ls = 1;
145 /* Use the ~/.netrc */
146 int use_netrc = 1;
148 /* Anonymous setup */
149 char *ftpfs_anonymous_passwd = NULL;
150 int ftpfs_directory_timeout = 900;
152 /* Proxy host */
153 char *ftpfs_proxy_host = NULL;
155 /* wether we have to use proxy by default? */
156 int ftpfs_always_use_proxy;
158 #ifdef FIXME_LATER_ALIGATOR
159 static struct linklist *connections_list;
160 #endif
162 /* ftpfs_command wait_flag: */
163 #define NONE 0x00
164 #define WAIT_REPLY 0x01
165 #define WANT_STRING 0x02
166 static char reply_str [80];
168 static struct vfs_class vfs_ftpfs_ops;
170 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
171 Translate a Unix path, i.e. MC's internal path representation (e.g.
172 /somedir/somefile) to a path valid for the remote server. Every path
173 transfered to the remote server has to be mangled by this function
174 right prior to sending it.
175 Currently only Amiga ftp servers are handled in a special manner.
177 When the remote server is an amiga:
178 a) strip leading slash if necesarry
179 b) replace first occurance of ":/" with ":"
180 c) strip trailing "/."
183 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
184 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path);
185 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
186 __attribute__ ((format (__printf__, 4, 5)));
187 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
188 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass);
189 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
191 static char *
192 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
194 if (!SUP.remote_is_amiga)
195 return g_strdup (remote_path);
196 else {
197 char *ret, *p;
199 if (MEDATA->logfile) {
200 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
201 fflush (MEDATA->logfile);
204 /* strip leading slash(es) */
205 while (*remote_path == '/')
206 remote_path++;
209 * Don't change "/" into "", e.g. "CWD " would be
210 * invalid.
212 if (*remote_path == '\0')
213 return g_strdup (".");
215 ret = g_strdup (remote_path);
217 /* replace first occurance of ":/" with ":" */
218 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
219 strcpy (p + 1, p + 2);
221 /* strip trailing "/." */
222 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
223 *p = '\0';
224 return ret;
228 /* Extract the hostname and username from the path */
231 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
232 * ftp://sunsite.unc.edu/pub/linux
233 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
234 * ftp://tsx-11.mit.edu:8192/
235 * ftp://joe@foo.edu:11321/private
236 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
237 * is supplied.
241 #define FTP_COMMAND_PORT 21
243 static void
244 ftpfs_split_url(char *path, char **host, char **user, int *port, char **pass)
246 char *p;
248 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
249 URL_ALLOW_ANON);
251 if (!*user) {
252 /* Look up user and password in netrc */
253 if (use_netrc)
254 ftpfs_netrc_lookup (*host, user, pass);
255 if (!*user)
256 *user = g_strdup ("anonymous");
259 /* Look up password in netrc for known user */
260 if (use_netrc && *user && pass && !*pass) {
261 char *new_user;
263 ftpfs_netrc_lookup (*host, &new_user, pass);
265 /* If user is different, remove password */
266 if (new_user && strcmp (*user, new_user)) {
267 g_free (*pass);
268 *pass = NULL;
271 g_free (new_user);
274 g_free (p);
277 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
278 static int
279 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
281 char answer[BUF_1K];
282 int i;
284 for (;;) {
285 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
286 if (string_buf)
287 *string_buf = 0;
288 code = 421;
289 return 4;
291 switch (sscanf(answer, "%d", &code)){
292 case 0:
293 if (string_buf)
294 g_strlcpy (string_buf, answer, string_len);
295 code = 500;
296 return 5;
297 case 1:
298 if (answer[3] == '-') {
299 while (1) {
300 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
301 if (string_buf)
302 *string_buf = 0;
303 code = 421;
304 return 4;
306 if ((sscanf (answer, "%d", &i) > 0) &&
307 (code == i) && (answer[3] == ' '))
308 break;
311 if (string_buf)
312 g_strlcpy (string_buf, answer, string_len);
313 return code / 100;
318 static int
319 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
321 int sock = ftpfs_open_socket (me, super);
322 if (sock != -1){
323 char *cwdir = SUP.cwdir;
324 close (SUP.sock);
325 SUP.sock = sock;
326 SUP.cwdir = NULL;
327 if (ftpfs_login_server (me, super, SUP.password)){
328 if (!cwdir)
329 return 1;
330 sock = ftpfs_chdir_internal (me, super, cwdir);
331 g_free (cwdir);
332 return sock == COMPLETE;
334 SUP.cwdir = cwdir;
336 return 0;
339 static int
340 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
342 va_list ap;
343 char *cmdstr;
344 int status, cmdlen;
345 static int retry = 0;
346 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
348 va_start (ap, fmt);
349 cmdstr = g_strdup_vprintf (fmt, ap);
350 va_end (ap);
352 cmdlen = strlen (cmdstr);
353 cmdstr = g_realloc (cmdstr, cmdlen + 3);
354 strcpy (cmdstr + cmdlen, "\r\n");
355 cmdlen += 2;
357 if (MEDATA->logfile) {
358 if (strncmp (cmdstr, "PASS ", 5) == 0) {
359 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
360 } else
361 fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
363 fflush (MEDATA->logfile);
366 got_sigpipe = 0;
367 tty_enable_interrupt_key ();
368 status = write (SUP.sock, cmdstr, cmdlen);
370 if (status < 0) {
371 code = 421;
373 if (errno == EPIPE) { /* Remote server has closed connection */
374 if (level == 0) {
375 level = 1;
376 status = ftpfs_reconnect (me, super);
377 level = 0;
378 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
379 goto ok;
383 got_sigpipe = 1;
385 g_free (cmdstr);
386 tty_disable_interrupt_key ();
387 return TRANSIENT;
389 retry = 0;
391 tty_disable_interrupt_key ();
393 if (wait_reply)
395 status = ftpfs_get_reply (me, SUP.sock,
396 (wait_reply & WANT_STRING) ? reply_str : NULL,
397 sizeof (reply_str) - 1);
398 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
400 retry = 1;
401 level = 1;
402 status = ftpfs_reconnect (me, super);
403 level = 0;
404 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
405 goto ok;
408 retry = 0;
409 g_free (cmdstr);
410 return status;
412 g_free (cmdstr);
413 return COMPLETE;
416 static void
417 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
419 if (SUP.sock != -1){
420 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
421 ftpfs_command(me, super, NONE, "QUIT");
422 close(SUP.sock);
424 g_free (SUP.host);
425 g_free (SUP.user);
426 g_free (SUP.cwdir);
427 g_free (SUP.password);
430 /* some defines only used by ftpfs_changetype */
431 /* These two are valid values for the second parameter */
432 #define TYPE_ASCII 0
433 #define TYPE_BINARY 1
435 /* This one is only used to initialize bucket->isbinary, don't use it as
436 second parameter to ftpfs_changetype. */
437 #define TYPE_UNKNOWN -1
439 static int
440 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
442 if (binary != SUP.isbinary) {
443 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
444 ERRNOR (EIO, -1);
445 SUP.isbinary = binary;
447 return binary;
450 /* This routine logs the user in */
451 static int
452 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
453 const char *netrcpass)
455 char *pass;
456 char *op;
457 char *name; /* login user name */
458 int anon = 0;
459 char reply_string[BUF_MEDIUM];
461 SUP.isbinary = TYPE_UNKNOWN;
463 if (SUP.password) /* explicit password */
464 op = g_strdup (SUP.password);
465 else if (netrcpass) /* password from netrc */
466 op = g_strdup (netrcpass);
467 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp")) {
468 if (!ftpfs_anonymous_passwd) /* default anonymous password */
469 ftpfs_init_passwd ();
470 op = g_strdup (ftpfs_anonymous_passwd);
471 anon = 1;
472 } else { /* ask user */
473 char *p;
475 p = g_strconcat (_(" FTP: Password required for "), SUP.user, " ",
476 NULL);
477 op = vfs_get_password (p);
478 g_free (p);
479 if (op == NULL)
480 ERRNOR (EPERM, 0);
481 SUP.password = g_strdup (op);
484 if (!anon || MEDATA->logfile)
485 pass = op;
486 else {
487 pass = g_strconcat ("-", op, (char *) NULL);
488 wipe_password (op);
491 /* Proxy server accepts: username@host-we-want-to-connect */
492 if (SUP.proxy) {
493 name =
494 g_strconcat (SUP.user, "@",
495 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host,
496 NULL);
497 } else
498 name = g_strdup (SUP.user);
500 if (ftpfs_get_reply
501 (me, SUP.sock, reply_string,
502 sizeof (reply_string) - 1) == COMPLETE) {
503 g_strup (reply_string);
504 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
505 if (MEDATA->logfile) {
506 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n",
507 SUP.remote_is_amiga);
508 fflush (MEDATA->logfile);
511 print_vfs_message (_("ftpfs: sending login name"));
513 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name)) {
514 case CONTINUE:
515 print_vfs_message (_("ftpfs: sending user password"));
516 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
517 if (code == CONTINUE) {
518 char *p;
520 p = g_strdup_printf (_
521 ("FTP: Account required for user %s"),
522 SUP.user);
523 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
524 g_free (p);
525 if (op == NULL)
526 ERRNOR (EPERM, 0);
527 print_vfs_message (_("ftpfs: sending user account"));
528 code =
529 ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
530 g_free (op);
532 if (code != COMPLETE)
533 break;
534 /* fall through */
536 case COMPLETE:
537 print_vfs_message (_("ftpfs: logged in"));
538 wipe_password (pass);
539 g_free (name);
540 return 1;
542 default:
543 SUP.failed_on_login = 1;
544 if (SUP.password)
545 wipe_password (SUP.password);
546 SUP.password = 0;
548 goto login_fail;
551 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
552 SUP.user);
553 login_fail:
554 wipe_password (pass);
555 g_free (name);
556 ERRNOR (EPERM, 0);
559 static struct no_proxy_entry {
560 char *domain;
561 void *next;
562 } *no_proxy;
564 static void
565 ftpfs_load_no_proxy_list (void)
567 /* FixMe: shouldn't be hardcoded!!! */
568 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
569 struct no_proxy_entry *np, *current = 0;
570 FILE *npf;
571 int c;
572 char *p;
573 static char *mc_file;
575 if (mc_file)
576 return;
578 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
579 if (exist_file (mc_file) &&
580 (npf = fopen (mc_file, "r"))) {
581 while (fgets (s, sizeof (s), npf)) {
582 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
583 while ((c = fgetc (npf)) != EOF && c != '\n')
585 continue;
588 if (p == s)
589 continue;
591 *p = '\0';
593 np = g_new (struct no_proxy_entry, 1);
594 np->domain = g_strdup (s);
595 np->next = NULL;
596 if (no_proxy)
597 current->next = np;
598 else
599 no_proxy = np;
600 current = np;
603 fclose (npf);
605 g_free (mc_file);
608 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
609 static int
610 ftpfs_check_proxy (const char *host)
612 struct no_proxy_entry *npe;
614 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
615 return 0; /* sanity check */
617 if (*host == '!')
618 return 1;
620 if (!ftpfs_always_use_proxy)
621 return 0;
623 if (!strchr (host, '.'))
624 return 0;
626 ftpfs_load_no_proxy_list ();
627 for (npe = no_proxy; npe; npe=npe->next) {
628 char *domain = npe->domain;
630 if (domain[0] == '.') {
631 int ld = strlen (domain);
632 int lh = strlen (host);
634 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
635 ld--;
636 lh--;
639 if (!ld)
640 return 0;
641 } else
642 if (!g_strcasecmp (host, domain))
643 return 0;
646 return 1;
649 static void
650 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
652 char *user, *dir;
654 dir =
655 vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT,
656 URL_ALLOW_ANON);
657 g_free (user);
658 g_free (dir);
661 static int
662 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
664 struct addrinfo hints, *res, *curr_res;
665 int my_socket = 0;
666 char *host = NULL;
667 char *port = NULL;
668 int tmp_port;
669 int e;
671 (void) me;
673 /* Use a proxy host? */
674 host = g_strdup(SUP.host);
676 if (!host || !*host){
677 print_vfs_message (_("ftpfs: Invalid host name."));
678 ftpfs_errno = EINVAL;
679 return -1;
682 /* Hosts to connect to that start with a ! should use proxy */
683 tmp_port = SUP.port;
685 if (SUP.proxy){
686 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
689 port = g_strdup_printf("%hu", (unsigned short) tmp_port);
690 if (port == NULL) {
691 g_free (host);
692 ftpfs_errno = errno;
693 return -1;
696 tty_enable_interrupt_key(); /* clear the interrupt flag */
698 memset (&hints, 0, sizeof (struct addrinfo));
699 hints.ai_socktype = SOCK_STREAM;
700 hints.ai_flags = AI_ADDRCONFIG;
703 e = getaddrinfo (host, port, &hints, &res);
704 g_free (port);
705 port = NULL;
707 if ( e != 0 ) {
708 tty_disable_interrupt_key ();
709 print_vfs_message (_("ftpfs: %s"), gai_strerror (e));
710 g_free (host);
711 ftpfs_errno = EINVAL;
712 return -1;
715 for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next) {
717 my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
719 if (my_socket < 0) {
721 if (curr_res->ai_next != NULL)
722 continue;
724 tty_disable_interrupt_key();
725 print_vfs_message (_("ftpfs: %s"), unix_error_string (errno));
726 g_free (host);
727 freeaddrinfo (res);
728 ftpfs_errno = errno;
729 return -1;
732 print_vfs_message (_("ftpfs: making connection to %s"), host);
733 g_free (host);
734 host = NULL;
736 if ( connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0 )
737 break;
739 ftpfs_errno = errno;
740 close (my_socket);
742 if (errno == EINTR && tty_got_interrupt ()) {
743 print_vfs_message (_("ftpfs: connection interrupted by user"));
744 } else if (res->ai_next == NULL) {
745 print_vfs_message (_("ftpfs: connection to server failed: %s"),
746 unix_error_string (errno));
747 } else {
748 continue;
751 freeaddrinfo (res);
752 tty_disable_interrupt_key ();
753 return -1;
756 freeaddrinfo (res);
757 tty_disable_interrupt_key ();
758 return my_socket;
761 static int
762 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
764 int retry_seconds, count_down;
766 /* We do not want to use the passive if we are using proxies */
767 if (SUP.proxy)
768 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
770 retry_seconds = 0;
771 do {
772 SUP.failed_on_login = 0;
774 SUP.sock = ftpfs_open_socket (me, super);
775 if (SUP.sock == -1)
776 return -1;
778 if (ftpfs_login_server (me, super, NULL)) {
779 /* Logged in, no need to retry the connection */
780 break;
781 } else {
782 if (SUP.failed_on_login){
783 /* Close only the socket descriptor */
784 close (SUP.sock);
785 } else {
786 return -1;
788 if (ftpfs_retry_seconds){
789 retry_seconds = ftpfs_retry_seconds;
790 tty_enable_interrupt_key ();
791 for (count_down = retry_seconds; count_down; count_down--){
792 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
793 sleep (1);
794 if (tty_got_interrupt ()) {
795 /* ftpfs_errno = E; */
796 tty_disable_interrupt_key ();
797 return 0;
800 tty_disable_interrupt_key ();
803 } while (retry_seconds);
805 SUP.cwdir = ftpfs_get_current_directory (me, super);
806 if (!SUP.cwdir)
807 SUP.cwdir = g_strdup (PATH_SEP_STR);
808 return 0;
811 static int
812 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
813 const char *archive_name, char *op)
815 char *host, *user, *password;
816 int port;
818 (void) archive_name;
820 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
822 SUP.host = host;
823 SUP.user = user;
824 SUP.port = port;
825 SUP.cwdir = NULL;
826 SUP.proxy = 0;
827 if (ftpfs_check_proxy (host))
828 SUP.proxy = ftpfs_proxy_host;
829 SUP.password = password;
830 SUP.use_passive_connection = ftpfs_use_passive_connections;
831 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
832 SUP.isbinary = TYPE_UNKNOWN;
833 SUP.remote_is_amiga = 0;
834 super->name = g_strdup ("/");
835 super->root =
836 vfs_s_new_inode (me, super,
837 vfs_s_default_stat (me, S_IFDIR | 0755));
839 return ftpfs_open_archive_int (me, super);
842 static int
843 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
844 const char *archive_name, char *op, void *cookie)
846 char *host, *user;
847 int port;
849 (void) me;
850 (void) archive_name;
851 (void) cookie;
853 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
855 port = ((strcmp (host, SUP.host) == 0)
856 && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
858 g_free (host);
859 g_free (user);
861 return port;
864 /* The returned directory should always contain a trailing slash */
865 static char *
866 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
868 char buf[BUF_8K], *bufp, *bufq;
870 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
871 ftpfs_get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
872 bufp = NULL;
873 for (bufq = buf; *bufq; bufq++)
874 if (*bufq == '"') {
875 if (!bufp) {
876 bufp = bufq + 1;
877 } else {
878 *bufq = 0;
879 if (*bufp) {
880 if (*(bufq - 1) != '/') {
881 *bufq++ = '/';
882 *bufq = 0;
884 if (*bufp == '/')
885 return g_strdup (bufp);
886 else {
887 /* If the remote server is an Amiga a leading slash
888 might be missing. MC needs it because it is used
889 as separator between hostname and path internally. */
890 return g_strconcat( "/", bufp, NULL);
892 } else {
893 ftpfs_errno = EIO;
894 return NULL;
899 ftpfs_errno = EIO;
900 return NULL;
904 /* Setup Passive ftp connection, we use it for source routed connections */
905 static int
906 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
907 int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
909 char *c;
911 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") == COMPLETE) {
912 int port;
913 /* (|||<port>|) */
914 c = strchr (reply_str, '|');
915 if (c == NULL)
916 return 0;
917 if(strlen(c) > 3)
918 c+=3;
919 else
920 return 0;
922 port = atoi (c);
923 if (port < 0 || port > 65535)
924 return 0;
925 port = htons (port);
927 switch (sa->ss_family) {
928 case AF_INET:
929 ((struct sockaddr_in *)sa)->sin_port = port;
930 break;
931 case AF_INET6:
932 ((struct sockaddr_in6 *)sa)->sin6_port = port;
933 break;
934 default:
935 print_vfs_message (_("ftpfs: invalid address family"));
936 ERRNOR (EINVAL, -1);
938 } else if (sa->ss_family == AF_INET) {
939 int xa, xb, xc, xd, xe, xf;
940 char n [6];
942 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
943 return 0;
945 /* Parse remote parameters */
946 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
948 if (!*c)
949 return 0;
950 if (!isdigit ((unsigned char) *c))
951 return 0;
952 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
953 return 0;
955 n [0] = (unsigned char) xa;
956 n [1] = (unsigned char) xb;
957 n [2] = (unsigned char) xc;
958 n [3] = (unsigned char) xd;
959 n [4] = (unsigned char) xe;
960 n [5] = (unsigned char) xf;
962 memcpy (&(((struct sockaddr_in *)sa)->sin_addr.s_addr), (void *)n, 4);
963 memcpy (&(((struct sockaddr_in *)sa)->sin_port), (void *)&n[4], 2);
964 } else
965 return 0;
967 if (connect (my_socket, (struct sockaddr *) sa, *salen ) < 0)
968 return 0;
970 return 1;
973 static int
974 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
976 struct sockaddr_storage data_addr;
977 socklen_t data_addrlen;
978 int data_sock;
980 again:
981 memset (&data_addr, 0, sizeof (struct sockaddr_storage));
982 data_addrlen = sizeof (struct sockaddr_storage);
984 if (getpeername (SUP.sock, (struct sockaddr *) &data_addr, &data_addrlen) == -1)
985 return -1;
987 switch (data_addr.ss_family) {
988 case AF_INET:
989 ((struct sockaddr_in *)&data_addr)->sin_port = 0;
990 break;
991 case AF_INET6:
992 ((struct sockaddr_in6 *)&data_addr)->sin6_port = 0;
993 break;
994 default:
995 print_vfs_message (_("ftpfs: invalid address family"));
996 ERRNOR(EINVAL, -1);
999 data_sock = socket (data_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
1000 if (data_sock < 0) {
1001 if (SUP.use_passive_connection) {
1002 print_vfs_message (_("ftpfs: could not setup passive mode: %s"), unix_error_string (errno));
1003 SUP.use_passive_connection = 0;
1004 goto again;
1007 print_vfs_message (_("ftpfs: could not create socket: %s"), unix_error_string (errno));
1008 return -1;
1011 if (SUP.use_passive_connection) {
1013 if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1014 return data_sock;
1016 SUP.use_passive_connection = 0;
1017 print_vfs_message (_("ftpfs: could not setup passive mode"));
1019 close (data_sock);
1020 goto again;
1023 /* If passive setup fails, fallback to active connections */
1024 /* Active FTP connection */
1025 if ((bind (data_sock, (struct sockaddr *)&data_addr, data_addrlen) == 0) &&
1026 (getsockname (data_sock, (struct sockaddr *)&data_addr, &data_addrlen) == 0) &&
1027 (listen (data_sock, 1) == 0)) {
1028 unsigned short int port;
1029 char *addr;
1030 unsigned int af;
1032 switch (data_addr.ss_family) {
1033 case AF_INET:
1034 af = FTP_INET;
1035 port = ((struct sockaddr_in *)&data_addr)->sin_port;
1036 break;
1037 case AF_INET6:
1038 af = FTP_INET6;
1039 port = ((struct sockaddr_in6 *)&data_addr)->sin6_port;
1040 break;
1041 default:
1042 print_vfs_message (_("ftpfs: invalid address family"));
1043 ERRNOR (EINVAL, -1);
1046 port = ntohs (port);
1048 addr = g_malloc (NI_MAXHOST);
1049 if (addr == NULL)
1050 ERRNOR (ENOMEM, -1);
1052 if (getnameinfo ((struct sockaddr *)&data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) {
1053 g_free (addr);
1054 ERRNOR (EIO, -1);
1057 if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE) {
1058 g_free (addr);
1059 return data_sock;
1061 g_free (addr);
1063 if (FTP_INET == af) {
1064 unsigned char *a = (unsigned char *)&((struct sockaddr_in *)&data_addr)->sin_addr;
1065 unsigned char *p = (unsigned char *)&port;
1067 if (ftpfs_command (me, super, WAIT_REPLY,
1068 "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1069 p[0], p[1]) == COMPLETE)
1070 return data_sock;
1073 close (data_sock);
1074 ftpfs_errno = EIO;
1075 return -1;
1078 static int
1079 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
1080 const char *remote, int isbinary, int reget)
1082 struct sockaddr_storage from;
1083 int s, j, data;
1084 socklen_t fromlen = sizeof(from);
1086 if ((s = ftpfs_initconn (me, super)) == -1)
1087 return -1;
1088 if (ftpfs_changetype (me, super, isbinary) == -1)
1089 return -1;
1090 if (reget > 0){
1091 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1092 if (j != CONTINUE)
1093 return -1;
1095 if (remote) {
1096 char *remote_path = ftpfs_translate_path (me, super, remote);
1097 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1098 /* WarFtpD can't STORE //filename */
1099 (*remote_path == '/') ? remote_path + 1 : remote_path);
1100 g_free (remote_path);
1101 } else
1102 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1103 if (j != PRELIM)
1104 ERRNOR (EPERM, -1);
1105 tty_enable_interrupt_key ();
1106 if (SUP.use_passive_connection)
1107 data = s;
1108 else {
1109 data = accept (s, (struct sockaddr *)&from, &fromlen);
1110 if (data < 0) {
1111 ftpfs_errno = errno;
1112 close (s);
1113 return -1;
1115 close (s);
1117 tty_disable_interrupt_key ();
1118 return data;
1121 #define ABORT_TIMEOUT 5
1122 static void
1123 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
1125 struct vfs_s_super *super = FH_SUPER;
1126 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1127 fd_set mask;
1128 char buf[1024];
1129 int dsock = FH_SOCK;
1130 FH_SOCK = -1;
1131 SUP.ctl_connection_busy = 0;
1133 print_vfs_message (_("ftpfs: aborting transfer."));
1134 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
1135 print_vfs_message (_("ftpfs: abort error: %s"),
1136 unix_error_string (errno));
1137 if (dsock != -1)
1138 close (dsock);
1139 return;
1142 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
1143 print_vfs_message (_("ftpfs: abort failed"));
1144 if (dsock != -1)
1145 close (dsock);
1146 return;
1148 if (dsock != -1) {
1149 FD_ZERO (&mask);
1150 FD_SET (dsock, &mask);
1151 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1152 struct timeval start_tim, tim;
1153 gettimeofday (&start_tim, NULL);
1154 /* flush the remaining data */
1155 while (read (dsock, buf, sizeof (buf)) > 0) {
1156 gettimeofday (&tim, NULL);
1157 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1158 /* server keeps sending, drop the connection and ftpfs_reconnect */
1159 close (dsock);
1160 ftpfs_reconnect (me, super);
1161 return;
1165 close (dsock);
1167 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1168 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1171 #if 0
1172 static void
1173 resolve_symlink_without_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1175 struct linklist *flist;
1176 struct direntry *fe, *fel;
1177 char tmp[MC_MAXPATHLEN];
1178 int depth;
1180 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1181 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1182 /* flist->data->l_stat is alread initialized with 0 */
1183 fel = flist->data;
1184 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1185 if (fel->linkname[0] == '/') {
1186 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1187 continue;
1188 strcpy (tmp, fel->linkname);
1189 } else {
1190 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1191 continue;
1192 strcpy (tmp, dir->remote_path);
1193 if (tmp[1] != '\0')
1194 strcat (tmp, "/");
1195 strcat (tmp + 1, fel->linkname);
1197 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1198 canonicalize_pathname (tmp);
1199 fe = _get_file_entry(bucket, tmp, 0, 0);
1200 if (fe) {
1201 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1202 /* Symlink points to link which isn't resolved, yet. */
1203 if (fe->linkname[0] == '/') {
1204 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1205 break;
1206 strcpy (tmp, fe->linkname);
1207 } else {
1208 /* at this point tmp looks always like this
1209 /directory/filename, i.e. no need to check
1210 strrchr's return value */
1211 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1212 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1213 break;
1214 strcat (tmp, fe->linkname);
1216 continue;
1217 } else {
1218 fel->l_stat = g_new (struct stat, 1);
1219 if ( S_ISLNK (fe->s.st_mode))
1220 *fel->l_stat = *fe->l_stat;
1221 else
1222 *fel->l_stat = fe->s;
1223 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1226 break;
1230 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1233 static void
1234 resolve_symlink_with_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1236 char buffer[2048] = "", *filename;
1237 int sock;
1238 FILE *fp;
1239 struct stat s;
1240 struct linklist *flist;
1241 struct direntry *fe;
1242 int switch_method = 0;
1244 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1245 if (strchr (dir->remote_path, ' ')) {
1246 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1247 print_vfs_message(_("ftpfs: CWD failed."));
1248 return;
1250 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1252 else
1253 sock = ftpfs_open_data_connection (bucket, "LIST -lLa",
1254 dir->remote_path, TYPE_ASCII, 0);
1256 if (sock == -1) {
1257 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1258 return;
1261 fp = fdopen(sock, "r");
1262 if (fp == NULL) {
1263 close(sock);
1264 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1265 return;
1267 tty_enable_interrupt_key ();
1268 flist = dir->file_list->next;
1269 while (1) {
1270 do {
1271 if (flist == dir->file_list)
1272 goto done;
1273 fe = flist->data;
1274 flist = flist->next;
1275 } while (!S_ISLNK(fe->s.st_mode));
1276 while (1) {
1277 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1278 goto done;
1279 if (MEDATA->logfile){
1280 fputs (buffer, MEDATA->logfile);
1281 fflush (MEDATA->logfile);
1283 vfs_die("This code should be commented out\n");
1284 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1285 int r = strcmp(fe->name, filename);
1286 g_free(filename);
1287 if (r == 0) {
1288 if (S_ISLNK (s.st_mode)) {
1289 /* This server doesn't understand LIST -lLa */
1290 switch_method = 1;
1291 goto done;
1293 fe->l_stat = g_new (struct stat, 1);
1294 if (fe->l_stat == NULL)
1295 goto done;
1296 *fe->l_stat = s;
1297 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1298 break;
1300 if (r < 0)
1301 break;
1305 done:
1306 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1307 tty_disable_interrupt_key ();
1308 fclose(fp);
1309 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1312 static void
1313 resolve_symlink(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1315 print_vfs_message(_("Resolving symlink..."));
1317 if (SUP.strict_rfc959_list_cmd)
1318 resolve_symlink_without_ls_options(me, super, dir);
1319 else
1320 resolve_symlink_with_ls_options(me, super, dir);
1322 #endif
1324 static int
1325 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1327 struct vfs_s_entry *ent;
1328 struct vfs_s_super *super = dir->super;
1329 int sock, num_entries = 0;
1330 char buffer[BUF_8K];
1331 int cd_first;
1333 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1334 || (strchr (remote_path, ' ') != NULL);
1336 again:
1337 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1338 remote_path,
1339 SUP.strict ==
1340 RFC_STRICT ? _("(strict rfc959)") : "",
1341 cd_first ? _("(chdir first)") : "");
1343 if (cd_first) {
1344 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE) {
1345 ftpfs_errno = ENOENT;
1346 print_vfs_message (_("ftpfs: CWD failed."));
1347 return -1;
1351 gettimeofday (&dir->timestamp, NULL);
1352 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1354 if (SUP.strict == RFC_STRICT)
1355 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1356 else if (cd_first)
1357 /* Dirty hack to avoid autoprepending / to . */
1358 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1359 sock =
1360 ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1361 else {
1362 /* Trailing "/." is necessary if remote_path is a symlink */
1363 char *path = concat_dir_and_file (remote_path, ".");
1364 sock =
1365 ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII,
1367 g_free (path);
1370 if (sock == -1)
1371 goto fallback;
1373 /* Clear the interrupt flag */
1374 tty_enable_interrupt_key ();
1376 while (1) {
1377 int i;
1378 int res =
1379 vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1380 sock);
1381 if (!res)
1382 break;
1384 if (res == EINTR) {
1385 me->verrno = ECONNRESET;
1386 close (sock);
1387 tty_disable_interrupt_key ();
1388 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1389 print_vfs_message (_("%s: failure"), me->name);
1390 return -1;
1393 if (MEDATA->logfile) {
1394 fputs (buffer, MEDATA->logfile);
1395 fputs ("\n", MEDATA->logfile);
1396 fflush (MEDATA->logfile);
1399 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1400 i = ent->ino->st.st_nlink;
1401 if (!vfs_parse_ls_lga
1402 (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1403 vfs_s_free_entry (me, ent);
1404 continue;
1406 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1407 num_entries++;
1408 vfs_s_insert_entry (me, dir, ent);
1411 close (sock);
1412 me->verrno = E_REMOTE;
1413 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1414 goto fallback;
1416 if (num_entries == 0 && cd_first == 0) {
1417 /* The LIST command may produce an empty output. In such scenario
1418 it is not clear whether this is caused by `remote_path' being
1419 a non-existent path or for some other reason (listing emtpy
1420 directory without the -a option, non-readable directory, etc.).
1422 Since `dir_load' is a crucial method, when it comes to determine
1423 whether a given path is a _directory_, the code must try its best
1424 to determine the type of `remote_path'. The only reliable way to
1425 achieve this is trough issuing a CWD command. */
1427 cd_first = 1;
1428 goto again;
1431 if (SUP.strict == RFC_AUTODETECT)
1432 SUP.strict = RFC_DARING;
1434 print_vfs_message (_("%s: done."), me->name);
1435 return 0;
1437 fallback:
1438 if (SUP.strict == RFC_AUTODETECT) {
1439 /* It's our first attempt to get a directory listing from this
1440 server (UNIX style LIST command) */
1441 SUP.strict = RFC_STRICT;
1442 /* I hate goto, but recursive call needs another 8K on stack */
1443 /* return ftpfs_dir_load (me, dir, remote_path); */
1444 cd_first = 1;
1445 goto again;
1447 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1448 ERRNOR (EACCES, -1);
1451 static int
1452 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name,
1453 char *localname)
1455 int h, sock, n_read, n_written;
1456 off_t n_stored;
1457 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1458 struct linger li;
1459 #else
1460 int flag_one = 1;
1461 #endif
1462 char buffer[8192];
1463 struct stat s;
1464 char *w_buf;
1465 struct vfs_s_super *super = FH_SUPER;
1467 h = open (localname, O_RDONLY);
1468 if (h == -1)
1469 ERRNOR (EIO, -1);
1470 sock =
1471 ftpfs_open_data_connection (me, super,
1472 fh->u.ftp.append ? "APPE" : "STOR", name,
1473 TYPE_BINARY, 0);
1474 if (sock < 0 || fstat (h, &s) == -1) {
1475 close (h);
1476 return -1;
1478 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1479 li.l_onoff = 1;
1480 li.l_linger = 120;
1481 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1482 #else
1483 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1484 #endif
1485 n_stored = 0;
1487 tty_enable_interrupt_key ();
1488 while (1) {
1489 while ((n_read = read (h, buffer, sizeof (buffer))) == -1) {
1490 if (errno == EINTR) {
1491 if (tty_got_interrupt ()) {
1492 ftpfs_errno = EINTR;
1493 goto error_return;
1494 } else
1495 continue;
1497 ftpfs_errno = errno;
1498 goto error_return;
1500 if (n_read == 0)
1501 break;
1502 n_stored += n_read;
1503 w_buf = buffer;
1504 while ((n_written = write (sock, w_buf, n_read)) != n_read) {
1505 if (n_written == -1) {
1506 if (errno == EINTR && !tty_got_interrupt ()) {
1507 continue;
1509 ftpfs_errno = errno;
1510 goto error_return;
1512 w_buf += n_written;
1513 n_read -= n_written;
1515 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1516 (unsigned long) n_stored, (unsigned long) s.st_size);
1518 tty_disable_interrupt_key ();
1519 close (sock);
1520 close (h);
1521 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1522 ERRNOR (EIO, -1);
1523 return 0;
1524 error_return:
1525 tty_disable_interrupt_key ();
1526 close (sock);
1527 close (h);
1528 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1529 return -1;
1532 static int
1533 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1535 char *name = vfs_s_fullpath (me, fh->ino);
1537 if (!name)
1538 return 0;
1539 FH_SOCK = ftpfs_open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1540 g_free (name);
1541 if (FH_SOCK == -1)
1542 ERRNOR (EACCES, 0);
1543 fh->linear = LS_LINEAR_OPEN;
1544 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1545 fh->u.ftp.append = 0;
1546 return 1;
1549 static int
1550 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1552 int n;
1553 struct vfs_s_super *super = FH_SUPER;
1555 while ((n = read (FH_SOCK, buf, len))<0) {
1556 if ((errno == EINTR) && !tty_got_interrupt ())
1557 continue;
1558 break;
1561 if (n<0)
1562 ftpfs_linear_abort(me, fh);
1564 if (!n) {
1565 SUP.ctl_connection_busy = 0;
1566 close (FH_SOCK);
1567 FH_SOCK = -1;
1568 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1569 ERRNOR (E_REMOTE, -1);
1570 return 0;
1572 ERRNOR (errno, n);
1575 static void
1576 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1578 if (FH_SOCK != -1)
1579 ftpfs_linear_abort(me, fh);
1582 static int ftpfs_ctl (void *fh, int ctlop, void *arg)
1584 (void) arg;
1586 switch (ctlop) {
1587 case VFS_CTL_IS_NOTREADY:
1589 int v;
1591 if (!FH->linear)
1592 vfs_die ("You may not do this");
1593 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1594 return 0;
1596 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1597 if (((v < 0) && (errno == EINTR)) || v == 0)
1598 return 1;
1599 return 0;
1601 default:
1602 return 0;
1606 static int
1607 ftpfs_send_command(struct vfs_class *me, const char *filename, const char *cmd, int flags)
1609 const char *rpath;
1610 char *p, *mpath = g_strdup(filename);
1611 struct vfs_s_super *super;
1612 int r;
1613 int flush_directory_cache = (flags & OPT_FLUSH);
1615 if (!(rpath = vfs_s_get_path_mangle(me, mpath, &super, 0))) {
1616 g_free(mpath);
1617 return -1;
1619 p = ftpfs_translate_path (me, super, rpath);
1620 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1621 g_free (p);
1622 vfs_stamp_create (&vfs_ftpfs_ops, super);
1623 if (flags & OPT_IGNORE_ERROR)
1624 r = COMPLETE;
1625 if (r != COMPLETE) {
1626 me->verrno = EPERM;
1627 g_free (mpath);
1628 return -1;
1630 if (flush_directory_cache)
1631 vfs_s_invalidate(me, super);
1632 g_free(mpath);
1633 return 0;
1636 /* This routine is called as the last step in load_setup */
1637 void
1638 ftpfs_init_passwd(void)
1640 ftpfs_anonymous_passwd = load_anon_passwd ();
1641 if (ftpfs_anonymous_passwd)
1642 return;
1644 /* If there is no anonymous ftp password specified
1645 * then we'll just use anonymous@
1646 * We don't send any other thing because:
1647 * - We want to remain anonymous
1648 * - We want to stop SPAM
1649 * - We don't want to let ftp sites to discriminate by the user,
1650 * host or country.
1652 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1655 static int ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1657 char buf[BUF_SMALL];
1659 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1660 return ftpfs_send_command(me, path, buf, OPT_FLUSH);
1663 static int ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1665 #if 0
1666 ftpfs_errno = EPERM;
1667 return -1;
1668 #else
1669 /* Everyone knows it is not possible to chown remotely, so why bother them.
1670 If someone's root, then copy/move will always try to chown it... */
1671 (void) me;
1672 (void) path;
1673 (void) owner;
1674 (void) group;
1675 return 0;
1676 #endif
1679 static int ftpfs_unlink (struct vfs_class *me, const char *path)
1681 return ftpfs_send_command(me, path, "DELE /%s", OPT_FLUSH);
1684 /* Return 1 if path is the same directory as the one we are in now */
1685 static int
1686 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1688 (void) me;
1690 if (!SUP.cwdir)
1691 return 0;
1692 if (strcmp (path, SUP.cwdir) == 0)
1693 return 1;
1694 return 0;
1697 static int
1698 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1700 int r;
1701 char *p;
1703 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1704 return COMPLETE;
1706 p = ftpfs_translate_path (me, super, remote_path);
1707 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1708 g_free (p);
1710 if (r != COMPLETE) {
1711 ftpfs_errno = EIO;
1712 } else {
1713 g_free(SUP.cwdir);
1714 SUP.cwdir = g_strdup (remote_path);
1715 SUP.cwd_deferred = 0;
1717 return r;
1720 static int ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1722 ftpfs_send_command(me, path1, "RNFR /%s", OPT_FLUSH);
1723 return ftpfs_send_command(me, path2, "RNTO /%s", OPT_FLUSH);
1726 static int ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1728 (void) mode; /* FIXME: should be used */
1730 return ftpfs_send_command(me, path, "MKD /%s", OPT_FLUSH);
1733 static int ftpfs_rmdir (struct vfs_class *me, const char *path)
1735 return ftpfs_send_command(me, path, "RMD /%s", OPT_FLUSH);
1738 static int
1739 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
1740 int mode)
1742 (void) mode;
1744 fh->u.ftp.append = 0;
1745 /* File will be written only, so no need to retrieve it from ftp server */
1746 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
1747 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1748 struct linger li;
1749 #else
1750 int li = 1;
1751 #endif
1752 char *name;
1754 /* ftpfs_linear_start() called, so data will be written
1755 * to local temporary file and stored to ftp server
1756 * by vfs_s_close later
1758 if (FH_SUPER->u.ftp.ctl_connection_busy) {
1759 if (!fh->ino->localname) {
1760 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1761 fh->ino->ent->name);
1762 if (handle == -1)
1763 return -1;
1764 close (handle);
1765 fh->u.ftp.append = flags & O_APPEND;
1767 return 0;
1769 name = vfs_s_fullpath (me, fh->ino);
1770 if (!name)
1771 return -1;
1772 fh->handle =
1773 ftpfs_open_data_connection (me, fh->ino->super,
1774 (flags & O_APPEND) ? "APPE" :
1775 "STOR", name, TYPE_BINARY, 0);
1776 g_free (name);
1778 if (fh->handle < 0)
1779 return -1;
1780 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1781 li.l_onoff = 1;
1782 li.l_linger = 120;
1783 #endif
1784 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1786 if (fh->ino->localname) {
1787 unlink (fh->ino->localname);
1788 g_free (fh->ino->localname);
1789 fh->ino->localname = NULL;
1791 return 0;
1794 if (!fh->ino->localname)
1795 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1796 return -1;
1797 if (!fh->ino->localname)
1798 vfs_die ("retrieve_file failed to fill in localname");
1799 return 0;
1802 static int ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1804 if (fh->handle != -1 && !fh->ino->localname){
1805 close (fh->handle);
1806 fh->handle = -1;
1807 /* File is stored to destination already, so
1808 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1810 fh->changed = 0;
1811 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1812 ERRNOR (EIO, -1);
1813 vfs_s_invalidate (me, FH_SUPER);
1815 return 0;
1818 static void
1819 ftpfs_done (struct vfs_class *me)
1821 struct no_proxy_entry *np;
1823 (void) me;
1825 while (no_proxy) {
1826 np = no_proxy->next;
1827 g_free (no_proxy->domain);
1828 g_free (no_proxy);
1829 no_proxy = np;
1831 g_free (ftpfs_anonymous_passwd);
1832 g_free (ftpfs_proxy_host);
1835 static void
1836 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
1838 struct vfs_s_super *super = MEDATA->supers;
1839 char *name;
1841 while (super){
1842 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
1843 (*func)(name);
1844 g_free (name);
1845 super = super->next;
1849 static char buffer[BUF_MEDIUM];
1850 static char *netrc;
1851 static const char *netrcp;
1853 /* This should match the keywords[] array below */
1854 typedef enum {
1855 NETRC_NONE = 0,
1856 NETRC_DEFAULT,
1857 NETRC_MACHINE,
1858 NETRC_LOGIN,
1859 NETRC_PASSWORD,
1860 NETRC_PASSWD,
1861 NETRC_ACCOUNT,
1862 NETRC_MACDEF,
1863 NETRC_UNKNOWN
1864 } keyword_t;
1866 static keyword_t ftpfs_netrc_next (void)
1868 char *p;
1869 keyword_t i;
1870 static const char *const keywords[] = { "default", "machine",
1871 "login", "password", "passwd", "account", "macdef", NULL
1875 while (1) {
1876 netrcp = skip_separators (netrcp);
1877 if (*netrcp != '\n')
1878 break;
1879 netrcp++;
1881 if (!*netrcp)
1882 return NETRC_NONE;
1883 p = buffer;
1884 if (*netrcp == '"') {
1885 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1886 if (*netrcp == '\\')
1887 netrcp++;
1888 *p++ = *netrcp;
1890 } else {
1891 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1892 *netrcp != ',' && *netrcp; netrcp++) {
1893 if (*netrcp == '\\')
1894 netrcp++;
1895 *p++ = *netrcp;
1898 *p = 0;
1899 if (!*buffer)
1900 return NETRC_NONE;
1902 i = NETRC_DEFAULT;
1903 while (keywords[i - 1]) {
1904 if (!strcmp (keywords[i - 1], buffer))
1905 return i;
1907 i++;
1910 return NETRC_UNKNOWN;
1913 static int ftpfs_netrc_bad_mode (const char *netrcname)
1915 static int be_angry = 1;
1916 struct stat mystat;
1918 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1919 if (be_angry) {
1920 message (D_ERROR, MSG_ERROR,
1921 _("~/.netrc file has incorrect mode.\n"
1922 "Remove password or correct mode."));
1923 be_angry = 0;
1925 return 1;
1927 return 0;
1930 /* Scan .netrc until we find matching "machine" or "default"
1931 * domain is used for additional matching
1932 * No search is done after "default" in compliance with "man netrc"
1933 * Return 0 if found, -1 otherwise */
1934 static int ftpfs_find_machine (const char *host, const char *domain)
1936 keyword_t keyword;
1938 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) {
1939 if (keyword == NETRC_DEFAULT)
1940 return 0;
1942 if (keyword == NETRC_MACDEF) {
1943 /* Scan for an empty line, which concludes "macdef" */
1944 do {
1945 while (*netrcp && *netrcp != '\n')
1946 netrcp++;
1947 if (*netrcp != '\n')
1948 break;
1949 netrcp++;
1950 } while (*netrcp && *netrcp != '\n');
1951 continue;
1954 if (keyword != NETRC_MACHINE)
1955 continue;
1957 /* Take machine name */
1958 if (ftpfs_netrc_next () == NETRC_NONE)
1959 break;
1961 if (g_strcasecmp (host, buffer)) {
1962 /* Try adding our domain to short names in .netrc */
1963 const char *host_domain = strchr (host, '.');
1964 if (!host_domain)
1965 continue;
1967 /* Compare domain part */
1968 if (g_strcasecmp (host_domain, domain))
1969 continue;
1971 /* Compare local part */
1972 if (g_strncasecmp (host, buffer, host_domain - host))
1973 continue;
1976 return 0;
1979 /* end of .netrc */
1980 return -1;
1983 /* Extract login and password from .netrc for the host.
1984 * pass may be NULL.
1985 * Returns 0 for success, -1 for error */
1986 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass)
1988 char *netrcname;
1989 char *tmp_pass = NULL;
1990 char hostname[MAXHOSTNAMELEN];
1991 const char *domain;
1992 keyword_t keyword;
1993 static struct rupcache {
1994 struct rupcache *next;
1995 char *host;
1996 char *login;
1997 char *pass;
1998 } *rup_cache = NULL, *rupp;
2000 /* Initialize *login and *pass */
2001 if (!login)
2002 return 0;
2003 *login = NULL;
2004 if (pass)
2005 *pass = NULL;
2007 /* Look up in the cache first */
2008 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
2009 if (!strcmp (host, rupp->host)) {
2010 if (rupp->login)
2011 *login = g_strdup (rupp->login);
2012 if (pass && rupp->pass)
2013 *pass = g_strdup (rupp->pass);
2014 return 0;
2018 /* Load current .netrc */
2019 netrcname = concat_dir_and_file (home_dir, ".netrc");
2020 netrcp = netrc = load_file (netrcname);
2021 if (netrc == NULL) {
2022 g_free (netrcname);
2023 return 0;
2026 /* Find our own domain name */
2027 if (gethostname (hostname, sizeof (hostname)) < 0)
2028 *hostname = 0;
2029 if (!(domain = strchr (hostname, '.')))
2030 domain = "";
2032 /* Scan for "default" and matching "machine" keywords */
2033 ftpfs_find_machine (host, domain);
2035 /* Scan for keywords following "default" and "machine" */
2036 while (1) {
2037 int need_break = 0;
2038 keyword = ftpfs_netrc_next ();
2040 switch (keyword) {
2041 case NETRC_LOGIN:
2042 if (ftpfs_netrc_next () == NETRC_NONE) {
2043 need_break = 1;
2044 break;
2047 /* We have another name already - should not happen */
2048 if (*login) {
2049 need_break = 1;
2050 break;
2053 /* We have login name now */
2054 *login = g_strdup (buffer);
2055 break;
2057 case NETRC_PASSWORD:
2058 case NETRC_PASSWD:
2059 if (ftpfs_netrc_next () == NETRC_NONE) {
2060 need_break = 1;
2061 break;
2064 /* Ignore unsafe passwords */
2065 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2066 && ftpfs_netrc_bad_mode (netrcname)) {
2067 need_break = 1;
2068 break;
2071 /* Remember password. pass may be NULL, so use tmp_pass */
2072 if (tmp_pass == NULL)
2073 tmp_pass = g_strdup (buffer);
2074 break;
2076 case NETRC_ACCOUNT:
2077 /* "account" is followed by a token which we ignore */
2078 if (ftpfs_netrc_next () == NETRC_NONE) {
2079 need_break = 1;
2080 break;
2083 /* Ignore account, but warn user anyways */
2084 ftpfs_netrc_bad_mode (netrcname);
2085 break;
2087 default:
2088 /* Unexpected keyword or end of file */
2089 need_break = 1;
2090 break;
2093 if (need_break)
2094 break;
2097 g_free (netrc);
2098 g_free (netrcname);
2100 rupp = g_new (struct rupcache, 1);
2101 rupp->host = g_strdup (host);
2102 rupp->login = rupp->pass = 0;
2104 if (*login != NULL) {
2105 rupp->login = g_strdup (*login);
2107 if (tmp_pass != NULL)
2108 rupp->pass = g_strdup (tmp_pass);
2109 rupp->next = rup_cache;
2110 rup_cache = rupp;
2112 if (pass)
2113 *pass = tmp_pass;
2115 return 0;
2118 void
2119 init_ftpfs (void)
2121 static struct vfs_s_subclass ftpfs_subclass;
2123 ftpfs_subclass.flags = VFS_S_REMOTE;
2124 ftpfs_subclass.archive_same = ftpfs_archive_same;
2125 ftpfs_subclass.open_archive = ftpfs_open_archive;
2126 ftpfs_subclass.free_archive = ftpfs_free_archive;
2127 ftpfs_subclass.fh_open = ftpfs_fh_open;
2128 ftpfs_subclass.fh_close = ftpfs_fh_close;
2129 ftpfs_subclass.dir_load = ftpfs_dir_load;
2130 ftpfs_subclass.file_store = ftpfs_file_store;
2131 ftpfs_subclass.linear_start = ftpfs_linear_start;
2132 ftpfs_subclass.linear_read = ftpfs_linear_read;
2133 ftpfs_subclass.linear_close = ftpfs_linear_close;
2135 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2136 vfs_ftpfs_ops.name = "ftpfs";
2137 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2138 vfs_ftpfs_ops.prefix = "ftp:";
2139 vfs_ftpfs_ops.done = &ftpfs_done;
2140 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2141 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2142 vfs_ftpfs_ops.chown = ftpfs_chown;
2143 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2144 vfs_ftpfs_ops.rename = ftpfs_rename;
2145 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2146 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2147 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2148 vfs_register_class (&vfs_ftpfs_ops);