Ticket #1395 (Copying to fish is broken)
[midnight-commander.git] / vfs / ftpfs.c
blob9ac03bcfb06ca2f74425cf67f86fec5f04d53f29
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"
84 #include "../src/tty.h" /* enable/disable interrupt key */
85 #include "../src/wtools.h" /* message() */
86 #include "../src/main.h" /* print_vfs_message */
87 #include "../src/history.h"
88 #include "utilvfs.h"
89 #include "xdirentry.h"
90 #include "vfs.h"
91 #include "vfs-impl.h"
92 #include "gc.h" /* vfs_stamp_create */
93 #include "tcputil.h"
94 #include "../src/setup.h" /* for load_anon_passwd */
95 #include "ftpfs.h"
96 #ifndef MAXHOSTNAMELEN
97 # define MAXHOSTNAMELEN 64
98 #endif
100 #define UPLOAD_ZERO_LENGTH_FILE
101 #define SUP super->u.ftp
102 #define FH_SOCK fh->u.ftp.sock
104 #ifndef INADDR_NONE
105 #define INADDR_NONE 0xffffffff
106 #endif
108 #define RFC_AUTODETECT 0
109 #define RFC_DARING 1
110 #define RFC_STRICT 2
112 #ifndef HAVE_SOCKLEN_T
113 typedef int socklen_t;
114 #endif
116 static int ftpfs_errno;
117 static int code;
119 /* Delay to retry a connection */
120 int ftpfs_retry_seconds = 30;
122 /* Method to use to connect to ftp sites */
123 int ftpfs_use_passive_connections = 1;
124 int ftpfs_use_passive_connections_over_proxy = 0;
126 /* Method used to get directory listings:
127 * 1: try 'LIST -la <path>', if it fails
128 * fall back to CWD <path>; LIST
129 * 0: always use CWD <path>; LIST
131 int ftpfs_use_unix_list_options = 1;
133 /* First "CWD <path>", then "LIST -la ." */
134 int ftpfs_first_cd_then_ls = 1;
136 /* Use the ~/.netrc */
137 int use_netrc = 1;
139 /* Anonymous setup */
140 char *ftpfs_anonymous_passwd = NULL;
141 int ftpfs_directory_timeout = 900;
143 /* Proxy host */
144 char *ftpfs_proxy_host = NULL;
146 /* wether we have to use proxy by default? */
147 int ftpfs_always_use_proxy;
149 #ifdef FIXME_LATER_ALIGATOR
150 static struct linklist *connections_list;
151 #endif
153 /* ftpfs_command wait_flag: */
154 #define NONE 0x00
155 #define WAIT_REPLY 0x01
156 #define WANT_STRING 0x02
157 static char reply_str [80];
159 static struct vfs_class vfs_ftpfs_ops;
161 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
162 Translate a Unix path, i.e. MC's internal path representation (e.g.
163 /somedir/somefile) to a path valid for the remote server. Every path
164 transfered to the remote server has to be mangled by this function
165 right prior to sending it.
166 Currently only Amiga ftp servers are handled in a special manner.
168 When the remote server is an amiga:
169 a) strip leading slash if necesarry
170 b) replace first occurance of ":/" with ":"
171 c) strip trailing "/."
174 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
175 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path);
176 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
177 __attribute__ ((format (__printf__, 4, 5)));
178 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
179 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass);
180 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
182 static char *
183 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
185 if (!SUP.remote_is_amiga)
186 return g_strdup (remote_path);
187 else {
188 char *ret, *p;
190 if (MEDATA->logfile) {
191 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
192 fflush (MEDATA->logfile);
195 /* strip leading slash(es) */
196 while (*remote_path == '/')
197 remote_path++;
200 * Don't change "/" into "", e.g. "CWD " would be
201 * invalid.
203 if (*remote_path == '\0')
204 return g_strdup (".");
206 ret = g_strdup (remote_path);
208 /* replace first occurance of ":/" with ":" */
209 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
210 strcpy (p + 1, p + 2);
212 /* strip trailing "/." */
213 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
214 *p = '\0';
215 return ret;
219 /* Extract the hostname and username from the path */
222 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
223 * ftp://sunsite.unc.edu/pub/linux
224 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
225 * ftp://tsx-11.mit.edu:8192/
226 * ftp://joe@foo.edu:11321/private
227 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
228 * is supplied.
232 #define FTP_COMMAND_PORT 21
234 static void
235 ftpfs_split_url(char *path, char **host, char **user, int *port, char **pass)
237 char *p;
239 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
240 URL_ALLOW_ANON);
242 if (!*user) {
243 /* Look up user and password in netrc */
244 if (use_netrc)
245 ftpfs_netrc_lookup (*host, user, pass);
246 if (!*user)
247 *user = g_strdup ("anonymous");
250 /* Look up password in netrc for known user */
251 if (use_netrc && *user && pass && !*pass) {
252 char *new_user;
254 ftpfs_netrc_lookup (*host, &new_user, pass);
256 /* If user is different, remove password */
257 if (new_user && strcmp (*user, new_user)) {
258 g_free (*pass);
259 *pass = NULL;
262 g_free (new_user);
265 g_free (p);
268 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
269 static int
270 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
272 char answer[BUF_1K];
273 int i;
275 for (;;) {
276 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
277 if (string_buf)
278 *string_buf = 0;
279 code = 421;
280 return 4;
282 switch (sscanf(answer, "%d", &code)){
283 case 0:
284 if (string_buf)
285 g_strlcpy (string_buf, answer, string_len);
286 code = 500;
287 return 5;
288 case 1:
289 if (answer[3] == '-') {
290 while (1) {
291 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
292 if (string_buf)
293 *string_buf = 0;
294 code = 421;
295 return 4;
297 if ((sscanf (answer, "%d", &i) > 0) &&
298 (code == i) && (answer[3] == ' '))
299 break;
302 if (string_buf)
303 g_strlcpy (string_buf, answer, string_len);
304 return code / 100;
309 static int
310 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
312 int sock = ftpfs_open_socket (me, super);
313 if (sock != -1){
314 char *cwdir = SUP.cwdir;
315 close (SUP.sock);
316 SUP.sock = sock;
317 SUP.cwdir = NULL;
318 if (ftpfs_login_server (me, super, SUP.password)){
319 if (!cwdir)
320 return 1;
321 sock = ftpfs_chdir_internal (me, super, cwdir);
322 g_free (cwdir);
323 return sock == COMPLETE;
325 SUP.cwdir = cwdir;
327 return 0;
330 static int
331 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
333 va_list ap;
334 char *cmdstr;
335 int status, cmdlen;
336 static int retry = 0;
337 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
339 va_start (ap, fmt);
340 cmdstr = g_strdup_vprintf (fmt, ap);
341 va_end (ap);
343 cmdlen = strlen (cmdstr);
344 cmdstr = g_realloc (cmdstr, cmdlen + 3);
345 strcpy (cmdstr + cmdlen, "\r\n");
346 cmdlen += 2;
348 if (MEDATA->logfile) {
349 if (strncmp (cmdstr, "PASS ", 5) == 0) {
350 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
351 } else
352 fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
354 fflush (MEDATA->logfile);
357 got_sigpipe = 0;
358 enable_interrupt_key ();
359 status = write (SUP.sock, cmdstr, cmdlen);
361 if (status < 0) {
362 code = 421;
364 if (errno == EPIPE) { /* Remote server has closed connection */
365 if (level == 0) {
366 level = 1;
367 status = ftpfs_reconnect (me, super);
368 level = 0;
369 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
370 goto ok;
374 got_sigpipe = 1;
376 g_free (cmdstr);
377 disable_interrupt_key ();
378 return TRANSIENT;
380 retry = 0;
382 disable_interrupt_key ();
384 if (wait_reply)
386 status = ftpfs_get_reply (me, SUP.sock,
387 (wait_reply & WANT_STRING) ? reply_str : NULL,
388 sizeof (reply_str) - 1);
389 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
391 retry = 1;
392 level = 1;
393 status = ftpfs_reconnect (me, super);
394 level = 0;
395 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
396 goto ok;
399 retry = 0;
400 g_free (cmdstr);
401 return status;
403 g_free (cmdstr);
404 return COMPLETE;
407 static void
408 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
410 if (SUP.sock != -1){
411 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
412 ftpfs_command(me, super, NONE, "QUIT");
413 close(SUP.sock);
415 g_free (SUP.host);
416 g_free (SUP.user);
417 g_free (SUP.cwdir);
418 g_free (SUP.password);
421 /* some defines only used by ftpfs_changetype */
422 /* These two are valid values for the second parameter */
423 #define TYPE_ASCII 0
424 #define TYPE_BINARY 1
426 /* This one is only used to initialize bucket->isbinary, don't use it as
427 second parameter to ftpfs_changetype. */
428 #define TYPE_UNKNOWN -1
430 static int
431 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
433 if (binary != SUP.isbinary) {
434 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
435 ERRNOR (EIO, -1);
436 SUP.isbinary = binary;
438 return binary;
441 /* This routine logs the user in */
442 static int
443 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
444 const char *netrcpass)
446 char *pass;
447 char *op;
448 char *name; /* login user name */
449 int anon = 0;
450 char reply_string[BUF_MEDIUM];
452 SUP.isbinary = TYPE_UNKNOWN;
454 if (SUP.password) /* explicit password */
455 op = g_strdup (SUP.password);
456 else if (netrcpass) /* password from netrc */
457 op = g_strdup (netrcpass);
458 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp")) {
459 if (!ftpfs_anonymous_passwd) /* default anonymous password */
460 ftpfs_init_passwd ();
461 op = g_strdup (ftpfs_anonymous_passwd);
462 anon = 1;
463 } else { /* ask user */
464 char *p;
466 p = g_strconcat (_(" FTP: Password required for "), SUP.user, " ",
467 NULL);
468 op = vfs_get_password (p);
469 g_free (p);
470 if (op == NULL)
471 ERRNOR (EPERM, 0);
472 SUP.password = g_strdup (op);
475 if (!anon || MEDATA->logfile)
476 pass = op;
477 else {
478 pass = g_strconcat ("-", op, (char *) NULL);
479 wipe_password (op);
482 /* Proxy server accepts: username@host-we-want-to-connect */
483 if (SUP.proxy) {
484 name =
485 g_strconcat (SUP.user, "@",
486 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host,
487 NULL);
488 } else
489 name = g_strdup (SUP.user);
491 if (ftpfs_get_reply
492 (me, SUP.sock, reply_string,
493 sizeof (reply_string) - 1) == COMPLETE) {
494 g_strup (reply_string);
495 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
496 if (MEDATA->logfile) {
497 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n",
498 SUP.remote_is_amiga);
499 fflush (MEDATA->logfile);
502 print_vfs_message (_("ftpfs: sending login name"));
504 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name)) {
505 case CONTINUE:
506 print_vfs_message (_("ftpfs: sending user password"));
507 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
508 if (code == CONTINUE) {
509 char *p;
511 p = g_strdup_printf (_
512 ("FTP: Account required for user %s"),
513 SUP.user);
514 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
515 g_free (p);
516 if (op == NULL)
517 ERRNOR (EPERM, 0);
518 print_vfs_message (_("ftpfs: sending user account"));
519 code =
520 ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
521 g_free (op);
523 if (code != COMPLETE)
524 break;
525 /* fall through */
527 case COMPLETE:
528 print_vfs_message (_("ftpfs: logged in"));
529 wipe_password (pass);
530 g_free (name);
531 return 1;
533 default:
534 SUP.failed_on_login = 1;
535 if (SUP.password)
536 wipe_password (SUP.password);
537 SUP.password = 0;
539 goto login_fail;
542 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
543 SUP.user);
544 login_fail:
545 wipe_password (pass);
546 g_free (name);
547 ERRNOR (EPERM, 0);
550 static struct no_proxy_entry {
551 char *domain;
552 void *next;
553 } *no_proxy;
555 static void
556 ftpfs_load_no_proxy_list (void)
558 /* FixMe: shouldn't be hardcoded!!! */
559 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
560 struct no_proxy_entry *np, *current = 0;
561 FILE *npf;
562 int c;
563 char *p;
564 static char *mc_file;
566 if (mc_file)
567 return;
569 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
570 if (exist_file (mc_file) &&
571 (npf = fopen (mc_file, "r"))) {
572 while (fgets (s, sizeof (s), npf)) {
573 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
574 while ((c = fgetc (npf)) != EOF && c != '\n')
576 continue;
579 if (p == s)
580 continue;
582 *p = '\0';
584 np = g_new (struct no_proxy_entry, 1);
585 np->domain = g_strdup (s);
586 np->next = NULL;
587 if (no_proxy)
588 current->next = np;
589 else
590 no_proxy = np;
591 current = np;
594 fclose (npf);
596 g_free (mc_file);
599 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
600 static int
601 ftpfs_check_proxy (const char *host)
603 struct no_proxy_entry *npe;
605 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
606 return 0; /* sanity check */
608 if (*host == '!')
609 return 1;
611 if (!ftpfs_always_use_proxy)
612 return 0;
614 if (!strchr (host, '.'))
615 return 0;
617 ftpfs_load_no_proxy_list ();
618 for (npe = no_proxy; npe; npe=npe->next) {
619 char *domain = npe->domain;
621 if (domain[0] == '.') {
622 int ld = strlen (domain);
623 int lh = strlen (host);
625 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
626 ld--;
627 lh--;
630 if (!ld)
631 return 0;
632 } else
633 if (!g_strcasecmp (host, domain))
634 return 0;
637 return 1;
640 static void
641 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
643 char *user, *dir;
645 dir =
646 vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT,
647 URL_ALLOW_ANON);
648 g_free (user);
649 g_free (dir);
652 static int
653 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
655 struct sockaddr_in server_address;
656 struct hostent *hp;
657 int my_socket;
658 char *host;
659 int port = SUP.port;
660 int free_host = 0;
662 (void) me;
664 /* Use a proxy host? */
665 host = SUP.host;
667 if (!host || !*host){
668 print_vfs_message (_("ftpfs: Invalid host name."));
669 ftpfs_errno = EINVAL;
670 return -1;
673 /* Hosts to connect to that start with a ! should use proxy */
674 if (SUP.proxy){
675 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &port);
676 free_host = 1;
679 enable_interrupt_key(); /* clear the interrupt flag */
681 /* Get host address */
682 memset ((char *) &server_address, 0, sizeof (server_address));
683 server_address.sin_family = AF_INET;
684 server_address.sin_addr.s_addr = inet_addr (host);
685 if (server_address.sin_addr.s_addr == INADDR_NONE) {
686 hp = gethostbyname (host);
687 if (hp == NULL){
688 disable_interrupt_key();
689 print_vfs_message (_("ftpfs: Invalid host address."));
690 ftpfs_errno = EINVAL;
691 if (free_host)
692 g_free (host);
693 return -1;
695 server_address.sin_family = hp->h_addrtype;
697 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
698 memcpy ((char *) &server_address.sin_addr, (char *) hp->h_addr, 4);
701 server_address.sin_port = htons (port);
703 /* Connect */
704 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
705 disable_interrupt_key();
706 ftpfs_errno = errno;
707 if (free_host)
708 g_free (host);
709 return -1;
712 print_vfs_message (_("ftpfs: making connection to %s"), host);
713 if (free_host)
714 g_free (host);
716 if (connect (my_socket, (struct sockaddr *) &server_address,
717 sizeof (server_address)) < 0){
718 ftpfs_errno = errno;
719 if (errno == EINTR && got_interrupt ())
720 print_vfs_message (_("ftpfs: connection interrupted by user"));
721 else
722 print_vfs_message (_("ftpfs: connection to server failed: %s"),
723 unix_error_string(errno));
724 disable_interrupt_key();
725 close (my_socket);
726 return -1;
728 disable_interrupt_key();
729 return my_socket;
732 static int
733 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
735 int retry_seconds, count_down;
737 /* We do not want to use the passive if we are using proxies */
738 if (SUP.proxy)
739 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
741 retry_seconds = 0;
742 do {
743 SUP.failed_on_login = 0;
745 SUP.sock = ftpfs_open_socket (me, super);
746 if (SUP.sock == -1)
747 return -1;
749 if (ftpfs_login_server (me, super, NULL)) {
750 /* Logged in, no need to retry the connection */
751 break;
752 } else {
753 if (SUP.failed_on_login){
754 /* Close only the socket descriptor */
755 close (SUP.sock);
756 } else {
757 return -1;
759 if (ftpfs_retry_seconds){
760 retry_seconds = ftpfs_retry_seconds;
761 enable_interrupt_key ();
762 for (count_down = retry_seconds; count_down; count_down--){
763 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
764 sleep (1);
765 if (got_interrupt ()){
766 /* ftpfs_errno = E; */
767 disable_interrupt_key ();
768 return 0;
771 disable_interrupt_key ();
774 } while (retry_seconds);
776 SUP.cwdir = ftpfs_get_current_directory (me, super);
777 if (!SUP.cwdir)
778 SUP.cwdir = g_strdup (PATH_SEP_STR);
779 return 0;
782 static int
783 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
784 const char *archive_name, char *op)
786 char *host, *user, *password;
787 int port;
789 (void) archive_name;
791 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
793 SUP.host = host;
794 SUP.user = user;
795 SUP.port = port;
796 SUP.cwdir = NULL;
797 SUP.proxy = 0;
798 if (ftpfs_check_proxy (host))
799 SUP.proxy = ftpfs_proxy_host;
800 SUP.password = password;
801 SUP.use_passive_connection = ftpfs_use_passive_connections;
802 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
803 SUP.isbinary = TYPE_UNKNOWN;
804 SUP.remote_is_amiga = 0;
805 super->name = g_strdup ("/");
806 super->root =
807 vfs_s_new_inode (me, super,
808 vfs_s_default_stat (me, S_IFDIR | 0755));
810 return ftpfs_open_archive_int (me, super);
813 static int
814 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
815 const char *archive_name, char *op, void *cookie)
817 char *host, *user;
818 int port;
820 (void) me;
821 (void) archive_name;
822 (void) cookie;
824 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
826 port = ((strcmp (host, SUP.host) == 0)
827 && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
829 g_free (host);
830 g_free (user);
832 return port;
835 /* The returned directory should always contain a trailing slash */
836 static char *
837 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
839 char buf[BUF_8K], *bufp, *bufq;
841 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
842 ftpfs_get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
843 bufp = NULL;
844 for (bufq = buf; *bufq; bufq++)
845 if (*bufq == '"') {
846 if (!bufp) {
847 bufp = bufq + 1;
848 } else {
849 *bufq = 0;
850 if (*bufp) {
851 if (*(bufq - 1) != '/') {
852 *bufq++ = '/';
853 *bufq = 0;
855 if (*bufp == '/')
856 return g_strdup (bufp);
857 else {
858 /* If the remote server is an Amiga a leading slash
859 might be missing. MC needs it because it is used
860 as separator between hostname and path internally. */
861 return g_strconcat( "/", bufp, NULL);
863 } else {
864 ftpfs_errno = EIO;
865 return NULL;
870 ftpfs_errno = EIO;
871 return NULL;
875 /* Setup Passive ftp connection, we use it for source routed connections */
876 static int
877 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super, int my_socket, struct sockaddr_in *sa)
879 int xa, xb, xc, xd, xe, xf;
880 char n [6];
881 char *c;
883 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
884 return 0;
886 /* Parse remote parameters */
887 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++)
889 if (!*c)
890 return 0;
891 if (!isdigit ((unsigned char) *c))
892 return 0;
893 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
894 return 0;
895 n [0] = (unsigned char) xa;
896 n [1] = (unsigned char) xb;
897 n [2] = (unsigned char) xc;
898 n [3] = (unsigned char) xd;
899 n [4] = (unsigned char) xe;
900 n [5] = (unsigned char) xf;
902 memcpy (&(sa->sin_addr.s_addr), (void *)n, 4);
903 memcpy (&(sa->sin_port), (void *)&n[4], 2);
904 if (connect (my_socket, (struct sockaddr *) sa, sizeof (struct sockaddr_in)) < 0)
905 return 0;
906 return 1;
909 static int
910 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
912 struct sockaddr_in data_addr;
913 int data;
914 socklen_t len = sizeof(data_addr);
915 struct protoent *pe;
917 pe = getprotobyname ("tcp");
918 if (pe == NULL)
919 ERRNOR (EIO, -1);
920 again:
921 if (getsockname (SUP.sock, (struct sockaddr *) &data_addr, &len) == -1)
922 ERRNOR (EIO, -1);
923 data_addr.sin_port = 0;
925 data = socket (AF_INET, SOCK_STREAM, pe->p_proto);
926 if (data < 0)
927 ERRNOR (EIO, -1);
929 if (SUP.use_passive_connection) {
930 if (ftpfs_setup_passive (me, super, data, &data_addr))
931 return data;
933 SUP.use_passive_connection = 0;
934 print_vfs_message (_("ftpfs: could not setup passive mode"));
936 /* data or data_addr may be damaged by ftpfs_setup_passive */
937 close (data);
938 goto again;
941 /* If passive setup fails, fallback to active connections */
942 /* Active FTP connection */
943 if ((bind (data, (struct sockaddr *)&data_addr, len) == 0) &&
944 (getsockname (data, (struct sockaddr *) &data_addr, &len) == 0) &&
945 (listen (data, 1) == 0))
947 unsigned char *a = (unsigned char *)&data_addr.sin_addr;
948 unsigned char *p = (unsigned char *)&data_addr.sin_port;
950 if (ftpfs_command (me, super, WAIT_REPLY, "PORT %d,%d,%d,%d,%d,%d", a[0], a[1],
951 a[2], a[3], p[0], p[1]) == COMPLETE)
952 return data;
954 close (data);
955 ftpfs_errno = EIO;
956 return -1;
959 static int
960 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
961 const char *remote, int isbinary, int reget)
963 struct sockaddr_in from;
964 int s, j, data;
965 socklen_t fromlen = sizeof(from);
967 if ((s = ftpfs_initconn (me, super)) == -1)
968 return -1;
969 if (ftpfs_changetype (me, super, isbinary) == -1)
970 return -1;
971 if (reget > 0){
972 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
973 if (j != CONTINUE)
974 return -1;
976 if (remote) {
977 char *remote_path = ftpfs_translate_path (me, super, remote);
978 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
979 /* WarFtpD can't STORE //filename */
980 (*remote_path == '/') ? remote_path + 1 : remote_path);
981 g_free (remote_path);
982 } else
983 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
984 if (j != PRELIM)
985 ERRNOR (EPERM, -1);
986 enable_interrupt_key();
987 if (SUP.use_passive_connection)
988 data = s;
989 else {
990 data = accept (s, (struct sockaddr *)&from, &fromlen);
991 if (data < 0) {
992 ftpfs_errno = errno;
993 close (s);
994 return -1;
996 close (s);
998 disable_interrupt_key();
999 return data;
1002 #define ABORT_TIMEOUT 5
1003 static void
1004 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
1006 struct vfs_s_super *super = FH_SUPER;
1007 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1008 fd_set mask;
1009 char buf[1024];
1010 int dsock = FH_SOCK;
1011 FH_SOCK = -1;
1012 SUP.ctl_connection_busy = 0;
1014 print_vfs_message (_("ftpfs: aborting transfer."));
1015 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
1016 print_vfs_message (_("ftpfs: abort error: %s"),
1017 unix_error_string (errno));
1018 if (dsock != -1)
1019 close (dsock);
1020 return;
1023 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
1024 print_vfs_message (_("ftpfs: abort failed"));
1025 if (dsock != -1)
1026 close (dsock);
1027 return;
1029 if (dsock != -1) {
1030 FD_ZERO (&mask);
1031 FD_SET (dsock, &mask);
1032 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1033 struct timeval start_tim, tim;
1034 gettimeofday (&start_tim, NULL);
1035 /* flush the remaining data */
1036 while (read (dsock, buf, sizeof (buf)) > 0) {
1037 gettimeofday (&tim, NULL);
1038 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1039 /* server keeps sending, drop the connection and ftpfs_reconnect */
1040 close (dsock);
1041 ftpfs_reconnect (me, super);
1042 return;
1046 close (dsock);
1048 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1049 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1052 #if 0
1053 static void
1054 resolve_symlink_without_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1056 struct linklist *flist;
1057 struct direntry *fe, *fel;
1058 char tmp[MC_MAXPATHLEN];
1059 int depth;
1061 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1062 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1063 /* flist->data->l_stat is alread initialized with 0 */
1064 fel = flist->data;
1065 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1066 if (fel->linkname[0] == '/') {
1067 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1068 continue;
1069 strcpy (tmp, fel->linkname);
1070 } else {
1071 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1072 continue;
1073 strcpy (tmp, dir->remote_path);
1074 if (tmp[1] != '\0')
1075 strcat (tmp, "/");
1076 strcat (tmp + 1, fel->linkname);
1078 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1079 canonicalize_pathname (tmp);
1080 fe = _get_file_entry(bucket, tmp, 0, 0);
1081 if (fe) {
1082 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1083 /* Symlink points to link which isn't resolved, yet. */
1084 if (fe->linkname[0] == '/') {
1085 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1086 break;
1087 strcpy (tmp, fe->linkname);
1088 } else {
1089 /* at this point tmp looks always like this
1090 /directory/filename, i.e. no need to check
1091 strrchr's return value */
1092 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1093 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1094 break;
1095 strcat (tmp, fe->linkname);
1097 continue;
1098 } else {
1099 fel->l_stat = g_new (struct stat, 1);
1100 if ( S_ISLNK (fe->s.st_mode))
1101 *fel->l_stat = *fe->l_stat;
1102 else
1103 *fel->l_stat = fe->s;
1104 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1107 break;
1111 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1114 static void
1115 resolve_symlink_with_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1117 char buffer[2048] = "", *filename;
1118 int sock;
1119 FILE *fp;
1120 struct stat s;
1121 struct linklist *flist;
1122 struct direntry *fe;
1123 int switch_method = 0;
1125 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1126 if (strchr (dir->remote_path, ' ')) {
1127 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1128 print_vfs_message(_("ftpfs: CWD failed."));
1129 return;
1131 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1133 else
1134 sock = ftpfs_open_data_connection (bucket, "LIST -lLa",
1135 dir->remote_path, TYPE_ASCII, 0);
1137 if (sock == -1) {
1138 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1139 return;
1142 fp = fdopen(sock, "r");
1143 if (fp == NULL) {
1144 close(sock);
1145 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1146 return;
1148 enable_interrupt_key();
1149 flist = dir->file_list->next;
1150 while (1) {
1151 do {
1152 if (flist == dir->file_list)
1153 goto done;
1154 fe = flist->data;
1155 flist = flist->next;
1156 } while (!S_ISLNK(fe->s.st_mode));
1157 while (1) {
1158 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1159 goto done;
1160 if (MEDATA->logfile){
1161 fputs (buffer, MEDATA->logfile);
1162 fflush (MEDATA->logfile);
1164 vfs_die("This code should be commented out\n");
1165 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1166 int r = strcmp(fe->name, filename);
1167 g_free(filename);
1168 if (r == 0) {
1169 if (S_ISLNK (s.st_mode)) {
1170 /* This server doesn't understand LIST -lLa */
1171 switch_method = 1;
1172 goto done;
1174 fe->l_stat = g_new (struct stat, 1);
1175 if (fe->l_stat == NULL)
1176 goto done;
1177 *fe->l_stat = s;
1178 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1179 break;
1181 if (r < 0)
1182 break;
1186 done:
1187 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1188 disable_interrupt_key();
1189 fclose(fp);
1190 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1193 static void
1194 resolve_symlink(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1196 print_vfs_message(_("Resolving symlink..."));
1198 if (SUP.strict_rfc959_list_cmd)
1199 resolve_symlink_without_ls_options(me, super, dir);
1200 else
1201 resolve_symlink_with_ls_options(me, super, dir);
1203 #endif
1205 static int
1206 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1208 struct vfs_s_entry *ent;
1209 struct vfs_s_super *super = dir->super;
1210 int sock, num_entries = 0;
1211 char buffer[BUF_8K];
1212 int cd_first;
1214 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1215 || (strchr (remote_path, ' ') != NULL);
1217 again:
1218 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1219 remote_path,
1220 SUP.strict ==
1221 RFC_STRICT ? _("(strict rfc959)") : "",
1222 cd_first ? _("(chdir first)") : "");
1224 if (cd_first) {
1225 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE) {
1226 ftpfs_errno = ENOENT;
1227 print_vfs_message (_("ftpfs: CWD failed."));
1228 return -1;
1232 gettimeofday (&dir->timestamp, NULL);
1233 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1235 if (SUP.strict == RFC_STRICT)
1236 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1237 else if (cd_first)
1238 /* Dirty hack to avoid autoprepending / to . */
1239 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1240 sock =
1241 ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1242 else {
1243 /* Trailing "/." is necessary if remote_path is a symlink */
1244 char *path = concat_dir_and_file (remote_path, ".");
1245 sock =
1246 ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII,
1248 g_free (path);
1251 if (sock == -1)
1252 goto fallback;
1254 /* Clear the interrupt flag */
1255 enable_interrupt_key ();
1257 while (1) {
1258 int i;
1259 int res =
1260 vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1261 sock);
1262 if (!res)
1263 break;
1265 if (res == EINTR) {
1266 me->verrno = ECONNRESET;
1267 close (sock);
1268 disable_interrupt_key ();
1269 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1270 print_vfs_message (_("%s: failure"), me->name);
1271 return -1;
1274 if (MEDATA->logfile) {
1275 fputs (buffer, MEDATA->logfile);
1276 fputs ("\n", MEDATA->logfile);
1277 fflush (MEDATA->logfile);
1280 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1281 i = ent->ino->st.st_nlink;
1282 if (!vfs_parse_ls_lga
1283 (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1284 vfs_s_free_entry (me, ent);
1285 continue;
1287 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1288 num_entries++;
1289 vfs_s_insert_entry (me, dir, ent);
1292 close (sock);
1293 me->verrno = E_REMOTE;
1294 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1295 goto fallback;
1297 if (num_entries == 0 && cd_first == 0) {
1298 /* The LIST command may produce an empty output. In such scenario
1299 it is not clear whether this is caused by `remote_path' being
1300 a non-existent path or for some other reason (listing emtpy
1301 directory without the -a option, non-readable directory, etc.).
1303 Since `dir_load' is a crucial method, when it comes to determine
1304 whether a given path is a _directory_, the code must try its best
1305 to determine the type of `remote_path'. The only reliable way to
1306 achieve this is trough issuing a CWD command. */
1308 cd_first = 1;
1309 goto again;
1312 if (SUP.strict == RFC_AUTODETECT)
1313 SUP.strict = RFC_DARING;
1315 print_vfs_message (_("%s: done."), me->name);
1316 return 0;
1318 fallback:
1319 if (SUP.strict == RFC_AUTODETECT) {
1320 /* It's our first attempt to get a directory listing from this
1321 server (UNIX style LIST command) */
1322 SUP.strict = RFC_STRICT;
1323 /* I hate goto, but recursive call needs another 8K on stack */
1324 /* return ftpfs_dir_load (me, dir, remote_path); */
1325 cd_first = 1;
1326 goto again;
1328 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1329 ERRNOR (EACCES, -1);
1332 static int
1333 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name,
1334 char *localname)
1336 int h, sock, n_read, n_written;
1337 off_t n_stored;
1338 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1339 struct linger li;
1340 #else
1341 int flag_one = 1;
1342 #endif
1343 char buffer[8192];
1344 struct stat s;
1345 char *w_buf;
1346 struct vfs_s_super *super = FH_SUPER;
1348 h = open (localname, O_RDONLY);
1349 if (h == -1)
1350 ERRNOR (EIO, -1);
1351 sock =
1352 ftpfs_open_data_connection (me, super,
1353 fh->u.ftp.append ? "APPE" : "STOR", name,
1354 TYPE_BINARY, 0);
1355 if (sock < 0 || fstat (h, &s) == -1) {
1356 close (h);
1357 return -1;
1359 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1360 li.l_onoff = 1;
1361 li.l_linger = 120;
1362 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1363 #else
1364 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1365 #endif
1366 n_stored = 0;
1368 enable_interrupt_key ();
1369 while (1) {
1370 while ((n_read = read (h, buffer, sizeof (buffer))) == -1) {
1371 if (errno == EINTR) {
1372 if (got_interrupt ()) {
1373 ftpfs_errno = EINTR;
1374 goto error_return;
1375 } else
1376 continue;
1378 ftpfs_errno = errno;
1379 goto error_return;
1381 if (n_read == 0)
1382 break;
1383 n_stored += n_read;
1384 w_buf = buffer;
1385 while ((n_written = write (sock, w_buf, n_read)) != n_read) {
1386 if (n_written == -1) {
1387 if (errno == EINTR && !got_interrupt ()) {
1388 continue;
1390 ftpfs_errno = errno;
1391 goto error_return;
1393 w_buf += n_written;
1394 n_read -= n_written;
1396 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1397 (unsigned long) n_stored, (unsigned long) s.st_size);
1399 disable_interrupt_key ();
1400 close (sock);
1401 close (h);
1402 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1403 ERRNOR (EIO, -1);
1404 return 0;
1405 error_return:
1406 disable_interrupt_key ();
1407 close (sock);
1408 close (h);
1409 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1410 return -1;
1413 static int
1414 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1416 char *name = vfs_s_fullpath (me, fh->ino);
1418 if (!name)
1419 return 0;
1420 FH_SOCK = ftpfs_open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1421 g_free (name);
1422 if (FH_SOCK == -1)
1423 ERRNOR (EACCES, 0);
1424 fh->linear = LS_LINEAR_OPEN;
1425 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1426 fh->u.ftp.append = 0;
1427 return 1;
1430 static int
1431 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1433 int n;
1434 struct vfs_s_super *super = FH_SUPER;
1436 while ((n = read (FH_SOCK, buf, len))<0) {
1437 if ((errno == EINTR) && !got_interrupt())
1438 continue;
1439 break;
1442 if (n<0)
1443 ftpfs_linear_abort(me, fh);
1445 if (!n) {
1446 SUP.ctl_connection_busy = 0;
1447 close (FH_SOCK);
1448 FH_SOCK = -1;
1449 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1450 ERRNOR (E_REMOTE, -1);
1451 return 0;
1453 ERRNOR (errno, n);
1456 static void
1457 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1459 if (FH_SOCK != -1)
1460 ftpfs_linear_abort(me, fh);
1463 static int ftpfs_ctl (void *fh, int ctlop, void *arg)
1465 (void) arg;
1467 switch (ctlop) {
1468 case VFS_CTL_IS_NOTREADY:
1470 int v;
1472 if (!FH->linear)
1473 vfs_die ("You may not do this");
1474 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1475 return 0;
1477 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1478 if (((v < 0) && (errno == EINTR)) || v == 0)
1479 return 1;
1480 return 0;
1482 default:
1483 return 0;
1487 static int
1488 ftpfs_send_command(struct vfs_class *me, const char *filename, const char *cmd, int flags)
1490 const char *rpath;
1491 char *p, *mpath = g_strdup(filename);
1492 struct vfs_s_super *super;
1493 int r;
1494 int flush_directory_cache = (flags & OPT_FLUSH);
1496 if (!(rpath = vfs_s_get_path_mangle(me, mpath, &super, 0))) {
1497 g_free(mpath);
1498 return -1;
1500 p = ftpfs_translate_path (me, super, rpath);
1501 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1502 g_free (p);
1503 vfs_stamp_create (&vfs_ftpfs_ops, super);
1504 if (flags & OPT_IGNORE_ERROR)
1505 r = COMPLETE;
1506 if (r != COMPLETE) {
1507 me->verrno = EPERM;
1508 g_free (mpath);
1509 return -1;
1511 if (flush_directory_cache)
1512 vfs_s_invalidate(me, super);
1513 g_free(mpath);
1514 return 0;
1517 /* This routine is called as the last step in load_setup */
1518 void
1519 ftpfs_init_passwd(void)
1521 ftpfs_anonymous_passwd = load_anon_passwd ();
1522 if (ftpfs_anonymous_passwd)
1523 return;
1525 /* If there is no anonymous ftp password specified
1526 * then we'll just use anonymous@
1527 * We don't send any other thing because:
1528 * - We want to remain anonymous
1529 * - We want to stop SPAM
1530 * - We don't want to let ftp sites to discriminate by the user,
1531 * host or country.
1533 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1536 static int ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1538 char buf[BUF_SMALL];
1540 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1541 return ftpfs_send_command(me, path, buf, OPT_FLUSH);
1544 static int ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1546 #if 0
1547 ftpfs_errno = EPERM;
1548 return -1;
1549 #else
1550 /* Everyone knows it is not possible to chown remotely, so why bother them.
1551 If someone's root, then copy/move will always try to chown it... */
1552 (void) me;
1553 (void) path;
1554 (void) owner;
1555 (void) group;
1556 return 0;
1557 #endif
1560 static int ftpfs_unlink (struct vfs_class *me, const char *path)
1562 return ftpfs_send_command(me, path, "DELE /%s", OPT_FLUSH);
1565 /* Return 1 if path is the same directory as the one we are in now */
1566 static int
1567 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1569 (void) me;
1571 if (!SUP.cwdir)
1572 return 0;
1573 if (strcmp (path, SUP.cwdir) == 0)
1574 return 1;
1575 return 0;
1578 static int
1579 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1581 int r;
1582 char *p;
1584 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1585 return COMPLETE;
1587 p = ftpfs_translate_path (me, super, remote_path);
1588 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1589 g_free (p);
1591 if (r != COMPLETE) {
1592 ftpfs_errno = EIO;
1593 } else {
1594 g_free(SUP.cwdir);
1595 SUP.cwdir = g_strdup (remote_path);
1596 SUP.cwd_deferred = 0;
1598 return r;
1601 static int ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1603 ftpfs_send_command(me, path1, "RNFR /%s", OPT_FLUSH);
1604 return ftpfs_send_command(me, path2, "RNTO /%s", OPT_FLUSH);
1607 static int ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1609 (void) mode; /* FIXME: should be used */
1611 return ftpfs_send_command(me, path, "MKD /%s", OPT_FLUSH);
1614 static int ftpfs_rmdir (struct vfs_class *me, const char *path)
1616 return ftpfs_send_command(me, path, "RMD /%s", OPT_FLUSH);
1619 static int
1620 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
1621 int mode)
1623 (void) mode;
1625 fh->u.ftp.append = 0;
1626 /* File will be written only, so no need to retrieve it from ftp server */
1627 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
1628 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1629 struct linger li;
1630 #else
1631 int li = 1;
1632 #endif
1633 char *name;
1635 /* ftpfs_linear_start() called, so data will be written
1636 * to local temporary file and stored to ftp server
1637 * by vfs_s_close later
1639 if (FH_SUPER->u.ftp.ctl_connection_busy) {
1640 if (!fh->ino->localname) {
1641 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1642 fh->ino->ent->name);
1643 if (handle == -1)
1644 return -1;
1645 close (handle);
1646 fh->u.ftp.append = flags & O_APPEND;
1648 return 0;
1650 name = vfs_s_fullpath (me, fh->ino);
1651 if (!name)
1652 return -1;
1653 fh->handle =
1654 ftpfs_open_data_connection (me, fh->ino->super,
1655 (flags & O_APPEND) ? "APPE" :
1656 "STOR", name, TYPE_BINARY, 0);
1657 g_free (name);
1659 if (fh->handle < 0)
1660 return -1;
1661 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1662 li.l_onoff = 1;
1663 li.l_linger = 120;
1664 #endif
1665 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1667 if (fh->ino->localname) {
1668 unlink (fh->ino->localname);
1669 g_free (fh->ino->localname);
1670 fh->ino->localname = NULL;
1672 return 0;
1675 if (!fh->ino->localname)
1676 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1677 return -1;
1678 if (!fh->ino->localname)
1679 vfs_die ("retrieve_file failed to fill in localname");
1680 return 0;
1683 static int ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1685 if (fh->handle != -1 && !fh->ino->localname){
1686 close (fh->handle);
1687 fh->handle = -1;
1688 /* File is stored to destination already, so
1689 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1691 fh->changed = 0;
1692 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1693 ERRNOR (EIO, -1);
1694 vfs_s_invalidate (me, FH_SUPER);
1696 return 0;
1699 static void
1700 ftpfs_done (struct vfs_class *me)
1702 struct no_proxy_entry *np;
1704 (void) me;
1706 while (no_proxy) {
1707 np = no_proxy->next;
1708 g_free (no_proxy->domain);
1709 g_free (no_proxy);
1710 no_proxy = np;
1712 g_free (ftpfs_anonymous_passwd);
1713 g_free (ftpfs_proxy_host);
1716 static void
1717 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
1719 struct vfs_s_super *super = MEDATA->supers;
1720 char *name;
1722 while (super){
1723 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
1724 (*func)(name);
1725 g_free (name);
1726 super = super->next;
1730 static char buffer[BUF_MEDIUM];
1731 static char *netrc;
1732 static const char *netrcp;
1734 /* This should match the keywords[] array below */
1735 typedef enum {
1736 NETRC_NONE = 0,
1737 NETRC_DEFAULT,
1738 NETRC_MACHINE,
1739 NETRC_LOGIN,
1740 NETRC_PASSWORD,
1741 NETRC_PASSWD,
1742 NETRC_ACCOUNT,
1743 NETRC_MACDEF,
1744 NETRC_UNKNOWN
1745 } keyword_t;
1747 static keyword_t ftpfs_netrc_next (void)
1749 char *p;
1750 keyword_t i;
1751 static const char *const keywords[] = { "default", "machine",
1752 "login", "password", "passwd", "account", "macdef", NULL
1756 while (1) {
1757 netrcp = skip_separators (netrcp);
1758 if (*netrcp != '\n')
1759 break;
1760 netrcp++;
1762 if (!*netrcp)
1763 return NETRC_NONE;
1764 p = buffer;
1765 if (*netrcp == '"') {
1766 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1767 if (*netrcp == '\\')
1768 netrcp++;
1769 *p++ = *netrcp;
1771 } else {
1772 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1773 *netrcp != ',' && *netrcp; netrcp++) {
1774 if (*netrcp == '\\')
1775 netrcp++;
1776 *p++ = *netrcp;
1779 *p = 0;
1780 if (!*buffer)
1781 return NETRC_NONE;
1783 i = NETRC_DEFAULT;
1784 while (keywords[i - 1]) {
1785 if (!strcmp (keywords[i - 1], buffer))
1786 return i;
1788 i++;
1791 return NETRC_UNKNOWN;
1794 static int ftpfs_netrc_bad_mode (const char *netrcname)
1796 static int be_angry = 1;
1797 struct stat mystat;
1799 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1800 if (be_angry) {
1801 message (D_ERROR, MSG_ERROR,
1802 _("~/.netrc file has incorrect mode.\n"
1803 "Remove password or correct mode."));
1804 be_angry = 0;
1806 return 1;
1808 return 0;
1811 /* Scan .netrc until we find matching "machine" or "default"
1812 * domain is used for additional matching
1813 * No search is done after "default" in compliance with "man netrc"
1814 * Return 0 if found, -1 otherwise */
1815 static int ftpfs_find_machine (const char *host, const char *domain)
1817 keyword_t keyword;
1819 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) {
1820 if (keyword == NETRC_DEFAULT)
1821 return 0;
1823 if (keyword == NETRC_MACDEF) {
1824 /* Scan for an empty line, which concludes "macdef" */
1825 do {
1826 while (*netrcp && *netrcp != '\n')
1827 netrcp++;
1828 if (*netrcp != '\n')
1829 break;
1830 netrcp++;
1831 } while (*netrcp && *netrcp != '\n');
1832 continue;
1835 if (keyword != NETRC_MACHINE)
1836 continue;
1838 /* Take machine name */
1839 if (ftpfs_netrc_next () == NETRC_NONE)
1840 break;
1842 if (g_strcasecmp (host, buffer)) {
1843 /* Try adding our domain to short names in .netrc */
1844 const char *host_domain = strchr (host, '.');
1845 if (!host_domain)
1846 continue;
1848 /* Compare domain part */
1849 if (g_strcasecmp (host_domain, domain))
1850 continue;
1852 /* Compare local part */
1853 if (g_strncasecmp (host, buffer, host_domain - host))
1854 continue;
1857 return 0;
1860 /* end of .netrc */
1861 return -1;
1864 /* Extract login and password from .netrc for the host.
1865 * pass may be NULL.
1866 * Returns 0 for success, -1 for error */
1867 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass)
1869 char *netrcname;
1870 char *tmp_pass = NULL;
1871 char hostname[MAXHOSTNAMELEN];
1872 const char *domain;
1873 keyword_t keyword;
1874 static struct rupcache {
1875 struct rupcache *next;
1876 char *host;
1877 char *login;
1878 char *pass;
1879 } *rup_cache = NULL, *rupp;
1881 /* Initialize *login and *pass */
1882 if (!login)
1883 return 0;
1884 *login = NULL;
1885 if (pass)
1886 *pass = NULL;
1888 /* Look up in the cache first */
1889 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
1890 if (!strcmp (host, rupp->host)) {
1891 if (rupp->login)
1892 *login = g_strdup (rupp->login);
1893 if (pass && rupp->pass)
1894 *pass = g_strdup (rupp->pass);
1895 return 0;
1899 /* Load current .netrc */
1900 netrcname = concat_dir_and_file (home_dir, ".netrc");
1901 netrcp = netrc = load_file (netrcname);
1902 if (netrc == NULL) {
1903 g_free (netrcname);
1904 return 0;
1907 /* Find our own domain name */
1908 if (gethostname (hostname, sizeof (hostname)) < 0)
1909 *hostname = 0;
1910 if (!(domain = strchr (hostname, '.')))
1911 domain = "";
1913 /* Scan for "default" and matching "machine" keywords */
1914 ftpfs_find_machine (host, domain);
1916 /* Scan for keywords following "default" and "machine" */
1917 while (1) {
1918 int need_break = 0;
1919 keyword = ftpfs_netrc_next ();
1921 switch (keyword) {
1922 case NETRC_LOGIN:
1923 if (ftpfs_netrc_next () == NETRC_NONE) {
1924 need_break = 1;
1925 break;
1928 /* We have another name already - should not happen */
1929 if (*login) {
1930 need_break = 1;
1931 break;
1934 /* We have login name now */
1935 *login = g_strdup (buffer);
1936 break;
1938 case NETRC_PASSWORD:
1939 case NETRC_PASSWD:
1940 if (ftpfs_netrc_next () == NETRC_NONE) {
1941 need_break = 1;
1942 break;
1945 /* Ignore unsafe passwords */
1946 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
1947 && ftpfs_netrc_bad_mode (netrcname)) {
1948 need_break = 1;
1949 break;
1952 /* Remember password. pass may be NULL, so use tmp_pass */
1953 if (tmp_pass == NULL)
1954 tmp_pass = g_strdup (buffer);
1955 break;
1957 case NETRC_ACCOUNT:
1958 /* "account" is followed by a token which we ignore */
1959 if (ftpfs_netrc_next () == NETRC_NONE) {
1960 need_break = 1;
1961 break;
1964 /* Ignore account, but warn user anyways */
1965 ftpfs_netrc_bad_mode (netrcname);
1966 break;
1968 default:
1969 /* Unexpected keyword or end of file */
1970 need_break = 1;
1971 break;
1974 if (need_break)
1975 break;
1978 g_free (netrc);
1979 g_free (netrcname);
1981 rupp = g_new (struct rupcache, 1);
1982 rupp->host = g_strdup (host);
1983 rupp->login = rupp->pass = 0;
1985 if (*login != NULL) {
1986 rupp->login = g_strdup (*login);
1988 if (tmp_pass != NULL)
1989 rupp->pass = g_strdup (tmp_pass);
1990 rupp->next = rup_cache;
1991 rup_cache = rupp;
1993 if (pass)
1994 *pass = tmp_pass;
1996 return 0;
1999 void
2000 init_ftpfs (void)
2002 static struct vfs_s_subclass ftpfs_subclass;
2004 ftpfs_subclass.flags = VFS_S_REMOTE;
2005 ftpfs_subclass.archive_same = ftpfs_archive_same;
2006 ftpfs_subclass.open_archive = ftpfs_open_archive;
2007 ftpfs_subclass.free_archive = ftpfs_free_archive;
2008 ftpfs_subclass.fh_open = ftpfs_fh_open;
2009 ftpfs_subclass.fh_close = ftpfs_fh_close;
2010 ftpfs_subclass.dir_load = ftpfs_dir_load;
2011 ftpfs_subclass.file_store = ftpfs_file_store;
2012 ftpfs_subclass.linear_start = ftpfs_linear_start;
2013 ftpfs_subclass.linear_read = ftpfs_linear_read;
2014 ftpfs_subclass.linear_close = ftpfs_linear_close;
2016 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2017 vfs_ftpfs_ops.name = "ftpfs";
2018 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2019 vfs_ftpfs_ops.prefix = "ftp:";
2020 vfs_ftpfs_ops.done = &ftpfs_done;
2021 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2022 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2023 vfs_ftpfs_ops.chown = ftpfs_chown;
2024 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2025 vfs_ftpfs_ops.rename = ftpfs_rename;
2026 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2027 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2028 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2029 vfs_register_class (&vfs_ftpfs_ops);