Reverted the use of bool in favour of gboolean
[midnight-commander.git] / vfs / ftpfs.c
blob796e7dfa61d8e959a8c9a650e037ef41bc1d1c7c
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 = mhl_str_dir_plus_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 <mhl/memory.h>
73 #include <mhl/string.h>
75 #include "../src/global.h"
76 #include "../src/tty.h" /* enable/disable interrupt key */
77 #include "../src/wtools.h" /* message() */
78 #include "../src/main.h" /* print_vfs_message */
79 #include "../src/history.h"
80 #include "utilvfs.h"
81 #include "xdirentry.h"
82 #include "vfs.h"
83 #include "vfs-impl.h"
84 #include "gc.h" /* vfs_stamp_create */
85 #include "tcputil.h"
86 #include "../src/setup.h" /* for load_anon_passwd */
87 #include "ftpfs.h"
88 #ifndef MAXHOSTNAMELEN
89 # define MAXHOSTNAMELEN 64
90 #endif
92 #define UPLOAD_ZERO_LENGTH_FILE
93 #define SUP super->u.ftp
94 #define FH_SOCK fh->u.ftp.sock
96 #ifndef INADDR_NONE
97 #define INADDR_NONE 0xffffffff
98 #endif
100 #define RFC_AUTODETECT 0
101 #define RFC_DARING 1
102 #define RFC_STRICT 2
104 #ifndef HAVE_SOCKLEN_T
105 typedef int socklen_t;
106 #endif
108 static int ftpfs_errno;
109 static int code;
111 /* Delay to retry a connection */
112 int ftpfs_retry_seconds = 30;
114 /* Method to use to connect to ftp sites */
115 int ftpfs_use_passive_connections = 1;
116 int ftpfs_use_passive_connections_over_proxy = 0;
118 /* Method used to get directory listings:
119 * 1: try 'LIST -la <path>', if it fails
120 * fall back to CWD <path>; LIST
121 * 0: always use CWD <path>; LIST
123 int ftpfs_use_unix_list_options = 1;
125 /* First "CWD <path>", then "LIST -la ." */
126 int ftpfs_first_cd_then_ls = 1;
128 /* Use the ~/.netrc */
129 int use_netrc = 1;
131 /* Anonymous setup */
132 char *ftpfs_anonymous_passwd = NULL;
133 int ftpfs_directory_timeout = 900;
135 /* Proxy host */
136 char *ftpfs_proxy_host = NULL;
138 /* wether we have to use proxy by default? */
139 int ftpfs_always_use_proxy;
141 #ifdef FIXME_LATER_ALIGATOR
142 static struct linklist *connections_list;
143 #endif
145 /* ftpfs_command wait_flag: */
146 #define NONE 0x00
147 #define WAIT_REPLY 0x01
148 #define WANT_STRING 0x02
149 static char reply_str [80];
151 static struct vfs_class vfs_ftpfs_ops;
153 /* char *ftpfs_translate_path (struct ftpfs_connection *bucket, char *remote_path)
154 Translate a Unix path, i.e. MC's internal path representation (e.g.
155 /somedir/somefile) to a path valid for the remote server. Every path
156 transfered to the remote server has to be mangled by this function
157 right prior to sending it.
158 Currently only Amiga ftp servers are handled in a special manner.
160 When the remote server is an amiga:
161 a) strip leading slash if necesarry
162 b) replace first occurance of ":/" with ":"
163 c) strip trailing "/."
166 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
167 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path);
168 static int ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
169 __attribute__ ((format (__printf__, 4, 5)));
170 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
171 static int ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass);
172 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass);
174 static char *
175 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
177 if (!SUP.remote_is_amiga)
178 return g_strdup (remote_path);
179 else {
180 char *ret, *p;
182 if (MEDATA->logfile) {
183 fprintf (MEDATA->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
184 fflush (MEDATA->logfile);
187 /* strip leading slash(es) */
188 while (*remote_path == '/')
189 remote_path++;
192 * Don't change "/" into "", e.g. "CWD " would be
193 * invalid.
195 if (*remote_path == '\0')
196 return g_strdup (".");
198 ret = g_strdup (remote_path);
200 /* replace first occurance of ":/" with ":" */
201 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
202 strcpy (p + 1, p + 2);
204 /* strip trailing "/." */
205 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
206 *p = '\0';
207 return ret;
211 /* Extract the hostname and username from the path */
214 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
215 * ftp://sunsite.unc.edu/pub/linux
216 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
217 * ftp://tsx-11.mit.edu:8192/
218 * ftp://joe@foo.edu:11321/private
219 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
220 * is supplied.
224 #define FTP_COMMAND_PORT 21
226 static void
227 ftpfs_split_url(char *path, char **host, char **user, int *port, char **pass)
229 char *p;
231 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
232 URL_ALLOW_ANON);
234 if (!*user) {
235 /* Look up user and password in netrc */
236 if (use_netrc)
237 ftpfs_netrc_lookup (*host, user, pass);
238 if (!*user)
239 *user = g_strdup ("anonymous");
242 /* Look up password in netrc for known user */
243 if (use_netrc && *user && pass && !*pass) {
244 char *new_user;
246 ftpfs_netrc_lookup (*host, &new_user, pass);
248 /* If user is different, remove password */
249 if (new_user && strcmp (*user, new_user)) {
250 g_free (*pass);
251 *pass = NULL;
254 g_free (new_user);
257 g_free (p);
260 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
261 static int
262 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
264 char answer[BUF_1K];
265 int i;
267 for (;;) {
268 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
269 if (string_buf)
270 *string_buf = 0;
271 code = 421;
272 return 4;
274 switch (sscanf(answer, "%d", &code)){
275 case 0:
276 if (string_buf)
277 g_strlcpy (string_buf, answer, string_len);
278 code = 500;
279 return 5;
280 case 1:
281 if (answer[3] == '-') {
282 while (1) {
283 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
284 if (string_buf)
285 *string_buf = 0;
286 code = 421;
287 return 4;
289 if ((sscanf (answer, "%d", &i) > 0) &&
290 (code == i) && (answer[3] == ' '))
291 break;
294 if (string_buf)
295 g_strlcpy (string_buf, answer, string_len);
296 return code / 100;
301 static int
302 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
304 int sock = ftpfs_open_socket (me, super);
305 if (sock != -1){
306 char *cwdir = SUP.cwdir;
307 close (SUP.sock);
308 SUP.sock = sock;
309 SUP.cwdir = NULL;
310 if (ftpfs_login_server (me, super, SUP.password)){
311 if (!cwdir)
312 return 1;
313 sock = ftpfs_chdir_internal (me, super, cwdir);
314 g_free (cwdir);
315 return sock == COMPLETE;
317 SUP.cwdir = cwdir;
319 return 0;
322 static int
323 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
325 va_list ap;
326 char *cmdstr;
327 int status, cmdlen;
328 static int retry = 0;
329 static int level = 0; /* ftpfs_login_server() use ftpfs_command() */
331 va_start (ap, fmt);
332 cmdstr = g_strdup_vprintf (fmt, ap);
333 va_end (ap);
335 cmdlen = strlen (cmdstr);
336 cmdstr = g_realloc (cmdstr, cmdlen + 3);
337 strcpy (cmdstr + cmdlen, "\r\n");
338 cmdlen += 2;
340 if (MEDATA->logfile) {
341 if (strncmp (cmdstr, "PASS ", 5) == 0) {
342 fputs ("PASS <Password not logged>\r\n", MEDATA->logfile);
343 } else
344 fwrite (cmdstr, cmdlen, 1, MEDATA->logfile);
346 fflush (MEDATA->logfile);
349 got_sigpipe = 0;
350 enable_interrupt_key ();
351 status = write (SUP.sock, cmdstr, cmdlen);
353 if (status < 0) {
354 code = 421;
356 if (errno == EPIPE) { /* Remote server has closed connection */
357 if (level == 0) {
358 level = 1;
359 status = ftpfs_reconnect (me, super);
360 level = 0;
361 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
362 goto ok;
366 got_sigpipe = 1;
368 g_free (cmdstr);
369 disable_interrupt_key ();
370 return TRANSIENT;
372 retry = 0;
374 disable_interrupt_key ();
376 if (wait_reply)
378 status = ftpfs_get_reply (me, SUP.sock,
379 (wait_reply & WANT_STRING) ? reply_str : NULL,
380 sizeof (reply_str) - 1);
381 if ((wait_reply & WANT_STRING) && !retry && !level && code == 421)
383 retry = 1;
384 level = 1;
385 status = ftpfs_reconnect (me, super);
386 level = 0;
387 if (status && (write (SUP.sock, cmdstr, cmdlen) > 0)) {
388 goto ok;
391 retry = 0;
392 g_free (cmdstr);
393 return status;
395 g_free (cmdstr);
396 return COMPLETE;
399 static void
400 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
402 if (SUP.sock != -1){
403 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
404 ftpfs_command(me, super, NONE, "QUIT");
405 close(SUP.sock);
407 g_free (SUP.host);
408 g_free (SUP.user);
409 g_free (SUP.cwdir);
410 g_free (SUP.password);
413 /* some defines only used by ftpfs_changetype */
414 /* These two are valid values for the second parameter */
415 #define TYPE_ASCII 0
416 #define TYPE_BINARY 1
418 /* This one is only used to initialize bucket->isbinary, don't use it as
419 second parameter to ftpfs_changetype. */
420 #define TYPE_UNKNOWN -1
422 static int
423 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
425 if (binary != SUP.isbinary) {
426 if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
427 ERRNOR (EIO, -1);
428 SUP.isbinary = binary;
430 return binary;
433 /* This routine logs the user in */
434 static int
435 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
436 const char *netrcpass)
438 char *pass;
439 char *op;
440 char *name; /* login user name */
441 int anon = 0;
442 char reply_string[BUF_MEDIUM];
444 SUP.isbinary = TYPE_UNKNOWN;
446 if (SUP.password) /* explicit password */
447 op = g_strdup (SUP.password);
448 else if (netrcpass) /* password from netrc */
449 op = g_strdup (netrcpass);
450 else if (!strcmp (SUP.user, "anonymous") || !strcmp (SUP.user, "ftp")) {
451 if (!ftpfs_anonymous_passwd) /* default anonymous password */
452 ftpfs_init_passwd ();
453 op = g_strdup (ftpfs_anonymous_passwd);
454 anon = 1;
455 } else { /* ask user */
456 char *p;
458 p = g_strconcat (_(" FTP: Password required for "), SUP.user, " ",
459 NULL);
460 op = vfs_get_password (p);
461 g_free (p);
462 if (op == NULL)
463 ERRNOR (EPERM, 0);
464 SUP.password = g_strdup (op);
467 if (!anon || MEDATA->logfile)
468 pass = op;
469 else {
470 pass = g_strconcat ("-", op, (char *) NULL);
471 wipe_password (op);
474 /* Proxy server accepts: username@host-we-want-to-connect */
475 if (SUP.proxy) {
476 name =
477 g_strconcat (SUP.user, "@",
478 SUP.host[0] == '!' ? SUP.host + 1 : SUP.host,
479 NULL);
480 } else
481 name = g_strdup (SUP.user);
483 if (ftpfs_get_reply
484 (me, SUP.sock, reply_string,
485 sizeof (reply_string) - 1) == COMPLETE) {
486 g_strup (reply_string);
487 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
488 if (MEDATA->logfile) {
489 fprintf (MEDATA->logfile, "MC -- remote_is_amiga = %d\n",
490 SUP.remote_is_amiga);
491 fflush (MEDATA->logfile);
494 print_vfs_message (_("ftpfs: sending login name"));
496 switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name)) {
497 case CONTINUE:
498 print_vfs_message (_("ftpfs: sending user password"));
499 code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
500 if (code == CONTINUE) {
501 char *p;
503 p = g_strdup_printf (_
504 ("FTP: Account required for user %s"),
505 SUP.user);
506 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "");
507 g_free (p);
508 if (op == NULL)
509 ERRNOR (EPERM, 0);
510 print_vfs_message (_("ftpfs: sending user account"));
511 code =
512 ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
513 g_free (op);
515 if (code != COMPLETE)
516 break;
517 /* fall through */
519 case COMPLETE:
520 print_vfs_message (_("ftpfs: logged in"));
521 wipe_password (pass);
522 g_free (name);
523 return 1;
525 default:
526 SUP.failed_on_login = 1;
527 if (SUP.password)
528 wipe_password (SUP.password);
529 SUP.password = 0;
531 goto login_fail;
534 message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
535 SUP.user);
536 login_fail:
537 wipe_password (pass);
538 g_free (name);
539 ERRNOR (EPERM, 0);
542 static struct no_proxy_entry {
543 char *domain;
544 void *next;
545 } *no_proxy;
547 static void
548 ftpfs_load_no_proxy_list (void)
550 /* FixMe: shouldn't be hardcoded!!! */
551 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
552 struct no_proxy_entry *np, *current = 0;
553 FILE *npf;
554 int c;
555 char *p;
556 static char *mc_file;
558 if (mc_file)
559 return;
561 mc_file = mhl_str_dir_plus_file (mc_home, "mc.no_proxy");
562 if (exist_file (mc_file) &&
563 (npf = fopen (mc_file, "r"))) {
564 while (fgets (s, sizeof (s), npf)) {
565 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
566 while ((c = fgetc (npf)) != EOF && c != '\n')
568 continue;
571 if (p == s)
572 continue;
574 *p = '\0';
576 np = g_new (struct no_proxy_entry, 1);
577 np->domain = g_strdup (s);
578 np->next = NULL;
579 if (no_proxy)
580 current->next = np;
581 else
582 no_proxy = np;
583 current = np;
586 fclose (npf);
588 g_free (mc_file);
591 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
592 static int
593 ftpfs_check_proxy (const char *host)
595 struct no_proxy_entry *npe;
597 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
598 return 0; /* sanity check */
600 if (*host == '!')
601 return 1;
603 if (!ftpfs_always_use_proxy)
604 return 0;
606 if (!strchr (host, '.'))
607 return 0;
609 ftpfs_load_no_proxy_list ();
610 for (npe = no_proxy; npe; npe=npe->next) {
611 char *domain = npe->domain;
613 if (domain[0] == '.') {
614 int ld = strlen (domain);
615 int lh = strlen (host);
617 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
618 ld--;
619 lh--;
622 if (!ld)
623 return 0;
624 } else
625 if (!g_strcasecmp (host, domain))
626 return 0;
629 return 1;
632 static void
633 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
635 char *user, *dir;
637 dir =
638 vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT,
639 URL_ALLOW_ANON);
640 g_free (user);
641 g_free (dir);
644 static int
645 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
647 struct sockaddr_in server_address;
648 struct hostent *hp;
649 int my_socket;
650 char *host;
651 int port = SUP.port;
652 int free_host = 0;
654 (void) me;
656 /* Use a proxy host? */
657 host = SUP.host;
659 if (!host || !*host){
660 print_vfs_message (_("ftpfs: Invalid host name."));
661 ftpfs_errno = EINVAL;
662 return -1;
665 /* Hosts to connect to that start with a ! should use proxy */
666 if (SUP.proxy){
667 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &port);
668 free_host = 1;
671 enable_interrupt_key(); /* clear the interrupt flag */
673 /* Get host address */
674 memset ((char *) &server_address, 0, sizeof (server_address));
675 server_address.sin_family = AF_INET;
676 server_address.sin_addr.s_addr = inet_addr (host);
677 if (server_address.sin_addr.s_addr == INADDR_NONE) {
678 hp = gethostbyname (host);
679 if (hp == NULL){
680 disable_interrupt_key();
681 print_vfs_message (_("ftpfs: Invalid host address."));
682 ftpfs_errno = EINVAL;
683 if (free_host)
684 g_free (host);
685 return -1;
687 server_address.sin_family = hp->h_addrtype;
689 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
690 memcpy ((char *) &server_address.sin_addr, (char *) hp->h_addr, 4);
693 server_address.sin_port = htons (port);
695 /* Connect */
696 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
697 disable_interrupt_key();
698 ftpfs_errno = errno;
699 if (free_host)
700 g_free (host);
701 return -1;
704 print_vfs_message (_("ftpfs: making connection to %s"), host);
705 if (free_host)
706 g_free (host);
708 if (connect (my_socket, (struct sockaddr *) &server_address,
709 sizeof (server_address)) < 0){
710 ftpfs_errno = errno;
711 if (errno == EINTR && got_interrupt ())
712 print_vfs_message (_("ftpfs: connection interrupted by user"));
713 else
714 print_vfs_message (_("ftpfs: connection to server failed: %s"),
715 unix_error_string(errno));
716 disable_interrupt_key();
717 close (my_socket);
718 return -1;
720 disable_interrupt_key();
721 return my_socket;
724 static int
725 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
727 int retry_seconds, count_down;
729 /* We do not want to use the passive if we are using proxies */
730 if (SUP.proxy)
731 SUP.use_passive_connection = ftpfs_use_passive_connections_over_proxy;
733 retry_seconds = 0;
734 do {
735 SUP.failed_on_login = 0;
737 SUP.sock = ftpfs_open_socket (me, super);
738 if (SUP.sock == -1)
739 return -1;
741 if (ftpfs_login_server (me, super, NULL)) {
742 /* Logged in, no need to retry the connection */
743 break;
744 } else {
745 if (SUP.failed_on_login){
746 /* Close only the socket descriptor */
747 close (SUP.sock);
748 } else {
749 return -1;
751 if (ftpfs_retry_seconds){
752 retry_seconds = ftpfs_retry_seconds;
753 enable_interrupt_key ();
754 for (count_down = retry_seconds; count_down; count_down--){
755 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
756 sleep (1);
757 if (got_interrupt ()){
758 /* ftpfs_errno = E; */
759 disable_interrupt_key ();
760 return 0;
763 disable_interrupt_key ();
766 } while (retry_seconds);
768 SUP.cwdir = ftpfs_get_current_directory (me, super);
769 if (!SUP.cwdir)
770 SUP.cwdir = g_strdup (PATH_SEP_STR);
771 return 0;
774 static int
775 ftpfs_open_archive (struct vfs_class *me, struct vfs_s_super *super,
776 const char *archive_name, char *op)
778 char *host, *user, *password;
779 int port;
781 (void) archive_name;
783 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
785 SUP.host = host;
786 SUP.user = user;
787 SUP.port = port;
788 SUP.cwdir = NULL;
789 SUP.proxy = 0;
790 if (ftpfs_check_proxy (host))
791 SUP.proxy = ftpfs_proxy_host;
792 SUP.password = password;
793 SUP.use_passive_connection = ftpfs_use_passive_connections;
794 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
795 SUP.isbinary = TYPE_UNKNOWN;
796 SUP.remote_is_amiga = 0;
797 super->name = g_strdup ("/");
798 super->root =
799 vfs_s_new_inode (me, super,
800 vfs_s_default_stat (me, S_IFDIR | 0755));
802 return ftpfs_open_archive_int (me, super);
805 static int
806 ftpfs_archive_same (struct vfs_class *me, struct vfs_s_super *super,
807 const char *archive_name, char *op, void *cookie)
809 char *host, *user;
810 int port;
812 (void) me;
813 (void) archive_name;
814 (void) cookie;
816 ftpfs_split_url (strchr (op, ':') + 1, &host, &user, &port, 0);
818 port = ((strcmp (host, SUP.host) == 0)
819 && (strcmp (user, SUP.user) == 0) && (port == SUP.port));
821 g_free (host);
822 g_free (user);
824 return port;
827 /* The returned directory should always contain a trailing slash */
828 static char *
829 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
831 char buf[BUF_8K], *bufp, *bufq;
833 if (ftpfs_command (me, super, NONE, "PWD") == COMPLETE &&
834 ftpfs_get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
835 bufp = NULL;
836 for (bufq = buf; *bufq; bufq++)
837 if (*bufq == '"') {
838 if (!bufp) {
839 bufp = bufq + 1;
840 } else {
841 *bufq = 0;
842 if (*bufp) {
843 if (*(bufq - 1) != '/') {
844 *bufq++ = '/';
845 *bufq = 0;
847 if (*bufp == '/')
848 return g_strdup (bufp);
849 else {
850 /* If the remote server is an Amiga a leading slash
851 might be missing. MC needs it because it is used
852 as separator between hostname and path internally. */
853 return g_strconcat( "/", bufp, NULL);
855 } else {
856 ftpfs_errno = EIO;
857 return NULL;
862 ftpfs_errno = EIO;
863 return NULL;
867 /* Setup Passive ftp connection, we use it for source routed connections */
868 static int
869 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super, int my_socket, struct sockaddr_in *sa)
871 int xa, xb, xc, xd, xe, xf;
872 char n [6];
873 char *c;
875 if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
876 return 0;
878 /* Parse remote parameters */
879 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++)
881 if (!*c)
882 return 0;
883 if (!isdigit ((unsigned char) *c))
884 return 0;
885 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
886 return 0;
887 n [0] = (unsigned char) xa;
888 n [1] = (unsigned char) xb;
889 n [2] = (unsigned char) xc;
890 n [3] = (unsigned char) xd;
891 n [4] = (unsigned char) xe;
892 n [5] = (unsigned char) xf;
894 memcpy (&(sa->sin_addr.s_addr), (void *)n, 4);
895 memcpy (&(sa->sin_port), (void *)&n[4], 2);
896 if (connect (my_socket, (struct sockaddr *) sa, sizeof (struct sockaddr_in)) < 0)
897 return 0;
898 return 1;
901 static int
902 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
904 struct sockaddr_in data_addr;
905 int data;
906 socklen_t len = sizeof(data_addr);
907 struct protoent *pe;
909 pe = getprotobyname ("tcp");
910 if (pe == NULL)
911 ERRNOR (EIO, -1);
912 again:
913 if (getsockname (SUP.sock, (struct sockaddr *) &data_addr, &len) == -1)
914 ERRNOR (EIO, -1);
915 data_addr.sin_port = 0;
917 data = socket (AF_INET, SOCK_STREAM, pe->p_proto);
918 if (data < 0)
919 ERRNOR (EIO, -1);
921 if (SUP.use_passive_connection) {
922 if (ftpfs_setup_passive (me, super, data, &data_addr))
923 return data;
925 SUP.use_passive_connection = 0;
926 print_vfs_message (_("ftpfs: could not setup passive mode"));
928 /* data or data_addr may be damaged by ftpfs_setup_passive */
929 close (data);
930 goto again;
933 /* If passive setup fails, fallback to active connections */
934 /* Active FTP connection */
935 if ((bind (data, (struct sockaddr *)&data_addr, len) == 0) &&
936 (getsockname (data, (struct sockaddr *) &data_addr, &len) == 0) &&
937 (listen (data, 1) == 0))
939 unsigned char *a = (unsigned char *)&data_addr.sin_addr;
940 unsigned char *p = (unsigned char *)&data_addr.sin_port;
942 if (ftpfs_command (me, super, WAIT_REPLY, "PORT %d,%d,%d,%d,%d,%d", a[0], a[1],
943 a[2], a[3], p[0], p[1]) == COMPLETE)
944 return data;
946 close (data);
947 ftpfs_errno = EIO;
948 return -1;
951 static int
952 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
953 const char *remote, int isbinary, int reget)
955 struct sockaddr_in from;
956 int s, j, data;
957 socklen_t fromlen = sizeof(from);
959 if ((s = ftpfs_initconn (me, super)) == -1)
960 return -1;
961 if (ftpfs_changetype (me, super, isbinary) == -1)
962 return -1;
963 if (reget > 0){
964 j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
965 if (j != CONTINUE)
966 return -1;
968 if (remote) {
969 char *remote_path = ftpfs_translate_path (me, super, remote);
970 j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
971 /* WarFtpD can't STORE //filename */
972 (*remote_path == '/') ? remote_path + 1 : remote_path);
973 g_free (remote_path);
974 } else
975 j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
976 if (j != PRELIM)
977 ERRNOR (EPERM, -1);
978 enable_interrupt_key();
979 if (SUP.use_passive_connection)
980 data = s;
981 else {
982 data = accept (s, (struct sockaddr *)&from, &fromlen);
983 if (data < 0) {
984 ftpfs_errno = errno;
985 close (s);
986 return -1;
988 close (s);
990 disable_interrupt_key();
991 return data;
994 #define ABORT_TIMEOUT 5
995 static void
996 ftpfs_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
998 struct vfs_s_super *super = FH_SUPER;
999 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1000 fd_set mask;
1001 char buf[1024];
1002 int dsock = FH_SOCK;
1003 FH_SOCK = -1;
1004 SUP.ctl_connection_busy = 0;
1006 print_vfs_message (_("ftpfs: aborting transfer."));
1007 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
1008 print_vfs_message (_("ftpfs: abort error: %s"),
1009 unix_error_string (errno));
1010 if (dsock != -1)
1011 close (dsock);
1012 return;
1015 if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
1016 print_vfs_message (_("ftpfs: abort failed"));
1017 if (dsock != -1)
1018 close (dsock);
1019 return;
1021 if (dsock != -1) {
1022 FD_ZERO (&mask);
1023 FD_SET (dsock, &mask);
1024 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1025 struct timeval start_tim, tim;
1026 gettimeofday (&start_tim, NULL);
1027 /* flush the remaining data */
1028 while (read (dsock, buf, sizeof (buf)) > 0) {
1029 gettimeofday (&tim, NULL);
1030 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1031 /* server keeps sending, drop the connection and ftpfs_reconnect */
1032 close (dsock);
1033 ftpfs_reconnect (me, super);
1034 return;
1038 close (dsock);
1040 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1041 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1044 #if 0
1045 static void
1046 resolve_symlink_without_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1048 struct linklist *flist;
1049 struct direntry *fe, *fel;
1050 char tmp[MC_MAXPATHLEN];
1051 int depth;
1053 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1054 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1055 /* flist->data->l_stat is alread initialized with 0 */
1056 fel = flist->data;
1057 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1058 if (fel->linkname[0] == '/') {
1059 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1060 continue;
1061 strcpy (tmp, fel->linkname);
1062 } else {
1063 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1064 continue;
1065 strcpy (tmp, dir->remote_path);
1066 if (tmp[1] != '\0')
1067 strcat (tmp, "/");
1068 strcat (tmp + 1, fel->linkname);
1070 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1071 canonicalize_pathname (tmp);
1072 fe = _get_file_entry(bucket, tmp, 0, 0);
1073 if (fe) {
1074 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1075 /* Symlink points to link which isn't resolved, yet. */
1076 if (fe->linkname[0] == '/') {
1077 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1078 break;
1079 strcpy (tmp, fe->linkname);
1080 } else {
1081 /* at this point tmp looks always like this
1082 /directory/filename, i.e. no need to check
1083 strrchr's return value */
1084 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1085 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1086 break;
1087 strcat (tmp, fe->linkname);
1089 continue;
1090 } else {
1091 fel->l_stat = g_new (struct stat, 1);
1092 if ( S_ISLNK (fe->s.st_mode))
1093 *fel->l_stat = *fe->l_stat;
1094 else
1095 *fel->l_stat = fe->s;
1096 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1099 break;
1103 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1106 static void
1107 resolve_symlink_with_ls_options(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1109 char buffer[2048] = "", *filename;
1110 int sock;
1111 FILE *fp;
1112 struct stat s;
1113 struct linklist *flist;
1114 struct direntry *fe;
1115 int switch_method = 0;
1117 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1118 if (strchr (dir->remote_path, ' ')) {
1119 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1120 print_vfs_message(_("ftpfs: CWD failed."));
1121 return;
1123 sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1125 else
1126 sock = ftpfs_open_data_connection (bucket, "LIST -lLa",
1127 dir->remote_path, TYPE_ASCII, 0);
1129 if (sock == -1) {
1130 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1131 return;
1134 fp = fdopen(sock, "r");
1135 if (fp == NULL) {
1136 close(sock);
1137 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1138 return;
1140 enable_interrupt_key();
1141 flist = dir->file_list->next;
1142 while (1) {
1143 do {
1144 if (flist == dir->file_list)
1145 goto done;
1146 fe = flist->data;
1147 flist = flist->next;
1148 } while (!S_ISLNK(fe->s.st_mode));
1149 while (1) {
1150 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1151 goto done;
1152 if (MEDATA->logfile){
1153 fputs (buffer, MEDATA->logfile);
1154 fflush (MEDATA->logfile);
1156 vfs_die("This code should be commented out\n");
1157 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1158 int r = strcmp(fe->name, filename);
1159 g_free(filename);
1160 if (r == 0) {
1161 if (S_ISLNK (s.st_mode)) {
1162 /* This server doesn't understand LIST -lLa */
1163 switch_method = 1;
1164 goto done;
1166 fe->l_stat = g_new (struct stat, 1);
1167 if (fe->l_stat == NULL)
1168 goto done;
1169 *fe->l_stat = s;
1170 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1171 break;
1173 if (r < 0)
1174 break;
1178 done:
1179 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1180 disable_interrupt_key();
1181 fclose(fp);
1182 ftpfs_get_reply(me, SUP.sock, NULL, 0);
1185 static void
1186 resolve_symlink(struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
1188 print_vfs_message(_("Resolving symlink..."));
1190 if (SUP.strict_rfc959_list_cmd)
1191 resolve_symlink_without_ls_options(me, super, dir);
1192 else
1193 resolve_symlink_with_ls_options(me, super, dir);
1195 #endif
1197 static int
1198 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
1200 struct vfs_s_entry *ent;
1201 struct vfs_s_super *super = dir->super;
1202 int sock, num_entries = 0;
1203 char buffer[BUF_8K];
1204 int cd_first;
1206 cd_first = ftpfs_first_cd_then_ls || (SUP.strict == RFC_STRICT)
1207 || (strchr (remote_path, ' ') != NULL);
1209 again:
1210 print_vfs_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1211 remote_path,
1212 SUP.strict ==
1213 RFC_STRICT ? _("(strict rfc959)") : "",
1214 cd_first ? _("(chdir first)") : "");
1216 if (cd_first) {
1217 if (ftpfs_chdir_internal (me, super, remote_path) != COMPLETE) {
1218 ftpfs_errno = ENOENT;
1219 print_vfs_message (_("ftpfs: CWD failed."));
1220 return -1;
1224 gettimeofday (&dir->timestamp, NULL);
1225 dir->timestamp.tv_sec += ftpfs_directory_timeout;
1227 if (SUP.strict == RFC_STRICT)
1228 sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1229 else if (cd_first)
1230 /* Dirty hack to avoid autoprepending / to . */
1231 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1232 sock =
1233 ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1234 else {
1235 /* Trailing "/." is necessary if remote_path is a symlink */
1236 char *path = mhl_str_dir_plus_file (remote_path, ".");
1237 sock =
1238 ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII,
1240 g_free (path);
1243 if (sock == -1)
1244 goto fallback;
1246 /* Clear the interrupt flag */
1247 enable_interrupt_key ();
1249 while (1) {
1250 int i;
1251 int res =
1252 vfs_s_get_line_interruptible (me, buffer, sizeof (buffer),
1253 sock);
1254 if (!res)
1255 break;
1257 if (res == EINTR) {
1258 me->verrno = ECONNRESET;
1259 close (sock);
1260 disable_interrupt_key ();
1261 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1262 print_vfs_message (_("%s: failure"), me->name);
1263 return -1;
1266 if (MEDATA->logfile) {
1267 fputs (buffer, MEDATA->logfile);
1268 fputs ("\n", MEDATA->logfile);
1269 fflush (MEDATA->logfile);
1272 ent = vfs_s_generate_entry (me, NULL, dir, 0);
1273 i = ent->ino->st.st_nlink;
1274 if (!vfs_parse_ls_lga
1275 (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1276 vfs_s_free_entry (me, ent);
1277 continue;
1279 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1280 num_entries++;
1281 vfs_s_insert_entry (me, dir, ent);
1284 close (sock);
1285 me->verrno = E_REMOTE;
1286 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1287 goto fallback;
1289 if (num_entries == 0 && cd_first == 0) {
1290 /* The LIST command may produce an empty output. In such scenario
1291 it is not clear whether this is caused by `remote_path' being
1292 a non-existent path or for some other reason (listing emtpy
1293 directory without the -a option, non-readable directory, etc.).
1295 Since `dir_load' is a crucial method, when it comes to determine
1296 whether a given path is a _directory_, the code must try its best
1297 to determine the type of `remote_path'. The only reliable way to
1298 achieve this is trough issuing a CWD command. */
1300 cd_first = 1;
1301 goto again;
1304 if (SUP.strict == RFC_AUTODETECT)
1305 SUP.strict = RFC_DARING;
1307 print_vfs_message (_("%s: done."), me->name);
1308 return 0;
1310 fallback:
1311 if (SUP.strict == RFC_AUTODETECT) {
1312 /* It's our first attempt to get a directory listing from this
1313 server (UNIX style LIST command) */
1314 SUP.strict = RFC_STRICT;
1315 /* I hate goto, but recursive call needs another 8K on stack */
1316 /* return ftpfs_dir_load (me, dir, remote_path); */
1317 cd_first = 1;
1318 goto again;
1320 print_vfs_message (_("ftpfs: failed; nowhere to fallback to"));
1321 ERRNOR (EACCES, -1);
1324 static int
1325 ftpfs_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name,
1326 char *localname)
1328 int h, sock, n_read, n_written;
1329 off_t n_stored;
1330 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1331 struct linger li;
1332 #else
1333 int flag_one = 1;
1334 #endif
1335 char buffer[8192];
1336 struct stat s;
1337 char *w_buf;
1338 struct vfs_s_super *super = FH_SUPER;
1340 h = open (localname, O_RDONLY);
1341 if (h == -1)
1342 ERRNOR (EIO, -1);
1343 sock =
1344 ftpfs_open_data_connection (me, super,
1345 fh->u.ftp.append ? "APPE" : "STOR", name,
1346 TYPE_BINARY, 0);
1347 if (sock < 0 || fstat (h, &s) == -1) {
1348 close (h);
1349 return -1;
1351 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1352 li.l_onoff = 1;
1353 li.l_linger = 120;
1354 setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1355 #else
1356 setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1357 #endif
1358 n_stored = 0;
1360 enable_interrupt_key ();
1361 while (1) {
1362 while ((n_read = read (h, buffer, sizeof (buffer))) == -1) {
1363 if (errno == EINTR) {
1364 if (got_interrupt ()) {
1365 ftpfs_errno = EINTR;
1366 goto error_return;
1367 } else
1368 continue;
1370 ftpfs_errno = errno;
1371 goto error_return;
1373 if (n_read == 0)
1374 break;
1375 n_stored += n_read;
1376 w_buf = buffer;
1377 while ((n_written = write (sock, w_buf, n_read)) != n_read) {
1378 if (n_written == -1) {
1379 if (errno == EINTR && !got_interrupt ()) {
1380 continue;
1382 ftpfs_errno = errno;
1383 goto error_return;
1385 w_buf += n_written;
1386 n_read -= n_written;
1388 print_vfs_message (_("ftpfs: storing file %lu (%lu)"),
1389 (unsigned long) n_stored, (unsigned long) s.st_size);
1391 disable_interrupt_key ();
1392 close (sock);
1393 close (h);
1394 if (ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1395 ERRNOR (EIO, -1);
1396 return 0;
1397 error_return:
1398 disable_interrupt_key ();
1399 close (sock);
1400 close (h);
1401 ftpfs_get_reply (me, SUP.sock, NULL, 0);
1402 return -1;
1405 static int
1406 ftpfs_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
1408 char *name = vfs_s_fullpath (me, fh->ino);
1410 if (!name)
1411 return 0;
1412 FH_SOCK = ftpfs_open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1413 g_free (name);
1414 if (FH_SOCK == -1)
1415 ERRNOR (EACCES, 0);
1416 fh->linear = LS_LINEAR_OPEN;
1417 FH_SUPER->u.ftp.ctl_connection_busy = 1;
1418 fh->u.ftp.append = 0;
1419 return 1;
1422 static int
1423 ftpfs_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
1425 int n;
1426 struct vfs_s_super *super = FH_SUPER;
1428 while ((n = read (FH_SOCK, buf, len))<0) {
1429 if ((errno == EINTR) && !got_interrupt())
1430 continue;
1431 break;
1434 if (n<0)
1435 ftpfs_linear_abort(me, fh);
1437 if (!n) {
1438 SUP.ctl_connection_busy = 0;
1439 close (FH_SOCK);
1440 FH_SOCK = -1;
1441 if ((ftpfs_get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1442 ERRNOR (E_REMOTE, -1);
1443 return 0;
1445 ERRNOR (errno, n);
1448 static void
1449 ftpfs_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
1451 if (FH_SOCK != -1)
1452 ftpfs_linear_abort(me, fh);
1455 static int ftpfs_ctl (void *fh, int ctlop, void *arg)
1457 (void) arg;
1459 switch (ctlop) {
1460 case VFS_CTL_IS_NOTREADY:
1462 int v;
1464 if (!FH->linear)
1465 vfs_die ("You may not do this");
1466 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1467 return 0;
1469 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1470 if (((v < 0) && (errno == EINTR)) || v == 0)
1471 return 1;
1472 return 0;
1474 default:
1475 return 0;
1479 static int
1480 ftpfs_send_command(struct vfs_class *me, const char *filename, const char *cmd, int flags)
1482 const char *rpath;
1483 char *p, *mpath = g_strdup(filename);
1484 struct vfs_s_super *super;
1485 int r;
1486 int flush_directory_cache = (flags & OPT_FLUSH);
1488 if (!(rpath = vfs_s_get_path_mangle(me, mpath, &super, 0))) {
1489 g_free(mpath);
1490 return -1;
1492 p = ftpfs_translate_path (me, super, rpath);
1493 r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
1494 g_free (p);
1495 vfs_stamp_create (&vfs_ftpfs_ops, super);
1496 if (flags & OPT_IGNORE_ERROR)
1497 r = COMPLETE;
1498 if (r != COMPLETE) {
1499 me->verrno = EPERM;
1500 g_free (mpath);
1501 return -1;
1503 if (flush_directory_cache)
1504 vfs_s_invalidate(me, super);
1505 g_free(mpath);
1506 return 0;
1509 /* This routine is called as the last step in load_setup */
1510 void
1511 ftpfs_init_passwd(void)
1513 ftpfs_anonymous_passwd = load_anon_passwd ();
1514 if (ftpfs_anonymous_passwd)
1515 return;
1517 /* If there is no anonymous ftp password specified
1518 * then we'll just use anonymous@
1519 * We don't send any other thing because:
1520 * - We want to remain anonymous
1521 * - We want to stop SPAM
1522 * - We don't want to let ftp sites to discriminate by the user,
1523 * host or country.
1525 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1528 static int ftpfs_chmod (struct vfs_class *me, const char *path, int mode)
1530 char buf[BUF_SMALL];
1532 snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1533 return ftpfs_send_command(me, path, buf, OPT_FLUSH);
1536 static int ftpfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1538 #if 0
1539 ftpfs_errno = EPERM;
1540 return -1;
1541 #else
1542 /* Everyone knows it is not possible to chown remotely, so why bother them.
1543 If someone's root, then copy/move will always try to chown it... */
1544 (void) me;
1545 (void) path;
1546 (void) owner;
1547 (void) group;
1548 return 0;
1549 #endif
1552 static int ftpfs_unlink (struct vfs_class *me, const char *path)
1554 return ftpfs_send_command(me, path, "DELE /%s", OPT_FLUSH);
1557 /* Return 1 if path is the same directory as the one we are in now */
1558 static int
1559 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
1561 (void) me;
1563 if (!SUP.cwdir)
1564 return 0;
1565 if (strcmp (path, SUP.cwdir) == 0)
1566 return 1;
1567 return 0;
1570 static int
1571 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
1573 int r;
1574 char *p;
1576 if (!SUP.cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
1577 return COMPLETE;
1579 p = ftpfs_translate_path (me, super, remote_path);
1580 r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
1581 g_free (p);
1583 if (r != COMPLETE) {
1584 ftpfs_errno = EIO;
1585 } else {
1586 g_free(SUP.cwdir);
1587 SUP.cwdir = g_strdup (remote_path);
1588 SUP.cwd_deferred = 0;
1590 return r;
1593 static int ftpfs_rename (struct vfs_class *me, const char *path1, const char *path2)
1595 ftpfs_send_command(me, path1, "RNFR /%s", OPT_FLUSH);
1596 return ftpfs_send_command(me, path2, "RNTO /%s", OPT_FLUSH);
1599 static int ftpfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1601 (void) mode; /* FIXME: should be used */
1603 return ftpfs_send_command(me, path, "MKD /%s", OPT_FLUSH);
1606 static int ftpfs_rmdir (struct vfs_class *me, const char *path)
1608 return ftpfs_send_command(me, path, "RMD /%s", OPT_FLUSH);
1611 static int
1612 ftpfs_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
1613 int mode)
1615 (void) mode;
1617 fh->u.ftp.append = 0;
1618 /* File will be written only, so no need to retrieve it from ftp server */
1619 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
1620 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1621 struct linger li;
1622 #else
1623 int li = 1;
1624 #endif
1625 char *name;
1627 /* ftpfs_linear_start() called, so data will be written
1628 * to local temporary file and stored to ftp server
1629 * by vfs_s_close later
1631 if (FH_SUPER->u.ftp.ctl_connection_busy) {
1632 if (!fh->ino->localname) {
1633 int handle = vfs_mkstemps (&fh->ino->localname, me->name,
1634 fh->ino->ent->name);
1635 if (handle == -1)
1636 return -1;
1637 close (handle);
1638 fh->u.ftp.append = flags & O_APPEND;
1640 return 0;
1642 name = vfs_s_fullpath (me, fh->ino);
1643 if (!name)
1644 return -1;
1645 fh->handle =
1646 ftpfs_open_data_connection (me, fh->ino->super,
1647 (flags & O_APPEND) ? "APPE" :
1648 "STOR", name, TYPE_BINARY, 0);
1649 g_free (name);
1651 if (fh->handle < 0)
1652 return -1;
1653 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1654 li.l_onoff = 1;
1655 li.l_linger = 120;
1656 #endif
1657 setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
1659 if (fh->ino->localname) {
1660 unlink (fh->ino->localname);
1661 g_free (fh->ino->localname);
1662 fh->ino->localname = NULL;
1664 return 0;
1667 if (!fh->ino->localname)
1668 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1669 return -1;
1670 if (!fh->ino->localname)
1671 vfs_die ("retrieve_file failed to fill in localname");
1672 return 0;
1675 static int ftpfs_fh_close (struct vfs_class *me, struct vfs_s_fh *fh)
1677 if (fh->handle != -1 && !fh->ino->localname){
1678 close (fh->handle);
1679 fh->handle = -1;
1680 /* File is stored to destination already, so
1681 * we prevent MEDATA->ftpfs_file_store() call from vfs_s_close ()
1683 fh->changed = 0;
1684 if (ftpfs_get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1685 ERRNOR (EIO, -1);
1686 vfs_s_invalidate (me, FH_SUPER);
1688 return 0;
1691 static void
1692 ftpfs_done (struct vfs_class *me)
1694 struct no_proxy_entry *np;
1696 (void) me;
1698 while (no_proxy) {
1699 np = no_proxy->next;
1700 g_free (no_proxy->domain);
1701 g_free (no_proxy);
1702 no_proxy = np;
1704 g_free (ftpfs_anonymous_passwd);
1705 g_free (ftpfs_proxy_host);
1708 static void
1709 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
1711 struct vfs_s_super *super = MEDATA->supers;
1712 char *name;
1714 while (super){
1715 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, (char *) NULL);
1716 (*func)(name);
1717 g_free (name);
1718 super = super->next;
1722 static char buffer[BUF_MEDIUM];
1723 static char *netrc;
1724 static const char *netrcp;
1726 /* This should match the keywords[] array below */
1727 typedef enum {
1728 NETRC_NONE = 0,
1729 NETRC_DEFAULT,
1730 NETRC_MACHINE,
1731 NETRC_LOGIN,
1732 NETRC_PASSWORD,
1733 NETRC_PASSWD,
1734 NETRC_ACCOUNT,
1735 NETRC_MACDEF,
1736 NETRC_UNKNOWN
1737 } keyword_t;
1739 static keyword_t ftpfs_netrc_next (void)
1741 char *p;
1742 keyword_t i;
1743 static const char *const keywords[] = { "default", "machine",
1744 "login", "password", "passwd", "account", "macdef", NULL
1748 while (1) {
1749 netrcp = skip_separators (netrcp);
1750 if (*netrcp != '\n')
1751 break;
1752 netrcp++;
1754 if (!*netrcp)
1755 return NETRC_NONE;
1756 p = buffer;
1757 if (*netrcp == '"') {
1758 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1759 if (*netrcp == '\\')
1760 netrcp++;
1761 *p++ = *netrcp;
1763 } else {
1764 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1765 *netrcp != ',' && *netrcp; netrcp++) {
1766 if (*netrcp == '\\')
1767 netrcp++;
1768 *p++ = *netrcp;
1771 *p = 0;
1772 if (!*buffer)
1773 return NETRC_NONE;
1775 i = NETRC_DEFAULT;
1776 while (keywords[i - 1]) {
1777 if (!strcmp (keywords[i - 1], buffer))
1778 return i;
1780 i++;
1783 return NETRC_UNKNOWN;
1786 static int ftpfs_netrc_bad_mode (const char *netrcname)
1788 static int be_angry = 1;
1789 struct stat mystat;
1791 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1792 if (be_angry) {
1793 message (D_ERROR, MSG_ERROR,
1794 _("~/.netrc file has incorrect mode.\n"
1795 "Remove password or correct mode."));
1796 be_angry = 0;
1798 return 1;
1800 return 0;
1803 /* Scan .netrc until we find matching "machine" or "default"
1804 * domain is used for additional matching
1805 * No search is done after "default" in compliance with "man netrc"
1806 * Return 0 if found, -1 otherwise */
1807 static int ftpfs_find_machine (const char *host, const char *domain)
1809 keyword_t keyword;
1811 while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) {
1812 if (keyword == NETRC_DEFAULT)
1813 return 0;
1815 if (keyword == NETRC_MACDEF) {
1816 /* Scan for an empty line, which concludes "macdef" */
1817 do {
1818 while (*netrcp && *netrcp != '\n')
1819 netrcp++;
1820 if (*netrcp != '\n')
1821 break;
1822 netrcp++;
1823 } while (*netrcp && *netrcp != '\n');
1824 continue;
1827 if (keyword != NETRC_MACHINE)
1828 continue;
1830 /* Take machine name */
1831 if (ftpfs_netrc_next () == NETRC_NONE)
1832 break;
1834 if (g_strcasecmp (host, buffer)) {
1835 /* Try adding our domain to short names in .netrc */
1836 const char *host_domain = strchr (host, '.');
1837 if (!host_domain)
1838 continue;
1840 /* Compare domain part */
1841 if (g_strcasecmp (host_domain, domain))
1842 continue;
1844 /* Compare local part */
1845 if (g_strncasecmp (host, buffer, host_domain - host))
1846 continue;
1849 return 0;
1852 /* end of .netrc */
1853 return -1;
1856 /* Extract login and password from .netrc for the host.
1857 * pass may be NULL.
1858 * Returns 0 for success, -1 for error */
1859 static int ftpfs_netrc_lookup (const char *host, char **login, char **pass)
1861 char *netrcname;
1862 char *tmp_pass = NULL;
1863 char hostname[MAXHOSTNAMELEN];
1864 const char *domain;
1865 keyword_t keyword;
1866 static struct rupcache {
1867 struct rupcache *next;
1868 char *host;
1869 char *login;
1870 char *pass;
1871 } *rup_cache = NULL, *rupp;
1873 /* Initialize *login and *pass */
1874 if (!login)
1875 return 0;
1876 *login = NULL;
1877 if (pass)
1878 *pass = NULL;
1880 /* Look up in the cache first */
1881 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
1882 if (!strcmp (host, rupp->host)) {
1883 if (rupp->login)
1884 *login = g_strdup (rupp->login);
1885 if (pass && rupp->pass)
1886 *pass = g_strdup (rupp->pass);
1887 return 0;
1891 /* Load current .netrc */
1892 netrcname = mhl_str_dir_plus_file (home_dir, ".netrc");
1893 netrcp = netrc = load_file (netrcname);
1894 if (netrc == NULL) {
1895 g_free (netrcname);
1896 return 0;
1899 /* Find our own domain name */
1900 if (gethostname (hostname, sizeof (hostname)) < 0)
1901 *hostname = 0;
1902 if (!(domain = strchr (hostname, '.')))
1903 domain = "";
1905 /* Scan for "default" and matching "machine" keywords */
1906 ftpfs_find_machine (host, domain);
1908 /* Scan for keywords following "default" and "machine" */
1909 while (1) {
1910 int need_break = 0;
1911 keyword = ftpfs_netrc_next ();
1913 switch (keyword) {
1914 case NETRC_LOGIN:
1915 if (ftpfs_netrc_next () == NETRC_NONE) {
1916 need_break = 1;
1917 break;
1920 /* We have another name already - should not happen */
1921 if (*login) {
1922 need_break = 1;
1923 break;
1926 /* We have login name now */
1927 *login = g_strdup (buffer);
1928 break;
1930 case NETRC_PASSWORD:
1931 case NETRC_PASSWD:
1932 if (ftpfs_netrc_next () == NETRC_NONE) {
1933 need_break = 1;
1934 break;
1937 /* Ignore unsafe passwords */
1938 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
1939 && ftpfs_netrc_bad_mode (netrcname)) {
1940 need_break = 1;
1941 break;
1944 /* Remember password. pass may be NULL, so use tmp_pass */
1945 if (tmp_pass == NULL)
1946 tmp_pass = g_strdup (buffer);
1947 break;
1949 case NETRC_ACCOUNT:
1950 /* "account" is followed by a token which we ignore */
1951 if (ftpfs_netrc_next () == NETRC_NONE) {
1952 need_break = 1;
1953 break;
1956 /* Ignore account, but warn user anyways */
1957 ftpfs_netrc_bad_mode (netrcname);
1958 break;
1960 default:
1961 /* Unexpected keyword or end of file */
1962 need_break = 1;
1963 break;
1966 if (need_break)
1967 break;
1970 g_free (netrc);
1971 g_free (netrcname);
1973 rupp = g_new (struct rupcache, 1);
1974 rupp->host = g_strdup (host);
1975 rupp->login = rupp->pass = 0;
1977 if (*login != NULL) {
1978 rupp->login = g_strdup (*login);
1980 if (tmp_pass != NULL)
1981 rupp->pass = g_strdup (tmp_pass);
1982 rupp->next = rup_cache;
1983 rup_cache = rupp;
1985 if (pass)
1986 *pass = tmp_pass;
1988 return 0;
1991 void
1992 init_ftpfs (void)
1994 static struct vfs_s_subclass ftpfs_subclass;
1996 ftpfs_subclass.flags = VFS_S_REMOTE;
1997 ftpfs_subclass.archive_same = ftpfs_archive_same;
1998 ftpfs_subclass.open_archive = ftpfs_open_archive;
1999 ftpfs_subclass.free_archive = ftpfs_free_archive;
2000 ftpfs_subclass.fh_open = ftpfs_fh_open;
2001 ftpfs_subclass.fh_close = ftpfs_fh_close;
2002 ftpfs_subclass.dir_load = ftpfs_dir_load;
2003 ftpfs_subclass.file_store = ftpfs_file_store;
2004 ftpfs_subclass.linear_start = ftpfs_linear_start;
2005 ftpfs_subclass.linear_read = ftpfs_linear_read;
2006 ftpfs_subclass.linear_close = ftpfs_linear_close;
2008 vfs_s_init_class (&vfs_ftpfs_ops, &ftpfs_subclass);
2009 vfs_ftpfs_ops.name = "ftpfs";
2010 vfs_ftpfs_ops.flags = VFSF_NOLINKS;
2011 vfs_ftpfs_ops.prefix = "ftp:";
2012 vfs_ftpfs_ops.done = &ftpfs_done;
2013 vfs_ftpfs_ops.fill_names = ftpfs_fill_names;
2014 vfs_ftpfs_ops.chmod = ftpfs_chmod;
2015 vfs_ftpfs_ops.chown = ftpfs_chown;
2016 vfs_ftpfs_ops.unlink = ftpfs_unlink;
2017 vfs_ftpfs_ops.rename = ftpfs_rename;
2018 vfs_ftpfs_ops.mkdir = ftpfs_mkdir;
2019 vfs_ftpfs_ops.rmdir = ftpfs_rmdir;
2020 vfs_ftpfs_ops.ctl = ftpfs_ctl;
2021 vfs_register_class (&vfs_ftpfs_ops);