Corrected a misdeleted row that created a crash on f1-f1 key pressing...
[midnight-commander.git] / vfs / ftpfs.c
blobd88b4a4ef36776c4cefae5a18a270a809a97ac89
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 #ifdef HAVE_ARPA_INET_H
68 #include <arpa/inet.h>
69 #endif
70 #include <arpa/ftp.h>
71 #include <arpa/telnet.h>
72 #include <sys/param.h>
74 #include "utilvfs.h"
76 #include "xdirentry.h"
77 #include "vfs.h"
78 #include "tcputil.h"
79 #include "../src/dialog.h"
80 #include "../src/setup.h" /* for load_anon_passwd */
81 #include "container.h"
82 #include "ftpfs.h"
83 #ifndef MAXHOSTNAMELEN
84 # define MAXHOSTNAMELEN 64
85 #endif
87 #define UPLOAD_ZERO_LENGTH_FILE
88 #define SUP super->u.ftp
89 #define FH_SOCK fh->u.ftp.sock
91 static int my_errno;
92 static int code;
94 /* Delay to retry a connection */
95 int ftpfs_retry_seconds = 30;
97 /* Method to use to connect to ftp sites */
98 int ftpfs_use_passive_connections = 1;
100 /* Method used to get directory listings:
101 * 1: try 'LIST -la <path>', if it fails
102 * fall back to CWD <path>; LIST
103 * 0: always use CWD <path>; LIST
105 int ftpfs_use_unix_list_options = 1;
107 /* First "CWD <path>", then "LIST -la ." */
108 int ftpfs_first_cd_then_ls;
110 /* Use the ~/.netrc */
111 int use_netrc = 1;
113 extern char *home_dir;
115 /* Anonymous setup */
116 char *ftpfs_anonymous_passwd = NULL;
117 int ftpfs_directory_timeout = 900;
119 /* Proxy host */
120 char *ftpfs_proxy_host = NULL;
122 /* wether we have to use proxy by default? */
123 int ftpfs_always_use_proxy;
125 /* source routing host */
126 extern int source_route;
128 /* Where we store the transactions */
129 static FILE *logfile = NULL;
131 /* If true, the directory cache is forced to reload */
132 static int force_expiration = 0;
134 #ifdef FIXME_LATER_ALIGATOR
135 static struct linklist *connections_list;
136 #endif
138 /* command wait_flag: */
139 #define NONE 0x00
140 #define WAIT_REPLY 0x01
141 #define WANT_STRING 0x02
142 static char reply_str [80];
144 /* char *translate_path (struct ftpfs_connection *bucket, char *remote_path)
145 Translate a Unix path, i.e. MC's internal path representation (e.g.
146 /somedir/somefile) to a path valid for the remote server. Every path
147 transfered to the remote server has to be mangled by this function
148 right prior to sending it.
149 Currently only Amiga ftp servers are handled in a special manner.
151 When the remote server is an amiga:
152 a) strip leading slash if necesarry
153 b) replace first occurance of ":/" with ":"
154 c) strip trailing "/."
157 static char *ftpfs_get_current_directory (vfs *me, vfs_s_super *super);
158 static int ftpfs_chdir_internal (vfs *me, vfs_s_super *super, char *remote_path);
159 static int command (vfs *me, vfs_s_super *super, int wait_reply, char *fmt, ...)
160 __attribute__ ((format (printf, 4, 5)));
161 static int ftpfs_open_socket (vfs *me, vfs_s_super *super);
162 static int login_server (vfs *me, vfs_s_super *super, const char *netrcpass);
163 static int lookup_netrc (const char *host, char **login, char **pass);
165 static char *
166 translate_path (vfs *me, vfs_s_super *super, const char *remote_path)
168 if (!SUP.remote_is_amiga)
169 return g_strdup (remote_path);
170 else {
171 char *ret, *p;
173 if (logfile) {
174 fprintf (logfile, "MC -- translate_path: %s\n", remote_path);
175 fflush (logfile);
178 /* strip leading slash(es) */
179 while (*remote_path == '/')
180 remote_path++;
183 * Don't change "/" into "", e.g. "CWD " would be
184 * invalid.
186 if (*remote_path == '\0')
187 return g_strdup (".");
189 ret = g_strdup (remote_path);
191 /* replace first occurance of ":/" with ":" */
192 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
193 strcpy (p + 1, p + 2);
195 /* strip trailing "/." */
196 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
197 *p = '\0';
198 return ret;
202 /* Extract the hostname and username from the path */
205 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
206 * ftp://sunsite.unc.edu/pub/linux
207 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
208 * ftp://tsx-11.mit.edu:8192/
209 * ftp://joe@foo.edu:11321/private
210 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
211 * is supplied.
215 #define FTP_COMMAND_PORT 21
216 #define HSC_PROXY_PORT 9875
218 static void
219 ftp_split_url(char *path, char **host, char **user, int *port, char **pass)
221 char *p;
223 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
224 URL_ALLOW_ANON);
226 if (!*user) {
227 /* Look up user and password in netrc */
228 if (use_netrc)
229 lookup_netrc (*host, user, pass);
230 if (!*user)
231 *user = g_strdup ("anonymous");
234 /* Look up password in netrc for known user */
235 if (use_netrc && *user && pass && !*pass) {
236 char *new_user;
238 lookup_netrc (*host, &new_user, pass);
240 /* If user is different, remove password */
241 if (new_user && strcmp (*user, new_user)) {
242 g_free (*pass);
243 *pass = NULL;
246 g_free (new_user);
249 if (p)
250 g_free (p);
253 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
254 static int
255 get_reply (vfs *me, int sock, char *string_buf, int string_len)
257 char answer[BUF_1K];
258 int i;
260 for (;;) {
261 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
262 if (string_buf)
263 *string_buf = 0;
264 code = 421;
265 return 4;
267 switch (sscanf(answer, "%d", &code)){
268 case 0:
269 if (string_buf) {
270 strncpy (string_buf, answer, string_len - 1);
271 *(string_buf + string_len - 1) = 0;
273 code = 500;
274 return 5;
275 case 1:
276 if (answer[3] == '-') {
277 while (1) {
278 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
279 if (string_buf)
280 *string_buf = 0;
281 code = 421;
282 return 4;
284 if ((sscanf (answer, "%d", &i) > 0) &&
285 (code == i) && (answer[3] == ' '))
286 break;
289 if (string_buf){
290 strncpy (string_buf, answer, string_len - 1);
291 *(string_buf + string_len - 1) = 0;
293 return code / 100;
298 static int
299 reconnect (vfs *me, vfs_s_super *super)
301 int sock = ftpfs_open_socket (me, super);
302 if (sock != -1){
303 char *cwdir = SUP.cwdir;
304 close (SUP.sock);
305 SUP.sock = sock;
306 SUP.cwdir = NULL;
307 if (login_server (me, super, SUP.password)){
308 if (!cwdir)
309 return 1;
310 sock = ftpfs_chdir_internal (me, super, cwdir);
311 g_free (cwdir);
312 return sock == COMPLETE;
314 SUP.cwdir = cwdir;
316 return 0;
319 static int
320 command (vfs *me, vfs_s_super *super, int wait_reply, char *fmt, ...)
322 va_list ap;
323 char *str, *fmt_str;
324 int status;
325 int sock = SUP.sock;
327 va_start (ap, fmt);
328 fmt_str = g_strdup_vprintf (fmt, ap);
329 va_end (ap);
331 status = strlen (fmt_str);
332 str = g_realloc (fmt_str, status + 3);
333 strcpy (str + status, "\r\n");
335 if (logfile){
336 if (strncmp (str, "PASS ", 5) == 0){
337 fputs ("PASS <Password not logged>\r\n", logfile);
338 } else
339 fwrite (str, status + 2, 1, logfile);
341 fflush (logfile);
344 got_sigpipe = 0;
345 enable_interrupt_key ();
346 status = write (SUP.sock, str, status + 2);
348 if (status < 0){
349 code = 421;
351 if (errno == EPIPE){ /* Remote server has closed connection */
352 static int level = 0; /* login_server() use command() */
353 if (level == 0){
354 level = 1;
355 status = reconnect (me, super);
356 level = 0;
357 if (status && write (SUP.sock, str, status + 2) > 0)
358 goto ok;
360 got_sigpipe = 1;
362 g_free (str);
363 disable_interrupt_key ();
364 return TRANSIENT;
367 g_free (str);
368 disable_interrupt_key ();
370 if (wait_reply)
371 return get_reply (me, sock, (wait_reply & WANT_STRING) ? reply_str : NULL, sizeof (reply_str)-1);
372 return COMPLETE;
375 static void
376 free_archive (vfs *me, vfs_s_super *super)
378 if (SUP.sock != -1){
379 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
380 command(me, super, NONE, "QUIT");
381 close(SUP.sock);
383 g_free (SUP.host);
384 g_free (SUP.user);
385 g_free (SUP.cwdir);
386 g_free (SUP.password);
389 /* some defines only used by changetype */
390 /* These two are valid values for the second parameter */
391 #define TYPE_ASCII 0
392 #define TYPE_BINARY 1
394 /* This one is only used to initialize bucket->isbinary, don't use it as
395 second parameter to changetype. */
396 #define TYPE_UNKNOWN -1
398 static int
399 changetype (vfs *me, vfs_s_super *super, int binary)
401 if (binary != SUP.isbinary) {
402 if (command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
403 ERRNOR (EIO, -1);
404 SUP.isbinary = binary;
406 return binary;
409 /* This routine logs the user in */
410 static int
411 login_server (vfs *me, vfs_s_super *super, const char *netrcpass)
413 #if defined(HSC_PROXY)
414 char *proxypass, *proxyname;
415 #endif
416 char *pass;
417 char *op;
418 char *name; /* login user name */
419 int anon = 0;
420 char reply_string[BUF_MEDIUM];
422 SUP.isbinary = TYPE_UNKNOWN;
423 if (netrcpass)
424 op = g_strdup (netrcpass);
425 else {
426 if (!strcmp (SUP.user, "anonymous") ||
427 !strcmp (SUP.user, "ftp")) {
428 if (!ftpfs_anonymous_passwd)
429 ftpfs_init_passwd();
430 op = g_strdup (ftpfs_anonymous_passwd);
431 anon = 1;
432 } else {
433 char *p;
435 if (!SUP.password){
436 p = g_strconcat (_(" FTP: Password required for "), SUP.user,
437 " ", NULL);
438 op = vfs_get_password (p);
439 g_free (p);
440 if (op == NULL)
441 ERRNOR (EPERM, 0);
442 SUP.password = g_strdup (op);
443 } else
444 op = g_strdup (SUP.password);
448 if (!anon || logfile)
449 pass = op;
450 else {
451 pass = g_strconcat ("-", op, NULL);
452 wipe_password (op);
455 /* Proxy server accepts: username@host-we-want-to-connect*/
456 if (SUP.proxy){
457 #if defined(HSC_PROXY)
458 char *p;
459 int port;
460 char *proxyhost = NULL;
462 ftp_split_url (ftpfs_proxy_host, &proxyhost, &proxyname, &port, 0);
463 g_free (proxyhost);
464 p = g_strconcat (_(" Proxy: Password required for "), proxyname, " ",
465 NULL);
466 proxypass = vfs_get_password (p);
467 g_free (p);
468 if (proxypass == NULL) {
469 wipe_password (pass);
470 g_free (proxyname);
471 ERRNOR (EPERM, 0);
473 name = g_strdup (SUP.user);
474 #else
475 name = g_strconcat (SUP.user, "@",
476 SUP.host[0] == '!' ? SUP.host+1 : SUP.host, NULL);
477 #endif
478 } else
479 name = g_strdup (SUP.user);
481 if (get_reply (me, SUP.sock, reply_string, sizeof (reply_string) - 1) == COMPLETE) {
482 g_strup (reply_string);
483 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
484 if (logfile) {
485 fprintf (logfile, "MC -- remote_is_amiga = %d\n", SUP.remote_is_amiga);
486 fflush (logfile);
488 #if defined(HSC_PROXY)
489 if (SUP.proxy){
490 print_vfs_message (_("ftpfs: sending proxy login name"));
491 if (command (me, super, 1, "USER %s", proxyname) != CONTINUE)
492 goto proxyfail;
494 print_vfs_message (_("ftpfs: sending proxy user password"));
495 if (command (me, super, 1, "PASS %s", proxypass) != COMPLETE)
496 goto proxyfail;
498 print_vfs_message (_("ftpfs: proxy authentication succeeded"));
499 if (command (me, super, 1, "SITE %s", SUP.host+1) != COMPLETE)
500 goto proxyfail;
502 print_vfs_message (_("ftpfs: connected to %s"), SUP.host+1);
503 if (0) {
504 proxyfail:
505 SUP.failed_on_login = 1;
506 /* my_errno = E; */
507 if (proxypass)
508 wipe_password (proxypass);
509 wipe_password (pass);
510 g_free (proxyname);
511 g_free (name);
512 ERRNOR (EPERM, 0);
514 if (proxypass)
515 wipe_password (proxypass);
516 g_free (proxyname);
518 #endif
519 print_vfs_message (_("ftpfs: sending login name"));
520 code = command (me, super, WAIT_REPLY, "USER %s", name);
522 switch (code){
523 case CONTINUE:
524 print_vfs_message (_("ftpfs: sending user password"));
525 if (command (me, super, WAIT_REPLY, "PASS %s", pass) != COMPLETE)
526 break;
528 case COMPLETE:
529 print_vfs_message (_("ftpfs: logged in"));
530 wipe_password (pass);
531 g_free (name);
532 return 1;
534 default:
535 SUP.failed_on_login = 1;
536 /* my_errno = E; */
537 if (SUP.password)
538 wipe_password (SUP.password);
539 SUP.password = 0;
541 goto login_fail;
544 message_2s (1, MSG_ERROR, _("ftpfs: Login incorrect for user %s "), SUP.user);
545 login_fail:
546 wipe_password (pass);
547 g_free (name);
548 ERRNOR (EPERM, 0);
551 #ifdef HAVE_SETSOCKOPT
552 static void
553 setup_source_route (int socket, int dest)
555 char buffer [20];
556 char *ptr = buffer;
558 if (!source_route)
559 return;
560 memset (buffer, 0, sizeof (buffer));
561 *ptr++ = IPOPT_LSRR;
562 *ptr++ = 3 + 8;
563 *ptr++ = 4; /* pointer */
565 /* First hop */
566 memcpy (ptr, (char *) &source_route, sizeof (int));
567 ptr += 4;
569 /* Second hop (ie, final destination) */
570 memcpy (ptr, (char *) &dest, sizeof (int));
571 ptr += 4;
572 while ((ptr - buffer) & 3)
573 ptr++;
574 if (setsockopt (socket, IPPROTO_IP, IP_OPTIONS,
575 buffer, ptr - buffer) < 0)
576 message_2s (1, MSG_ERROR, _(" Could not set source routing (%s)"), unix_error_string (errno));
578 #else
579 #define setup_source_route(x,y)
580 #endif
582 static struct no_proxy_entry {
583 char *domain;
584 void *next;
585 } *no_proxy;
587 static void
588 load_no_proxy_list (void)
590 /* FixMe: shouldn't be hardcoded!!! */
591 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
592 struct no_proxy_entry *np, *current = 0;
593 FILE *npf;
594 int c;
595 char *p;
596 static char *mc_file;
598 if (mc_file)
599 return;
601 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
602 if (exist_file (mc_file) &&
603 (npf = fopen (mc_file, "r"))) {
604 while (fgets (s, sizeof(s), npf) || !(feof (npf) || ferror (npf))) {
605 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
606 while ((c = fgetc (npf)) != EOF && c != '\n')
608 continue;
611 if (p == s)
612 continue;
614 *p = '\0';
616 np = g_new (struct no_proxy_entry, 1);
617 np->domain = g_strdup (s);
618 np->next = NULL;
619 if (no_proxy)
620 current->next = np;
621 else
622 no_proxy = np;
623 current = np;
626 fclose (npf);
628 g_free (mc_file);
631 static int
632 ftpfs_check_proxy (const char *host)
634 struct no_proxy_entry *npe;
636 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
637 return 0; /* sanity check */
639 if (*host == '!')
640 return 1;
642 if (!ftpfs_always_use_proxy)
643 return 0;
645 if (!strchr (host, '.'))
646 return 0;
648 load_no_proxy_list ();
649 for (npe = no_proxy; npe; npe=npe->next) {
650 char *domain = npe->domain;
652 if (domain[0] == '.') {
653 int ld = strlen (domain);
654 int lh = strlen (host);
656 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
657 ld--;
658 lh--;
661 if (!ld)
662 return 0;
663 } else
664 if (!g_strcasecmp (host, domain))
665 return 0;
668 return 1;
671 static void
672 ftpfs_get_proxy_host_and_port (char *proxy, char **host, int *port)
674 char *user, *dir;
676 #if defined(HSC_PROXY)
677 dir = vfs_split_url (proxy, host, &user, port, 0, HSC_PROXY_PORT, URL_ALLOW_ANON);
678 #else
679 dir = vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT, URL_ALLOW_ANON);
680 #endif
682 if (user)
683 g_free (user);
685 if (dir)
686 g_free (dir);
689 static int
690 ftpfs_open_socket (vfs *me, vfs_s_super *super)
692 struct sockaddr_in server_address;
693 struct hostent *hp;
694 int my_socket;
695 char *host;
696 int port = SUP.port;
697 int free_host = 0;
699 /* Use a proxy host? */
700 host = SUP.host;
702 if (!host || !*host){
703 print_vfs_message (_("ftpfs: Invalid host name."));
704 my_errno = EINVAL;
705 return -1;
708 /* Hosts to connect to that start with a ! should use proxy */
709 if (SUP.proxy){
710 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &port);
711 free_host = 1;
714 /* Get host address */
715 memset ((char *) &server_address, 0, sizeof (server_address));
716 server_address.sin_family = AF_INET;
717 server_address.sin_addr.s_addr = inet_addr (host);
718 if (server_address.sin_addr.s_addr != -1)
719 server_address.sin_family = AF_INET;
720 else {
721 hp = gethostbyname (host);
722 if (hp == NULL){
723 print_vfs_message (_("ftpfs: Invalid host address."));
724 my_errno = EINVAL;
725 if (free_host)
726 g_free (host);
727 return -1;
729 server_address.sin_family = hp->h_addrtype;
731 /* We copy only 4 bytes, we can not trust hp->h_length, as it comes from the DNS */
732 memcpy ((char *) &server_address.sin_addr, (char *) hp->h_addr, 4);
735 server_address.sin_port = htons (port);
737 /* Connect */
738 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
739 my_errno = errno;
740 if (free_host)
741 g_free (host);
742 return -1;
744 setup_source_route (my_socket, server_address.sin_addr.s_addr);
746 print_vfs_message (_("ftpfs: making connection to %s"), host);
747 if (free_host)
748 g_free (host);
750 enable_interrupt_key (); /* clear the interrupt flag */
752 if (connect (my_socket, (struct sockaddr *) &server_address,
753 sizeof (server_address)) < 0){
754 my_errno = errno;
755 if (errno == EINTR && got_interrupt ())
756 print_vfs_message (_("ftpfs: connection interrupted by user"));
757 else
758 print_vfs_message (_("ftpfs: connection to server failed: %s"),
759 unix_error_string(errno));
760 disable_interrupt_key();
761 close (my_socket);
762 return -1;
764 disable_interrupt_key();
765 return my_socket;
768 static int
769 open_archive_int (vfs *me, vfs_s_super *super)
771 int retry_seconds, count_down;
773 /* We do not want to use the passive if we are using proxies */
774 if (SUP.proxy)
775 SUP.use_passive_connection = 0;
777 retry_seconds = 0;
778 do {
779 SUP.failed_on_login = 0;
781 SUP.sock = ftpfs_open_socket (me, super);
782 if (SUP.sock == -1)
783 return -1;
785 if (login_server (me, super, NULL)) {
786 /* Logged in, no need to retry the connection */
787 break;
788 } else {
789 if (SUP.failed_on_login){
790 /* Close only the socket descriptor */
791 close (SUP.sock);
792 } else {
793 return -1;
795 if (ftpfs_retry_seconds){
796 retry_seconds = ftpfs_retry_seconds;
797 enable_interrupt_key ();
798 for (count_down = retry_seconds; count_down; count_down--){
799 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
800 sleep (1);
801 if (got_interrupt ()){
802 /* my_errno = E; */
803 disable_interrupt_key ();
804 return 0;
807 disable_interrupt_key ();
810 } while (retry_seconds);
812 SUP.cwdir = ftpfs_get_current_directory (me, super);
813 if (!SUP.cwdir)
814 SUP.cwdir = g_strdup (PATH_SEP_STR);
815 return 0;
818 static int
819 open_archive (vfs *me, vfs_s_super *super, char *archive_name, char *op)
821 char *host, *user, *password;
822 int port;
824 ftp_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
826 SUP.host = host;
827 SUP.user = user;
828 SUP.port = port;
829 SUP.cwdir = NULL;
830 SUP.proxy = 0;
831 if (ftpfs_check_proxy (host))
832 SUP.proxy = ftpfs_proxy_host;
833 SUP.password = password;
834 SUP.use_passive_connection = ftpfs_use_passive_connections | source_route;
835 SUP.use_source_route = source_route;
836 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
837 SUP.isbinary = TYPE_UNKNOWN;
838 SUP.remote_is_amiga = 0;
839 super->name = g_strdup("/");
840 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat(me, S_IFDIR | 0755));
842 return open_archive_int (me, super);
845 static int
846 archive_same(vfs *me, vfs_s_super *super, char *archive_name, char *op, void *cookie)
848 char *host, *user;
849 int port;
851 ftp_split_url (strchr(op, ':') + 1, &host, &user, &port, 0);
853 port = ((strcmp (host, SUP.host) == 0) &&
854 (strcmp (user, SUP.user) == 0) &&
855 (port == SUP.port));
857 g_free (host);
858 g_free (user);
860 return port;
863 void
864 ftpfs_flushdir (void)
866 force_expiration = 1;
869 static int
870 dir_uptodate(vfs *me, vfs_s_inode *ino)
872 struct timeval tim;
874 if (force_expiration) {
875 force_expiration = 0;
876 return 0;
878 gettimeofday(&tim, NULL);
879 if (tim.tv_sec < ino->u.ftp.timestamp.tv_sec)
880 return 1;
881 return 0;
884 /* The returned directory should always contain a trailing slash */
885 static char *
886 ftpfs_get_current_directory (vfs *me, vfs_s_super *super)
888 char buf[BUF_8K], *bufp, *bufq;
890 if (command (me, super, NONE, "PWD") == COMPLETE &&
891 get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
892 bufp = NULL;
893 for (bufq = buf; *bufq; bufq++)
894 if (*bufq == '"') {
895 if (!bufp) {
896 bufp = bufq + 1;
897 } else {
898 *bufq = 0;
899 if (*bufp) {
900 if (*(bufq - 1) != '/') {
901 *bufq++ = '/';
902 *bufq = 0;
904 if (*bufp == '/')
905 return g_strdup (bufp);
906 else {
907 /* If the remote server is an Amiga a leading slash
908 might be missing. MC needs it because it is used
909 as seperator between hostname and path internally. */
910 return g_strconcat( "/", bufp, 0);
912 } else {
913 my_errno = EIO;
914 return NULL;
919 my_errno = EIO;
920 return NULL;
924 /* Setup Passive ftp connection, we use it for source routed connections */
925 static int
926 setup_passive (vfs *me, vfs_s_super *super, int my_socket, struct sockaddr_in *sa)
928 int xa, xb, xc, xd, xe, xf;
929 char n [6];
930 char *c = reply_str;
932 if (command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
933 return 0;
935 /* Parse remote parameters */
936 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++)
938 if (!*c)
939 return 0;
940 if (!isdigit ((unsigned char) *c))
941 return 0;
942 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
943 return 0;
944 n [0] = (unsigned char) xa;
945 n [1] = (unsigned char) xb;
946 n [2] = (unsigned char) xc;
947 n [3] = (unsigned char) xd;
948 n [4] = (unsigned char) xe;
949 n [5] = (unsigned char) xf;
951 memcpy (&(sa->sin_addr.s_addr), (void *)n, 4);
952 memcpy (&(sa->sin_port), (void *)&n[4], 2);
953 setup_source_route (my_socket, sa->sin_addr.s_addr);
954 if (connect (my_socket, (struct sockaddr *) sa, sizeof (struct sockaddr_in)) < 0)
955 return 0;
956 return 1;
959 static int
960 initconn (vfs *me, vfs_s_super *super)
962 struct sockaddr_in data_addr;
963 int data, len = sizeof(data_addr);
964 struct protoent *pe;
966 getsockname(SUP.sock, (struct sockaddr *) &data_addr, &len);
967 data_addr.sin_port = 0;
969 pe = getprotobyname("tcp");
970 if (pe == NULL)
971 ERRNOR (EIO, -1);
972 data = socket (AF_INET, SOCK_STREAM, pe->p_proto);
973 if (data < 0)
974 ERRNOR (EIO, -1);
976 if (SUP.use_passive_connection){
977 if ((SUP.use_passive_connection = setup_passive (me, super, data, &data_addr)))
978 return data;
980 SUP.use_source_route = 0;
981 SUP.use_passive_connection = 0;
982 print_vfs_message (_("ftpfs: could not setup passive mode"));
985 /* If passive setup fails, fallback to active connections */
986 /* Active FTP connection */
987 if ((bind (data, (struct sockaddr *)&data_addr, len) == 0) &&
988 (getsockname (data, (struct sockaddr *) &data_addr, &len) == 0) &&
989 (listen (data, 1) == 0))
991 unsigned char *a = (unsigned char *)&data_addr.sin_addr;
992 unsigned char *p = (unsigned char *)&data_addr.sin_port;
994 if (command (me, super, WAIT_REPLY, "PORT %d,%d,%d,%d,%d,%d", a[0], a[1],
995 a[2], a[3], p[0], p[1]) == COMPLETE)
996 return data;
998 close(data);
999 my_errno = EIO;
1000 return -1;
1003 static int
1004 open_data_connection (vfs *me, vfs_s_super *super, char *cmd, char *remote,
1005 int isbinary, int reget)
1007 struct sockaddr_in from;
1008 int s, j, data, fromlen = sizeof(from);
1010 if ((s = initconn (me, super)) == -1)
1011 return -1;
1012 if (changetype (me, super, isbinary) == -1)
1013 return -1;
1014 if (reget > 0){
1015 j = command (me, super, WAIT_REPLY, "REST %d", reget);
1016 if (j != CONTINUE)
1017 return -1;
1019 if (remote) {
1020 char * remote_path = translate_path (me, super, remote);
1021 j = command (me, super, WAIT_REPLY, "%s /%s", cmd,
1022 /* WarFtpD can't STORE //filename */
1023 (*remote_path == '/') ? remote_path + 1 : remote_path);
1024 g_free (remote_path);
1025 } else
1026 j = command (me, super, WAIT_REPLY, "%s", cmd);
1027 if (j != PRELIM)
1028 ERRNOR (EPERM, -1);
1029 enable_interrupt_key();
1030 if (SUP.use_passive_connection)
1031 data = s;
1032 else {
1033 data = accept (s, (struct sockaddr *)&from, &fromlen);
1034 close(s);
1035 if (data < 0) {
1036 my_errno = errno;
1037 return -1;
1040 disable_interrupt_key();
1041 return data;
1044 static void
1045 linear_abort (vfs *me, vfs_s_fh *fh)
1047 vfs_s_super *super = FH_SUPER;
1048 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1049 fd_set mask;
1050 char buf[1024];
1051 int dsock = FH_SOCK;
1052 FH_SOCK = -1;
1053 SUP.control_connection_buzy = 0;
1055 print_vfs_message(_("ftpfs: aborting transfer."));
1056 if (send(SUP.sock, ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
1057 print_vfs_message(_("ftpfs: abort error: %s"), unix_error_string(errno));
1058 return;
1061 if (command(me, super, NONE, "%cABOR", DM) != COMPLETE){
1062 print_vfs_message (_("ftpfs: abort failed"));
1063 return;
1065 if (dsock != -1) {
1066 FD_ZERO(&mask);
1067 FD_SET(dsock, &mask);
1068 if (select(dsock + 1, &mask, NULL, NULL, NULL) > 0)
1069 while (read(dsock, buf, sizeof(buf)) > 0);
1071 if ((get_reply(me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1072 get_reply(me, SUP.sock, NULL, 0);
1075 #if 0
1076 static void
1077 resolve_symlink_without_ls_options(vfs *me, vfs_s_super *super, vfs_s_inode *dir)
1079 struct linklist *flist;
1080 struct direntry *fe, *fel;
1081 char tmp[MC_MAXPATHLEN];
1082 int depth;
1084 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1085 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1086 /* flist->data->l_stat is alread initialized with 0 */
1087 fel = flist->data;
1088 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1089 if (fel->linkname[0] == '/') {
1090 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1091 continue;
1092 strcpy (tmp, fel->linkname);
1093 } else {
1094 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1095 continue;
1096 strcpy (tmp, dir->remote_path);
1097 if (tmp[1] != '\0')
1098 strcat (tmp, "/");
1099 strcat (tmp + 1, fel->linkname);
1101 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1102 canonicalize_pathname (tmp);
1103 fe = _get_file_entry(bucket, tmp, 0, 0);
1104 if (fe) {
1105 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1106 /* Symlink points to link which isn't resolved, yet. */
1107 if (fe->linkname[0] == '/') {
1108 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1109 break;
1110 strcpy (tmp, fe->linkname);
1111 } else {
1112 /* at this point tmp looks always like this
1113 /directory/filename, i.e. no need to check
1114 strrchr's return value */
1115 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1116 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1117 break;
1118 strcat (tmp, fe->linkname);
1120 continue;
1121 } else {
1122 fel->l_stat = g_new (struct stat, 1);
1123 if ( S_ISLNK (fe->s.st_mode))
1124 *fel->l_stat = *fe->l_stat;
1125 else
1126 *fel->l_stat = fe->s;
1127 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1130 break;
1134 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1137 static void
1138 resolve_symlink_with_ls_options(vfs *me, vfs_s_super *super, vfs_s_inode *dir)
1140 char buffer[2048] = "", *filename;
1141 int sock;
1142 FILE *fp;
1143 struct stat s;
1144 struct linklist *flist;
1145 struct direntry *fe;
1146 int switch_method = 0;
1148 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1149 if (strchr (dir->remote_path, ' ')) {
1150 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1151 print_vfs_message(_("ftpfs: CWD failed."));
1152 return;
1154 sock = open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1156 else
1157 sock = open_data_connection (bucket, "LIST -lLa",
1158 dir->remote_path, TYPE_ASCII, 0);
1160 if (sock == -1) {
1161 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1162 return;
1165 fp = fdopen(sock, "r");
1166 if (fp == NULL) {
1167 close(sock);
1168 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1169 return;
1171 enable_interrupt_key();
1172 flist = dir->file_list->next;
1173 while (1) {
1174 do {
1175 if (flist == dir->file_list)
1176 goto done;
1177 fe = flist->data;
1178 flist = flist->next;
1179 } while (!S_ISLNK(fe->s.st_mode));
1180 while (1) {
1181 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1182 goto done;
1183 if (logfile){
1184 fputs (buffer, logfile);
1185 fflush (logfile);
1187 vfs_die("This code should be commented out\n");
1188 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1189 int r = strcmp(fe->name, filename);
1190 g_free(filename);
1191 if (r == 0) {
1192 if (S_ISLNK (s.st_mode)) {
1193 /* This server doesn't understand LIST -lLa */
1194 switch_method = 1;
1195 goto done;
1197 fe->l_stat = g_new (struct stat, 1);
1198 if (fe->l_stat == NULL)
1199 goto done;
1200 *fe->l_stat = s;
1201 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1202 break;
1204 if (r < 0)
1205 break;
1209 done:
1210 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1211 disable_interrupt_key();
1212 fclose(fp);
1213 get_reply(me, SUP.sock, NULL, 0);
1216 static void
1217 resolve_symlink(vfs *me, vfs_s_super *super, vfs_s_inode *dir)
1219 print_vfs_message(_("Resolving symlink..."));
1221 if (SUP.strict_rfc959_list_cmd)
1222 resolve_symlink_without_ls_options(me, super, dir);
1223 else
1224 resolve_symlink_with_ls_options(me, super, dir);
1226 #endif
1228 static int
1229 dir_load(vfs *me, vfs_s_inode *dir, char *remote_path)
1231 vfs_s_entry *ent;
1232 vfs_s_super *super = dir->super;
1233 int sock, num_entries = 0;
1234 #ifdef FIXME_LATER
1235 int has_symlinks = 0;
1236 #endif
1237 char buffer[BUF_8K];
1238 int cd_first;
1240 cd_first = ftpfs_first_cd_then_ls || (strchr (remote_path, ' ') != NULL)
1241 || (SUP.strict == RFC_STRICT);
1243 again:
1244 print_vfs_message(_("ftpfs: Reading FTP directory %s... %s%s"), remote_path,
1245 SUP.strict == RFC_STRICT ? _("(strict rfc959)") : "",
1246 cd_first ? _("(chdir first)") : "");
1248 if (cd_first) {
1249 char *p;
1251 p = translate_path (me, super, remote_path);
1253 if (ftpfs_chdir_internal (me, super, p) != COMPLETE) {
1254 g_free (p);
1255 my_errno = ENOENT;
1256 print_vfs_message(_("ftpfs: CWD failed."));
1257 return -1;
1259 g_free (p);
1262 gettimeofday(&dir->u.ftp.timestamp, NULL);
1263 dir->u.ftp.timestamp.tv_sec += ftpfs_directory_timeout;
1265 if (SUP.strict == RFC_STRICT)
1266 sock = open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1267 else if (cd_first)
1268 /* Dirty hack to avoid autoprepending / to . */
1269 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1270 sock = open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1271 else {
1272 /* Trailing "/." is necessary if remote_path is a symlink */
1273 char *path = concat_dir_and_file (remote_path, ".");
1274 sock = open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1275 g_free (path);
1278 if (sock == -1)
1279 goto fallback;
1281 /* Clear the interrupt flag */
1282 enable_interrupt_key ();
1284 #if 1
1286 /* added 20001006 by gisburn
1287 * add dots '.' and '..'. This must be _executed_ before scanning the dir as the
1288 * code below may jump directly into error handling code (without executing
1289 * remaining code). And C doesn't have try {...} finally {}; :-)
1291 vfs_s_inode *parent = dir->ent->dir;
1293 if( parent==NULL )
1294 parent = dir;
1296 ent = vfs_s_generate_entry(me, ".", dir, 0);
1297 ent->ino->st=dir->st;
1298 num_entries++;
1299 vfs_s_insert_entry(me, dir, ent);
1301 ent = vfs_s_generate_entry(me, "..", parent, 0);
1302 ent->ino->st=parent->st;
1303 num_entries++;
1304 vfs_s_insert_entry(me, dir, ent);
1306 #endif
1308 while (1) {
1309 int i;
1310 int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), sock);
1311 if (!res)
1312 break;
1314 if (res == EINTR) {
1315 me->verrno = ECONNRESET;
1316 close (sock);
1317 disable_interrupt_key();
1318 get_reply(me, SUP.sock, NULL, 0);
1319 print_vfs_message (_("%s: failure"), me->name);
1320 return -1;
1323 if (logfile){
1324 fputs (buffer, logfile);
1325 fputs ("\n", logfile);
1326 fflush (logfile);
1329 ent = vfs_s_generate_entry(me, NULL, dir, 0);
1330 i = ent->ino->st.st_nlink;
1331 if (!vfs_parse_ls_lga (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1332 vfs_s_free_entry (me, ent);
1333 continue;
1335 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1336 num_entries++;
1337 if ((!strcmp(ent->name, ".")) || (!strcmp (ent->name, ".."))) {
1338 g_free (ent->name);
1339 ent->name = NULL; /* Ouch, vfs_s_free_entry "knows" about . and .. being special :-( */
1340 vfs_s_free_entry (me, ent);
1341 continue;
1344 vfs_s_insert_entry(me, dir, ent);
1347 /* vfs_s_add_dots(me, dir, NULL);
1348 FIXME This really should be here; but we need to provide correct parent.
1349 Disabled for now, please fix it. Pavel
1351 close(sock);
1352 me->verrno = E_REMOTE;
1353 if ((get_reply (me, SUP.sock, NULL, 0) != COMPLETE) || !num_entries)
1354 goto fallback;
1356 if (SUP.strict == RFC_AUTODETECT)
1357 SUP.strict = RFC_DARING;
1359 #ifdef FIXME_LATER
1360 if (has_symlinks) {
1361 if (resolve_symlinks)
1362 resolve_symlink(me, super, dcache);
1363 else
1364 dcache->symlink_status = FTPFS_UNRESOLVED_SYMLINKS;
1366 #endif
1367 print_vfs_message (_("%s: done."), me->name);
1368 return 0;
1370 fallback:
1371 if (SUP.strict == RFC_AUTODETECT) {
1372 /* It's our first attempt to get a directory listing from this
1373 server (UNIX style LIST command) */
1374 SUP.strict = RFC_STRICT;
1375 /* I hate goto, but recursive call needs another 8K on stack */
1376 /* return dir_load (me, dir, remote_path); */
1377 cd_first = 1;
1378 goto again;
1380 print_vfs_message(_("ftpfs: failed; nowhere to fallback to"));
1381 ERRNOR(-1, EACCES);
1384 static int
1385 file_store(vfs *me, vfs_s_fh *fh, char *name, char *localname)
1387 int h, sock, n;
1388 off_t total;
1389 #ifdef HAVE_STRUCT_LINGER
1390 struct linger li;
1391 #else
1392 int flag_one = 1;
1393 #endif
1394 char buffer[8192];
1395 struct stat s;
1396 vfs_s_super *super = FH_SUPER;
1398 h = open(localname, O_RDONLY);
1399 if (h == -1)
1400 ERRNOR (EIO, -1);
1401 fstat(h, &s);
1402 sock = open_data_connection(me, super, fh->u.ftp.append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1403 if (sock < 0) {
1404 close(h);
1405 return -1;
1407 #ifdef HAVE_STRUCT_LINGER
1408 li.l_onoff = 1;
1409 li.l_linger = 120;
1410 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));
1411 #else
1412 setsockopt(sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1413 #endif
1414 total = 0;
1416 enable_interrupt_key();
1417 while (1) {
1418 while ((n = read(h, buffer, sizeof(buffer))) < 0) {
1419 if (errno == EINTR) {
1420 if (got_interrupt()) {
1421 my_errno = EINTR;
1422 goto error_return;
1424 else
1425 continue;
1427 my_errno = errno;
1428 goto error_return;
1430 if (n == 0)
1431 break;
1432 while (write(sock, buffer, n) < 0) {
1433 if (errno == EINTR) {
1434 if (got_interrupt()) {
1435 my_errno = EINTR;
1436 goto error_return;
1438 else
1439 continue;
1441 my_errno = errno;
1442 goto error_return;
1444 total += n;
1445 print_vfs_message(_("ftpfs: storing file %lu (%lu)"),
1446 (unsigned long) total, (unsigned long) s.st_size);
1448 disable_interrupt_key();
1449 close(sock);
1450 close(h);
1451 if (get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1452 ERRNOR (EIO, -1);
1453 return 0;
1454 error_return:
1455 disable_interrupt_key();
1456 close(sock);
1457 close(h);
1458 get_reply(me, SUP.sock, NULL, 0);
1459 return -1;
1462 //#define FH_SOCK fh->u.ftp.sock
1464 static int
1465 linear_start(vfs *me, vfs_s_fh *fh, int offset)
1467 char *name = vfs_s_fullpath (me, fh->ino);
1469 if (!name)
1470 return 0;
1471 FH_SOCK = open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1472 g_free (name);
1473 if (FH_SOCK == -1)
1474 ERRNOR (EACCES, 0);
1475 fh->linear = LS_LINEAR_OPEN;
1476 FH_SUPER->u.ftp.control_connection_buzy = 1;
1477 fh->u.ftp.append = 0;
1478 return 1;
1481 static int
1482 linear_read (vfs *me, vfs_s_fh *fh, void *buf, int len)
1484 int n;
1485 vfs_s_super *super = FH_SUPER;
1487 while ((n = read (FH_SOCK, buf, len))<0) {
1488 if ((errno == EINTR) && !got_interrupt())
1489 continue;
1490 break;
1493 if (n<0)
1494 linear_abort(me, fh);
1496 if (!n) {
1497 SUP.control_connection_buzy = 0;
1498 close (FH_SOCK);
1499 FH_SOCK = -1;
1500 if ((get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1501 ERRNOR (E_REMOTE, -1);
1502 return 0;
1504 ERRNOR (errno, n);
1507 static void
1508 linear_close (vfs *me, vfs_s_fh *fh)
1510 if (FH_SOCK != -1)
1511 linear_abort(me, fh);
1514 static int ftpfs_ctl (void *fh, int ctlop, int arg)
1516 switch (ctlop) {
1517 case MCCTL_IS_NOTREADY:
1519 int v;
1521 if (!FH->linear)
1522 vfs_die ("You may not do this");
1523 if (FH->linear == LS_LINEAR_CLOSED)
1524 return 0;
1526 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1527 if (((v < 0) && (errno == EINTR)) || v == 0)
1528 return 1;
1529 return 0;
1531 default:
1532 return 0;
1536 /* Warning: filename passed to this command is damaged */
1537 static int
1538 send_ftp_command(vfs *me, char *filename, char *cmd, int flags)
1540 char *rpath, *p;
1541 vfs_s_super *super;
1542 int r;
1543 int flush_directory_cache = (flags & OPT_FLUSH);
1545 if (!(rpath = vfs_s_get_path_mangle(me, filename, &super, 0)))
1546 return -1;
1547 p = translate_path (me, super, rpath);
1548 r = command (me, super, WAIT_REPLY, cmd, p);
1549 g_free (p);
1550 vfs_add_noncurrent_stamps (&vfs_ftpfs_ops, (vfsid) super, NULL);
1551 if (flags & OPT_IGNORE_ERROR)
1552 r = COMPLETE;
1553 if (r != COMPLETE)
1554 ERRNOR (EPERM, -1);
1555 if (flush_directory_cache)
1556 vfs_s_invalidate(me, super);
1557 return 0;
1560 /* This routine is called as the last step in load_setup */
1561 void
1562 ftpfs_init_passwd(void)
1564 ftpfs_anonymous_passwd = load_anon_passwd ();
1565 if (ftpfs_anonymous_passwd)
1566 return;
1568 /* If there is no anonymous ftp password specified
1569 * then we'll just use anonymous@
1570 * We don't send any other thing because:
1571 * - We want to remain anonymous
1572 * - We want to stop SPAM
1573 * - We don't want to let ftp sites to discriminate by the user,
1574 * host or country.
1576 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1579 static int ftpfs_chmod (vfs *me, char *path, int mode)
1581 char buf[BUF_SMALL];
1583 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1584 return send_ftp_command(me, path, buf, OPT_FLUSH);
1587 static int ftpfs_chown (vfs *me, char *path, int owner, int group)
1589 #if 0
1590 my_errno = EPERM;
1591 return -1;
1592 #else
1593 /* Everyone knows it is not possible to chown remotely, so why bother them.
1594 If someone's root, then copy/move will always try to chown it... */
1595 return 0;
1596 #endif
1599 static int ftpfs_unlink (vfs *me, char *path)
1601 return send_ftp_command(me, path, "DELE /%s", OPT_FLUSH);
1604 /* Return 1 if path is the same directory as the one we are in now */
1605 static int
1606 is_same_dir (vfs *me, vfs_s_super *super, const char *path)
1608 if (!SUP.cwdir)
1609 return 0;
1610 if (strcmp (path, SUP.cwdir) == 0)
1611 return 1;
1612 return 0;
1615 static int
1616 ftpfs_chdir_internal (vfs *me, vfs_s_super *super, char *remote_path)
1618 int r;
1619 char *p;
1621 if (!SUP.cwd_defered && is_same_dir (me, super, remote_path))
1622 return COMPLETE;
1624 p = translate_path (me, super, remote_path);
1625 r = command (me, super, WAIT_REPLY, "CWD /%s", p);
1626 g_free (p);
1628 if (r != COMPLETE) {
1629 my_errno = EIO;
1630 } else {
1631 g_free(SUP.cwdir);
1632 SUP.cwdir = g_strdup (remote_path);
1633 SUP.cwd_defered = 0;
1635 return r;
1638 static int ftpfs_rename (vfs *me, char *path1, char *path2)
1640 send_ftp_command(me, path1, "RNFR /%s", OPT_FLUSH);
1641 return send_ftp_command(me, path2, "RNTO /%s", OPT_FLUSH);
1644 static int ftpfs_mkdir (vfs *me, char *path, mode_t mode)
1646 return send_ftp_command(me, path, "MKD /%s", OPT_FLUSH);
1649 static int ftpfs_rmdir (vfs *me, char *path)
1651 return send_ftp_command(me, path, "RMD /%s", OPT_FLUSH);
1654 static int ftpfs_fh_open (vfs *me, vfs_s_fh *fh, int flags, int mode)
1656 fh->u.ftp.append = 0;
1657 /* File will be written only, so no need to retrieve it from ftp server */
1658 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY|O_RDWR))){
1659 #ifdef HAVE_STRUCT_LINGER
1660 struct linger li;
1661 #else
1662 int li = 1;
1663 #endif
1664 char * name;
1666 /* linear_start() called, so data will be written
1667 * to local temporary file and stored to ftp server
1668 * by vfs_s_close later
1670 if (FH_SUPER->u.ftp.control_connection_buzy){
1671 if (!fh->ino->localname){
1672 int handle = mc_mkstemps (&fh->ino->localname, me->name, NULL);
1673 if (handle == -1)
1674 return -1;
1675 close (handle);
1676 fh->u.ftp.append = flags & O_APPEND;
1678 return 0;
1680 name = vfs_s_fullpath (me, fh->ino);
1681 if (!name)
1682 return -1;
1683 fh->handle = open_data_connection(me, fh->ino->super,
1684 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1685 g_free (name);
1687 if (fh->handle < 0)
1688 return -1;
1689 #ifdef HAVE_STRUCT_LINGER
1690 li.l_onoff = 1;
1691 li.l_linger = 120;
1692 #endif
1693 setsockopt(fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof(li));
1695 if (fh->ino->localname){
1696 unlink (fh->ino->localname);
1697 g_free (fh->ino->localname);
1698 fh->ino->localname = NULL;
1700 return 0;
1703 if (!fh->ino->localname)
1704 if (vfs_s_retrieve_file (me, fh->ino)==-1)
1705 return -1;
1706 if (!fh->ino->localname)
1707 vfs_die( "retrieve_file failed to fill in localname" );
1708 return 0;
1711 static int ftpfs_fh_close (vfs *me, vfs_s_fh *fh)
1713 if (fh->handle != -1 && !fh->ino->localname){
1714 close (fh->handle);
1715 fh->handle = -1;
1716 /* File is stored to destination already, so
1717 * we prevent MEDATA->file_store() call from vfs_s_close ()
1719 fh->changed = 0;
1720 if (get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1721 ERRNOR (EIO, -1);
1722 vfs_s_invalidate (me, FH_SUPER);
1724 return 0;
1727 static struct vfs_s_data ftp_data = {
1728 NULL,
1731 NULL, /* logfile */
1733 NULL, /* init_inode */
1734 NULL, /* free_inode */
1735 NULL, /* init_entry */
1737 NULL, /* archive_check */
1738 archive_same,
1739 open_archive,
1740 free_archive,
1742 ftpfs_fh_open, /* fh_open */
1743 ftpfs_fh_close, /* fh_close */
1745 vfs_s_find_entry_linear,
1746 dir_load,
1747 dir_uptodate,
1748 file_store,
1750 linear_start,
1751 linear_read,
1752 linear_close
1755 static void
1756 ftpfs_fill_names (vfs *me, void (*func)(char *))
1758 struct vfs_s_super * super = ftp_data.supers;
1759 char *name;
1761 while (super){
1762 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, NULL);
1763 (*func)(name);
1764 g_free (name);
1765 super = super->next;
1769 vfs vfs_ftpfs_ops = {
1770 NULL, /* This is place of next pointer */
1771 "ftpfs",
1772 F_NET, /* flags */
1773 "ftp:", /* prefix */
1774 &ftp_data, /* data */
1775 0, /* errno */
1776 NULL, /* init */
1777 NULL, /* done */
1778 ftpfs_fill_names,
1779 NULL,
1781 vfs_s_open,
1782 vfs_s_close,
1783 vfs_s_read,
1784 vfs_s_write,
1786 vfs_s_opendir,
1787 vfs_s_readdir,
1788 vfs_s_closedir,
1789 vfs_s_telldir,
1790 vfs_s_seekdir,
1792 vfs_s_stat,
1793 vfs_s_lstat,
1794 vfs_s_fstat,
1796 ftpfs_chmod,
1797 ftpfs_chown, /* not really implemented but returns success */
1798 NULL,
1800 vfs_s_readlink,
1801 NULL,
1802 NULL,
1803 ftpfs_unlink,
1805 ftpfs_rename,
1806 vfs_s_chdir,
1807 vfs_s_ferrno,
1808 vfs_s_lseek,
1809 NULL,
1811 vfs_s_getid,
1812 vfs_s_nothingisopen,
1813 vfs_s_free,
1815 NULL,
1816 NULL,
1818 ftpfs_mkdir,
1819 ftpfs_rmdir,
1820 ftpfs_ctl,
1821 vfs_s_setctl
1823 MMAPNULL
1826 void ftpfs_set_debug (const char *file)
1828 logfile = fopen (file, "w+");
1829 if (logfile)
1830 ftp_data.logfile = logfile;
1833 static char buffer[BUF_MEDIUM];
1834 static char *netrc, *netrcp;
1836 /* This should match the keywords[] array below */
1837 typedef enum {
1838 NETRC_NONE = 0,
1839 NETRC_DEFAULT,
1840 NETRC_MACHINE,
1841 NETRC_LOGIN,
1842 NETRC_PASSWORD,
1843 NETRC_PASSWD,
1844 NETRC_ACCOUNT,
1845 NETRC_MACDEF,
1846 NETRC_UNKNOWN
1847 } keyword_t;
1849 static keyword_t netrc_next (void)
1851 char *p;
1852 keyword_t i;
1853 static const char *const keywords[] = { "default", "machine",
1854 "login", "password", "passwd", "account", "macdef", NULL
1858 while (1) {
1859 netrcp = skip_separators (netrcp);
1860 if (*netrcp != '\n')
1861 break;
1862 netrcp++;
1864 if (!*netrcp)
1865 return NETRC_NONE;
1866 p = buffer;
1867 if (*netrcp == '"') {
1868 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1869 if (*netrcp == '\\')
1870 netrcp++;
1871 *p++ = *netrcp;
1873 } else {
1874 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1875 *netrcp != ',' && *netrcp; netrcp++) {
1876 if (*netrcp == '\\')
1877 netrcp++;
1878 *p++ = *netrcp;
1881 *p = 0;
1882 if (!*buffer)
1883 return 0;
1885 i = NETRC_DEFAULT;
1886 while (keywords[i - 1]) {
1887 if (!strcmp (keywords[i - 1], buffer))
1888 return i;
1890 i++;
1893 return NETRC_UNKNOWN;
1896 static int netrc_has_incorrect_mode (char *netrcname, char *netrc)
1898 static int be_angry = 1;
1899 struct stat mystat;
1901 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1902 if (be_angry) {
1903 message_1s (1, MSG_ERROR,
1904 _("~/.netrc file has not correct mode.\n"
1905 "Remove password or correct mode."));
1906 be_angry = 0;
1908 return 1;
1910 return 0;
1913 /* Scan .netrc until we find matching "machine" or "default"
1914 * domain is used for additional matching
1915 * No search is done after "default" in compliance with "man netrc"
1916 * Return 0 if found, -1 otherwise */
1917 static int find_machine (const char *host, const char *domain)
1919 keyword_t keyword;
1921 while ((keyword = netrc_next ()) != NETRC_NONE) {
1922 if (keyword == NETRC_DEFAULT)
1923 return 0;
1925 if (keyword == NETRC_MACDEF) {
1926 /* Scan for an empty line, which concludes "macdef" */
1927 do {
1928 while (*netrcp && *netrcp != '\n')
1929 netrcp++;
1930 if (*netrcp != '\n')
1931 break;
1932 netrcp++;
1933 } while (*netrcp && *netrcp != '\n');
1934 continue;
1937 if (keyword != NETRC_MACHINE)
1938 continue;
1940 /* Take machine name */
1941 if (netrc_next () == NETRC_NONE)
1942 break;
1944 if (g_strcasecmp (host, buffer)) {
1945 /* Try adding our domain to short names in .netrc */
1946 char *host_domain = strchr (host, '.');
1947 if (!host_domain)
1948 continue;
1950 /* Compare domain part */
1951 if (g_strcasecmp (host_domain, domain))
1952 continue;
1954 /* Compare local part */
1955 if (g_strncasecmp (host, buffer, host_domain - host))
1956 continue;
1959 return 0;
1962 /* end of .netrc */
1963 return -1;
1966 /* Extract login and password from .netrc for the host.
1967 * pass may be NULL.
1968 * Returns 0 for success, -1 for error */
1969 static int lookup_netrc (const char *host, char **login, char **pass)
1971 char *netrcname;
1972 char *tmp_pass = NULL;
1973 char hostname[MAXHOSTNAMELEN], *domain;
1974 keyword_t keyword;
1975 static struct rupcache {
1976 struct rupcache *next;
1977 char *host;
1978 char *login;
1979 char *pass;
1980 } *rup_cache = NULL, *rupp;
1982 /* Initialize *login and *pass */
1983 if (!login)
1984 return 0;
1985 *login = NULL;
1986 if (pass)
1987 *pass = NULL;
1989 /* Look up in the cache first */
1990 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
1991 if (!strcmp (host, rupp->host)) {
1992 if (rupp->login)
1993 *login = g_strdup (rupp->login);
1994 if (pass && rupp->pass)
1995 *pass = g_strdup (rupp->pass);
1996 return 0;
2000 /* Load current .netrc */
2001 netrcname = concat_dir_and_file (home_dir, ".netrc");
2002 netrcp = netrc = load_file (netrcname);
2003 if (netrc == NULL) {
2004 g_free (netrcname);
2005 return 0;
2008 /* Find our own domain name */
2009 if (gethostname (hostname, sizeof (hostname)) < 0)
2010 *hostname = 0;
2011 if (!(domain = strchr (hostname, '.')))
2012 domain = "";
2014 /* Scan for "default" and matching "machine" keywords */
2015 find_machine (host, domain);
2017 /* Scan for keywords following "default" and "machine" */
2018 while (1) {
2019 int need_break = 0;
2020 keyword = netrc_next ();
2022 switch (keyword) {
2023 case NETRC_LOGIN:
2024 if (netrc_next () == NETRC_NONE) {
2025 need_break = 1;
2026 break;
2029 /* We have another name already - should not happen */
2030 if (*login) {
2031 need_break = 1;
2032 break;
2035 /* We have login name now */
2036 *login = g_strdup (buffer);
2037 break;
2039 case NETRC_PASSWORD:
2040 case NETRC_PASSWD:
2041 if (netrc_next () == NETRC_NONE) {
2042 need_break = 1;
2043 break;
2046 /* Ignore unsafe passwords */
2047 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2048 && netrc_has_incorrect_mode (netrcname, netrc)) {
2049 need_break = 1;
2050 break;
2053 /* Remember password. pass may be NULL, so use tmp_pass */
2054 if (tmp_pass == NULL)
2055 tmp_pass = g_strdup (buffer);
2056 break;
2058 case NETRC_ACCOUNT:
2059 /* "account" is followed by a token which we ignore */
2060 if (netrc_next () == NETRC_NONE) {
2061 need_break = 1;
2062 break;
2065 /* Ignore account, but warn user anyways */
2066 netrc_has_incorrect_mode (netrcname, netrc);
2067 break;
2069 default:
2070 /* Unexpected keyword or end of file */
2071 need_break = 1;
2072 break;
2075 if (need_break)
2076 break;
2079 g_free (netrc);
2080 g_free (netrcname);
2082 rupp = g_new (struct rupcache, 1);
2083 rupp->host = g_strdup (host);
2084 rupp->login = rupp->pass = 0;
2086 if (*login != NULL) {
2087 rupp->login = g_strdup (*login);
2089 if (tmp_pass != NULL)
2090 rupp->pass = g_strdup (tmp_pass);
2091 rupp->next = rup_cache;
2092 rup_cache = rupp;
2094 if (pass)
2095 *pass = tmp_pass;
2097 return 0;