* subshell.c (do_subshell_chdir): Use mc_realpath() to fix tcsh's
[midnight-commander.git] / vfs / ftpfs.c
blob524df4cb86935ae86ef36482fbf9061ca684dd76
1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Ching Hui
5 1995 Jakub Jelinek
6 1995, 1996, 1997 Miguel de Icaza
7 1997 Norbert Warmuth
8 1998 Pavel Machek
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public License
12 as published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 /* FTPfs TODO:
26 - make it more robust - all the connects etc. should handle EADDRINUSE and
27 ERETRY (have I spelled these names correctly?)
28 - make the user able to flush a connection - all the caches will get empty
29 etc., (tarfs as well), we should give there a user selectable timeout
30 and assign a key sequence.
31 - use hash table instead of linklist to cache ftpfs directory.
33 What to do with this?
36 * NOTE: Usage of tildes is deprecated, consider:
37 * cd /#ftp:pavel@hobit
38 * cd ~
39 * And now: what do I want to do? Do I want to go to /home/pavel or to
40 * /#ftp:hobit/home/pavel? I think first has better sense...
43 int f = !strcmp( remote_path, "/~" );
44 if (f || !strncmp( remote_path, "/~/", 3 )) {
45 char *s;
46 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
47 g_free (remote_path);
48 remote_path = s;
55 /* Namespace pollution: horrible */
57 #include <config.h>
58 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
59 #include <netdb.h> /* struct hostent */
60 #include <sys/socket.h> /* AF_INET */
61 #include <netinet/in.h> /* struct in_addr */
62 #ifdef HAVE_ARPA_INET_H
63 #include <arpa/inet.h>
64 #endif
65 #include <arpa/ftp.h>
66 #include <arpa/telnet.h>
67 #include <sys/param.h>
68 #include <errno.h>
69 #include <ctype.h>
71 #include "utilvfs.h"
73 #include "xdirentry.h"
74 #include "vfs.h"
75 #include "vfs-impl.h"
76 #include "gc.h" /* vfs_stamp_create */
77 #include "tcputil.h"
78 #include "../src/setup.h" /* for load_anon_passwd */
79 #include "ftpfs.h"
80 #ifndef MAXHOSTNAMELEN
81 # define MAXHOSTNAMELEN 64
82 #endif
84 #define UPLOAD_ZERO_LENGTH_FILE
85 #define SUP super->u.ftp
86 #define FH_SOCK fh->u.ftp.sock
88 #ifndef INADDR_NONE
89 #define INADDR_NONE 0xffffffff
90 #endif
92 #define RFC_AUTODETECT 0
93 #define RFC_DARING 1
94 #define RFC_STRICT 2
96 #ifndef HAVE_C_TYPE_SOCKLEN_T
97 typedef int socklen_t;
98 #endif
100 static int ftpfs_errno;
101 static int code;
103 /* Delay to retry a connection */
104 int ftpfs_retry_seconds = 30;
106 /* Method to use to connect to ftp sites */
107 int ftpfs_use_passive_connections = 1;
109 /* Method used to get directory listings:
110 * 1: try 'LIST -la <path>', if it fails
111 * fall back to CWD <path>; LIST
112 * 0: always use CWD <path>; LIST
114 int ftpfs_use_unix_list_options = 1;
116 /* First "CWD <path>", then "LIST -la ." */
117 int ftpfs_first_cd_then_ls;
119 /* Use the ~/.netrc */
120 int use_netrc = 1;
122 /* Anonymous setup */
123 char *ftpfs_anonymous_passwd = NULL;
124 int ftpfs_directory_timeout = 900;
126 /* Proxy host */
127 char *ftpfs_proxy_host = NULL;
129 /* wether we have to use proxy by default? */
130 int ftpfs_always_use_proxy;
132 #ifdef FIXME_LATER_ALIGATOR
133 static struct linklist *connections_list;
134 #endif
136 /* ftpfs_command wait_flag: */
137 #define NONE 0x00
138 #define WAIT_REPLY 0x01
139 #define WANT_STRING 0x02
140 static char reply_str [80];
142 static struct vfs_class vfs_ftpfs_ops;
144 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
145 Translate a Unix path, i.e. MC's internal path representation (e.g.
146 /somedir/somefile) to a path valid for the remote server. Every path
147 transfered to the remote server has to be mangled by this function
148 right prior to sending it.
149 Currently only Amiga ftp servers are handled in a special manner.
151 When the remote server is an amiga:
152 a) strip leading slash if necesarry
153 b) replace first occurance of ":/" with ":"
154 c) strip trailing "/."
157 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
158 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path);
159 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
160 __attribute__ ((format (printf, 4, 5)));
161 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
162 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass);
163 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
165 static char *
166 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
168 if (!SUP.remote_is_amiga)
169 return g_strdup (remote_path);
170 else {
171 char *ret, *p;
173 if (MEDATA->logfile) {
174 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
175 fflush (MEDATA->logfile);
178 /* strip leading slash(es) */
179 while (*remote_path == '/')
180 remote_path++;
183 * Don't change "/" into "", e.g. "CWD " would be
184 * invalid.
186 if (*remote_path == '\0')
187 return g_strdup (".");
189 ret = g_strdup (remote_path);
191 /* replace first occurance of ":/" with ":" */
192 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
193 strcpy (p + 1, p + 2);
195 /* strip trailing "/." */
196 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
197 *p = '\0';
198 return ret;
202 /* Extract the hostname and username from the path */
205 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
206 * ftp://sunsite.unc.edu/pub/linux
207 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
208 * ftp://tsx-11.mit.edu:8192/
209 * ftp://joe@foo.edu:11321/private
210 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
211 * is supplied.
215 #define FTP_COMMAND_PORT 21
217 static void
218 ftpfs_split_url(char *path, char **host, char **user, int *port, char **pass)
220 char *p;
222 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
223 URL_ALLOW_ANON);
225 if (!*user) {
226 /* Look up user and password in netrc */
227 if (use_netrc)
228 ftpfs_netrc_lookup (*host, user, pass);
229 if (!*user)
230 *user = g_strdup ("anonymous");
233 /* Look up password in netrc for known user */
234 if (use_netrc && *user && pass && !*pass) {
235 char *new_user;
237 ftpfs_netrc_lookup (*host, &new_user, pass);
239 /* If user is different, remove password */
240 if (new_user && strcmp (*user, new_user)) {
241 g_free (*pass);
242 *pass = NULL;
245 g_free (new_user);
248 g_free (p);
251 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
252 static int
253 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
255 char answer[BUF_1K];
256 int i;
258 for (;;) {
259 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
260 if (string_buf)
261 *string_buf = 0;
262 code = 421;
263 return 4;
265 switch (sscanf(answer, "%d", &code)){
266 case 0:
267 if (string_buf)
268 g_strlcpy (string_buf, answer, string_len);
269 code = 500;
270 return 5;
271 case 1:
272 if (answer[3] == '-') {
273 while (1) {
274 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
275 if (string_buf)
276 *string_buf = 0;
277 code = 421;
278 return 4;
280 if ((sscanf (answer, "%d", &i) > 0) &&
281 (code == i) && (answer[3] == ' '))
282 break;
285 if (string_buf)
286 g_strlcpy (string_buf, answer, string_len);
287 return code / 100;
292 static int
293 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
295 int sock = ftpfs_open_socket (me, super);
296 if (sock != -1){
297 char *cwdir = SUP.cwdir;
298 close (SUP.sock);
299 SUP.sock = sock;
300 SUP.cwdir = NULL;
301 if (ftpfs_login_server (me, super, SUP.password)){
302 if (!cwdir)
303 return 1;
304 sock = ftpfs_chdir_internal (me, super, cwdir);
305 g_free (cwdir);
306 return sock == COMPLETE;
308 SUP.cwdir = cwdir;
310 return 0;
313 static int
314 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
316 va_list ap;
317 char *cmdstr;
318 int status, cmdlen;
320 va_start (ap, fmt);
321 cmdstr = g_strdup_vprintf (fmt, ap);
322 va_end (ap);
324 cmdlen = strlen (cmdstr);
325 cmdstr = g_realloc (cmdstr, cmdlen + 3);
326 strcpy (cmdstr + cmdlen, "\r\n");
327 cmdlen += 2;
329 if (MEDATA->logfile) {
330 if (strncmp (cmdstr, "PASS ", 5) == 0) {
331 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
332 } else
333 fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
335 fflush (MEDATA->logfile);
338 got_sigpipe = 0;
339 enable_interrupt_key ();
340 status = write (SUP.sock, cmdstr, cmdlen);
342 if (status < 0) {
343 code = 421;
345 if (errno == EPIPE) { /* Remote server has closed connection */
346 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
347 if (level == 0) {
348 level = 1;
349 status = ftpfs_reconnect (me, super);
350 level = 0;
351 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
352 goto ok;
356 got_sigpipe = 1;
358 g_free (cmdstr);
359 disable_interrupt_key ();
360 return TRANSIENT;
363 g_free (cmdstr);
364 disable_interrupt_key ();
366 if (wait_reply)
367 return ftpfs_get_reply (me, SUP.sock,
368 (wait_reply & WANT_STRING) ? reply_str : NULL,
369 sizeof (reply_str) - 1);
370 return COMPLETE;
373 static void
374 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
376 if (SUP.sock != -1){
377 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
378 ftpfs_command(me, super, NONE, "QUIT");
379 close(SUP.sock);
381 g_free (SUP.host);
382 g_free (SUP.user);
383 g_free (SUP.cwdir);
384 g_free (SUP.password);
387 /* some defines only used by ftpfs_changetype */
388 /* These two are valid values for the second parameter */
389 #define TYPE_ASCII 0
390 #define TYPE_BINARY 1
392 /* This one is only used to initialize bucket->isbinary, don't use it as
393 second parameter to ftpfs_changetype. */
394 #define TYPE_UNKNOWN -1
396 static int
397 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
399 if (binary != SUP.isbinary) {
400 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
401 ERRNOR (EIO, -1);
402 SUP.isbinary = binary;
404 return binary;
407 /* This routine logs the user in */
408 static int
409 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
410 const char *netrcpass)
412 char *pass;
413 char *op;
414 char *name; /* login user name */
415 int anon = 0;
416 char reply_string[BUF_MEDIUM];
418 SUP.isbinary = TYPE_UNKNOWN;
420 if (SUP.password) /* explicit password */
421 op = g_strdup (SUP.password);
422 else if (netrcpass) /* password from netrc */
423 op = g_strdup (netrcpass);
424 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp")) {
425 if (!ftpfs_anonymous_passwd) /* default anonymous password */
426 ftpfs_init_passwd ();
427 op = g_strdup (ftpfs_anonymous_passwd);
428 anon = 1;
429 } else { /* ask user */
430 char *p;
432 p = g_strconcat (_(" FTP: Password required for "), SUP.user, " ",
433 NULL);
434 op = vfs_get_password (p);
435 g_free (p);
436 if (op == NULL)
437 ERRNOR (EPERM, 0);
438 SUP.password = g_strdup (op);
441 if (!anon || MEDATA->logfile)
442 pass = op;
443 else {
444 pass = g_strconcat ("-", op, (char *) NULL);
445 wipe_password (op);
448 /* Proxy server accepts: username@host-we-want-to-connect */
449 if (SUP.proxy) {
450 name =
451 g_strconcat (SUP.user, "@",
452 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host,
453 NULL);
454 } else
455 name = g_strdup (SUP.user);
457 if (ftpfs_get_reply
458 (me, SUP.sock, reply_string,
459 sizeof (reply_string) - 1) == COMPLETE) {
460 g_strup (reply_string);
461 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
462 if (MEDATA->logfile) {
463 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n",
464 SUP.remote_is_amiga);
465 fflush (MEDATA->logfile);
468 print_vfs_message (_("ftpfs: sending login name"));
470 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name)) {
471 case CONTINUE:
472 print_vfs_message (_("ftpfs: sending user password"));
473 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
474 if (code == CONTINUE) {
475 char *p;
477 p = g_strdup_printf (_
478 ("FTP: Account required for user %s"),
479 SUP.user);
480 op = input_dialog (p, _("Account:"), "");
481 g_free (p);
482 if (op == NULL)
483 ERRNOR (EPERM, 0);
484 print_vfs_message (_("ftpfs: sending user account"));
485 code =
486 ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
487 g_free (op);
489 if (code != COMPLETE)
490 break;
491 /* fall through */
493 case COMPLETE:
494 print_vfs_message (_("ftpfs: logged in"));
495 wipe_password (pass);
496 g_free (name);
497 return 1;
499 default:
500 SUP.failed_on_login = 1;
501 if (SUP.password)
502 wipe_password (SUP.password);
503 SUP.password = 0;
505 goto login_fail;
508 message (1, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
509 SUP.user);
510 login_fail:
511 wipe_password (pass);
512 g_free (name);
513 ERRNOR (EPERM, 0);
516 static struct no_proxy_entry {
517 char *domain;
518 void *next;
519 } *no_proxy;
521 static void
522 ftpfs_load_no_proxy_list (void)
524 /* FixMe: shouldn't be hardcoded!!! */
525 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
526 struct no_proxy_entry *np, *current = 0;
527 FILE *npf;
528 int c;
529 char *p;
530 static char *mc_file;
532 if (mc_file)
533 return;
535 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
536 if (exist_file (mc_file) &&
537 (npf = fopen (mc_file, "r"))) {
538 while (fgets (s, sizeof (s), npf)) {
539 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
540 while ((c = fgetc (npf)) != EOF && c != '\n')
542 continue;
545 if (p == s)
546 continue;
548 *p = '\0';
550 np = g_new (struct no_proxy_entry, 1);
551 np->domain = g_strdup (s);
552 np->next = NULL;
553 if (no_proxy)
554 current->next = np;
555 else
556 no_proxy = np;
557 current = np;
560 fclose (npf);
562 g_free (mc_file);
565 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
566 static int
567 ftpfs_check_proxy (const char *host)
569 struct no_proxy_entry *npe;
571 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
572 return 0; /* sanity check */
574 if (*host == '!')
575 return 1;
577 if (!ftpfs_always_use_proxy)
578 return 0;
580 if (!strchr (host, '.'))
581 return 0;
583 ftpfs_load_no_proxy_list ();
584 for (npe = no_proxy; npe; npe=npe->next) {
585 char *domain = npe->domain;
587 if (domain[0] == '.') {
588 int ld = strlen (domain);
589 int lh = strlen (host);
591 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
592 ld--;
593 lh--;
596 if (!ld)
597 return 0;
598 } else
599 if (!g_strcasecmp (host, domain))
600 return 0;
603 return 1;
606 static void
607 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
609 char *user, *dir;
611 dir =
612 vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT,
613 URL_ALLOW_ANON);
614 g_free (user);
615 g_free (dir);
618 static int
619 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
621 struct sockaddr_in server_address;
622 struct hostent *hp;
623 int my_socket;
624 char *host;
625 int port = SUP.port;
626 int free_host = 0;
628 /* Use a proxy host? */
629 host = SUP.host;
631 if (!host || !*host){
632 print_vfs_message (_("ftpfs: Invalid host name."));
633 ftpfs_errno = EINVAL;
634 return -1;
637 /* Hosts to connect to that start with a ! should use proxy */
638 if (SUP.proxy){
639 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &port);
640 free_host = 1;
643 /* Get host address */
644 memset ((char *) &server_address, 0, sizeof (server_address));
645 server_address.sin_family = AF_INET;
646 server_address.sin_addr.s_addr = inet_addr (host);
647 if (server_address.sin_addr.s_addr == INADDR_NONE) {
648 hp = gethostbyname (host);
649 if (hp == NULL){
650 print_vfs_message (_("ftpfs: Invalid host address."));
651 ftpfs_errno = EINVAL;
652 if (free_host)
653 g_free (host);
654 return -1;
656 server_address.sin_family = hp->h_addrtype;
658 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
659 memcpy ((char *) &server_address.sin_addr, (char *) hp->h_addr, 4);
662 server_address.sin_port = htons (port);
664 /* Connect */
665 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
666 ftpfs_errno = errno;
667 if (free_host)
668 g_free (host);
669 return -1;
672 print_vfs_message (_("ftpfs: making connection to %s"), host);
673 if (free_host)
674 g_free (host);
676 enable_interrupt_key (); /* clear the interrupt flag */
678 if (connect (my_socket, (struct sockaddr *) &server_address,
679 sizeof (server_address)) < 0){
680 ftpfs_errno = errno;
681 if (errno == EINTR && got_interrupt ())
682 print_vfs_message (_("ftpfs: connection interrupted by user"));
683 else
684 print_vfs_message (_("ftpfs: connection to server failed: %s"),
685 unix_error_string(errno));
686 disable_interrupt_key();
687 close (my_socket);
688 return -1;
690 disable_interrupt_key();
691 return my_socket;
694 static int
695 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
697 int retry_seconds, count_down;
699 /* We do not want to use the passive if we are using proxies */
700 if (SUP.proxy)
701 SUP.use_passive_connection = 0;
703 retry_seconds = 0;
704 do {
705 SUP.failed_on_login = 0;
707 SUP.sock = ftpfs_open_socket (me, super);
708 if (SUP.sock == -1)
709 return -1;
711 if (ftpfs_login_server (me, super, NULL)) {
712 /* Logged in, no need to retry the connection */
713 break;
714 } else {
715 if (SUP.failed_on_login){
716 /* Close only the socket descriptor */
717 close (SUP.sock);
718 } else {
719 return -1;
721 if (ftpfs_retry_seconds){
722 retry_seconds = ftpfs_retry_seconds;
723 enable_interrupt_key ();
724 for (count_down = retry_seconds; count_down; count_down--){
725 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
726 sleep (1);
727 if (got_interrupt ()){
728 /* ftpfs_errno = E; */
729 disable_interrupt_key ();
730 return 0;
733 disable_interrupt_key ();
736 } while (retry_seconds);
738 SUP.cwdir = ftpfs_get_current_directory (me, super);
739 if (!SUP.cwdir)
740 SUP.cwdir = g_strdup (PATH_SEP_STR);
741 return 0;
744 static int
745 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
746 const char *archive_name, char *op)
748 char *host, *user, *password;
749 int port;
751 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
753 SUP.host = host;
754 SUP.user = user;
755 SUP.port = port;
756 SUP.cwdir = NULL;
757 SUP.proxy = 0;
758 if (ftpfs_check_proxy (host))
759 SUP.proxy = ftpfs_proxy_host;
760 SUP.password = password;
761 SUP.use_passive_connection = ftpfs_use_passive_connections;
762 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
763 SUP.isbinary = TYPE_UNKNOWN;
764 SUP.remote_is_amiga = 0;
765 super->name = g_strdup ("/");
766 super->root =
767 vfs_s_new_inode (me, super,
768 vfs_s_default_stat (me, S_IFDIR | 0755));
770 return ftpfs_open_archive_int (me, super);
773 static int
774 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
775 const char *archive_name, char *op, void *cookie)
777 char *host, *user;
778 int port;
780 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
782 port = ((strcmp (host, SUP.host) == 0)
783 && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
785 g_free (host);
786 g_free (user);
788 return port;
791 static int
792 ftpfs_dir_uptodate(struct vfs_class *me, struct vfs_s_inode *ino)
794 struct timeval tim;
796 if (MEDATA->flush) {
797 MEDATA->flush = 0;
798 return 0;
800 gettimeofday(&tim, NULL);
801 if (tim.tv_sec < ino->timestamp.tv_sec)
802 return 1;
803 return 0;
806 /* The returned directory should always contain a trailing slash */
807 static char *
808 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
810 char buf[BUF_8K], *bufp, *bufq;
812 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
813 ftpfs_get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
814 bufp = NULL;
815 for (bufq = buf; *bufq; bufq++)
816 if (*bufq == '"') {
817 if (!bufp) {
818 bufp = bufq + 1;
819 } else {
820 *bufq = 0;
821 if (*bufp) {
822 if (*(bufq - 1) != '/') {
823 *bufq++ = '/';
824 *bufq = 0;
826 if (*bufp == '/')
827 return g_strdup (bufp);
828 else {
829 /* If the remote server is an Amiga a leading slash
830 might be missing. MC needs it because it is used
831 as separator between hostname and path internally. */
832 return g_strconcat( "/", bufp, 0);
834 } else {
835 ftpfs_errno = EIO;
836 return NULL;
841 ftpfs_errno = EIO;
842 return NULL;
846 /* Setup Passive ftp connection, we use it for source routed connections */
847 static int
848 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super, int my_socket, struct sockaddr_in *sa)
850 int xa, xb, xc, xd, xe, xf;
851 char n [6];
852 char *c;
854 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
855 return 0;
857 /* Parse remote parameters */
858 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++)
860 if (!*c)
861 return 0;
862 if (!isdigit ((unsigned char) *c))
863 return 0;
864 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
865 return 0;
866 n [0] = (unsigned char) xa;
867 n [1] = (unsigned char) xb;
868 n [2] = (unsigned char) xc;
869 n [3] = (unsigned char) xd;
870 n [4] = (unsigned char) xe;
871 n [5] = (unsigned char) xf;
873 memcpy (&(sa->sin_addr.s_addr), (void *)n, 4);
874 memcpy (&(sa->sin_port), (void *)&n[4], 2);
875 if (connect (my_socket, (struct sockaddr *) sa, sizeof (struct sockaddr_in)) < 0)
876 return 0;
877 return 1;
880 static int
881 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
883 struct sockaddr_in data_addr;
884 int data;
885 socklen_t len = sizeof(data_addr);
886 struct protoent *pe;
888 pe = getprotobyname ("tcp");
889 if (pe == NULL)
890 ERRNOR (EIO, -1);
891 again:
892 if (getsockname (SUP.sock, (struct sockaddr *) &data_addr, &len) == -1)
893 ERRNOR (EIO, -1);
894 data_addr.sin_port = 0;
896 data = socket (AF_INET, SOCK_STREAM, pe->p_proto);
897 if (data < 0)
898 ERRNOR (EIO, -1);
900 if (SUP.use_passive_connection) {
901 if (ftpfs_setup_passive (me, super, data, &data_addr))
902 return data;
904 SUP.use_passive_connection = 0;
905 print_vfs_message (_("ftpfs: could not setup passive mode"));
907 /* data or data_addr may be damaged by ftpfs_setup_passive */
908 close (data);
909 goto again;
912 /* If passive setup fails, fallback to active connections */
913 /* Active FTP connection */
914 if ((bind (data, (struct sockaddr *)&data_addr, len) == 0) &&
915 (getsockname (data, (struct sockaddr *) &data_addr, &len) == 0) &&
916 (listen (data, 1) == 0))
918 unsigned char *a = (unsigned char *)&data_addr.sin_addr;
919 unsigned char *p = (unsigned char *)&data_addr.sin_port;
921 if (ftpfs_command (me, super, WAIT_REPLY, "PORT %d,%d,%d,%d,%d,%d", a[0], a[1],
922 a[2], a[3], p[0], p[1]) == COMPLETE)
923 return data;
925 close (data);
926 ftpfs_errno = EIO;
927 return -1;
930 static int
931 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
932 const char *remote, int isbinary, int reget)
934 struct sockaddr_in from;
935 int s, j, data;
936 socklen_t fromlen = sizeof(from);
938 if ((s = ftpfs_initconn (me, super)) == -1)
939 return -1;
940 if (ftpfs_changetype (me, super, isbinary) == -1)
941 return -1;
942 if (reget > 0){
943 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
944 if (j != CONTINUE)
945 return -1;
947 if (remote) {
948 char *remote_path = ftpfs_translate_path (me, super, remote);
949 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
950 /* WarFtpD can't STORE //filename */
951 (*remote_path == '/') ? remote_path + 1 : remote_path);
952 g_free (remote_path);
953 } else
954 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
955 if (j != PRELIM)
956 ERRNOR (EPERM, -1);
957 enable_interrupt_key();
958 if (SUP.use_passive_connection)
959 data = s;
960 else {
961 data = accept (s, (struct sockaddr *)&from, &fromlen);
962 if (data < 0) {
963 ftpfs_errno = errno;
964 close (s);
965 return -1;
967 close (s);
969 disable_interrupt_key();
970 return data;
973 #define ABORT_TIMEOUT 5
974 static void
975 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
977 struct vfs_s_super *super = FH_SUPER;
978 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
979 fd_set mask;
980 char buf[1024];
981 int dsock = FH_SOCK;
982 FH_SOCK = -1;
983 SUP.ctl_connection_busy = 0;
985 print_vfs_message (_("ftpfs: aborting transfer."));
986 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
987 print_vfs_message (_("ftpfs: abort error: %s"),
988 unix_error_string (errno));
989 if (dsock != -1)
990 close (dsock);
991 return;
994 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
995 print_vfs_message (_("ftpfs: abort failed"));
996 if (dsock != -1)
997 close (dsock);
998 return;
1000 if (dsock != -1) {
1001 FD_ZERO (&mask);
1002 FD_SET (dsock, &mask);
1003 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1004 struct timeval start_tim, tim;
1005 gettimeofday (&start_tim, NULL);
1006 /* flush the remaining data */
1007 while (read (dsock, buf, sizeof (buf)) > 0) {
1008 gettimeofday (&tim, NULL);
1009 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1010 /* server keeps sending, drop the connection and ftpfs_reconnect */
1011 close (dsock);
1012 ftpfs_reconnect (me, super);
1013 return;
1017 close (dsock);
1019 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1020 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1023 #if 0
1024 static void
1025 resolve_symlink_without_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1027 struct linklist *flist;
1028 struct direntry *fe, *fel;
1029 char tmp[MC_MAXPATHLEN];
1030 int depth;
1032 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1033 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1034 /* flist->data->l_stat is alread initialized with 0 */
1035 fel = flist->data;
1036 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1037 if (fel->linkname[0] == '/') {
1038 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1039 continue;
1040 strcpy (tmp, fel->linkname);
1041 } else {
1042 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1043 continue;
1044 strcpy (tmp, dir->remote_path);
1045 if (tmp[1] != '\0')
1046 strcat (tmp, "/");
1047 strcat (tmp + 1, fel->linkname);
1049 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1050 canonicalize_pathname (tmp);
1051 fe = _get_file_entry(bucket, tmp, 0, 0);
1052 if (fe) {
1053 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1054 /* Symlink points to link which isn't resolved, yet. */
1055 if (fe->linkname[0] == '/') {
1056 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1057 break;
1058 strcpy (tmp, fe->linkname);
1059 } else {
1060 /* at this point tmp looks always like this
1061 /directory/filename, i.e. no need to check
1062 strrchr's return value */
1063 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1064 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1065 break;
1066 strcat (tmp, fe->linkname);
1068 continue;
1069 } else {
1070 fel->l_stat = g_new (struct stat, 1);
1071 if ( S_ISLNK (fe->s.st_mode))
1072 *fel->l_stat = *fe->l_stat;
1073 else
1074 *fel->l_stat = fe->s;
1075 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1078 break;
1082 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1085 static void
1086 resolve_symlink_with_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1088 char buffer[2048] = "", *filename;
1089 int sock;
1090 FILE *fp;
1091 struct stat s;
1092 struct linklist *flist;
1093 struct direntry *fe;
1094 int switch_method = 0;
1096 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1097 if (strchr (dir->remote_path, ' ')) {
1098 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1099 print_vfs_message(_("ftpfs: CWD failed."));
1100 return;
1102 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1104 else
1105 sock = ftpfs_open_data_connection (bucket, "LIST -lLa",
1106 dir->remote_path, TYPE_ASCII, 0);
1108 if (sock == -1) {
1109 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1110 return;
1113 fp = fdopen(sock, "r");
1114 if (fp == NULL) {
1115 close(sock);
1116 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1117 return;
1119 enable_interrupt_key();
1120 flist = dir->file_list->next;
1121 while (1) {
1122 do {
1123 if (flist == dir->file_list)
1124 goto done;
1125 fe = flist->data;
1126 flist = flist->next;
1127 } while (!S_ISLNK(fe->s.st_mode));
1128 while (1) {
1129 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1130 goto done;
1131 if (MEDATA->logfile){
1132 fputs (buffer, MEDATA->logfile);
1133 fflush (MEDATA->logfile);
1135 vfs_die("This code should be commented out\n");
1136 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1137 int r = strcmp(fe->name, filename);
1138 g_free(filename);
1139 if (r == 0) {
1140 if (S_ISLNK (s.st_mode)) {
1141 /* This server doesn't understand LIST -lLa */
1142 switch_method = 1;
1143 goto done;
1145 fe->l_stat = g_new (struct stat, 1);
1146 if (fe->l_stat == NULL)
1147 goto done;
1148 *fe->l_stat = s;
1149 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1150 break;
1152 if (r < 0)
1153 break;
1157 done:
1158 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1159 disable_interrupt_key();
1160 fclose(fp);
1161 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1164 static void
1165 resolve_symlink(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1167 print_vfs_message(_("Resolving symlink..."));
1169 if (SUP.strict_rfc959_list_cmd)
1170 resolve_symlink_without_ls_options(me, super, dir);
1171 else
1172 resolve_symlink_with_ls_options(me, super, dir);
1174 #endif
1176 static int
1177 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1179 struct vfs_s_entry *ent;
1180 struct vfs_s_super *super = dir->super;
1181 int sock, num_entries = 0;
1182 char buffer[BUF_8K];
1183 int cd_first;
1185 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1186 || (strchr (remote_path, ' ') != NULL);
1188 again:
1189 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1190 remote_path,
1191 SUP.strict ==
1192 RFC_STRICT ? _("(strict rfc959)") : "",
1193 cd_first ? _("(chdir first)") : "");
1195 if (cd_first) {
1196 char *p;
1198 p = ftpfs_translate_path (me, super, remote_path);
1200 if (ftpfs_chdir_internal (me, super, p) != COMPLETE) {
1201 g_free (p);
1202 ftpfs_errno = ENOENT;
1203 print_vfs_message (_("ftpfs: CWD failed."));
1204 return -1;
1206 g_free (p);
1209 gettimeofday (&dir->timestamp, NULL);
1210 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1212 if (SUP.strict == RFC_STRICT)
1213 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1214 else if (cd_first)
1215 /* Dirty hack to avoid autoprepending / to . */
1216 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1217 sock =
1218 ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1219 else {
1220 /* Trailing "/." is necessary if remote_path is a symlink */
1221 char *path = concat_dir_and_file (remote_path, ".");
1222 sock =
1223 ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII,
1225 g_free (path);
1228 if (sock == -1)
1229 goto fallback;
1231 /* Clear the interrupt flag */
1232 enable_interrupt_key ();
1234 while (1) {
1235 int i;
1236 int res =
1237 vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1238 sock);
1239 if (!res)
1240 break;
1242 if (res == EINTR) {
1243 me->verrno = ECONNRESET;
1244 close (sock);
1245 disable_interrupt_key ();
1246 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1247 print_vfs_message (_("%s: failure"), me->name);
1248 return -1;
1251 if (MEDATA->logfile) {
1252 fputs (buffer, MEDATA->logfile);
1253 fputs ("\n", MEDATA->logfile);
1254 fflush (MEDATA->logfile);
1257 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1258 i = ent->ino->st.st_nlink;
1259 if (!vfs_parse_ls_lga
1260 (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1261 vfs_s_free_entry (me, ent);
1262 continue;
1264 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1265 num_entries++;
1266 vfs_s_insert_entry (me, dir, ent);
1269 close (sock);
1270 me->verrno = E_REMOTE;
1271 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE) || !num_entries)
1272 goto fallback;
1274 if (SUP.strict == RFC_AUTODETECT)
1275 SUP.strict = RFC_DARING;
1277 print_vfs_message (_("%s: done."), me->name);
1278 return 0;
1280 fallback:
1281 if (SUP.strict == RFC_AUTODETECT) {
1282 /* It's our first attempt to get a directory listing from this
1283 server (UNIX style LIST command) */
1284 SUP.strict = RFC_STRICT;
1285 /* I hate goto, but recursive call needs another 8K on stack */
1286 /* return ftpfs_dir_load (me, dir, remote_path); */
1287 cd_first = 1;
1288 goto again;
1290 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1291 ERRNOR (-1, EACCES);
1294 static int
1295 ftpfs_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *localname)
1297 int h, sock, n;
1298 off_t total;
1299 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1300 struct linger li;
1301 #else
1302 int flag_one = 1;
1303 #endif
1304 char buffer[8192];
1305 struct stat s;
1306 struct vfs_s_super *super = FH_SUPER;
1308 h = open(localname, O_RDONLY);
1309 if (h == -1)
1310 ERRNOR (EIO, -1);
1311 fstat(h, &s);
1312 sock = ftpfs_open_data_connection(me, super, fh->u.ftp.append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1313 if (sock < 0) {
1314 close(h);
1315 return -1;
1317 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1318 li.l_onoff = 1;
1319 li.l_linger = 120;
1320 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));
1321 #else
1322 setsockopt(sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1323 #endif
1324 total = 0;
1326 enable_interrupt_key();
1327 while (1) {
1328 while ((n = read(h, buffer, sizeof(buffer))) < 0) {
1329 if (errno == EINTR) {
1330 if (got_interrupt()) {
1331 ftpfs_errno = EINTR;
1332 goto error_return;
1334 else
1335 continue;
1337 ftpfs_errno = errno;
1338 goto error_return;
1340 if (n == 0)
1341 break;
1342 while (write(sock, buffer, n) < 0) {
1343 if (errno == EINTR) {
1344 if (got_interrupt()) {
1345 ftpfs_errno = EINTR;
1346 goto error_return;
1348 else
1349 continue;
1351 ftpfs_errno = errno;
1352 goto error_return;
1354 total += n;
1355 print_vfs_message(_("ftpfs: storing file %lu (%lu)"),
1356 (unsigned long) total, (unsigned long) s.st_size);
1358 disable_interrupt_key();
1359 close(sock);
1360 close(h);
1361 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1362 ERRNOR (EIO, -1);
1363 return 0;
1364 error_return:
1365 disable_interrupt_key();
1366 close(sock);
1367 close(h);
1368 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1369 return -1;
1372 static int
1373 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1375 char *name = vfs_s_fullpath (me, fh->ino);
1377 if (!name)
1378 return 0;
1379 FH_SOCK = ftpfs_open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1380 g_free (name);
1381 if (FH_SOCK == -1)
1382 ERRNOR (EACCES, 0);
1383 fh->linear = LS_LINEAR_OPEN;
1384 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1385 fh->u.ftp.append = 0;
1386 return 1;
1389 static int
1390 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1392 int n;
1393 struct vfs_s_super *super = FH_SUPER;
1395 while ((n = read (FH_SOCK, buf, len))<0) {
1396 if ((errno == EINTR) && !got_interrupt())
1397 continue;
1398 break;
1401 if (n<0)
1402 ftpfs_linear_abort(me, fh);
1404 if (!n) {
1405 SUP.ctl_connection_busy = 0;
1406 close (FH_SOCK);
1407 FH_SOCK = -1;
1408 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1409 ERRNOR (E_REMOTE, -1);
1410 return 0;
1412 ERRNOR (errno, n);
1415 static void
1416 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1418 if (FH_SOCK != -1)
1419 ftpfs_linear_abort(me, fh);
1422 static int ftpfs_ctl (void *fh, int ctlop, void *arg)
1424 switch (ctlop) {
1425 case VFS_CTL_IS_NOTREADY:
1427 int v;
1429 if (!FH->linear)
1430 vfs_die ("You may not do this");
1431 if (FH->linear == LS_LINEAR_CLOSED)
1432 return 0;
1434 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1435 if (((v < 0) && (errno == EINTR)) || v == 0)
1436 return 1;
1437 return 0;
1439 default:
1440 return 0;
1444 static int
1445 ftpfs_send_command(struct vfs_class *me, const char *filename, const char *cmd, int flags)
1447 char *rpath, *p, *mpath = g_strdup(filename);
1448 struct vfs_s_super *super;
1449 int r;
1450 int flush_directory_cache = (flags & OPT_FLUSH);
1452 if (!(rpath = vfs_s_get_path_mangle(me, mpath, &super, 0))) {
1453 g_free(mpath);
1454 return -1;
1456 p = ftpfs_translate_path (me, super, rpath);
1457 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1458 g_free (p);
1459 vfs_stamp_create (&vfs_ftpfs_ops, super);
1460 if (flags & OPT_IGNORE_ERROR)
1461 r = COMPLETE;
1462 if (r != COMPLETE) {
1463 me->verrno = EPERM;
1464 g_free (mpath);
1465 return -1;
1467 if (flush_directory_cache)
1468 vfs_s_invalidate(me, super);
1469 g_free(mpath);
1470 return 0;
1473 /* This routine is called as the last step in load_setup */
1474 void
1475 ftpfs_init_passwd(void)
1477 ftpfs_anonymous_passwd = load_anon_passwd ();
1478 if (ftpfs_anonymous_passwd)
1479 return;
1481 /* If there is no anonymous ftp password specified
1482 * then we'll just use anonymous@
1483 * We don't send any other thing because:
1484 * - We want to remain anonymous
1485 * - We want to stop SPAM
1486 * - We don't want to let ftp sites to discriminate by the user,
1487 * host or country.
1489 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1492 static int ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1494 char buf[BUF_SMALL];
1496 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1497 return ftpfs_send_command(me, path, buf, OPT_FLUSH);
1500 static int ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1502 #if 0
1503 ftpfs_errno = EPERM;
1504 return -1;
1505 #else
1506 /* Everyone knows it is not possible to chown remotely, so why bother them.
1507 If someone's root, then copy/move will always try to chown it... */
1508 return 0;
1509 #endif
1512 static int ftpfs_unlink (struct vfs_class *me, const char *path)
1514 return ftpfs_send_command(me, path, "DELE /%s", OPT_FLUSH);
1517 /* Return 1 if path is the same directory as the one we are in now */
1518 static int
1519 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1521 if (!SUP.cwdir)
1522 return 0;
1523 if (strcmp (path, SUP.cwdir) == 0)
1524 return 1;
1525 return 0;
1528 static int
1529 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1531 int r;
1532 char *p;
1534 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1535 return COMPLETE;
1537 p = ftpfs_translate_path (me, super, remote_path);
1538 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1539 g_free (p);
1541 if (r != COMPLETE) {
1542 ftpfs_errno = EIO;
1543 } else {
1544 g_free(SUP.cwdir);
1545 SUP.cwdir = g_strdup (remote_path);
1546 SUP.cwd_deferred = 0;
1548 return r;
1551 static int ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1553 ftpfs_send_command(me, path1, "RNFR /%s", OPT_FLUSH);
1554 return ftpfs_send_command(me, path2, "RNTO /%s", OPT_FLUSH);
1557 static int ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1559 return ftpfs_send_command(me, path, "MKD /%s", OPT_FLUSH);
1562 static int ftpfs_rmdir (struct vfs_class *me, const char *path)
1564 return ftpfs_send_command(me, path, "RMD /%s", OPT_FLUSH);
1567 static int
1568 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
1569 int mode)
1571 fh->u.ftp.append = 0;
1572 /* File will be written only, so no need to retrieve it from ftp server */
1573 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
1574 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1575 struct linger li;
1576 #else
1577 int li = 1;
1578 #endif
1579 char *name;
1581 /* ftpfs_linear_start() called, so data will be written
1582 * to local temporary file and stored to ftp server
1583 * by vfs_s_close later
1585 if (FH_SUPER->u.ftp.ctl_connection_busy) {
1586 if (!fh->ino->localname) {
1587 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1588 fh->ino->ent->name);
1589 if (handle == -1)
1590 return -1;
1591 close (handle);
1592 fh->u.ftp.append = flags & O_APPEND;
1594 return 0;
1596 name = vfs_s_fullpath (me, fh->ino);
1597 if (!name)
1598 return -1;
1599 fh->handle =
1600 ftpfs_open_data_connection (me, fh->ino->super,
1601 (flags & O_APPEND) ? "APPE" :
1602 "STOR", name, TYPE_BINARY, 0);
1603 g_free (name);
1605 if (fh->handle < 0)
1606 return -1;
1607 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1608 li.l_onoff = 1;
1609 li.l_linger = 120;
1610 #endif
1611 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1613 if (fh->ino->localname) {
1614 unlink (fh->ino->localname);
1615 g_free (fh->ino->localname);
1616 fh->ino->localname = NULL;
1618 return 0;
1621 if (!fh->ino->localname)
1622 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1623 return -1;
1624 if (!fh->ino->localname)
1625 vfs_die ("retrieve_file failed to fill in localname");
1626 return 0;
1629 static int ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1631 if (fh->handle != -1 && !fh->ino->localname){
1632 close (fh->handle);
1633 fh->handle = -1;
1634 /* File is stored to destination already, so
1635 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1637 fh->changed = 0;
1638 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1639 ERRNOR (EIO, -1);
1640 vfs_s_invalidate (me, FH_SUPER);
1642 return 0;
1645 static void
1646 ftpfs_done (struct vfs_class *me)
1648 struct no_proxy_entry *np;
1650 while (no_proxy) {
1651 np = no_proxy->next;
1652 g_free (no_proxy->domain);
1653 g_free (no_proxy);
1654 no_proxy = np;
1656 g_free (ftpfs_anonymous_passwd);
1657 g_free (ftpfs_proxy_host);
1660 static void
1661 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
1663 struct vfs_s_super *super = MEDATA->supers;
1664 char *name;
1666 while (super){
1667 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
1668 (*func)(name);
1669 g_free (name);
1670 super = super->next;
1674 static char buffer[BUF_MEDIUM];
1675 static char *netrc;
1676 static const char *netrcp;
1678 /* This should match the keywords[] array below */
1679 typedef enum {
1680 NETRC_NONE = 0,
1681 NETRC_DEFAULT,
1682 NETRC_MACHINE,
1683 NETRC_LOGIN,
1684 NETRC_PASSWORD,
1685 NETRC_PASSWD,
1686 NETRC_ACCOUNT,
1687 NETRC_MACDEF,
1688 NETRC_UNKNOWN
1689 } keyword_t;
1691 static keyword_t ftpfs_netrc_next (void)
1693 char *p;
1694 keyword_t i;
1695 static const char *const keywords[] = { "default", "machine",
1696 "login", "password", "passwd", "account", "macdef", NULL
1700 while (1) {
1701 netrcp = skip_separators (netrcp);
1702 if (*netrcp != '\n')
1703 break;
1704 netrcp++;
1706 if (!*netrcp)
1707 return NETRC_NONE;
1708 p = buffer;
1709 if (*netrcp == '"') {
1710 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1711 if (*netrcp == '\\')
1712 netrcp++;
1713 *p++ = *netrcp;
1715 } else {
1716 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1717 *netrcp != ',' && *netrcp; netrcp++) {
1718 if (*netrcp == '\\')
1719 netrcp++;
1720 *p++ = *netrcp;
1723 *p = 0;
1724 if (!*buffer)
1725 return 0;
1727 i = NETRC_DEFAULT;
1728 while (keywords[i - 1]) {
1729 if (!strcmp (keywords[i - 1], buffer))
1730 return i;
1732 i++;
1735 return NETRC_UNKNOWN;
1738 static int ftpfs_netrc_bad_mode (const char *netrcname, const char *arg_netrc)
1740 static int be_angry = 1;
1741 struct stat mystat;
1743 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1744 if (be_angry) {
1745 message (1, MSG_ERROR,
1746 _("~/.netrc file has incorrect mode.\n"
1747 "Remove password or correct mode."));
1748 be_angry = 0;
1750 return 1;
1752 return 0;
1755 /* Scan .netrc until we find matching "machine" or "default"
1756 * domain is used for additional matching
1757 * No search is done after "default" in compliance with "man netrc"
1758 * Return 0 if found, -1 otherwise */
1759 static int ftpfs_find_machine (const char *host, const char *domain)
1761 keyword_t keyword;
1763 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) {
1764 if (keyword == NETRC_DEFAULT)
1765 return 0;
1767 if (keyword == NETRC_MACDEF) {
1768 /* Scan for an empty line, which concludes "macdef" */
1769 do {
1770 while (*netrcp && *netrcp != '\n')
1771 netrcp++;
1772 if (*netrcp != '\n')
1773 break;
1774 netrcp++;
1775 } while (*netrcp && *netrcp != '\n');
1776 continue;
1779 if (keyword != NETRC_MACHINE)
1780 continue;
1782 /* Take machine name */
1783 if (ftpfs_netrc_next () == NETRC_NONE)
1784 break;
1786 if (g_strcasecmp (host, buffer)) {
1787 /* Try adding our domain to short names in .netrc */
1788 const char *host_domain = strchr (host, '.');
1789 if (!host_domain)
1790 continue;
1792 /* Compare domain part */
1793 if (g_strcasecmp (host_domain, domain))
1794 continue;
1796 /* Compare local part */
1797 if (g_strncasecmp (host, buffer, host_domain - host))
1798 continue;
1801 return 0;
1804 /* end of .netrc */
1805 return -1;
1808 /* Extract login and password from .netrc for the host.
1809 * pass may be NULL.
1810 * Returns 0 for success, -1 for error */
1811 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass)
1813 char *netrcname;
1814 char *tmp_pass = NULL;
1815 char hostname[MAXHOSTNAMELEN];
1816 const char *domain;
1817 keyword_t keyword;
1818 static struct rupcache {
1819 struct rupcache *next;
1820 char *host;
1821 char *login;
1822 char *pass;
1823 } *rup_cache = NULL, *rupp;
1825 /* Initialize *login and *pass */
1826 if (!login)
1827 return 0;
1828 *login = NULL;
1829 if (pass)
1830 *pass = NULL;
1832 /* Look up in the cache first */
1833 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
1834 if (!strcmp (host, rupp->host)) {
1835 if (rupp->login)
1836 *login = g_strdup (rupp->login);
1837 if (pass && rupp->pass)
1838 *pass = g_strdup (rupp->pass);
1839 return 0;
1843 /* Load current .netrc */
1844 netrcname = concat_dir_and_file (home_dir, ".netrc");
1845 netrcp = netrc = load_file (netrcname);
1846 if (netrc == NULL) {
1847 g_free (netrcname);
1848 return 0;
1851 /* Find our own domain name */
1852 if (gethostname (hostname, sizeof (hostname)) < 0)
1853 *hostname = 0;
1854 if (!(domain = strchr (hostname, '.')))
1855 domain = "";
1857 /* Scan for "default" and matching "machine" keywords */
1858 ftpfs_find_machine (host, domain);
1860 /* Scan for keywords following "default" and "machine" */
1861 while (1) {
1862 int need_break = 0;
1863 keyword = ftpfs_netrc_next ();
1865 switch (keyword) {
1866 case NETRC_LOGIN:
1867 if (ftpfs_netrc_next () == NETRC_NONE) {
1868 need_break = 1;
1869 break;
1872 /* We have another name already - should not happen */
1873 if (*login) {
1874 need_break = 1;
1875 break;
1878 /* We have login name now */
1879 *login = g_strdup (buffer);
1880 break;
1882 case NETRC_PASSWORD:
1883 case NETRC_PASSWD:
1884 if (ftpfs_netrc_next () == NETRC_NONE) {
1885 need_break = 1;
1886 break;
1889 /* Ignore unsafe passwords */
1890 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
1891 && ftpfs_netrc_bad_mode (netrcname, netrc)) {
1892 need_break = 1;
1893 break;
1896 /* Remember password. pass may be NULL, so use tmp_pass */
1897 if (tmp_pass == NULL)
1898 tmp_pass = g_strdup (buffer);
1899 break;
1901 case NETRC_ACCOUNT:
1902 /* "account" is followed by a token which we ignore */
1903 if (ftpfs_netrc_next () == NETRC_NONE) {
1904 need_break = 1;
1905 break;
1908 /* Ignore account, but warn user anyways */
1909 ftpfs_netrc_bad_mode (netrcname, netrc);
1910 break;
1912 default:
1913 /* Unexpected keyword or end of file */
1914 need_break = 1;
1915 break;
1918 if (need_break)
1919 break;
1922 g_free (netrc);
1923 g_free (netrcname);
1925 rupp = g_new (struct rupcache, 1);
1926 rupp->host = g_strdup (host);
1927 rupp->login = rupp->pass = 0;
1929 if (*login != NULL) {
1930 rupp->login = g_strdup (*login);
1932 if (tmp_pass != NULL)
1933 rupp->pass = g_strdup (tmp_pass);
1934 rupp->next = rup_cache;
1935 rup_cache = rupp;
1937 if (pass)
1938 *pass = tmp_pass;
1940 return 0;
1943 void
1944 init_ftpfs (void)
1946 static struct vfs_s_subclass ftpfs_subclass;
1948 ftpfs_subclass.flags = VFS_S_REMOTE;
1949 ftpfs_subclass.archive_same = ftpfs_archive_same;
1950 ftpfs_subclass.open_archive = ftpfs_open_archive;
1951 ftpfs_subclass.free_archive = ftpfs_free_archive;
1952 ftpfs_subclass.fh_open = ftpfs_fh_open;
1953 ftpfs_subclass.fh_close = ftpfs_fh_close;
1954 ftpfs_subclass.dir_load = ftpfs_dir_load;
1955 ftpfs_subclass.dir_uptodate = ftpfs_dir_uptodate;
1956 ftpfs_subclass.file_store = ftpfs_file_store;
1957 ftpfs_subclass.linear_start = ftpfs_linear_start;
1958 ftpfs_subclass.linear_read = ftpfs_linear_read;
1959 ftpfs_subclass.linear_close = ftpfs_linear_close;
1961 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
1962 vfs_ftpfs_ops.name = "ftpfs";
1963 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
1964 vfs_ftpfs_ops.prefix = "ftp:";
1965 vfs_ftpfs_ops.done = &ftpfs_done;
1966 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
1967 vfs_ftpfs_ops.chmod = ftpfs_chmod;
1968 vfs_ftpfs_ops.chown = ftpfs_chown;
1969 vfs_ftpfs_ops.unlink = ftpfs_unlink;
1970 vfs_ftpfs_ops.rename = ftpfs_rename;
1971 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
1972 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
1973 vfs_ftpfs_ops.ctl = ftpfs_ctl;
1974 vfs_register_class (&vfs_ftpfs_ops);