Revert patches from slavaz and moved them into a separate branch
[midnight-commander.git] / vfs / ftpfs.c
blob43d363c31680c263522e84cdb730cabef26c2ed4
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 /* FTPfs TODO:
27 - make it more robust - all the connects etc. should handle EADDRINUSE and
28 ERETRY (have I spelled these names correctly?)
29 - make the user able to flush a connection - all the caches will get empty
30 etc., (tarfs as well), we should give there a user selectable timeout
31 and assign a key sequence.
32 - use hash table instead of linklist to cache ftpfs directory.
34 What to do with this?
37 * NOTE: Usage of tildes is deprecated, consider:
38 * cd /#ftp:pavel@hobit
39 * cd ~
40 * And now: what do I want to do? Do I want to go to /home/pavel or to
41 * /#ftp:hobit/home/pavel? I think first has better sense...
44 int f = !strcmp( remote_path, "/~" );
45 if (f || !strncmp( remote_path, "/~/", 3 )) {
46 char *s;
47 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
48 g_free (remote_path);
49 remote_path = s;
56 /* Namespace pollution: horrible */
58 #include <config.h>
59 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
60 #include <netdb.h> /* struct hostent */
61 #include <sys/socket.h> /* AF_INET */
62 #include <netinet/in.h> /* struct in_addr */
63 #ifdef HAVE_ARPA_INET_H
64 #include <arpa/inet.h>
65 #endif
66 #include <arpa/ftp.h>
67 #include <arpa/telnet.h>
68 #include <sys/param.h>
69 #include <errno.h>
70 #include <ctype.h>
72 #include "../src/global.h"
73 #include "../src/tty.h" /* enable/disable interrupt key */
74 #include "../src/wtools.h" /* message() */
75 #include "../src/main.h" /* print_vfs_message */
76 #include "utilvfs.h"
77 #include "xdirentry.h"
78 #include "vfs.h"
79 #include "vfs-impl.h"
80 #include "gc.h" /* vfs_stamp_create */
81 #include "tcputil.h"
82 #include "../src/setup.h" /* for load_anon_passwd */
83 #include "ftpfs.h"
84 #ifndef MAXHOSTNAMELEN
85 # define MAXHOSTNAMELEN 64
86 #endif
88 #define UPLOAD_ZERO_LENGTH_FILE
89 #define SUP super->u.ftp
90 #define FH_SOCK fh->u.ftp.sock
92 #ifndef INADDR_NONE
93 #define INADDR_NONE 0xffffffff
94 #endif
96 #define RFC_AUTODETECT 0
97 #define RFC_DARING 1
98 #define RFC_STRICT 2
100 #ifndef HAVE_SOCKLEN_T
101 typedef int socklen_t;
102 #endif
104 static int ftpfs_errno;
105 static int code;
107 /* Delay to retry a connection */
108 int ftpfs_retry_seconds = 30;
110 /* Method to use to connect to ftp sites */
111 int ftpfs_use_passive_connections = 1;
112 int ftpfs_use_passive_connections_over_proxy = 0;
114 /* Method used to get directory listings:
115 * 1: try 'LIST -la <path>', if it fails
116 * fall back to CWD <path>; LIST
117 * 0: always use CWD <path>; LIST
119 int ftpfs_use_unix_list_options = 1;
121 /* First "CWD <path>", then "LIST -la ." */
122 int ftpfs_first_cd_then_ls = 1;
124 /* Use the ~/.netrc */
125 int use_netrc = 1;
127 /* Anonymous setup */
128 char *ftpfs_anonymous_passwd = NULL;
129 int ftpfs_directory_timeout = 900;
131 /* Proxy host */
132 char *ftpfs_proxy_host = NULL;
134 /* wether we have to use proxy by default? */
135 int ftpfs_always_use_proxy;
137 #ifdef FIXME_LATER_ALIGATOR
138 static struct linklist *connections_list;
139 #endif
141 /* ftpfs_command wait_flag: */
142 #define NONE 0x00
143 #define WAIT_REPLY 0x01
144 #define WANT_STRING 0x02
145 static char reply_str [80];
147 static struct vfs_class vfs_ftpfs_ops;
149 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
150 Translate a Unix path, i.e. MC's internal path representation (e.g.
151 /somedir/somefile) to a path valid for the remote server. Every path
152 transfered to the remote server has to be mangled by this function
153 right prior to sending it.
154 Currently only Amiga ftp servers are handled in a special manner.
156 When the remote server is an amiga:
157 a) strip leading slash if necesarry
158 b) replace first occurance of ":/" with ":"
159 c) strip trailing "/."
162 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
163 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path);
164 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
165 __attribute__ ((format (__printf__, 4, 5)));
166 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
167 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass);
168 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
170 static char *
171 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
173 if (!SUP.remote_is_amiga)
174 return g_strdup (remote_path);
175 else {
176 char *ret, *p;
178 if (MEDATA->logfile) {
179 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
180 fflush (MEDATA->logfile);
183 /* strip leading slash(es) */
184 while (*remote_path == '/')
185 remote_path++;
188 * Don't change "/" into "", e.g. "CWD " would be
189 * invalid.
191 if (*remote_path == '\0')
192 return g_strdup (".");
194 ret = g_strdup (remote_path);
196 /* replace first occurance of ":/" with ":" */
197 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
198 strcpy (p + 1, p + 2);
200 /* strip trailing "/." */
201 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
202 *p = '\0';
203 return ret;
207 /* Extract the hostname and username from the path */
210 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
211 * ftp://sunsite.unc.edu/pub/linux
212 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
213 * ftp://tsx-11.mit.edu:8192/
214 * ftp://joe@foo.edu:11321/private
215 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
216 * is supplied.
220 #define FTP_COMMAND_PORT 21
222 static void
223 ftpfs_split_url(char *path, char **host, char **user, int *port, char **pass)
225 char *p;
227 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
228 URL_ALLOW_ANON);
230 if (!*user) {
231 /* Look up user and password in netrc */
232 if (use_netrc)
233 ftpfs_netrc_lookup (*host, user, pass);
234 if (!*user)
235 *user = g_strdup ("anonymous");
238 /* Look up password in netrc for known user */
239 if (use_netrc && *user && pass && !*pass) {
240 char *new_user;
242 ftpfs_netrc_lookup (*host, &new_user, pass);
244 /* If user is different, remove password */
245 if (new_user && strcmp (*user, new_user)) {
246 g_free (*pass);
247 *pass = NULL;
250 g_free (new_user);
253 g_free (p);
256 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
257 static int
258 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
260 char answer[BUF_1K];
261 int i;
263 for (;;) {
264 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
265 if (string_buf)
266 *string_buf = 0;
267 code = 421;
268 return 4;
270 switch (sscanf(answer, "%d", &code)){
271 case 0:
272 if (string_buf)
273 g_strlcpy (string_buf, answer, string_len);
274 code = 500;
275 return 5;
276 case 1:
277 if (answer[3] == '-') {
278 while (1) {
279 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
280 if (string_buf)
281 *string_buf = 0;
282 code = 421;
283 return 4;
285 if ((sscanf (answer, "%d", &i) > 0) &&
286 (code == i) && (answer[3] == ' '))
287 break;
290 if (string_buf)
291 g_strlcpy (string_buf, answer, string_len);
292 return code / 100;
297 static int
298 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
300 int sock = ftpfs_open_socket (me, super);
301 if (sock != -1){
302 char *cwdir = SUP.cwdir;
303 close (SUP.sock);
304 SUP.sock = sock;
305 SUP.cwdir = NULL;
306 if (ftpfs_login_server (me, super, SUP.password)){
307 if (!cwdir)
308 return 1;
309 sock = ftpfs_chdir_internal (me, super, cwdir);
310 g_free (cwdir);
311 return sock == COMPLETE;
313 SUP.cwdir = cwdir;
315 return 0;
318 static int
319 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
321 va_list ap;
322 char *cmdstr;
323 int status, cmdlen;
324 static int retry = 0;
325 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
327 va_start (ap, fmt);
328 cmdstr = g_strdup_vprintf (fmt, ap);
329 va_end (ap);
331 cmdlen = strlen (cmdstr);
332 cmdstr = g_realloc (cmdstr, cmdlen + 3);
333 strcpy (cmdstr + cmdlen, "\r\n");
334 cmdlen += 2;
336 if (MEDATA->logfile) {
337 if (strncmp (cmdstr, "PASS ", 5) == 0) {
338 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
339 } else
340 fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
342 fflush (MEDATA->logfile);
345 got_sigpipe = 0;
346 enable_interrupt_key ();
347 status = write (SUP.sock, cmdstr, cmdlen);
349 if (status < 0) {
350 code = 421;
352 if (errno == EPIPE) { /* Remote server has closed connection */
353 if (level == 0) {
354 level = 1;
355 status = ftpfs_reconnect (me, super);
356 level = 0;
357 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
358 goto ok;
362 got_sigpipe = 1;
364 g_free (cmdstr);
365 disable_interrupt_key ();
366 return TRANSIENT;
368 retry = 0;
370 disable_interrupt_key ();
372 if (wait_reply)
374 status = ftpfs_get_reply (me, SUP.sock,
375 (wait_reply & WANT_STRING) ? reply_str : NULL,
376 sizeof (reply_str) - 1);
377 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
379 retry = 1;
380 level = 1;
381 status = ftpfs_reconnect (me, super);
382 level = 0;
383 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
384 goto ok;
387 retry = 0;
388 g_free (cmdstr);
389 return status;
391 g_free (cmdstr);
392 return COMPLETE;
395 static void
396 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
398 if (SUP.sock != -1){
399 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
400 ftpfs_command(me, super, NONE, "QUIT");
401 close(SUP.sock);
403 g_free (SUP.host);
404 g_free (SUP.user);
405 g_free (SUP.cwdir);
406 g_free (SUP.password);
409 /* some defines only used by ftpfs_changetype */
410 /* These two are valid values for the second parameter */
411 #define TYPE_ASCII 0
412 #define TYPE_BINARY 1
414 /* This one is only used to initialize bucket->isbinary, don't use it as
415 second parameter to ftpfs_changetype. */
416 #define TYPE_UNKNOWN -1
418 static int
419 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
421 if (binary != SUP.isbinary) {
422 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
423 ERRNOR (EIO, -1);
424 SUP.isbinary = binary;
426 return binary;
429 /* This routine logs the user in */
430 static int
431 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
432 const char *netrcpass)
434 char *pass;
435 char *op;
436 char *name; /* login user name */
437 int anon = 0;
438 char reply_string[BUF_MEDIUM];
440 SUP.isbinary = TYPE_UNKNOWN;
442 if (SUP.password) /* explicit password */
443 op = g_strdup (SUP.password);
444 else if (netrcpass) /* password from netrc */
445 op = g_strdup (netrcpass);
446 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp")) {
447 if (!ftpfs_anonymous_passwd) /* default anonymous password */
448 ftpfs_init_passwd ();
449 op = g_strdup (ftpfs_anonymous_passwd);
450 anon = 1;
451 } else { /* ask user */
452 char *p;
454 p = g_strconcat (_(" FTP: Password required for "), SUP.user, " ",
455 NULL);
456 op = vfs_get_password (p);
457 g_free (p);
458 if (op == NULL)
459 ERRNOR (EPERM, 0);
460 SUP.password = g_strdup (op);
463 if (!anon || MEDATA->logfile)
464 pass = op;
465 else {
466 pass = g_strconcat ("-", op, (char *) NULL);
467 wipe_password (op);
470 /* Proxy server accepts: username@host-we-want-to-connect */
471 if (SUP.proxy) {
472 name =
473 g_strconcat (SUP.user, "@",
474 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host,
475 NULL);
476 } else
477 name = g_strdup (SUP.user);
479 if (ftpfs_get_reply
480 (me, SUP.sock, reply_string,
481 sizeof (reply_string) - 1) == COMPLETE) {
482 g_strup (reply_string);
483 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
484 if (MEDATA->logfile) {
485 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n",
486 SUP.remote_is_amiga);
487 fflush (MEDATA->logfile);
490 print_vfs_message (_("ftpfs: sending login name"));
492 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name)) {
493 case CONTINUE:
494 print_vfs_message (_("ftpfs: sending user password"));
495 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
496 if (code == CONTINUE) {
497 char *p;
499 p = g_strdup_printf (_
500 ("FTP: Account required for user %s"),
501 SUP.user);
502 op = input_dialog (p, _("Account:"), "");
503 g_free (p);
504 if (op == NULL)
505 ERRNOR (EPERM, 0);
506 print_vfs_message (_("ftpfs: sending user account"));
507 code =
508 ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
509 g_free (op);
511 if (code != COMPLETE)
512 break;
513 /* fall through */
515 case COMPLETE:
516 print_vfs_message (_("ftpfs: logged in"));
517 wipe_password (pass);
518 g_free (name);
519 return 1;
521 default:
522 SUP.failed_on_login = 1;
523 if (SUP.password)
524 wipe_password (SUP.password);
525 SUP.password = 0;
527 goto login_fail;
530 message (1, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
531 SUP.user);
532 login_fail:
533 wipe_password (pass);
534 g_free (name);
535 ERRNOR (EPERM, 0);
538 static struct no_proxy_entry {
539 char *domain;
540 void *next;
541 } *no_proxy;
543 static void
544 ftpfs_load_no_proxy_list (void)
546 /* FixMe: shouldn't be hardcoded!!! */
547 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
548 struct no_proxy_entry *np, *current = 0;
549 FILE *npf;
550 int c;
551 char *p;
552 static char *mc_file;
554 if (mc_file)
555 return;
557 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
558 if (exist_file (mc_file) &&
559 (npf = fopen (mc_file, "r"))) {
560 while (fgets (s, sizeof (s), npf)) {
561 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
562 while ((c = fgetc (npf)) != EOF && c != '\n')
564 continue;
567 if (p == s)
568 continue;
570 *p = '\0';
572 np = g_new (struct no_proxy_entry, 1);
573 np->domain = g_strdup (s);
574 np->next = NULL;
575 if (no_proxy)
576 current->next = np;
577 else
578 no_proxy = np;
579 current = np;
582 fclose (npf);
584 g_free (mc_file);
587 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
588 static int
589 ftpfs_check_proxy (const char *host)
591 struct no_proxy_entry *npe;
593 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
594 return 0; /* sanity check */
596 if (*host == '!')
597 return 1;
599 if (!ftpfs_always_use_proxy)
600 return 0;
602 if (!strchr (host, '.'))
603 return 0;
605 ftpfs_load_no_proxy_list ();
606 for (npe = no_proxy; npe; npe=npe->next) {
607 char *domain = npe->domain;
609 if (domain[0] == '.') {
610 int ld = strlen (domain);
611 int lh = strlen (host);
613 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
614 ld--;
615 lh--;
618 if (!ld)
619 return 0;
620 } else
621 if (!g_strcasecmp (host, domain))
622 return 0;
625 return 1;
628 static void
629 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
631 char *user, *dir;
633 dir =
634 vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT,
635 URL_ALLOW_ANON);
636 g_free (user);
637 g_free (dir);
640 static int
641 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
643 struct sockaddr_in server_address;
644 struct hostent *hp;
645 int my_socket;
646 char *host;
647 int port = SUP.port;
648 int free_host = 0;
650 (void) me;
652 /* Use a proxy host? */
653 host = SUP.host;
655 if (!host || !*host){
656 print_vfs_message (_("ftpfs: Invalid host name."));
657 ftpfs_errno = EINVAL;
658 return -1;
661 /* Hosts to connect to that start with a ! should use proxy */
662 if (SUP.proxy){
663 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &port);
664 free_host = 1;
667 enable_interrupt_key(); /* clear the interrupt flag */
669 /* Get host address */
670 memset ((char *) &server_address, 0, sizeof (server_address));
671 server_address.sin_family = AF_INET;
672 server_address.sin_addr.s_addr = inet_addr (host);
673 if (server_address.sin_addr.s_addr == INADDR_NONE) {
674 hp = gethostbyname (host);
675 if (hp == NULL){
676 disable_interrupt_key();
677 print_vfs_message (_("ftpfs: Invalid host address."));
678 ftpfs_errno = EINVAL;
679 if (free_host)
680 g_free (host);
681 return -1;
683 server_address.sin_family = hp->h_addrtype;
685 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
686 memcpy ((char *) &server_address.sin_addr, (char *) hp->h_addr, 4);
689 server_address.sin_port = htons (port);
691 /* Connect */
692 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
693 disable_interrupt_key();
694 ftpfs_errno = errno;
695 if (free_host)
696 g_free (host);
697 return -1;
700 print_vfs_message (_("ftpfs: making connection to %s"), host);
701 if (free_host)
702 g_free (host);
704 if (connect (my_socket, (struct sockaddr *) &server_address,
705 sizeof (server_address)) < 0){
706 ftpfs_errno = errno;
707 if (errno == EINTR && got_interrupt ())
708 print_vfs_message (_("ftpfs: connection interrupted by user"));
709 else
710 print_vfs_message (_("ftpfs: connection to server failed: %s"),
711 unix_error_string(errno));
712 disable_interrupt_key();
713 close (my_socket);
714 return -1;
716 disable_interrupt_key();
717 return my_socket;
720 static int
721 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
723 int retry_seconds, count_down;
725 /* We do not want to use the passive if we are using proxies */
726 if (SUP.proxy)
727 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
729 retry_seconds = 0;
730 do {
731 SUP.failed_on_login = 0;
733 SUP.sock = ftpfs_open_socket (me, super);
734 if (SUP.sock == -1)
735 return -1;
737 if (ftpfs_login_server (me, super, NULL)) {
738 /* Logged in, no need to retry the connection */
739 break;
740 } else {
741 if (SUP.failed_on_login){
742 /* Close only the socket descriptor */
743 close (SUP.sock);
744 } else {
745 return -1;
747 if (ftpfs_retry_seconds){
748 retry_seconds = ftpfs_retry_seconds;
749 enable_interrupt_key ();
750 for (count_down = retry_seconds; count_down; count_down--){
751 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
752 sleep (1);
753 if (got_interrupt ()){
754 /* ftpfs_errno = E; */
755 disable_interrupt_key ();
756 return 0;
759 disable_interrupt_key ();
762 } while (retry_seconds);
764 SUP.cwdir = ftpfs_get_current_directory (me, super);
765 if (!SUP.cwdir)
766 SUP.cwdir = g_strdup (PATH_SEP_STR);
767 return 0;
770 static int
771 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
772 const char *archive_name, char *op)
774 char *host, *user, *password;
775 int port;
777 (void) archive_name;
779 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
781 SUP.host = host;
782 SUP.user = user;
783 SUP.port = port;
784 SUP.cwdir = NULL;
785 SUP.proxy = 0;
786 if (ftpfs_check_proxy (host))
787 SUP.proxy = ftpfs_proxy_host;
788 SUP.password = password;
789 SUP.use_passive_connection = ftpfs_use_passive_connections;
790 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
791 SUP.isbinary = TYPE_UNKNOWN;
792 SUP.remote_is_amiga = 0;
793 super->name = g_strdup ("/");
794 super->root =
795 vfs_s_new_inode (me, super,
796 vfs_s_default_stat (me, S_IFDIR | 0755));
798 return ftpfs_open_archive_int (me, super);
801 static int
802 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
803 const char *archive_name, char *op, void *cookie)
805 char *host, *user;
806 int port;
808 (void) me;
809 (void) archive_name;
810 (void) cookie;
812 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
814 port = ((strcmp (host, SUP.host) == 0)
815 && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
817 g_free (host);
818 g_free (user);
820 return port;
823 /* The returned directory should always contain a trailing slash */
824 static char *
825 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
827 char buf[BUF_8K], *bufp, *bufq;
829 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
830 ftpfs_get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
831 bufp = NULL;
832 for (bufq = buf; *bufq; bufq++)
833 if (*bufq == '"') {
834 if (!bufp) {
835 bufp = bufq + 1;
836 } else {
837 *bufq = 0;
838 if (*bufp) {
839 if (*(bufq - 1) != '/') {
840 *bufq++ = '/';
841 *bufq = 0;
843 if (*bufp == '/')
844 return g_strdup (bufp);
845 else {
846 /* If the remote server is an Amiga a leading slash
847 might be missing. MC needs it because it is used
848 as separator between hostname and path internally. */
849 return g_strconcat( "/", bufp, NULL);
851 } else {
852 ftpfs_errno = EIO;
853 return NULL;
858 ftpfs_errno = EIO;
859 return NULL;
863 /* Setup Passive ftp connection, we use it for source routed connections */
864 static int
865 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super, int my_socket, struct sockaddr_in *sa)
867 int xa, xb, xc, xd, xe, xf;
868 char n [6];
869 char *c;
871 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
872 return 0;
874 /* Parse remote parameters */
875 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++)
877 if (!*c)
878 return 0;
879 if (!isdigit ((unsigned char) *c))
880 return 0;
881 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
882 return 0;
883 n [0] = (unsigned char) xa;
884 n [1] = (unsigned char) xb;
885 n [2] = (unsigned char) xc;
886 n [3] = (unsigned char) xd;
887 n [4] = (unsigned char) xe;
888 n [5] = (unsigned char) xf;
890 memcpy (&(sa->sin_addr.s_addr), (void *)n, 4);
891 memcpy (&(sa->sin_port), (void *)&n[4], 2);
892 if (connect (my_socket, (struct sockaddr *) sa, sizeof (struct sockaddr_in)) < 0)
893 return 0;
894 return 1;
897 static int
898 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
900 struct sockaddr_in data_addr;
901 int data;
902 socklen_t len = sizeof(data_addr);
903 struct protoent *pe;
905 pe = getprotobyname ("tcp");
906 if (pe == NULL)
907 ERRNOR (EIO, -1);
908 again:
909 if (getsockname (SUP.sock, (struct sockaddr *) &data_addr, &len) == -1)
910 ERRNOR (EIO, -1);
911 data_addr.sin_port = 0;
913 data = socket (AF_INET, SOCK_STREAM, pe->p_proto);
914 if (data < 0)
915 ERRNOR (EIO, -1);
917 if (SUP.use_passive_connection) {
918 if (ftpfs_setup_passive (me, super, data, &data_addr))
919 return data;
921 SUP.use_passive_connection = 0;
922 print_vfs_message (_("ftpfs: could not setup passive mode"));
924 /* data or data_addr may be damaged by ftpfs_setup_passive */
925 close (data);
926 goto again;
929 /* If passive setup fails, fallback to active connections */
930 /* Active FTP connection */
931 if ((bind (data, (struct sockaddr *)&data_addr, len) == 0) &&
932 (getsockname (data, (struct sockaddr *) &data_addr, &len) == 0) &&
933 (listen (data, 1) == 0))
935 unsigned char *a = (unsigned char *)&data_addr.sin_addr;
936 unsigned char *p = (unsigned char *)&data_addr.sin_port;
938 if (ftpfs_command (me, super, WAIT_REPLY, "PORT %d,%d,%d,%d,%d,%d", a[0], a[1],
939 a[2], a[3], p[0], p[1]) == COMPLETE)
940 return data;
942 close (data);
943 ftpfs_errno = EIO;
944 return -1;
947 static int
948 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
949 const char *remote, int isbinary, int reget)
951 struct sockaddr_in from;
952 int s, j, data;
953 socklen_t fromlen = sizeof(from);
955 if ((s = ftpfs_initconn (me, super)) == -1)
956 return -1;
957 if (ftpfs_changetype (me, super, isbinary) == -1)
958 return -1;
959 if (reget > 0){
960 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
961 if (j != CONTINUE)
962 return -1;
964 if (remote) {
965 char *remote_path = ftpfs_translate_path (me, super, remote);
966 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
967 /* WarFtpD can't STORE //filename */
968 (*remote_path == '/') ? remote_path + 1 : remote_path);
969 g_free (remote_path);
970 } else
971 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
972 if (j != PRELIM)
973 ERRNOR (EPERM, -1);
974 enable_interrupt_key();
975 if (SUP.use_passive_connection)
976 data = s;
977 else {
978 data = accept (s, (struct sockaddr *)&from, &fromlen);
979 if (data < 0) {
980 ftpfs_errno = errno;
981 close (s);
982 return -1;
984 close (s);
986 disable_interrupt_key();
987 return data;
990 #define ABORT_TIMEOUT 5
991 static void
992 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
994 struct vfs_s_super *super = FH_SUPER;
995 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
996 fd_set mask;
997 char buf[1024];
998 int dsock = FH_SOCK;
999 FH_SOCK = -1;
1000 SUP.ctl_connection_busy = 0;
1002 print_vfs_message (_("ftpfs: aborting transfer."));
1003 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
1004 print_vfs_message (_("ftpfs: abort error: %s"),
1005 unix_error_string (errno));
1006 if (dsock != -1)
1007 close (dsock);
1008 return;
1011 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
1012 print_vfs_message (_("ftpfs: abort failed"));
1013 if (dsock != -1)
1014 close (dsock);
1015 return;
1017 if (dsock != -1) {
1018 FD_ZERO (&mask);
1019 FD_SET (dsock, &mask);
1020 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1021 struct timeval start_tim, tim;
1022 gettimeofday (&start_tim, NULL);
1023 /* flush the remaining data */
1024 while (read (dsock, buf, sizeof (buf)) > 0) {
1025 gettimeofday (&tim, NULL);
1026 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1027 /* server keeps sending, drop the connection and ftpfs_reconnect */
1028 close (dsock);
1029 ftpfs_reconnect (me, super);
1030 return;
1034 close (dsock);
1036 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1037 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1040 #if 0
1041 static void
1042 resolve_symlink_without_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1044 struct linklist *flist;
1045 struct direntry *fe, *fel;
1046 char tmp[MC_MAXPATHLEN];
1047 int depth;
1049 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1050 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1051 /* flist->data->l_stat is alread initialized with 0 */
1052 fel = flist->data;
1053 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1054 if (fel->linkname[0] == '/') {
1055 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1056 continue;
1057 strcpy (tmp, fel->linkname);
1058 } else {
1059 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1060 continue;
1061 strcpy (tmp, dir->remote_path);
1062 if (tmp[1] != '\0')
1063 strcat (tmp, "/");
1064 strcat (tmp + 1, fel->linkname);
1066 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1067 canonicalize_pathname (tmp);
1068 fe = _get_file_entry(bucket, tmp, 0, 0);
1069 if (fe) {
1070 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1071 /* Symlink points to link which isn't resolved, yet. */
1072 if (fe->linkname[0] == '/') {
1073 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1074 break;
1075 strcpy (tmp, fe->linkname);
1076 } else {
1077 /* at this point tmp looks always like this
1078 /directory/filename, i.e. no need to check
1079 strrchr's return value */
1080 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1081 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1082 break;
1083 strcat (tmp, fe->linkname);
1085 continue;
1086 } else {
1087 fel->l_stat = g_new (struct stat, 1);
1088 if ( S_ISLNK (fe->s.st_mode))
1089 *fel->l_stat = *fe->l_stat;
1090 else
1091 *fel->l_stat = fe->s;
1092 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1095 break;
1099 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1102 static void
1103 resolve_symlink_with_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1105 char buffer[2048] = "", *filename;
1106 int sock;
1107 FILE *fp;
1108 struct stat s;
1109 struct linklist *flist;
1110 struct direntry *fe;
1111 int switch_method = 0;
1113 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1114 if (strchr (dir->remote_path, ' ')) {
1115 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1116 print_vfs_message(_("ftpfs: CWD failed."));
1117 return;
1119 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1121 else
1122 sock = ftpfs_open_data_connection (bucket, "LIST -lLa",
1123 dir->remote_path, TYPE_ASCII, 0);
1125 if (sock == -1) {
1126 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1127 return;
1130 fp = fdopen(sock, "r");
1131 if (fp == NULL) {
1132 close(sock);
1133 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1134 return;
1136 enable_interrupt_key();
1137 flist = dir->file_list->next;
1138 while (1) {
1139 do {
1140 if (flist == dir->file_list)
1141 goto done;
1142 fe = flist->data;
1143 flist = flist->next;
1144 } while (!S_ISLNK(fe->s.st_mode));
1145 while (1) {
1146 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1147 goto done;
1148 if (MEDATA->logfile){
1149 fputs (buffer, MEDATA->logfile);
1150 fflush (MEDATA->logfile);
1152 vfs_die("This code should be commented out\n");
1153 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1154 int r = strcmp(fe->name, filename);
1155 g_free(filename);
1156 if (r == 0) {
1157 if (S_ISLNK (s.st_mode)) {
1158 /* This server doesn't understand LIST -lLa */
1159 switch_method = 1;
1160 goto done;
1162 fe->l_stat = g_new (struct stat, 1);
1163 if (fe->l_stat == NULL)
1164 goto done;
1165 *fe->l_stat = s;
1166 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1167 break;
1169 if (r < 0)
1170 break;
1174 done:
1175 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1176 disable_interrupt_key();
1177 fclose(fp);
1178 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1181 static void
1182 resolve_symlink(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1184 print_vfs_message(_("Resolving symlink..."));
1186 if (SUP.strict_rfc959_list_cmd)
1187 resolve_symlink_without_ls_options(me, super, dir);
1188 else
1189 resolve_symlink_with_ls_options(me, super, dir);
1191 #endif
1193 static int
1194 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1196 struct vfs_s_entry *ent;
1197 struct vfs_s_super *super = dir->super;
1198 int sock, num_entries = 0;
1199 char buffer[BUF_8K];
1200 int cd_first;
1202 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1203 || (strchr (remote_path, ' ') != NULL);
1205 again:
1206 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1207 remote_path,
1208 SUP.strict ==
1209 RFC_STRICT ? _("(strict rfc959)") : "",
1210 cd_first ? _("(chdir first)") : "");
1212 if (cd_first) {
1213 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE) {
1214 ftpfs_errno = ENOENT;
1215 print_vfs_message (_("ftpfs: CWD failed."));
1216 return -1;
1220 gettimeofday (&dir->timestamp, NULL);
1221 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1223 if (SUP.strict == RFC_STRICT)
1224 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1225 else if (cd_first)
1226 /* Dirty hack to avoid autoprepending / to . */
1227 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1228 sock =
1229 ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1230 else {
1231 /* Trailing "/." is necessary if remote_path is a symlink */
1232 char *path = concat_dir_and_file (remote_path, ".");
1233 sock =
1234 ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII,
1236 g_free (path);
1239 if (sock == -1)
1240 goto fallback;
1242 /* Clear the interrupt flag */
1243 enable_interrupt_key ();
1245 while (1) {
1246 int i;
1247 int res =
1248 vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1249 sock);
1250 if (!res)
1251 break;
1253 if (res == EINTR) {
1254 me->verrno = ECONNRESET;
1255 close (sock);
1256 disable_interrupt_key ();
1257 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1258 print_vfs_message (_("%s: failure"), me->name);
1259 return -1;
1262 if (MEDATA->logfile) {
1263 fputs (buffer, MEDATA->logfile);
1264 fputs ("\n", MEDATA->logfile);
1265 fflush (MEDATA->logfile);
1268 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1269 i = ent->ino->st.st_nlink;
1270 if (!vfs_parse_ls_lga
1271 (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1272 vfs_s_free_entry (me, ent);
1273 continue;
1275 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1276 num_entries++;
1277 vfs_s_insert_entry (me, dir, ent);
1280 close (sock);
1281 me->verrno = E_REMOTE;
1282 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1283 goto fallback;
1285 if (num_entries == 0 && cd_first == 0) {
1286 /* The LIST command may produce an empty output. In such scenario
1287 it is not clear whether this is caused by `remote_path' being
1288 a non-existent path or for some other reason (listing emtpy
1289 directory without the -a option, non-readable directory, etc.).
1291 Since `dir_load' is a crucial method, when it comes to determine
1292 whether a given path is a _directory_, the code must try its best
1293 to determine the type of `remote_path'. The only reliable way to
1294 achieve this is trough issuing a CWD command. */
1296 cd_first = 1;
1297 goto again;
1300 if (SUP.strict == RFC_AUTODETECT)
1301 SUP.strict = RFC_DARING;
1303 print_vfs_message (_("%s: done."), me->name);
1304 return 0;
1306 fallback:
1307 if (SUP.strict == RFC_AUTODETECT) {
1308 /* It's our first attempt to get a directory listing from this
1309 server (UNIX style LIST command) */
1310 SUP.strict = RFC_STRICT;
1311 /* I hate goto, but recursive call needs another 8K on stack */
1312 /* return ftpfs_dir_load (me, dir, remote_path); */
1313 cd_first = 1;
1314 goto again;
1316 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1317 ERRNOR (EACCES, -1);
1320 static int
1321 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name,
1322 char *localname)
1324 int h, sock, n_read, n_written;
1325 off_t n_stored;
1326 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1327 struct linger li;
1328 #else
1329 int flag_one = 1;
1330 #endif
1331 char buffer[8192];
1332 struct stat s;
1333 char *w_buf;
1334 struct vfs_s_super *super = FH_SUPER;
1336 h = open (localname, O_RDONLY);
1337 if (h == -1)
1338 ERRNOR (EIO, -1);
1339 sock =
1340 ftpfs_open_data_connection (me, super,
1341 fh->u.ftp.append ? "APPE" : "STOR", name,
1342 TYPE_BINARY, 0);
1343 if (sock < 0 || fstat (h, &s) == -1) {
1344 close (h);
1345 return -1;
1347 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1348 li.l_onoff = 1;
1349 li.l_linger = 120;
1350 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1351 #else
1352 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1353 #endif
1354 n_stored = 0;
1356 enable_interrupt_key ();
1357 while (1) {
1358 while ((n_read = read (h, buffer, sizeof (buffer))) == -1) {
1359 if (errno == EINTR) {
1360 if (got_interrupt ()) {
1361 ftpfs_errno = EINTR;
1362 goto error_return;
1363 } else
1364 continue;
1366 ftpfs_errno = errno;
1367 goto error_return;
1369 if (n_read == 0)
1370 break;
1371 n_stored += n_read;
1372 w_buf = buffer;
1373 while ((n_written = write (sock, w_buf, n_read)) != n_read) {
1374 if (n_written == -1) {
1375 if (errno == EINTR && !got_interrupt ()) {
1376 continue;
1378 ftpfs_errno = errno;
1379 goto error_return;
1381 w_buf += n_written;
1382 n_read -= n_written;
1384 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1385 (unsigned long) n_stored, (unsigned long) s.st_size);
1387 disable_interrupt_key ();
1388 close (sock);
1389 close (h);
1390 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1391 ERRNOR (EIO, -1);
1392 return 0;
1393 error_return:
1394 disable_interrupt_key ();
1395 close (sock);
1396 close (h);
1397 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1398 return -1;
1401 static int
1402 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1404 char *name = vfs_s_fullpath (me, fh->ino);
1406 if (!name)
1407 return 0;
1408 FH_SOCK = ftpfs_open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1409 g_free (name);
1410 if (FH_SOCK == -1)
1411 ERRNOR (EACCES, 0);
1412 fh->linear = LS_LINEAR_OPEN;
1413 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1414 fh->u.ftp.append = 0;
1415 return 1;
1418 static int
1419 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1421 int n;
1422 struct vfs_s_super *super = FH_SUPER;
1424 while ((n = read (FH_SOCK, buf, len))<0) {
1425 if ((errno == EINTR) && !got_interrupt())
1426 continue;
1427 break;
1430 if (n<0)
1431 ftpfs_linear_abort(me, fh);
1433 if (!n) {
1434 SUP.ctl_connection_busy = 0;
1435 close (FH_SOCK);
1436 FH_SOCK = -1;
1437 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1438 ERRNOR (E_REMOTE, -1);
1439 return 0;
1441 ERRNOR (errno, n);
1444 static void
1445 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1447 if (FH_SOCK != -1)
1448 ftpfs_linear_abort(me, fh);
1451 static int ftpfs_ctl (void *fh, int ctlop, void *arg)
1453 (void) arg;
1455 switch (ctlop) {
1456 case VFS_CTL_IS_NOTREADY:
1458 int v;
1460 if (!FH->linear)
1461 vfs_die ("You may not do this");
1462 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1463 return 0;
1465 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1466 if (((v < 0) && (errno == EINTR)) || v == 0)
1467 return 1;
1468 return 0;
1470 default:
1471 return 0;
1475 static int
1476 ftpfs_send_command(struct vfs_class *me, const char *filename, const char *cmd, int flags)
1478 const char *rpath;
1479 char *p, *mpath = g_strdup(filename);
1480 struct vfs_s_super *super;
1481 int r;
1482 int flush_directory_cache = (flags & OPT_FLUSH);
1484 if (!(rpath = vfs_s_get_path_mangle(me, mpath, &super, 0))) {
1485 g_free(mpath);
1486 return -1;
1488 p = ftpfs_translate_path (me, super, rpath);
1489 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1490 g_free (p);
1491 vfs_stamp_create (&vfs_ftpfs_ops, super);
1492 if (flags & OPT_IGNORE_ERROR)
1493 r = COMPLETE;
1494 if (r != COMPLETE) {
1495 me->verrno = EPERM;
1496 g_free (mpath);
1497 return -1;
1499 if (flush_directory_cache)
1500 vfs_s_invalidate(me, super);
1501 g_free(mpath);
1502 return 0;
1505 /* This routine is called as the last step in load_setup */
1506 void
1507 ftpfs_init_passwd(void)
1509 ftpfs_anonymous_passwd = load_anon_passwd ();
1510 if (ftpfs_anonymous_passwd)
1511 return;
1513 /* If there is no anonymous ftp password specified
1514 * then we'll just use anonymous@
1515 * We don't send any other thing because:
1516 * - We want to remain anonymous
1517 * - We want to stop SPAM
1518 * - We don't want to let ftp sites to discriminate by the user,
1519 * host or country.
1521 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1524 static int ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1526 char buf[BUF_SMALL];
1528 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1529 return ftpfs_send_command(me, path, buf, OPT_FLUSH);
1532 static int ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1534 #if 0
1535 ftpfs_errno = EPERM;
1536 return -1;
1537 #else
1538 /* Everyone knows it is not possible to chown remotely, so why bother them.
1539 If someone's root, then copy/move will always try to chown it... */
1540 (void) me;
1541 (void) path;
1542 (void) owner;
1543 (void) group;
1544 return 0;
1545 #endif
1548 static int ftpfs_unlink (struct vfs_class *me, const char *path)
1550 return ftpfs_send_command(me, path, "DELE /%s", OPT_FLUSH);
1553 /* Return 1 if path is the same directory as the one we are in now */
1554 static int
1555 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1557 (void) me;
1559 if (!SUP.cwdir)
1560 return 0;
1561 if (strcmp (path, SUP.cwdir) == 0)
1562 return 1;
1563 return 0;
1566 static int
1567 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1569 int r;
1570 char *p;
1572 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1573 return COMPLETE;
1575 p = ftpfs_translate_path (me, super, remote_path);
1576 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1577 g_free (p);
1579 if (r != COMPLETE) {
1580 ftpfs_errno = EIO;
1581 } else {
1582 g_free(SUP.cwdir);
1583 SUP.cwdir = g_strdup (remote_path);
1584 SUP.cwd_deferred = 0;
1586 return r;
1589 static int ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1591 ftpfs_send_command(me, path1, "RNFR /%s", OPT_FLUSH);
1592 return ftpfs_send_command(me, path2, "RNTO /%s", OPT_FLUSH);
1595 static int ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1597 (void) mode; /* FIXME: should be used */
1599 return ftpfs_send_command(me, path, "MKD /%s", OPT_FLUSH);
1602 static int ftpfs_rmdir (struct vfs_class *me, const char *path)
1604 return ftpfs_send_command(me, path, "RMD /%s", OPT_FLUSH);
1607 static int
1608 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
1609 int mode)
1611 (void) mode;
1613 fh->u.ftp.append = 0;
1614 /* File will be written only, so no need to retrieve it from ftp server */
1615 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
1616 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1617 struct linger li;
1618 #else
1619 int li = 1;
1620 #endif
1621 char *name;
1623 /* ftpfs_linear_start() called, so data will be written
1624 * to local temporary file and stored to ftp server
1625 * by vfs_s_close later
1627 if (FH_SUPER->u.ftp.ctl_connection_busy) {
1628 if (!fh->ino->localname) {
1629 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1630 fh->ino->ent->name);
1631 if (handle == -1)
1632 return -1;
1633 close (handle);
1634 fh->u.ftp.append = flags & O_APPEND;
1636 return 0;
1638 name = vfs_s_fullpath (me, fh->ino);
1639 if (!name)
1640 return -1;
1641 fh->handle =
1642 ftpfs_open_data_connection (me, fh->ino->super,
1643 (flags & O_APPEND) ? "APPE" :
1644 "STOR", name, TYPE_BINARY, 0);
1645 g_free (name);
1647 if (fh->handle < 0)
1648 return -1;
1649 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1650 li.l_onoff = 1;
1651 li.l_linger = 120;
1652 #endif
1653 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1655 if (fh->ino->localname) {
1656 unlink (fh->ino->localname);
1657 g_free (fh->ino->localname);
1658 fh->ino->localname = NULL;
1660 return 0;
1663 if (!fh->ino->localname)
1664 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1665 return -1;
1666 if (!fh->ino->localname)
1667 vfs_die ("retrieve_file failed to fill in localname");
1668 return 0;
1671 static int ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1673 if (fh->handle != -1 && !fh->ino->localname){
1674 close (fh->handle);
1675 fh->handle = -1;
1676 /* File is stored to destination already, so
1677 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1679 fh->changed = 0;
1680 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1681 ERRNOR (EIO, -1);
1682 vfs_s_invalidate (me, FH_SUPER);
1684 return 0;
1687 static void
1688 ftpfs_done (struct vfs_class *me)
1690 struct no_proxy_entry *np;
1692 (void) me;
1694 while (no_proxy) {
1695 np = no_proxy->next;
1696 g_free (no_proxy->domain);
1697 g_free (no_proxy);
1698 no_proxy = np;
1700 g_free (ftpfs_anonymous_passwd);
1701 g_free (ftpfs_proxy_host);
1704 static void
1705 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
1707 struct vfs_s_super *super = MEDATA->supers;
1708 char *name;
1710 while (super){
1711 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
1712 (*func)(name);
1713 g_free (name);
1714 super = super->next;
1718 static char buffer[BUF_MEDIUM];
1719 static char *netrc;
1720 static const char *netrcp;
1722 /* This should match the keywords[] array below */
1723 typedef enum {
1724 NETRC_NONE = 0,
1725 NETRC_DEFAULT,
1726 NETRC_MACHINE,
1727 NETRC_LOGIN,
1728 NETRC_PASSWORD,
1729 NETRC_PASSWD,
1730 NETRC_ACCOUNT,
1731 NETRC_MACDEF,
1732 NETRC_UNKNOWN
1733 } keyword_t;
1735 static keyword_t ftpfs_netrc_next (void)
1737 char *p;
1738 keyword_t i;
1739 static const char *const keywords[] = { "default", "machine",
1740 "login", "password", "passwd", "account", "macdef", NULL
1744 while (1) {
1745 netrcp = skip_separators (netrcp);
1746 if (*netrcp != '\n')
1747 break;
1748 netrcp++;
1750 if (!*netrcp)
1751 return NETRC_NONE;
1752 p = buffer;
1753 if (*netrcp == '"') {
1754 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1755 if (*netrcp == '\\')
1756 netrcp++;
1757 *p++ = *netrcp;
1759 } else {
1760 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1761 *netrcp != ',' && *netrcp; netrcp++) {
1762 if (*netrcp == '\\')
1763 netrcp++;
1764 *p++ = *netrcp;
1767 *p = 0;
1768 if (!*buffer)
1769 return NETRC_NONE;
1771 i = NETRC_DEFAULT;
1772 while (keywords[i - 1]) {
1773 if (!strcmp (keywords[i - 1], buffer))
1774 return i;
1776 i++;
1779 return NETRC_UNKNOWN;
1782 static int ftpfs_netrc_bad_mode (const char *netrcname)
1784 static int be_angry = 1;
1785 struct stat mystat;
1787 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1788 if (be_angry) {
1789 message (1, MSG_ERROR,
1790 _("~/.netrc file has incorrect mode.\n"
1791 "Remove password or correct mode."));
1792 be_angry = 0;
1794 return 1;
1796 return 0;
1799 /* Scan .netrc until we find matching "machine" or "default"
1800 * domain is used for additional matching
1801 * No search is done after "default" in compliance with "man netrc"
1802 * Return 0 if found, -1 otherwise */
1803 static int ftpfs_find_machine (const char *host, const char *domain)
1805 keyword_t keyword;
1807 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) {
1808 if (keyword == NETRC_DEFAULT)
1809 return 0;
1811 if (keyword == NETRC_MACDEF) {
1812 /* Scan for an empty line, which concludes "macdef" */
1813 do {
1814 while (*netrcp && *netrcp != '\n')
1815 netrcp++;
1816 if (*netrcp != '\n')
1817 break;
1818 netrcp++;
1819 } while (*netrcp && *netrcp != '\n');
1820 continue;
1823 if (keyword != NETRC_MACHINE)
1824 continue;
1826 /* Take machine name */
1827 if (ftpfs_netrc_next () == NETRC_NONE)
1828 break;
1830 if (g_strcasecmp (host, buffer)) {
1831 /* Try adding our domain to short names in .netrc */
1832 const char *host_domain = strchr (host, '.');
1833 if (!host_domain)
1834 continue;
1836 /* Compare domain part */
1837 if (g_strcasecmp (host_domain, domain))
1838 continue;
1840 /* Compare local part */
1841 if (g_strncasecmp (host, buffer, host_domain - host))
1842 continue;
1845 return 0;
1848 /* end of .netrc */
1849 return -1;
1852 /* Extract login and password from .netrc for the host.
1853 * pass may be NULL.
1854 * Returns 0 for success, -1 for error */
1855 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass)
1857 char *netrcname;
1858 char *tmp_pass = NULL;
1859 char hostname[MAXHOSTNAMELEN];
1860 const char *domain;
1861 keyword_t keyword;
1862 static struct rupcache {
1863 struct rupcache *next;
1864 char *host;
1865 char *login;
1866 char *pass;
1867 } *rup_cache = NULL, *rupp;
1869 /* Initialize *login and *pass */
1870 if (!login)
1871 return 0;
1872 *login = NULL;
1873 if (pass)
1874 *pass = NULL;
1876 /* Look up in the cache first */
1877 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
1878 if (!strcmp (host, rupp->host)) {
1879 if (rupp->login)
1880 *login = g_strdup (rupp->login);
1881 if (pass && rupp->pass)
1882 *pass = g_strdup (rupp->pass);
1883 return 0;
1887 /* Load current .netrc */
1888 netrcname = concat_dir_and_file (home_dir, ".netrc");
1889 netrcp = netrc = load_file (netrcname);
1890 if (netrc == NULL) {
1891 g_free (netrcname);
1892 return 0;
1895 /* Find our own domain name */
1896 if (gethostname (hostname, sizeof (hostname)) < 0)
1897 *hostname = 0;
1898 if (!(domain = strchr (hostname, '.')))
1899 domain = "";
1901 /* Scan for "default" and matching "machine" keywords */
1902 ftpfs_find_machine (host, domain);
1904 /* Scan for keywords following "default" and "machine" */
1905 while (1) {
1906 int need_break = 0;
1907 keyword = ftpfs_netrc_next ();
1909 switch (keyword) {
1910 case NETRC_LOGIN:
1911 if (ftpfs_netrc_next () == NETRC_NONE) {
1912 need_break = 1;
1913 break;
1916 /* We have another name already - should not happen */
1917 if (*login) {
1918 need_break = 1;
1919 break;
1922 /* We have login name now */
1923 *login = g_strdup (buffer);
1924 break;
1926 case NETRC_PASSWORD:
1927 case NETRC_PASSWD:
1928 if (ftpfs_netrc_next () == NETRC_NONE) {
1929 need_break = 1;
1930 break;
1933 /* Ignore unsafe passwords */
1934 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
1935 && ftpfs_netrc_bad_mode (netrcname)) {
1936 need_break = 1;
1937 break;
1940 /* Remember password. pass may be NULL, so use tmp_pass */
1941 if (tmp_pass == NULL)
1942 tmp_pass = g_strdup (buffer);
1943 break;
1945 case NETRC_ACCOUNT:
1946 /* "account" is followed by a token which we ignore */
1947 if (ftpfs_netrc_next () == NETRC_NONE) {
1948 need_break = 1;
1949 break;
1952 /* Ignore account, but warn user anyways */
1953 ftpfs_netrc_bad_mode (netrcname);
1954 break;
1956 default:
1957 /* Unexpected keyword or end of file */
1958 need_break = 1;
1959 break;
1962 if (need_break)
1963 break;
1966 g_free (netrc);
1967 g_free (netrcname);
1969 rupp = g_new (struct rupcache, 1);
1970 rupp->host = g_strdup (host);
1971 rupp->login = rupp->pass = 0;
1973 if (*login != NULL) {
1974 rupp->login = g_strdup (*login);
1976 if (tmp_pass != NULL)
1977 rupp->pass = g_strdup (tmp_pass);
1978 rupp->next = rup_cache;
1979 rup_cache = rupp;
1981 if (pass)
1982 *pass = tmp_pass;
1984 return 0;
1987 void
1988 init_ftpfs (void)
1990 static struct vfs_s_subclass ftpfs_subclass;
1992 ftpfs_subclass.flags = VFS_S_REMOTE;
1993 ftpfs_subclass.archive_same = ftpfs_archive_same;
1994 ftpfs_subclass.open_archive = ftpfs_open_archive;
1995 ftpfs_subclass.free_archive = ftpfs_free_archive;
1996 ftpfs_subclass.fh_open = ftpfs_fh_open;
1997 ftpfs_subclass.fh_close = ftpfs_fh_close;
1998 ftpfs_subclass.dir_load = ftpfs_dir_load;
1999 ftpfs_subclass.file_store = ftpfs_file_store;
2000 ftpfs_subclass.linear_start = ftpfs_linear_start;
2001 ftpfs_subclass.linear_read = ftpfs_linear_read;
2002 ftpfs_subclass.linear_close = ftpfs_linear_close;
2004 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2005 vfs_ftpfs_ops.name = "ftpfs";
2006 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2007 vfs_ftpfs_ops.prefix = "ftp:";
2008 vfs_ftpfs_ops.done = &ftpfs_done;
2009 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2010 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2011 vfs_ftpfs_ops.chown = ftpfs_chown;
2012 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2013 vfs_ftpfs_ops.rename = ftpfs_rename;
2014 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2015 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2016 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2017 vfs_register_class (&vfs_ftpfs_ops);