Completely removed MHL stuff
[midnight-commander.git] / vfs / ftpfs.c
blob7737e93e2973d9d47de7092618a74b72d3a593aa
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 "../src/history.h"
77 #include "utilvfs.h"
78 #include "xdirentry.h"
79 #include "vfs.h"
80 #include "vfs-impl.h"
81 #include "gc.h" /* vfs_stamp_create */
82 #include "tcputil.h"
83 #include "../src/setup.h" /* for load_anon_passwd */
84 #include "ftpfs.h"
85 #ifndef MAXHOSTNAMELEN
86 # define MAXHOSTNAMELEN 64
87 #endif
89 #define UPLOAD_ZERO_LENGTH_FILE
90 #define SUP super->u.ftp
91 #define FH_SOCK fh->u.ftp.sock
93 #ifndef INADDR_NONE
94 #define INADDR_NONE 0xffffffff
95 #endif
97 #define RFC_AUTODETECT 0
98 #define RFC_DARING 1
99 #define RFC_STRICT 2
101 #ifndef HAVE_SOCKLEN_T
102 typedef int socklen_t;
103 #endif
105 static int ftpfs_errno;
106 static int code;
108 /* Delay to retry a connection */
109 int ftpfs_retry_seconds = 30;
111 /* Method to use to connect to ftp sites */
112 int ftpfs_use_passive_connections = 1;
113 int ftpfs_use_passive_connections_over_proxy = 0;
115 /* Method used to get directory listings:
116 * 1: try 'LIST -la <path>', if it fails
117 * fall back to CWD <path>; LIST
118 * 0: always use CWD <path>; LIST
120 int ftpfs_use_unix_list_options = 1;
122 /* First "CWD <path>", then "LIST -la ." */
123 int ftpfs_first_cd_then_ls = 1;
125 /* Use the ~/.netrc */
126 int use_netrc = 1;
128 /* Anonymous setup */
129 char *ftpfs_anonymous_passwd = NULL;
130 int ftpfs_directory_timeout = 900;
132 /* Proxy host */
133 char *ftpfs_proxy_host = NULL;
135 /* wether we have to use proxy by default? */
136 int ftpfs_always_use_proxy;
138 #ifdef FIXME_LATER_ALIGATOR
139 static struct linklist *connections_list;
140 #endif
142 /* ftpfs_command wait_flag: */
143 #define NONE 0x00
144 #define WAIT_REPLY 0x01
145 #define WANT_STRING 0x02
146 static char reply_str [80];
148 static struct vfs_class vfs_ftpfs_ops;
150 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
151 Translate a Unix path, i.e. MC's internal path representation (e.g.
152 /somedir/somefile) to a path valid for the remote server. Every path
153 transfered to the remote server has to be mangled by this function
154 right prior to sending it.
155 Currently only Amiga ftp servers are handled in a special manner.
157 When the remote server is an amiga:
158 a) strip leading slash if necesarry
159 b) replace first occurance of ":/" with ":"
160 c) strip trailing "/."
163 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
164 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path);
165 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
166 __attribute__ ((format (__printf__, 4, 5)));
167 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
168 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass);
169 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
171 static char *
172 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
174 if (!SUP.remote_is_amiga)
175 return g_strdup (remote_path);
176 else {
177 char *ret, *p;
179 if (MEDATA->logfile) {
180 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
181 fflush (MEDATA->logfile);
184 /* strip leading slash(es) */
185 while (*remote_path == '/')
186 remote_path++;
189 * Don't change "/" into "", e.g. "CWD " would be
190 * invalid.
192 if (*remote_path == '\0')
193 return g_strdup (".");
195 ret = g_strdup (remote_path);
197 /* replace first occurance of ":/" with ":" */
198 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
199 strcpy (p + 1, p + 2);
201 /* strip trailing "/." */
202 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
203 *p = '\0';
204 return ret;
208 /* Extract the hostname and username from the path */
211 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
212 * ftp://sunsite.unc.edu/pub/linux
213 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
214 * ftp://tsx-11.mit.edu:8192/
215 * ftp://joe@foo.edu:11321/private
216 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
217 * is supplied.
221 #define FTP_COMMAND_PORT 21
223 static void
224 ftpfs_split_url(char *path, char **host, char **user, int *port, char **pass)
226 char *p;
228 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
229 URL_ALLOW_ANON);
231 if (!*user) {
232 /* Look up user and password in netrc */
233 if (use_netrc)
234 ftpfs_netrc_lookup (*host, user, pass);
235 if (!*user)
236 *user = g_strdup ("anonymous");
239 /* Look up password in netrc for known user */
240 if (use_netrc && *user && pass && !*pass) {
241 char *new_user;
243 ftpfs_netrc_lookup (*host, &new_user, pass);
245 /* If user is different, remove password */
246 if (new_user && strcmp (*user, new_user)) {
247 g_free (*pass);
248 *pass = NULL;
251 g_free (new_user);
254 g_free (p);
257 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
258 static int
259 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
261 char answer[BUF_1K];
262 int i;
264 for (;;) {
265 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
266 if (string_buf)
267 *string_buf = 0;
268 code = 421;
269 return 4;
271 switch (sscanf(answer, "%d", &code)){
272 case 0:
273 if (string_buf)
274 g_strlcpy (string_buf, answer, string_len);
275 code = 500;
276 return 5;
277 case 1:
278 if (answer[3] == '-') {
279 while (1) {
280 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
281 if (string_buf)
282 *string_buf = 0;
283 code = 421;
284 return 4;
286 if ((sscanf (answer, "%d", &i) > 0) &&
287 (code == i) && (answer[3] == ' '))
288 break;
291 if (string_buf)
292 g_strlcpy (string_buf, answer, string_len);
293 return code / 100;
298 static int
299 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
301 int sock = ftpfs_open_socket (me, super);
302 if (sock != -1){
303 char *cwdir = SUP.cwdir;
304 close (SUP.sock);
305 SUP.sock = sock;
306 SUP.cwdir = NULL;
307 if (ftpfs_login_server (me, super, SUP.password)){
308 if (!cwdir)
309 return 1;
310 sock = ftpfs_chdir_internal (me, super, cwdir);
311 g_free (cwdir);
312 return sock == COMPLETE;
314 SUP.cwdir = cwdir;
316 return 0;
319 static int
320 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
322 va_list ap;
323 char *cmdstr;
324 int status, cmdlen;
325 static int retry = 0;
326 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
328 va_start (ap, fmt);
329 cmdstr = g_strdup_vprintf (fmt, ap);
330 va_end (ap);
332 cmdlen = strlen (cmdstr);
333 cmdstr = g_realloc (cmdstr, cmdlen + 3);
334 strcpy (cmdstr + cmdlen, "\r\n");
335 cmdlen += 2;
337 if (MEDATA->logfile) {
338 if (strncmp (cmdstr, "PASS ", 5) == 0) {
339 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
340 } else
341 fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
343 fflush (MEDATA->logfile);
346 got_sigpipe = 0;
347 enable_interrupt_key ();
348 status = write (SUP.sock, cmdstr, cmdlen);
350 if (status < 0) {
351 code = 421;
353 if (errno == EPIPE) { /* Remote server has closed connection */
354 if (level == 0) {
355 level = 1;
356 status = ftpfs_reconnect (me, super);
357 level = 0;
358 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
359 goto ok;
363 got_sigpipe = 1;
365 g_free (cmdstr);
366 disable_interrupt_key ();
367 return TRANSIENT;
369 retry = 0;
371 disable_interrupt_key ();
373 if (wait_reply)
375 status = ftpfs_get_reply (me, SUP.sock,
376 (wait_reply & WANT_STRING) ? reply_str : NULL,
377 sizeof (reply_str) - 1);
378 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
380 retry = 1;
381 level = 1;
382 status = ftpfs_reconnect (me, super);
383 level = 0;
384 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
385 goto ok;
388 retry = 0;
389 g_free (cmdstr);
390 return status;
392 g_free (cmdstr);
393 return COMPLETE;
396 static void
397 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
399 if (SUP.sock != -1){
400 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
401 ftpfs_command(me, super, NONE, "QUIT");
402 close(SUP.sock);
404 g_free (SUP.host);
405 g_free (SUP.user);
406 g_free (SUP.cwdir);
407 g_free (SUP.password);
410 /* some defines only used by ftpfs_changetype */
411 /* These two are valid values for the second parameter */
412 #define TYPE_ASCII 0
413 #define TYPE_BINARY 1
415 /* This one is only used to initialize bucket->isbinary, don't use it as
416 second parameter to ftpfs_changetype. */
417 #define TYPE_UNKNOWN -1
419 static int
420 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
422 if (binary != SUP.isbinary) {
423 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
424 ERRNOR (EIO, -1);
425 SUP.isbinary = binary;
427 return binary;
430 /* This routine logs the user in */
431 static int
432 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
433 const char *netrcpass)
435 char *pass;
436 char *op;
437 char *name; /* login user name */
438 int anon = 0;
439 char reply_string[BUF_MEDIUM];
441 SUP.isbinary = TYPE_UNKNOWN;
443 if (SUP.password) /* explicit password */
444 op = g_strdup (SUP.password);
445 else if (netrcpass) /* password from netrc */
446 op = g_strdup (netrcpass);
447 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp")) {
448 if (!ftpfs_anonymous_passwd) /* default anonymous password */
449 ftpfs_init_passwd ();
450 op = g_strdup (ftpfs_anonymous_passwd);
451 anon = 1;
452 } else { /* ask user */
453 char *p;
455 p = g_strconcat (_(" FTP: Password required for "), SUP.user, " ",
456 NULL);
457 op = vfs_get_password (p);
458 g_free (p);
459 if (op == NULL)
460 ERRNOR (EPERM, 0);
461 SUP.password = g_strdup (op);
464 if (!anon || MEDATA->logfile)
465 pass = op;
466 else {
467 pass = g_strconcat ("-", op, (char *) NULL);
468 wipe_password (op);
471 /* Proxy server accepts: username@host-we-want-to-connect */
472 if (SUP.proxy) {
473 name =
474 g_strconcat (SUP.user, "@",
475 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host,
476 NULL);
477 } else
478 name = g_strdup (SUP.user);
480 if (ftpfs_get_reply
481 (me, SUP.sock, reply_string,
482 sizeof (reply_string) - 1) == COMPLETE) {
483 g_strup (reply_string);
484 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
485 if (MEDATA->logfile) {
486 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n",
487 SUP.remote_is_amiga);
488 fflush (MEDATA->logfile);
491 print_vfs_message (_("ftpfs: sending login name"));
493 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name)) {
494 case CONTINUE:
495 print_vfs_message (_("ftpfs: sending user password"));
496 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
497 if (code == CONTINUE) {
498 char *p;
500 p = g_strdup_printf (_
501 ("FTP: Account required for user %s"),
502 SUP.user);
503 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
504 g_free (p);
505 if (op == NULL)
506 ERRNOR (EPERM, 0);
507 print_vfs_message (_("ftpfs: sending user account"));
508 code =
509 ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
510 g_free (op);
512 if (code != COMPLETE)
513 break;
514 /* fall through */
516 case COMPLETE:
517 print_vfs_message (_("ftpfs: logged in"));
518 wipe_password (pass);
519 g_free (name);
520 return 1;
522 default:
523 SUP.failed_on_login = 1;
524 if (SUP.password)
525 wipe_password (SUP.password);
526 SUP.password = 0;
528 goto login_fail;
531 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
532 SUP.user);
533 login_fail:
534 wipe_password (pass);
535 g_free (name);
536 ERRNOR (EPERM, 0);
539 static struct no_proxy_entry {
540 char *domain;
541 void *next;
542 } *no_proxy;
544 static void
545 ftpfs_load_no_proxy_list (void)
547 /* FixMe: shouldn't be hardcoded!!! */
548 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
549 struct no_proxy_entry *np, *current = 0;
550 FILE *npf;
551 int c;
552 char *p;
553 static char *mc_file;
555 if (mc_file)
556 return;
558 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
559 if (exist_file (mc_file) &&
560 (npf = fopen (mc_file, "r"))) {
561 while (fgets (s, sizeof (s), npf)) {
562 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
563 while ((c = fgetc (npf)) != EOF && c != '\n')
565 continue;
568 if (p == s)
569 continue;
571 *p = '\0';
573 np = g_new (struct no_proxy_entry, 1);
574 np->domain = g_strdup (s);
575 np->next = NULL;
576 if (no_proxy)
577 current->next = np;
578 else
579 no_proxy = np;
580 current = np;
583 fclose (npf);
585 g_free (mc_file);
588 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
589 static int
590 ftpfs_check_proxy (const char *host)
592 struct no_proxy_entry *npe;
594 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
595 return 0; /* sanity check */
597 if (*host == '!')
598 return 1;
600 if (!ftpfs_always_use_proxy)
601 return 0;
603 if (!strchr (host, '.'))
604 return 0;
606 ftpfs_load_no_proxy_list ();
607 for (npe = no_proxy; npe; npe=npe->next) {
608 char *domain = npe->domain;
610 if (domain[0] == '.') {
611 int ld = strlen (domain);
612 int lh = strlen (host);
614 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
615 ld--;
616 lh--;
619 if (!ld)
620 return 0;
621 } else
622 if (!g_strcasecmp (host, domain))
623 return 0;
626 return 1;
629 static void
630 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
632 char *user, *dir;
634 dir =
635 vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT,
636 URL_ALLOW_ANON);
637 g_free (user);
638 g_free (dir);
641 static int
642 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
644 struct sockaddr_in server_address;
645 struct hostent *hp;
646 int my_socket;
647 char *host;
648 int port = SUP.port;
649 int free_host = 0;
651 (void) me;
653 /* Use a proxy host? */
654 host = SUP.host;
656 if (!host || !*host){
657 print_vfs_message (_("ftpfs: Invalid host name."));
658 ftpfs_errno = EINVAL;
659 return -1;
662 /* Hosts to connect to that start with a ! should use proxy */
663 if (SUP.proxy){
664 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &port);
665 free_host = 1;
668 enable_interrupt_key(); /* clear the interrupt flag */
670 /* Get host address */
671 memset ((char *) &server_address, 0, sizeof (server_address));
672 server_address.sin_family = AF_INET;
673 server_address.sin_addr.s_addr = inet_addr (host);
674 if (server_address.sin_addr.s_addr == INADDR_NONE) {
675 hp = gethostbyname (host);
676 if (hp == NULL){
677 disable_interrupt_key();
678 print_vfs_message (_("ftpfs: Invalid host address."));
679 ftpfs_errno = EINVAL;
680 if (free_host)
681 g_free (host);
682 return -1;
684 server_address.sin_family = hp->h_addrtype;
686 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
687 memcpy ((char *) &server_address.sin_addr, (char *) hp->h_addr, 4);
690 server_address.sin_port = htons (port);
692 /* Connect */
693 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
694 disable_interrupt_key();
695 ftpfs_errno = errno;
696 if (free_host)
697 g_free (host);
698 return -1;
701 print_vfs_message (_("ftpfs: making connection to %s"), host);
702 if (free_host)
703 g_free (host);
705 if (connect (my_socket, (struct sockaddr *) &server_address,
706 sizeof (server_address)) < 0){
707 ftpfs_errno = errno;
708 if (errno == EINTR && got_interrupt ())
709 print_vfs_message (_("ftpfs: connection interrupted by user"));
710 else
711 print_vfs_message (_("ftpfs: connection to server failed: %s"),
712 unix_error_string(errno));
713 disable_interrupt_key();
714 close (my_socket);
715 return -1;
717 disable_interrupt_key();
718 return my_socket;
721 static int
722 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
724 int retry_seconds, count_down;
726 /* We do not want to use the passive if we are using proxies */
727 if (SUP.proxy)
728 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
730 retry_seconds = 0;
731 do {
732 SUP.failed_on_login = 0;
734 SUP.sock = ftpfs_open_socket (me, super);
735 if (SUP.sock == -1)
736 return -1;
738 if (ftpfs_login_server (me, super, NULL)) {
739 /* Logged in, no need to retry the connection */
740 break;
741 } else {
742 if (SUP.failed_on_login){
743 /* Close only the socket descriptor */
744 close (SUP.sock);
745 } else {
746 return -1;
748 if (ftpfs_retry_seconds){
749 retry_seconds = ftpfs_retry_seconds;
750 enable_interrupt_key ();
751 for (count_down = retry_seconds; count_down; count_down--){
752 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
753 sleep (1);
754 if (got_interrupt ()){
755 /* ftpfs_errno = E; */
756 disable_interrupt_key ();
757 return 0;
760 disable_interrupt_key ();
763 } while (retry_seconds);
765 SUP.cwdir = ftpfs_get_current_directory (me, super);
766 if (!SUP.cwdir)
767 SUP.cwdir = g_strdup (PATH_SEP_STR);
768 return 0;
771 static int
772 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
773 const char *archive_name, char *op)
775 char *host, *user, *password;
776 int port;
778 (void) archive_name;
780 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
782 SUP.host = host;
783 SUP.user = user;
784 SUP.port = port;
785 SUP.cwdir = NULL;
786 SUP.proxy = 0;
787 if (ftpfs_check_proxy (host))
788 SUP.proxy = ftpfs_proxy_host;
789 SUP.password = password;
790 SUP.use_passive_connection = ftpfs_use_passive_connections;
791 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
792 SUP.isbinary = TYPE_UNKNOWN;
793 SUP.remote_is_amiga = 0;
794 super->name = g_strdup ("/");
795 super->root =
796 vfs_s_new_inode (me, super,
797 vfs_s_default_stat (me, S_IFDIR | 0755));
799 return ftpfs_open_archive_int (me, super);
802 static int
803 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
804 const char *archive_name, char *op, void *cookie)
806 char *host, *user;
807 int port;
809 (void) me;
810 (void) archive_name;
811 (void) cookie;
813 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
815 port = ((strcmp (host, SUP.host) == 0)
816 && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
818 g_free (host);
819 g_free (user);
821 return port;
824 /* The returned directory should always contain a trailing slash */
825 static char *
826 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
828 char buf[BUF_8K], *bufp, *bufq;
830 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
831 ftpfs_get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
832 bufp = NULL;
833 for (bufq = buf; *bufq; bufq++)
834 if (*bufq == '"') {
835 if (!bufp) {
836 bufp = bufq + 1;
837 } else {
838 *bufq = 0;
839 if (*bufp) {
840 if (*(bufq - 1) != '/') {
841 *bufq++ = '/';
842 *bufq = 0;
844 if (*bufp == '/')
845 return g_strdup (bufp);
846 else {
847 /* If the remote server is an Amiga a leading slash
848 might be missing. MC needs it because it is used
849 as separator between hostname and path internally. */
850 return g_strconcat( "/", bufp, NULL);
852 } else {
853 ftpfs_errno = EIO;
854 return NULL;
859 ftpfs_errno = EIO;
860 return NULL;
864 /* Setup Passive ftp connection, we use it for source routed connections */
865 static int
866 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super, int my_socket, struct sockaddr_in *sa)
868 int xa, xb, xc, xd, xe, xf;
869 char n [6];
870 char *c;
872 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
873 return 0;
875 /* Parse remote parameters */
876 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++)
878 if (!*c)
879 return 0;
880 if (!isdigit ((unsigned char) *c))
881 return 0;
882 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
883 return 0;
884 n [0] = (unsigned char) xa;
885 n [1] = (unsigned char) xb;
886 n [2] = (unsigned char) xc;
887 n [3] = (unsigned char) xd;
888 n [4] = (unsigned char) xe;
889 n [5] = (unsigned char) xf;
891 memcpy (&(sa->sin_addr.s_addr), (void *)n, 4);
892 memcpy (&(sa->sin_port), (void *)&n[4], 2);
893 if (connect (my_socket, (struct sockaddr *) sa, sizeof (struct sockaddr_in)) < 0)
894 return 0;
895 return 1;
898 static int
899 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
901 struct sockaddr_in data_addr;
902 int data;
903 socklen_t len = sizeof(data_addr);
904 struct protoent *pe;
906 pe = getprotobyname ("tcp");
907 if (pe == NULL)
908 ERRNOR (EIO, -1);
909 again:
910 if (getsockname (SUP.sock, (struct sockaddr *) &data_addr, &len) == -1)
911 ERRNOR (EIO, -1);
912 data_addr.sin_port = 0;
914 data = socket (AF_INET, SOCK_STREAM, pe->p_proto);
915 if (data < 0)
916 ERRNOR (EIO, -1);
918 if (SUP.use_passive_connection) {
919 if (ftpfs_setup_passive (me, super, data, &data_addr))
920 return data;
922 SUP.use_passive_connection = 0;
923 print_vfs_message (_("ftpfs: could not setup passive mode"));
925 /* data or data_addr may be damaged by ftpfs_setup_passive */
926 close (data);
927 goto again;
930 /* If passive setup fails, fallback to active connections */
931 /* Active FTP connection */
932 if ((bind (data, (struct sockaddr *)&data_addr, len) == 0) &&
933 (getsockname (data, (struct sockaddr *) &data_addr, &len) == 0) &&
934 (listen (data, 1) == 0))
936 unsigned char *a = (unsigned char *)&data_addr.sin_addr;
937 unsigned char *p = (unsigned char *)&data_addr.sin_port;
939 if (ftpfs_command (me, super, WAIT_REPLY, "PORT %d,%d,%d,%d,%d,%d", a[0], a[1],
940 a[2], a[3], p[0], p[1]) == COMPLETE)
941 return data;
943 close (data);
944 ftpfs_errno = EIO;
945 return -1;
948 static int
949 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
950 const char *remote, int isbinary, int reget)
952 struct sockaddr_in from;
953 int s, j, data;
954 socklen_t fromlen = sizeof(from);
956 if ((s = ftpfs_initconn (me, super)) == -1)
957 return -1;
958 if (ftpfs_changetype (me, super, isbinary) == -1)
959 return -1;
960 if (reget > 0){
961 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
962 if (j != CONTINUE)
963 return -1;
965 if (remote) {
966 char *remote_path = ftpfs_translate_path (me, super, remote);
967 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
968 /* WarFtpD can't STORE //filename */
969 (*remote_path == '/') ? remote_path + 1 : remote_path);
970 g_free (remote_path);
971 } else
972 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
973 if (j != PRELIM)
974 ERRNOR (EPERM, -1);
975 enable_interrupt_key();
976 if (SUP.use_passive_connection)
977 data = s;
978 else {
979 data = accept (s, (struct sockaddr *)&from, &fromlen);
980 if (data < 0) {
981 ftpfs_errno = errno;
982 close (s);
983 return -1;
985 close (s);
987 disable_interrupt_key();
988 return data;
991 #define ABORT_TIMEOUT 5
992 static void
993 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
995 struct vfs_s_super *super = FH_SUPER;
996 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
997 fd_set mask;
998 char buf[1024];
999 int dsock = FH_SOCK;
1000 FH_SOCK = -1;
1001 SUP.ctl_connection_busy = 0;
1003 print_vfs_message (_("ftpfs: aborting transfer."));
1004 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
1005 print_vfs_message (_("ftpfs: abort error: %s"),
1006 unix_error_string (errno));
1007 if (dsock != -1)
1008 close (dsock);
1009 return;
1012 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
1013 print_vfs_message (_("ftpfs: abort failed"));
1014 if (dsock != -1)
1015 close (dsock);
1016 return;
1018 if (dsock != -1) {
1019 FD_ZERO (&mask);
1020 FD_SET (dsock, &mask);
1021 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1022 struct timeval start_tim, tim;
1023 gettimeofday (&start_tim, NULL);
1024 /* flush the remaining data */
1025 while (read (dsock, buf, sizeof (buf)) > 0) {
1026 gettimeofday (&tim, NULL);
1027 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1028 /* server keeps sending, drop the connection and ftpfs_reconnect */
1029 close (dsock);
1030 ftpfs_reconnect (me, super);
1031 return;
1035 close (dsock);
1037 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1038 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1041 #if 0
1042 static void
1043 resolve_symlink_without_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1045 struct linklist *flist;
1046 struct direntry *fe, *fel;
1047 char tmp[MC_MAXPATHLEN];
1048 int depth;
1050 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1051 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1052 /* flist->data->l_stat is alread initialized with 0 */
1053 fel = flist->data;
1054 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1055 if (fel->linkname[0] == '/') {
1056 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1057 continue;
1058 strcpy (tmp, fel->linkname);
1059 } else {
1060 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1061 continue;
1062 strcpy (tmp, dir->remote_path);
1063 if (tmp[1] != '\0')
1064 strcat (tmp, "/");
1065 strcat (tmp + 1, fel->linkname);
1067 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1068 canonicalize_pathname (tmp);
1069 fe = _get_file_entry(bucket, tmp, 0, 0);
1070 if (fe) {
1071 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1072 /* Symlink points to link which isn't resolved, yet. */
1073 if (fe->linkname[0] == '/') {
1074 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1075 break;
1076 strcpy (tmp, fe->linkname);
1077 } else {
1078 /* at this point tmp looks always like this
1079 /directory/filename, i.e. no need to check
1080 strrchr's return value */
1081 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1082 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1083 break;
1084 strcat (tmp, fe->linkname);
1086 continue;
1087 } else {
1088 fel->l_stat = g_new (struct stat, 1);
1089 if ( S_ISLNK (fe->s.st_mode))
1090 *fel->l_stat = *fe->l_stat;
1091 else
1092 *fel->l_stat = fe->s;
1093 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1096 break;
1100 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1103 static void
1104 resolve_symlink_with_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1106 char buffer[2048] = "", *filename;
1107 int sock;
1108 FILE *fp;
1109 struct stat s;
1110 struct linklist *flist;
1111 struct direntry *fe;
1112 int switch_method = 0;
1114 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1115 if (strchr (dir->remote_path, ' ')) {
1116 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1117 print_vfs_message(_("ftpfs: CWD failed."));
1118 return;
1120 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1122 else
1123 sock = ftpfs_open_data_connection (bucket, "LIST -lLa",
1124 dir->remote_path, TYPE_ASCII, 0);
1126 if (sock == -1) {
1127 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1128 return;
1131 fp = fdopen(sock, "r");
1132 if (fp == NULL) {
1133 close(sock);
1134 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1135 return;
1137 enable_interrupt_key();
1138 flist = dir->file_list->next;
1139 while (1) {
1140 do {
1141 if (flist == dir->file_list)
1142 goto done;
1143 fe = flist->data;
1144 flist = flist->next;
1145 } while (!S_ISLNK(fe->s.st_mode));
1146 while (1) {
1147 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1148 goto done;
1149 if (MEDATA->logfile){
1150 fputs (buffer, MEDATA->logfile);
1151 fflush (MEDATA->logfile);
1153 vfs_die("This code should be commented out\n");
1154 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1155 int r = strcmp(fe->name, filename);
1156 g_free(filename);
1157 if (r == 0) {
1158 if (S_ISLNK (s.st_mode)) {
1159 /* This server doesn't understand LIST -lLa */
1160 switch_method = 1;
1161 goto done;
1163 fe->l_stat = g_new (struct stat, 1);
1164 if (fe->l_stat == NULL)
1165 goto done;
1166 *fe->l_stat = s;
1167 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1168 break;
1170 if (r < 0)
1171 break;
1175 done:
1176 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1177 disable_interrupt_key();
1178 fclose(fp);
1179 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1182 static void
1183 resolve_symlink(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1185 print_vfs_message(_("Resolving symlink..."));
1187 if (SUP.strict_rfc959_list_cmd)
1188 resolve_symlink_without_ls_options(me, super, dir);
1189 else
1190 resolve_symlink_with_ls_options(me, super, dir);
1192 #endif
1194 static int
1195 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1197 struct vfs_s_entry *ent;
1198 struct vfs_s_super *super = dir->super;
1199 int sock, num_entries = 0;
1200 char buffer[BUF_8K];
1201 int cd_first;
1203 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1204 || (strchr (remote_path, ' ') != NULL);
1206 again:
1207 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1208 remote_path,
1209 SUP.strict ==
1210 RFC_STRICT ? _("(strict rfc959)") : "",
1211 cd_first ? _("(chdir first)") : "");
1213 if (cd_first) {
1214 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE) {
1215 ftpfs_errno = ENOENT;
1216 print_vfs_message (_("ftpfs: CWD failed."));
1217 return -1;
1221 gettimeofday (&dir->timestamp, NULL);
1222 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1224 if (SUP.strict == RFC_STRICT)
1225 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1226 else if (cd_first)
1227 /* Dirty hack to avoid autoprepending / to . */
1228 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1229 sock =
1230 ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1231 else {
1232 /* Trailing "/." is necessary if remote_path is a symlink */
1233 char *path = concat_dir_and_file (remote_path, ".");
1234 sock =
1235 ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII,
1237 g_free (path);
1240 if (sock == -1)
1241 goto fallback;
1243 /* Clear the interrupt flag */
1244 enable_interrupt_key ();
1246 while (1) {
1247 int i;
1248 int res =
1249 vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1250 sock);
1251 if (!res)
1252 break;
1254 if (res == EINTR) {
1255 me->verrno = ECONNRESET;
1256 close (sock);
1257 disable_interrupt_key ();
1258 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1259 print_vfs_message (_("%s: failure"), me->name);
1260 return -1;
1263 if (MEDATA->logfile) {
1264 fputs (buffer, MEDATA->logfile);
1265 fputs ("\n", MEDATA->logfile);
1266 fflush (MEDATA->logfile);
1269 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1270 i = ent->ino->st.st_nlink;
1271 if (!vfs_parse_ls_lga
1272 (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1273 vfs_s_free_entry (me, ent);
1274 continue;
1276 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1277 num_entries++;
1278 vfs_s_insert_entry (me, dir, ent);
1281 close (sock);
1282 me->verrno = E_REMOTE;
1283 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1284 goto fallback;
1286 if (num_entries == 0 && cd_first == 0) {
1287 /* The LIST command may produce an empty output. In such scenario
1288 it is not clear whether this is caused by `remote_path' being
1289 a non-existent path or for some other reason (listing emtpy
1290 directory without the -a option, non-readable directory, etc.).
1292 Since `dir_load' is a crucial method, when it comes to determine
1293 whether a given path is a _directory_, the code must try its best
1294 to determine the type of `remote_path'. The only reliable way to
1295 achieve this is trough issuing a CWD command. */
1297 cd_first = 1;
1298 goto again;
1301 if (SUP.strict == RFC_AUTODETECT)
1302 SUP.strict = RFC_DARING;
1304 print_vfs_message (_("%s: done."), me->name);
1305 return 0;
1307 fallback:
1308 if (SUP.strict == RFC_AUTODETECT) {
1309 /* It's our first attempt to get a directory listing from this
1310 server (UNIX style LIST command) */
1311 SUP.strict = RFC_STRICT;
1312 /* I hate goto, but recursive call needs another 8K on stack */
1313 /* return ftpfs_dir_load (me, dir, remote_path); */
1314 cd_first = 1;
1315 goto again;
1317 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1318 ERRNOR (EACCES, -1);
1321 static int
1322 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name,
1323 char *localname)
1325 int h, sock, n_read, n_written;
1326 off_t n_stored;
1327 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1328 struct linger li;
1329 #else
1330 int flag_one = 1;
1331 #endif
1332 char buffer[8192];
1333 struct stat s;
1334 char *w_buf;
1335 struct vfs_s_super *super = FH_SUPER;
1337 h = open (localname, O_RDONLY);
1338 if (h == -1)
1339 ERRNOR (EIO, -1);
1340 sock =
1341 ftpfs_open_data_connection (me, super,
1342 fh->u.ftp.append ? "APPE" : "STOR", name,
1343 TYPE_BINARY, 0);
1344 if (sock < 0 || fstat (h, &s) == -1) {
1345 close (h);
1346 return -1;
1348 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1349 li.l_onoff = 1;
1350 li.l_linger = 120;
1351 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1352 #else
1353 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1354 #endif
1355 n_stored = 0;
1357 enable_interrupt_key ();
1358 while (1) {
1359 while ((n_read = read (h, buffer, sizeof (buffer))) == -1) {
1360 if (errno == EINTR) {
1361 if (got_interrupt ()) {
1362 ftpfs_errno = EINTR;
1363 goto error_return;
1364 } else
1365 continue;
1367 ftpfs_errno = errno;
1368 goto error_return;
1370 if (n_read == 0)
1371 break;
1372 n_stored += n_read;
1373 w_buf = buffer;
1374 while ((n_written = write (sock, w_buf, n_read)) != n_read) {
1375 if (n_written == -1) {
1376 if (errno == EINTR && !got_interrupt ()) {
1377 continue;
1379 ftpfs_errno = errno;
1380 goto error_return;
1382 w_buf += n_written;
1383 n_read -= n_written;
1385 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1386 (unsigned long) n_stored, (unsigned long) s.st_size);
1388 disable_interrupt_key ();
1389 close (sock);
1390 close (h);
1391 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1392 ERRNOR (EIO, -1);
1393 return 0;
1394 error_return:
1395 disable_interrupt_key ();
1396 close (sock);
1397 close (h);
1398 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1399 return -1;
1402 static int
1403 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1405 char *name = vfs_s_fullpath (me, fh->ino);
1407 if (!name)
1408 return 0;
1409 FH_SOCK = ftpfs_open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1410 g_free (name);
1411 if (FH_SOCK == -1)
1412 ERRNOR (EACCES, 0);
1413 fh->linear = LS_LINEAR_OPEN;
1414 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1415 fh->u.ftp.append = 0;
1416 return 1;
1419 static int
1420 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1422 int n;
1423 struct vfs_s_super *super = FH_SUPER;
1425 while ((n = read (FH_SOCK, buf, len))<0) {
1426 if ((errno == EINTR) && !got_interrupt())
1427 continue;
1428 break;
1431 if (n<0)
1432 ftpfs_linear_abort(me, fh);
1434 if (!n) {
1435 SUP.ctl_connection_busy = 0;
1436 close (FH_SOCK);
1437 FH_SOCK = -1;
1438 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1439 ERRNOR (E_REMOTE, -1);
1440 return 0;
1442 ERRNOR (errno, n);
1445 static void
1446 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1448 if (FH_SOCK != -1)
1449 ftpfs_linear_abort(me, fh);
1452 static int ftpfs_ctl (void *fh, int ctlop, void *arg)
1454 (void) arg;
1456 switch (ctlop) {
1457 case VFS_CTL_IS_NOTREADY:
1459 int v;
1461 if (!FH->linear)
1462 vfs_die ("You may not do this");
1463 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1464 return 0;
1466 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1467 if (((v < 0) && (errno == EINTR)) || v == 0)
1468 return 1;
1469 return 0;
1471 default:
1472 return 0;
1476 static int
1477 ftpfs_send_command(struct vfs_class *me, const char *filename, const char *cmd, int flags)
1479 const char *rpath;
1480 char *p, *mpath = g_strdup(filename);
1481 struct vfs_s_super *super;
1482 int r;
1483 int flush_directory_cache = (flags & OPT_FLUSH);
1485 if (!(rpath = vfs_s_get_path_mangle(me, mpath, &super, 0))) {
1486 g_free(mpath);
1487 return -1;
1489 p = ftpfs_translate_path (me, super, rpath);
1490 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1491 g_free (p);
1492 vfs_stamp_create (&vfs_ftpfs_ops, super);
1493 if (flags & OPT_IGNORE_ERROR)
1494 r = COMPLETE;
1495 if (r != COMPLETE) {
1496 me->verrno = EPERM;
1497 g_free (mpath);
1498 return -1;
1500 if (flush_directory_cache)
1501 vfs_s_invalidate(me, super);
1502 g_free(mpath);
1503 return 0;
1506 /* This routine is called as the last step in load_setup */
1507 void
1508 ftpfs_init_passwd(void)
1510 ftpfs_anonymous_passwd = load_anon_passwd ();
1511 if (ftpfs_anonymous_passwd)
1512 return;
1514 /* If there is no anonymous ftp password specified
1515 * then we'll just use anonymous@
1516 * We don't send any other thing because:
1517 * - We want to remain anonymous
1518 * - We want to stop SPAM
1519 * - We don't want to let ftp sites to discriminate by the user,
1520 * host or country.
1522 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1525 static int ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1527 char buf[BUF_SMALL];
1529 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1530 return ftpfs_send_command(me, path, buf, OPT_FLUSH);
1533 static int ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1535 #if 0
1536 ftpfs_errno = EPERM;
1537 return -1;
1538 #else
1539 /* Everyone knows it is not possible to chown remotely, so why bother them.
1540 If someone's root, then copy/move will always try to chown it... */
1541 (void) me;
1542 (void) path;
1543 (void) owner;
1544 (void) group;
1545 return 0;
1546 #endif
1549 static int ftpfs_unlink (struct vfs_class *me, const char *path)
1551 return ftpfs_send_command(me, path, "DELE /%s", OPT_FLUSH);
1554 /* Return 1 if path is the same directory as the one we are in now */
1555 static int
1556 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1558 (void) me;
1560 if (!SUP.cwdir)
1561 return 0;
1562 if (strcmp (path, SUP.cwdir) == 0)
1563 return 1;
1564 return 0;
1567 static int
1568 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1570 int r;
1571 char *p;
1573 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1574 return COMPLETE;
1576 p = ftpfs_translate_path (me, super, remote_path);
1577 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1578 g_free (p);
1580 if (r != COMPLETE) {
1581 ftpfs_errno = EIO;
1582 } else {
1583 g_free(SUP.cwdir);
1584 SUP.cwdir = g_strdup (remote_path);
1585 SUP.cwd_deferred = 0;
1587 return r;
1590 static int ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1592 ftpfs_send_command(me, path1, "RNFR /%s", OPT_FLUSH);
1593 return ftpfs_send_command(me, path2, "RNTO /%s", OPT_FLUSH);
1596 static int ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1598 (void) mode; /* FIXME: should be used */
1600 return ftpfs_send_command(me, path, "MKD /%s", OPT_FLUSH);
1603 static int ftpfs_rmdir (struct vfs_class *me, const char *path)
1605 return ftpfs_send_command(me, path, "RMD /%s", OPT_FLUSH);
1608 static int
1609 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
1610 int mode)
1612 (void) mode;
1614 fh->u.ftp.append = 0;
1615 /* File will be written only, so no need to retrieve it from ftp server */
1616 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
1617 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1618 struct linger li;
1619 #else
1620 int li = 1;
1621 #endif
1622 char *name;
1624 /* ftpfs_linear_start() called, so data will be written
1625 * to local temporary file and stored to ftp server
1626 * by vfs_s_close later
1628 if (FH_SUPER->u.ftp.ctl_connection_busy) {
1629 if (!fh->ino->localname) {
1630 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1631 fh->ino->ent->name);
1632 if (handle == -1)
1633 return -1;
1634 close (handle);
1635 fh->u.ftp.append = flags & O_APPEND;
1637 return 0;
1639 name = vfs_s_fullpath (me, fh->ino);
1640 if (!name)
1641 return -1;
1642 fh->handle =
1643 ftpfs_open_data_connection (me, fh->ino->super,
1644 (flags & O_APPEND) ? "APPE" :
1645 "STOR", name, TYPE_BINARY, 0);
1646 g_free (name);
1648 if (fh->handle < 0)
1649 return -1;
1650 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1651 li.l_onoff = 1;
1652 li.l_linger = 120;
1653 #endif
1654 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1656 if (fh->ino->localname) {
1657 unlink (fh->ino->localname);
1658 g_free (fh->ino->localname);
1659 fh->ino->localname = NULL;
1661 return 0;
1664 if (!fh->ino->localname)
1665 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1666 return -1;
1667 if (!fh->ino->localname)
1668 vfs_die ("retrieve_file failed to fill in localname");
1669 return 0;
1672 static int ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1674 if (fh->handle != -1 && !fh->ino->localname){
1675 close (fh->handle);
1676 fh->handle = -1;
1677 /* File is stored to destination already, so
1678 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1680 fh->changed = 0;
1681 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1682 ERRNOR (EIO, -1);
1683 vfs_s_invalidate (me, FH_SUPER);
1685 return 0;
1688 static void
1689 ftpfs_done (struct vfs_class *me)
1691 struct no_proxy_entry *np;
1693 (void) me;
1695 while (no_proxy) {
1696 np = no_proxy->next;
1697 g_free (no_proxy->domain);
1698 g_free (no_proxy);
1699 no_proxy = np;
1701 g_free (ftpfs_anonymous_passwd);
1702 g_free (ftpfs_proxy_host);
1705 static void
1706 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
1708 struct vfs_s_super *super = MEDATA->supers;
1709 char *name;
1711 while (super){
1712 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
1713 (*func)(name);
1714 g_free (name);
1715 super = super->next;
1719 static char buffer[BUF_MEDIUM];
1720 static char *netrc;
1721 static const char *netrcp;
1723 /* This should match the keywords[] array below */
1724 typedef enum {
1725 NETRC_NONE = 0,
1726 NETRC_DEFAULT,
1727 NETRC_MACHINE,
1728 NETRC_LOGIN,
1729 NETRC_PASSWORD,
1730 NETRC_PASSWD,
1731 NETRC_ACCOUNT,
1732 NETRC_MACDEF,
1733 NETRC_UNKNOWN
1734 } keyword_t;
1736 static keyword_t ftpfs_netrc_next (void)
1738 char *p;
1739 keyword_t i;
1740 static const char *const keywords[] = { "default", "machine",
1741 "login", "password", "passwd", "account", "macdef", NULL
1745 while (1) {
1746 netrcp = skip_separators (netrcp);
1747 if (*netrcp != '\n')
1748 break;
1749 netrcp++;
1751 if (!*netrcp)
1752 return NETRC_NONE;
1753 p = buffer;
1754 if (*netrcp == '"') {
1755 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1756 if (*netrcp == '\\')
1757 netrcp++;
1758 *p++ = *netrcp;
1760 } else {
1761 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1762 *netrcp != ',' && *netrcp; netrcp++) {
1763 if (*netrcp == '\\')
1764 netrcp++;
1765 *p++ = *netrcp;
1768 *p = 0;
1769 if (!*buffer)
1770 return NETRC_NONE;
1772 i = NETRC_DEFAULT;
1773 while (keywords[i - 1]) {
1774 if (!strcmp (keywords[i - 1], buffer))
1775 return i;
1777 i++;
1780 return NETRC_UNKNOWN;
1783 static int ftpfs_netrc_bad_mode (const char *netrcname)
1785 static int be_angry = 1;
1786 struct stat mystat;
1788 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1789 if (be_angry) {
1790 message (D_ERROR, MSG_ERROR,
1791 _("~/.netrc file has incorrect mode.\n"
1792 "Remove password or correct mode."));
1793 be_angry = 0;
1795 return 1;
1797 return 0;
1800 /* Scan .netrc until we find matching "machine" or "default"
1801 * domain is used for additional matching
1802 * No search is done after "default" in compliance with "man netrc"
1803 * Return 0 if found, -1 otherwise */
1804 static int ftpfs_find_machine (const char *host, const char *domain)
1806 keyword_t keyword;
1808 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) {
1809 if (keyword == NETRC_DEFAULT)
1810 return 0;
1812 if (keyword == NETRC_MACDEF) {
1813 /* Scan for an empty line, which concludes "macdef" */
1814 do {
1815 while (*netrcp && *netrcp != '\n')
1816 netrcp++;
1817 if (*netrcp != '\n')
1818 break;
1819 netrcp++;
1820 } while (*netrcp && *netrcp != '\n');
1821 continue;
1824 if (keyword != NETRC_MACHINE)
1825 continue;
1827 /* Take machine name */
1828 if (ftpfs_netrc_next () == NETRC_NONE)
1829 break;
1831 if (g_strcasecmp (host, buffer)) {
1832 /* Try adding our domain to short names in .netrc */
1833 const char *host_domain = strchr (host, '.');
1834 if (!host_domain)
1835 continue;
1837 /* Compare domain part */
1838 if (g_strcasecmp (host_domain, domain))
1839 continue;
1841 /* Compare local part */
1842 if (g_strncasecmp (host, buffer, host_domain - host))
1843 continue;
1846 return 0;
1849 /* end of .netrc */
1850 return -1;
1853 /* Extract login and password from .netrc for the host.
1854 * pass may be NULL.
1855 * Returns 0 for success, -1 for error */
1856 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass)
1858 char *netrcname;
1859 char *tmp_pass = NULL;
1860 char hostname[MAXHOSTNAMELEN];
1861 const char *domain;
1862 keyword_t keyword;
1863 static struct rupcache {
1864 struct rupcache *next;
1865 char *host;
1866 char *login;
1867 char *pass;
1868 } *rup_cache = NULL, *rupp;
1870 /* Initialize *login and *pass */
1871 if (!login)
1872 return 0;
1873 *login = NULL;
1874 if (pass)
1875 *pass = NULL;
1877 /* Look up in the cache first */
1878 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
1879 if (!strcmp (host, rupp->host)) {
1880 if (rupp->login)
1881 *login = g_strdup (rupp->login);
1882 if (pass && rupp->pass)
1883 *pass = g_strdup (rupp->pass);
1884 return 0;
1888 /* Load current .netrc */
1889 netrcname = concat_dir_and_file (home_dir, ".netrc");
1890 netrcp = netrc = load_file (netrcname);
1891 if (netrc == NULL) {
1892 g_free (netrcname);
1893 return 0;
1896 /* Find our own domain name */
1897 if (gethostname (hostname, sizeof (hostname)) < 0)
1898 *hostname = 0;
1899 if (!(domain = strchr (hostname, '.')))
1900 domain = "";
1902 /* Scan for "default" and matching "machine" keywords */
1903 ftpfs_find_machine (host, domain);
1905 /* Scan for keywords following "default" and "machine" */
1906 while (1) {
1907 int need_break = 0;
1908 keyword = ftpfs_netrc_next ();
1910 switch (keyword) {
1911 case NETRC_LOGIN:
1912 if (ftpfs_netrc_next () == NETRC_NONE) {
1913 need_break = 1;
1914 break;
1917 /* We have another name already - should not happen */
1918 if (*login) {
1919 need_break = 1;
1920 break;
1923 /* We have login name now */
1924 *login = g_strdup (buffer);
1925 break;
1927 case NETRC_PASSWORD:
1928 case NETRC_PASSWD:
1929 if (ftpfs_netrc_next () == NETRC_NONE) {
1930 need_break = 1;
1931 break;
1934 /* Ignore unsafe passwords */
1935 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
1936 && ftpfs_netrc_bad_mode (netrcname)) {
1937 need_break = 1;
1938 break;
1941 /* Remember password. pass may be NULL, so use tmp_pass */
1942 if (tmp_pass == NULL)
1943 tmp_pass = g_strdup (buffer);
1944 break;
1946 case NETRC_ACCOUNT:
1947 /* "account" is followed by a token which we ignore */
1948 if (ftpfs_netrc_next () == NETRC_NONE) {
1949 need_break = 1;
1950 break;
1953 /* Ignore account, but warn user anyways */
1954 ftpfs_netrc_bad_mode (netrcname);
1955 break;
1957 default:
1958 /* Unexpected keyword or end of file */
1959 need_break = 1;
1960 break;
1963 if (need_break)
1964 break;
1967 g_free (netrc);
1968 g_free (netrcname);
1970 rupp = g_new (struct rupcache, 1);
1971 rupp->host = g_strdup (host);
1972 rupp->login = rupp->pass = 0;
1974 if (*login != NULL) {
1975 rupp->login = g_strdup (*login);
1977 if (tmp_pass != NULL)
1978 rupp->pass = g_strdup (tmp_pass);
1979 rupp->next = rup_cache;
1980 rup_cache = rupp;
1982 if (pass)
1983 *pass = tmp_pass;
1985 return 0;
1988 void
1989 init_ftpfs (void)
1991 static struct vfs_s_subclass ftpfs_subclass;
1993 ftpfs_subclass.flags = VFS_S_REMOTE;
1994 ftpfs_subclass.archive_same = ftpfs_archive_same;
1995 ftpfs_subclass.open_archive = ftpfs_open_archive;
1996 ftpfs_subclass.free_archive = ftpfs_free_archive;
1997 ftpfs_subclass.fh_open = ftpfs_fh_open;
1998 ftpfs_subclass.fh_close = ftpfs_fh_close;
1999 ftpfs_subclass.dir_load = ftpfs_dir_load;
2000 ftpfs_subclass.file_store = ftpfs_file_store;
2001 ftpfs_subclass.linear_start = ftpfs_linear_start;
2002 ftpfs_subclass.linear_read = ftpfs_linear_read;
2003 ftpfs_subclass.linear_close = ftpfs_linear_close;
2005 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2006 vfs_ftpfs_ops.name = "ftpfs";
2007 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2008 vfs_ftpfs_ops.prefix = "ftp:";
2009 vfs_ftpfs_ops.done = &ftpfs_done;
2010 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2011 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2012 vfs_ftpfs_ops.chown = ftpfs_chown;
2013 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2014 vfs_ftpfs_ops.rename = ftpfs_rename;
2015 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2016 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2017 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2018 vfs_register_class (&vfs_ftpfs_ops);