Committed some stilistic changes to the italian manual pages.
[midnight-commander.git] / vfs / ftpfs.c
blob5254f7217ecc28248926d50970a2a01656045470
1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Ching Hui
5 1995 Jakub Jelinek
6 1995, 1996, 1997 Miguel de Icaza
7 1997 Norbert Warmuth
8 1998 Pavel Machek
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public License
12 as published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 /* FTPfs TODO:
26 - make it more robust - all the connects etc. should handle EADDRINUSE and
27 ERETRY (have I spelled these names correctly?)
28 - make the user able to flush a connection - all the caches will get empty
29 etc., (tarfs as well), we should give there a user selectable timeout
30 and assign a key sequence.
31 - use hash table instead of linklist to cache ftpfs directory.
33 What to do with this?
36 * NOTE: Usage of tildes is deprecated, consider:
37 * cd /#ftp:pavel@hobit
38 * cd ~
39 * And now: what do I want to do? Do I want to go to /home/pavel or to
40 * /#ftp:hobit/home/pavel? I think first has better sense...
43 int f = !strcmp( remote_path, "/~" );
44 if (f || !strncmp( remote_path, "/~/", 3 )) {
45 char *s;
46 s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
47 g_free (remote_path);
48 remote_path = s;
55 /* Namespace pollution: horrible */
57 #include <config.h>
58 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
59 #include <netdb.h> /* struct hostent */
60 #include <sys/socket.h> /* AF_INET */
61 #include <netinet/in.h> /* struct in_addr */
62 #ifdef HAVE_SETSOCKOPT
63 # include <netinet/ip.h> /* IP options */
64 #endif
65 #ifdef HAVE_ARPA_INET_H
66 #include <arpa/inet.h>
67 #endif
68 #include <arpa/ftp.h>
69 #include <arpa/telnet.h>
70 #include <sys/param.h>
72 #include "utilvfs.h"
74 #include "xdirentry.h"
75 #include "vfs.h"
76 #include "tcputil.h"
77 #include "../src/dialog.h"
78 #include "../src/setup.h" /* for load_anon_passwd */
79 #include "ftpfs.h"
80 #ifndef MAXHOSTNAMELEN
81 # define MAXHOSTNAMELEN 64
82 #endif
84 #define UPLOAD_ZERO_LENGTH_FILE
85 #define SUP super->u.ftp
86 #define FH_SOCK fh->u.ftp.sock
88 #ifndef INADDR_NONE
89 #define INADDR_NONE 0xffffffff
90 #endif
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 /* 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, const 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
217 static void
218 ftp_split_url(char *path, char **host, char **user, int *port, char **pass)
220 char *p;
222 p = vfs_split_url (path, host, user, port, pass, FTP_COMMAND_PORT,
223 URL_ALLOW_ANON);
225 if (!*user) {
226 /* Look up user and password in netrc */
227 if (use_netrc)
228 lookup_netrc (*host, user, pass);
229 if (!*user)
230 *user = g_strdup ("anonymous");
233 /* Look up password in netrc for known user */
234 if (use_netrc && *user && pass && !*pass) {
235 char *new_user;
237 lookup_netrc (*host, &new_user, pass);
239 /* If user is different, remove password */
240 if (new_user && strcmp (*user, new_user)) {
241 g_free (*pass);
242 *pass = NULL;
245 g_free (new_user);
248 if (p)
249 g_free (p);
252 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
253 static int
254 get_reply (vfs *me, int sock, char *string_buf, int string_len)
256 char answer[BUF_1K];
257 int i;
259 for (;;) {
260 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')){
261 if (string_buf)
262 *string_buf = 0;
263 code = 421;
264 return 4;
266 switch (sscanf(answer, "%d", &code)){
267 case 0:
268 if (string_buf) {
269 strncpy (string_buf, answer, string_len - 1);
270 *(string_buf + string_len - 1) = 0;
272 code = 500;
273 return 5;
274 case 1:
275 if (answer[3] == '-') {
276 while (1) {
277 if (!vfs_s_get_line (me, sock, answer, sizeof(answer), '\n')){
278 if (string_buf)
279 *string_buf = 0;
280 code = 421;
281 return 4;
283 if ((sscanf (answer, "%d", &i) > 0) &&
284 (code == i) && (answer[3] == ' '))
285 break;
288 if (string_buf){
289 strncpy (string_buf, answer, string_len - 1);
290 *(string_buf + string_len - 1) = 0;
292 return code / 100;
297 static int
298 reconnect (vfs *me, vfs_s_super *super)
300 int sock = ftpfs_open_socket (me, super);
301 if (sock != -1){
302 char *cwdir = SUP.cwdir;
303 close (SUP.sock);
304 SUP.sock = sock;
305 SUP.cwdir = NULL;
306 if (login_server (me, super, SUP.password)){
307 if (!cwdir)
308 return 1;
309 sock = ftpfs_chdir_internal (me, super, cwdir);
310 g_free (cwdir);
311 return sock == COMPLETE;
313 SUP.cwdir = cwdir;
315 return 0;
318 static int
319 command (vfs *me, vfs_s_super *super, int wait_reply, const char *fmt, ...)
321 va_list ap;
322 char *str, *fmt_str;
323 int status;
324 int sock = SUP.sock;
326 va_start (ap, fmt);
327 fmt_str = g_strdup_vprintf (fmt, ap);
328 va_end (ap);
330 status = strlen (fmt_str);
331 str = g_realloc (fmt_str, status + 3);
332 strcpy (str + status, "\r\n");
334 if (logfile){
335 if (strncmp (str, "PASS ", 5) == 0){
336 fputs ("PASS <Password not logged>\r\n", logfile);
337 } else
338 fwrite (str, status + 2, 1, logfile);
340 fflush (logfile);
343 got_sigpipe = 0;
344 enable_interrupt_key ();
345 status = write (SUP.sock, str, status + 2);
347 if (status < 0){
348 code = 421;
350 if (errno == EPIPE){ /* Remote server has closed connection */
351 static int level = 0; /* login_server() use command() */
352 if (level == 0){
353 level = 1;
354 status = reconnect (me, super);
355 level = 0;
356 if (status && write (SUP.sock, str, status + 2) > 0)
357 goto ok;
359 got_sigpipe = 1;
361 g_free (str);
362 disable_interrupt_key ();
363 return TRANSIENT;
366 g_free (str);
367 disable_interrupt_key ();
369 if (wait_reply)
370 return get_reply (me, sock, (wait_reply & WANT_STRING) ? reply_str : NULL, sizeof (reply_str)-1);
371 return COMPLETE;
374 static void
375 free_archive (vfs *me, vfs_s_super *super)
377 if (SUP.sock != -1){
378 print_vfs_message (_("ftpfs: Disconnecting from %s"), SUP.host);
379 command(me, super, NONE, "QUIT");
380 close(SUP.sock);
382 g_free (SUP.host);
383 g_free (SUP.user);
384 g_free (SUP.cwdir);
385 g_free (SUP.password);
388 /* some defines only used by changetype */
389 /* These two are valid values for the second parameter */
390 #define TYPE_ASCII 0
391 #define TYPE_BINARY 1
393 /* This one is only used to initialize bucket->isbinary, don't use it as
394 second parameter to changetype. */
395 #define TYPE_UNKNOWN -1
397 static int
398 changetype (vfs *me, vfs_s_super *super, int binary)
400 if (binary != SUP.isbinary) {
401 if (command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
402 ERRNOR (EIO, -1);
403 SUP.isbinary = binary;
405 return binary;
408 /* This routine logs the user in */
409 static int
410 login_server (vfs *me, vfs_s_super *super, const char *netrcpass)
412 char *pass;
413 char *op;
414 char *name; /* login user name */
415 int anon = 0;
416 char reply_string[BUF_MEDIUM];
418 SUP.isbinary = TYPE_UNKNOWN;
419 if (netrcpass)
420 op = g_strdup (netrcpass);
421 else {
422 if (!strcmp (SUP.user, "anonymous") ||
423 !strcmp (SUP.user, "ftp")) {
424 if (!ftpfs_anonymous_passwd)
425 ftpfs_init_passwd();
426 op = g_strdup (ftpfs_anonymous_passwd);
427 anon = 1;
428 } else {
429 char *p;
431 if (!SUP.password){
432 p = g_strconcat (_(" FTP: Password required for "), SUP.user,
433 " ", NULL);
434 op = vfs_get_password (p);
435 g_free (p);
436 if (op == NULL)
437 ERRNOR (EPERM, 0);
438 SUP.password = g_strdup (op);
439 } else
440 op = g_strdup (SUP.password);
444 if (!anon || logfile)
445 pass = op;
446 else {
447 pass = g_strconcat ("-", op, NULL);
448 wipe_password (op);
451 /* Proxy server accepts: username@host-we-want-to-connect*/
452 if (SUP.proxy){
453 name = g_strconcat (SUP.user, "@",
454 SUP.host[0] == '!' ? SUP.host+1 : SUP.host, NULL);
455 } else
456 name = g_strdup (SUP.user);
458 if (get_reply (me, SUP.sock, reply_string, sizeof (reply_string) - 1) == COMPLETE) {
459 g_strup (reply_string);
460 SUP.remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
461 if (logfile) {
462 fprintf (logfile, "MC -- remote_is_amiga = %d\n", SUP.remote_is_amiga);
463 fflush (logfile);
466 print_vfs_message (_("ftpfs: sending login name"));
467 code = command (me, super, WAIT_REPLY, "USER %s", name);
469 switch (code){
470 case CONTINUE:
471 print_vfs_message (_("ftpfs: sending user password"));
472 if (command (me, super, WAIT_REPLY, "PASS %s", pass) != COMPLETE)
473 break;
475 case COMPLETE:
476 print_vfs_message (_("ftpfs: logged in"));
477 wipe_password (pass);
478 g_free (name);
479 return 1;
481 default:
482 SUP.failed_on_login = 1;
483 /* my_errno = E; */
484 if (SUP.password)
485 wipe_password (SUP.password);
486 SUP.password = 0;
488 goto login_fail;
491 message_2s (1, MSG_ERROR, _("ftpfs: Login incorrect for user %s "), SUP.user);
492 login_fail:
493 wipe_password (pass);
494 g_free (name);
495 ERRNOR (EPERM, 0);
498 #ifdef HAVE_SETSOCKOPT
499 static void
500 setup_source_route (int socket, int dest)
502 char buffer [20];
503 char *ptr = buffer;
505 if (!source_route)
506 return;
507 memset (buffer, 0, sizeof (buffer));
508 *ptr++ = IPOPT_LSRR;
509 *ptr++ = 3 + 8;
510 *ptr++ = 4; /* pointer */
512 /* First hop */
513 memcpy (ptr, (char *) &source_route, sizeof (int));
514 ptr += 4;
516 /* Second hop (ie, final destination) */
517 memcpy (ptr, (char *) &dest, sizeof (int));
518 ptr += 4;
519 while ((ptr - buffer) & 3)
520 ptr++;
521 if (setsockopt (socket, IPPROTO_IP, IP_OPTIONS,
522 buffer, ptr - buffer) < 0)
523 message_2s (1, MSG_ERROR, _(" Cannot set source routing (%s)"), unix_error_string (errno));
525 #else
526 #define setup_source_route(x,y)
527 #endif
529 static struct no_proxy_entry {
530 char *domain;
531 void *next;
532 } *no_proxy;
534 static void
535 load_no_proxy_list (void)
537 /* FixMe: shouldn't be hardcoded!!! */
538 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
539 struct no_proxy_entry *np, *current = 0;
540 FILE *npf;
541 int c;
542 char *p;
543 static char *mc_file;
545 if (mc_file)
546 return;
548 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
549 if (exist_file (mc_file) &&
550 (npf = fopen (mc_file, "r"))) {
551 while (fgets (s, sizeof(s), npf) || !(feof (npf) || ferror (npf))) {
552 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
553 while ((c = fgetc (npf)) != EOF && c != '\n')
555 continue;
558 if (p == s)
559 continue;
561 *p = '\0';
563 np = g_new (struct no_proxy_entry, 1);
564 np->domain = g_strdup (s);
565 np->next = NULL;
566 if (no_proxy)
567 current->next = np;
568 else
569 no_proxy = np;
570 current = np;
573 fclose (npf);
575 g_free (mc_file);
578 /* Return 1 if FTP proxy should be used for this host, 0 otherwise */
579 static int
580 ftpfs_check_proxy (const char *host)
582 struct no_proxy_entry *npe;
584 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
585 return 0; /* sanity check */
587 if (*host == '!')
588 return 1;
590 if (!ftpfs_always_use_proxy)
591 return 0;
593 if (!strchr (host, '.'))
594 return 0;
596 load_no_proxy_list ();
597 for (npe = no_proxy; npe; npe=npe->next) {
598 char *domain = npe->domain;
600 if (domain[0] == '.') {
601 int ld = strlen (domain);
602 int lh = strlen (host);
604 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
605 ld--;
606 lh--;
609 if (!ld)
610 return 0;
611 } else
612 if (!g_strcasecmp (host, domain))
613 return 0;
616 return 1;
619 static void
620 ftpfs_get_proxy_host_and_port (char *proxy, char **host, int *port)
622 char *user, *dir;
624 dir =
625 vfs_split_url (proxy, host, &user, port, 0, FTP_COMMAND_PORT,
626 URL_ALLOW_ANON);
627 g_free (user);
628 g_free (dir);
631 static int
632 ftpfs_open_socket (vfs *me, vfs_s_super *super)
634 struct sockaddr_in server_address;
635 struct hostent *hp;
636 int my_socket;
637 char *host;
638 int port = SUP.port;
639 int free_host = 0;
641 /* Use a proxy host? */
642 host = SUP.host;
644 if (!host || !*host){
645 print_vfs_message (_("ftpfs: Invalid host name."));
646 my_errno = EINVAL;
647 return -1;
650 /* Hosts to connect to that start with a ! should use proxy */
651 if (SUP.proxy){
652 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &port);
653 free_host = 1;
656 /* Get host address */
657 memset ((char *) &server_address, 0, sizeof (server_address));
658 server_address.sin_family = AF_INET;
659 server_address.sin_addr.s_addr = inet_addr (host);
660 if (server_address.sin_addr.s_addr == INADDR_NONE) {
661 hp = gethostbyname (host);
662 if (hp == NULL){
663 print_vfs_message (_("ftpfs: Invalid host address."));
664 my_errno = EINVAL;
665 if (free_host)
666 g_free (host);
667 return -1;
669 server_address.sin_family = hp->h_addrtype;
671 /* We copy only 4 bytes, we cannot trust hp->h_length, as it comes from the DNS */
672 memcpy ((char *) &server_address.sin_addr, (char *) hp->h_addr, 4);
675 server_address.sin_port = htons (port);
677 /* Connect */
678 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
679 my_errno = errno;
680 if (free_host)
681 g_free (host);
682 return -1;
684 setup_source_route (my_socket, server_address.sin_addr.s_addr);
686 print_vfs_message (_("ftpfs: making connection to %s"), host);
687 if (free_host)
688 g_free (host);
690 enable_interrupt_key (); /* clear the interrupt flag */
692 if (connect (my_socket, (struct sockaddr *) &server_address,
693 sizeof (server_address)) < 0){
694 my_errno = errno;
695 if (errno == EINTR && got_interrupt ())
696 print_vfs_message (_("ftpfs: connection interrupted by user"));
697 else
698 print_vfs_message (_("ftpfs: connection to server failed: %s"),
699 unix_error_string(errno));
700 disable_interrupt_key();
701 close (my_socket);
702 return -1;
704 disable_interrupt_key();
705 return my_socket;
708 static int
709 open_archive_int (vfs *me, vfs_s_super *super)
711 int retry_seconds, count_down;
713 /* We do not want to use the passive if we are using proxies */
714 if (SUP.proxy)
715 SUP.use_passive_connection = 0;
717 retry_seconds = 0;
718 do {
719 SUP.failed_on_login = 0;
721 SUP.sock = ftpfs_open_socket (me, super);
722 if (SUP.sock == -1)
723 return -1;
725 if (login_server (me, super, NULL)) {
726 /* Logged in, no need to retry the connection */
727 break;
728 } else {
729 if (SUP.failed_on_login){
730 /* Close only the socket descriptor */
731 close (SUP.sock);
732 } else {
733 return -1;
735 if (ftpfs_retry_seconds){
736 retry_seconds = ftpfs_retry_seconds;
737 enable_interrupt_key ();
738 for (count_down = retry_seconds; count_down; count_down--){
739 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
740 sleep (1);
741 if (got_interrupt ()){
742 /* my_errno = E; */
743 disable_interrupt_key ();
744 return 0;
747 disable_interrupt_key ();
750 } while (retry_seconds);
752 SUP.cwdir = ftpfs_get_current_directory (me, super);
753 if (!SUP.cwdir)
754 SUP.cwdir = g_strdup (PATH_SEP_STR);
755 return 0;
758 static int
759 open_archive (vfs *me, vfs_s_super *super, char *archive_name, char *op)
761 char *host, *user, *password;
762 int port;
764 ftp_split_url (strchr (op, ':') + 1, &host, &user, &port, &password);
766 SUP.host = host;
767 SUP.user = user;
768 SUP.port = port;
769 SUP.cwdir = NULL;
770 SUP.proxy = 0;
771 if (ftpfs_check_proxy (host))
772 SUP.proxy = ftpfs_proxy_host;
773 SUP.password = password;
774 SUP.use_passive_connection = ftpfs_use_passive_connections | source_route;
775 SUP.use_source_route = source_route;
776 SUP.strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
777 SUP.isbinary = TYPE_UNKNOWN;
778 SUP.remote_is_amiga = 0;
779 super->name = g_strdup("/");
780 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat(me, S_IFDIR | 0755));
782 return open_archive_int (me, super);
785 static int
786 archive_same(vfs *me, vfs_s_super *super, char *archive_name, char *op, void *cookie)
788 char *host, *user;
789 int port;
791 ftp_split_url (strchr(op, ':') + 1, &host, &user, &port, 0);
793 port = ((strcmp (host, SUP.host) == 0) &&
794 (strcmp (user, SUP.user) == 0) &&
795 (port == SUP.port));
797 g_free (host);
798 g_free (user);
800 return port;
803 void
804 ftpfs_flushdir (void)
806 force_expiration = 1;
809 static int
810 dir_uptodate(vfs *me, vfs_s_inode *ino)
812 struct timeval tim;
814 if (force_expiration) {
815 force_expiration = 0;
816 return 0;
818 gettimeofday(&tim, NULL);
819 if (tim.tv_sec < ino->u.ftp.timestamp.tv_sec)
820 return 1;
821 return 0;
824 /* The returned directory should always contain a trailing slash */
825 static char *
826 ftpfs_get_current_directory (vfs *me, vfs_s_super *super)
828 char buf[BUF_8K], *bufp, *bufq;
830 if (command (me, super, NONE, "PWD") == COMPLETE &&
831 get_reply(me, SUP.sock, buf, sizeof(buf)) == COMPLETE) {
832 bufp = NULL;
833 for (bufq = buf; *bufq; bufq++)
834 if (*bufq == '"') {
835 if (!bufp) {
836 bufp = bufq + 1;
837 } else {
838 *bufq = 0;
839 if (*bufp) {
840 if (*(bufq - 1) != '/') {
841 *bufq++ = '/';
842 *bufq = 0;
844 if (*bufp == '/')
845 return g_strdup (bufp);
846 else {
847 /* If the remote server is an Amiga a leading slash
848 might be missing. MC needs it because it is used
849 as seperator between hostname and path internally. */
850 return g_strconcat( "/", bufp, 0);
852 } else {
853 my_errno = EIO;
854 return NULL;
859 my_errno = EIO;
860 return NULL;
864 /* Setup Passive ftp connection, we use it for source routed connections */
865 static int
866 setup_passive (vfs *me, vfs_s_super *super, int my_socket, struct sockaddr_in *sa)
868 int xa, xb, xc, xd, xe, xf;
869 char n [6];
870 char *c = reply_str;
872 if (command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
873 return 0;
875 /* Parse remote parameters */
876 for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++)
878 if (!*c)
879 return 0;
880 if (!isdigit ((unsigned char) *c))
881 return 0;
882 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
883 return 0;
884 n [0] = (unsigned char) xa;
885 n [1] = (unsigned char) xb;
886 n [2] = (unsigned char) xc;
887 n [3] = (unsigned char) xd;
888 n [4] = (unsigned char) xe;
889 n [5] = (unsigned char) xf;
891 memcpy (&(sa->sin_addr.s_addr), (void *)n, 4);
892 memcpy (&(sa->sin_port), (void *)&n[4], 2);
893 setup_source_route (my_socket, sa->sin_addr.s_addr);
894 if (connect (my_socket, (struct sockaddr *) sa, sizeof (struct sockaddr_in)) < 0)
895 return 0;
896 return 1;
899 static int
900 initconn (vfs *me, vfs_s_super *super)
902 struct sockaddr_in data_addr;
903 int data, len = sizeof(data_addr);
904 struct protoent *pe;
906 getsockname(SUP.sock, (struct sockaddr *) &data_addr, &len);
907 data_addr.sin_port = 0;
909 pe = getprotobyname("tcp");
910 if (pe == NULL)
911 ERRNOR (EIO, -1);
912 data = socket (AF_INET, SOCK_STREAM, pe->p_proto);
913 if (data < 0)
914 ERRNOR (EIO, -1);
916 if (SUP.use_passive_connection){
917 if ((SUP.use_passive_connection = setup_passive (me, super, data, &data_addr)))
918 return data;
920 SUP.use_source_route = 0;
921 SUP.use_passive_connection = 0;
922 print_vfs_message (_("ftpfs: could not setup passive mode"));
925 /* If passive setup fails, fallback to active connections */
926 /* Active FTP connection */
927 if ((bind (data, (struct sockaddr *)&data_addr, len) == 0) &&
928 (getsockname (data, (struct sockaddr *) &data_addr, &len) == 0) &&
929 (listen (data, 1) == 0))
931 unsigned char *a = (unsigned char *)&data_addr.sin_addr;
932 unsigned char *p = (unsigned char *)&data_addr.sin_port;
934 if (command (me, super, WAIT_REPLY, "PORT %d,%d,%d,%d,%d,%d", a[0], a[1],
935 a[2], a[3], p[0], p[1]) == COMPLETE)
936 return data;
938 close(data);
939 my_errno = EIO;
940 return -1;
943 static int
944 open_data_connection (vfs *me, vfs_s_super *super, char *cmd, char *remote,
945 int isbinary, int reget)
947 struct sockaddr_in from;
948 int s, j, data, fromlen = sizeof(from);
950 if ((s = initconn (me, super)) == -1)
951 return -1;
952 if (changetype (me, super, isbinary) == -1)
953 return -1;
954 if (reget > 0){
955 j = command (me, super, WAIT_REPLY, "REST %d", reget);
956 if (j != CONTINUE)
957 return -1;
959 if (remote) {
960 char * remote_path = translate_path (me, super, remote);
961 j = command (me, super, WAIT_REPLY, "%s /%s", cmd,
962 /* WarFtpD can't STORE //filename */
963 (*remote_path == '/') ? remote_path + 1 : remote_path);
964 g_free (remote_path);
965 } else
966 j = command (me, super, WAIT_REPLY, "%s", cmd);
967 if (j != PRELIM)
968 ERRNOR (EPERM, -1);
969 enable_interrupt_key();
970 if (SUP.use_passive_connection)
971 data = s;
972 else {
973 data = accept (s, (struct sockaddr *)&from, &fromlen);
974 close(s);
975 if (data < 0) {
976 my_errno = errno;
977 return -1;
980 disable_interrupt_key();
981 return data;
984 #define ABORT_TIMEOUT 5
985 static void
986 linear_abort (vfs *me, vfs_s_fh *fh)
988 vfs_s_super *super = FH_SUPER;
989 static unsigned char const ipbuf[3] = { IAC, IP, IAC };
990 fd_set mask;
991 char buf[1024];
992 int dsock = FH_SOCK;
993 FH_SOCK = -1;
994 SUP.control_connection_buzy = 0;
996 print_vfs_message (_("ftpfs: aborting transfer."));
997 if (send (SUP.sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) {
998 print_vfs_message (_("ftpfs: abort error: %s"),
999 unix_error_string (errno));
1000 if (dsock != -1)
1001 close (dsock);
1002 return;
1005 if (command (me, super, NONE, "%cABOR", DM) != COMPLETE) {
1006 print_vfs_message (_("ftpfs: abort failed"));
1007 if (dsock != -1)
1008 close (dsock);
1009 return;
1011 if (dsock != -1) {
1012 FD_ZERO (&mask);
1013 FD_SET (dsock, &mask);
1014 if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) {
1015 struct timeval start_tim, tim;
1016 gettimeofday (&start_tim, NULL);
1017 /* flush the remaining data */
1018 while (read (dsock, buf, sizeof (buf)) > 0) {
1019 gettimeofday (&tim, NULL);
1020 if (tim.tv_sec > start_tim.tv_sec + ABORT_TIMEOUT) {
1021 /* server keeps sending, drop the connection and reconnect */
1022 reconnect (me, super);
1023 return;
1027 close (dsock);
1029 if ((get_reply (me, SUP.sock, NULL, 0) == TRANSIENT) && (code == 426))
1030 get_reply (me, SUP.sock, NULL, 0);
1033 #if 0
1034 static void
1035 resolve_symlink_without_ls_options(vfs *me, vfs_s_super *super, vfs_s_inode *dir)
1037 struct linklist *flist;
1038 struct direntry *fe, *fel;
1039 char tmp[MC_MAXPATHLEN];
1040 int depth;
1042 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1043 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1044 /* flist->data->l_stat is alread initialized with 0 */
1045 fel = flist->data;
1046 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1047 if (fel->linkname[0] == '/') {
1048 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1049 continue;
1050 strcpy (tmp, fel->linkname);
1051 } else {
1052 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1053 continue;
1054 strcpy (tmp, dir->remote_path);
1055 if (tmp[1] != '\0')
1056 strcat (tmp, "/");
1057 strcat (tmp + 1, fel->linkname);
1059 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1060 canonicalize_pathname (tmp);
1061 fe = _get_file_entry(bucket, tmp, 0, 0);
1062 if (fe) {
1063 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1064 /* Symlink points to link which isn't resolved, yet. */
1065 if (fe->linkname[0] == '/') {
1066 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1067 break;
1068 strcpy (tmp, fe->linkname);
1069 } else {
1070 /* at this point tmp looks always like this
1071 /directory/filename, i.e. no need to check
1072 strrchr's return value */
1073 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1074 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1075 break;
1076 strcat (tmp, fe->linkname);
1078 continue;
1079 } else {
1080 fel->l_stat = g_new (struct stat, 1);
1081 if ( S_ISLNK (fe->s.st_mode))
1082 *fel->l_stat = *fe->l_stat;
1083 else
1084 *fel->l_stat = fe->s;
1085 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1088 break;
1092 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1095 static void
1096 resolve_symlink_with_ls_options(vfs *me, vfs_s_super *super, vfs_s_inode *dir)
1098 char buffer[2048] = "", *filename;
1099 int sock;
1100 FILE *fp;
1101 struct stat s;
1102 struct linklist *flist;
1103 struct direntry *fe;
1104 int switch_method = 0;
1106 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1107 if (strchr (dir->remote_path, ' ')) {
1108 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1109 print_vfs_message(_("ftpfs: CWD failed."));
1110 return;
1112 sock = open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1114 else
1115 sock = open_data_connection (bucket, "LIST -lLa",
1116 dir->remote_path, TYPE_ASCII, 0);
1118 if (sock == -1) {
1119 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1120 return;
1123 fp = fdopen(sock, "r");
1124 if (fp == NULL) {
1125 close(sock);
1126 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1127 return;
1129 enable_interrupt_key();
1130 flist = dir->file_list->next;
1131 while (1) {
1132 do {
1133 if (flist == dir->file_list)
1134 goto done;
1135 fe = flist->data;
1136 flist = flist->next;
1137 } while (!S_ISLNK(fe->s.st_mode));
1138 while (1) {
1139 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1140 goto done;
1141 if (logfile){
1142 fputs (buffer, logfile);
1143 fflush (logfile);
1145 vfs_die("This code should be commented out\n");
1146 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1147 int r = strcmp(fe->name, filename);
1148 g_free(filename);
1149 if (r == 0) {
1150 if (S_ISLNK (s.st_mode)) {
1151 /* This server doesn't understand LIST -lLa */
1152 switch_method = 1;
1153 goto done;
1155 fe->l_stat = g_new (struct stat, 1);
1156 if (fe->l_stat == NULL)
1157 goto done;
1158 *fe->l_stat = s;
1159 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1160 break;
1162 if (r < 0)
1163 break;
1167 done:
1168 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1169 disable_interrupt_key();
1170 fclose(fp);
1171 get_reply(me, SUP.sock, NULL, 0);
1174 static void
1175 resolve_symlink(vfs *me, vfs_s_super *super, vfs_s_inode *dir)
1177 print_vfs_message(_("Resolving symlink..."));
1179 if (SUP.strict_rfc959_list_cmd)
1180 resolve_symlink_without_ls_options(me, super, dir);
1181 else
1182 resolve_symlink_with_ls_options(me, super, dir);
1184 #endif
1186 static int
1187 dir_load(vfs *me, vfs_s_inode *dir, char *remote_path)
1189 vfs_s_entry *ent;
1190 vfs_s_super *super = dir->super;
1191 int sock, num_entries = 0;
1192 #ifdef FIXME_LATER
1193 int has_symlinks = 0;
1194 #endif
1195 char buffer[BUF_8K];
1196 int cd_first;
1198 cd_first = ftpfs_first_cd_then_ls || (strchr (remote_path, ' ') != NULL)
1199 || (SUP.strict == RFC_STRICT);
1201 again:
1202 print_vfs_message(_("ftpfs: Reading FTP directory %s... %s%s"), remote_path,
1203 SUP.strict == RFC_STRICT ? _("(strict rfc959)") : "",
1204 cd_first ? _("(chdir first)") : "");
1206 if (cd_first) {
1207 char *p;
1209 p = translate_path (me, super, remote_path);
1211 if (ftpfs_chdir_internal (me, super, p) != COMPLETE) {
1212 g_free (p);
1213 my_errno = ENOENT;
1214 print_vfs_message(_("ftpfs: CWD failed."));
1215 return -1;
1217 g_free (p);
1220 gettimeofday(&dir->u.ftp.timestamp, NULL);
1221 dir->u.ftp.timestamp.tv_sec += ftpfs_directory_timeout;
1223 if (SUP.strict == RFC_STRICT)
1224 sock = open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1225 else if (cd_first)
1226 /* Dirty hack to avoid autoprepending / to . */
1227 /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1228 sock = open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1229 else {
1230 /* Trailing "/." is necessary if remote_path is a symlink */
1231 char *path = concat_dir_and_file (remote_path, ".");
1232 sock = open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1233 g_free (path);
1236 if (sock == -1)
1237 goto fallback;
1239 /* Clear the interrupt flag */
1240 enable_interrupt_key ();
1242 #if 1
1244 /* added 20001006 by gisburn
1245 * add dots '.' and '..'. This must be _executed_ before scanning the dir as the
1246 * code below may jump directly into error handling code (without executing
1247 * remaining code). And C doesn't have try {...} finally {}; :-)
1249 vfs_s_inode *parent = dir->ent->dir;
1251 if( parent==NULL )
1252 parent = dir;
1254 ent = vfs_s_generate_entry(me, ".", dir, 0);
1255 ent->ino->st=dir->st;
1256 num_entries++;
1257 vfs_s_insert_entry(me, dir, ent);
1259 ent = vfs_s_generate_entry(me, "..", parent, 0);
1260 ent->ino->st=parent->st;
1261 num_entries++;
1262 vfs_s_insert_entry(me, dir, ent);
1264 #endif
1266 while (1) {
1267 int i;
1268 int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), sock);
1269 if (!res)
1270 break;
1272 if (res == EINTR) {
1273 me->verrno = ECONNRESET;
1274 close (sock);
1275 disable_interrupt_key();
1276 get_reply(me, SUP.sock, NULL, 0);
1277 print_vfs_message (_("%s: failure"), me->name);
1278 return -1;
1281 if (logfile){
1282 fputs (buffer, logfile);
1283 fputs ("\n", logfile);
1284 fflush (logfile);
1287 ent = vfs_s_generate_entry(me, NULL, dir, 0);
1288 i = ent->ino->st.st_nlink;
1289 if (!vfs_parse_ls_lga (buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) {
1290 vfs_s_free_entry (me, ent);
1291 continue;
1293 ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */
1294 num_entries++;
1295 if ((!strcmp(ent->name, ".")) || (!strcmp (ent->name, ".."))) {
1296 g_free (ent->name);
1297 ent->name = NULL; /* Ouch, vfs_s_free_entry "knows" about . and .. being special :-( */
1298 vfs_s_free_entry (me, ent);
1299 continue;
1302 vfs_s_insert_entry(me, dir, ent);
1305 /* vfs_s_add_dots(me, dir, NULL);
1306 FIXME This really should be here; but we need to provide correct parent.
1307 Disabled for now, please fix it. Pavel
1309 close(sock);
1310 me->verrno = E_REMOTE;
1311 if ((get_reply (me, SUP.sock, NULL, 0) != COMPLETE) || !num_entries)
1312 goto fallback;
1314 if (SUP.strict == RFC_AUTODETECT)
1315 SUP.strict = RFC_DARING;
1317 #ifdef FIXME_LATER
1318 if (has_symlinks) {
1319 if (resolve_symlinks)
1320 resolve_symlink(me, super, dcache);
1321 else
1322 dcache->symlink_status = FTPFS_UNRESOLVED_SYMLINKS;
1324 #endif
1325 print_vfs_message (_("%s: done."), me->name);
1326 return 0;
1328 fallback:
1329 if (SUP.strict == RFC_AUTODETECT) {
1330 /* It's our first attempt to get a directory listing from this
1331 server (UNIX style LIST command) */
1332 SUP.strict = RFC_STRICT;
1333 /* I hate goto, but recursive call needs another 8K on stack */
1334 /* return dir_load (me, dir, remote_path); */
1335 cd_first = 1;
1336 goto again;
1338 print_vfs_message(_("ftpfs: failed; nowhere to fallback to"));
1339 ERRNOR(-1, EACCES);
1342 static int
1343 file_store(vfs *me, vfs_s_fh *fh, char *name, char *localname)
1345 int h, sock, n;
1346 off_t total;
1347 #ifdef HAVE_STRUCT_LINGER
1348 struct linger li;
1349 #else
1350 int flag_one = 1;
1351 #endif
1352 char buffer[8192];
1353 struct stat s;
1354 vfs_s_super *super = FH_SUPER;
1356 h = open(localname, O_RDONLY);
1357 if (h == -1)
1358 ERRNOR (EIO, -1);
1359 fstat(h, &s);
1360 sock = open_data_connection(me, super, fh->u.ftp.append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1361 if (sock < 0) {
1362 close(h);
1363 return -1;
1365 #ifdef HAVE_STRUCT_LINGER
1366 li.l_onoff = 1;
1367 li.l_linger = 120;
1368 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));
1369 #else
1370 setsockopt(sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1371 #endif
1372 total = 0;
1374 enable_interrupt_key();
1375 while (1) {
1376 while ((n = read(h, buffer, sizeof(buffer))) < 0) {
1377 if (errno == EINTR) {
1378 if (got_interrupt()) {
1379 my_errno = EINTR;
1380 goto error_return;
1382 else
1383 continue;
1385 my_errno = errno;
1386 goto error_return;
1388 if (n == 0)
1389 break;
1390 while (write(sock, buffer, n) < 0) {
1391 if (errno == EINTR) {
1392 if (got_interrupt()) {
1393 my_errno = EINTR;
1394 goto error_return;
1396 else
1397 continue;
1399 my_errno = errno;
1400 goto error_return;
1402 total += n;
1403 print_vfs_message(_("ftpfs: storing file %lu (%lu)"),
1404 (unsigned long) total, (unsigned long) s.st_size);
1406 disable_interrupt_key();
1407 close(sock);
1408 close(h);
1409 if (get_reply (me, SUP.sock, NULL, 0) != COMPLETE)
1410 ERRNOR (EIO, -1);
1411 return 0;
1412 error_return:
1413 disable_interrupt_key();
1414 close(sock);
1415 close(h);
1416 get_reply(me, SUP.sock, NULL, 0);
1417 return -1;
1420 static int
1421 linear_start(vfs *me, vfs_s_fh *fh, int offset)
1423 char *name = vfs_s_fullpath (me, fh->ino);
1425 if (!name)
1426 return 0;
1427 FH_SOCK = open_data_connection(me, FH_SUPER, "RETR", name, TYPE_BINARY, offset);
1428 g_free (name);
1429 if (FH_SOCK == -1)
1430 ERRNOR (EACCES, 0);
1431 fh->linear = LS_LINEAR_OPEN;
1432 FH_SUPER->u.ftp.control_connection_buzy = 1;
1433 fh->u.ftp.append = 0;
1434 return 1;
1437 static int
1438 linear_read (vfs *me, vfs_s_fh *fh, void *buf, int len)
1440 int n;
1441 vfs_s_super *super = FH_SUPER;
1443 while ((n = read (FH_SOCK, buf, len))<0) {
1444 if ((errno == EINTR) && !got_interrupt())
1445 continue;
1446 break;
1449 if (n<0)
1450 linear_abort(me, fh);
1452 if (!n) {
1453 SUP.control_connection_buzy = 0;
1454 close (FH_SOCK);
1455 FH_SOCK = -1;
1456 if ((get_reply (me, SUP.sock, NULL, 0) != COMPLETE))
1457 ERRNOR (E_REMOTE, -1);
1458 return 0;
1460 ERRNOR (errno, n);
1463 static void
1464 linear_close (vfs *me, vfs_s_fh *fh)
1466 if (FH_SOCK != -1)
1467 linear_abort(me, fh);
1470 static int ftpfs_ctl (void *fh, int ctlop, int arg)
1472 switch (ctlop) {
1473 case MCCTL_IS_NOTREADY:
1475 int v;
1477 if (!FH->linear)
1478 vfs_die ("You may not do this");
1479 if (FH->linear == LS_LINEAR_CLOSED)
1480 return 0;
1482 v = vfs_s_select_on_two (FH->u.ftp.sock, 0);
1483 if (((v < 0) && (errno == EINTR)) || v == 0)
1484 return 1;
1485 return 0;
1487 default:
1488 return 0;
1492 /* Warning: filename passed to this command is damaged */
1493 static int
1494 send_ftp_command(vfs *me, char *filename, char *cmd, int flags)
1496 char *rpath, *p;
1497 vfs_s_super *super;
1498 int r;
1499 int flush_directory_cache = (flags & OPT_FLUSH);
1501 if (!(rpath = vfs_s_get_path_mangle(me, filename, &super, 0)))
1502 return -1;
1503 p = translate_path (me, super, rpath);
1504 r = command (me, super, WAIT_REPLY, cmd, p);
1505 g_free (p);
1506 vfs_add_noncurrent_stamps (&vfs_ftpfs_ops, (vfsid) super, NULL);
1507 if (flags & OPT_IGNORE_ERROR)
1508 r = COMPLETE;
1509 if (r != COMPLETE)
1510 ERRNOR (EPERM, -1);
1511 if (flush_directory_cache)
1512 vfs_s_invalidate(me, super);
1513 return 0;
1516 /* This routine is called as the last step in load_setup */
1517 void
1518 ftpfs_init_passwd(void)
1520 ftpfs_anonymous_passwd = load_anon_passwd ();
1521 if (ftpfs_anonymous_passwd)
1522 return;
1524 /* If there is no anonymous ftp password specified
1525 * then we'll just use anonymous@
1526 * We don't send any other thing because:
1527 * - We want to remain anonymous
1528 * - We want to stop SPAM
1529 * - We don't want to let ftp sites to discriminate by the user,
1530 * host or country.
1532 ftpfs_anonymous_passwd = g_strdup ("anonymous@");
1535 static int ftpfs_chmod (vfs *me, char *path, int mode)
1537 char buf[BUF_SMALL];
1539 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o /%%s", mode & 07777);
1540 return send_ftp_command(me, path, buf, OPT_FLUSH);
1543 static int ftpfs_chown (vfs *me, char *path, int owner, int group)
1545 #if 0
1546 my_errno = EPERM;
1547 return -1;
1548 #else
1549 /* Everyone knows it is not possible to chown remotely, so why bother them.
1550 If someone's root, then copy/move will always try to chown it... */
1551 return 0;
1552 #endif
1555 static int ftpfs_unlink (vfs *me, char *path)
1557 return send_ftp_command(me, path, "DELE /%s", OPT_FLUSH);
1560 /* Return 1 if path is the same directory as the one we are in now */
1561 static int
1562 is_same_dir (vfs *me, vfs_s_super *super, const char *path)
1564 if (!SUP.cwdir)
1565 return 0;
1566 if (strcmp (path, SUP.cwdir) == 0)
1567 return 1;
1568 return 0;
1571 static int
1572 ftpfs_chdir_internal (vfs *me, vfs_s_super *super, char *remote_path)
1574 int r;
1575 char *p;
1577 if (!SUP.cwd_defered && is_same_dir (me, super, remote_path))
1578 return COMPLETE;
1580 p = translate_path (me, super, remote_path);
1581 r = command (me, super, WAIT_REPLY, "CWD /%s", p);
1582 g_free (p);
1584 if (r != COMPLETE) {
1585 my_errno = EIO;
1586 } else {
1587 g_free(SUP.cwdir);
1588 SUP.cwdir = g_strdup (remote_path);
1589 SUP.cwd_defered = 0;
1591 return r;
1594 static int ftpfs_rename (vfs *me, char *path1, char *path2)
1596 send_ftp_command(me, path1, "RNFR /%s", OPT_FLUSH);
1597 return send_ftp_command(me, path2, "RNTO /%s", OPT_FLUSH);
1600 static int ftpfs_mkdir (vfs *me, char *path, mode_t mode)
1602 return send_ftp_command(me, path, "MKD /%s", OPT_FLUSH);
1605 static int ftpfs_rmdir (vfs *me, char *path)
1607 return send_ftp_command(me, path, "RMD /%s", OPT_FLUSH);
1610 static int ftpfs_fh_open (vfs *me, vfs_s_fh *fh, int flags, int mode)
1612 fh->u.ftp.append = 0;
1613 /* File will be written only, so no need to retrieve it from ftp server */
1614 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY|O_RDWR))){
1615 #ifdef HAVE_STRUCT_LINGER
1616 struct linger li;
1617 #else
1618 int li = 1;
1619 #endif
1620 char * name;
1622 /* linear_start() called, so data will be written
1623 * to local temporary file and stored to ftp server
1624 * by vfs_s_close later
1626 if (FH_SUPER->u.ftp.control_connection_buzy){
1627 if (!fh->ino->localname){
1628 int handle = mc_mkstemps (&fh->ino->localname, me->name, NULL);
1629 if (handle == -1)
1630 return -1;
1631 close (handle);
1632 fh->u.ftp.append = flags & O_APPEND;
1634 return 0;
1636 name = vfs_s_fullpath (me, fh->ino);
1637 if (!name)
1638 return -1;
1639 fh->handle = open_data_connection(me, fh->ino->super,
1640 (flags & O_APPEND) ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1641 g_free (name);
1643 if (fh->handle < 0)
1644 return -1;
1645 #ifdef HAVE_STRUCT_LINGER
1646 li.l_onoff = 1;
1647 li.l_linger = 120;
1648 #endif
1649 setsockopt(fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof(li));
1651 if (fh->ino->localname){
1652 unlink (fh->ino->localname);
1653 g_free (fh->ino->localname);
1654 fh->ino->localname = NULL;
1656 return 0;
1659 if (!fh->ino->localname)
1660 if (vfs_s_retrieve_file (me, fh->ino)==-1)
1661 return -1;
1662 if (!fh->ino->localname)
1663 vfs_die( "retrieve_file failed to fill in localname" );
1664 return 0;
1667 static int ftpfs_fh_close (vfs *me, vfs_s_fh *fh)
1669 if (fh->handle != -1 && !fh->ino->localname){
1670 close (fh->handle);
1671 fh->handle = -1;
1672 /* File is stored to destination already, so
1673 * we prevent MEDATA->file_store() call from vfs_s_close ()
1675 fh->changed = 0;
1676 if (get_reply (me, fh->ino->SUP.sock, NULL, 0) != COMPLETE)
1677 ERRNOR (EIO, -1);
1678 vfs_s_invalidate (me, FH_SUPER);
1680 return 0;
1683 static struct vfs_s_data ftp_data = {
1684 NULL,
1687 NULL, /* logfile */
1689 NULL, /* init_inode */
1690 NULL, /* free_inode */
1691 NULL, /* init_entry */
1693 NULL, /* archive_check */
1694 archive_same,
1695 open_archive,
1696 free_archive,
1698 ftpfs_fh_open, /* fh_open */
1699 ftpfs_fh_close, /* fh_close */
1701 vfs_s_find_entry_linear,
1702 dir_load,
1703 dir_uptodate,
1704 file_store,
1706 linear_start,
1707 linear_read,
1708 linear_close
1711 static void
1712 ftpfs_fill_names (vfs *me, void (*func)(char *))
1714 struct vfs_s_super * super = ftp_data.supers;
1715 char *name;
1717 while (super){
1718 name = g_strconcat ("/#ftp:", SUP.user, "@", SUP.host, "/", SUP.cwdir, NULL);
1719 (*func)(name);
1720 g_free (name);
1721 super = super->next;
1725 vfs vfs_ftpfs_ops = {
1726 NULL, /* This is place of next pointer */
1727 "ftpfs",
1728 F_NET, /* flags */
1729 "ftp:", /* prefix */
1730 &ftp_data, /* data */
1731 0, /* errno */
1732 NULL, /* init */
1733 NULL, /* done */
1734 ftpfs_fill_names,
1735 NULL,
1737 vfs_s_open,
1738 vfs_s_close,
1739 vfs_s_read,
1740 vfs_s_write,
1742 vfs_s_opendir,
1743 vfs_s_readdir,
1744 vfs_s_closedir,
1745 vfs_s_telldir,
1746 vfs_s_seekdir,
1748 vfs_s_stat,
1749 vfs_s_lstat,
1750 vfs_s_fstat,
1752 ftpfs_chmod,
1753 ftpfs_chown, /* not really implemented but returns success */
1754 NULL,
1756 vfs_s_readlink,
1757 NULL,
1758 NULL,
1759 ftpfs_unlink,
1761 ftpfs_rename,
1762 vfs_s_chdir,
1763 vfs_s_ferrno,
1764 vfs_s_lseek,
1765 NULL,
1767 vfs_s_getid,
1768 vfs_s_nothingisopen,
1769 vfs_s_free,
1771 NULL,
1772 NULL,
1774 ftpfs_mkdir,
1775 ftpfs_rmdir,
1776 ftpfs_ctl,
1777 vfs_s_setctl
1779 MMAPNULL
1782 void ftpfs_set_debug (const char *file)
1784 logfile = fopen (file, "w+");
1785 if (logfile)
1786 ftp_data.logfile = logfile;
1789 static char buffer[BUF_MEDIUM];
1790 static char *netrc, *netrcp;
1792 /* This should match the keywords[] array below */
1793 typedef enum {
1794 NETRC_NONE = 0,
1795 NETRC_DEFAULT,
1796 NETRC_MACHINE,
1797 NETRC_LOGIN,
1798 NETRC_PASSWORD,
1799 NETRC_PASSWD,
1800 NETRC_ACCOUNT,
1801 NETRC_MACDEF,
1802 NETRC_UNKNOWN
1803 } keyword_t;
1805 static keyword_t netrc_next (void)
1807 char *p;
1808 keyword_t i;
1809 static const char *const keywords[] = { "default", "machine",
1810 "login", "password", "passwd", "account", "macdef", NULL
1814 while (1) {
1815 netrcp = skip_separators (netrcp);
1816 if (*netrcp != '\n')
1817 break;
1818 netrcp++;
1820 if (!*netrcp)
1821 return NETRC_NONE;
1822 p = buffer;
1823 if (*netrcp == '"') {
1824 for (netrcp++; *netrcp != '"' && *netrcp; netrcp++) {
1825 if (*netrcp == '\\')
1826 netrcp++;
1827 *p++ = *netrcp;
1829 } else {
1830 for (; *netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1831 *netrcp != ',' && *netrcp; netrcp++) {
1832 if (*netrcp == '\\')
1833 netrcp++;
1834 *p++ = *netrcp;
1837 *p = 0;
1838 if (!*buffer)
1839 return 0;
1841 i = NETRC_DEFAULT;
1842 while (keywords[i - 1]) {
1843 if (!strcmp (keywords[i - 1], buffer))
1844 return i;
1846 i++;
1849 return NETRC_UNKNOWN;
1852 static int netrc_has_incorrect_mode (char *netrcname, char *netrc)
1854 static int be_angry = 1;
1855 struct stat mystat;
1857 if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077)) {
1858 if (be_angry) {
1859 message_1s (1, MSG_ERROR,
1860 _("~/.netrc file has not correct mode.\n"
1861 "Remove password or correct mode."));
1862 be_angry = 0;
1864 return 1;
1866 return 0;
1869 /* Scan .netrc until we find matching "machine" or "default"
1870 * domain is used for additional matching
1871 * No search is done after "default" in compliance with "man netrc"
1872 * Return 0 if found, -1 otherwise */
1873 static int find_machine (const char *host, const char *domain)
1875 keyword_t keyword;
1877 while ((keyword = netrc_next ()) != NETRC_NONE) {
1878 if (keyword == NETRC_DEFAULT)
1879 return 0;
1881 if (keyword == NETRC_MACDEF) {
1882 /* Scan for an empty line, which concludes "macdef" */
1883 do {
1884 while (*netrcp && *netrcp != '\n')
1885 netrcp++;
1886 if (*netrcp != '\n')
1887 break;
1888 netrcp++;
1889 } while (*netrcp && *netrcp != '\n');
1890 continue;
1893 if (keyword != NETRC_MACHINE)
1894 continue;
1896 /* Take machine name */
1897 if (netrc_next () == NETRC_NONE)
1898 break;
1900 if (g_strcasecmp (host, buffer)) {
1901 /* Try adding our domain to short names in .netrc */
1902 char *host_domain = strchr (host, '.');
1903 if (!host_domain)
1904 continue;
1906 /* Compare domain part */
1907 if (g_strcasecmp (host_domain, domain))
1908 continue;
1910 /* Compare local part */
1911 if (g_strncasecmp (host, buffer, host_domain - host))
1912 continue;
1915 return 0;
1918 /* end of .netrc */
1919 return -1;
1922 /* Extract login and password from .netrc for the host.
1923 * pass may be NULL.
1924 * Returns 0 for success, -1 for error */
1925 static int lookup_netrc (const char *host, char **login, char **pass)
1927 char *netrcname;
1928 char *tmp_pass = NULL;
1929 char hostname[MAXHOSTNAMELEN], *domain;
1930 keyword_t keyword;
1931 static struct rupcache {
1932 struct rupcache *next;
1933 char *host;
1934 char *login;
1935 char *pass;
1936 } *rup_cache = NULL, *rupp;
1938 /* Initialize *login and *pass */
1939 if (!login)
1940 return 0;
1941 *login = NULL;
1942 if (pass)
1943 *pass = NULL;
1945 /* Look up in the cache first */
1946 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) {
1947 if (!strcmp (host, rupp->host)) {
1948 if (rupp->login)
1949 *login = g_strdup (rupp->login);
1950 if (pass && rupp->pass)
1951 *pass = g_strdup (rupp->pass);
1952 return 0;
1956 /* Load current .netrc */
1957 netrcname = concat_dir_and_file (home_dir, ".netrc");
1958 netrcp = netrc = load_file (netrcname);
1959 if (netrc == NULL) {
1960 g_free (netrcname);
1961 return 0;
1964 /* Find our own domain name */
1965 if (gethostname (hostname, sizeof (hostname)) < 0)
1966 *hostname = 0;
1967 if (!(domain = strchr (hostname, '.')))
1968 domain = "";
1970 /* Scan for "default" and matching "machine" keywords */
1971 find_machine (host, domain);
1973 /* Scan for keywords following "default" and "machine" */
1974 while (1) {
1975 int need_break = 0;
1976 keyword = netrc_next ();
1978 switch (keyword) {
1979 case NETRC_LOGIN:
1980 if (netrc_next () == NETRC_NONE) {
1981 need_break = 1;
1982 break;
1985 /* We have another name already - should not happen */
1986 if (*login) {
1987 need_break = 1;
1988 break;
1991 /* We have login name now */
1992 *login = g_strdup (buffer);
1993 break;
1995 case NETRC_PASSWORD:
1996 case NETRC_PASSWD:
1997 if (netrc_next () == NETRC_NONE) {
1998 need_break = 1;
1999 break;
2002 /* Ignore unsafe passwords */
2003 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp")
2004 && netrc_has_incorrect_mode (netrcname, netrc)) {
2005 need_break = 1;
2006 break;
2009 /* Remember password. pass may be NULL, so use tmp_pass */
2010 if (tmp_pass == NULL)
2011 tmp_pass = g_strdup (buffer);
2012 break;
2014 case NETRC_ACCOUNT:
2015 /* "account" is followed by a token which we ignore */
2016 if (netrc_next () == NETRC_NONE) {
2017 need_break = 1;
2018 break;
2021 /* Ignore account, but warn user anyways */
2022 netrc_has_incorrect_mode (netrcname, netrc);
2023 break;
2025 default:
2026 /* Unexpected keyword or end of file */
2027 need_break = 1;
2028 break;
2031 if (need_break)
2032 break;
2035 g_free (netrc);
2036 g_free (netrcname);
2038 rupp = g_new (struct rupcache, 1);
2039 rupp->host = g_strdup (host);
2040 rupp->login = rupp->pass = 0;
2042 if (*login != NULL) {
2043 rupp->login = g_strdup (*login);
2045 if (tmp_pass != NULL)
2046 rupp->pass = g_strdup (tmp_pass);
2047 rupp->next = rup_cache;
2048 rup_cache = rupp;
2050 if (pass)
2051 *pass = tmp_pass;
2053 return 0;