*** empty log message ***
[midnight-commander.git] / vfs / ftpfs.c
blob653091ecaa515aa0f661817082257beae1d8372b
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 $Id$
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Library General Public License
14 as published by the Free Software Foundation; either version 2 of
15 the License, or (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU Library General Public License for more details.
22 You should have received a copy of the GNU Library General Public
23 License along with this program; if not, write to the Free Software
24 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
26 /* FTPfs TODO:
28 - make it more robust - all the connects etc. should handle EADDRINUSE and
29 ERETRY (have I spelled these names correctly?)
30 - make the user able to flush a connection - all the caches will get empty
31 etc., (tarfs as well), we should give there a user selectable timeout
32 and assign a key sequence.
33 - use hash table instead of linklist to cache ftpfs directory.
35 What to do with this?
38 * NOTE: Usage of tildes is deprecated, consider:
39 * cd /#ftp:pavel@hobit
40 * cd ~
41 * And now: what do I want to do? Do I want to go to /home/pavel or to
42 * /#ftp:hobit/home/pavel? I think first has better sense...
45 int f = !strcmp( remote_path, "/~" );
46 if (f || !strncmp( remote_path, "/~/", 3 )) {
47 char *s;
48 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
49 g_free (remote_path);
50 remote_path = s;
57 /* Namespace pollution: horrible */
59 #include <config.h>
60 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
61 #include <netdb.h> /* struct hostent */
62 #include <sys/socket.h> /* AF_INET */
63 #include <netinet/in.h> /* struct in_addr */
64 #ifdef HAVE_SETSOCKOPT
65 # include <netinet/ip.h> /* IP options */
66 #endif
67 #include <arpa/inet.h>
68 #include <arpa/ftp.h>
69 #include <arpa/telnet.h>
70 #include <sys/param.h>
72 #ifdef USE_TERMNET
73 #include <termnet.h>
74 #endif
76 #include "utilvfs.h"
78 #include "xdirentry.h"
79 #include "vfs.h"
80 #include "tcputil.h"
81 #include "../src/dialog.h"
82 #include "../src/setup.h" /* for load_anon_passwd */
83 #include "container.h"
84 #include "ftpfs.h"
85 #ifndef MAXHOSTNAMELEN
86 # define MAXHOSTNAMELEN 64
87 #endif
89 #define UPLOAD_ZERO_LENGTH_FILE
90 #define SUP super->u.ftp
91 #define FH_SOCK fh->u.ftp.sock
93 static int my_errno;
94 static int code;
96 /* Delay to retry a connection */
97 int ftpfs_retry_seconds = 30;
99 /* Method to use to connect to ftp sites */
100 int ftpfs_use_passive_connections = 1;
102 /* Method used to get directory listings:
103 * 1: try 'LIST -la <path>', if it fails
104 * fall back to CWD <path>; LIST
105 * 0: always use CWD <path>; LIST
107 int ftpfs_use_unix_list_options = 1;
109 /* First "CWD <path>", then "LIST -la ." */
110 int ftpfs_first_cd_then_ls;
112 /* Use the ~/.netrc */
113 int use_netrc = 1;
115 extern char *home_dir;
117 /* Anonymous setup */
118 char *ftpfs_anonymous_passwd = NULL;
119 int ftpfs_directory_timeout = 900;
121 /* Proxy host */
122 char *ftpfs_proxy_host = NULL;
124 /* wether we have to use proxy by default? */
125 int ftpfs_always_use_proxy;
127 /* source routing host */
128 extern int source_route;
130 /* Where we store the transactions */
131 static FILE *logfile = NULL;
133 /* If true, the directory cache is forced to reload */
134 static int force_expiration = 0;
136 #ifdef FIXME_LATER_ALIGATOR
137 static struct linklist *connections_list;
138 #endif
140 /* command wait_flag: */
141 #define NONE 0x00
142 #define WAIT_REPLY 0x01
143 #define WANT_STRING 0x02
144 static char reply_str [80];
146 /* char *translate_path (struct ftpfs_connection *bucket, char *remote_path)
147 Translate a Unix path, i.e. MC's internal path representation (e.g.
148 /somedir/somefile) to a path valid for the remote server. Every path
149 transfered to the remote server has to be mangled by this function
150 right prior to sending it.
151 Currently only Amiga ftp servers are handled in a special manner.
153 When the remote server is an amiga:
154 a) strip leading slash if necesarry
155 b) replace first occurance of ":/" with ":"
156 c) strip trailing "/."
159 static char *ftpfs_get_current_directory (vfs *me, vfs_s_super *super);
160 static int ftpfs_chdir_internal (vfs *me, vfs_s_super *super, char *remote_path);
161 static int command (vfs *me, vfs_s_super *super, int wait_reply, char *fmt, ...)
162 __attribute__ ((format (printf, 4, 5)));
163 static int ftpfs_open_socket (vfs *me, vfs_s_super *super);
164 static int login_server (vfs *me, vfs_s_super *super, const char *netrcpass);
165 static int lookup_netrc (const char *host, char **login, char **pass);
167 static char *
168 translate_path (vfs *me, vfs_s_super *super, const char *remote_path)
170 if (!SUP.remote_is_amiga)
171 return g_strdup (remote_path);
172 else {
173 char *ret, *p;
175 if (logfile) {
176 fprintf (logfile, "MC -- translate_path: %s\n", remote_path);
177 fflush (logfile);
180 /* strip leading slash(es) */
181 while (*remote_path == '/')
182 remote_path++;
185 * Don't change "/" into "", e.g. "CWD " would be
186 * invalid.
188 if (*remote_path == '\0')
189 return g_strdup (".");
191 ret = g_strdup (remote_path);
193 /* replace first occurance of ":/" with ":" */
194 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
195 strcpy (p + 1, p + 2);
197 /* strip trailing "/." */
198 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
199 *p = '\0';
200 return ret;
204 /* Extract the hostname and username from the path */
207 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
208 * ftp://sunsite.unc.edu/pub/linux
209 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
210 * ftp://tsx-11.mit.edu:8192/
211 * ftp://joe@foo.edu:11321/private
212 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
213 * is supplied.
217 #define FTP_COMMAND_PORT 21
218 #define HSC_PROXY_PORT 9875
220 static void
221 ftp_split_url(char *path, char **host, char **user, int *port, char **pass)
223 char *p;
225 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
226 URL_ALLOW_ANON);
228 if (!*user) {
229 /* Look up user and password in netrc */
230 if (use_netrc)
231 lookup_netrc (*host, user, pass);
232 if (!*user)
233 *user = g_strdup ("anonymous");
236 /* Look up password in netrc for known user */
237 if (use_netrc && *user && pass && !*pass) {
238 char *new_user;
240 lookup_netrc (*host, &new_user, pass);
242 /* If user is different, remove password */
243 if (new_user && strcmp (*user, new_user)) {
244 g_free (*pass);
245 *pass = NULL;
248 g_free (new_user);
251 if (p)
252 g_free (p);
255 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
256 static int
257 get_reply (vfs *me, int sock, char *string_buf, int string_len)
259 char answer[BUF_1K];
260 int i;
262 for (;;) {
263 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
264 if (string_buf)
265 *string_buf = 0;
266 code = 421;
267 return 4;
269 switch (sscanf(answer, "%d", &code)){
270 case 0:
271 if (string_buf) {
272 strncpy (string_buf, answer, string_len - 1);
273 *(string_buf + string_len - 1) = 0;
275 code = 500;
276 return 5;
277 case 1:
278 if (answer[3] == '-') {
279 while (1) {
280 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
281 if (string_buf)
282 *string_buf = 0;
283 code = 421;
284 return 4;
286 if ((sscanf (answer, "%d", &i) > 0) &&
287 (code == i) && (answer[3] == ' '))
288 break;
291 if (string_buf){
292 strncpy (string_buf, answer, string_len - 1);
293 *(string_buf + string_len - 1) = 0;
295 return code / 100;
300 static int
301 reconnect (vfs *me, vfs_s_super *super)
303 int sock = ftpfs_open_socket (me, super);
304 if (sock != -1){
305 char *cwdir = SUP.cwdir;
306 close (SUP.sock);
307 SUP.sock = sock;
308 SUP.cwdir = NULL;
309 if (login_server (me, super, SUP.password)){
310 if (!cwdir)
311 return 1;
312 sock = ftpfs_chdir_internal (me, super, cwdir);
313 g_free (cwdir);
314 return sock == COMPLETE;
316 SUP.cwdir = cwdir;
318 return 0;
321 static int
322 command (vfs *me, vfs_s_super *super, int wait_reply, char *fmt, ...)
324 va_list ap;
325 char *str, *fmt_str;
326 int status;
327 int sock = SUP.sock;
329 va_start (ap, fmt);
330 fmt_str = g_strdup_vprintf (fmt, ap);
331 va_end (ap);
333 status = strlen (fmt_str);
334 str = g_realloc (fmt_str, status + 3);
335 strcpy (str + status, "\r\n");
337 if (logfile){
338 if (strncmp (str, "PASS ", 5) == 0){
339 fputs ("PASS <Password not logged>\r\n", logfile);
340 } else
341 fwrite (str, status + 2, 1, logfile);
343 fflush (logfile);
346 got_sigpipe = 0;
347 enable_interrupt_key ();
348 status = write (SUP.sock, str, status + 2);
350 if (status < 0){
351 code = 421;
353 if (errno == EPIPE){ /* Remote server has closed connection */
354 static int level = 0; /* login_server() use command() */
355 if (level == 0){
356 level = 1;
357 status = reconnect (me, super);
358 level = 0;
359 if (status && write (SUP.sock, str, status + 2) > 0)
360 goto ok;
362 got_sigpipe = 1;
364 g_free (str);
365 disable_interrupt_key ();
366 return TRANSIENT;
369 g_free (str);
370 disable_interrupt_key ();
372 if (wait_reply)
373 return get_reply (me, sock, (wait_reply & WANT_STRING) ? reply_str : NULL, sizeof (reply_str)-1);
374 return COMPLETE;
377 static void
378 free_archive (vfs *me, vfs_s_super *super)
380 if (SUP.sock != -1){
381 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
382 command(me, super, NONE, "QUIT");
383 close(SUP.sock);
385 g_free (SUP.host);
386 g_free (SUP.user);
387 g_free (SUP.cwdir);
388 g_free (SUP.password);
391 /* some defines only used by changetype */
392 /* These two are valid values for the second parameter */
393 #define TYPE_ASCII 0
394 #define TYPE_BINARY 1
396 /* This one is only used to initialize bucket->isbinary, don't use it as
397 second parameter to changetype. */
398 #define TYPE_UNKNOWN -1
400 static int
401 changetype (vfs *me, vfs_s_super *super, int binary)
403 if (binary != SUP.isbinary) {
404 if (command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
405 ERRNOR (EIO, -1);
406 SUP.isbinary = binary;
408 return binary;
411 /* This routine logs the user in */
412 static int
413 login_server (vfs *me, vfs_s_super *super, const char *netrcpass)
415 #if defined(HSC_PROXY)
416 char *proxypass, *proxyname;
417 #endif
418 char *pass;
419 char *op;
420 char *name; /* login user name */
421 int anon = 0;
422 char reply_string[BUF_MEDIUM];
424 SUP.isbinary = TYPE_UNKNOWN;
425 if (netrcpass)
426 op = g_strdup (netrcpass);
427 else {
428 if (!strcmp (SUP.user, "anonymous") ||
429 !strcmp (SUP.user, "ftp")) {
430 if (!ftpfs_anonymous_passwd)
431 ftpfs_init_passwd();
432 op = g_strdup (ftpfs_anonymous_passwd);
433 anon = 1;
434 } else {
435 char *p;
437 if (!SUP.password){
438 p = g_strconcat (_(" FTP: Password required for "), SUP.user,
439 " ", NULL);
440 op = vfs_get_password (p);
441 g_free (p);
442 if (op == NULL)
443 ERRNOR (EPERM, 0);
444 SUP.password = g_strdup (op);
445 } else
446 op = g_strdup (SUP.password);
450 if (!anon || logfile)
451 pass = op;
452 else {
453 pass = g_strconcat ("-", op, NULL);
454 wipe_password (op);
457 /* Proxy server accepts: username@host-we-want-to-connect*/
458 if (SUP.proxy){
459 #if defined(HSC_PROXY)
460 char *p;
461 int port;
462 char *proxyhost = NULL;
464 ftp_split_url (ftpfs_proxy_host, &proxyhost, &proxyname, &port, 0);
465 g_free (proxyhost);
466 p = g_strconcat (_(" Proxy: Password required for "), proxyname, " ",
467 NULL);
468 proxypass = vfs_get_password (p);
469 g_free (p);
470 if (proxypass == NULL) {
471 wipe_password (pass);
472 g_free (proxyname);
473 ERRNOR (EPERM, 0);
475 name = g_strdup (SUP.user);
476 #else
477 name = g_strconcat (SUP.user, "@",
478 SUP.host[0] == '!' ? SUP.host+1 : SUP.host, NULL);
479 #endif
480 } else
481 name = g_strdup (SUP.user);
483 if (get_reply (me, SUP.sock, reply_string, sizeof (reply_string) - 1) == COMPLETE) {
484 g_strup (reply_string);
485 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
486 if (logfile) {
487 fprintf (logfile, "MC -- remote_is_amiga = %d\n", SUP.remote_is_amiga);
488 fflush (logfile);
490 #if defined(HSC_PROXY)
491 if (SUP.proxy){
492 print_vfs_message (_("ftpfs: sending proxy login name"));
493 if (command (me, super, 1, "USER %s", proxyname) != CONTINUE)
494 goto proxyfail;
496 print_vfs_message (_("ftpfs: sending proxy user password"));
497 if (command (me, super, 1, "PASS %s", proxypass) != COMPLETE)
498 goto proxyfail;
500 print_vfs_message (_("ftpfs: proxy authentication succeeded"));
501 if (command (me, super, 1, "SITE %s", SUP.host+1) != COMPLETE)
502 goto proxyfail;
504 print_vfs_message (_("ftpfs: connected to %s"), SUP.host+1);
505 if (0) {
506 proxyfail:
507 SUP.failed_on_login = 1;
508 /* my_errno = E; */
509 if (proxypass)
510 wipe_password (proxypass);
511 wipe_password (pass);
512 g_free (proxyname);
513 g_free (name);
514 ERRNOR (EPERM, 0);
516 if (proxypass)
517 wipe_password (proxypass);
518 g_free (proxyname);
520 #endif
521 print_vfs_message (_("ftpfs: sending login name"));
522 code = command (me, super, WAIT_REPLY, "USER %s", name);
524 switch (code){
525 case CONTINUE:
526 print_vfs_message (_("ftpfs: sending user password"));
527 if (command (me, super, WAIT_REPLY, "PASS %s", pass) != COMPLETE)
528 break;
530 case COMPLETE:
531 print_vfs_message (_("ftpfs: logged in"));
532 wipe_password (pass);
533 g_free (name);
534 return 1;
536 default:
537 SUP.failed_on_login = 1;
538 /* my_errno = E; */
539 if (SUP.password)
540 wipe_password (SUP.password);
541 SUP.password = 0;
543 goto login_fail;
546 message_2s (1, MSG_ERROR, _("ftpfs: Login incorrect for user %s "), SUP.user);
547 login_fail:
548 wipe_password (pass);
549 g_free (name);
550 ERRNOR (EPERM, 0);
553 #ifdef HAVE_SETSOCKOPT
554 static void
555 setup_source_route (int socket, int dest)
557 char buffer [20];
558 char *ptr = buffer;
560 if (!source_route)
561 return;
562 memset (buffer, 0, sizeof (buffer));
563 *ptr++ = IPOPT_LSRR;
564 *ptr++ = 3 + 8;
565 *ptr++ = 4; /* pointer */
567 /* First hop */
568 memcpy (ptr, (char *) &source_route, sizeof (int));
569 ptr += 4;
571 /* Second hop (ie, final destination) */
572 memcpy (ptr, (char *) &dest, sizeof (int));
573 ptr += 4;
574 while ((ptr - buffer) & 3)
575 ptr++;
576 if (setsockopt (socket, IPPROTO_IP, IP_OPTIONS,
577 buffer, ptr - buffer) < 0)
578 message_2s (1, MSG_ERROR, _(" Could not set source routing (%s)"), unix_error_string (errno));
580 #else
581 #define setup_source_route(x,y)
582 #endif
584 static struct no_proxy_entry {
585 char *domain;
586 void *next;
587 } *no_proxy;
589 static void
590 load_no_proxy_list (void)
592 /* FixMe: shouldn't be hardcoded!!! */
593 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
594 struct no_proxy_entry *np, *current = 0;
595 FILE *npf;
596 int c;
597 char *p;
598 static char *mc_file;
600 if (mc_file)
601 return;
603 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
604 if (exist_file (mc_file) &&
605 (npf = fopen (mc_file, "r"))) {
606 while (fgets (s, sizeof(s), npf) || !(feof (npf) || ferror (npf))) {
607 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
608 while ((c = fgetc (npf)) != EOF && c != '\n')
610 continue;
613 if (p == s)
614 continue;
616 *p = '\0';
618 np = g_new (struct no_proxy_entry, 1);
619 np->domain = g_strdup (s);
620 np->next = NULL;
621 if (no_proxy)
622 current->next = np;
623 else
624 no_proxy = np;
625 current = np;
628 fclose (npf);
630 g_free (mc_file);
633 static int
634 ftpfs_check_proxy (const char *host)
636 struct no_proxy_entry *npe;
638 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
639 return 0; /* sanity check */
641 if (*host == '!')
642 return 1;
644 if (!ftpfs_always_use_proxy)
645 return 0;
647 if (!strchr (host, '.'))
648 return 0;
650 load_no_proxy_list ();
651 for (npe = no_proxy; npe; npe=npe->next) {
652 char *domain = npe->domain;
654 if (domain[0] == '.') {
655 int ld = strlen (domain);
656 int lh = strlen (host);
658 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
659 ld--;
660 lh--;
663 if (!ld)
664 return 0;
665 } else
666 if (!g_strcasecmp (host, domain))
667 return 0;
670 return 1;
673 static void
674 ftpfs_get_proxy_host_and_port (char *proxy, char **host, int *port)
676 char *user, *dir;
678 #if defined(HSC_PROXY)
679 dir = vfs_split_url (proxy, host, &user, port, 0, HSC_PROXY_PORT, URL_ALLOW_ANON);
680 #else
681 dir = vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT, URL_ALLOW_ANON);
682 #endif
684 if (user)
685 g_free (user);
687 if (dir)
688 g_free (dir);
691 static int
692 ftpfs_open_socket (vfs *me, vfs_s_super *super)
694 struct sockaddr_in server_address;
695 struct hostent *hp;
696 int my_socket;
697 char *host;
698 int port = SUP.port;
699 int free_host = 0;
701 /* Use a proxy host? */
702 host = SUP.host;
704 if (!host || !*host){
705 print_vfs_message (_("ftpfs: Invalid host name."));
706 my_errno = EINVAL;
707 return -1;
710 /* Hosts to connect to that start with a ! should use proxy */
711 if (SUP.proxy){
712 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &port);
713 free_host = 1;
716 /* Get host address */
717 memset ((char *) &server_address, 0, sizeof (server_address));
718 server_address.sin_family = AF_INET;
719 server_address.sin_addr.s_addr = inet_addr (host);
720 if (server_address.sin_addr.s_addr != -1)
721 server_address.sin_family = AF_INET;
722 else {
723 hp = gethostbyname (host);
724 if (hp == NULL){
725 print_vfs_message (_("ftpfs: Invalid host address."));
726 my_errno = EINVAL;
727 if (free_host)
728 g_free (host);
729 return -1;
731 server_address.sin_family = hp->h_addrtype;
733 /* We copy only 4 bytes, we can not trust hp->h_length, as it comes from the DNS */
734 memcpy ((char *) &server_address.sin_addr, (char *) hp->h_addr, 4);
737 server_address.sin_port = htons (port);
739 /* Connect */
740 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
741 my_errno = errno;
742 if (free_host)
743 g_free (host);
744 return -1;
746 setup_source_route (my_socket, server_address.sin_addr.s_addr);
748 print_vfs_message (_("ftpfs: making connection to %s"), host);
749 if (free_host)
750 g_free (host);
752 enable_interrupt_key (); /* clear the interrupt flag */
754 if (connect (my_socket, (struct sockaddr *) &server_address,
755 sizeof (server_address)) < 0){
756 my_errno = errno;
757 if (errno == EINTR && got_interrupt ())
758 print_vfs_message (_("ftpfs: connection interrupted by user"));
759 else
760 print_vfs_message (_("ftpfs: connection to server failed: %s"),
761 unix_error_string(errno));
762 disable_interrupt_key();
763 close (my_socket);
764 return -1;
766 disable_interrupt_key();
767 return my_socket;
770 static int
771 open_archive_int (vfs *me, vfs_s_super *super)
773 int retry_seconds, count_down;
775 /* We do not want to use the passive if we are using proxies */
776 if (SUP.proxy)
777 SUP.use_passive_connection = 0;
779 retry_seconds = 0;
780 do {
781 SUP.failed_on_login = 0;
783 SUP.sock = ftpfs_open_socket (me, super);
784 if (SUP.sock == -1)
785 return -1;
787 if (login_server (me, super, NULL)) {
788 /* Logged in, no need to retry the connection */
789 break;
790 } else {
791 if (SUP.failed_on_login){
792 /* Close only the socket descriptor */
793 close (SUP.sock);
794 } else {
795 return -1;
797 if (ftpfs_retry_seconds){
798 retry_seconds = ftpfs_retry_seconds;
799 enable_interrupt_key ();
800 for (count_down = retry_seconds; count_down; count_down--){
801 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
802 sleep (1);
803 if (got_interrupt ()){
804 /* my_errno = E; */
805 disable_interrupt_key ();
806 return 0;
809 disable_interrupt_key ();
812 } while (retry_seconds);
814 SUP.cwdir = ftpfs_get_current_directory (me, super);
815 if (!SUP.cwdir)
816 SUP.cwdir = g_strdup (PATH_SEP_STR);
817 return 0;
820 static int
821 open_archive (vfs *me, vfs_s_super *super, char *archive_name, char *op)
823 char *host, *user, *password;
824 int port;
826 ftp_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
828 SUP.host = host;
829 SUP.user = user;
830 SUP.port = port;
831 SUP.cwdir = NULL;
832 SUP.proxy = 0;
833 if (ftpfs_check_proxy (host))
834 SUP.proxy = ftpfs_proxy_host;
835 SUP.password = password;
836 SUP.use_passive_connection = ftpfs_use_passive_connections | source_route;
837 SUP.use_source_route = source_route;
838 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
839 SUP.isbinary = TYPE_UNKNOWN;
840 SUP.remote_is_amiga = 0;
841 super->name = g_strdup("/");
842 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat(me, S_IFDIR | 0755));
844 return open_archive_int (me, super);
847 static int
848 archive_same(vfs *me, vfs_s_super *super, char *archive_name, char *op, void *cookie)
850 char *host, *user;
851 int port;
853 ftp_split_url (strchr(op, ':') + 1, &host, &user, &port, 0);
855 port = ((strcmp (host, SUP.host) == 0) &&
856 (strcmp (user, SUP.user) == 0) &&
857 (port == SUP.port));
859 g_free (host);
860 g_free (user);
862 return port;
865 void
866 ftpfs_flushdir (void)
868 force_expiration = 1;
871 static int
872 dir_uptodate(vfs *me, vfs_s_inode *ino)
874 struct timeval tim;
876 if (force_expiration) {
877 force_expiration = 0;
878 return 0;
880 gettimeofday(&tim, NULL);
881 if (tim.tv_sec < ino->u.ftp.timestamp.tv_sec)
882 return 1;
883 return 0;
886 /* The returned directory should always contain a trailing slash */
887 static char *
888 ftpfs_get_current_directory (vfs *me, vfs_s_super *super)
890 char buf[BUF_8K], *bufp, *bufq;
892 if (command (me, super, NONE, "PWD") == COMPLETE &&
893 get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
894 bufp = NULL;
895 for (bufq = buf; *bufq; bufq++)
896 if (*bufq == '"') {
897 if (!bufp) {
898 bufp = bufq + 1;
899 } else {
900 *bufq = 0;
901 if (*bufp) {
902 if (*(bufq - 1) != '/') {
903 *bufq++ = '/';
904 *bufq = 0;
906 if (*bufp == '/')
907 return g_strdup (bufp);
908 else {
909 /* If the remote server is an Amiga a leading slash
910 might be missing. MC needs it because it is used
911 as seperator between hostname and path internally. */
912 return g_strconcat( "/", bufp, 0);
914 } else {
915 my_errno = EIO;
916 return NULL;
921 my_errno = EIO;
922 return NULL;
926 /* Setup Passive ftp connection, we use it for source routed connections */
927 static int
928 setup_passive (vfs *me, vfs_s_super *super, int my_socket, struct sockaddr_in *sa)
930 int xa, xb, xc, xd, xe, xf;
931 char n [6];
932 char *c = reply_str;
934 if (command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
935 return 0;
937 /* Parse remote parameters */
938 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++)
940 if (!*c)
941 return 0;
942 if (!isdigit ((unsigned char) *c))
943 return 0;
944 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
945 return 0;
946 n [0] = (unsigned char) xa;
947 n [1] = (unsigned char) xb;
948 n [2] = (unsigned char) xc;
949 n [3] = (unsigned char) xd;
950 n [4] = (unsigned char) xe;
951 n [5] = (unsigned char) xf;
953 memcpy (&(sa->sin_addr.s_addr), (void *)n, 4);
954 memcpy (&(sa->sin_port), (void *)&n[4], 2);
955 setup_source_route (my_socket, sa->sin_addr.s_addr);
956 if (connect (my_socket, (struct sockaddr *) sa, sizeof (struct sockaddr_in)) < 0)
957 return 0;
958 return 1;
961 static int
962 initconn (vfs *me, vfs_s_super *super)
964 struct sockaddr_in data_addr;
965 int data, len = sizeof(data_addr);
966 struct protoent *pe;
968 getsockname(SUP.sock, (struct sockaddr *) &data_addr, &len);
969 data_addr.sin_port = 0;
971 pe = getprotobyname("tcp");
972 if (pe == NULL)
973 ERRNOR (EIO, -1);
974 data = socket (AF_INET, SOCK_STREAM, pe->p_proto);
975 if (data < 0)
976 ERRNOR (EIO, -1);
978 if (SUP.use_passive_connection){
979 if ((SUP.use_passive_connection = setup_passive (me, super, data, &data_addr)))
980 return data;
982 SUP.use_source_route = 0;
983 SUP.use_passive_connection = 0;
984 print_vfs_message (_("ftpfs: could not setup passive mode"));
987 /* If passive setup fails, fallback to active connections */
988 /* Active FTP connection */
989 if ((bind (data, (struct sockaddr *)&data_addr, len) == 0) &&
990 (getsockname (data, (struct sockaddr *) &data_addr, &len) == 0) &&
991 (listen (data, 1) == 0))
993 unsigned char *a = (unsigned char *)&data_addr.sin_addr;
994 unsigned char *p = (unsigned char *)&data_addr.sin_port;
996 if (command (me, super, WAIT_REPLY, "PORT %d,%d,%d,%d,%d,%d", a[0], a[1],
997 a[2], a[3], p[0], p[1]) == COMPLETE)
998 return data;
1000 close(data);
1001 my_errno = EIO;
1002 return -1;
1005 static int
1006 open_data_connection (vfs *me, vfs_s_super *super, char *cmd, char *remote,
1007 int isbinary, int reget)
1009 struct sockaddr_in from;
1010 int s, j, data, fromlen = sizeof(from);
1012 if ((s = initconn (me, super)) == -1)
1013 return -1;
1014 if (changetype (me, super, isbinary) == -1)
1015 return -1;
1016 if (reget > 0){
1017 j = command (me, super, WAIT_REPLY, "REST %d", reget);
1018 if (j != CONTINUE)
1019 return -1;
1021 if (remote) {
1022 char * remote_path = translate_path (me, super, remote);
1023 j = command (me, super, WAIT_REPLY, "%s /%s", cmd,
1024 /* WarFtpD can't STORE //filename */
1025 (*remote_path == '/') ? remote_path + 1 : remote_path);
1026 g_free (remote_path);
1027 } else
1028 j = command (me, super, WAIT_REPLY, "%s", cmd);
1029 if (j != PRELIM)
1030 ERRNOR (EPERM, -1);
1031 enable_interrupt_key();
1032 if (SUP.use_passive_connection)
1033 data = s;
1034 else {
1035 data = accept (s, (struct sockaddr *)&from, &fromlen);
1036 close(s);
1037 if (data < 0) {
1038 my_errno = errno;
1039 return -1;
1042 disable_interrupt_key();
1043 return data;
1046 static void
1047 linear_abort (vfs *me, vfs_s_fh *fh)
1049 vfs_s_super *super = FH_SUPER;
1050 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1051 fd_set mask;
1052 char buf[1024];
1053 int dsock = FH_SOCK;
1054 FH_SOCK = -1;
1055 SUP.control_connection_buzy = 0;
1057 print_vfs_message(_("ftpfs: aborting transfer."));
1058 if (send(SUP.sock, ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
1059 print_vfs_message(_("ftpfs: abort error: %s"), unix_error_string(errno));
1060 return;
1063 if (command(me, super, NONE, "%cABOR", DM) != COMPLETE){
1064 print_vfs_message (_("ftpfs: abort failed"));
1065 return;
1067 if (dsock != -1) {
1068 FD_ZERO(&mask);
1069 FD_SET(dsock, &mask);
1070 if (select(dsock + 1, &mask, NULL, NULL, NULL) > 0)
1071 while (read(dsock, buf, sizeof(buf)) > 0);
1073 if ((get_reply(me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1074 get_reply(me, SUP.sock, NULL, 0);
1077 #if 0
1078 static void
1079 resolve_symlink_without_ls_options(vfs *me, vfs_s_super *super, vfs_s_inode *dir)
1081 struct linklist *flist;
1082 struct direntry *fe, *fel;
1083 char tmp[MC_MAXPATHLEN];
1084 int depth;
1086 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1087 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1088 /* flist->data->l_stat is alread initialized with 0 */
1089 fel = flist->data;
1090 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1091 if (fel->linkname[0] == '/') {
1092 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1093 continue;
1094 strcpy (tmp, fel->linkname);
1095 } else {
1096 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1097 continue;
1098 strcpy (tmp, dir->remote_path);
1099 if (tmp[1] != '\0')
1100 strcat (tmp, "/");
1101 strcat (tmp + 1, fel->linkname);
1103 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1104 canonicalize_pathname (tmp);
1105 fe = _get_file_entry(bucket, tmp, 0, 0);
1106 if (fe) {
1107 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1108 /* Symlink points to link which isn't resolved, yet. */
1109 if (fe->linkname[0] == '/') {
1110 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1111 break;
1112 strcpy (tmp, fe->linkname);
1113 } else {
1114 /* at this point tmp looks always like this
1115 /directory/filename, i.e. no need to check
1116 strrchr's return value */
1117 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1118 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1119 break;
1120 strcat (tmp, fe->linkname);
1122 continue;
1123 } else {
1124 fel->l_stat = g_new (struct stat, 1);
1125 if ( S_ISLNK (fe->s.st_mode))
1126 *fel->l_stat = *fe->l_stat;
1127 else
1128 *fel->l_stat = fe->s;
1129 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1132 break;
1136 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1139 static void
1140 resolve_symlink_with_ls_options(vfs *me, vfs_s_super *super, vfs_s_inode *dir)
1142 char buffer[2048] = "", *filename;
1143 int sock;
1144 FILE *fp;
1145 struct stat s;
1146 struct linklist *flist;
1147 struct direntry *fe;
1148 int switch_method = 0;
1150 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1151 if (strchr (dir->remote_path, ' ')) {
1152 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1153 print_vfs_message(_("ftpfs: CWD failed."));
1154 return;
1156 sock = open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1158 else
1159 sock = open_data_connection (bucket, "LIST -lLa",
1160 dir->remote_path, TYPE_ASCII, 0);
1162 if (sock == -1) {
1163 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1164 return;
1167 fp = fdopen(sock, "r");
1168 if (fp == NULL) {
1169 close(sock);
1170 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1171 return;
1173 enable_interrupt_key();
1174 flist = dir->file_list->next;
1175 while (1) {
1176 do {
1177 if (flist == dir->file_list)
1178 goto done;
1179 fe = flist->data;
1180 flist = flist->next;
1181 } while (!S_ISLNK(fe->s.st_mode));
1182 while (1) {
1183 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1184 goto done;
1185 if (logfile){
1186 fputs (buffer, logfile);
1187 fflush (logfile);
1189 vfs_die("This code should be commented out\n");
1190 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1191 int r = strcmp(fe->name, filename);
1192 g_free(filename);
1193 if (r == 0) {
1194 if (S_ISLNK (s.st_mode)) {
1195 /* This server doesn't understand LIST -lLa */
1196 switch_method = 1;
1197 goto done;
1199 fe->l_stat = g_new (struct stat, 1);
1200 if (fe->l_stat == NULL)
1201 goto done;
1202 *fe->l_stat = s;
1203 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1204 break;
1206 if (r < 0)
1207 break;
1211 done:
1212 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1213 disable_interrupt_key();
1214 fclose(fp);
1215 get_reply(me, SUP.sock, NULL, 0);
1218 static void
1219 resolve_symlink(vfs *me, vfs_s_super *super, vfs_s_inode *dir)
1221 print_vfs_message(_("Resolving symlink..."));
1223 if (SUP.strict_rfc959_list_cmd)
1224 resolve_symlink_without_ls_options(me, super, dir);
1225 else
1226 resolve_symlink_with_ls_options(me, super, dir);
1228 #endif
1230 static int
1231 dir_load(vfs *me, vfs_s_inode *dir, char *remote_path)
1233 vfs_s_entry *ent;
1234 vfs_s_super *super = dir->super;
1235 int sock, num_entries = 0;
1236 #ifdef FIXME_LATER
1237 int has_symlinks = 0;
1238 #endif
1239 char buffer[BUF_8K];
1240 int cd_first;
1242 cd_first = ftpfs_first_cd_then_ls || (strchr (remote_path, ' ') != NULL)
1243 || (SUP.strict == RFC_STRICT);
1245 again:
1246 print_vfs_message(_("ftpfs: Reading FTP directory %s... %s%s"), remote_path,
1247 SUP.strict == RFC_STRICT ? _("(strict rfc959)") : "",
1248 cd_first ? _("(chdir first)") : "");
1250 if (cd_first) {
1251 char *p;
1253 p = translate_path (me, super, remote_path);
1255 if (ftpfs_chdir_internal (me, super, p) != COMPLETE) {
1256 g_free (p);
1257 my_errno = ENOENT;
1258 print_vfs_message(_("ftpfs: CWD failed."));
1259 return -1;
1261 g_free (p);
1264 gettimeofday(&dir->u.ftp.timestamp, NULL);
1265 dir->u.ftp.timestamp.tv_sec += ftpfs_directory_timeout;
1267 if (SUP.strict == RFC_STRICT)
1268 sock = open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1269 else if (cd_first)
1270 /* Dirty hack to avoid autoprepending / to . */
1271 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1272 sock = open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1273 else {
1274 /* Trailing "/." is necessary if remote_path is a symlink */
1275 char *path = concat_dir_and_file (remote_path, ".");
1276 sock = open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1277 g_free (path);
1280 if (sock == -1)
1281 goto fallback;
1283 /* Clear the interrupt flag */
1284 enable_interrupt_key ();
1286 #if 1
1288 /* added 20001006 by gisburn
1289 * add dots '.' and '..'. This must be _executed_ before scanning the dir as the
1290 * code below may jump directly into error handling code (without executing
1291 * remaining code). And C doesn't have try {...} finally {}; :-)
1293 vfs_s_inode *parent = dir->ent->dir;
1295 if( parent==NULL )
1296 parent = dir;
1298 ent = vfs_s_generate_entry(me, ".", dir, 0);
1299 ent->ino->st=dir->st;
1300 num_entries++;
1301 vfs_s_insert_entry(me, dir, ent);
1303 ent = vfs_s_generate_entry(me, "..", parent, 0);
1304 ent->ino->st=parent->st;
1305 num_entries++;
1306 vfs_s_insert_entry(me, dir, ent);
1308 #endif
1310 while (1) {
1311 int i;
1312 int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), sock);
1313 if (!res)
1314 break;
1316 if (res == EINTR) {
1317 me->verrno = ECONNRESET;
1318 close (sock);
1319 disable_interrupt_key();
1320 get_reply(me, SUP.sock, NULL, 0);
1321 print_vfs_message (_("%s: failure"), me->name);
1322 return -1;
1325 if (logfile){
1326 fputs (buffer, logfile);
1327 fputs ("\n", logfile);
1328 fflush (logfile);
1331 ent = vfs_s_generate_entry(me, NULL, dir, 0);
1332 i = ent->ino->st.st_nlink;
1333 if (!vfs_parse_ls_lga (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1334 vfs_s_free_entry (me, ent);
1335 continue;
1337 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1338 num_entries++;
1339 if ((!strcmp(ent->name, ".")) || (!strcmp (ent->name, ".."))) {
1340 g_free (ent->name);
1341 ent->name = NULL; /* Ouch, vfs_s_free_entry "knows" about . and .. being special :-( */
1342 vfs_s_free_entry (me, ent);
1343 continue;
1346 vfs_s_insert_entry(me, dir, ent);
1349 /* vfs_s_add_dots(me, dir, NULL);
1350 FIXME This really should be here; but we need to provide correct parent.
1351 Disabled for now, please fix it. Pavel
1353 close(sock);
1354 me->verrno = E_REMOTE;
1355 if ((get_reply (me, SUP.sock, NULL, 0) != COMPLETE) || !num_entries)
1356 goto fallback;
1358 if (SUP.strict == RFC_AUTODETECT)
1359 SUP.strict = RFC_DARING;
1361 #ifdef FIXME_LATER
1362 if (has_symlinks) {
1363 if (resolve_symlinks)
1364 resolve_symlink(me, super, dcache);
1365 else
1366 dcache->symlink_status = FTPFS_UNRESOLVED_SYMLINKS;
1368 #endif
1369 print_vfs_message (_("%s: done."), me->name);
1370 return 0;
1372 fallback:
1373 if (SUP.strict == RFC_AUTODETECT) {
1374 /* It's our first attempt to get a directory listing from this
1375 server (UNIX style LIST command) */
1376 SUP.strict = RFC_STRICT;
1377 /* I hate goto, but recursive call needs another 8K on stack */
1378 /* return dir_load (me, dir, remote_path); */
1379 cd_first = 1;
1380 goto again;
1382 print_vfs_message(_("ftpfs: failed; nowhere to fallback to"));
1383 ERRNOR(-1, EACCES);
1386 static int
1387 file_store(vfs *me, vfs_s_fh *fh, char *name, char *localname)
1389 int h, sock, n;
1390 off_t total;
1391 #ifdef HAVE_STRUCT_LINGER
1392 struct linger li;
1393 #else
1394 int flag_one = 1;
1395 #endif
1396 char buffer[8192];
1397 struct stat s;
1398 vfs_s_super *super = FH_SUPER;
1400 h = open(localname, O_RDONLY);
1401 if (h == -1)
1402 ERRNOR (EIO, -1);
1403 fstat(h, &s);
1404 sock = open_data_connection(me, super, fh->u.ftp.append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1405 if (sock < 0) {
1406 close(h);
1407 return -1;
1409 #ifdef HAVE_STRUCT_LINGER
1410 li.l_onoff = 1;
1411 li.l_linger = 120;
1412 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));
1413 #else
1414 setsockopt(sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1415 #endif
1416 total = 0;
1418 enable_interrupt_key();
1419 while (1) {
1420 while ((n = read(h, buffer, sizeof(buffer))) < 0) {
1421 if (errno == EINTR) {
1422 if (got_interrupt()) {
1423 my_errno = EINTR;
1424 goto error_return;
1426 else
1427 continue;
1429 my_errno = errno;
1430 goto error_return;
1432 if (n == 0)
1433 break;
1434 while (write(sock, buffer, n) < 0) {
1435 if (errno == EINTR) {
1436 if (got_interrupt()) {
1437 my_errno = EINTR;
1438 goto error_return;
1440 else
1441 continue;
1443 my_errno = errno;
1444 goto error_return;
1446 total += n;
1447 print_vfs_message(_("ftpfs: storing file %lu (%lu)"),
1448 (unsigned long) total, (unsigned long) s.st_size);
1450 disable_interrupt_key();
1451 close(sock);
1452 close(h);
1453 if (get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1454 ERRNOR (EIO, -1);
1455 return 0;
1456 error_return:
1457 disable_interrupt_key();
1458 close(sock);
1459 close(h);
1460 get_reply(me, SUP.sock, NULL, 0);
1461 return -1;
1464 //#define FH_SOCK fh->u.ftp.sock
1466 static int
1467 linear_start(vfs *me, vfs_s_fh *fh, int offset)
1469 char *name = vfs_s_fullpath (me, fh->ino);
1471 if (!name)
1472 return 0;
1473 FH_SOCK = open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1474 g_free (name);
1475 if (FH_SOCK == -1)
1476 ERRNOR (EACCES, 0);
1477 fh->linear = LS_LINEAR_OPEN;
1478 FH_SUPER->u.ftp.control_connection_buzy = 1;
1479 fh->u.ftp.append = 0;
1480 return 1;
1483 static int
1484 linear_read (vfs *me, vfs_s_fh *fh, void *buf, int len)
1486 int n;
1487 vfs_s_super *super = FH_SUPER;
1489 while ((n = read (FH_SOCK, buf, len))<0) {
1490 if ((errno == EINTR) && !got_interrupt())
1491 continue;
1492 break;
1495 if (n<0)
1496 linear_abort(me, fh);
1498 if (!n) {
1499 SUP.control_connection_buzy = 0;
1500 close (FH_SOCK);
1501 FH_SOCK = -1;
1502 if ((get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1503 ERRNOR (E_REMOTE, -1);
1504 return 0;
1506 ERRNOR (errno, n);
1509 static void
1510 linear_close (vfs *me, vfs_s_fh *fh)
1512 if (FH_SOCK != -1)
1513 linear_abort(me, fh);
1516 static int ftpfs_ctl (void *fh, int ctlop, int arg)
1518 switch (ctlop) {
1519 case MCCTL_IS_NOTREADY:
1521 int v;
1523 if (!FH->linear)
1524 vfs_die ("You may not do this");
1525 if (FH->linear == LS_LINEAR_CLOSED)
1526 return 0;
1528 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1529 if (((v < 0) && (errno == EINTR)) || v == 0)
1530 return 1;
1531 return 0;
1533 default:
1534 return 0;
1538 /* Warning: filename passed to this command is damaged */
1539 static int
1540 send_ftp_command(vfs *me, char *filename, char *cmd, int flags)
1542 char *rpath, *p;
1543 vfs_s_super *super;
1544 int r;
1545 int flush_directory_cache = (flags & OPT_FLUSH);
1547 if (!(rpath = vfs_s_get_path_mangle(me, filename, &super, 0)))
1548 return -1;
1549 p = translate_path (me, super, rpath);
1550 r = command (me, super, WAIT_REPLY, cmd, p);
1551 g_free (p);
1552 vfs_add_noncurrent_stamps (&vfs_ftpfs_ops, (vfsid) super, NULL);
1553 if (flags & OPT_IGNORE_ERROR)
1554 r = COMPLETE;
1555 if (r != COMPLETE)
1556 ERRNOR (EPERM, -1);
1557 if (flush_directory_cache)
1558 vfs_s_invalidate(me, super);
1559 return 0;
1562 /* This routine is called as the last step in load_setup */
1563 void
1564 ftpfs_init_passwd(void)
1566 ftpfs_anonymous_passwd = load_anon_passwd ();
1567 if (ftpfs_anonymous_passwd)
1568 return;
1570 /* If there is no anonymous ftp password specified
1571 * then we'll just use anonymous@
1572 * We don't send any other thing because:
1573 * - We want to remain anonymous
1574 * - We want to stop SPAM
1575 * - We don't want to let ftp sites to discriminate by the user,
1576 * host or country.
1578 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1581 static int ftpfs_chmod (vfs *me, char *path, int mode)
1583 char buf[BUF_SMALL];
1585 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1586 return send_ftp_command(me, path, buf, OPT_FLUSH);
1589 static int ftpfs_chown (vfs *me, char *path, int owner, int group)
1591 #if 0
1592 my_errno = EPERM;
1593 return -1;
1594 #else
1595 /* Everyone knows it is not possible to chown remotely, so why bother them.
1596 If someone's root, then copy/move will always try to chown it... */
1597 return 0;
1598 #endif
1601 static int ftpfs_unlink (vfs *me, char *path)
1603 return send_ftp_command(me, path, "DELE /%s", OPT_FLUSH);
1606 /* Return 1 if path is the same directory as the one we are in now */
1607 static int
1608 is_same_dir (vfs *me, vfs_s_super *super, const char *path)
1610 if (!SUP.cwdir)
1611 return 0;
1612 if (strcmp (path, SUP.cwdir) == 0)
1613 return 1;
1614 return 0;
1617 static int
1618 ftpfs_chdir_internal (vfs *me, vfs_s_super *super, char *remote_path)
1620 int r;
1621 char *p;
1623 if (!SUP.cwd_defered && is_same_dir (me, super, remote_path))
1624 return COMPLETE;
1626 p = translate_path (me, super, remote_path);
1627 r = command (me, super, WAIT_REPLY, "CWD /%s", p);
1628 g_free (p);
1630 if (r != COMPLETE) {
1631 my_errno = EIO;
1632 } else {
1633 g_free(SUP.cwdir);
1634 SUP.cwdir = g_strdup (remote_path);
1635 SUP.cwd_defered = 0;
1637 return r;
1640 static int ftpfs_rename (vfs *me, char *path1, char *path2)
1642 send_ftp_command(me, path1, "RNFR /%s", OPT_FLUSH);
1643 return send_ftp_command(me, path2, "RNTO /%s", OPT_FLUSH);
1646 static int ftpfs_mkdir (vfs *me, char *path, mode_t mode)
1648 return send_ftp_command(me, path, "MKD /%s", OPT_FLUSH);
1651 static int ftpfs_rmdir (vfs *me, char *path)
1653 return send_ftp_command(me, path, "RMD /%s", OPT_FLUSH);
1656 static int ftpfs_fh_open (vfs *me, vfs_s_fh *fh, int flags, int mode)
1658 fh->u.ftp.append = 0;
1659 /* File will be written only, so no need to retrieve it from ftp server */
1660 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY|O_RDWR))){
1661 #ifdef HAVE_STRUCT_LINGER
1662 struct linger li;
1663 #else
1664 int li = 1;
1665 #endif
1666 char * name;
1668 /* linear_start() called, so data will be written
1669 * to local temporary file and stored to ftp server
1670 * by vfs_s_close later
1672 if (FH_SUPER->u.ftp.control_connection_buzy){
1673 if (!fh->ino->localname){
1674 int handle = mc_mkstemps (&fh->ino->localname, me->name, NULL);
1675 if (handle == -1)
1676 return -1;
1677 close (handle);
1678 fh->u.ftp.append = flags & O_APPEND;
1680 return 0;
1682 name = vfs_s_fullpath (me, fh->ino);
1683 if (!name)
1684 return -1;
1685 fh->handle = open_data_connection(me, fh->ino->super,
1686 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1687 g_free (name);
1689 if (fh->handle < 0)
1690 return -1;
1691 #ifdef HAVE_STRUCT_LINGER
1692 li.l_onoff = 1;
1693 li.l_linger = 120;
1694 #endif
1695 setsockopt(fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof(li));
1697 if (fh->ino->localname){
1698 unlink (fh->ino->localname);
1699 g_free (fh->ino->localname);
1700 fh->ino->localname = NULL;
1702 return 0;
1705 if (!fh->ino->localname)
1706 if (vfs_s_retrieve_file (me, fh->ino)==-1)
1707 return -1;
1708 if (!fh->ino->localname)
1709 vfs_die( "retrieve_file failed to fill in localname" );
1710 return 0;
1713 static int ftpfs_fh_close (vfs *me, vfs_s_fh *fh)
1715 if (fh->handle != -1 && !fh->ino->localname){
1716 close (fh->handle);
1717 fh->handle = -1;
1718 /* File is stored to destination already, so
1719 * we prevent MEDATA->file_store() call from vfs_s_close ()
1721 fh->changed = 0;
1722 if (get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1723 ERRNOR (EIO, -1);
1724 vfs_s_invalidate (me, FH_SUPER);
1726 return 0;
1729 static struct vfs_s_data ftp_data = {
1730 NULL,
1733 NULL, /* logfile */
1735 NULL, /* init_inode */
1736 NULL, /* free_inode */
1737 NULL, /* init_entry */
1739 NULL, /* archive_check */
1740 archive_same,
1741 open_archive,
1742 free_archive,
1744 ftpfs_fh_open, /* fh_open */
1745 ftpfs_fh_close, /* fh_close */
1747 vfs_s_find_entry_linear,
1748 dir_load,
1749 dir_uptodate,
1750 file_store,
1752 linear_start,
1753 linear_read,
1754 linear_close
1757 static void
1758 ftpfs_fill_names (vfs *me, void (*func)(char *))
1760 struct vfs_s_super * super = ftp_data.supers;
1761 char *name;
1763 while (super){
1764 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, NULL);
1765 (*func)(name);
1766 g_free (name);
1767 super = super->next;
1771 vfs vfs_ftpfs_ops = {
1772 NULL, /* This is place of next pointer */
1773 "ftpfs",
1774 F_NET, /* flags */
1775 "ftp:", /* prefix */
1776 &ftp_data, /* data */
1777 0, /* errno */
1778 NULL, /* init */
1779 NULL, /* done */
1780 ftpfs_fill_names,
1781 NULL,
1783 vfs_s_open,
1784 vfs_s_close,
1785 vfs_s_read,
1786 vfs_s_write,
1788 vfs_s_opendir,
1789 vfs_s_readdir,
1790 vfs_s_closedir,
1791 vfs_s_telldir,
1792 vfs_s_seekdir,
1794 vfs_s_stat,
1795 vfs_s_lstat,
1796 vfs_s_fstat,
1798 ftpfs_chmod,
1799 ftpfs_chown, /* not really implemented but returns success */
1800 NULL,
1802 vfs_s_readlink,
1803 NULL,
1804 NULL,
1805 ftpfs_unlink,
1807 ftpfs_rename,
1808 vfs_s_chdir,
1809 vfs_s_ferrno,
1810 vfs_s_lseek,
1811 NULL,
1813 vfs_s_getid,
1814 vfs_s_nothingisopen,
1815 vfs_s_free,
1817 NULL,
1818 NULL,
1820 ftpfs_mkdir,
1821 ftpfs_rmdir,
1822 ftpfs_ctl,
1823 vfs_s_setctl
1825 MMAPNULL
1828 void ftpfs_set_debug (const char *file)
1830 logfile = fopen (file, "w+");
1831 if (logfile)
1832 ftp_data.logfile = logfile;
1835 static char buffer[BUF_MEDIUM];
1836 static char *netrc, *netrcp;
1838 /* This should match the keywords[] array below */
1839 typedef enum {
1840 NETRC_NONE = 0,
1841 NETRC_DEFAULT,
1842 NETRC_MACHINE,
1843 NETRC_LOGIN,
1844 NETRC_PASSWORD,
1845 NETRC_PASSWD,
1846 NETRC_ACCOUNT,
1847 NETRC_MACDEF,
1848 NETRC_UNKNOWN
1849 } keyword_t;
1851 static keyword_t netrc_next (void)
1853 char *p;
1854 keyword_t i;
1855 static const char *const keywords[] = { "default", "machine",
1856 "login", "password", "passwd", "account", "macdef", NULL
1860 while (1) {
1861 netrcp = skip_separators (netrcp);
1862 if (*netrcp != '\n')
1863 break;
1864 netrcp++;
1866 if (!*netrcp)
1867 return NETRC_NONE;
1868 p = buffer;
1869 if (*netrcp == '"') {
1870 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1871 if (*netrcp == '\\')
1872 netrcp++;
1873 *p++ = *netrcp;
1875 } else {
1876 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1877 *netrcp != ',' && *netrcp; netrcp++) {
1878 if (*netrcp == '\\')
1879 netrcp++;
1880 *p++ = *netrcp;
1883 *p = 0;
1884 if (!*buffer)
1885 return 0;
1887 i = NETRC_DEFAULT;
1888 while (keywords[i - 1]) {
1889 if (!strcmp (keywords[i - 1], buffer))
1890 return i;
1892 i++;
1895 return NETRC_UNKNOWN;
1898 static int netrc_has_incorrect_mode (char *netrcname, char *netrc)
1900 static int be_angry = 1;
1901 struct stat mystat;
1903 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1904 if (be_angry) {
1905 message_1s (1, MSG_ERROR,
1906 _("~/.netrc file has not correct mode.\n"
1907 "Remove password or correct mode."));
1908 be_angry = 0;
1910 return 1;
1912 return 0;
1915 /* Scan .netrc until we find matching "machine" or "default"
1916 * domain is used for additional matching
1917 * No search is done after "default" in compliance with "man netrc"
1918 * Return 0 if found, -1 otherwise */
1919 static int find_machine (const char *host, const char *domain)
1921 keyword_t keyword;
1923 while ((keyword = netrc_next ()) != NETRC_NONE) {
1924 if (keyword == NETRC_DEFAULT)
1925 return 0;
1927 if (keyword == NETRC_MACDEF) {
1928 /* Scan for an empty line, which concludes "macdef" */
1929 do {
1930 while (*netrcp && *netrcp != '\n')
1931 netrcp++;
1932 if (*netrcp != '\n')
1933 break;
1934 netrcp++;
1935 } while (*netrcp && *netrcp != '\n');
1936 continue;
1939 if (keyword != NETRC_MACHINE)
1940 continue;
1942 /* Take machine name */
1943 if (netrc_next () == NETRC_NONE)
1944 break;
1946 if (g_strcasecmp (host, buffer)) {
1947 /* Try adding our domain to short names in .netrc */
1948 char *host_domain = strchr (host, '.');
1949 if (!host_domain)
1950 continue;
1952 /* Compare domain part */
1953 if (g_strcasecmp (host_domain, domain))
1954 continue;
1956 /* Compare local part */
1957 if (g_strncasecmp (host, buffer, host_domain - host))
1958 continue;
1961 return 0;
1964 /* end of .netrc */
1965 return -1;
1968 /* Extract login and password from .netrc for the host.
1969 * pass may be NULL.
1970 * Returns 0 for success, -1 for error */
1971 static int lookup_netrc (const char *host, char **login, char **pass)
1973 char *netrcname;
1974 char *tmp_pass = NULL;
1975 char hostname[MAXHOSTNAMELEN], *domain;
1976 keyword_t keyword;
1977 static struct rupcache {
1978 struct rupcache *next;
1979 char *host;
1980 char *login;
1981 char *pass;
1982 } *rup_cache = NULL, *rupp;
1984 /* Initialize *login and *pass */
1985 if (!login)
1986 return 0;
1987 *login = NULL;
1988 if (pass)
1989 *pass = NULL;
1991 /* Look up in the cache first */
1992 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
1993 if (!strcmp (host, rupp->host)) {
1994 if (rupp->login)
1995 *login = g_strdup (rupp->login);
1996 if (pass && rupp->pass)
1997 *pass = g_strdup (rupp->pass);
1998 return 0;
2002 /* Load current .netrc */
2003 netrcname = concat_dir_and_file (home_dir, ".netrc");
2004 netrcp = netrc = load_file (netrcname);
2005 if (netrc == NULL) {
2006 g_free (netrcname);
2007 return 0;
2010 /* Find our own domain name */
2011 if (gethostname (hostname, sizeof (hostname)) < 0)
2012 *hostname = 0;
2013 if (!(domain = strchr (hostname, '.')))
2014 domain = "";
2016 /* Scan for "default" and matching "machine" keywords */
2017 find_machine (host, domain);
2019 /* Scan for keywords following "default" and "machine" */
2020 while (1) {
2021 int need_break = 0;
2022 keyword = netrc_next ();
2024 switch (keyword) {
2025 case NETRC_LOGIN:
2026 if (netrc_next () == NETRC_NONE) {
2027 need_break = 1;
2028 break;
2031 /* We have another name already - should not happen */
2032 if (*login) {
2033 need_break = 1;
2034 break;
2037 /* We have login name now */
2038 *login = g_strdup (buffer);
2039 break;
2041 case NETRC_PASSWORD:
2042 case NETRC_PASSWD:
2043 if (netrc_next () == NETRC_NONE) {
2044 need_break = 1;
2045 break;
2048 /* Ignore unsafe passwords */
2049 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2050 && netrc_has_incorrect_mode (netrcname, netrc)) {
2051 need_break = 1;
2052 break;
2055 /* Remember password. pass may be NULL, so use tmp_pass */
2056 if (tmp_pass == NULL)
2057 tmp_pass = g_strdup (buffer);
2058 break;
2060 case NETRC_ACCOUNT:
2061 /* "account" is followed by a token which we ignore */
2062 if (netrc_next () == NETRC_NONE) {
2063 need_break = 1;
2064 break;
2067 /* Ignore account, but warn user anyways */
2068 netrc_has_incorrect_mode (netrcname, netrc);
2069 break;
2071 default:
2072 /* Unexpected keyword or end of file */
2073 need_break = 1;
2074 break;
2077 if (need_break)
2078 break;
2081 g_free (netrc);
2082 g_free (netrcname);
2084 rupp = g_new (struct rupcache, 1);
2085 rupp->host = g_strdup (host);
2086 rupp->login = rupp->pass = 0;
2088 if (*login != NULL) {
2089 rupp->login = g_strdup (*login);
2091 if (tmp_pass != NULL)
2092 rupp->pass = g_strdup (tmp_pass);
2093 rupp->next = rup_cache;
2094 rup_cache = rupp;
2096 if (pass)
2097 *pass = tmp_pass;
2099 return 0;